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