added test case for the 'filename' package
[swftools.git] / lib / pdf / BitmapOutputDev.cc
1 /* InfoOutputDev.h
2
3    Output Device which creates a bitmap.
4
5    Swftools is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 2 of the License, or
8    (at your option) any later version.
9
10    Swftools is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with swftools; if not, write to the Free Software
17    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
18
19 #include <stdlib.h>
20 #include <stdio.h>
21 #include <memory.h>
22 #include <assert.h>
23 #include "config.h"
24 #include "BitmapOutputDev.h"
25 #include "GFXOutputDev.h"
26 #include "SplashBitmap.h"
27 #include "SplashPattern.h"
28 #include "Splash.h"
29 #include "../log.h"
30 #include "../png.h"
31 #include "../devices/record.h"
32 #include "../types.h"
33
34 #define UNKNOWN_BOUNDING_BOX 0,0,0,0
35
36 static SplashColor splash_white = {255,255,255};
37 static SplashColor splash_black = {0,0,0};
38     
39 ClipState::ClipState()
40 {
41     this->next = 0;
42     this->clipbitmap = 0;
43     this->written = 0;
44 }
45
46 BitmapOutputDev::BitmapOutputDev(InfoOutputDev*info, PDFDoc*doc)
47 {
48     this->info = info;
49     this->doc = doc;
50     this->xref = doc->getXRef();
51     
52     /* color graphic output device, for creating bitmaps */
53     this->rgbdev = new SplashOutputDev(splashModeRGB8, 1, gFalse, splash_white, gTrue, gTrue);
54   
55     /* color mode for binary bitmaps */
56     SplashColorMode colorMode = splashModeMono1;
57
58     /* two devices for testing things against clipping: one clips, the other doesn't */
59     this->clip0dev = new SplashOutputDev(colorMode, 1, gFalse, splash_black, gTrue, gFalse);
60     this->clip1dev = new SplashOutputDev(colorMode, 1, gFalse, splash_black, gTrue, gFalse);
61     
62     /* device indicating where polygonal pixels were drawn */
63     this->boolpolydev = new SplashOutputDev(colorMode, 1, gFalse, splash_black, gTrue, gFalse);
64     /* device indicating where text pixels were drawn */
65     this->booltextdev = new SplashOutputDev(colorMode, 1, gFalse, splash_black, gTrue, gFalse);
66
67     /* device for handling texts and links */
68     this->gfxdev = new GFXOutputDev(info, this->doc);
69
70     this->rgbdev->startDoc(this->xref);
71     this->boolpolydev->startDoc(this->xref);
72     this->booltextdev->startDoc(this->xref);
73     this->clip0dev->startDoc(this->xref);
74     this->clip1dev->startDoc(this->xref);
75
76     this->gfxoutput = (gfxdevice_t*)malloc(sizeof(gfxdevice_t));
77     gfxdevice_record_init(this->gfxoutput);
78
79     this->gfxdev->setDevice(this->gfxoutput);
80     
81     this->config_extrafontdata = 0;
82     this->bboxpath = 0;
83     //this->clipdev = 0;
84     //this->clipstates = 0;
85 }
86 BitmapOutputDev::~BitmapOutputDev()
87 {
88     if(this->gfxoutput) {
89         gfxresult_t*r = this->gfxoutput->finish(this->gfxoutput);
90         r->destroy(r);
91         free(this->gfxoutput);this->gfxoutput = 0;
92     }
93     if(this->bboxpath) {
94         delete this->bboxpath;this->bboxpath = 0;
95     }
96     if(this->rgbdev) {
97         delete this->rgbdev;this->rgbdev = 0;
98     }
99     if(this->gfxdev) {
100         delete this->gfxdev;this->gfxdev= 0;
101     }
102     if(this->boolpolydev) {
103         delete this->boolpolydev;this->boolpolydev = 0;
104     }
105     if(this->booltextdev) {
106         delete this->booltextdev;this->booltextdev = 0;
107     }
108     if(this->clip0dev) {
109         delete this->clip0dev;this->clip0dev = 0;
110     }
111     if(this->clip1dev) {
112         delete this->clip1dev;this->clip1dev = 0;
113     }
114     //if(this->clipbitmap) {
115     //    delete this->clipbitmap;this->clipbitmap = 0;
116     //}
117     //if(this->clipdev) {
118     //    delete this->clipdev;this->clipdev = 0;
119     //}
120
121 }
122
123 GBool BitmapOutputDev::getVectorAntialias()
124 {
125     return this->rgbdev->getVectorAntialias();
126 }
127 void BitmapOutputDev::setVectorAntialias(GBool vaa)
128 {
129     this->rgbdev->setVectorAntialias(vaa);
130 }
131 void BitmapOutputDev::setDevice(gfxdevice_t*dev)
132 {
133     this->dev = dev;
134 }
135 void BitmapOutputDev::setMove(int x,int y)
136 {
137     this->gfxdev->setMove(x,y);
138     this->user_movex = x;
139     this->user_movey = y;
140 }
141 void BitmapOutputDev::setClip(int x1,int y1,int x2,int y2)
142 {
143     this->gfxdev->setClip(x1,y1,x2,y2);
144     this->user_clipx1 = x1;
145     this->user_clipy1 = y1;
146     this->user_clipx2 = x2;
147     this->user_clipy2 = y2;
148 }
149 void BitmapOutputDev::setParameter(const char*key, const char*value)
150 {
151     if(!strcmp(key, "extrafontdata")) {
152         this->config_extrafontdata = atoi(value);
153     }
154     this->gfxdev->setParameter(key, value);
155 }
156 void BitmapOutputDev::setPageMap(int*page2page, int num_pages)
157 {
158     this->gfxdev->setPageMap(page2page, num_pages);
159 }
160
161 static void getBitmapBBox(Guchar*alpha, int width, int height, int*xmin, int*ymin, int*xmax, int*ymax)
162 {
163     *ymin = -1;
164     *xmin = width;
165     *xmax = 0;
166     int x,y;
167     for(y=0;y<height;y++) {
168         Guchar*a = &alpha[y*width];
169         for(x=0;x<width;x++) {
170             if(a[x]) break;
171         }
172         int left = x; //first occupied pixel from left
173         int right = x+1; //last non-occupied pixel from right
174         for(;x<width;x++) {
175             if(a[x]) right=x+1;
176         }
177
178         if(left!=width) {
179             if(*ymin<0) 
180                 *ymin=y;
181             *ymax=y+1;
182             if(left<*xmin) *xmin = left;
183             if(right>*xmax) *xmax = right;
184         }
185     }
186     if(*xmin>=*xmax || *ymin>=*ymax) {
187         *xmin = 0;
188         *ymin = 0;
189         *xmax = 0;
190         *ymax = 0;
191     }
192 }
193
194 void BitmapOutputDev::flushBitmap()
195 {
196     int width = rgbdev->getBitmapWidth();
197     int height = rgbdev->getBitmapHeight();
198     
199     SplashColorPtr rgb = rgbbitmap->getDataPtr();
200     Guchar*alpha = rgbbitmap->getAlphaPtr();
201     Guchar*alpha2 = boolpolybitmap->getAlphaPtr();
202
203     int xmin,ymin,xmax,ymax;
204     getBitmapBBox(alpha, width, height, &xmin,&ymin,&xmax,&ymax);
205
206     /* clip against (-movex, -movey, -movex+width, -movey+height) */
207     if(xmin < -this->movex) xmin = -this->movex;
208     if(ymin < -this->movey) ymin = -this->movey;
209     if(xmax > -this->movex + width) xmax = -this->movex+this->width;
210     if(ymax > -this->movey + height) ymax = -this->movey+this->height;
211
212     msg("<verbose> Flushing bitmap (bbox: %d,%d,%d,%d)", xmin,ymin,xmax,ymax);
213     
214     if((xmax-xmin)<=0 || (ymax-ymin)<=0) // no bitmap, nothing to do
215         return;
216
217     if(sizeof(SplashColor)!=3) {
218         msg("<error> sizeof(SplashColor)!=3");
219         return;
220     }
221     //xmin = ymin = 0;
222     //xmax = width;
223     //ymax = height;
224     
225     int rangex = xmax-xmin;
226     int rangey = ymax-ymin;
227     gfximage_t*img = (gfximage_t*)malloc(sizeof(gfximage_t)); 
228     img->data = (gfxcolor_t*)malloc(rangex * rangey * 4);
229     img->width = rangex;
230     img->height = rangey;
231     int x,y;
232     for(y=0;y<rangey;y++) {
233         SplashColorPtr in=&rgb[((y+ymin)*width+xmin)*sizeof(SplashColor)];
234         gfxcolor_t*out = &img->data[y*rangex];
235         Guchar*ain = &alpha[(y+ymin)*width+xmin];
236         Guchar*ain2 = &alpha2[(y+ymin)*width+xmin];
237         if(this->emptypage) {
238             for(x=0;x<rangex;x++) {
239                 /* the first bitmap on the page doesn't need to have an alpha channel-
240                    blend against a white background*/
241                 out[x].r = (in[x*3+0]*ain[x])/255 + 255-ain[x];
242                 out[x].g = (in[x*3+1]*ain[x])/255 + 255-ain[x];
243                 out[x].b = (in[x*3+2]*ain[x])/255 + 255-ain[x];
244                 out[x].a = 255;
245             }
246         } else {
247             for(x=0;x<rangex;x++) {
248                 /*
249                 if(!ain2[x]) {
250                     out[x].r = 0;out[x].g = 0;out[x].b = 0;out[x].a = 0;
251                 } else {*/
252
253                 /* according to endPage()/compositeBackground() in xpdf/SplashOutputDev.cc, we
254                    have to premultiply alpha (mix background and pixel according to the alpha channel).
255                 */
256                 out[x].r = (in[x*3+0]*ain[x])/255;
257                 out[x].g = (in[x*3+1]*ain[x])/255;
258                 out[x].b = (in[x*3+2]*ain[x])/255;
259                 out[x].a = ain[x];
260             }
261         }
262     }
263     /* transform bitmap rectangle to "device space" */
264     xmin += movex;
265     ymin += movey;
266     xmax += movex;
267     ymax += movey;
268
269     gfxmatrix_t m;
270     m.tx = xmin;
271     m.ty = ymin;
272     m.m00 = m.m11 = 1;
273     m.m10 = m.m01 = 0;
274     m.tx -= 0.5;
275     m.ty -= 0.5;
276
277     gfxline_t* line = gfxline_makerectangle(xmin, ymin, xmax, ymax);
278     dev->fillbitmap(dev, line, img, &m, 0);
279     gfxline_free(line);
280
281     memset(rgbbitmap->getAlphaPtr(), 0, rgbbitmap->getWidth()*rgbbitmap->getHeight());
282     memset(rgbbitmap->getDataPtr(), 0, rgbbitmap->getRowSize()*rgbbitmap->getHeight());
283
284     free(img->data);img->data=0;free(img);img=0;
285
286     this->emptypage = 0;
287 }
288
289 void BitmapOutputDev::flushText()
290 {
291     msg("<verbose> Flushing text/polygons");
292     gfxdevice_record_flush(this->gfxoutput, this->dev);
293     
294     this->emptypage = 0;
295 }
296
297 void writeBitmap(SplashBitmap*bitmap, char*filename)
298 {
299     int y,x;
300     
301     int width = bitmap->getWidth();
302     int height = bitmap->getHeight();
303
304     gfxcolor_t*data = (gfxcolor_t*)malloc(sizeof(gfxcolor_t)*width*height);
305
306     unsigned char aa=0;
307     if(bitmap->getMode()==splashModeMono1)
308         aa=255;
309
310     for(y=0;y<height;y++) {
311         gfxcolor_t*line = &data[y*width];
312         for(x=0;x<width;x++) {
313             Guchar c[4] = {0,0,0,0};
314             bitmap->getPixel(x,y,c);
315             line[x].r = c[0];
316             line[x].g = c[1];
317             line[x].b = c[2];
318             if(aa) {
319                 line[x].a = aa;
320             } else {
321                 int a = bitmap->getAlpha(x,y);
322                 line[x].a = a;
323             }
324         }
325     }
326     writePNG(filename, (unsigned char*)data, width, height);
327     free(data);
328 }
329
330 void writeAlpha(SplashBitmap*bitmap, char*filename)
331 {
332     int y,x;
333     
334     int width = bitmap->getWidth();
335     int height = bitmap->getHeight();
336     
337     if(bitmap->getMode()==splashModeMono1) {
338         writeBitmap(bitmap, filename);
339         return;
340     }
341
342     gfxcolor_t*data = (gfxcolor_t*)malloc(sizeof(gfxcolor_t)*width*height);
343
344     for(y=0;y<height;y++) {
345         gfxcolor_t*line = &data[y*width];
346         for(x=0;x<width;x++) {
347             int a = bitmap->getAlpha(x,y);
348             line[x].r = a;
349             line[x].g = a;
350             line[x].b = a;
351             line[x].a = 255;
352         }
353     }
354     writePNG(filename, (unsigned char*)data, width, height);
355     free(data);
356 }
357 static int dbg_btm_counter=1;
358
359 static const char*STATE_NAME[] = {"parallel", "textabovebitmap", "bitmapabovetext"};
360
361 int checkAlphaSanity(SplashBitmap*boolbtm, SplashBitmap*alphabtm)
362 {
363     assert(boolbtm->getWidth() == alphabtm->getWidth());
364     assert(boolbtm->getHeight() == alphabtm->getHeight());
365     if(boolbtm->getMode()==splashModeMono1) {
366         return 1;
367     }
368
369     int width = boolbtm->getWidth();
370     int height = boolbtm->getHeight();
371
372     int bad=0;
373     int x,y;
374     for(y=0;y<height;y++) {
375         for(x=0;x<width;x++) {
376             int a1 = alphabtm->getAlpha(x,y);
377             int a2 = boolbtm->getAlpha(x,y);
378             if(a1!=a2) {
379                 bad++;
380             }
381         }
382     }
383     double badness = bad/(double)(width*height);
384     if(badness>0.2) {
385         msg("<error> Bitmaps don't correspond: %d out of %d pixels wrong (%.2f%%)", bad, width*height, 
386                 badness*100.0);
387         return 0;
388     }
389     msg("<notice> %f", badness);
390     return 1;
391 }
392
393 GBool BitmapOutputDev::checkNewText(int x1, int y1, int x2, int y2)
394 {
395     /* called once some new text was drawn on booltextdev, and
396        before the same thing is drawn on gfxdev */
397    
398     msg("<trace> Testing new text data against current bitmap data, state=%s, counter=%d\n", STATE_NAME[layerstate], dbg_btm_counter);
399     
400     char filename1[80];
401     char filename2[80];
402     char filename3[80];
403     sprintf(filename1, "state%dboolbitmap_afternewtext.png", dbg_btm_counter);
404     sprintf(filename2, "state%dbooltext_afternewtext.png", dbg_btm_counter);
405     sprintf(filename3, "state%dbitmap_afternewtext.png", dbg_btm_counter);
406     if(0) {
407         msg("<verbose> %s %s %s", filename1, filename2, filename3);
408         writeAlpha(boolpolybitmap, filename1);
409         writeAlpha(booltextbitmap, filename2);
410         writeBitmap(rgbdev->getBitmap(), filename3);
411     }
412     dbg_btm_counter++;
413
414     GBool ret = false;
415     if(intersection(x1,y1,x2,y2)) {
416         if(layerstate==STATE_PARALLEL) {
417             /* the new text is above the bitmap. So record that fact,
418                and also clear the bitmap buffer, so we can check for
419                new intersections */
420             msg("<verbose> Text is above current bitmap/polygon data");
421             layerstate=STATE_TEXT_IS_ABOVE;
422             clearBoolPolyDev(x1,y1,x2,y2);
423         } else if(layerstate==STATE_BITMAP_IS_ABOVE) {
424             /* there's a bitmap above the (old) text. So we need
425                to flush out that text, and record that the *new*
426                text is now *above* the bitmap
427              */
428             msg("<verbose> Text is above current bitmap/polygon data (which is above some other text)");
429             flushText();
430             layerstate=STATE_TEXT_IS_ABOVE;
431             /* clear both bool devices- the text device because
432                we just dumped out all the (old) text, and the
433                poly dev so we can check for new intersections */
434             clearBoolPolyDev(x1,y1,x2,y2);
435             /* FIXME this destroys the text pixels we just
436                drew to test for the intersection- however we need
437                those to check for the *new* intersections */
438             clearBoolTextDev(x1,y1,x2,y2);
439             ret = true;
440         } else {
441             /* we already know that the current text section is
442                above the current bitmap section- now just new
443                bitmap data *and* new text data was drawn, and
444                *again* it's above the current bitmap- so clear
445                the polygon bitmap again, so we can check for
446                new intersections */
447             msg("<verbose> Text is still above current bitmap/polygon data");
448             clearBoolPolyDev(x1,y1,x2,y2);
449         }
450     } 
451     return ret;
452 }
453
454 GBool BitmapOutputDev::checkNewBitmap(int x1, int y1, int x2, int y2)
455 {
456     /* similar to checkNewText() above, only in reverse */
457     msg("<trace> Testing new graphics data against current text data, state=%s, counter=%d\n", STATE_NAME[layerstate], dbg_btm_counter);
458
459     char filename1[80];
460     char filename2[80];
461     char filename3[80];
462     sprintf(filename1, "state%dboolbitmap_afternewgfx.png", dbg_btm_counter);
463     sprintf(filename2, "state%dbooltext_afternewgfx.png", dbg_btm_counter);
464     sprintf(filename3, "state%dbitmap_afternewgfx.png", dbg_btm_counter);
465
466     if(0) {
467         msg("<verbose> %s %s %s", filename1, filename2, filename3);
468         writeAlpha(boolpolybitmap, filename1);
469         writeAlpha(booltextbitmap, filename2);
470         writeBitmap(rgbdev->getBitmap(), filename3);
471     }
472     dbg_btm_counter++;
473
474     GBool ret = false;
475     if(intersection(x1,y1,x2,y2)) {
476         if(layerstate==STATE_PARALLEL) {
477             msg("<verbose> Bitmap is above current text data");
478             layerstate=STATE_BITMAP_IS_ABOVE;
479             clearBoolTextDev(x1,y1,x2,y2);
480         } else if(layerstate==STATE_TEXT_IS_ABOVE) {
481             msg("<verbose> Bitmap is above current text data (which is above some bitmap)");
482             flushBitmap();
483             layerstate=STATE_BITMAP_IS_ABOVE;
484             clearBoolTextDev(x1,y1,x2,y2);
485             /* FIXME this destroys the polygon pixels we just
486                drew to test for the intersection- however we need
487                those to check for the *new* intersections */
488             clearBoolPolyDev(x1,y1,x2,y2);
489             ret = true;
490         } else {
491             msg("<verbose> Bitmap is still above current text data");
492             clearBoolTextDev(x1,y1,x2,y2);
493         }
494     } 
495     return ret;
496 }
497
498 //void checkNewText() {
499 //    Guchar*alpha = rgbbitmap->getAlphaPtr();
500 //    Guchar*charpixels = clip1bitmap->getDataPtr();
501 //    int xx,yy;
502 //    for(yy=0;yy<height;yy++) {
503 //        Guchar*aline = &alpha[yy*width];
504 //        Guchar*cline = &charpixels[yy*width8];
505 //        for(xx=0;xx<width;xx++) {
506 //            int bit = xx&7;
507 //            int bytepos = xx>>3;
508 //            /* TODO: is the bit order correct? */
509 //            if(aline[xx] && (cline[bytepos]&(1<<bit))) 
510 //              break;
511 //        }
512 //        if(xx!=width)
513 //            break;
514 //}
515
516 static inline GBool fixBBox(int*x1, int*y1, int*x2, int*y2, int width, int height)
517 {
518     if(!(*x1|*y1|*x2|*y2)) {
519         // undefined bbox
520         *x1 = *y1 = 0;
521         *x2 = width;
522         *y2 = height;
523         return gTrue;
524     }
525     if(*x2<=*x1) return gFalse;
526     if(*x2<0) return gFalse;
527     if(*x1<0) *x1 = 0;
528     if(*x1>=width) return gFalse;
529     if(*x2>width) *x2=width;
530
531     if(*y2<=*y1) return gFalse;
532     if(*y2<0) return gFalse;
533     if(*y1<0) *y1 = 0;
534     if(*y1>=height) return gFalse;
535     if(*y2>height) *y2=height;
536     return gTrue;
537 }
538
539 GBool BitmapOutputDev::clip0and1differ(int x1,int y1,int x2,int y2)
540 {
541     if(clip0bitmap->getMode()==splashModeMono1) {
542         int width = clip0bitmap->getWidth();
543         int width8 = (width+7)/8;
544         int height = clip0bitmap->getHeight();
545
546         if(fixBBox(&x1,&y1,&x2,&y2,width,height)) {
547             /* area is outside or null */
548             return gFalse;
549         }
550         
551         SplashBitmap*clip0 = clip0bitmap;
552         SplashBitmap*clip1 = clip1bitmap;
553         int x18 = x1/8;
554         int x28 = (x2+7)/8;
555         int y;
556
557         for(y=y1;y<y2;y++) {
558             unsigned char*row1 = &clip0bitmap->getDataPtr()[width8*y+x18];
559             unsigned char*row2 = &clip1bitmap->getDataPtr()[width8*y+x28];
560             if(memcmp(row1, row2, x28-x18))
561                 return gTrue;
562         }
563         return gFalse;
564     } else {
565         SplashBitmap*clip0 = clip0bitmap;
566         SplashBitmap*clip1 = clip1bitmap;
567         int width = clip0->getAlphaRowSize();
568         int height = clip0->getHeight();
569
570         if(!fixBBox(&x1, &y1, &x2, &y2, width, height)) {
571             x1=y1=0;x2=y2=1;
572         }
573
574         Guchar*a0 = clip0->getAlphaPtr();
575         Guchar*a1 = clip1->getAlphaPtr();
576         int x,y;
577         char differs=0;
578         for(y=y1;y<y2;y++) {
579             for(x=x1;x<x2;x++) {
580                 if(a0[y*width+x]!=a1[y*width+x]) {
581                     differs=1;
582                     break;
583                 }
584             }
585             if(differs)
586                 break;
587         }
588         char differs2 = memcmp(a0, a1, width*height);
589         if(differs && !differs2) 
590             msg("<warning> Strange internal error (2)");
591         else if(!differs && differs2) {
592             msg("<warning> Bad Bounding Box: Difference in clip0 and clip1 outside bbox");
593             msg("<warning> %d %d %d %d", x1, y1, x2, y2);
594         }
595         return differs2;
596     }
597 }
598
599 static void clearBooleanBitmap(SplashBitmap*btm, int x1, int y1, int x2, int y2)
600 {
601     if(!(x1|y1|x2|y2)) {
602         x1 = y1 = 0;
603         x2 = btm->getWidth();
604         y2 = btm->getHeight();
605     }
606     if(btm->getMode()==splashModeMono1) {
607         int width8 = (btm->getWidth()+7)/8;
608         int width = btm->getWidth();
609         int height = btm->getHeight();
610         memset(btm->getDataPtr(), 0, width8*height);
611     } else {
612         int width = btm->getAlphaRowSize();
613         int height = btm->getHeight();
614         memset(btm->getAlphaPtr(), 0, width*height);
615     }
616 }
617
618 GBool compare8(unsigned char*data1, unsigned char*data2, int len)
619 {
620     if(!len)
621         return 0;
622     if(((ptroff_t)data1&7)==((ptroff_t)data2&7)) {
623         // oh good, we can do aligning
624         while((ptroff_t)data1&7) {
625             if(*data1&*data2)
626                 return 1;
627             data1++;
628             data2++;
629             if(!--len)
630                 return 0;
631         }
632     }
633     /* use 64 bit for the (hopefully aligned) middle section */
634     int l8 = len/8;
635     long long unsigned int*d1 = (long long unsigned int*)data1;
636     long long unsigned int*d2 = (long long unsigned int*)data2;
637     long long unsigned int x = 0;
638     int t;
639     for(t=0;t<l8;t++) {
640         x |= d1[t]&d2[t];
641     }
642     if(x)
643         return 1;
644
645     data1+=l8*8;
646     data2+=l8*8;
647     len -= l8*8;
648     for(t=0;t<len;t++) {
649         if(data1[t]&data2[t]) {
650             return 1;
651         }
652     }
653     return 0;
654 }
655
656 GBool BitmapOutputDev::intersection(int x1, int y1, int x2, int y2)
657 {
658     SplashBitmap*boolpoly = boolpolybitmap;
659     SplashBitmap*booltext = booltextbitmap;
660             
661     if(boolpoly->getMode()==splashModeMono1) {
662         /* alternative implementation, using one bit per pixel-
663            needs the no-dither patch in xpdf */
664         
665         int width = boolpoly->getWidth();
666         int height = boolpoly->getHeight();
667
668         if(!fixBBox(&x1,&y1,&x2,&y2, width, height)) {
669             return gFalse;
670         }
671
672         Guchar*polypixels = boolpoly->getDataPtr();
673         Guchar*textpixels = booltext->getDataPtr();
674
675         int width8 = (width+7)/8;
676         int runx = width8;
677         int runy = height;
678         
679         if(x1|y1|x2|y2) {
680             polypixels+=y1*width8+x1/8;
681             textpixels+=y1*width8+x1/8;
682             runx=(x2+7)/8 - x1/8;
683             runy=y2-y1;
684         }
685         
686         int t;
687         unsigned char c=0;
688         
689         /*assert(sizeof(unsigned long long int)==8);
690         if(((ptroff_t)polypixels&7) || ((ptroff_t)textpixels&7)) {
691             //msg("<warning> Non-optimal alignment");
692         }*/
693
694         int x,y;
695         unsigned char*data1 = (unsigned char*)polypixels;
696         unsigned char*data2 = (unsigned char*)textpixels;
697         msg("<verbose> Testing area (%d,%d,%d,%d), runx=%d,runy=%d", x1,y1,x2,y2, runx, runy);
698         for(y=0;y<runy;y++) {
699             compare8(data1,data2,runx);
700             /*for(x=0;x<runx;x++) {
701                 if(data1[x]&data2[x])
702                     return gTrue;
703             }*/
704             data1+=width8;
705             data2+=width8;
706         }
707         return gFalse;
708     } else {
709         int width = boolpoly->getAlphaRowSize();
710         int height = boolpoly->getHeight();
711
712         if(!fixBBox(&x1, &y1, &x2, &y2, width, height)) {
713             x1=y1=0;x2=y2=1;
714         }
715         Guchar*polypixels = boolpoly->getAlphaPtr();
716         Guchar*textpixels = booltext->getAlphaPtr();
717
718         int x,y;
719         char overlap1 = 0;
720         char overlap2 = 0;
721         for(x=x1;x<x2;x++) {
722             for(y=y1;y<y2;y++) {
723                 if(polypixels[width*y+x]&&textpixels[width*y+x])
724                     overlap1 = 1;
725             }
726         }
727         int ax1=0,ay1=0,ax2=0,ay2=0;
728         for(y=0;y<height;y++) {
729             for(x=0;x<width;x++) {
730                 if(polypixels[width*y+x]&&textpixels[width*y+x]) {
731                     overlap2 = 1;
732                     if(!(ax1|ay1|ax2|ay2)) {
733                         ax1 = ax2 = x;
734                         ay1 = ay2 = y;
735                     } else {
736                         ax1 = ax1<x?ax1:x;
737                         ay1 = ay1<y?ay1:y;
738                         ax2 = ax2>x?ax2:x;
739                         ay2 = ay2>y?ay2:y;
740                     }
741                 }
742             }
743         }
744         if(overlap1 && !overlap2)
745             msg("<warning> strange internal error");
746         if(!overlap1 && overlap2) {
747             msg("<warning> Bad bounding box: intersection outside bbox");
748             msg("<warning> given bbox: %d %d %d %d", x1, y1, x2, y2);
749             msg("<warning> changed area: %d %d %d %d", ax1, ay1, ax2, ay2);
750             //writeAlpha(booltextbitmap, "alpha.png");
751         }
752         return overlap2;
753     }
754 }
755
756
757 void BitmapOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2)
758 {
759     double x1,y1,x2,y2;
760     state->transform(crop_x1,crop_y1,&x1,&y1);
761     state->transform(crop_x2,crop_y2,&x2,&y2);
762     if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
763     if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
764     
765     this->movex = -(int)x1 - user_movex;
766     this->movey = -(int)y1 - user_movey;
767     
768     if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
769         x1 = user_clipx1;
770         x2 = user_clipx2;
771         y1 = user_clipy1;
772         y2 = user_clipy2;
773     }
774     this->width = (int)(x2-x1);
775     this->height = (int)(y2-y1);
776
777     msg("<debug> startPage");
778     rgbdev->startPage(pageNum, state, crop_x1, crop_y1, crop_x2, crop_y2);
779     boolpolydev->startPage(pageNum, state, crop_x1, crop_y1, crop_x2, crop_y2);
780     booltextdev->startPage(pageNum, state, crop_x1, crop_y1, crop_x2, crop_y2);
781     clip0dev->startPage(pageNum, state, crop_x1, crop_y1, crop_x2, crop_y2);
782     clip1dev->startPage(pageNum, state, crop_x1, crop_y1, crop_x2, crop_y2);
783     gfxdev->startPage(pageNum, state, crop_x1, crop_y1, crop_x2, crop_y2);
784
785     boolpolybitmap = boolpolydev->getBitmap();
786     booltextbitmap = booltextdev->getBitmap();
787     clip0bitmap = clip0dev->getBitmap();
788     clip1bitmap = clip1dev->getBitmap();
789     rgbbitmap = rgbdev->getBitmap();
790     
791     flushText(); // write out the initial clipping rectangle
792
793     /* just in case any device did draw a white background rectangle 
794        into the device */
795     clearBoolTextDev(UNKNOWN_BOUNDING_BOX);
796     clearBoolPolyDev(UNKNOWN_BOUNDING_BOX);
797
798     this->layerstate = STATE_PARALLEL;
799     this->emptypage = 1;
800     msg("<debug> startPage done");
801 }
802
803 void BitmapOutputDev::endPage()
804 {
805     msg("<verbose> endPage (BitmapOutputDev)");
806
807     /* notice: we're not fully done yet with this page- there might still be 
808        a few calls to drawLink() yet to come */
809 }
810 void BitmapOutputDev::finishPage()
811 {
812     msg("<verbose> finishPage (BitmapOutputDev)");
813     gfxdev->endPage();
814    
815     if(layerstate == STATE_BITMAP_IS_ABOVE) {
816         this->flushText();
817         this->flushBitmap();
818     } else {
819         this->flushBitmap();
820         this->flushText();
821     }
822
823     /* splash will now destroy alpha, and paint the 
824        background color into the "holes" in the bitmap */
825     boolpolydev->endPage();
826     booltextdev->endPage();
827     rgbdev->endPage();
828     clip0dev->endPage();
829     clip1dev->endPage();
830 }
831
832 GBool BitmapOutputDev::upsideDown()
833 {
834     boolpolydev->upsideDown();
835     booltextdev->upsideDown();
836     clip0dev->upsideDown();
837     clip1dev->upsideDown();
838     return rgbdev->upsideDown();
839 }
840
841 GBool BitmapOutputDev::useDrawChar()
842 {
843     boolpolydev->useDrawChar();
844     booltextdev->useDrawChar();
845     clip0dev->useDrawChar();
846     clip1dev->useDrawChar();
847     return rgbdev->useDrawChar();
848 }
849
850 GBool BitmapOutputDev::useTilingPatternFill()
851 {
852     boolpolydev->useTilingPatternFill();
853     booltextdev->useTilingPatternFill();
854     clip0dev->useTilingPatternFill();
855     clip1dev->useTilingPatternFill();
856     return rgbdev->useTilingPatternFill();
857 }
858
859 GBool BitmapOutputDev::useShadedFills()
860 {
861     boolpolydev->useShadedFills();
862     booltextdev->useShadedFills();
863     clip0dev->useShadedFills();
864     clip1dev->useShadedFills();
865     return rgbdev->useShadedFills();
866 }
867
868 GBool BitmapOutputDev::useDrawForm()
869 {
870     boolpolydev->useDrawForm();
871     booltextdev->useDrawForm();
872     clip0dev->useDrawForm();
873     clip1dev->useDrawForm();
874     return rgbdev->useDrawForm();
875 }
876
877 GBool BitmapOutputDev::interpretType3Chars()
878 {
879     boolpolydev->interpretType3Chars();
880     booltextdev->interpretType3Chars();
881     clip0dev->interpretType3Chars();
882     clip1dev->interpretType3Chars();
883     return rgbdev->interpretType3Chars();
884 }
885
886 GBool BitmapOutputDev::needNonText() 
887 {
888     boolpolydev->needNonText();
889     booltextdev->needNonText();
890     clip0dev->needNonText();
891     clip1dev->needNonText();
892     return rgbdev->needNonText();
893 }
894 /*GBool BitmapOutputDev::checkPageSlice(Page *page, double hDPI, double vDPI,
895                            int rotate, GBool useMediaBox, GBool crop,
896                            int sliceX, int sliceY, int sliceW, int sliceH,
897                            GBool printing, Catalog *catalog,
898                            GBool (*abortCheckCbk)(void *data),
899                            void *abortCheckCbkData)
900 {
901     return gTrue;
902 }*/
903 void BitmapOutputDev::setDefaultCTM(double *ctm) 
904 {
905     boolpolydev->setDefaultCTM(ctm);
906     booltextdev->setDefaultCTM(ctm);
907     rgbdev->setDefaultCTM(ctm);
908     clip0dev->setDefaultCTM(ctm);
909     clip1dev->setDefaultCTM(ctm);
910     gfxdev->setDefaultCTM(ctm);
911 }
912 void BitmapOutputDev::saveState(GfxState *state) 
913 {
914     boolpolydev->saveState(state);
915     booltextdev->saveState(state);
916     rgbdev->saveState(state);
917     clip0dev->saveState(state);
918     clip1dev->saveState(state);
919
920     /*ClipState*cstate = new ClipState();
921     cstate->next = this->clipstates;
922     this->clipstates = cstate;*/
923 }
924 void BitmapOutputDev::restoreState(GfxState *state) 
925 {
926     boolpolydev->restoreState(state);
927     booltextdev->restoreState(state);
928     rgbdev->restoreState(state);
929     clip0dev->restoreState(state);
930     clip1dev->restoreState(state);
931
932     /*if(this->clipstates) {
933         ClipState*old = this->clipstates;
934         if(old->written) {
935             gfxdev->restoreState(state);
936         }
937         this->clipstates = this->clipstates->next;
938         delete(old);
939     } else {
940         msg("<error> invalid restoreState()");
941     }*/
942 }
943 void BitmapOutputDev::updateAll(GfxState *state)
944 {
945     boolpolydev->updateAll(state);
946     booltextdev->updateAll(state);
947     rgbdev->updateAll(state);
948     clip0dev->updateAll(state);
949     clip1dev->updateAll(state);
950     gfxdev->updateAll(state);
951 }
952 void BitmapOutputDev::updateCTM(GfxState *state, double m11, double m12, double m21, double m22, double m31, double m32)
953 {
954     boolpolydev->updateCTM(state,m11,m12,m21,m22,m31,m32);
955     booltextdev->updateCTM(state,m11,m12,m21,m22,m31,m32);
956     rgbdev->updateCTM(state,m11,m12,m21,m22,m31,m32);
957     clip0dev->updateCTM(state,m11,m12,m21,m22,m31,m32);
958     clip1dev->updateCTM(state,m11,m12,m21,m22,m31,m32);
959     gfxdev->updateCTM(state,m11,m12,m21,m22,m31,m32);
960 }
961 void BitmapOutputDev::updateLineDash(GfxState *state)
962 {
963     boolpolydev->updateLineDash(state);
964     booltextdev->updateLineDash(state);
965     rgbdev->updateLineDash(state);
966     clip0dev->updateLineDash(state);
967     clip1dev->updateLineDash(state);
968     gfxdev->updateLineDash(state);
969 }
970 void BitmapOutputDev::updateFlatness(GfxState *state)
971 {
972     boolpolydev->updateFlatness(state);
973     booltextdev->updateFlatness(state);
974     rgbdev->updateFlatness(state);
975     clip0dev->updateFlatness(state);
976     clip1dev->updateFlatness(state);
977     gfxdev->updateFlatness(state);
978 }
979 void BitmapOutputDev::updateLineJoin(GfxState *state)
980 {
981     boolpolydev->updateLineJoin(state);
982     booltextdev->updateLineJoin(state);
983     rgbdev->updateLineJoin(state);
984     clip0dev->updateLineJoin(state);
985     clip1dev->updateLineJoin(state);
986     gfxdev->updateLineJoin(state);
987 }
988 void BitmapOutputDev::updateLineCap(GfxState *state)
989 {
990     boolpolydev->updateLineCap(state);
991     booltextdev->updateLineCap(state);
992     rgbdev->updateLineCap(state);
993     clip0dev->updateLineCap(state);
994     clip1dev->updateLineCap(state);
995     gfxdev->updateLineCap(state);
996 }
997 void BitmapOutputDev::updateMiterLimit(GfxState *state)
998 {
999     boolpolydev->updateMiterLimit(state);
1000     booltextdev->updateMiterLimit(state);
1001     rgbdev->updateMiterLimit(state);
1002     clip0dev->updateMiterLimit(state);
1003     clip1dev->updateMiterLimit(state);
1004     gfxdev->updateMiterLimit(state);
1005 }
1006 void BitmapOutputDev::updateLineWidth(GfxState *state)
1007 {
1008     boolpolydev->updateLineWidth(state);
1009     booltextdev->updateLineWidth(state);
1010     rgbdev->updateLineWidth(state);
1011     clip0dev->updateLineWidth(state);
1012     clip1dev->updateLineWidth(state);
1013     gfxdev->updateLineWidth(state);
1014 }
1015 void BitmapOutputDev::updateStrokeAdjust(GfxState *state)
1016 {
1017     boolpolydev->updateStrokeAdjust(state);
1018     booltextdev->updateStrokeAdjust(state);
1019     rgbdev->updateStrokeAdjust(state);
1020     clip0dev->updateStrokeAdjust(state);
1021     clip1dev->updateStrokeAdjust(state);
1022     gfxdev->updateStrokeAdjust(state);
1023 }
1024 void BitmapOutputDev::updateFillColorSpace(GfxState *state)
1025 {
1026     boolpolydev->updateFillColorSpace(state);
1027     booltextdev->updateFillColorSpace(state);
1028     rgbdev->updateFillColorSpace(state);
1029     clip0dev->updateFillColorSpace(state);
1030     clip1dev->updateFillColorSpace(state);
1031     gfxdev->updateFillColorSpace(state);
1032 }
1033 void BitmapOutputDev::updateStrokeColorSpace(GfxState *state)
1034 {
1035     boolpolydev->updateStrokeColorSpace(state);
1036     booltextdev->updateStrokeColorSpace(state);
1037     rgbdev->updateStrokeColorSpace(state);
1038     clip0dev->updateStrokeColorSpace(state);
1039     clip1dev->updateStrokeColorSpace(state);
1040     gfxdev->updateStrokeColorSpace(state);
1041 }
1042 void BitmapOutputDev::updateFillColor(GfxState *state)
1043 {
1044     boolpolydev->updateFillColor(state);
1045     booltextdev->updateFillColor(state);
1046     rgbdev->updateFillColor(state);
1047     clip0dev->updateFillColor(state);
1048     clip1dev->updateFillColor(state);
1049     gfxdev->updateFillColor(state);
1050 }
1051 void BitmapOutputDev::updateStrokeColor(GfxState *state)
1052 {
1053     boolpolydev->updateStrokeColor(state);
1054     booltextdev->updateStrokeColor(state);
1055     rgbdev->updateStrokeColor(state);
1056     clip0dev->updateStrokeColor(state);
1057     clip1dev->updateStrokeColor(state);
1058     gfxdev->updateStrokeColor(state);
1059 }
1060 void BitmapOutputDev::updateBlendMode(GfxState *state)
1061 {
1062     boolpolydev->updateBlendMode(state);
1063     booltextdev->updateBlendMode(state);
1064     rgbdev->updateBlendMode(state);
1065     clip0dev->updateBlendMode(state);
1066     clip1dev->updateBlendMode(state);
1067     gfxdev->updateBlendMode(state);
1068 }
1069 void BitmapOutputDev::updateFillOpacity(GfxState *state)
1070 {
1071     boolpolydev->updateFillOpacity(state);
1072     booltextdev->updateFillOpacity(state);
1073     rgbdev->updateFillOpacity(state);
1074     clip0dev->updateFillOpacity(state);
1075     clip1dev->updateFillOpacity(state);
1076     gfxdev->updateFillOpacity(state);
1077 }
1078 void BitmapOutputDev::updateStrokeOpacity(GfxState *state)
1079 {
1080     boolpolydev->updateStrokeOpacity(state);
1081     booltextdev->updateStrokeOpacity(state);
1082     rgbdev->updateStrokeOpacity(state);
1083     clip0dev->updateStrokeOpacity(state);
1084     clip1dev->updateStrokeOpacity(state);
1085     gfxdev->updateStrokeOpacity(state);
1086 }
1087 void BitmapOutputDev::updateFillOverprint(GfxState *state)
1088 {
1089     boolpolydev->updateFillOverprint(state);
1090     booltextdev->updateFillOverprint(state);
1091     rgbdev->updateFillOverprint(state);
1092     clip0dev->updateFillOverprint(state);
1093     clip1dev->updateFillOverprint(state);
1094     gfxdev->updateFillOverprint(state);
1095 }
1096 void BitmapOutputDev::updateStrokeOverprint(GfxState *state)
1097 {
1098     boolpolydev->updateStrokeOverprint(state);
1099     booltextdev->updateStrokeOverprint(state);
1100     rgbdev->updateStrokeOverprint(state);
1101     clip0dev->updateStrokeOverprint(state);
1102     clip1dev->updateStrokeOverprint(state);
1103     gfxdev->updateStrokeOverprint(state);
1104 }
1105 void BitmapOutputDev::updateTransfer(GfxState *state)
1106 {
1107     boolpolydev->updateTransfer(state);
1108     booltextdev->updateTransfer(state);
1109     rgbdev->updateTransfer(state);
1110     clip0dev->updateTransfer(state);
1111     clip1dev->updateTransfer(state);
1112     gfxdev->updateTransfer(state);
1113 }
1114
1115 void BitmapOutputDev::updateFont(GfxState *state)
1116 {
1117     boolpolydev->updateFont(state);
1118     booltextdev->updateFont(state);
1119     rgbdev->updateFont(state);
1120     clip0dev->updateFont(state);
1121     clip1dev->updateFont(state);
1122     gfxdev->updateFont(state);
1123 }
1124 void BitmapOutputDev::updateTextMat(GfxState *state)
1125 {
1126     boolpolydev->updateTextMat(state);
1127     booltextdev->updateTextMat(state);
1128     rgbdev->updateTextMat(state);
1129     clip0dev->updateTextMat(state);
1130     clip1dev->updateTextMat(state);
1131     gfxdev->updateTextMat(state);
1132 }
1133 void BitmapOutputDev::updateCharSpace(GfxState *state)
1134 {
1135     boolpolydev->updateCharSpace(state);
1136     booltextdev->updateCharSpace(state);
1137     rgbdev->updateCharSpace(state);
1138     clip0dev->updateCharSpace(state);
1139     clip1dev->updateCharSpace(state);
1140     gfxdev->updateCharSpace(state);
1141 }
1142 void BitmapOutputDev::updateRender(GfxState *state)
1143 {
1144     boolpolydev->updateRender(state);
1145     booltextdev->updateRender(state);
1146     rgbdev->updateRender(state);
1147     clip0dev->updateRender(state);
1148     clip1dev->updateRender(state);
1149     gfxdev->updateRender(state);
1150 }
1151 void BitmapOutputDev::updateRise(GfxState *state)
1152 {
1153     boolpolydev->updateRise(state);
1154     booltextdev->updateRise(state);
1155     rgbdev->updateRise(state);
1156     clip0dev->updateRise(state);
1157     clip1dev->updateRise(state);
1158     gfxdev->updateRise(state);
1159 }
1160 void BitmapOutputDev::updateWordSpace(GfxState *state)
1161 {
1162     boolpolydev->updateWordSpace(state);
1163     booltextdev->updateWordSpace(state);
1164     rgbdev->updateWordSpace(state);
1165     clip0dev->updateWordSpace(state);
1166     clip1dev->updateWordSpace(state);
1167     gfxdev->updateWordSpace(state);
1168 }
1169 void BitmapOutputDev::updateHorizScaling(GfxState *state)
1170 {
1171     boolpolydev->updateHorizScaling(state);
1172     booltextdev->updateHorizScaling(state);
1173     rgbdev->updateHorizScaling(state);
1174     clip0dev->updateHorizScaling(state);
1175     clip1dev->updateHorizScaling(state);
1176     gfxdev->updateHorizScaling(state);
1177 }
1178 void BitmapOutputDev::updateTextPos(GfxState *state)
1179 {
1180     boolpolydev->updateTextPos(state);
1181     booltextdev->updateTextPos(state);
1182     rgbdev->updateTextPos(state);
1183     clip0dev->updateTextPos(state);
1184     clip1dev->updateTextPos(state);
1185     gfxdev->updateTextPos(state);
1186 }
1187 void BitmapOutputDev::updateTextShift(GfxState *state, double shift)
1188 {
1189     boolpolydev->updateTextShift(state, shift);
1190     booltextdev->updateTextShift(state, shift);
1191     rgbdev->updateTextShift(state, shift);
1192     clip0dev->updateTextShift(state, shift);
1193     clip1dev->updateTextShift(state, shift);
1194     gfxdev->updateTextShift(state, shift);
1195 }
1196
1197 double max(double x, double y) 
1198 {
1199     return x>y?x:y;
1200 }
1201 double min(double x, double y) 
1202 {
1203     return x<y?x:y;
1204 }
1205
1206 gfxbbox_t BitmapOutputDev::getBBox(GfxState*state)
1207 {
1208     GfxPath * path = state->getPath();
1209     int num = path->getNumSubpaths();
1210     gfxbbox_t bbox = {0,0,1,1};
1211     char valid=0;
1212     int t;
1213     for(t = 0; t < num; t++) {
1214         GfxSubpath *subpath = path->getSubpath(t);
1215         int subnum = subpath->getNumPoints();
1216         int s;
1217         for(s=0;s<subnum;s++) {
1218            double x,y;
1219            state->transform(subpath->getX(s),subpath->getY(s),&x,&y);
1220            if(!valid) {
1221                bbox.xmin = x; bbox.ymin = y;
1222                bbox.xmax = x; bbox.ymax = y;
1223                valid = 1;
1224            } else {
1225                bbox.xmin = min(bbox.xmin, x);
1226                bbox.ymin = min(bbox.ymin, y);
1227                bbox.xmax = max(bbox.xmax, x);
1228                bbox.ymax = max(bbox.ymax, y);
1229            }
1230         }
1231     }
1232     return bbox;
1233 }
1234
1235 void BitmapOutputDev::stroke(GfxState *state)
1236 {
1237     msg("<debug> stroke");
1238     boolpolydev->stroke(state);
1239     gfxbbox_t bbox = getBBox(state);
1240     double width = state->getTransformedLineWidth();
1241     bbox.xmin -= width; bbox.ymin -= width;
1242     bbox.xmax += width; bbox.ymax += width;
1243     checkNewBitmap(bbox.xmin, bbox.ymin, ceil(bbox.xmax), ceil(bbox.ymax));
1244     rgbdev->stroke(state);
1245 }
1246 void BitmapOutputDev::fill(GfxState *state)
1247 {
1248     msg("<debug> fill");
1249     boolpolydev->fill(state);
1250     gfxbbox_t bbox = getBBox(state);
1251     if(checkNewBitmap(bbox.xmin, bbox.ymin, ceil(bbox.xmax), ceil(bbox.ymax))) {
1252         boolpolydev->fill(state);
1253     }
1254     rgbdev->fill(state);
1255 }
1256 void BitmapOutputDev::eoFill(GfxState *state)
1257 {
1258     msg("<debug> eoFill");
1259     boolpolydev->eoFill(state);
1260     gfxbbox_t bbox = getBBox(state);
1261     if(checkNewBitmap(bbox.xmin, bbox.ymin, ceil(bbox.xmax), ceil(bbox.ymax))) {
1262         boolpolydev->eoFill(state);
1263     }
1264     rgbdev->eoFill(state);
1265 }
1266 #if (xpdfMajorVersion*10000 + xpdfMinorVersion*100 + xpdfUpdateVersion) < 30207
1267 void BitmapOutputDev::tilingPatternFill(GfxState *state, Object *str,
1268                                int paintType, Dict *resDict,
1269                                double *mat, double *bbox,
1270                                int x0, int y0, int x1, int y1,
1271                                double xStep, double yStep)
1272 {
1273     msg("<debug> tilingPatternFill");
1274     boolpolydev->tilingPatternFill(state, str, paintType, resDict, mat, bbox, x0, y0, x1, y1, xStep, yStep);
1275     checkNewBitmap(UNKNOWN_BOUNDING_BOX);
1276     rgbdev->tilingPatternFill(state, str, paintType, resDict, mat, bbox, x0, y0, x1, y1, xStep, yStep);
1277 }
1278 #else
1279 void BitmapOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, Object *str,
1280                                int paintType, Dict *resDict,
1281                                double *mat, double *bbox,
1282                                int x0, int y0, int x1, int y1,
1283                                double xStep, double yStep) 
1284 {
1285     msg("<debug> tilingPatternFill");
1286     boolpolydev->tilingPatternFill(state, gfx, str, paintType, resDict, mat, bbox, x0, y0, x1, y1, xStep, yStep);
1287     checkNewBitmap(UNKNOWN_BOUNDING_BOX);
1288     rgbdev->tilingPatternFill(state, gfx, str, paintType, resDict, mat, bbox, x0, y0, x1, y1, xStep, yStep);
1289 }
1290 #endif
1291
1292 GBool BitmapOutputDev::functionShadedFill(GfxState *state, GfxFunctionShading *shading) 
1293 {
1294     msg("<debug> functionShadedFill");
1295     boolpolydev->functionShadedFill(state, shading);
1296     checkNewBitmap(UNKNOWN_BOUNDING_BOX);
1297     return rgbdev->functionShadedFill(state, shading);
1298 }
1299 GBool BitmapOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading)
1300 {
1301     msg("<debug> axialShadedFill");
1302     boolpolydev->axialShadedFill(state, shading);
1303     checkNewBitmap(UNKNOWN_BOUNDING_BOX);
1304     return rgbdev->axialShadedFill(state, shading);
1305 }
1306 GBool BitmapOutputDev::radialShadedFill(GfxState *state, GfxRadialShading *shading)
1307 {
1308     msg("<debug> radialShadedFill");
1309     boolpolydev->radialShadedFill(state, shading);
1310     checkNewBitmap(UNKNOWN_BOUNDING_BOX);
1311     return rgbdev->radialShadedFill(state, shading);
1312 }
1313
1314 SplashColor black = {0,0,0};
1315 SplashColor white = {255,255,255};
1316
1317 void BitmapOutputDev::clip(GfxState *state)
1318 {
1319     msg("<debug> clip");
1320     boolpolydev->clip(state);
1321     booltextdev->clip(state);
1322     rgbdev->clip(state);
1323     clip1dev->clip(state);
1324 }
1325 void BitmapOutputDev::eoClip(GfxState *state)
1326 {
1327     msg("<debug> eoClip");
1328     boolpolydev->eoClip(state);
1329     booltextdev->eoClip(state);
1330     rgbdev->eoClip(state);
1331     clip1dev->eoClip(state);
1332 }
1333 void BitmapOutputDev::clipToStrokePath(GfxState *state)
1334 {
1335     msg("<debug> clipToStrokePath");
1336     boolpolydev->clipToStrokePath(state);
1337     booltextdev->clipToStrokePath(state);
1338     rgbdev->clipToStrokePath(state);
1339     clip1dev->clipToStrokePath(state);
1340 }
1341
1342 void BitmapOutputDev::beginStringOp(GfxState *state)
1343 {
1344     msg("<debug> beginStringOp");
1345     clip0dev->beginStringOp(state);
1346     clip1dev->beginStringOp(state);
1347     booltextdev->beginStringOp(state);
1348     gfxdev->beginStringOp(state);
1349 }
1350 void BitmapOutputDev::beginString(GfxState *state, GString *s)
1351 {
1352     msg("<debug> beginString");
1353     clip0dev->beginString(state, s);
1354     clip1dev->beginString(state, s);
1355     booltextdev->beginString(state, s);
1356     gfxdev->beginString(state, s);
1357 }
1358
1359 void BitmapOutputDev::clearClips()
1360 {
1361     clearBooleanBitmap(clip0bitmap, 0, 0, 0, 0);
1362     clearBooleanBitmap(clip1bitmap, 0, 0, 0, 0);
1363 }
1364 void BitmapOutputDev::clearBoolPolyDev(int x1, int y1, int x2, int y2)
1365 {
1366     clearBooleanBitmap(boolpolybitmap, x1, y1, x2, y2);
1367 }
1368 void BitmapOutputDev::clearBoolTextDev(int x1, int y1, int x2, int y2)
1369 {
1370     clearBooleanBitmap(booltextbitmap, x1, y1, x2, y2);
1371 }
1372 void BitmapOutputDev::drawChar(GfxState *state, double x, double y,
1373                       double dx, double dy,
1374                       double originX, double originY,
1375                       CharCode code, int nBytes, Unicode *u, int uLen)
1376 {
1377     msg("<debug> drawChar render=%d", state->getRender());
1378
1379     if(state->getRender()&RENDER_CLIP) {
1380         //char is just a clipping boundary
1381         rgbdev->drawChar(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen);
1382         boolpolydev->drawChar(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen);
1383         booltextdev->drawChar(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen);
1384         clip1dev->drawChar(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen);
1385     } else if(rgbbitmap != rgbdev->getBitmap()) {
1386         // we're doing softmasking or transparency grouping
1387         boolpolydev->drawChar(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen);
1388         //checkNewBitmap(UNKNOWN_BOUNDING_BOX);
1389         rgbdev->drawChar(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen);
1390     } else {
1391         // we're drawing a regular char
1392         clearClips();
1393         clip0dev->drawChar(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen);
1394         clip1dev->drawChar(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen);
1395    
1396         /* calculate the bbox of this character */
1397         int x1 = (int)x, x2 = (int)x+1, y1 = (int)y, y2 = (int)y+1;
1398         SplashPath*path = clip0dev->getCurrentFont()->getGlyphPath(code);
1399         if(!path) {
1400             if(code)
1401                 msg("<error> couldn't create outline for char %d", code);
1402             return;
1403         }
1404         x-=originX;
1405         y-=originY;
1406         path->offset((SplashCoord)x, (SplashCoord)y);
1407         int t;
1408         for(t=0;t<path->getLength();t++) {
1409             double xx,yy;
1410             Guchar f;
1411             path->getPoint(t,&xx,&yy,&f);
1412             state->transform(xx,yy,&xx,&yy);
1413             if(xx<x1) x1=(int)xx;
1414             if(yy<y1) y1=(int)yy;
1415             if(xx>=x2) x2=(int)xx+1;
1416             if(yy>=y2) y2=(int)yy+1;
1417         }
1418
1419         /* if this character is affected somehow by the various clippings (i.e., it looks
1420            different on a device without clipping), then draw it on the bitmap, not as
1421            text */
1422         if(clip0and1differ(x1,y1,x2,y2)) {
1423             msg("<verbose> Char %d is affected by clipping", code);
1424             boolpolydev->drawChar(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen);
1425             checkNewBitmap(x1,y1,x2,y2);
1426             rgbdev->drawChar(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen);
1427             if(config_extrafontdata) {
1428                 int oldrender = state->getRender();
1429                 state->setRender(3); //invisible
1430                 gfxdev->drawChar(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen);
1431                 state->setRender(oldrender);
1432             }
1433         } else {
1434             /* this char is not at all affected by clipping. 
1435                Now just dump out the bitmap we're currently working on, if necessary. */
1436             booltextdev->drawChar(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen);
1437             checkNewText(x1,y1,x2,y2);
1438             /* use polygonal output device to do the actual text handling */
1439             gfxdev->drawChar(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen);
1440         }
1441     }
1442 }
1443 void BitmapOutputDev::drawString(GfxState *state, GString *s)
1444 {
1445     msg("<error> internal error: drawString not implemented");
1446     return;
1447     clip0dev->drawString(state, s);
1448     clip1dev->drawString(state, s);
1449     booltextdev->drawString(state, s);
1450     gfxdev->drawString(state, s);
1451 }
1452 void BitmapOutputDev::endTextObject(GfxState *state)
1453 {
1454     msg("<debug> endTextObject");
1455     rgbdev->endTextObject(state);
1456     clip0dev->endTextObject(state);
1457     clip1dev->endTextObject(state);
1458     booltextdev->endTextObject(state);
1459     /* the only thing "drawn" here is clipping */
1460     //checkNewText(UNKNOWN_BOUNDING_BOX);
1461     gfxdev->endTextObject(state);
1462 }
1463 void BitmapOutputDev::endString(GfxState *state)
1464 {
1465     msg("<debug> endString");
1466     clip0dev->endString(state);
1467     clip1dev->endString(state);
1468     booltextdev->endString(state);
1469     int render = state->getRender();
1470     if(render != RENDER_INVISIBLE && render != RENDER_FILL) {
1471         checkNewText(UNKNOWN_BOUNDING_BOX);
1472     }
1473     gfxdev->endString(state);
1474 }
1475 void BitmapOutputDev::endStringOp(GfxState *state)
1476 {
1477     msg("<debug> endStringOp");
1478     clip0dev->endStringOp(state);
1479     clip1dev->endStringOp(state);
1480     booltextdev->endStringOp(state);
1481     gfxdev->endStringOp(state);
1482 }
1483
1484 /* TODO: these four operations below *should* do nothing, as type3
1485          chars are drawn using operations like fill() */
1486 GBool BitmapOutputDev::beginType3Char(GfxState *state, double x, double y,
1487                              double dx, double dy,
1488                              CharCode code, Unicode *u, int uLen)
1489 {
1490     msg("<debug> beginType3Char");
1491     /* call gfxdev so that it can generate "invisible" characters
1492        on top of the actual graphic content, for text extraction */
1493     return gfxdev->beginType3Char(state, x, y, dx, dy, code, u, uLen);
1494 }
1495 void BitmapOutputDev::type3D0(GfxState *state, double wx, double wy)
1496 {
1497     msg("<debug> type3D0");
1498     return gfxdev->type3D0(state, wx, wy);
1499 }
1500 void BitmapOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury)
1501 {
1502     msg("<debug> type3D1");
1503     return gfxdev->type3D1(state, wx, wy, llx, lly, urx, ury);
1504 }
1505 void BitmapOutputDev::endType3Char(GfxState *state)
1506 {
1507     msg("<debug> endType3Char");
1508     gfxdev->endType3Char(state);
1509 }
1510
1511 class CopyStream: public Object
1512 {
1513     Dict*dict;
1514     char*buf;
1515     MemStream*memstream;
1516     public:
1517     CopyStream(Stream*str, int len)
1518     {
1519         buf = 0;
1520         str->reset();
1521         if(len) {
1522             buf = (char*)malloc(len);
1523             int t;
1524             for (t=0; t<len; t++)
1525               buf[t] = str->getChar();
1526         }
1527         str->close();
1528         this->dict = str->getDict();
1529         this->memstream = new MemStream(buf, 0, len, this);
1530     }
1531     ~CopyStream() 
1532     {
1533         ::free(this->buf);this->buf = 0;
1534         delete this->memstream;
1535     }
1536     Dict* getDict() {return dict;}
1537     Stream* getStream() {return this->memstream;};
1538 };
1539
1540 gfxbbox_t BitmapOutputDev::getImageBBox(GfxState*state)
1541 {
1542     gfxbbox_t bbox;
1543     double x,y;
1544     state->transform(0, 1, &x, &y);
1545     bbox.xmin=bbox.xmax = x;
1546     bbox.ymin=bbox.ymax = x;
1547     state->transform(0, 0, &x, &y);
1548     bbox.xmin=min(bbox.xmin,x);
1549     bbox.ymin=min(bbox.ymin,y);
1550     bbox.xmax=max(bbox.xmin,x);
1551     bbox.ymax=max(bbox.ymin,y);
1552     state->transform(1, 0, &x, &y);
1553     bbox.xmin=min(bbox.xmin,x);
1554     bbox.ymin=min(bbox.ymin,y);
1555     bbox.xmax=max(bbox.xmin,x);
1556     bbox.ymax=max(bbox.ymin,y);
1557     state->transform(1, 1, &x, &y);
1558     bbox.xmin=min(bbox.xmin,x);
1559     bbox.ymin=min(bbox.ymin,y);
1560     bbox.xmax=max(bbox.xmin,x);
1561     bbox.ymax=max(bbox.ymin,y);
1562     return bbox;
1563 }
1564 void BitmapOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
1565                            int width, int height, GBool invert,
1566                            GBool inlineImg)
1567 {
1568     msg("<debug> drawImageMask streamkind=%d", str->getKind());
1569     CopyStream*cpystr = 0;
1570     if(inlineImg) {
1571         cpystr = new CopyStream(str, height * ((width + 7) / 8));
1572         str = cpystr->getStream();
1573     }
1574     boolpolydev->drawImageMask(state, ref, str, width, height, invert, inlineImg);
1575     gfxbbox_t bbox = getImageBBox(state);
1576     checkNewBitmap(bbox.xmin, bbox.ymin, ceil(bbox.xmax), ceil(bbox.ymax));
1577     rgbdev->drawImageMask(state, ref, str, width, height, invert, inlineImg);
1578     if(cpystr)
1579         delete cpystr;
1580 }
1581 void BitmapOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
1582                        int width, int height, GfxImageColorMap *colorMap,
1583                        int *maskColors, GBool inlineImg)
1584 {
1585     msg("<debug> drawImage streamkind=%d", str->getKind());
1586     CopyStream*cpystr = 0;
1587     if(inlineImg) {
1588         cpystr = new CopyStream(str, height * ((width * colorMap->getNumPixelComps() * colorMap->getBits() + 7) / 8));
1589         str = cpystr->getStream();
1590     }
1591     boolpolydev->drawImage(state, ref, str, width, height, colorMap, maskColors, inlineImg);
1592     gfxbbox_t bbox=getImageBBox(state);
1593     checkNewBitmap(bbox.xmin, bbox.ymin, ceil(bbox.xmax), ceil(bbox.ymax));
1594     rgbdev->drawImage(state, ref, str, width, height, colorMap, maskColors, inlineImg);
1595     if(cpystr)
1596         delete cpystr;
1597 }
1598 void BitmapOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
1599                              int width, int height,
1600                              GfxImageColorMap *colorMap,
1601                              Stream *maskStr, int maskWidth, int maskHeight,
1602                              GBool maskInvert)
1603 {
1604     msg("<debug> drawMaskedImage streamkind=%d", str->getKind());
1605     boolpolydev->drawMaskedImage(state, ref, str, width, height, colorMap, maskStr, maskWidth, maskHeight, maskInvert);
1606     gfxbbox_t bbox=getImageBBox(state);
1607     checkNewBitmap(bbox.xmin, bbox.ymin, ceil(bbox.xmax), ceil(bbox.ymax));
1608     rgbdev->drawMaskedImage(state, ref, str, width, height, colorMap, maskStr, maskWidth, maskHeight, maskInvert);
1609 }
1610 void BitmapOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
1611                                  int width, int height,
1612                                  GfxImageColorMap *colorMap,
1613                                  Stream *maskStr,
1614                                  int maskWidth, int maskHeight,
1615                                  GfxImageColorMap *maskColorMap)
1616 {
1617     msg("<debug> drawSoftMaskedImage %dx%d (%dx%d) streamkind=%d", width, height, maskWidth, maskHeight, str->getKind());
1618     boolpolydev->drawSoftMaskedImage(state, ref, str, width, height, colorMap, maskStr, maskWidth, maskHeight, maskColorMap);
1619     gfxbbox_t bbox=getImageBBox(state);
1620     checkNewBitmap(bbox.xmin, bbox.ymin, ceil(bbox.xmax), ceil(bbox.ymax));
1621     rgbdev->drawSoftMaskedImage(state, ref, str, width, height, colorMap, maskStr, maskWidth, maskHeight, maskColorMap);
1622 }
1623 void BitmapOutputDev::drawForm(Ref id)
1624 {
1625     msg("<debug> drawForm");
1626     boolpolydev->drawForm(id);
1627     checkNewBitmap(UNKNOWN_BOUNDING_BOX);
1628     rgbdev->drawForm(id);
1629 }
1630
1631 void BitmapOutputDev::processLink(Link *link, Catalog *catalog)
1632 {
1633     msg("<debug> processLink");
1634     gfxdev->processLink(link, catalog);
1635 }
1636
1637 void BitmapOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
1638                                     GfxColorSpace *blendingColorSpace,
1639                                     GBool isolated, GBool knockout,
1640                                     GBool forSoftMask)
1641 {
1642     msg("<debug> beginTransparencyGroup");
1643 #if (xpdfMajorVersion*10000 + xpdfMinorVersion*100 + xpdfUpdateVersion) < 30207
1644     GfxState*state1 = state->copy();
1645     GfxState*state2 = state->copy();
1646     state1->setPath(0);
1647     state1->setPath(state->getPath()->copy());
1648     state2->setPath(0);
1649     state2->setPath(state->getPath()->copy());
1650 #else
1651     GfxState*state1 = state->copy(gTrue);
1652     GfxState*state2 = state->copy(gTrue);
1653 #endif
1654     boolpolydev->beginTransparencyGroup(state1, bbox, blendingColorSpace, isolated, knockout, forSoftMask);
1655     rgbdev->beginTransparencyGroup(state2, bbox, blendingColorSpace, isolated, knockout, forSoftMask);
1656     clip1dev->beginTransparencyGroup(state, bbox, blendingColorSpace, isolated, knockout, forSoftMask);
1657     delete state1;
1658     delete state2;
1659 }
1660 void BitmapOutputDev::endTransparencyGroup(GfxState *state)
1661 {
1662     msg("<debug> endTransparencyGroup");
1663 #if (xpdfMajorVersion*10000 + xpdfMinorVersion*100 + xpdfUpdateVersion) < 30207
1664     GfxState*state1 = state->copy();
1665     GfxState*state2 = state->copy();
1666     state1->setPath(0);
1667     state1->setPath(state->getPath()->copy());
1668     state2->setPath(0);
1669     state2->setPath(state->getPath()->copy());
1670 #else
1671     GfxState*state1 = state->copy(gTrue);
1672     GfxState*state2 = state->copy(gTrue);
1673 #endif
1674     boolpolydev->endTransparencyGroup(state1);
1675     checkNewBitmap(UNKNOWN_BOUNDING_BOX);
1676     rgbdev->endTransparencyGroup(state2);
1677     delete state1;
1678     delete state2;
1679     clip1dev->endTransparencyGroup(state);
1680 }
1681 void BitmapOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
1682 {
1683     msg("<debug> paintTransparencyGroup");
1684     boolpolydev->paintTransparencyGroup(state,bbox);
1685     checkNewBitmap(UNKNOWN_BOUNDING_BOX);
1686     rgbdev->paintTransparencyGroup(state,bbox);
1687     clip1dev->paintTransparencyGroup(state,bbox);
1688 }
1689 void BitmapOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *backdropColor)
1690 {
1691     msg("<debug> setSoftMask");
1692     boolpolydev->setSoftMask(state, bbox, alpha, transferFunc, backdropColor);
1693     checkNewBitmap(UNKNOWN_BOUNDING_BOX);
1694     rgbdev->setSoftMask(state, bbox, alpha, transferFunc, backdropColor);
1695     clip1dev->setSoftMask(state, bbox, alpha, transferFunc, backdropColor);
1696 }
1697 void BitmapOutputDev::clearSoftMask(GfxState *state)
1698 {
1699     msg("<debug> clearSoftMask");
1700     boolpolydev->clearSoftMask(state);
1701     checkNewBitmap(UNKNOWN_BOUNDING_BOX);
1702     rgbdev->clearSoftMask(state);
1703     clip1dev->clearSoftMask(state);
1704 }