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