1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_vcl.hxx"
30 
31 #include "psputil.hxx"
32 
33 #include "printergfx.hxx"
34 #include "vcl/strhelper.hxx"
35 
36 namespace psp {
37 
38 const sal_uInt32 nLineLength = 80;
39 const sal_uInt32 nBufferSize = 16384;
40 
41 /*
42  *
43  * Bitmap compression / Hex encoding / Ascii85 Encoding
44  *
45  */
46 
47 PrinterBmp::~PrinterBmp ()
48 { /* dont need this, but C50 does */ }
49 
50 /* virtual base class */
51 
52 class ByteEncoder
53 {
54 private:
55 
56 public:
57 
58     virtual void    EncodeByte (sal_uInt8 nByte) = 0;
59     virtual         ~ByteEncoder () = 0;
60 };
61 
62 ByteEncoder::~ByteEncoder ()
63 { /* dont need this, but the C50 does */ }
64 
65 /* HexEncoder */
66 
67 class HexEncoder : public ByteEncoder
68 {
69 private:
70 
71     osl::File*      mpFile;
72     sal_uInt32      mnColumn;
73     sal_uInt32      mnOffset;
74     sal_Char        mpFileBuffer[nBufferSize + 16];
75 
76                     HexEncoder (); /* dont use */
77 
78 public:
79 
80     HexEncoder (osl::File* pFile);
81     virtual         ~HexEncoder ();
82     void            WriteAscii (sal_uInt8 nByte);
83     virtual void    EncodeByte (sal_uInt8 nByte);
84     void            FlushLine ();
85 };
86 
87 HexEncoder::HexEncoder (osl::File* pFile) :
88         mpFile (pFile),
89         mnColumn (0),
90         mnOffset (0)
91 {}
92 
93 HexEncoder::~HexEncoder ()
94 {
95     FlushLine ();
96     if (mnColumn > 0)
97         WritePS (mpFile, "\n");
98 }
99 
100 void
101 HexEncoder::WriteAscii (sal_uInt8 nByte)
102 {
103     sal_uInt32 nOff = psp::getHexValueOf (nByte, mpFileBuffer + mnOffset);
104     mnColumn += nOff;
105     mnOffset += nOff;
106 
107     if (mnColumn >= nLineLength)
108     {
109         mnOffset += psp::appendStr ("\n", mpFileBuffer + mnOffset);
110         mnColumn = 0;
111     }
112     if (mnOffset >= nBufferSize)
113         FlushLine ();
114 }
115 
116 void
117 HexEncoder::EncodeByte (sal_uInt8 nByte)
118 {
119     WriteAscii (nByte);
120 }
121 
122 void
123 HexEncoder::FlushLine ()
124 {
125     if (mnOffset > 0)
126     {
127         WritePS (mpFile, mpFileBuffer, mnOffset);
128         mnOffset = 0;
129     }
130 }
131 
132 /* Ascii85 encoder, is abi compatible with HexEncoder but writes a ~> to
133    indicate end of data EOD */
134 
135 class Ascii85Encoder : public ByteEncoder
136 {
137 private:
138 
139     osl::File*      mpFile;
140     sal_uInt32      mnByte;
141     sal_uInt8       mpByteBuffer[4];
142 
143     sal_uInt32      mnColumn;
144     sal_uInt32      mnOffset;
145     sal_Char        mpFileBuffer[nBufferSize + 16];
146 
147     Ascii85Encoder (); /* dont use */
148 
149     inline void     PutByte (sal_uInt8 nByte);
150     inline void     PutEOD ();
151     void            ConvertToAscii85 ();
152     void            FlushLine ();
153 
154 public:
155 
156     Ascii85Encoder (osl::File* pFile);
157     virtual         ~Ascii85Encoder ();
158     virtual void    EncodeByte (sal_uInt8 nByte);
159     void            WriteAscii (sal_uInt8 nByte);
160 };
161 
162 Ascii85Encoder::Ascii85Encoder (osl::File* pFile) :
163         mpFile (pFile),
164         mnByte (0),
165         mnColumn (0),
166         mnOffset (0)
167 {}
168 
169 inline void
170 Ascii85Encoder::PutByte (sal_uInt8 nByte)
171 {
172     mpByteBuffer [mnByte++] = nByte;
173 }
174 
175 inline void
176 Ascii85Encoder::PutEOD ()
177 {
178     WritePS (mpFile, "~>\n");
179 }
180 
181 void
182 Ascii85Encoder::ConvertToAscii85 ()
183 {
184     if (mnByte < 4)
185         std::memset (mpByteBuffer + mnByte, 0, (4 - mnByte) * sizeof(sal_uInt8));
186 
187     sal_uInt32 nByteValue =   mpByteBuffer[0] * 256 * 256 * 256
188         + mpByteBuffer[1] * 256 * 256
189         + mpByteBuffer[2] * 256
190         + mpByteBuffer[3];
191 
192     if (nByteValue == 0 && mnByte == 4)
193     {
194         /* special case of 4 Bytes in row */
195         mpFileBuffer [mnOffset] = 'z';
196 
197         mnOffset += 1;
198         mnColumn += 1;
199     }
200     else
201     {
202         /* real ascii85 encoding */
203         mpFileBuffer [mnOffset + 4] = (nByteValue % 85) + 33;
204         nByteValue /= 85;
205         mpFileBuffer [mnOffset + 3] = (nByteValue % 85) + 33;
206         nByteValue /= 85;
207         mpFileBuffer [mnOffset + 2] = (nByteValue % 85) + 33;
208         nByteValue /= 85;
209         mpFileBuffer [mnOffset + 1] = (nByteValue % 85) + 33;
210         nByteValue /= 85;
211         mpFileBuffer [mnOffset + 0] = (nByteValue % 85) + 33;
212 
213         mnColumn += (mnByte + 1);
214         mnOffset += (mnByte + 1);
215 
216         /* insert a newline if necessary */
217         if (mnColumn > nLineLength)
218         {
219             sal_uInt32 nEolOff = mnColumn - nLineLength;
220             sal_uInt32 nBufOff = mnOffset - nEolOff;
221 
222             std::memmove (mpFileBuffer + nBufOff + 1, mpFileBuffer + nBufOff, nEolOff);
223             mpFileBuffer[ nBufOff ] = '\n';
224 
225             mnOffset++;
226             mnColumn = nEolOff;
227         }
228     }
229 
230     mnByte = 0;
231 }
232 
233 void
234 Ascii85Encoder::WriteAscii (sal_uInt8 nByte)
235 {
236     PutByte (nByte);
237     if (mnByte == 4)
238         ConvertToAscii85 ();
239 
240     if (mnColumn >= nLineLength)
241     {
242         mnOffset += psp::appendStr ("\n", mpFileBuffer + mnOffset);
243         mnColumn = 0;
244     }
245     if (mnOffset >= nBufferSize)
246         FlushLine ();
247 }
248 
249 void
250 Ascii85Encoder::EncodeByte (sal_uInt8 nByte)
251 {
252     WriteAscii (nByte);
253 }
254 
255 void
256 Ascii85Encoder::FlushLine ()
257 {
258     if (mnOffset > 0)
259     {
260         WritePS (mpFile, mpFileBuffer, mnOffset);
261         mnOffset = 0;
262     }
263 }
264 
265 Ascii85Encoder::~Ascii85Encoder ()
266 {
267     if (mnByte > 0)
268         ConvertToAscii85 ();
269     if (mnOffset > 0)
270         FlushLine ();
271     PutEOD ();
272 }
273 
274 /* LZW encoder */
275 
276 class LZWEncoder : public Ascii85Encoder
277 {
278 private:
279 
280     struct LZWCTreeNode
281     {
282         LZWCTreeNode*   mpBrother;      // next node with same parent
283         LZWCTreeNode*   mpFirstChild;   // first son
284         sal_uInt16      mnCode;         // code for the string
285         sal_uInt16      mnValue;        // pixelvalue
286     };
287 
288     LZWCTreeNode*   mpTable;    // LZW compression data
289     LZWCTreeNode*   mpPrefix;   // the compression is as same as the TIFF compression
290     sal_uInt16      mnDataSize;
291     sal_uInt16      mnClearCode;
292     sal_uInt16      mnEOICode;
293     sal_uInt16      mnTableSize;
294     sal_uInt16      mnCodeSize;
295     sal_uInt32      mnOffset;
296     sal_uInt32      mdwShift;
297 
298     LZWEncoder ();
299     void            WriteBits (sal_uInt16 nCode, sal_uInt16 nCodeLen);
300 
301 public:
302 
303     LZWEncoder (osl::File* pOutputFile);
304     ~LZWEncoder ();
305 
306     virtual void    EncodeByte (sal_uInt8 nByte);
307 };
308 
309 LZWEncoder::LZWEncoder(osl::File* pOutputFile) :
310         Ascii85Encoder (pOutputFile)
311 {
312     mnDataSize  = 8;
313 
314     mnClearCode = 1 << mnDataSize;
315     mnEOICode   = mnClearCode + 1;
316     mnTableSize = mnEOICode   + 1;
317     mnCodeSize  = mnDataSize  + 1;
318 
319     mnOffset    = 32;   // free bits in dwShift
320     mdwShift    = 0;
321 
322     mpTable = new LZWCTreeNode[ 4096 ];
323 
324     for (sal_uInt32 i = 0; i < 4096; i++)
325     {
326         mpTable[i].mpBrother    = NULL;
327         mpTable[i].mpFirstChild = NULL;
328         mpTable[i].mnCode       = i;
329         mpTable[i].mnValue      = (sal_uInt8)mpTable[i].mnCode;
330     }
331 
332     mpPrefix = NULL;
333 
334     WriteBits( mnClearCode, mnCodeSize );
335 }
336 
337 LZWEncoder::~LZWEncoder()
338 {
339     if (mpPrefix)
340         WriteBits (mpPrefix->mnCode, mnCodeSize);
341 
342     WriteBits (mnEOICode, mnCodeSize);
343 
344     delete[] mpTable;
345 }
346 
347 void
348 LZWEncoder::WriteBits (sal_uInt16 nCode, sal_uInt16 nCodeLen)
349 {
350     mdwShift |= (nCode << (mnOffset - nCodeLen));
351     mnOffset -= nCodeLen;
352     while (mnOffset < 24)
353     {
354         WriteAscii ((sal_uInt8)(mdwShift >> 24));
355         mdwShift <<= 8;
356         mnOffset += 8;
357     }
358     if (nCode == 257 && mnOffset != 32)
359         WriteAscii ((sal_uInt8)(mdwShift >> 24));
360 }
361 
362 void
363 LZWEncoder::EncodeByte (sal_uInt8 nByte )
364 {
365     LZWCTreeNode*   p;
366     sal_uInt16      i;
367     sal_uInt8       nV;
368 
369     if (!mpPrefix)
370     {
371         mpPrefix = mpTable + nByte;
372     }
373     else
374     {
375         nV = nByte;
376         for (p = mpPrefix->mpFirstChild; p != NULL; p = p->mpBrother)
377         {
378             if (p->mnValue == nV)
379                 break;
380         }
381 
382         if (p != NULL)
383         {
384             mpPrefix = p;
385         }
386         else
387         {
388             WriteBits (mpPrefix->mnCode, mnCodeSize);
389 
390             if (mnTableSize == 409)
391             {
392                 WriteBits (mnClearCode, mnCodeSize);
393 
394                 for (i = 0; i < mnClearCode; i++)
395                     mpTable[i].mpFirstChild = NULL;
396 
397                 mnCodeSize = mnDataSize + 1;
398                 mnTableSize = mnEOICode + 1;
399             }
400             else
401             {
402                 if(mnTableSize == (sal_uInt16)((1 << mnCodeSize) - 1))
403                     mnCodeSize++;
404 
405                 p = mpTable + (mnTableSize++);
406                 p->mpBrother = mpPrefix->mpFirstChild;
407                 mpPrefix->mpFirstChild = p;
408                 p->mnValue = nV;
409                 p->mpFirstChild = NULL;
410             }
411 
412             mpPrefix = mpTable + nV;
413         }
414     }
415 }
416 
417 /*
418  *
419  * bitmap handling routines
420  *
421  */
422 
423 void
424 PrinterGfx::DrawBitmap (const Rectangle& rDest, const Rectangle& rSrc,
425                         const PrinterBmp& rBitmap)
426 {
427     double fScaleX = (double)rDest.GetWidth() / (double)rSrc.GetWidth();
428     double fScaleY = (double)rDest.GetHeight() / (double)rSrc.GetHeight();
429 
430     PSGSave ();
431     PSTranslate (rDest.BottomLeft());
432     PSScale (fScaleX, fScaleY);
433 
434     if (mnPSLevel >= 2)
435     {
436         if (rBitmap.GetDepth() == 1)
437         {
438             DrawPS2MonoImage (rBitmap, rSrc);
439         }
440         else
441         if (rBitmap.GetDepth() ==  8 && mbColor)
442         {
443             // if the palette is larger than the image itself print it as a truecolor
444             // image to save diskspace. This is important for printing transparent
445             // bitmaps that are disassembled into small pieces
446             sal_Int32 nImageSz   = rSrc.GetWidth() * rSrc.GetHeight();
447             sal_Int32 nPaletteSz = rBitmap.GetPaletteEntryCount();
448             if ((nImageSz < nPaletteSz) || (nImageSz < 24) )
449         	    DrawPS2TrueColorImage (rBitmap, rSrc);
450             else
451                 DrawPS2PaletteImage (rBitmap, rSrc);
452         }
453         else
454         if (rBitmap.GetDepth() == 24 && mbColor)
455         {
456         	DrawPS2TrueColorImage (rBitmap, rSrc);
457         }
458         else
459         {
460         	DrawPS2GrayImage (rBitmap, rSrc);
461         }
462     }
463     else
464     {
465         DrawPS1GrayImage (rBitmap, rSrc);
466     }
467 
468     PSGRestore ();
469 }
470 
471 /* XXX does not work XXX */
472 void
473 PrinterGfx::DrawBitmap (const Rectangle& rDest, const Rectangle& rSrc,
474                         const PrinterBmp& /*rBitmap*/, const PrinterBmp& /*rTransBitmap*/)
475 {
476     double fScaleX = (double)rDest.GetWidth() / (double)rSrc.GetWidth();
477     double fScaleY = (double)rDest.GetHeight() / (double)rSrc.GetHeight();
478 
479     PSGSave ();
480     PSTranslate (rDest.BottomLeft());
481     PSScale (fScaleX, fScaleY);
482     PSGRestore ();
483 }
484 
485 /* XXX does not work XXX */
486 void
487 PrinterGfx::DrawMask   (const Rectangle& rDest, const Rectangle& rSrc,
488                         const PrinterBmp &/*rBitmap*/, PrinterColor& /*rMaskColor*/)
489 {
490     double fScaleX = (double)rDest.GetWidth() / (double)rSrc.GetWidth();
491     double fScaleY = (double)rDest.GetHeight() / (double)rSrc.GetHeight();
492 
493     PSGSave ();
494     PSTranslate (rDest.BottomLeft());
495     PSScale (fScaleX, fScaleY);
496     PSGRestore ();
497 }
498 
499 /*
500  *
501  * Implementation: PS Level 1
502  *
503  */
504 
505 void
506 PrinterGfx::DrawPS1GrayImage (const PrinterBmp& rBitmap, const Rectangle& rArea)
507 {
508     sal_uInt32 nWidth  = rArea.GetWidth();
509     sal_uInt32 nHeight = rArea.GetHeight();
510 
511     sal_Char  pGrayImage [512];
512     sal_Int32 nChar = 0;
513 
514     // image header
515     nChar += psp::getValueOf (nWidth,                           pGrayImage + nChar);
516     nChar += psp::appendStr  (" ",                              pGrayImage + nChar);
517     nChar += psp::getValueOf (nHeight,                          pGrayImage + nChar);
518     nChar += psp::appendStr  (" 8 ",                            pGrayImage + nChar);
519     nChar += psp::appendStr  ("[ 1 0 0 1 0 ",                   pGrayImage + nChar);
520     nChar += psp::getValueOf (nHeight,                          pGrayImage + nChar);
521     nChar += psp::appendStr  ("]",                              pGrayImage + nChar);
522     nChar += psp::appendStr  (" {currentfile ",                 pGrayImage + nChar);
523     nChar += psp::getValueOf (nWidth,                           pGrayImage + nChar);
524     nChar += psp::appendStr  (" string readhexstring pop}\n",   pGrayImage + nChar);
525     nChar += psp::appendStr  ("image\n",                        pGrayImage + nChar);
526 
527     WritePS (mpPageBody, pGrayImage);
528 
529     // image body
530     HexEncoder* pEncoder = new HexEncoder (mpPageBody);
531 
532     for (long nRow = rArea.Top(); nRow <= rArea.Bottom(); nRow++)
533     {
534         for (long nColumn = rArea.Left(); nColumn <= rArea.Right(); nColumn++)
535         {
536             sal_uChar nByte = rBitmap.GetPixelGray (nRow, nColumn);
537             pEncoder->EncodeByte (nByte);
538         }
539     }
540 
541     delete pEncoder;
542 
543     WritePS (mpPageBody, "\n");
544 }
545 
546 /*
547  *
548  * Implementation: PS Level 2
549  *
550  */
551 
552 void
553 PrinterGfx::writePS2ImageHeader (const Rectangle& rArea, psp::ImageType nType)
554 {
555     sal_Int32 nChar = 0;
556     sal_Char  pImage [512];
557 
558     sal_Int32 nDictType = 0;
559     switch (nType)
560     {
561         case psp::TrueColorImage:  nDictType = 0; break;
562         case psp::PaletteImage:    nDictType = 1; break;
563         case psp::GrayScaleImage:  nDictType = 2; break;
564         case psp::MonochromeImage: nDictType = 3; break;
565         default: break;
566     }
567     sal_Int32 nCompressType = mbCompressBmp ? 1 : 0;
568 
569     nChar += psp::getValueOf (rArea.GetWidth(),  pImage + nChar);
570     nChar += psp::appendStr  (" ",               pImage + nChar);
571     nChar += psp::getValueOf (rArea.GetHeight(), pImage + nChar);
572     nChar += psp::appendStr  (" ",               pImage + nChar);
573     nChar += psp::getValueOf (nDictType,         pImage + nChar);
574     nChar += psp::appendStr  (" ",               pImage + nChar);
575     nChar += psp::getValueOf (nCompressType,     pImage + nChar);
576     nChar += psp::appendStr  (" psp_imagedict image\n", pImage + nChar);
577 
578     WritePS (mpPageBody, pImage);
579 }
580 
581 void
582 PrinterGfx::writePS2Colorspace(const PrinterBmp& rBitmap, psp::ImageType nType)
583 {
584     switch (nType)
585     {
586         case psp::GrayScaleImage:
587 
588             WritePS (mpPageBody, "/DeviceGray setcolorspace\n");
589             break;
590 
591         case psp::TrueColorImage:
592 
593             WritePS (mpPageBody, "/DeviceRGB setcolorspace\n");
594             break;
595 
596         case psp::MonochromeImage:
597         case psp::PaletteImage:
598         {
599 
600             sal_Int32 nChar = 0;
601             sal_Char  pImage [4096];
602 
603             const sal_uInt32 nSize = rBitmap.GetPaletteEntryCount();
604 
605             nChar += psp::appendStr ("[/Indexed /DeviceRGB ", pImage + nChar);
606             nChar += psp::getValueOf (nSize - 1, pImage + nChar);
607             if (mbCompressBmp)
608                 nChar += psp::appendStr ("\npsp_lzwstring\n", pImage + nChar);
609             else
610                 nChar += psp::appendStr ("\npsp_ascii85string\n", pImage + nChar);
611             WritePS (mpPageBody, pImage);
612 
613             ByteEncoder* pEncoder = mbCompressBmp ? new LZWEncoder(mpPageBody)
614                                                   : new Ascii85Encoder(mpPageBody);
615             for (sal_uInt32 i = 0; i < nSize; i++)
616             {
617                 PrinterColor aColor = rBitmap.GetPaletteColor(i);
618 
619                 pEncoder->EncodeByte (aColor.GetRed());
620                 pEncoder->EncodeByte (aColor.GetGreen());
621                 pEncoder->EncodeByte (aColor.GetBlue());
622             }
623             delete pEncoder;
624 
625             WritePS (mpPageBody, "pop ] setcolorspace\n");
626         }
627         break;
628         default: break;
629     }
630 }
631 
632 void
633 PrinterGfx::DrawPS2GrayImage (const PrinterBmp& rBitmap, const Rectangle& rArea)
634 {
635     writePS2Colorspace(rBitmap, psp::GrayScaleImage);
636     writePS2ImageHeader(rArea, psp::GrayScaleImage);
637 
638     ByteEncoder* pEncoder = mbCompressBmp ? new LZWEncoder(mpPageBody)
639                                           : new Ascii85Encoder(mpPageBody);
640 
641     for (long nRow = rArea.Top(); nRow <= rArea.Bottom(); nRow++)
642     {
643         for (long nColumn = rArea.Left(); nColumn <= rArea.Right(); nColumn++)
644         {
645             sal_uChar nByte = rBitmap.GetPixelGray (nRow, nColumn);
646             pEncoder->EncodeByte (nByte);
647         }
648     }
649 
650     delete pEncoder;
651 }
652 
653 void
654 PrinterGfx::DrawPS2MonoImage (const PrinterBmp& rBitmap, const Rectangle& rArea)
655 {
656     writePS2Colorspace(rBitmap, psp::MonochromeImage);
657     writePS2ImageHeader(rArea, psp::MonochromeImage);
658 
659     ByteEncoder* pEncoder = mbCompressBmp ? new LZWEncoder(mpPageBody)
660                                           : new Ascii85Encoder(mpPageBody);
661 
662     for (long nRow = rArea.Top(); nRow <= rArea.Bottom(); nRow++)
663     {
664         long      nBitPos = 0;
665         sal_uChar nBit    = 0;
666         sal_uChar nByte   = 0;
667 
668         for (long nColumn = rArea.Left(); nColumn <= rArea.Right(); nColumn++)
669         {
670             nBit   = rBitmap.GetPixelIdx (nRow, nColumn);
671             nByte |= nBit << (7 - nBitPos);
672 
673             if (++nBitPos == 8)
674             {
675                 pEncoder->EncodeByte (nByte);
676                 nBitPos = 0;
677                 nByte   = 0;
678             }
679         }
680         // keep the row byte aligned
681         if (nBitPos != 0)
682             pEncoder->EncodeByte (nByte);
683     }
684 
685     delete pEncoder;
686 }
687 
688 void
689 PrinterGfx::DrawPS2PaletteImage (const PrinterBmp& rBitmap, const Rectangle& rArea)
690 {
691     writePS2Colorspace(rBitmap, psp::PaletteImage);
692     writePS2ImageHeader(rArea, psp::PaletteImage);
693 
694     ByteEncoder* pEncoder = mbCompressBmp ? new LZWEncoder(mpPageBody)
695                                           : new Ascii85Encoder(mpPageBody);
696 
697     for (long nRow = rArea.Top(); nRow <= rArea.Bottom(); nRow++)
698     {
699         for (long nColumn = rArea.Left(); nColumn <= rArea.Right(); nColumn++)
700         {
701             sal_uChar nByte = rBitmap.GetPixelIdx (nRow, nColumn);
702             pEncoder->EncodeByte (nByte);
703         }
704     }
705 
706     delete pEncoder;
707 }
708 
709 void
710 PrinterGfx::DrawPS2TrueColorImage (const PrinterBmp& rBitmap, const Rectangle& rArea)
711 {
712     writePS2Colorspace(rBitmap, psp::TrueColorImage);
713     writePS2ImageHeader(rArea, psp::TrueColorImage);
714 
715     ByteEncoder* pEncoder = mbCompressBmp ? new LZWEncoder(mpPageBody)
716                                           : new Ascii85Encoder(mpPageBody);
717 
718     for (long nRow = rArea.Top(); nRow <= rArea.Bottom(); nRow++)
719     {
720         for (long nColumn = rArea.Left(); nColumn <= rArea.Right(); nColumn++)
721         {
722             PrinterColor aColor = rBitmap.GetPixelRGB (nRow, nColumn);
723             pEncoder->EncodeByte (aColor.GetRed());
724             pEncoder->EncodeByte (aColor.GetGreen());
725             pEncoder->EncodeByte (aColor.GetBlue());
726         }
727     }
728 
729     delete pEncoder;
730 }
731 
732 } /* namespace psp */
733