fixed 64 bit compile problem
[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 = splashModeMono8;
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     for(y=0;y<height;y++) {
306         gfxcolor_t*line = &data[y*width];
307         for(x=0;x<width;x++) {
308             Guchar c[4];
309             bitmap->getPixel(x,y,c);
310             int a = bitmap->getAlpha(x,y);
311             line[x].r = c[0];
312             line[x].g = c[1];
313             line[x].b = c[2];
314             line[x].a = a;
315         }
316     }
317     writePNG(filename, (unsigned char*)data, width, height);
318     free(data);
319 }
320
321 void writeAlpha(SplashBitmap*bitmap, char*filename)
322 {
323     int y,x;
324     
325     int width = bitmap->getWidth();
326     int height = bitmap->getHeight();
327
328     gfxcolor_t*data = (gfxcolor_t*)malloc(sizeof(gfxcolor_t)*width*height);
329
330     for(y=0;y<height;y++) {
331         gfxcolor_t*line = &data[y*width];
332         for(x=0;x<width;x++) {
333             int a = bitmap->getAlpha(x,y);
334             line[x].r = a;
335             line[x].g = a;
336             line[x].b = a;
337             line[x].a = 255;
338         }
339     }
340     writePNG(filename, (unsigned char*)data, width, height);
341     free(data);
342 }
343 static int dbg_btm_counter=1;
344
345 static const char*STATE_NAME[] = {"parallel", "textabovebitmap", "bitmapabovetext"};
346
347 int checkAlphaSanity(SplashBitmap*boolbtm, SplashBitmap*alphabtm)
348 {
349     assert(boolbtm->getWidth() == alphabtm->getWidth());
350     assert(boolbtm->getHeight() == alphabtm->getHeight());
351     if(boolbtm->getMode()==splashModeMono1) {
352         return 1;
353     }
354
355     int width = boolbtm->getWidth();
356     int height = boolbtm->getHeight();
357
358     int bad=0;
359     int x,y;
360     for(y=0;y<height;y++) {
361         for(x=0;x<width;x++) {
362             int a1 = alphabtm->getAlpha(x,y);
363             int a2 = boolbtm->getAlpha(x,y);
364             if(a1!=a2) {
365                 bad++;
366             }
367         }
368     }
369     double badness = bad/(double)(width*height);
370     if(badness>0.2) {
371         msg("<error> Bitmaps don't correspond: %d out of %d pixels wrong (%.2f%%)", bad, width*height, 
372                 badness*100.0);
373         return 0;
374     }
375     msg("<notice> %f", badness);
376     return 1;
377 }
378
379 GBool BitmapOutputDev::checkNewText(int x1, int y1, int x2, int y2)
380 {
381     /* called once some new text was drawn on booltextdev, and
382        before the same thing is drawn on gfxdev */
383    
384     msg("<trace> Testing new text data against current bitmap data, state=%s, counter=%d\n", STATE_NAME[layerstate], dbg_btm_counter);
385     
386     char filename1[80];
387     char filename2[80];
388     char filename3[80];
389     sprintf(filename1, "state%dboolbitmap_afternewtext.png", dbg_btm_counter);
390     sprintf(filename2, "state%dbooltext_afternewtext.png", dbg_btm_counter);
391     sprintf(filename3, "state%dbitmap_afternewtext.png", dbg_btm_counter);
392     if(0) {
393         msg("<verbose> %s %s %s", filename1, filename2, filename3);
394         writeAlpha(boolpolybitmap, filename1);
395         writeAlpha(booltextbitmap, filename2);
396         writeBitmap(rgbdev->getBitmap(), filename3);
397     }
398     dbg_btm_counter++;
399
400     GBool ret = false;
401     if(intersection(x1,y1,x2,y2)) {
402         if(layerstate==STATE_PARALLEL) {
403             /* the new text is above the bitmap. So record that fact,
404                and also clear the bitmap buffer, so we can check for
405                new intersections */
406             msg("<verbose> Text is above current bitmap/polygon data");
407             layerstate=STATE_TEXT_IS_ABOVE;
408             clearBoolPolyDev(x1,y1,x2,y2);
409         } else if(layerstate==STATE_BITMAP_IS_ABOVE) {
410             /* there's a bitmap above the (old) text. So we need
411                to flush out that text, and record that the *new*
412                text is now *above* the bitmap
413              */
414             msg("<verbose> Text is above current bitmap/polygon data (which is above some other text)");
415             flushText();
416             layerstate=STATE_TEXT_IS_ABOVE;
417             /* clear both bool devices- the text device because
418                we just dumped out all the (old) text, and the
419                poly dev so we can check for new intersections */
420             clearBoolPolyDev(x1,y1,x2,y2);
421             /* FIXME this destroys the text pixels we just
422                drew to test for the intersection- however we need
423                those to check for the *new* intersections */
424             clearBoolTextDev(x1,y1,x2,y2);
425             ret = true;
426         } else {
427             /* we already know that the current text section is
428                above the current bitmap section- now just new
429                bitmap data *and* new text data was drawn, and
430                *again* it's above the current bitmap- so clear
431                the polygon bitmap again, so we can check for
432                new intersections */
433             msg("<verbose> Text is still above current bitmap/polygon data");
434             clearBoolPolyDev(x1,y1,x2,y2);
435         }
436     } 
437     return ret;
438 }
439
440 GBool BitmapOutputDev::checkNewBitmap(int x1, int y1, int x2, int y2)
441 {
442     /* similar to checkNewText() above, only in reverse */
443     msg("<trace> Testing new graphics data against current text data, state=%s, counter=%d\n", STATE_NAME[layerstate], dbg_btm_counter);
444
445     char filename1[80];
446     char filename2[80];
447     char filename3[80];
448     sprintf(filename1, "state%dboolbitmap_afternewgfx.png", dbg_btm_counter);
449     sprintf(filename2, "state%dbooltext_afternewgfx.png", dbg_btm_counter);
450     sprintf(filename3, "state%dbitmap_afternewgfx.png", dbg_btm_counter);
451
452     if(0) {
453         msg("<verbose> %s %s %s", filename1, filename2, filename3);
454         writeAlpha(boolpolybitmap, filename1);
455         writeAlpha(booltextbitmap, filename2);
456         writeBitmap(rgbdev->getBitmap(), filename3);
457     }
458     dbg_btm_counter++;
459
460     GBool ret = false;
461     if(intersection(x1,y1,x2,y2)) {
462         if(layerstate==STATE_PARALLEL) {
463             msg("<verbose> Bitmap is above current text data");
464             layerstate=STATE_BITMAP_IS_ABOVE;
465             clearBoolTextDev(x1,y1,x2,y2);
466         } else if(layerstate==STATE_TEXT_IS_ABOVE) {
467             msg("<verbose> Bitmap is above current text data (which is above some bitmap)");
468             flushBitmap();
469             layerstate=STATE_BITMAP_IS_ABOVE;
470             clearBoolTextDev(x1,y1,x2,y2);
471             /* FIXME this destroys the polygon pixels we just
472                drew to test for the intersection- however we need
473                those to check for the *new* intersections */
474             clearBoolPolyDev(x1,y1,x2,y2);
475             ret = true;
476         } else {
477             msg("<verbose> Bitmap is still above current text data");
478             clearBoolTextDev(x1,y1,x2,y2);
479         }
480     } 
481     return ret;
482 }
483
484 //void checkNewText() {
485 //    Guchar*alpha = rgbbitmap->getAlphaPtr();
486 //    Guchar*charpixels = clip1bitmap->getDataPtr();
487 //    int xx,yy;
488 //    for(yy=0;yy<height;yy++) {
489 //        Guchar*aline = &alpha[yy*width];
490 //        Guchar*cline = &charpixels[yy*width8];
491 //        for(xx=0;xx<width;xx++) {
492 //            int bit = xx&7;
493 //            int bytepos = xx>>3;
494 //            /* TODO: is the bit order correct? */
495 //            if(aline[xx] && (cline[bytepos]&(1<<bit))) 
496 //              break;
497 //        }
498 //        if(xx!=width)
499 //            break;
500 //}
501
502 GBool BitmapOutputDev::clip0and1differ(int x1,int y1,int x2,int y2)
503 {
504     if(clip0bitmap->getMode()==splashModeMono1) {
505         if(x2<=x1)
506             return gFalse;
507         if(x2<0)
508             return gFalse;
509         if(x1<0)
510             x1 = 0;
511         if(x1>=clip0bitmap->getWidth())
512             return gFalse;
513         if(x2>clip0bitmap->getWidth())
514             x2=clip0bitmap->getWidth();
515
516         if(y2<=y1)
517             return gFalse;
518         if(y2<0)
519             return gFalse;
520         if(y1<0)
521             y1 = 0;
522         if(y1>=clip0bitmap->getHeight())
523             return gFalse;
524         if(y2>clip0bitmap->getHeight())
525             y2=clip0bitmap->getHeight();
526         
527         SplashBitmap*clip0 = clip0bitmap;
528         SplashBitmap*clip1 = clip1bitmap;
529         int width8 = (clip0bitmap->getWidth()+7)/8;
530         int height = clip0bitmap->getHeight();
531         int x18 = x1/8;
532         int x28 = (x2+7)/8;
533         int y;
534
535         for(y=y1;y<y2;y++) {
536             unsigned char*row1 = &clip0bitmap->getDataPtr()[width8*y+x18];
537             unsigned char*row2 = &clip1bitmap->getDataPtr()[width8*y+x28];
538             if(memcmp(row1, row2, x28-x18))
539                 return gTrue;
540         }
541         return gFalse;
542     } else {
543         SplashBitmap*clip0 = clip0bitmap;
544         SplashBitmap*clip1 = clip1bitmap;
545         int width = clip0->getAlphaRowSize();
546         int height = clip0->getHeight();
547         return memcmp(clip0->getAlphaPtr(), clip1->getAlphaPtr(), width*height);
548     }
549 }
550
551 static void clearBooleanBitmap(SplashBitmap*btm, int x1, int y1, int x2, int y2)
552 {
553     if(!(x1|y1|x2|y2)) {
554         x1 = y1 = 0;
555         x2 = btm->getWidth();
556         y2 = btm->getHeight();
557     }
558     if(btm->getMode()==splashModeMono1) {
559         int width8 = (btm->getWidth()+7)/8;
560         int width = btm->getWidth();
561         int height = btm->getHeight();
562         memset(btm->getDataPtr(), 0, width8*height);
563     } else {
564         int width = btm->getAlphaRowSize();
565         int height = btm->getHeight();
566         memset(btm->getAlphaPtr(), 0, width*height);
567     }
568 }
569
570 long long unsigned int compare64(long long unsigned int*data1, long long unsigned int*data2, int len)
571 {
572     long long unsigned int c;
573     int t;
574     for(t=0;t<len;t++) {
575         c |= data1[t]&data2[t];
576     }
577     return c;
578 }
579
580 GBool BitmapOutputDev::intersection(int x1, int y1, int x2, int y2)
581 {
582     SplashBitmap*boolpoly = boolpolybitmap;
583     SplashBitmap*booltext = booltextbitmap;
584         
585     if(boolpoly->getMode()==splashModeMono1) {
586         /* alternative implementation, using one bit per pixel-
587            would work if xpdf wouldn't try to dither everything */
588
589         Guchar*polypixels = boolpoly->getDataPtr();
590         Guchar*textpixels = booltext->getDataPtr();
591
592         int width8 = (width+7)/8;
593         int height = boolpoly->getHeight();
594         
595         if(x1|y1|x2|y2) {
596             if(y1>=0 && y1<=y2 && y2<=height) {
597                 polypixels+=y1*width8;
598                 textpixels+=y1*width8;
599                 height=y2-y1;
600             }
601         }
602         
603         int t;
604         int len = height*width8;
605         unsigned long long int c=0;
606         assert(sizeof(unsigned long long int)==8);
607         {
608             if(((ptroff_t)polypixels&7) || ((ptroff_t)textpixels&7)) {
609                 //msg("<warning> Non-optimal alignment");
610             }
611             int l2 = len;
612             len /= sizeof(unsigned long long int);
613             c = compare64((unsigned long long int*)polypixels, (unsigned long long int*)textpixels, len);
614             int l1 = len*sizeof(unsigned long long int);
615             for(t=l1;t<l2;t++) {
616                 c |= (unsigned long long int)(polypixels[t]&textpixels[t]);
617             }
618         }
619         if(c)
620             /* if graphic data and the characters overlap, they have common bits */
621             return gTrue;
622         else
623             return gFalse;
624     } else {
625         Guchar*polypixels = boolpoly->getAlphaPtr();
626         Guchar*textpixels = booltext->getAlphaPtr();
627         
628         int width = boolpoly->getAlphaRowSize();
629         int height = boolpoly->getHeight();
630         
631         int t;
632         int len = height*width;
633         unsigned int c=0;
634         if(len & (sizeof(unsigned int)-1)) {
635             Guchar c2=0;
636             for(t=0;t<len;t++) {
637                 if(polypixels[t]&&textpixels[t])
638                     return gTrue;
639             }
640         } else {
641             len /= sizeof(unsigned int);
642             for(t=0;t<len;t++) {
643                 if((((unsigned int*)polypixels)[t]) & (((unsigned int*)textpixels)[t]))
644                     return gTrue;
645             }
646         }
647         return gFalse;
648     }
649 }
650
651
652 void BitmapOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2)
653 {
654     double x1,y1,x2,y2;
655     state->transform(crop_x1,crop_y1,&x1,&y1);
656     state->transform(crop_x2,crop_y2,&x2,&y2);
657     if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
658     if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
659     
660     this->movex = -(int)x1 - user_movex;
661     this->movey = -(int)y1 - user_movey;
662     
663     if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
664         x1 = user_clipx1;
665         x2 = user_clipx2;
666         y1 = user_clipy1;
667         y2 = user_clipy2;
668     }
669     this->width = (int)(x2-x1);
670     this->height = (int)(y2-y1);
671
672     msg("<debug> startPage");
673     rgbdev->startPage(pageNum, state, crop_x1, crop_y1, crop_x2, crop_y2);
674     boolpolydev->startPage(pageNum, state, crop_x1, crop_y1, crop_x2, crop_y2);
675     booltextdev->startPage(pageNum, state, crop_x1, crop_y1, crop_x2, crop_y2);
676     clip0dev->startPage(pageNum, state, crop_x1, crop_y1, crop_x2, crop_y2);
677     clip1dev->startPage(pageNum, state, crop_x1, crop_y1, crop_x2, crop_y2);
678     gfxdev->startPage(pageNum, state, crop_x1, crop_y1, crop_x2, crop_y2);
679
680     boolpolybitmap = boolpolydev->getBitmap();
681     booltextbitmap = booltextdev->getBitmap();
682     clip0bitmap = clip0dev->getBitmap();
683     clip1bitmap = clip1dev->getBitmap();
684     rgbbitmap = rgbdev->getBitmap();
685     
686     flushText(); // write out the initial clipping rectangle
687
688     /* just in case any device did draw a white background rectangle 
689        into the device */
690     clearBoolTextDev(UNKNOWN_BOUNDING_BOX);
691     clearBoolPolyDev(UNKNOWN_BOUNDING_BOX);
692
693     this->layerstate = STATE_PARALLEL;
694     this->emptypage = 1;
695     msg("<debug> startPage done");
696 }
697
698 void BitmapOutputDev::endPage()
699 {
700     msg("<verbose> endPage (BitmapOutputDev)");
701
702     /* notice: we're not fully done yet with this page- there might still be 
703        a few calls to drawLink() yet to come */
704 }
705 void BitmapOutputDev::finishPage()
706 {
707     msg("<verbose> finishPage (BitmapOutputDev)");
708     gfxdev->endPage();
709    
710     if(layerstate == STATE_BITMAP_IS_ABOVE) {
711         this->flushText();
712         this->flushBitmap();
713     } else {
714         this->flushBitmap();
715         this->flushText();
716     }
717
718     /* splash will now destroy alpha, and paint the 
719        background color into the "holes" in the bitmap */
720     boolpolydev->endPage();
721     booltextdev->endPage();
722     rgbdev->endPage();
723     clip0dev->endPage();
724     clip1dev->endPage();
725 }
726
727 GBool BitmapOutputDev::upsideDown()
728 {
729     boolpolydev->upsideDown();
730     booltextdev->upsideDown();
731     clip0dev->upsideDown();
732     clip1dev->upsideDown();
733     return rgbdev->upsideDown();
734 }
735
736 GBool BitmapOutputDev::useDrawChar()
737 {
738     boolpolydev->useDrawChar();
739     booltextdev->useDrawChar();
740     clip0dev->useDrawChar();
741     clip1dev->useDrawChar();
742     return rgbdev->useDrawChar();
743 }
744
745 GBool BitmapOutputDev::useTilingPatternFill()
746 {
747     boolpolydev->useTilingPatternFill();
748     booltextdev->useTilingPatternFill();
749     clip0dev->useTilingPatternFill();
750     clip1dev->useTilingPatternFill();
751     return rgbdev->useTilingPatternFill();
752 }
753
754 GBool BitmapOutputDev::useShadedFills()
755 {
756     boolpolydev->useShadedFills();
757     booltextdev->useShadedFills();
758     clip0dev->useShadedFills();
759     clip1dev->useShadedFills();
760     return rgbdev->useShadedFills();
761 }
762
763 GBool BitmapOutputDev::useDrawForm()
764 {
765     boolpolydev->useDrawForm();
766     booltextdev->useDrawForm();
767     clip0dev->useDrawForm();
768     clip1dev->useDrawForm();
769     return rgbdev->useDrawForm();
770 }
771
772 GBool BitmapOutputDev::interpretType3Chars()
773 {
774     boolpolydev->interpretType3Chars();
775     booltextdev->interpretType3Chars();
776     clip0dev->interpretType3Chars();
777     clip1dev->interpretType3Chars();
778     return rgbdev->interpretType3Chars();
779 }
780
781 GBool BitmapOutputDev::needNonText() 
782 {
783     boolpolydev->needNonText();
784     booltextdev->needNonText();
785     clip0dev->needNonText();
786     clip1dev->needNonText();
787     return rgbdev->needNonText();
788 }
789 /*GBool BitmapOutputDev::checkPageSlice(Page *page, double hDPI, double vDPI,
790                            int rotate, GBool useMediaBox, GBool crop,
791                            int sliceX, int sliceY, int sliceW, int sliceH,
792                            GBool printing, Catalog *catalog,
793                            GBool (*abortCheckCbk)(void *data),
794                            void *abortCheckCbkData)
795 {
796     return gTrue;
797 }*/
798 void BitmapOutputDev::setDefaultCTM(double *ctm) 
799 {
800     boolpolydev->setDefaultCTM(ctm);
801     booltextdev->setDefaultCTM(ctm);
802     rgbdev->setDefaultCTM(ctm);
803     clip0dev->setDefaultCTM(ctm);
804     clip1dev->setDefaultCTM(ctm);
805     gfxdev->setDefaultCTM(ctm);
806 }
807 void BitmapOutputDev::saveState(GfxState *state) 
808 {
809     boolpolydev->saveState(state);
810     booltextdev->saveState(state);
811     rgbdev->saveState(state);
812     clip0dev->saveState(state);
813     clip1dev->saveState(state);
814
815     /*ClipState*cstate = new ClipState();
816     cstate->next = this->clipstates;
817     this->clipstates = cstate;*/
818 }
819 void BitmapOutputDev::restoreState(GfxState *state) 
820 {
821     boolpolydev->restoreState(state);
822     booltextdev->restoreState(state);
823     rgbdev->restoreState(state);
824     clip0dev->restoreState(state);
825     clip1dev->restoreState(state);
826
827     /*if(this->clipstates) {
828         ClipState*old = this->clipstates;
829         if(old->written) {
830             gfxdev->restoreState(state);
831         }
832         this->clipstates = this->clipstates->next;
833         delete(old);
834     } else {
835         msg("<error> invalid restoreState()");
836     }*/
837 }
838 void BitmapOutputDev::updateAll(GfxState *state)
839 {
840     boolpolydev->updateAll(state);
841     booltextdev->updateAll(state);
842     rgbdev->updateAll(state);
843     clip0dev->updateAll(state);
844     clip1dev->updateAll(state);
845     gfxdev->updateAll(state);
846 }
847 void BitmapOutputDev::updateCTM(GfxState *state, double m11, double m12, double m21, double m22, double m31, double m32)
848 {
849     boolpolydev->updateCTM(state,m11,m12,m21,m22,m31,m32);
850     booltextdev->updateCTM(state,m11,m12,m21,m22,m31,m32);
851     rgbdev->updateCTM(state,m11,m12,m21,m22,m31,m32);
852     clip0dev->updateCTM(state,m11,m12,m21,m22,m31,m32);
853     clip1dev->updateCTM(state,m11,m12,m21,m22,m31,m32);
854     gfxdev->updateCTM(state,m11,m12,m21,m22,m31,m32);
855 }
856 void BitmapOutputDev::updateLineDash(GfxState *state)
857 {
858     boolpolydev->updateLineDash(state);
859     booltextdev->updateLineDash(state);
860     rgbdev->updateLineDash(state);
861     clip0dev->updateLineDash(state);
862     clip1dev->updateLineDash(state);
863     gfxdev->updateLineDash(state);
864 }
865 void BitmapOutputDev::updateFlatness(GfxState *state)
866 {
867     boolpolydev->updateFlatness(state);
868     booltextdev->updateFlatness(state);
869     rgbdev->updateFlatness(state);
870     clip0dev->updateFlatness(state);
871     clip1dev->updateFlatness(state);
872     gfxdev->updateFlatness(state);
873 }
874 void BitmapOutputDev::updateLineJoin(GfxState *state)
875 {
876     boolpolydev->updateLineJoin(state);
877     booltextdev->updateLineJoin(state);
878     rgbdev->updateLineJoin(state);
879     clip0dev->updateLineJoin(state);
880     clip1dev->updateLineJoin(state);
881     gfxdev->updateLineJoin(state);
882 }
883 void BitmapOutputDev::updateLineCap(GfxState *state)
884 {
885     boolpolydev->updateLineCap(state);
886     booltextdev->updateLineCap(state);
887     rgbdev->updateLineCap(state);
888     clip0dev->updateLineCap(state);
889     clip1dev->updateLineCap(state);
890     gfxdev->updateLineCap(state);
891 }
892 void BitmapOutputDev::updateMiterLimit(GfxState *state)
893 {
894     boolpolydev->updateMiterLimit(state);
895     booltextdev->updateMiterLimit(state);
896     rgbdev->updateMiterLimit(state);
897     clip0dev->updateMiterLimit(state);
898     clip1dev->updateMiterLimit(state);
899     gfxdev->updateMiterLimit(state);
900 }
901 void BitmapOutputDev::updateLineWidth(GfxState *state)
902 {
903     boolpolydev->updateLineWidth(state);
904     booltextdev->updateLineWidth(state);
905     rgbdev->updateLineWidth(state);
906     clip0dev->updateLineWidth(state);
907     clip1dev->updateLineWidth(state);
908     gfxdev->updateLineWidth(state);
909 }
910 void BitmapOutputDev::updateStrokeAdjust(GfxState *state)
911 {
912     boolpolydev->updateStrokeAdjust(state);
913     booltextdev->updateStrokeAdjust(state);
914     rgbdev->updateStrokeAdjust(state);
915     clip0dev->updateStrokeAdjust(state);
916     clip1dev->updateStrokeAdjust(state);
917     gfxdev->updateStrokeAdjust(state);
918 }
919 void BitmapOutputDev::updateFillColorSpace(GfxState *state)
920 {
921     boolpolydev->updateFillColorSpace(state);
922     booltextdev->updateFillColorSpace(state);
923     rgbdev->updateFillColorSpace(state);
924     clip0dev->updateFillColorSpace(state);
925     clip1dev->updateFillColorSpace(state);
926     gfxdev->updateFillColorSpace(state);
927 }
928 void BitmapOutputDev::updateStrokeColorSpace(GfxState *state)
929 {
930     boolpolydev->updateStrokeColorSpace(state);
931     booltextdev->updateStrokeColorSpace(state);
932     rgbdev->updateStrokeColorSpace(state);
933     clip0dev->updateStrokeColorSpace(state);
934     clip1dev->updateStrokeColorSpace(state);
935     gfxdev->updateStrokeColorSpace(state);
936 }
937 void BitmapOutputDev::updateFillColor(GfxState *state)
938 {
939     boolpolydev->updateFillColor(state);
940     booltextdev->updateFillColor(state);
941     rgbdev->updateFillColor(state);
942     clip0dev->updateFillColor(state);
943     clip1dev->updateFillColor(state);
944     gfxdev->updateFillColor(state);
945 }
946 void BitmapOutputDev::updateStrokeColor(GfxState *state)
947 {
948     boolpolydev->updateStrokeColor(state);
949     booltextdev->updateStrokeColor(state);
950     rgbdev->updateStrokeColor(state);
951     clip0dev->updateStrokeColor(state);
952     clip1dev->updateStrokeColor(state);
953     gfxdev->updateStrokeColor(state);
954 }
955 void BitmapOutputDev::updateBlendMode(GfxState *state)
956 {
957     boolpolydev->updateBlendMode(state);
958     booltextdev->updateBlendMode(state);
959     rgbdev->updateBlendMode(state);
960     clip0dev->updateBlendMode(state);
961     clip1dev->updateBlendMode(state);
962     gfxdev->updateBlendMode(state);
963 }
964 void BitmapOutputDev::updateFillOpacity(GfxState *state)
965 {
966     boolpolydev->updateFillOpacity(state);
967     booltextdev->updateFillOpacity(state);
968     rgbdev->updateFillOpacity(state);
969     clip0dev->updateFillOpacity(state);
970     clip1dev->updateFillOpacity(state);
971     gfxdev->updateFillOpacity(state);
972 }
973 void BitmapOutputDev::updateStrokeOpacity(GfxState *state)
974 {
975     boolpolydev->updateStrokeOpacity(state);
976     booltextdev->updateStrokeOpacity(state);
977     rgbdev->updateStrokeOpacity(state);
978     clip0dev->updateStrokeOpacity(state);
979     clip1dev->updateStrokeOpacity(state);
980     gfxdev->updateStrokeOpacity(state);
981 }
982 void BitmapOutputDev::updateFillOverprint(GfxState *state)
983 {
984     boolpolydev->updateFillOverprint(state);
985     booltextdev->updateFillOverprint(state);
986     rgbdev->updateFillOverprint(state);
987     clip0dev->updateFillOverprint(state);
988     clip1dev->updateFillOverprint(state);
989     gfxdev->updateFillOverprint(state);
990 }
991 void BitmapOutputDev::updateStrokeOverprint(GfxState *state)
992 {
993     boolpolydev->updateStrokeOverprint(state);
994     booltextdev->updateStrokeOverprint(state);
995     rgbdev->updateStrokeOverprint(state);
996     clip0dev->updateStrokeOverprint(state);
997     clip1dev->updateStrokeOverprint(state);
998     gfxdev->updateStrokeOverprint(state);
999 }
1000 void BitmapOutputDev::updateTransfer(GfxState *state)
1001 {
1002     boolpolydev->updateTransfer(state);
1003     booltextdev->updateTransfer(state);
1004     rgbdev->updateTransfer(state);
1005     clip0dev->updateTransfer(state);
1006     clip1dev->updateTransfer(state);
1007     gfxdev->updateTransfer(state);
1008 }
1009
1010 void BitmapOutputDev::updateFont(GfxState *state)
1011 {
1012     boolpolydev->updateFont(state);
1013     booltextdev->updateFont(state);
1014     rgbdev->updateFont(state);
1015     clip0dev->updateFont(state);
1016     clip1dev->updateFont(state);
1017     gfxdev->updateFont(state);
1018 }
1019 void BitmapOutputDev::updateTextMat(GfxState *state)
1020 {
1021     boolpolydev->updateTextMat(state);
1022     booltextdev->updateTextMat(state);
1023     rgbdev->updateTextMat(state);
1024     clip0dev->updateTextMat(state);
1025     clip1dev->updateTextMat(state);
1026     gfxdev->updateTextMat(state);
1027 }
1028 void BitmapOutputDev::updateCharSpace(GfxState *state)
1029 {
1030     boolpolydev->updateCharSpace(state);
1031     booltextdev->updateCharSpace(state);
1032     rgbdev->updateCharSpace(state);
1033     clip0dev->updateCharSpace(state);
1034     clip1dev->updateCharSpace(state);
1035     gfxdev->updateCharSpace(state);
1036 }
1037 void BitmapOutputDev::updateRender(GfxState *state)
1038 {
1039     boolpolydev->updateRender(state);
1040     booltextdev->updateRender(state);
1041     rgbdev->updateRender(state);
1042     clip0dev->updateRender(state);
1043     clip1dev->updateRender(state);
1044     gfxdev->updateRender(state);
1045 }
1046 void BitmapOutputDev::updateRise(GfxState *state)
1047 {
1048     boolpolydev->updateRise(state);
1049     booltextdev->updateRise(state);
1050     rgbdev->updateRise(state);
1051     clip0dev->updateRise(state);
1052     clip1dev->updateRise(state);
1053     gfxdev->updateRise(state);
1054 }
1055 void BitmapOutputDev::updateWordSpace(GfxState *state)
1056 {
1057     boolpolydev->updateWordSpace(state);
1058     booltextdev->updateWordSpace(state);
1059     rgbdev->updateWordSpace(state);
1060     clip0dev->updateWordSpace(state);
1061     clip1dev->updateWordSpace(state);
1062     gfxdev->updateWordSpace(state);
1063 }
1064 void BitmapOutputDev::updateHorizScaling(GfxState *state)
1065 {
1066     boolpolydev->updateHorizScaling(state);
1067     booltextdev->updateHorizScaling(state);
1068     rgbdev->updateHorizScaling(state);
1069     clip0dev->updateHorizScaling(state);
1070     clip1dev->updateHorizScaling(state);
1071     gfxdev->updateHorizScaling(state);
1072 }
1073 void BitmapOutputDev::updateTextPos(GfxState *state)
1074 {
1075     boolpolydev->updateTextPos(state);
1076     booltextdev->updateTextPos(state);
1077     rgbdev->updateTextPos(state);
1078     clip0dev->updateTextPos(state);
1079     clip1dev->updateTextPos(state);
1080     gfxdev->updateTextPos(state);
1081 }
1082 void BitmapOutputDev::updateTextShift(GfxState *state, double shift)
1083 {
1084     boolpolydev->updateTextShift(state, shift);
1085     booltextdev->updateTextShift(state, shift);
1086     rgbdev->updateTextShift(state, shift);
1087     clip0dev->updateTextShift(state, shift);
1088     clip1dev->updateTextShift(state, shift);
1089     gfxdev->updateTextShift(state, shift);
1090 }
1091
1092 void BitmapOutputDev::stroke(GfxState *state)
1093 {
1094     msg("<debug> stroke");
1095     boolpolydev->stroke(state);
1096     checkNewBitmap(UNKNOWN_BOUNDING_BOX);
1097     rgbdev->stroke(state);
1098 }
1099 void BitmapOutputDev::fill(GfxState *state)
1100 {
1101     msg("<debug> fill");
1102     boolpolydev->fill(state);
1103     if(checkNewBitmap(UNKNOWN_BOUNDING_BOX)) {
1104         boolpolydev->fill(state);
1105     }
1106     rgbdev->fill(state);
1107 }
1108 void BitmapOutputDev::eoFill(GfxState *state)
1109 {
1110     msg("<debug> eoFill");
1111     boolpolydev->eoFill(state);
1112     if(checkNewBitmap(UNKNOWN_BOUNDING_BOX)) {
1113         boolpolydev->eoFill(state);
1114     }
1115     rgbdev->eoFill(state);
1116 }
1117 #if (xpdfMajorVersion*10000 + xpdfMinorVersion*100 + xpdfUpdateVersion) < 30207
1118 void BitmapOutputDev::tilingPatternFill(GfxState *state, Object *str,
1119                                int paintType, Dict *resDict,
1120                                double *mat, double *bbox,
1121                                int x0, int y0, int x1, int y1,
1122                                double xStep, double yStep)
1123 {
1124     msg("<debug> tilingPatternFill");
1125     boolpolydev->tilingPatternFill(state, str, paintType, resDict, mat, bbox, x0, y0, x1, y1, xStep, yStep);
1126     checkNewBitmap(UNKNOWN_BOUNDING_BOX);
1127     rgbdev->tilingPatternFill(state, str, paintType, resDict, mat, bbox, x0, y0, x1, y1, xStep, yStep);
1128 }
1129 #else
1130 void BitmapOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, Object *str,
1131                                int paintType, Dict *resDict,
1132                                double *mat, double *bbox,
1133                                int x0, int y0, int x1, int y1,
1134                                double xStep, double yStep) 
1135 {
1136     msg("<debug> tilingPatternFill");
1137     boolpolydev->tilingPatternFill(state, gfx, str, paintType, resDict, mat, bbox, x0, y0, x1, y1, xStep, yStep);
1138     checkNewBitmap(UNKNOWN_BOUNDING_BOX);
1139     rgbdev->tilingPatternFill(state, gfx, str, paintType, resDict, mat, bbox, x0, y0, x1, y1, xStep, yStep);
1140 }
1141 #endif
1142
1143 GBool BitmapOutputDev::functionShadedFill(GfxState *state, GfxFunctionShading *shading) 
1144 {
1145     msg("<debug> functionShadedFill");
1146     boolpolydev->functionShadedFill(state, shading);
1147     checkNewBitmap(UNKNOWN_BOUNDING_BOX);
1148     return rgbdev->functionShadedFill(state, shading);
1149 }
1150 GBool BitmapOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading)
1151 {
1152     msg("<debug> axialShadedFill");
1153     boolpolydev->axialShadedFill(state, shading);
1154     checkNewBitmap(UNKNOWN_BOUNDING_BOX);
1155     return rgbdev->axialShadedFill(state, shading);
1156 }
1157 GBool BitmapOutputDev::radialShadedFill(GfxState *state, GfxRadialShading *shading)
1158 {
1159     msg("<debug> radialShadedFill");
1160     boolpolydev->radialShadedFill(state, shading);
1161     checkNewBitmap(UNKNOWN_BOUNDING_BOX);
1162     return rgbdev->radialShadedFill(state, shading);
1163 }
1164
1165 SplashColor black = {0,0,0};
1166 SplashColor white = {255,255,255};
1167
1168 void BitmapOutputDev::clip(GfxState *state)
1169 {
1170     msg("<debug> clip");
1171     boolpolydev->clip(state);
1172     booltextdev->clip(state);
1173     rgbdev->clip(state);
1174     clip1dev->clip(state);
1175 }
1176 void BitmapOutputDev::eoClip(GfxState *state)
1177 {
1178     msg("<debug> eoClip");
1179     boolpolydev->eoClip(state);
1180     booltextdev->eoClip(state);
1181     rgbdev->eoClip(state);
1182     clip1dev->eoClip(state);
1183 }
1184 void BitmapOutputDev::clipToStrokePath(GfxState *state)
1185 {
1186     msg("<debug> clipToStrokePath");
1187     boolpolydev->clipToStrokePath(state);
1188     booltextdev->clipToStrokePath(state);
1189     rgbdev->clipToStrokePath(state);
1190     clip1dev->clipToStrokePath(state);
1191 }
1192
1193 void BitmapOutputDev::beginStringOp(GfxState *state)
1194 {
1195     msg("<debug> beginStringOp");
1196     clip0dev->beginStringOp(state);
1197     clip1dev->beginStringOp(state);
1198     booltextdev->beginStringOp(state);
1199     gfxdev->beginStringOp(state);
1200 }
1201 void BitmapOutputDev::endStringOp(GfxState *state)
1202 {
1203     msg("<debug> endStringOp");
1204     clip0dev->endStringOp(state);
1205     clip1dev->endStringOp(state);
1206     booltextdev->endStringOp(state);
1207     checkNewText(UNKNOWN_BOUNDING_BOX);
1208     gfxdev->endStringOp(state);
1209 }
1210 void BitmapOutputDev::beginString(GfxState *state, GString *s)
1211 {
1212     msg("<debug> beginString");
1213     clip0dev->beginString(state, s);
1214     clip1dev->beginString(state, s);
1215     booltextdev->beginString(state, s);
1216     gfxdev->beginString(state, s);
1217 }
1218 void BitmapOutputDev::endString(GfxState *state)
1219 {
1220     msg("<debug> endString");
1221     clip0dev->endString(state);
1222     clip1dev->endString(state);
1223     booltextdev->endString(state);
1224     checkNewText(UNKNOWN_BOUNDING_BOX);
1225     gfxdev->endString(state);
1226 }
1227
1228 void BitmapOutputDev::clearClips()
1229 {
1230     clearBooleanBitmap(clip0bitmap, 0, 0, 0, 0);
1231     clearBooleanBitmap(clip1bitmap, 0, 0, 0, 0);
1232 }
1233 void BitmapOutputDev::clearBoolPolyDev(int x1, int y1, int x2, int y2)
1234 {
1235     clearBooleanBitmap(boolpolybitmap, x1, y1, x2, y2);
1236 }
1237 void BitmapOutputDev::clearBoolTextDev(int x1, int y1, int x2, int y2)
1238 {
1239     clearBooleanBitmap(booltextbitmap, x1, y1, x2, y2);
1240 }
1241 void BitmapOutputDev::drawChar(GfxState *state, double x, double y,
1242                       double dx, double dy,
1243                       double originX, double originY,
1244                       CharCode code, int nBytes, Unicode *u, int uLen)
1245 {
1246     msg("<debug> drawChar render=%d", state->getRender());
1247
1248     if(state->getRender()&RENDER_CLIP) {
1249         //char is just a clipping boundary
1250         rgbdev->drawChar(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen);
1251         boolpolydev->drawChar(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen);
1252         booltextdev->drawChar(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen);
1253         clip1dev->drawChar(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen);
1254     } else if(rgbbitmap != rgbdev->getBitmap()) {
1255         // we're doing softmasking or transparency grouping
1256         boolpolydev->drawChar(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen);
1257         checkNewBitmap(UNKNOWN_BOUNDING_BOX);
1258         rgbdev->drawChar(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen);
1259     } else {
1260         // we're drawing a regular char
1261         clearClips();
1262         clip0dev->drawChar(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen);
1263         clip1dev->drawChar(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen);
1264    
1265         /* calculate the bbox of this character */
1266         int x1 = (int)x, x2 = (int)x+1, y1 = (int)y, y2 = (int)y+1;
1267         SplashPath*path = clip0dev->getCurrentFont()->getGlyphPath(code);
1268         if(!path) {
1269             if(code)
1270                 msg("<error> couldn't create outline for char %d", code);
1271             return;
1272         }
1273         int t;
1274         for(t=0;t<path->getLength();t++) {
1275             double xx,yy;
1276             Guchar f;
1277             path->getPoint(t,&xx,&yy,&f);
1278             xx+=x;
1279             yy+=y;
1280             if(xx<x1) x1=(int)xx;
1281             if(yy<y1) y1=(int)yy;
1282             if(xx>x2) x2=(int)xx+1;
1283             if(yy>y2) y2=(int)yy+1;
1284         }
1285
1286         /* if this character is affected somehow by the various clippings (i.e., it looks
1287            different on a device without clipping), then draw it on the bitmap, not as
1288            text */
1289         if(clip0and1differ(x1,y1,x2,y2)) {
1290             msg("<verbose> Char %d is affected by clipping", code);
1291             boolpolydev->drawChar(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen);
1292             checkNewBitmap(UNKNOWN_BOUNDING_BOX);
1293             rgbdev->drawChar(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen);
1294             if(config_extrafontdata) {
1295                 int oldrender = state->getRender();
1296                 state->setRender(3); //invisible
1297                 gfxdev->drawChar(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen);
1298                 state->setRender(oldrender);
1299             }
1300         } else {
1301             /* this char is not at all affected by clipping. 
1302                Now just dump out the bitmap we're currently working on, if necessary. */
1303             booltextdev->drawChar(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen);
1304             checkNewText(x1,y1,x2,y2);
1305             /* use polygonal output device to do the actual text handling */
1306             gfxdev->drawChar(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen);
1307         }
1308     }
1309 }
1310 void BitmapOutputDev::drawString(GfxState *state, GString *s)
1311 {
1312     msg("<error> internal error: drawString not implemented");
1313     return;
1314     clip0dev->drawString(state, s);
1315     clip1dev->drawString(state, s);
1316     booltextdev->drawString(state, s);
1317     gfxdev->drawString(state, s);
1318 }
1319 void BitmapOutputDev::endTextObject(GfxState *state)
1320 {
1321     msg("<debug> endTextObject");
1322     rgbdev->endTextObject(state);
1323     clip0dev->endTextObject(state);
1324     clip1dev->endTextObject(state);
1325     booltextdev->endTextObject(state);
1326     /* TODO: do this only if rendermode!=0 */
1327     checkNewText(UNKNOWN_BOUNDING_BOX);
1328     gfxdev->endTextObject(state);
1329 }
1330
1331 /* TODO: these four operations below *should* do nothing, as type3
1332          chars are drawn using operations like fill() */
1333 GBool BitmapOutputDev::beginType3Char(GfxState *state, double x, double y,
1334                              double dx, double dy,
1335                              CharCode code, Unicode *u, int uLen)
1336 {
1337     msg("<debug> beginType3Char");
1338     /* call gfxdev so that it can generate "invisible" characters
1339        on top of the actual graphic content, for text extraction */
1340     return gfxdev->beginType3Char(state, x, y, dx, dy, code, u, uLen);
1341 }
1342 void BitmapOutputDev::type3D0(GfxState *state, double wx, double wy)
1343 {
1344     msg("<debug> type3D0");
1345     return gfxdev->type3D0(state, wx, wy);
1346 }
1347 void BitmapOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury)
1348 {
1349     msg("<debug> type3D1");
1350     return gfxdev->type3D1(state, wx, wy, llx, lly, urx, ury);
1351 }
1352 void BitmapOutputDev::endType3Char(GfxState *state)
1353 {
1354     msg("<debug> endType3Char");
1355     gfxdev->endType3Char(state);
1356 }
1357
1358 class CopyStream: public Object
1359 {
1360     Dict*dict;
1361     char*buf;
1362     MemStream*memstream;
1363     public:
1364     CopyStream(Stream*str, int len)
1365     {
1366         buf = 0;
1367         str->reset();
1368         if(len) {
1369             buf = (char*)malloc(len);
1370             int t;
1371             for (t=0; t<len; t++)
1372               buf[t] = str->getChar();
1373         }
1374         str->close();
1375         this->dict = str->getDict();
1376         this->memstream = new MemStream(buf, 0, len, this);
1377     }
1378     ~CopyStream() 
1379     {
1380         ::free(this->buf);this->buf = 0;
1381         delete this->memstream;
1382     }
1383     Dict* getDict() {return dict;}
1384     Stream* getStream() {return this->memstream;};
1385 };
1386
1387 void BitmapOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
1388                            int width, int height, GBool invert,
1389                            GBool inlineImg)
1390 {
1391     msg("<debug> drawImageMask streamkind=%d", str->getKind());
1392     CopyStream*cpystr = 0;
1393     if(inlineImg) {
1394         cpystr = new CopyStream(str, height * ((width + 7) / 8));
1395         str = cpystr->getStream();
1396     }
1397     boolpolydev->drawImageMask(state, ref, str, width, height, invert, inlineImg);
1398     checkNewBitmap(UNKNOWN_BOUNDING_BOX);
1399     rgbdev->drawImageMask(state, ref, str, width, height, invert, inlineImg);
1400     if(cpystr)
1401         delete cpystr;
1402 }
1403 void BitmapOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
1404                        int width, int height, GfxImageColorMap *colorMap,
1405                        int *maskColors, GBool inlineImg)
1406 {
1407     msg("<debug> drawImage streamkind=%d", str->getKind());
1408     CopyStream*cpystr = 0;
1409     if(inlineImg) {
1410         cpystr = new CopyStream(str, height * ((width * colorMap->getNumPixelComps() * colorMap->getBits() + 7) / 8));
1411         str = cpystr->getStream();
1412     }
1413     boolpolydev->drawImage(state, ref, str, width, height, colorMap, maskColors, inlineImg);
1414     checkNewBitmap(UNKNOWN_BOUNDING_BOX);
1415     rgbdev->drawImage(state, ref, str, width, height, colorMap, maskColors, inlineImg);
1416     if(cpystr)
1417         delete cpystr;
1418 }
1419 void BitmapOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
1420                              int width, int height,
1421                              GfxImageColorMap *colorMap,
1422                              Stream *maskStr, int maskWidth, int maskHeight,
1423                              GBool maskInvert)
1424 {
1425     msg("<debug> drawMaskedImage streamkind=%d", str->getKind());
1426     boolpolydev->drawMaskedImage(state, ref, str, width, height, colorMap, maskStr, maskWidth, maskHeight, maskInvert);
1427     checkNewBitmap(UNKNOWN_BOUNDING_BOX);
1428     rgbdev->drawMaskedImage(state, ref, str, width, height, colorMap, maskStr, maskWidth, maskHeight, maskInvert);
1429 }
1430 void BitmapOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
1431                                  int width, int height,
1432                                  GfxImageColorMap *colorMap,
1433                                  Stream *maskStr,
1434                                  int maskWidth, int maskHeight,
1435                                  GfxImageColorMap *maskColorMap)
1436 {
1437     msg("<debug> drawSoftMaskedImage %dx%d (%dx%d) streamkind=%d", width, height, maskWidth, maskHeight, str->getKind());
1438     boolpolydev->drawSoftMaskedImage(state, ref, str, width, height, colorMap, maskStr, maskWidth, maskHeight, maskColorMap);
1439     checkNewBitmap(UNKNOWN_BOUNDING_BOX);
1440     rgbdev->drawSoftMaskedImage(state, ref, str, width, height, colorMap, maskStr, maskWidth, maskHeight, maskColorMap);
1441 }
1442 void BitmapOutputDev::drawForm(Ref id)
1443 {
1444     msg("<debug> drawForm");
1445     boolpolydev->drawForm(id);
1446     checkNewBitmap(UNKNOWN_BOUNDING_BOX);
1447     rgbdev->drawForm(id);
1448 }
1449
1450 void BitmapOutputDev::processLink(Link *link, Catalog *catalog)
1451 {
1452     msg("<debug> processLink");
1453     gfxdev->processLink(link, catalog);
1454 }
1455
1456 void BitmapOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
1457                                     GfxColorSpace *blendingColorSpace,
1458                                     GBool isolated, GBool knockout,
1459                                     GBool forSoftMask)
1460 {
1461     msg("<debug> beginTransparencyGroup");
1462 #if (xpdfMajorVersion*10000 + xpdfMinorVersion*100 + xpdfUpdateVersion) < 30207
1463     GfxState*state1 = state->copy();
1464     GfxState*state2 = state->copy();
1465     state1->setPath(0);
1466     state1->setPath(state->getPath()->copy());
1467     state2->setPath(0);
1468     state2->setPath(state->getPath()->copy());
1469 #else
1470     GfxState*state1 = state->copy(gTrue);
1471     GfxState*state2 = state->copy(gTrue);
1472 #endif
1473     boolpolydev->beginTransparencyGroup(state1, bbox, blendingColorSpace, isolated, knockout, forSoftMask);
1474     rgbdev->beginTransparencyGroup(state2, bbox, blendingColorSpace, isolated, knockout, forSoftMask);
1475     clip1dev->beginTransparencyGroup(state, bbox, blendingColorSpace, isolated, knockout, forSoftMask);
1476     delete state1;
1477     delete state2;
1478 }
1479 void BitmapOutputDev::endTransparencyGroup(GfxState *state)
1480 {
1481     msg("<debug> endTransparencyGroup");
1482 #if (xpdfMajorVersion*10000 + xpdfMinorVersion*100 + xpdfUpdateVersion) < 30207
1483     GfxState*state1 = state->copy();
1484     GfxState*state2 = state->copy();
1485     state1->setPath(0);
1486     state1->setPath(state->getPath()->copy());
1487     state2->setPath(0);
1488     state2->setPath(state->getPath()->copy());
1489 #else
1490     GfxState*state1 = state->copy(gTrue);
1491     GfxState*state2 = state->copy(gTrue);
1492 #endif
1493     boolpolydev->endTransparencyGroup(state1);
1494     checkNewBitmap(UNKNOWN_BOUNDING_BOX);
1495     rgbdev->endTransparencyGroup(state2);
1496     delete state1;
1497     delete state2;
1498     clip1dev->endTransparencyGroup(state);
1499 }
1500 void BitmapOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
1501 {
1502     msg("<debug> paintTransparencyGroup");
1503     boolpolydev->paintTransparencyGroup(state,bbox);
1504     checkNewBitmap(UNKNOWN_BOUNDING_BOX);
1505     rgbdev->paintTransparencyGroup(state,bbox);
1506     clip1dev->paintTransparencyGroup(state,bbox);
1507 }
1508 void BitmapOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *backdropColor)
1509 {
1510     msg("<debug> setSoftMask");
1511     boolpolydev->setSoftMask(state, bbox, alpha, transferFunc, backdropColor);
1512     checkNewBitmap(UNKNOWN_BOUNDING_BOX);
1513     rgbdev->setSoftMask(state, bbox, alpha, transferFunc, backdropColor);
1514     clip1dev->setSoftMask(state, bbox, alpha, transferFunc, backdropColor);
1515 }
1516 void BitmapOutputDev::clearSoftMask(GfxState *state)
1517 {
1518     msg("<debug> clearSoftMask");
1519     boolpolydev->clearSoftMask(state);
1520     checkNewBitmap(UNKNOWN_BOUNDING_BOX);
1521     rgbdev->clearSoftMask(state);
1522     clip1dev->clearSoftMask(state);
1523 }