0b49c2c8a765c93c411d3c5d996e7e939607948f
[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 "config.h"
23 #include "BitmapOutputDev.h"
24 #include "GFXOutputDev.h"
25 #include "SplashBitmap.h"
26 #include "SplashPattern.h"
27 #include "Splash.h"
28 #include "../log.h"
29
30 static SplashColor splash_white = {255,255,255};
31 static SplashColor splash_black = {0,0,0};
32     
33 ClipState::ClipState()
34 {
35     this->next = 0;
36     this->clipbitmap = 0;
37     this->written = 0;
38 }
39
40 BitmapOutputDev::BitmapOutputDev(InfoOutputDev*info, PDFDoc*doc)
41 {
42     this->info = info;
43     this->doc = doc;
44     this->xref = doc->getXRef();
45     this->rgbdev = new SplashOutputDev(splashModeRGB8, 1, gFalse, splash_white, gTrue, gTrue);
46     this->clip0dev = new SplashOutputDev(splashModeMono1, 1, gFalse, splash_black, gTrue, gFalse);
47     this->clip1dev = new SplashOutputDev(splashModeMono1, 1, gFalse, splash_black, gTrue, gFalse);
48     this->gfxdev = new GFXOutputDev(info, this->doc);
49     this->rgbdev->startDoc(this->xref);
50     this->clip0dev->startDoc(this->xref);
51     this->clip1dev->startDoc(this->xref);
52     
53     this->config_bitmapfonts = 0;
54     this->config_extrafontdata = 0;
55     this->bboxpath = 0;
56     //this->clipdev = 0;
57     //this->clipstates = 0;
58 }
59 BitmapOutputDev::~BitmapOutputDev()
60 {
61     if(this->bboxpath) {
62         delete this->bboxpath;this->bboxpath = 0;
63     }
64     if(this->rgbdev) {
65         delete this->rgbdev;this->rgbdev= 0;
66     }
67     if(this->clip0dev) {
68         delete this->clip0dev;this->clip0dev= 0;
69     }
70     if(this->clip1dev) {
71         delete this->clip1dev;this->clip1dev= 0;
72     }
73     //if(this->clipbitmap) {
74     //    delete this->clipbitmap;this->clipbitmap = 0;
75     //}
76     //if(this->clipdev) {
77     //    delete this->clipdev;this->clipdev = 0;
78     //}
79
80 }
81
82 GBool BitmapOutputDev::getVectorAntialias()
83 {
84     return this->rgbdev->getVectorAntialias();
85 }
86 void BitmapOutputDev::setVectorAntialias(GBool vaa)
87 {
88     this->rgbdev->setVectorAntialias(vaa);
89 }
90 void BitmapOutputDev::setDevice(gfxdevice_t*dev)
91 {
92     this->dev = dev;
93     this->gfxdev->setDevice(dev);
94 }
95 void BitmapOutputDev::setMove(int x,int y)
96 {
97     this->gfxdev->setMove(x,y);
98     this->user_movex = x;
99     this->user_movey = y;
100 }
101 void BitmapOutputDev::setClip(int x1,int y1,int x2,int y2)
102 {
103     this->gfxdev->setClip(x1,y1,x2,y2);
104     this->user_clipx1 = x1;
105     this->user_clipy1 = y1;
106     this->user_clipx2 = x2;
107     this->user_clipy2 = y2;
108 }
109 void BitmapOutputDev::setParameter(const char*key, const char*value)
110 {
111     if(!strcmp(key, "extrafontdata")) {
112         this->config_extrafontdata = atoi(value);
113     } else if(!strcmp(key, "bitmapfonts")) {
114         this->config_bitmapfonts = atoi(value);
115     }
116     this->gfxdev->setParameter(key, value);
117 }
118 void BitmapOutputDev::preparePage(int pdfpage, int outputpage)
119 {
120 }
121
122 void getBitmapBBox(Guchar*alpha, int width, int height, int*xmin, int*ymin, int*xmax, int*ymax)
123 {
124     *ymin = -1;
125     *xmin = width;
126     *xmax = 0;
127     int x,y;
128     for(y=0;y<height;y++) {
129         Guchar*a = &alpha[y*width];
130         for(x=0;x<width;x++) {
131             if(a[x]) break;
132         }
133         int left = x; //first occupied pixel from left
134         int right = x+1; //last non-occupied pixel from right
135         for(;x<width;x++) {
136             if(a[x]) right=x+1;
137         }
138
139         if(left!=width) {
140             if(*ymin<0) 
141                 *ymin=y;
142             *ymax=y+1;
143             if(left<*xmin) *xmin = left;
144             if(right>*xmax) *xmax = right;
145         }
146     }
147     if(*xmin>=*xmax || *ymin>=*ymax) {
148         *xmin = 0;
149         *ymin = 0;
150         *xmax = 0;
151         *ymax = 0;
152     }
153 }
154
155 void BitmapOutputDev::flush()
156 {
157     int width = rgbdev->getBitmapWidth();
158     int height = rgbdev->getBitmapHeight();
159     
160     SplashColorPtr rgb = rgbdev->getBitmap()->getDataPtr();
161     Guchar*alpha = rgbdev->getBitmap()->getAlphaPtr();
162
163     int xmin,ymin,xmax,ymax;
164     getBitmapBBox(alpha, width, height, &xmin,&ymin,&xmax,&ymax);
165
166     /* clip against (-movex, -movey, -movex+width, -movey+height) */
167     if(xmin < -this->movex) xmin = -this->movex;
168     if(ymin < -this->movey) ymin = -this->movey;
169     if(xmax > -this->movex + width) xmax = -this->movex+this->width;
170     if(ymax > -this->movey + height) ymax = -this->movey+this->height;
171
172     msg("<verbose> Flushing graphics (bbox: %d,%d,%d,%d)", xmin,ymin,xmax,ymax);
173     
174     if((xmax-xmin)<=0 || (ymax-ymin)<=0) // no bitmap, nothing to do
175         return;
176
177     if(sizeof(SplashColor)!=3) {
178         msg("<error> sizeof(SplashColor)!=3");
179         return;
180     }
181     //xmin = ymin = 0;
182     //xmax = width;
183     //ymax = height;
184
185     int rangex = xmax-xmin;
186     int rangey = ymax-ymin;
187     gfximage_t*img = (gfximage_t*)malloc(sizeof(gfximage_t)); 
188     img->data = (gfxcolor_t*)malloc(rangex * rangey * 4);
189     img->width = rangex;
190     img->height = rangey;
191     int x,y;
192     for(y=0;y<rangey;y++) {
193         SplashColorPtr in=&rgb[((y+ymin)*width+xmin)*sizeof(SplashColor)];
194         gfxcolor_t*out = &img->data[y*rangex];
195         Guchar*ain = &alpha[(y+ymin)*width+xmin];
196         for(x=0;x<rangex;x++) {
197             /* according to endPage()/compositeBackground() in xpdf/SplashOutputDev.cc, we
198                have to premultiply alpha (mix background and pixel according to the alpha channel).
199             */
200             out[x].r = (in[x*3+0]*ain[x])/255;
201             out[x].g = (in[x*3+1]*ain[x])/255;
202             out[x].b = (in[x*3+2]*ain[x])/255;
203             out[x].a = ain[x];
204         }
205     }
206     /* transform bitmap rectangle to "device space" */
207     xmin += movex;
208     ymin += movey;
209     xmax += movex;
210     ymax += movey;
211
212     gfxmatrix_t m;
213     m.tx = xmin;
214     m.ty = ymin;
215     m.m00 = m.m11 = 1;
216     m.m10 = m.m01 = 0;
217
218     gfxline_t* line = gfxline_makerectangle(xmin, ymin, xmax, ymax);
219     dev->fillbitmap(dev, line, img, &m, 0);
220     gfxline_free(line);
221
222     memset(rgbdev->getBitmap()->getAlphaPtr(), 0, rgbdev->getBitmap()->getWidth()*rgbdev->getBitmap()->getHeight());
223     memset(rgbdev->getBitmap()->getDataPtr(), 0, rgbdev->getBitmap()->getRowSize()*rgbdev->getBitmap()->getHeight());
224
225     free(img->data);img->data=0;free(img);img=0;
226 }
227
228 void BitmapOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2)
229 {
230     double x1,y1,x2,y2;
231     state->transform(crop_x1,crop_y1,&x1,&y1);
232     state->transform(crop_x2,crop_y2,&x2,&y2);
233     if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
234     if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
235     
236     this->movex = -(int)x1 - user_movex;
237     this->movey = -(int)y1 - user_movey;
238     
239     if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
240         x1 = user_clipx1;
241         x2 = user_clipx2;
242         y1 = user_clipy1;
243         y2 = user_clipy2;
244     }
245     this->width = (int)(x2-x1);
246     this->height = (int)(y2-y1);
247
248     msg("<verbose> startPage");
249     rgbdev->startPage(pageNum, state, crop_x1, crop_y1, crop_x2, crop_y2);
250     clip0dev->startPage(pageNum, state, crop_x1, crop_y1, crop_x2, crop_y2);
251     clip1dev->startPage(pageNum, state, crop_x1, crop_y1, crop_x2, crop_y2);
252     gfxdev->startPage(pageNum, state, crop_x1, crop_y1, crop_x2, crop_y2);
253 }
254
255 void BitmapOutputDev::endPage()
256 {
257     msg("<verbose> endPage");
258     this->flush();
259
260     /* splash will now destroy alpha, and paint the 
261        background color into the "holes" in the bitmap */
262     rgbdev->endPage();
263     clip0dev->endPage();
264     clip1dev->endPage();
265 }
266
267 GBool BitmapOutputDev::upsideDown()
268 {
269     clip0dev->upsideDown();
270     clip1dev->upsideDown();
271     return rgbdev->upsideDown();
272 }
273
274 GBool BitmapOutputDev::useDrawChar()
275 {
276     clip0dev->useDrawChar();
277     clip1dev->useDrawChar();
278     return rgbdev->useDrawChar();
279 }
280
281 GBool BitmapOutputDev::useTilingPatternFill()
282 {
283     clip0dev->useTilingPatternFill();
284     clip1dev->useTilingPatternFill();
285     return rgbdev->useTilingPatternFill();
286 }
287
288 GBool BitmapOutputDev::useShadedFills()
289 {
290     clip0dev->useShadedFills();
291     clip1dev->useShadedFills();
292     return rgbdev->useShadedFills();
293 }
294
295 GBool BitmapOutputDev::useDrawForm()
296 {
297     clip0dev->useDrawForm();
298     clip1dev->useDrawForm();
299     return rgbdev->useDrawForm();
300 }
301
302 GBool BitmapOutputDev::interpretType3Chars()
303 {
304     if(!config_bitmapfonts) {
305         clip0dev->interpretType3Chars();
306         clip1dev->interpretType3Chars();
307         return rgbdev->interpretType3Chars();
308     } else {
309         return gfxdev->interpretType3Chars();
310     }
311 }
312
313 GBool BitmapOutputDev::needNonText() 
314 {
315     clip0dev->needNonText();
316     clip1dev->needNonText();
317     return rgbdev->needNonText();
318 }
319 /*GBool BitmapOutputDev::checkPageSlice(Page *page, double hDPI, double vDPI,
320                            int rotate, GBool useMediaBox, GBool crop,
321                            int sliceX, int sliceY, int sliceW, int sliceH,
322                            GBool printing, Catalog *catalog,
323                            GBool (*abortCheckCbk)(void *data),
324                            void *abortCheckCbkData)
325 {
326     return gTrue;
327 }*/
328 void BitmapOutputDev::setDefaultCTM(double *ctm) 
329 {
330     rgbdev->setDefaultCTM(ctm);
331     clip0dev->setDefaultCTM(ctm);
332     clip1dev->setDefaultCTM(ctm);
333 }
334 void BitmapOutputDev::saveState(GfxState *state) 
335 {
336     rgbdev->saveState(state);
337     clip0dev->saveState(state);
338     clip1dev->saveState(state);
339
340     /*ClipState*cstate = new ClipState();
341     cstate->next = this->clipstates;
342     this->clipstates = cstate;*/
343 }
344 void BitmapOutputDev::restoreState(GfxState *state) 
345 {
346     rgbdev->restoreState(state);
347     clip0dev->restoreState(state);
348     clip1dev->restoreState(state);
349
350     /*if(this->clipstates) {
351         ClipState*old = this->clipstates;
352         if(old->written) {
353             gfxdev->restoreState(state);
354         }
355         this->clipstates = this->clipstates->next;
356         delete(old);
357     } else {
358         msg("<error> invalid restoreState()");
359     }*/
360 }
361 void BitmapOutputDev::updateAll(GfxState *state)
362 {
363     rgbdev->updateAll(state);
364     clip0dev->updateAll(state);
365     clip1dev->updateAll(state);
366 }
367 void BitmapOutputDev::updateCTM(GfxState *state, double m11, double m12, double m21, double m22, double m31, double m32)
368 {
369     rgbdev->updateCTM(state,m11,m12,m21,m22,m31,m32);
370     clip0dev->updateCTM(state,m11,m12,m21,m22,m31,m32);
371     clip1dev->updateCTM(state,m11,m12,m21,m22,m31,m32);
372 }
373 void BitmapOutputDev::updateLineDash(GfxState *state)
374 {
375     rgbdev->updateLineDash(state);
376     clip0dev->updateLineDash(state);
377     clip1dev->updateLineDash(state);
378 }
379 void BitmapOutputDev::updateFlatness(GfxState *state)
380 {
381     rgbdev->updateFlatness(state);
382     clip0dev->updateFlatness(state);
383     clip1dev->updateFlatness(state);
384 }
385 void BitmapOutputDev::updateLineJoin(GfxState *state)
386 {
387     rgbdev->updateLineJoin(state);
388     clip0dev->updateLineJoin(state);
389     clip1dev->updateLineJoin(state);
390 }
391 void BitmapOutputDev::updateLineCap(GfxState *state)
392 {
393     rgbdev->updateLineCap(state);
394     clip0dev->updateLineCap(state);
395     clip1dev->updateLineCap(state);
396 }
397 void BitmapOutputDev::updateMiterLimit(GfxState *state)
398 {
399     rgbdev->updateMiterLimit(state);
400     clip0dev->updateMiterLimit(state);
401     clip1dev->updateMiterLimit(state);
402 }
403 void BitmapOutputDev::updateLineWidth(GfxState *state)
404 {
405     rgbdev->updateLineWidth(state);
406     clip0dev->updateLineWidth(state);
407     clip1dev->updateLineWidth(state);
408 }
409 void BitmapOutputDev::updateStrokeAdjust(GfxState *state)
410 {
411     rgbdev->updateStrokeAdjust(state);
412     clip0dev->updateStrokeAdjust(state);
413     clip1dev->updateStrokeAdjust(state);
414 }
415 void BitmapOutputDev::updateFillColorSpace(GfxState *state)
416 {
417     rgbdev->updateFillColorSpace(state);
418     clip0dev->updateFillColorSpace(state);
419     clip1dev->updateFillColorSpace(state);
420 }
421 void BitmapOutputDev::updateStrokeColorSpace(GfxState *state)
422 {
423     rgbdev->updateStrokeColorSpace(state);
424     clip0dev->updateStrokeColorSpace(state);
425     clip1dev->updateStrokeColorSpace(state);
426 }
427 void BitmapOutputDev::updateFillColor(GfxState *state)
428 {
429     rgbdev->updateFillColor(state);
430     clip0dev->updateFillColor(state);
431     clip1dev->updateFillColor(state);
432     if(!config_bitmapfonts)
433         gfxdev->updateFillColor(state);
434 }
435 void BitmapOutputDev::updateStrokeColor(GfxState *state)
436 {
437     rgbdev->updateStrokeColor(state);
438     clip0dev->updateStrokeColor(state);
439     clip1dev->updateStrokeColor(state);
440 }
441 void BitmapOutputDev::updateBlendMode(GfxState *state)
442 {
443     rgbdev->updateBlendMode(state);
444     clip0dev->updateBlendMode(state);
445     clip1dev->updateBlendMode(state);
446 }
447 void BitmapOutputDev::updateFillOpacity(GfxState *state)
448 {
449     rgbdev->updateFillOpacity(state);
450     clip0dev->updateFillOpacity(state);
451     clip1dev->updateFillOpacity(state);
452 }
453 void BitmapOutputDev::updateStrokeOpacity(GfxState *state)
454 {
455     rgbdev->updateStrokeOpacity(state);
456     clip0dev->updateStrokeOpacity(state);
457     clip1dev->updateStrokeOpacity(state);
458 }
459 void BitmapOutputDev::updateFillOverprint(GfxState *state)
460 {
461     rgbdev->updateFillOverprint(state);
462     clip0dev->updateFillOverprint(state);
463     clip1dev->updateFillOverprint(state);
464 }
465 void BitmapOutputDev::updateStrokeOverprint(GfxState *state)
466 {
467     rgbdev->updateStrokeOverprint(state);
468     clip0dev->updateStrokeOverprint(state);
469     clip1dev->updateStrokeOverprint(state);
470 }
471 void BitmapOutputDev::updateTransfer(GfxState *state)
472 {
473     rgbdev->updateTransfer(state);
474     clip0dev->updateTransfer(state);
475     clip1dev->updateTransfer(state);
476 }
477 void BitmapOutputDev::updateFont(GfxState *state)
478 {
479     rgbdev->updateFont(state);
480     clip0dev->updateFont(state);
481     clip1dev->updateFont(state);
482     if(!config_bitmapfonts)
483         gfxdev->updateFont(state);
484 }
485 void BitmapOutputDev::updateTextMat(GfxState *state)
486 {
487     rgbdev->updateTextMat(state);
488     clip0dev->updateTextMat(state);
489     clip1dev->updateTextMat(state);
490     if(!config_bitmapfonts)
491         gfxdev->updateTextMat(state);
492 }
493 void BitmapOutputDev::updateCharSpace(GfxState *state)
494 {
495     rgbdev->updateCharSpace(state);
496     clip0dev->updateCharSpace(state);
497     clip1dev->updateCharSpace(state);
498     if(!config_bitmapfonts)
499         gfxdev->updateCharSpace(state);
500 }
501 void BitmapOutputDev::updateRender(GfxState *state)
502 {
503     rgbdev->updateRender(state);
504     clip0dev->updateRender(state);
505     clip1dev->updateRender(state);
506     if(!config_bitmapfonts)
507         gfxdev->updateRender(state);
508 }
509 void BitmapOutputDev::updateRise(GfxState *state)
510 {
511     rgbdev->updateRise(state);
512     clip0dev->updateRise(state);
513     clip1dev->updateRise(state);
514     if(!config_bitmapfonts)
515         gfxdev->updateRise(state);
516 }
517 void BitmapOutputDev::updateWordSpace(GfxState *state)
518 {
519     rgbdev->updateWordSpace(state);
520     clip0dev->updateWordSpace(state);
521     clip1dev->updateWordSpace(state);
522     if(!config_bitmapfonts)
523         gfxdev->updateWordSpace(state);
524 }
525 void BitmapOutputDev::updateHorizScaling(GfxState *state)
526 {
527     rgbdev->updateHorizScaling(state);
528     clip0dev->updateHorizScaling(state);
529     clip1dev->updateHorizScaling(state);
530     if(!config_bitmapfonts)
531         gfxdev->updateHorizScaling(state);
532 }
533 void BitmapOutputDev::updateTextPos(GfxState *state)
534 {
535     rgbdev->updateTextPos(state);
536     clip0dev->updateTextPos(state);
537     clip1dev->updateTextPos(state);
538     if(!config_bitmapfonts)
539         gfxdev->updateTextPos(state);
540 }
541 void BitmapOutputDev::updateTextShift(GfxState *state, double shift)
542 {
543     rgbdev->updateTextShift(state, shift);
544     clip0dev->updateTextShift(state, shift);
545     clip1dev->updateTextShift(state, shift);
546     if(!config_bitmapfonts)
547         gfxdev->updateTextShift(state, shift);
548 }
549
550 void BitmapOutputDev::stroke(GfxState *state)
551 {
552     msg("<verbose> stroke");
553     rgbdev->stroke(state);
554 }
555 void BitmapOutputDev::fill(GfxState *state)
556 {
557     msg("<verbose> fill");
558     rgbdev->fill(state);
559 }
560 void BitmapOutputDev::eoFill(GfxState *state)
561 {
562     msg("<verbose> eoFill");
563     rgbdev->eoFill(state);
564 }
565 #if (xpdfMajorVersion < 3) || (xpdfMinorVersion < 2) || (xpdfUpdateVersion < 7)
566 void BitmapOutputDev::tilingPatternFill(GfxState *state, Object *str,
567                                int paintType, Dict *resDict,
568                                double *mat, double *bbox,
569                                int x0, int y0, int x1, int y1,
570                                double xStep, double yStep)
571 {
572     msg("<verbose> tilingPatternFill");
573     rgbdev->tilingPatternFill(state, str, paintType, resDict, mat, bbox, x0, y0, x1, y1, xStep, yStep);
574 }
575 #else
576 void BitmapOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, Object *str,
577                                int paintType, Dict *resDict,
578                                double *mat, double *bbox,
579                                int x0, int y0, int x1, int y1,
580                                double xStep, double yStep) 
581 {
582     msg("<verbose> tilingPatternFill");
583     rgbdev->tilingPatternFill(state, gfx, str, paintType, resDict, mat, bbox, x0, y0, x1, y1, xStep, yStep);
584 }
585 #endif
586
587 GBool BitmapOutputDev::functionShadedFill(GfxState *state, GfxFunctionShading *shading) 
588 {
589     msg("<verbose> functionShadedFill");
590     return rgbdev->functionShadedFill(state, shading);
591 }
592 GBool BitmapOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading)
593 {
594     msg("<verbose> axialShadedFill");
595     return rgbdev->axialShadedFill(state, shading);
596 }
597 GBool BitmapOutputDev::radialShadedFill(GfxState *state, GfxRadialShading *shading)
598 {
599     msg("<verbose> radialShadedFill");
600     return rgbdev->radialShadedFill(state, shading);
601 }
602
603 SplashColor black = {0,0,0};
604 SplashColor white = {255,255,255};
605
606 void BitmapOutputDev::doClip(GfxState *state, GBool eo)
607 {
608     if(!eo) {
609         rgbdev->clip(state);
610         clip1dev->clip(state);
611     } else {
612         rgbdev->eoClip(state);
613         clip1dev->eoClip(state);
614     }
615
616     //SplashPattern *pblack = new SplashSolidColor(black);
617     //SplashPattern *pwhite = new SplashSolidColor(white);
618     //SplashPath* path = rgbdev->convertPath(state, state->getPath());
619     //clipdev->clear(black, 0);
620     //clipdev->clipToPath(path, eo);
621     //clipdev->setFillPattern(pwhite);
622     //clipdev->fill(bboxpath,gFalse);
623     //delete path;
624 }
625
626 void BitmapOutputDev::clip(GfxState *state)
627 {
628     msg("<verbose> clip");
629     doClip(state, gFalse);
630 }
631 void BitmapOutputDev::eoClip(GfxState *state)
632 {
633     msg("<verbose> eoClip");
634     doClip(state, gTrue);
635 }
636 void BitmapOutputDev::clipToStrokePath(GfxState *state)
637 {
638     msg("<verbose> clipToStrokePath");
639     rgbdev->clipToStrokePath(state);
640     clip1dev->clipToStrokePath(state);
641 }
642
643 void BitmapOutputDev::beginStringOp(GfxState *state)
644 {
645     msg("<verbose> beginStringOp");
646     if(this->config_bitmapfonts) {
647         rgbdev->beginStringOp(state);
648         clip0dev->beginStringOp(state);
649         clip1dev->beginStringOp(state);
650     } else {
651         gfxdev->beginStringOp(state);
652     }
653 }
654 void BitmapOutputDev::endStringOp(GfxState *state)
655 {
656     msg("<verbose> endStringOp");
657     if(this->config_bitmapfonts) {
658         rgbdev->endStringOp(state);
659         clip0dev->endStringOp(state);
660         clip1dev->endStringOp(state);
661     } else {
662         gfxdev->endStringOp(state);
663     }
664 }
665 void BitmapOutputDev::beginString(GfxState *state, GString *s)
666 {
667     msg("<verbose> beginString");
668     if(this->config_bitmapfonts) {
669         rgbdev->beginString(state, s);
670         clip0dev->beginString(state, s);
671         clip1dev->beginString(state, s);
672     } else {
673         gfxdev->beginString(state, s);
674     }
675 }
676 void BitmapOutputDev::endString(GfxState *state)
677 {
678     msg("<verbose> endString");
679     if(this->config_bitmapfonts) {
680         rgbdev->endString(state);
681         clip0dev->endString(state);
682         clip1dev->endString(state);
683     } else {
684         gfxdev->endString(state);
685     }
686 }
687 void BitmapOutputDev::drawChar(GfxState *state, double x, double y,
688                       double dx, double dy,
689                       double originX, double originY,
690                       CharCode code, int nBytes, Unicode *u, int uLen)
691 {
692     msg("<verbose> drawChar");
693     if(this->config_bitmapfonts || (state->getRender()&4) /*clip*/ ) {
694         rgbdev->drawChar(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen);
695     } else {
696         SplashBitmap*clip0 = clip0dev->getBitmap();
697         SplashBitmap*clip1 = clip1dev->getBitmap();
698         int width8 = (clip0->getWidth()+7)/8;
699         int width = clip0->getWidth();
700         int height = clip0->getHeight();
701         memset(clip0->getDataPtr(), 0, width8*height);
702         memset(clip1->getDataPtr(), 0, width8*height);
703         clip0dev->drawChar(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen);
704         clip1dev->drawChar(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen);
705
706         /* if this character is affected somehow by the various clippings (i.e., it looks
707            different on a device without clipping), then draw it on the bitmap, not as
708            text */
709         if(memcmp(clip0->getDataPtr(), clip1->getDataPtr(), width8*height)) {
710             msg("<verbose> Char %d is affected by clipping", code);
711             rgbdev->drawChar(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen);
712             if(config_extrafontdata) {
713                 int oldrender = state->getRender();
714                 state->setRender(3); //invisible
715                 gfxdev->drawChar(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen);
716                 state->setRender(oldrender);
717             }
718         } else {
719             /* this char is not at all affected by clipping. Now just find out whether the
720                bitmap we're currently working on needs to be dumped out first,
721                by checking whether any of the char pixels in clip1dev is above any non-alpha
722                pixels in rgbdev */
723     
724             Guchar*alpha = rgbdev->getBitmap()->getAlphaPtr();
725             Guchar*charpixels = clip1dev->getBitmap()->getDataPtr();
726             int xx,yy;
727             for(yy=0;yy<height;yy++) {
728                 Guchar*aline = &alpha[yy*width];
729                 Guchar*cline = &charpixels[yy*width8];
730                 for(xx=0;xx<width;xx++) {
731                     int bit = xx&7;
732                     int bytepos = xx>>3;
733                     /* TODO: is the bit order correct? */
734                     if(aline[xx] && (cline[bytepos]&(1<<bit))) 
735                         break;
736                 }
737                 if(xx!=width)
738                     break;
739             }
740
741             if(yy!=height) {
742                 /* yes, the graphic data and the characters overlap (the char is
743                    above the current bitmap. Flush the bitmap to the output. */
744                 msg("<verbose> Char %d is above current bitmap data", code);
745                 flush();
746             } 
747             gfxdev->drawChar(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen);
748         }
749     }
750 }
751 void BitmapOutputDev::drawString(GfxState *state, GString *s)
752 {
753     msg("<verbose> drawString");
754     if(this->config_bitmapfonts) {
755         rgbdev->drawString(state, s);
756         clip0dev->drawString(state, s);
757         clip1dev->drawString(state, s);
758     } else {
759         gfxdev->drawString(state, s);
760     }
761 }
762 void BitmapOutputDev::endTextObject(GfxState *state)
763 {
764     msg("<verbose> endTextObject");
765     if(this->config_bitmapfonts) {
766         rgbdev->endTextObject(state);
767         clip0dev->endTextObject(state);
768         clip1dev->endTextObject(state);
769     } else {
770         gfxdev->endType3Char(state);
771     }
772 }
773 GBool BitmapOutputDev::beginType3Char(GfxState *state, double x, double y,
774                              double dx, double dy,
775                              CharCode code, Unicode *u, int uLen)
776 {
777     msg("<verbose> beginType3Char");
778     if(this->config_bitmapfonts) {
779         return rgbdev->beginType3Char(state, x, y, dx, dy, code, u, uLen);
780     } else {
781         return gfxdev->beginType3Char(state, x, y, dx, dy, code, u, uLen);
782     }
783 }
784 void BitmapOutputDev::type3D0(GfxState *state, double wx, double wy)
785 {
786     msg("<verbose> type3D0");
787     if(this->config_bitmapfonts) {
788         rgbdev->type3D0(state, wx, wy);
789     } else {
790         return gfxdev->type3D0(state, wx, wy);
791     }
792 }
793 void BitmapOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury)
794 {
795     msg("<verbose> type3D1");
796     if(this->config_bitmapfonts) {
797         rgbdev->type3D1(state, wx, wy, llx, lly, urx, ury);
798     } else {
799         return gfxdev->type3D1(state, wx, wy, llx, lly, urx, ury);
800     }
801 }
802 void BitmapOutputDev::endType3Char(GfxState *state)
803 {
804     msg("<verbose> endType3Char");
805     if(this->config_bitmapfonts) {
806         rgbdev->endType3Char(state);
807     } else {
808         gfxdev->endType3Char(state);
809     }
810 }
811 void BitmapOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
812                            int width, int height, GBool invert,
813                            GBool inlineImg)
814 {
815     msg("<verbose> drawImageMask");
816     rgbdev->drawImageMask(state, ref, str, width, height, invert, inlineImg);
817 }
818 void BitmapOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
819                        int width, int height, GfxImageColorMap *colorMap,
820                        int *maskColors, GBool inlineImg)
821 {
822     msg("<verbose> drawImage");
823     rgbdev->drawImage(state, ref, str, width, height, colorMap, maskColors, inlineImg);
824 }
825 void BitmapOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
826                              int width, int height,
827                              GfxImageColorMap *colorMap,
828                              Stream *maskStr, int maskWidth, int maskHeight,
829                              GBool maskInvert)
830 {
831     msg("<verbose> drawMaskedImage");
832     rgbdev->drawMaskedImage(state, ref, str, width, height, colorMap, maskStr, maskWidth, maskHeight, maskInvert);
833 }
834 void BitmapOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
835                                  int width, int height,
836                                  GfxImageColorMap *colorMap,
837                                  Stream *maskStr,
838                                  int maskWidth, int maskHeight,
839                                  GfxImageColorMap *maskColorMap)
840 {
841     msg("<verbose> drawSoftMaskedImage");
842     rgbdev->drawSoftMaskedImage(state, ref, str, width, height, colorMap, maskStr, maskWidth, maskHeight, maskColorMap);
843 }
844 void BitmapOutputDev::drawForm(Ref id)
845 {
846     msg("<verbose> drawForm");
847     rgbdev->drawForm(id);
848 }
849
850 void BitmapOutputDev::processLink(Link *link, Catalog *catalog)
851 {
852     msg("<verbose> processLink");
853     gfxdev->processLink(link, catalog);
854 }
855
856 void BitmapOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
857                                     GfxColorSpace *blendingColorSpace,
858                                     GBool isolated, GBool knockout,
859                                     GBool forSoftMask)
860 {
861     msg("<verbose> beginTransparencyGroup");
862     rgbdev->beginTransparencyGroup(state, bbox, blendingColorSpace, isolated, knockout, forSoftMask);
863     clip1dev->beginTransparencyGroup(state, bbox, blendingColorSpace, isolated, knockout, forSoftMask);
864 }
865 void BitmapOutputDev::endTransparencyGroup(GfxState *state)
866 {
867     msg("<verbose> endTransparencyGroup");
868     rgbdev->endTransparencyGroup(state);
869     clip1dev->endTransparencyGroup(state);
870 }
871 void BitmapOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
872 {
873     msg("<verbose> paintTransparencyGroup");
874     rgbdev->paintTransparencyGroup(state,bbox);
875 }
876 void BitmapOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *backdropColor)
877 {
878     msg("<verbose> setSoftMask");
879     rgbdev->setSoftMask(state, bbox, alpha, transferFunc, backdropColor);
880     clip1dev->setSoftMask(state, bbox, alpha, transferFunc, backdropColor);
881 }
882 void BitmapOutputDev::clearSoftMask(GfxState *state)
883 {
884     msg("<verbose> clearSoftMask");
885     rgbdev->clearSoftMask(state);
886     clip1dev->clearSoftMask(state);
887 }