blob: 0e0f261d2d0dd40c365298bc4a6644ddd16d2de6 [file] [log] [blame]
/**************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_vcl.hxx"
#include "psputil.hxx"
#include "printergfx.hxx"
#include "vcl/strhelper.hxx"
namespace psp {
const sal_uInt32 nLineLength = 80;
const sal_uInt32 nBufferSize = 16384;
/*
*
* Bitmap compression / Hex encoding / Ascii85 Encoding
*
*/
PrinterBmp::~PrinterBmp ()
{ /* dont need this, but C50 does */ }
/* virtual base class */
class ByteEncoder
{
private:
public:
virtual void EncodeByte (sal_uInt8 nByte) = 0;
virtual ~ByteEncoder () = 0;
};
ByteEncoder::~ByteEncoder ()
{ /* dont need this, but the C50 does */ }
/* HexEncoder */
class HexEncoder : public ByteEncoder
{
private:
osl::File* mpFile;
sal_uInt32 mnColumn;
sal_uInt32 mnOffset;
sal_Char mpFileBuffer[nBufferSize + 16];
HexEncoder (); /* dont use */
public:
HexEncoder (osl::File* pFile);
virtual ~HexEncoder ();
void WriteAscii (sal_uInt8 nByte);
virtual void EncodeByte (sal_uInt8 nByte);
void FlushLine ();
};
HexEncoder::HexEncoder (osl::File* pFile) :
mpFile (pFile),
mnColumn (0),
mnOffset (0)
{}
HexEncoder::~HexEncoder ()
{
FlushLine ();
if (mnColumn > 0)
WritePS (mpFile, "\n");
}
void
HexEncoder::WriteAscii (sal_uInt8 nByte)
{
sal_uInt32 nOff = psp::getHexValueOf (nByte, mpFileBuffer + mnOffset);
mnColumn += nOff;
mnOffset += nOff;
if (mnColumn >= nLineLength)
{
mnOffset += psp::appendStr ("\n", mpFileBuffer + mnOffset);
mnColumn = 0;
}
if (mnOffset >= nBufferSize)
FlushLine ();
}
void
HexEncoder::EncodeByte (sal_uInt8 nByte)
{
WriteAscii (nByte);
}
void
HexEncoder::FlushLine ()
{
if (mnOffset > 0)
{
WritePS (mpFile, mpFileBuffer, mnOffset);
mnOffset = 0;
}
}
/* Ascii85 encoder, is abi compatible with HexEncoder but writes a ~> to
indicate end of data EOD */
class Ascii85Encoder : public ByteEncoder
{
private:
osl::File* mpFile;
sal_uInt32 mnByte;
sal_uInt8 mpByteBuffer[4];
sal_uInt32 mnColumn;
sal_uInt32 mnOffset;
sal_Char mpFileBuffer[nBufferSize + 16];
Ascii85Encoder (); /* dont use */
inline void PutByte (sal_uInt8 nByte);
inline void PutEOD ();
void ConvertToAscii85 ();
void FlushLine ();
public:
Ascii85Encoder (osl::File* pFile);
virtual ~Ascii85Encoder ();
virtual void EncodeByte (sal_uInt8 nByte);
void WriteAscii (sal_uInt8 nByte);
};
Ascii85Encoder::Ascii85Encoder (osl::File* pFile) :
mpFile (pFile),
mnByte (0),
mnColumn (0),
mnOffset (0)
{}
inline void
Ascii85Encoder::PutByte (sal_uInt8 nByte)
{
mpByteBuffer [mnByte++] = nByte;
}
inline void
Ascii85Encoder::PutEOD ()
{
WritePS (mpFile, "~>\n");
}
void
Ascii85Encoder::ConvertToAscii85 ()
{
if (mnByte < 4)
std::memset (mpByteBuffer + mnByte, 0, (4 - mnByte) * sizeof(sal_uInt8));
sal_uInt32 nByteValue = mpByteBuffer[0] * 256 * 256 * 256
+ mpByteBuffer[1] * 256 * 256
+ mpByteBuffer[2] * 256
+ mpByteBuffer[3];
if (nByteValue == 0 && mnByte == 4)
{
/* special case of 4 Bytes in row */
mpFileBuffer [mnOffset] = 'z';
mnOffset += 1;
mnColumn += 1;
}
else
{
/* real ascii85 encoding */
mpFileBuffer [mnOffset + 4] = (nByteValue % 85) + 33;
nByteValue /= 85;
mpFileBuffer [mnOffset + 3] = (nByteValue % 85) + 33;
nByteValue /= 85;
mpFileBuffer [mnOffset + 2] = (nByteValue % 85) + 33;
nByteValue /= 85;
mpFileBuffer [mnOffset + 1] = (nByteValue % 85) + 33;
nByteValue /= 85;
mpFileBuffer [mnOffset + 0] = (nByteValue % 85) + 33;
mnColumn += (mnByte + 1);
mnOffset += (mnByte + 1);
/* insert a newline if necessary */
if (mnColumn > nLineLength)
{
sal_uInt32 nEolOff = mnColumn - nLineLength;
sal_uInt32 nBufOff = mnOffset - nEolOff;
std::memmove (mpFileBuffer + nBufOff + 1, mpFileBuffer + nBufOff, nEolOff);
mpFileBuffer[ nBufOff ] = '\n';
mnOffset++;
mnColumn = nEolOff;
}
}
mnByte = 0;
}
void
Ascii85Encoder::WriteAscii (sal_uInt8 nByte)
{
PutByte (nByte);
if (mnByte == 4)
ConvertToAscii85 ();
if (mnColumn >= nLineLength)
{
mnOffset += psp::appendStr ("\n", mpFileBuffer + mnOffset);
mnColumn = 0;
}
if (mnOffset >= nBufferSize)
FlushLine ();
}
void
Ascii85Encoder::EncodeByte (sal_uInt8 nByte)
{
WriteAscii (nByte);
}
void
Ascii85Encoder::FlushLine ()
{
if (mnOffset > 0)
{
WritePS (mpFile, mpFileBuffer, mnOffset);
mnOffset = 0;
}
}
Ascii85Encoder::~Ascii85Encoder ()
{
if (mnByte > 0)
ConvertToAscii85 ();
if (mnOffset > 0)
FlushLine ();
PutEOD ();
}
/* LZW encoder */
class LZWEncoder : public Ascii85Encoder
{
private:
struct LZWCTreeNode
{
LZWCTreeNode* mpBrother; // next node with same parent
LZWCTreeNode* mpFirstChild; // first son
sal_uInt16 mnCode; // code for the string
sal_uInt16 mnValue; // pixelvalue
};
LZWCTreeNode* mpTable; // LZW compression data
LZWCTreeNode* mpPrefix; // the compression is as same as the TIFF compression
sal_uInt16 mnDataSize;
sal_uInt16 mnClearCode;
sal_uInt16 mnEOICode;
sal_uInt16 mnTableSize;
sal_uInt16 mnCodeSize;
sal_uInt32 mnOffset;
sal_uInt32 mdwShift;
LZWEncoder ();
void WriteBits (sal_uInt16 nCode, sal_uInt16 nCodeLen);
public:
LZWEncoder (osl::File* pOutputFile);
~LZWEncoder ();
virtual void EncodeByte (sal_uInt8 nByte);
};
LZWEncoder::LZWEncoder(osl::File* pOutputFile) :
Ascii85Encoder (pOutputFile)
{
mnDataSize = 8;
mnClearCode = 1 << mnDataSize;
mnEOICode = mnClearCode + 1;
mnTableSize = mnEOICode + 1;
mnCodeSize = mnDataSize + 1;
mnOffset = 32; // free bits in dwShift
mdwShift = 0;
mpTable = new LZWCTreeNode[ 4096 ];
for (sal_uInt32 i = 0; i < 4096; i++)
{
mpTable[i].mpBrother = NULL;
mpTable[i].mpFirstChild = NULL;
mpTable[i].mnCode = i;
mpTable[i].mnValue = (sal_uInt8)mpTable[i].mnCode;
}
mpPrefix = NULL;
WriteBits( mnClearCode, mnCodeSize );
}
LZWEncoder::~LZWEncoder()
{
if (mpPrefix)
WriteBits (mpPrefix->mnCode, mnCodeSize);
WriteBits (mnEOICode, mnCodeSize);
delete[] mpTable;
}
void
LZWEncoder::WriteBits (sal_uInt16 nCode, sal_uInt16 nCodeLen)
{
mdwShift |= (nCode << (mnOffset - nCodeLen));
mnOffset -= nCodeLen;
while (mnOffset < 24)
{
WriteAscii ((sal_uInt8)(mdwShift >> 24));
mdwShift <<= 8;
mnOffset += 8;
}
if (nCode == 257 && mnOffset != 32)
WriteAscii ((sal_uInt8)(mdwShift >> 24));
}
void
LZWEncoder::EncodeByte (sal_uInt8 nByte )
{
LZWCTreeNode* p;
sal_uInt16 i;
sal_uInt8 nV;
if (!mpPrefix)
{
mpPrefix = mpTable + nByte;
}
else
{
nV = nByte;
for (p = mpPrefix->mpFirstChild; p != NULL; p = p->mpBrother)
{
if (p->mnValue == nV)
break;
}
if (p != NULL)
{
mpPrefix = p;
}
else
{
WriteBits (mpPrefix->mnCode, mnCodeSize);
if (mnTableSize == 409)
{
WriteBits (mnClearCode, mnCodeSize);
for (i = 0; i < mnClearCode; i++)
mpTable[i].mpFirstChild = NULL;
mnCodeSize = mnDataSize + 1;
mnTableSize = mnEOICode + 1;
}
else
{
if(mnTableSize == (sal_uInt16)((1 << mnCodeSize) - 1))
mnCodeSize++;
p = mpTable + (mnTableSize++);
p->mpBrother = mpPrefix->mpFirstChild;
mpPrefix->mpFirstChild = p;
p->mnValue = nV;
p->mpFirstChild = NULL;
}
mpPrefix = mpTable + nV;
}
}
}
/*
*
* bitmap handling routines
*
*/
void
PrinterGfx::DrawBitmap (const Rectangle& rDest, const Rectangle& rSrc,
const PrinterBmp& rBitmap)
{
double fScaleX = (double)rDest.GetWidth() / (double)rSrc.GetWidth();
double fScaleY = (double)rDest.GetHeight() / (double)rSrc.GetHeight();
PSGSave ();
PSTranslate (rDest.BottomLeft());
PSScale (fScaleX, fScaleY);
if (mnPSLevel >= 2)
{
if (rBitmap.GetDepth() == 1)
{
DrawPS2MonoImage (rBitmap, rSrc);
}
else
if (rBitmap.GetDepth() == 8 && mbColor)
{
// if the palette is larger than the image itself print it as a truecolor
// image to save diskspace. This is important for printing transparent
// bitmaps that are disassembled into small pieces
sal_Int32 nImageSz = rSrc.GetWidth() * rSrc.GetHeight();
sal_Int32 nPaletteSz = rBitmap.GetPaletteEntryCount();
if ((nImageSz < nPaletteSz) || (nImageSz < 24) )
DrawPS2TrueColorImage (rBitmap, rSrc);
else
DrawPS2PaletteImage (rBitmap, rSrc);
}
else
if (rBitmap.GetDepth() == 24 && mbColor)
{
DrawPS2TrueColorImage (rBitmap, rSrc);
}
else
{
DrawPS2GrayImage (rBitmap, rSrc);
}
}
else
{
DrawPS1GrayImage (rBitmap, rSrc);
}
PSGRestore ();
}
/* XXX does not work XXX */
void
PrinterGfx::DrawBitmap (const Rectangle& rDest, const Rectangle& rSrc,
const PrinterBmp& /*rBitmap*/, const PrinterBmp& /*rTransBitmap*/)
{
double fScaleX = (double)rDest.GetWidth() / (double)rSrc.GetWidth();
double fScaleY = (double)rDest.GetHeight() / (double)rSrc.GetHeight();
PSGSave ();
PSTranslate (rDest.BottomLeft());
PSScale (fScaleX, fScaleY);
PSGRestore ();
}
/* XXX does not work XXX */
void
PrinterGfx::DrawMask (const Rectangle& rDest, const Rectangle& rSrc,
const PrinterBmp &/*rBitmap*/, PrinterColor& /*rMaskColor*/)
{
double fScaleX = (double)rDest.GetWidth() / (double)rSrc.GetWidth();
double fScaleY = (double)rDest.GetHeight() / (double)rSrc.GetHeight();
PSGSave ();
PSTranslate (rDest.BottomLeft());
PSScale (fScaleX, fScaleY);
PSGRestore ();
}
/*
*
* Implementation: PS Level 1
*
*/
void
PrinterGfx::DrawPS1GrayImage (const PrinterBmp& rBitmap, const Rectangle& rArea)
{
sal_uInt32 nWidth = rArea.GetWidth();
sal_uInt32 nHeight = rArea.GetHeight();
sal_Char pGrayImage [512];
sal_Int32 nChar = 0;
// image header
nChar += psp::getValueOf (nWidth, pGrayImage + nChar);
nChar += psp::appendStr (" ", pGrayImage + nChar);
nChar += psp::getValueOf (nHeight, pGrayImage + nChar);
nChar += psp::appendStr (" 8 ", pGrayImage + nChar);
nChar += psp::appendStr ("[ 1 0 0 1 0 ", pGrayImage + nChar);
nChar += psp::getValueOf (nHeight, pGrayImage + nChar);
nChar += psp::appendStr ("]", pGrayImage + nChar);
nChar += psp::appendStr (" {currentfile ", pGrayImage + nChar);
nChar += psp::getValueOf (nWidth, pGrayImage + nChar);
nChar += psp::appendStr (" string readhexstring pop}\n", pGrayImage + nChar);
nChar += psp::appendStr ("image\n", pGrayImage + nChar);
WritePS (mpPageBody, pGrayImage);
// image body
HexEncoder* pEncoder = new HexEncoder (mpPageBody);
for (long nRow = rArea.Top(); nRow <= rArea.Bottom(); nRow++)
{
for (long nColumn = rArea.Left(); nColumn <= rArea.Right(); nColumn++)
{
sal_uChar nByte = rBitmap.GetPixelGray (nRow, nColumn);
pEncoder->EncodeByte (nByte);
}
}
delete pEncoder;
WritePS (mpPageBody, "\n");
}
/*
*
* Implementation: PS Level 2
*
*/
void
PrinterGfx::writePS2ImageHeader (const Rectangle& rArea, psp::ImageType nType)
{
sal_Int32 nChar = 0;
sal_Char pImage [512];
sal_Int32 nDictType = 0;
switch (nType)
{
case psp::TrueColorImage: nDictType = 0; break;
case psp::PaletteImage: nDictType = 1; break;
case psp::GrayScaleImage: nDictType = 2; break;
case psp::MonochromeImage: nDictType = 3; break;
default: break;
}
sal_Int32 nCompressType = mbCompressBmp ? 1 : 0;
nChar += psp::getValueOf (rArea.GetWidth(), pImage + nChar);
nChar += psp::appendStr (" ", pImage + nChar);
nChar += psp::getValueOf (rArea.GetHeight(), pImage + nChar);
nChar += psp::appendStr (" ", pImage + nChar);
nChar += psp::getValueOf (nDictType, pImage + nChar);
nChar += psp::appendStr (" ", pImage + nChar);
nChar += psp::getValueOf (nCompressType, pImage + nChar);
nChar += psp::appendStr (" psp_imagedict image\n", pImage + nChar);
WritePS (mpPageBody, pImage);
}
void
PrinterGfx::writePS2Colorspace(const PrinterBmp& rBitmap, psp::ImageType nType)
{
switch (nType)
{
case psp::GrayScaleImage:
WritePS (mpPageBody, "/DeviceGray setcolorspace\n");
break;
case psp::TrueColorImage:
WritePS (mpPageBody, "/DeviceRGB setcolorspace\n");
break;
case psp::MonochromeImage:
case psp::PaletteImage:
{
sal_Int32 nChar = 0;
sal_Char pImage [4096];
const sal_uInt32 nSize = rBitmap.GetPaletteEntryCount();
nChar += psp::appendStr ("[/Indexed /DeviceRGB ", pImage + nChar);
nChar += psp::getValueOf (nSize - 1, pImage + nChar);
if (mbCompressBmp)
nChar += psp::appendStr ("\npsp_lzwstring\n", pImage + nChar);
else
nChar += psp::appendStr ("\npsp_ascii85string\n", pImage + nChar);
WritePS (mpPageBody, pImage);
ByteEncoder* pEncoder = mbCompressBmp ? new LZWEncoder(mpPageBody)
: new Ascii85Encoder(mpPageBody);
for (sal_uInt32 i = 0; i < nSize; i++)
{
PrinterColor aColor = rBitmap.GetPaletteColor(i);
pEncoder->EncodeByte (aColor.GetRed());
pEncoder->EncodeByte (aColor.GetGreen());
pEncoder->EncodeByte (aColor.GetBlue());
}
delete pEncoder;
WritePS (mpPageBody, "pop ] setcolorspace\n");
}
break;
default: break;
}
}
void
PrinterGfx::DrawPS2GrayImage (const PrinterBmp& rBitmap, const Rectangle& rArea)
{
writePS2Colorspace(rBitmap, psp::GrayScaleImage);
writePS2ImageHeader(rArea, psp::GrayScaleImage);
ByteEncoder* pEncoder = mbCompressBmp ? new LZWEncoder(mpPageBody)
: new Ascii85Encoder(mpPageBody);
for (long nRow = rArea.Top(); nRow <= rArea.Bottom(); nRow++)
{
for (long nColumn = rArea.Left(); nColumn <= rArea.Right(); nColumn++)
{
sal_uChar nByte = rBitmap.GetPixelGray (nRow, nColumn);
pEncoder->EncodeByte (nByte);
}
}
delete pEncoder;
}
void
PrinterGfx::DrawPS2MonoImage (const PrinterBmp& rBitmap, const Rectangle& rArea)
{
writePS2Colorspace(rBitmap, psp::MonochromeImage);
writePS2ImageHeader(rArea, psp::MonochromeImage);
ByteEncoder* pEncoder = mbCompressBmp ? new LZWEncoder(mpPageBody)
: new Ascii85Encoder(mpPageBody);
for (long nRow = rArea.Top(); nRow <= rArea.Bottom(); nRow++)
{
long nBitPos = 0;
sal_uChar nBit = 0;
sal_uChar nByte = 0;
for (long nColumn = rArea.Left(); nColumn <= rArea.Right(); nColumn++)
{
nBit = rBitmap.GetPixelIdx (nRow, nColumn);
nByte |= nBit << (7 - nBitPos);
if (++nBitPos == 8)
{
pEncoder->EncodeByte (nByte);
nBitPos = 0;
nByte = 0;
}
}
// keep the row byte aligned
if (nBitPos != 0)
pEncoder->EncodeByte (nByte);
}
delete pEncoder;
}
void
PrinterGfx::DrawPS2PaletteImage (const PrinterBmp& rBitmap, const Rectangle& rArea)
{
writePS2Colorspace(rBitmap, psp::PaletteImage);
writePS2ImageHeader(rArea, psp::PaletteImage);
ByteEncoder* pEncoder = mbCompressBmp ? new LZWEncoder(mpPageBody)
: new Ascii85Encoder(mpPageBody);
for (long nRow = rArea.Top(); nRow <= rArea.Bottom(); nRow++)
{
for (long nColumn = rArea.Left(); nColumn <= rArea.Right(); nColumn++)
{
sal_uChar nByte = rBitmap.GetPixelIdx (nRow, nColumn);
pEncoder->EncodeByte (nByte);
}
}
delete pEncoder;
}
void
PrinterGfx::DrawPS2TrueColorImage (const PrinterBmp& rBitmap, const Rectangle& rArea)
{
writePS2Colorspace(rBitmap, psp::TrueColorImage);
writePS2ImageHeader(rArea, psp::TrueColorImage);
ByteEncoder* pEncoder = mbCompressBmp ? new LZWEncoder(mpPageBody)
: new Ascii85Encoder(mpPageBody);
for (long nRow = rArea.Top(); nRow <= rArea.Bottom(); nRow++)
{
for (long nColumn = rArea.Left(); nColumn <= rArea.Right(); nColumn++)
{
PrinterColor aColor = rBitmap.GetPixelRGB (nRow, nColumn);
pEncoder->EncodeByte (aColor.GetRed());
pEncoder->EncodeByte (aColor.GetGreen());
pEncoder->EncodeByte (aColor.GetBlue());
}
}
delete pEncoder;
}
} /* namespace psp */