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