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