1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
22 #include "psputil.hxx"
24 #include <unx/printergfx.hxx>
28 const sal_uInt32 nLineLength
= 80;
29 const sal_uInt32 nBufferSize
= 16384;
33 * Bitmap compression / Hex encoding / Ascii85 Encoding
37 PrinterBmp::~PrinterBmp()
41 /* virtual base class */
49 virtual void EncodeByte (sal_uInt8 nByte
) = 0;
50 virtual ~ByteEncoder () = 0;
53 ByteEncoder::~ByteEncoder()
59 class HexEncoder
: public ByteEncoder
63 osl::File
* const mpFile
;
66 OStringBuffer mpFileBuffer
;
70 explicit HexEncoder (osl::File
* pFile
);
71 virtual ~HexEncoder () override
;
72 void WriteAscii (sal_uInt8 nByte
);
73 virtual void EncodeByte (sal_uInt8 nByte
) override
;
77 HexEncoder::HexEncoder (osl::File
* pFile
) :
83 HexEncoder::~HexEncoder ()
87 WritePS (mpFile
, "\n");
91 HexEncoder::WriteAscii (sal_uInt8 nByte
)
93 sal_uInt32 nOff
= psp::getHexValueOf (nByte
, mpFileBuffer
);
97 if (mnColumn
>= nLineLength
)
99 mnOffset
+= psp::appendStr ("\n", mpFileBuffer
);
102 if (mnOffset
>= nBufferSize
)
107 HexEncoder::EncodeByte (sal_uInt8 nByte
)
113 HexEncoder::FlushLine ()
117 WritePS (mpFile
, mpFileBuffer
.makeStringAndClear());
122 /* Ascii85 encoder, is abi compatible with HexEncoder but writes a ~> to
123 indicate end of data EOD */
125 class Ascii85Encoder
: public ByteEncoder
129 osl::File
* const mpFile
;
131 sal_uInt8 mpByteBuffer
[4];
135 OStringBuffer mpFileBuffer
;
137 inline void PutByte (sal_uInt8 nByte
);
138 inline void PutEOD ();
139 void ConvertToAscii85 ();
144 explicit Ascii85Encoder (osl::File
* pFile
);
145 virtual ~Ascii85Encoder () override
;
146 virtual void EncodeByte (sal_uInt8 nByte
) override
;
147 void WriteAscii (sal_uInt8 nByte
);
150 Ascii85Encoder::Ascii85Encoder (osl::File
* pFile
) :
158 Ascii85Encoder::PutByte (sal_uInt8 nByte
)
160 mpByteBuffer
[mnByte
++] = nByte
;
164 Ascii85Encoder::PutEOD ()
166 WritePS (mpFile
, "~>\n");
170 Ascii85Encoder::ConvertToAscii85 ()
172 // Add (4 - mnByte) zero padding bytes:
174 std::memset (mpByteBuffer
+ mnByte
, 0, (4 - mnByte
) * sizeof(sal_uInt8
));
176 sal_uInt32 nByteValue
= mpByteBuffer
[0] * 256 * 256 * 256
177 + mpByteBuffer
[1] * 256 * 256
178 + mpByteBuffer
[2] * 256
181 if (nByteValue
== 0 && mnByte
== 4)
183 /* special case of 4 Bytes in row */
184 mpFileBuffer
.append('z');
191 /* real ascii85 encoding */
193 // Of the up to 5 characters to be generated, do not generate the last (4 - mnByte) ones
194 // that correspond to the (4 - mnByte) zero padding bytes added to the input:
196 auto const pos
= mpFileBuffer
.getLength();
198 mpFileBuffer
.insert(pos
, char((nByteValue
% 85) + 33));
202 mpFileBuffer
.insert(pos
, char((nByteValue
% 85) + 33));
206 mpFileBuffer
.insert(pos
, char((nByteValue
% 85) + 33));
209 mpFileBuffer
.insert(pos
, char((nByteValue
% 85) + 33));
211 mpFileBuffer
.insert(pos
, char((nByteValue
% 85) + 33));
213 mnColumn
+= (mnByte
+ 1);
214 mnOffset
+= (mnByte
+ 1);
216 /* insert a newline if necessary */
217 if (mnColumn
> nLineLength
)
219 sal_uInt32 nEolOff
= mnColumn
- nLineLength
;
220 auto const posNl
= pos
+ (mnByte
+ 1) - nEolOff
;
222 mpFileBuffer
.insert(posNl
, '\n');
233 Ascii85Encoder::WriteAscii (sal_uInt8 nByte
)
239 if (mnColumn
>= nLineLength
)
241 mnOffset
+= psp::appendStr ("\n", mpFileBuffer
);
244 if (mnOffset
>= nBufferSize
)
249 Ascii85Encoder::EncodeByte (sal_uInt8 nByte
)
255 Ascii85Encoder::FlushLine ()
259 WritePS (mpFile
, mpFileBuffer
.makeStringAndClear());
264 Ascii85Encoder::~Ascii85Encoder ()
275 class LZWEncoder
: public Ascii85Encoder
281 LZWCTreeNode
* mpBrother
; // next node with same parent
282 LZWCTreeNode
* mpFirstChild
; // first son
283 sal_uInt16 mnCode
; // code for the string
284 sal_uInt16 mnValue
; // pixelvalue
287 std::array
<LZWCTreeNode
, 4096>
288 mpTable
; // LZW compression data
289 LZWCTreeNode
* mpPrefix
; // the compression is as same as the TIFF compression
290 static constexpr sal_uInt16 gnDataSize
= 8;
291 static constexpr sal_uInt16 gnClearCode
= 1 << gnDataSize
;
292 static constexpr sal_uInt16 gnEOICode
= gnClearCode
+ 1;
293 sal_uInt16 mnTableSize
;
294 sal_uInt16 mnCodeSize
;
298 void WriteBits (sal_uInt16 nCode
, sal_uInt16 nCodeLen
);
302 explicit LZWEncoder (osl::File
* pOutputFile
);
303 virtual ~LZWEncoder () override
;
305 virtual void EncodeByte (sal_uInt8 nByte
) override
;
308 LZWEncoder::LZWEncoder(osl::File
* pOutputFile
) :
309 Ascii85Encoder (pOutputFile
),
311 mnTableSize(gnEOICode
+ 1),
312 mnCodeSize(gnDataSize
+ 1),
313 mnOffset(32), // free bits in dwShift
316 for (sal_uInt32 i
= 0; i
< 4096; i
++)
318 mpTable
[i
].mpBrother
= nullptr;
319 mpTable
[i
].mpFirstChild
= nullptr;
320 mpTable
[i
].mnCode
= i
;
321 mpTable
[i
].mnValue
= static_cast<sal_uInt8
>(mpTable
[i
].mnCode
);
324 WriteBits( gnClearCode
, mnCodeSize
);
327 LZWEncoder::~LZWEncoder()
330 WriteBits (mpPrefix
->mnCode
, mnCodeSize
);
332 WriteBits (gnEOICode
, mnCodeSize
);
336 LZWEncoder::WriteBits (sal_uInt16 nCode
, sal_uInt16 nCodeLen
)
338 mdwShift
|= (nCode
<< (mnOffset
- nCodeLen
));
339 mnOffset
-= nCodeLen
;
340 while (mnOffset
< 24)
342 WriteAscii (static_cast<sal_uInt8
>(mdwShift
>> 24));
346 if (nCode
== 257 && mnOffset
!= 32)
347 WriteAscii (static_cast<sal_uInt8
>(mdwShift
>> 24));
351 LZWEncoder::EncodeByte (sal_uInt8 nByte
)
359 mpPrefix
= mpTable
.data() + nByte
;
364 for (p
= mpPrefix
->mpFirstChild
; p
!= nullptr; p
= p
->mpBrother
)
366 if (p
->mnValue
== nV
)
376 WriteBits (mpPrefix
->mnCode
, mnCodeSize
);
378 if (mnTableSize
== 409)
380 WriteBits (gnClearCode
, mnCodeSize
);
382 for (i
= 0; i
< gnClearCode
; i
++)
383 mpTable
[i
].mpFirstChild
= nullptr;
385 mnCodeSize
= gnDataSize
+ 1;
386 mnTableSize
= gnEOICode
+ 1;
390 if(mnTableSize
== static_cast<sal_uInt16
>((1 << mnCodeSize
) - 1))
393 p
= mpTable
.data() + (mnTableSize
++);
394 p
->mpBrother
= mpPrefix
->mpFirstChild
;
395 mpPrefix
->mpFirstChild
= p
;
397 p
->mpFirstChild
= nullptr;
400 mpPrefix
= mpTable
.data() + nV
;
407 * bitmap handling routines
412 PrinterGfx::DrawBitmap (const tools::Rectangle
& rDest
, const tools::Rectangle
& rSrc
,
413 const PrinterBmp
& rBitmap
)
415 double fScaleX
= static_cast<double>(rDest
.GetWidth());
416 double fScaleY
= static_cast<double>(rDest
.GetHeight());
417 if(rSrc
.GetWidth() > 0)
419 fScaleX
= static_cast<double>(rDest
.GetWidth()) / static_cast<double>(rSrc
.GetWidth());
421 if(rSrc
.GetHeight() > 0)
423 fScaleY
= static_cast<double>(rDest
.GetHeight()) / static_cast<double>(rSrc
.GetHeight());
426 PSTranslate (rDest
.BottomLeft());
427 PSScale (fScaleX
, fScaleY
);
431 if (rBitmap
.GetDepth() == 1)
433 DrawPS2MonoImage (rBitmap
, rSrc
);
436 if (rBitmap
.GetDepth() == 8 && mbColor
)
438 // if the palette is larger than the image itself print it as a truecolor
439 // image to save diskspace. This is important for printing transparent
440 // bitmaps that are disassembled into small pieces
441 sal_Int32 nImageSz
= rSrc
.GetWidth() * rSrc
.GetHeight();
442 sal_Int32 nPaletteSz
= rBitmap
.GetPaletteEntryCount();
443 if ((nImageSz
< nPaletteSz
) || (nImageSz
< 24) )
444 DrawPS2TrueColorImage (rBitmap
, rSrc
);
446 DrawPS2PaletteImage (rBitmap
, rSrc
);
449 if (rBitmap
.GetDepth() == 24 && mbColor
)
451 DrawPS2TrueColorImage (rBitmap
, rSrc
);
455 DrawPS2GrayImage (rBitmap
, rSrc
);
460 DrawPS1GrayImage (rBitmap
, rSrc
);
468 * Implementation: PS Level 1
473 PrinterGfx::DrawPS1GrayImage (const PrinterBmp
& rBitmap
, const tools::Rectangle
& rArea
)
475 sal_uInt32 nWidth
= rArea
.GetWidth();
476 sal_uInt32 nHeight
= rArea
.GetHeight();
478 OStringBuffer pGrayImage
;
481 psp::getValueOf (nWidth
, pGrayImage
);
482 psp::appendStr (" ", pGrayImage
);
483 psp::getValueOf (nHeight
, pGrayImage
);
484 psp::appendStr (" 8 ", pGrayImage
);
485 psp::appendStr ("[ 1 0 0 1 0 ", pGrayImage
);
486 psp::getValueOf (nHeight
, pGrayImage
);
487 psp::appendStr ("]", pGrayImage
);
488 psp::appendStr (" {currentfile ", pGrayImage
);
489 psp::getValueOf (nWidth
, pGrayImage
);
490 psp::appendStr (" string readhexstring pop}\n", pGrayImage
);
491 psp::appendStr ("image\n", pGrayImage
);
493 WritePS (mpPageBody
, pGrayImage
.makeStringAndClear());
496 std::unique_ptr
<HexEncoder
> xEncoder(new HexEncoder (mpPageBody
));
498 for (long nRow
= rArea
.Top(); nRow
<= rArea
.Bottom(); nRow
++)
500 for (long nColumn
= rArea
.Left(); nColumn
<= rArea
.Right(); nColumn
++)
502 unsigned char nByte
= rBitmap
.GetPixelGray (nRow
, nColumn
);
503 xEncoder
->EncodeByte (nByte
);
509 WritePS (mpPageBody
, "\n");
514 * Implementation: PS Level 2
519 PrinterGfx::writePS2ImageHeader (const tools::Rectangle
& rArea
, psp::ImageType nType
)
521 OStringBuffer pImage
;
523 sal_Int32 nDictType
= 0;
526 case psp::ImageType::TrueColorImage
: nDictType
= 0; break;
527 case psp::ImageType::PaletteImage
: nDictType
= 1; break;
528 case psp::ImageType::GrayScaleImage
: nDictType
= 2; break;
529 case psp::ImageType::MonochromeImage
: nDictType
= 3; break;
533 psp::getValueOf (rArea
.GetWidth(), pImage
);
534 psp::appendStr (" ", pImage
);
535 psp::getValueOf (rArea
.GetHeight(), pImage
);
536 psp::appendStr (" ", pImage
);
537 psp::getValueOf (nDictType
, pImage
);
538 psp::appendStr (" ", pImage
);
539 psp::getValueOf (sal_Int32(1), pImage
); // nCompressType
540 psp::appendStr (" psp_imagedict image\n", pImage
);
542 WritePS (mpPageBody
, pImage
.makeStringAndClear());
546 PrinterGfx::writePS2Colorspace(const PrinterBmp
& rBitmap
, psp::ImageType nType
)
550 case psp::ImageType::GrayScaleImage
:
552 WritePS (mpPageBody
, "/DeviceGray setcolorspace\n");
555 case psp::ImageType::TrueColorImage
:
557 WritePS (mpPageBody
, "/DeviceRGB setcolorspace\n");
560 case psp::ImageType::MonochromeImage
:
561 case psp::ImageType::PaletteImage
:
564 OStringBuffer pImage
;
566 const sal_uInt32 nSize
= rBitmap
.GetPaletteEntryCount();
568 psp::appendStr ("[/Indexed /DeviceRGB ", pImage
);
569 psp::getValueOf (nSize
- 1, pImage
);
570 psp::appendStr ("\npsp_lzwstring\n", pImage
);
571 WritePS (mpPageBody
, pImage
.makeStringAndClear());
573 std::unique_ptr
<ByteEncoder
> xEncoder(new LZWEncoder(mpPageBody
));
574 for (sal_uInt32 i
= 0; i
< nSize
; i
++)
576 PrinterColor aColor
= rBitmap
.GetPaletteColor(i
);
578 xEncoder
->EncodeByte (aColor
.GetRed());
579 xEncoder
->EncodeByte (aColor
.GetGreen());
580 xEncoder
->EncodeByte (aColor
.GetBlue());
584 WritePS (mpPageBody
, "pop ] setcolorspace\n");
592 PrinterGfx::DrawPS2GrayImage (const PrinterBmp
& rBitmap
, const tools::Rectangle
& rArea
)
594 writePS2Colorspace(rBitmap
, psp::ImageType::GrayScaleImage
);
595 writePS2ImageHeader(rArea
, psp::ImageType::GrayScaleImage
);
597 std::unique_ptr
<ByteEncoder
> xEncoder(new LZWEncoder(mpPageBody
));
599 for (long nRow
= rArea
.Top(); nRow
<= rArea
.Bottom(); nRow
++)
601 for (long nColumn
= rArea
.Left(); nColumn
<= rArea
.Right(); nColumn
++)
603 unsigned char nByte
= rBitmap
.GetPixelGray (nRow
, nColumn
);
604 xEncoder
->EncodeByte (nByte
);
610 PrinterGfx::DrawPS2MonoImage (const PrinterBmp
& rBitmap
, const tools::Rectangle
& rArea
)
612 writePS2Colorspace(rBitmap
, psp::ImageType::MonochromeImage
);
613 writePS2ImageHeader(rArea
, psp::ImageType::MonochromeImage
);
615 std::unique_ptr
<ByteEncoder
> xEncoder(new LZWEncoder(mpPageBody
));
617 for (long nRow
= rArea
.Top(); nRow
<= rArea
.Bottom(); nRow
++)
620 unsigned char nByte
= 0;
622 for (long nColumn
= rArea
.Left(); nColumn
<= rArea
.Right(); nColumn
++)
624 unsigned char nBit
= rBitmap
.GetPixelIdx (nRow
, nColumn
);
625 nByte
|= nBit
<< (7 - nBitPos
);
629 xEncoder
->EncodeByte (nByte
);
634 // keep the row byte aligned
636 xEncoder
->EncodeByte (nByte
);
641 PrinterGfx::DrawPS2PaletteImage (const PrinterBmp
& rBitmap
, const tools::Rectangle
& rArea
)
643 writePS2Colorspace(rBitmap
, psp::ImageType::PaletteImage
);
644 writePS2ImageHeader(rArea
, psp::ImageType::PaletteImage
);
646 std::unique_ptr
<ByteEncoder
> xEncoder(new LZWEncoder(mpPageBody
));
648 for (long nRow
= rArea
.Top(); nRow
<= rArea
.Bottom(); nRow
++)
650 for (long nColumn
= rArea
.Left(); nColumn
<= rArea
.Right(); nColumn
++)
652 unsigned char nByte
= rBitmap
.GetPixelIdx (nRow
, nColumn
);
653 xEncoder
->EncodeByte (nByte
);
659 PrinterGfx::DrawPS2TrueColorImage (const PrinterBmp
& rBitmap
, const tools::Rectangle
& rArea
)
661 writePS2Colorspace(rBitmap
, psp::ImageType::TrueColorImage
);
662 writePS2ImageHeader(rArea
, psp::ImageType::TrueColorImage
);
664 std::unique_ptr
<ByteEncoder
> xEncoder(new LZWEncoder(mpPageBody
));
666 for (long nRow
= rArea
.Top(); nRow
<= rArea
.Bottom(); nRow
++)
668 for (long nColumn
= rArea
.Left(); nColumn
<= rArea
.Right(); nColumn
++)
670 PrinterColor aColor
= rBitmap
.GetPixelRGB (nRow
, nColumn
);
671 xEncoder
->EncodeByte (aColor
.GetRed());
672 xEncoder
->EncodeByte (aColor
.GetGreen());
673 xEncoder
->EncodeByte (aColor
.GetBlue());
678 } /* namespace psp */
680 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */