diff options
Diffstat (limited to 'vcl/unx/generic/printergfx')
-rw-r--r-- | vcl/unx/generic/printergfx/bitmap_gfx.cxx | 732 | ||||
-rw-r--r-- | vcl/unx/generic/printergfx/common_gfx.cxx | 1284 | ||||
-rw-r--r-- | vcl/unx/generic/printergfx/glyphset.cxx | 942 | ||||
-rw-r--r-- | vcl/unx/generic/printergfx/glyphset.hxx | 135 | ||||
-rw-r--r-- | vcl/unx/generic/printergfx/printerjob.cxx | 1204 | ||||
-rw-r--r-- | vcl/unx/generic/printergfx/psheader.ps | 368 | ||||
-rw-r--r-- | vcl/unx/generic/printergfx/psputil.cxx | 268 | ||||
-rw-r--r-- | vcl/unx/generic/printergfx/psputil.hxx | 78 | ||||
-rw-r--r-- | vcl/unx/generic/printergfx/text_gfx.cxx | 862 |
9 files changed, 5873 insertions, 0 deletions
diff --git a/vcl/unx/generic/printergfx/bitmap_gfx.cxx b/vcl/unx/generic/printergfx/bitmap_gfx.cxx new file mode 100644 index 000000000000..9275db1e469f --- /dev/null +++ b/vcl/unx/generic/printergfx/bitmap_gfx.cxx @@ -0,0 +1,732 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 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 */ diff --git a/vcl/unx/generic/printergfx/common_gfx.cxx b/vcl/unx/generic/printergfx/common_gfx.cxx new file mode 100644 index 000000000000..6ebf79cfac73 --- /dev/null +++ b/vcl/unx/generic/printergfx/common_gfx.cxx @@ -0,0 +1,1284 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include "psputil.hxx" +#include "glyphset.hxx" + +#include "printergfx.hxx" +#include "printerjob.hxx" +#include "vcl/fontmanager.hxx" +#include "vcl/strhelper.hxx" +#include "vcl/printerinfomanager.hxx" + +#include "tools/debug.hxx" +#include "tools/color.hxx" +#include "tools/poly.hxx" + +using namespace psp ; + +static const sal_Int32 nMaxTextColumn = 80; + +GraphicsStatus::GraphicsStatus() : + mbArtItalic( false ), + mbArtBold( false ), + mnTextHeight( 0 ), + mnTextWidth( 0 ), + mfLineWidth( -1 ) +{ +} + +/* + * non graphics graphics routines + */ + +sal_Bool +PrinterGfx::Init (PrinterJob &rPrinterJob) +{ + mpPageHeader = rPrinterJob.GetCurrentPageHeader (); + mpPageBody = rPrinterJob.GetCurrentPageBody (); + mnDepth = rPrinterJob.GetDepth (); + mnPSLevel = rPrinterJob.GetPostscriptLevel (); + mbColor = rPrinterJob.IsColorPrinter (); + + mnDpi = rPrinterJob.GetResolution(); + rPrinterJob.GetScale (mfScaleX, mfScaleY); + const PrinterInfo& rInfo( PrinterInfoManager::get().getPrinterInfo( rPrinterJob.GetPrinterName() ) ); + if( mpFontSubstitutes ) + delete const_cast< ::std::hash_map<fontID,fontID>* >(mpFontSubstitutes); + if( rInfo.m_bPerformFontSubstitution ) + mpFontSubstitutes = new ::std::hash_map< fontID, fontID >( rInfo.m_aFontSubstitutions ); + else + mpFontSubstitutes = NULL; + mbUploadPS42Fonts = rInfo.m_pParser ? ( rInfo.m_pParser->isType42Capable() ? sal_True : sal_False ) : sal_False; + + return sal_True; +} + +sal_Bool +PrinterGfx::Init (const JobData& rData) +{ + mpPageHeader = NULL; + mpPageBody = NULL; + mnDepth = rData.m_nColorDepth; + mnPSLevel = rData.m_nPSLevel ? rData.m_nPSLevel : (rData.m_pParser ? rData.m_pParser->getLanguageLevel() : 2 ); + mbColor = rData.m_nColorDevice ? ( rData.m_nColorDevice == -1 ? sal_False : sal_True ) : (( rData.m_pParser ? (rData.m_pParser->isColorDevice() ? sal_True : sal_False ) : sal_True ) ); + int nRes = rData.m_aContext.getRenderResolution(); + mnDpi = nRes; + mfScaleX = (double)72.0 / (double)mnDpi; + mfScaleY = (double)72.0 / (double)mnDpi; + const PrinterInfo& rInfo( PrinterInfoManager::get().getPrinterInfo( rData.m_aPrinterName ) ); + if( mpFontSubstitutes ) + delete const_cast< ::std::hash_map<fontID,fontID>* >(mpFontSubstitutes); + if( rInfo.m_bPerformFontSubstitution ) + mpFontSubstitutes = new ::std::hash_map< fontID, fontID >( rInfo.m_aFontSubstitutions ); + else + mpFontSubstitutes = NULL; + mbUploadPS42Fonts = rInfo.m_pParser ? ( rInfo.m_pParser->isType42Capable() ? sal_True : sal_False ) : sal_False; + + return sal_True; +} + +void +PrinterGfx::GetResolution (sal_Int32 &rDpiX, sal_Int32 &rDpiY) const +{ + rDpiX = mnDpi; + rDpiY = mnDpi; +} + +sal_uInt16 +PrinterGfx::GetBitCount () +{ + return mnDepth; +} + +PrinterGfx::PrinterGfx() : + mpPageHeader (NULL), + mpPageBody (NULL), + mnFontID (0), + mnFallbackID (0), + mnTextAngle (0), + mbTextVertical (false), + mrFontMgr (PrintFontManager::get()), + mbCompressBmp (sal_True), + maFillColor (0xff,0,0), + maTextColor (0,0,0), + maLineColor (0, 0xff, 0), + mpFontSubstitutes( NULL ), + mbStrictSO52Compatibility( false ) +{ + maVirtualStatus.mfLineWidth = 1.0; + maVirtualStatus.mnTextHeight = 12; + maVirtualStatus.mnTextWidth = 0; + + maGraphicsStack.push_back( GraphicsStatus() ); +} + +PrinterGfx::~PrinterGfx() +{ + /* + * #95810# the original reasoning why mpFontSubstitutes is a pointer was + * that applications should release all PrinterGfx when printers change + * because they are really invalid; the corresponding printers may have + * changed their settings or even not exist anymore. + * + * Alas, this is not always done real time. So we keep a local copy of + * the font substitutes now in case of bad timing. + */ + delete const_cast< ::std::hash_map<fontID,fontID>* >(mpFontSubstitutes); +} + +void +PrinterGfx::Clear() +{ + mpPageHeader = NULL; + mpPageBody = NULL; + mnFontID = 0; + maVirtualStatus = GraphicsStatus(); + maVirtualStatus.mnTextHeight = 12; + maVirtualStatus.mnTextWidth = 0; + maVirtualStatus.mfLineWidth = 1.0; + mbTextVertical = false; + maLineColor = PrinterColor(); + maFillColor = PrinterColor(); + maTextColor = PrinterColor(); + mbCompressBmp = sal_True; + mnDpi = 300; + mnDepth = 24; + mnPSLevel = 2; + mbColor = sal_True; + mnTextAngle = 0; + + maClipRegion.clear(); + maGraphicsStack.clear(); + maGraphicsStack.push_back( GraphicsStatus() ); +} + +/* + * clip region handling + */ + +void +PrinterGfx::ResetClipRegion() +{ + maClipRegion.clear(); + PSGRestore (); + PSGSave (); // get "clean" clippath +} + +void +PrinterGfx::BeginSetClipRegion( sal_uInt32 ) +{ + maClipRegion.clear(); +} + +sal_Bool +PrinterGfx::UnionClipRegion (sal_Int32 nX,sal_Int32 nY,sal_Int32 nDX,sal_Int32 nDY) +{ + if( nDX && nDY ) + maClipRegion.push_back (Rectangle(Point(nX,nY ), Size(nDX,nDY))); + return sal_True; +} + +sal_Bool +PrinterGfx::JoinVerticalClipRectangles( std::list< Rectangle >::iterator& it, + Point& rOldPoint, sal_Int32& rColumn ) +{ + sal_Bool bSuccess = sal_False; + + std::list< Rectangle >::iterator tempit, nextit; + nextit = it; + ++nextit; + std::list< Point > leftside, rightside; + + Rectangle aLastRect( *it ); + leftside.push_back( Point( it->Left(), it->Top() ) ); + rightside.push_back( Point( it->Right()+1, it->Top() ) ); + while( nextit != maClipRegion.end() ) + { + tempit = nextit; + ++tempit; + if( nextit->Top() == aLastRect.Bottom()+1 ) + { + if( + ( nextit->Left() >= aLastRect.Left() && nextit->Left() <= aLastRect.Right() ) // left endpoint touches last rectangle + || + ( nextit->Right() >= aLastRect.Left() && nextit->Right() <= aLastRect.Right() ) // right endpoint touches last rectangle + || + ( nextit->Left() <= aLastRect.Left() && nextit->Right() >= aLastRect.Right() ) // whole line touches last rectangle + ) + { + if( aLastRect.GetHeight() > 1 || + abs( aLastRect.Left() - nextit->Left() ) > 2 || + abs( aLastRect.Right() - nextit->Right() ) > 2 + ) + { + leftside.push_back( Point( aLastRect.Left(), aLastRect.Bottom()+1 ) ); + rightside.push_back( Point( aLastRect.Right()+1, aLastRect.Bottom()+1 ) ); + } + aLastRect = *nextit; + leftside.push_back( aLastRect.TopLeft() ); + rightside.push_back( aLastRect.TopRight() ); + maClipRegion.erase( nextit ); + } + } + nextit = tempit; + } + if( leftside.size() > 1 ) + { + // push the last coordinates + leftside.push_back( Point( aLastRect.Left(), aLastRect.Bottom()+1 ) ); + rightside.push_back( Point( aLastRect.Right()+1, aLastRect.Bottom()+1 ) ); + + // cool, we can concatenate rectangles + int nDX = -65536, nDY = 65536; + int nNewDX = 0, nNewDY = 0; + + Point aLastPoint = leftside.front(); + PSBinMoveTo (aLastPoint, rOldPoint, rColumn); + leftside.pop_front(); + while( leftside.begin() != leftside.end() ) + { + Point aPoint (leftside.front()); + leftside.pop_front(); + // may have been the last one + if( leftside.begin() != leftside.end() ) + { + nNewDX = aPoint.X() - aLastPoint.X(); + nNewDY = aPoint.Y() - aLastPoint.Y(); + if( nNewDX == 0 && nDX == 0 ) + continue; + if( nDX != 0 && nNewDX != 0 && + (double)nNewDY/(double)nNewDX == (double)nDY/(double)nDX ) + continue; + } + PSBinLineTo (aPoint, rOldPoint, rColumn); + aLastPoint = aPoint; + } + + aLastPoint = rightside.back(); + nDX = -65536; + nDY = 65536; + PSBinLineTo (aLastPoint, rOldPoint, rColumn); + rightside.pop_back(); + while( rightside.begin() != rightside.end() ) + { + Point aPoint (rightside.back()); + rightside.pop_back(); + if( rightside.begin() != rightside.end() ) + { + nNewDX = aPoint.X() - aLastPoint.X(); + nNewDY = aPoint.Y() - aLastPoint.Y(); + if( nNewDX == 0 && nDX == 0 ) + continue; + if( nDX != 0 && nNewDX != 0 && + (double)nNewDY/(double)nNewDX == (double)nDY/(double)nDX ) + continue; + } + PSBinLineTo (aPoint, rOldPoint, rColumn); + } + + tempit = it; + ++tempit; + maClipRegion.erase( it ); + it = tempit; + bSuccess = sal_True; + } + return bSuccess; +} + +void +PrinterGfx::EndSetClipRegion() +{ + PSGRestore (); + PSGSave (); // get "clean" clippath + + PSBinStartPath (); + Point aOldPoint (0, 0); + sal_Int32 nColumn = 0; + + std::list< Rectangle >::iterator it = maClipRegion.begin(); + while( it != maClipRegion.end() ) + { + // try to concatenate adjacent rectangles + // first try in y direction, then in x direction + if( ! JoinVerticalClipRectangles( it, aOldPoint, nColumn ) ) + { + // failed, so it is a single rectangle + PSBinMoveTo (it->TopLeft(), aOldPoint, nColumn ); + PSBinLineTo (Point( it->Left(), it->Bottom()+1 ), aOldPoint, nColumn ); + PSBinLineTo (Point( it->Right()+1, it->Bottom()+1 ), aOldPoint, nColumn ); + PSBinLineTo (Point( it->Right()+1, it->Top() ), aOldPoint, nColumn ); + ++it; + } + } + + PSBinEndPath (); + + WritePS (mpPageBody, "closepath clip newpath\n"); + maClipRegion.clear(); +} + +/* + * draw graphic primitives + */ + +void +PrinterGfx::DrawRect (const Rectangle& rRectangle ) +{ + char pRect [128]; + sal_Int32 nChar = 0; + + nChar = psp::getValueOf (rRectangle.TopLeft().X(), pRect); + nChar += psp::appendStr (" ", pRect + nChar); + nChar += psp::getValueOf (rRectangle.TopLeft().Y(), pRect + nChar); + nChar += psp::appendStr (" ", pRect + nChar); + nChar += psp::getValueOf (rRectangle.GetWidth(), pRect + nChar); + nChar += psp::appendStr (" ", pRect + nChar); + nChar += psp::getValueOf (rRectangle.GetHeight(), pRect + nChar); + nChar += psp::appendStr (" ", pRect + nChar); + + if( maFillColor.Is() ) + { + PSSetColor (maFillColor); + PSSetColor (); + WritePS (mpPageBody, pRect, nChar); + WritePS (mpPageBody, "rectfill\n"); + } + if( maLineColor.Is() ) + { + PSSetColor (maLineColor); + PSSetColor (); + PSSetLineWidth (); + WritePS (mpPageBody, pRect, nChar); + WritePS (mpPageBody, "rectstroke\n"); + } +} + +void +PrinterGfx::DrawLine (const Point& rFrom, const Point& rTo) +{ + if( maLineColor.Is() ) + { + PSSetColor (maLineColor); + PSSetColor (); + PSSetLineWidth (); + + PSMoveTo (rFrom); + PSLineTo (rTo); + WritePS (mpPageBody, "stroke\n" ); + } +} + +void +PrinterGfx::DrawPixel (const Point& rPoint, const PrinterColor& rPixelColor) +{ + if( rPixelColor.Is() ) + { + PSSetColor (rPixelColor); + PSSetColor (); + + PSMoveTo (rPoint); + PSLineTo (Point (rPoint.X ()+1, rPoint.Y ())); + PSLineTo (Point (rPoint.X ()+1, rPoint.Y ()+1)); + PSLineTo (Point (rPoint.X (), rPoint.Y ()+1)); + WritePS (mpPageBody, "fill\n" ); + } +} + +void +PrinterGfx::DrawPolyLine (sal_uInt32 nPoints, const Point* pPath) +{ + if( maLineColor.Is() && nPoints && pPath ) + { + PSSetColor (maLineColor); + PSSetColor (); + PSSetLineWidth (); + + PSBinCurrentPath (nPoints, pPath); + + WritePS (mpPageBody, "stroke\n" ); + } +} + +void +PrinterGfx::DrawPolygon (sal_uInt32 nPoints, const Point* pPath) +{ + // premature end of operation + if (!(nPoints > 1) || (pPath == NULL) || !(maFillColor.Is() || maLineColor.Is())) + return; + + // setup closed path + Point aPoint( 0, 0 ); + sal_Int32 nColumn( 0 ); + + PSBinStartPath(); + PSBinMoveTo( pPath[0], aPoint, nColumn ); + for( unsigned int n = 1; n < nPoints; n++ ) + PSBinLineTo( pPath[n], aPoint, nColumn ); + if( pPath[0] != pPath[nPoints-1] ) + PSBinLineTo( pPath[0], aPoint, nColumn ); + PSBinEndPath(); + + // fill the polygon first, then draw the border, note that fill and + // stroke reset the currentpath + + // if fill and stroke, save the current path + if( maFillColor.Is() && maLineColor.Is()) + PSGSave(); + + if (maFillColor.Is ()) + { + PSSetColor (maFillColor); + PSSetColor (); + WritePS (mpPageBody, "eofill\n"); + } + + // restore the current path + if( maFillColor.Is() && maLineColor.Is()) + PSGRestore(); + + if (maLineColor.Is ()) + { + PSSetColor (maLineColor); + PSSetColor (); + PSSetLineWidth (); + WritePS (mpPageBody, "stroke\n"); + } +} + +void +PrinterGfx::DrawPolyPolygon (sal_uInt32 nPoly, const sal_uInt32* pSizes, const Point** pPaths ) +{ + // sanity check + if ( !nPoly || !pPaths || !(maFillColor.Is() || maLineColor.Is())) + return; + + + // setup closed path + for( unsigned int i = 0; i < nPoly; i++ ) + { + Point aPoint( 0, 0 ); + sal_Int32 nColumn( 0 ); + + PSBinStartPath(); + PSBinMoveTo( pPaths[i][0], aPoint, nColumn ); + for( unsigned int n = 1; n < pSizes[i]; n++ ) + PSBinLineTo( pPaths[i][n], aPoint, nColumn ); + if( pPaths[i][0] != pPaths[i][pSizes[i]-1] ) + PSBinLineTo( pPaths[i][0], aPoint, nColumn ); + PSBinEndPath(); + } + + // if eofill and stroke, save the current path + if( maFillColor.Is() && maLineColor.Is()) + PSGSave(); + + // first draw area + if( maFillColor.Is() ) + { + PSSetColor (maFillColor); + PSSetColor (); + WritePS (mpPageBody, "eofill\n"); + } + + // restore the current path + if( maFillColor.Is() && maLineColor.Is()) + PSGRestore(); + + // now draw outlines + if( maLineColor.Is() ) + { + PSSetColor (maLineColor); + PSSetColor (); + PSSetLineWidth (); + WritePS (mpPageBody, "stroke\n"); + } +} + +/* + * Bezier Polygon Drawing methods. + */ + +void +PrinterGfx::DrawPolyLineBezier (sal_uInt32 nPoints, const Point* pPath, const sal_uInt8* pFlgAry) +{ + const sal_uInt32 nBezString= 1024; + sal_Char pString[nBezString]; + + if ( nPoints > 1 && maLineColor.Is() && pPath ) + { + PSSetColor (maLineColor); + PSSetColor (); + PSSetLineWidth (); + + snprintf(pString, nBezString, "%li %li moveto\n", pPath[0].X(), pPath[0].Y()); + WritePS(mpPageBody, pString); + + // Handle the drawing of mixed lines mixed with curves + // - a normal point followed by a normal point is a line + // - a normal point followed by 2 control points and a normal point is a curve + for (unsigned int i=1; i<nPoints;) + { + if (pFlgAry[i] != POLY_CONTROL) //If the next point is a POLY_NORMAL, we're drawing a line + { + snprintf(pString, nBezString, "%li %li lineto\n", pPath[i].X(), pPath[i].Y()); + i++; + } + else //Otherwise we're drawing a spline + { + if (i+2 >= nPoints) + return; //Error: wrong sequence of contol/normal points somehow + if ((pFlgAry[i] == POLY_CONTROL) && (pFlgAry[i+1] == POLY_CONTROL) && + (pFlgAry[i+2] != POLY_CONTROL)) + { + snprintf(pString, nBezString, "%li %li %li %li %li %li curveto\n", + pPath[i].X(), pPath[i].Y(), + pPath[i+1].X(), pPath[i+1].Y(), + pPath[i+2].X(), pPath[i+2].Y()); + } + else + { + DBG_ERROR( "PrinterGfx::DrawPolyLineBezier: Strange output" ); + } + i+=3; + } + WritePS(mpPageBody, pString); + } + + // now draw outlines + WritePS (mpPageBody, "stroke\n"); + } +} + +void +PrinterGfx::DrawPolygonBezier (sal_uInt32 nPoints, const Point* pPath, const sal_uInt8* pFlgAry) +{ + const sal_uInt32 nBezString = 1024; + sal_Char pString[nBezString]; + // premature end of operation + if (!(nPoints > 1) || (pPath == NULL) || !(maFillColor.Is() || maLineColor.Is())) + return; + + snprintf(pString, nBezString, "%li %li moveto\n", pPath[0].X(), pPath[0].Y()); + WritePS(mpPageBody, pString); //Move to the starting point for the PolyPoygon + for (unsigned int i=1; i < nPoints;) + { + if (pFlgAry[i] != POLY_CONTROL) + { + snprintf(pString, nBezString, "%li %li lineto\n", pPath[i].X(), pPath[i].Y()); + WritePS(mpPageBody, pString); + i++; + } + else + { + if (i+2 >= nPoints) + return; //Error: wrong sequence of contol/normal points somehow + if ((pFlgAry[i] == POLY_CONTROL) && (pFlgAry[i+1] == POLY_CONTROL) && + (pFlgAry[i+2] != POLY_CONTROL)) + { + snprintf(pString, nBezString, "%li %li %li %li %li %li curveto\n", + pPath[i].X(), pPath[i].Y(), + pPath[i+1].X(), pPath[i+1].Y(), + pPath[i+2].X(), pPath[i+2].Y()); + WritePS(mpPageBody, pString); + } + else + { + DBG_ERROR( "PrinterGfx::DrawPolygonBezier: Strange output" ); + } + i+=3; + } + } + + // if fill and stroke, save the current path + if( maFillColor.Is() && maLineColor.Is()) + PSGSave(); + + if (maFillColor.Is ()) + { + PSSetColor (maFillColor); + PSSetColor (); + WritePS (mpPageBody, "eofill\n"); + } + + // restore the current path + if( maFillColor.Is() && maLineColor.Is()) + PSGRestore(); +} + +void +PrinterGfx::DrawPolyPolygonBezier (sal_uInt32 nPoly, const sal_uInt32 * pPoints, const Point* const * pPtAry, const sal_uInt8* const* pFlgAry) +{ + const sal_uInt32 nBezString = 1024; + sal_Char pString[nBezString]; + if ( !nPoly || !pPtAry || !pPoints || !(maFillColor.Is() || maLineColor.Is())) + return; + + + for (unsigned int i=0; i<nPoly;i++) + { + sal_uInt32 nPoints = pPoints[i]; + // #112689# sanity check + if( nPoints == 0 || pPtAry[i] == NULL ) + continue; + + snprintf(pString, nBezString, "%li %li moveto\n", pPtAry[i][0].X(), pPtAry[i][0].Y()); //Move to the starting point + WritePS(mpPageBody, pString); + for (unsigned int j=1; j < nPoints;) + { + // if no flag array exists for this polygon, then it must be a regular + // polygon without beziers + if ( ! pFlgAry[i] || pFlgAry[i][j] != POLY_CONTROL) + { + snprintf(pString, nBezString, "%li %li lineto\n", pPtAry[i][j].X(), pPtAry[i][j].Y()); + WritePS(mpPageBody, pString); + j++; + } + else + { + if (j+2 >= nPoints) + break; //Error: wrong sequence of contol/normal points somehow + if ((pFlgAry[i][j] == POLY_CONTROL) && (pFlgAry[i][j+1] == POLY_CONTROL) && (pFlgAry[i][j+2] != POLY_CONTROL)) + { + snprintf(pString, nBezString, "%li %li %li %li %li %li curveto\n", + pPtAry[i][j].X(), pPtAry[i][j].Y(), + pPtAry[i][j+1].X(), pPtAry[i][j+1].Y(), + pPtAry[i][j+2].X(), pPtAry[i][j+2].Y()); + WritePS(mpPageBody, pString); + } + else + { + DBG_ERROR( "PrinterGfx::DrawPolyPolygonBezier: Strange output" ); + } + j+=3; + } + } + } + + // if fill and stroke, save the current path + if( maFillColor.Is() && maLineColor.Is()) + PSGSave(); + + if (maFillColor.Is ()) + { + PSSetColor (maFillColor); + PSSetColor (); + WritePS (mpPageBody, "eofill\n"); + } + + // restore the current path + if( maFillColor.Is() && maLineColor.Is()) + PSGRestore(); +} + + +/* + * postscript generating routines + */ +void +PrinterGfx::PSGSave () +{ + WritePS (mpPageBody, "gsave\n" ); + GraphicsStatus aNewState; + if( maGraphicsStack.begin() != maGraphicsStack.end() ) + aNewState = maGraphicsStack.front(); + maGraphicsStack.push_front( aNewState ); +} + +void +PrinterGfx::PSGRestore () +{ + WritePS (mpPageBody, "grestore\n" ); + if( maGraphicsStack.begin() == maGraphicsStack.end() ) + WritePS (mpPageBody, "Error: too many grestores\n" ); + else + maGraphicsStack.pop_front(); +} + +void +PrinterGfx::PSSetLineWidth () +{ + if( currentState().mfLineWidth != maVirtualStatus.mfLineWidth ) + { + char pBuffer[128]; + sal_Int32 nChar = 0; + + currentState().mfLineWidth = maVirtualStatus.mfLineWidth; + nChar = psp::getValueOfDouble (pBuffer, maVirtualStatus.mfLineWidth, 5); + nChar += psp::appendStr (" setlinewidth\n", pBuffer + nChar); + WritePS (mpPageBody, pBuffer, nChar); + } +} + +void +PrinterGfx::PSSetColor () +{ + PrinterColor& rColor( maVirtualStatus.maColor ); + + if( currentState().maColor != rColor ) + { + currentState().maColor = rColor; + + char pBuffer[128]; + sal_Int32 nChar = 0; + + if( mbColor ) + { + nChar = psp::getValueOfDouble (pBuffer, + (double)rColor.GetRed() / 255.0, 5); + nChar += psp::appendStr (" ", pBuffer + nChar); + nChar += psp::getValueOfDouble (pBuffer + nChar, + (double)rColor.GetGreen() / 255.0, 5); + nChar += psp::appendStr (" ", pBuffer + nChar); + nChar += psp::getValueOfDouble (pBuffer + nChar, + (double)rColor.GetBlue() / 255.0, 5); + nChar += psp::appendStr (" setrgbcolor\n", pBuffer + nChar ); + } + else + { + Color aColor( rColor.GetRed(), rColor.GetGreen(), rColor.GetBlue() ); + sal_uInt8 nCol = aColor.GetLuminance(); + nChar = psp::getValueOfDouble( pBuffer, (double)nCol / 255.0, 5 ); + nChar += psp::appendStr( " setgray\n", pBuffer + nChar ); + } + + WritePS (mpPageBody, pBuffer, nChar); + } +} + +void +PrinterGfx::PSSetFont () +{ + GraphicsStatus& rCurrent( currentState() ); + if( maVirtualStatus.maFont != rCurrent.maFont || + maVirtualStatus.mnTextHeight != rCurrent.mnTextHeight || + maVirtualStatus.maEncoding != rCurrent.maEncoding || + maVirtualStatus.mnTextWidth != rCurrent.mnTextWidth || + maVirtualStatus.mbArtBold != rCurrent.mbArtBold || + maVirtualStatus.mbArtItalic != rCurrent.mbArtItalic + ) + { + rCurrent.maFont = maVirtualStatus.maFont; + rCurrent.maEncoding = maVirtualStatus.maEncoding; + rCurrent.mnTextWidth = maVirtualStatus.mnTextWidth; + rCurrent.mnTextHeight = maVirtualStatus.mnTextHeight; + rCurrent.mbArtItalic = maVirtualStatus.mbArtItalic; + rCurrent.mbArtBold = maVirtualStatus.mbArtBold; + + sal_Int32 nTextHeight = rCurrent.mnTextHeight; + sal_Int32 nTextWidth = rCurrent.mnTextWidth ? rCurrent.mnTextWidth + : rCurrent.mnTextHeight; + + sal_Char pSetFont [256]; + sal_Int32 nChar = 0; + + // postscript based fonts need reencoding + if ( ( rCurrent.maEncoding == RTL_TEXTENCODING_MS_1252) + || ( rCurrent.maEncoding == RTL_TEXTENCODING_ISO_8859_1) + || ( rCurrent.maEncoding >= RTL_TEXTENCODING_USER_START + && rCurrent.maEncoding <= RTL_TEXTENCODING_USER_END) + ) + { + rtl::OString aReencodedFont = + psp::GlyphSet::GetReencodedFontName (rCurrent.maEncoding, + rCurrent.maFont); + + nChar += psp::appendStr ("(", pSetFont + nChar); + nChar += psp::appendStr (aReencodedFont.getStr(), + pSetFont + nChar); + nChar += psp::appendStr (") cvn findfont ", + pSetFont + nChar); + } + else + // tt based fonts mustn't reencode, the encoding is implied by the fontname + // same for symbol type1 fonts, dont try to touch them + { + nChar += psp::appendStr ("(", pSetFont + nChar); + nChar += psp::appendStr (rCurrent.maFont.getStr(), + pSetFont + nChar); + nChar += psp::appendStr (") cvn findfont ", + pSetFont + nChar); + } + + if( ! rCurrent.mbArtItalic ) + { + nChar += psp::getValueOf (nTextWidth, pSetFont + nChar); + nChar += psp::appendStr (" ", pSetFont + nChar); + nChar += psp::getValueOf (-nTextHeight, pSetFont + nChar); + nChar += psp::appendStr (" matrix scale makefont setfont\n", pSetFont + nChar); + } + else // skew 15 degrees to right + { + nChar += psp::appendStr ( " [", pSetFont + nChar); + nChar += psp::getValueOf (nTextWidth, pSetFont + nChar); + nChar += psp::appendStr (" 0 ", pSetFont + nChar); + nChar += psp::getValueOfDouble (pSetFont + nChar, 0.27*(double)nTextWidth, 3 ); + nChar += psp::appendStr ( " ", pSetFont + nChar); + nChar += psp::getValueOf (-nTextHeight, pSetFont + nChar); + + nChar += psp::appendStr (" 0 0] makefont setfont\n", pSetFont + nChar); + } + + WritePS (mpPageBody, pSetFont); + } +} + +void +PrinterGfx::PSRotate (sal_Int32 nAngle) +{ + sal_Int32 nPostScriptAngle = -nAngle; + while( nPostScriptAngle < 0 ) + nPostScriptAngle += 3600; + + if (nPostScriptAngle == 0) + return; + + sal_Int32 nFullAngle = nPostScriptAngle / 10; + sal_Int32 nTenthAngle = nPostScriptAngle % 10; + + sal_Char pRotate [48]; + sal_Int32 nChar = 0; + + nChar = psp::getValueOf (nFullAngle, pRotate); + nChar += psp::appendStr (".", pRotate + nChar); + nChar += psp::getValueOf (nTenthAngle, pRotate + nChar); + nChar += psp::appendStr (" rotate\n", pRotate + nChar); + + WritePS (mpPageBody, pRotate); +} + +void +PrinterGfx::PSPointOp (const Point& rPoint, const sal_Char* pOperator) +{ + sal_Char pPSCommand [48]; + sal_Int32 nChar = 0; + + nChar = psp::getValueOf (rPoint.X(), pPSCommand); + nChar += psp::appendStr (" ", pPSCommand + nChar); + nChar += psp::getValueOf (rPoint.Y(), pPSCommand + nChar); + nChar += psp::appendStr (" ", pPSCommand + nChar); + nChar += psp::appendStr (pOperator, pPSCommand + nChar); + nChar += psp::appendStr ("\n", pPSCommand + nChar); + + DBG_ASSERT (nChar < 48, "Buffer overflow in PSPointOp"); + + WritePS (mpPageBody, pPSCommand); +} + +void +PrinterGfx::PSTranslate (const Point& rPoint) +{ + PSPointOp (rPoint, "translate"); +} + +void +PrinterGfx::PSMoveTo (const Point& rPoint) +{ + PSPointOp (rPoint, "moveto"); +} + +void +PrinterGfx::PSLineTo (const Point& rPoint) +{ + PSPointOp (rPoint, "lineto"); +} + +void +PrinterGfx::PSRMoveTo (sal_Int32 nDx, sal_Int32 nDy) +{ + Point aPoint(nDx, nDy); + PSPointOp (aPoint, "rmoveto"); +} + +/* get a compressed representation of the path information */ + +#define DEBUG_BINPATH 0 + +void +PrinterGfx::PSBinLineTo (const Point& rCurrent, Point& rOld, sal_Int32& nColumn) +{ +#if (DEBUG_BINPATH == 1) + PSLineTo (rCurrent); +#else + PSBinPath (rCurrent, rOld, lineto, nColumn); +#endif +} + +void +PrinterGfx::PSBinMoveTo (const Point& rCurrent, Point& rOld, sal_Int32& nColumn) +{ +#if (DEBUG_BINPATH == 1) + PSMoveTo (rCurrent); +#else + PSBinPath (rCurrent, rOld, moveto, nColumn); +#endif +} + +void +PrinterGfx::PSBinStartPath () +{ +#if (DEBUG_BINPATH == 1) + WritePS (mpPageBody, "% PSBinStartPath\n"); +#else + WritePS (mpPageBody, "readpath\n" ); +#endif +} + +void +PrinterGfx::PSBinEndPath () +{ +#if (DEBUG_BINPATH == 1) + WritePS (mpPageBody, "% PSBinEndPath\n"); +#else + WritePS (mpPageBody, "~\n"); +#endif +} + +void +PrinterGfx::PSBinCurrentPath (sal_uInt32 nPoints, const Point* pPath) +{ + // create the path + Point aPoint (0, 0); + sal_Int32 nColumn = 0; + + PSBinStartPath (); + PSBinMoveTo (*pPath, aPoint, nColumn); + for (unsigned int i = 1; i < nPoints; i++) + PSBinLineTo (pPath[i], aPoint, nColumn); + PSBinEndPath (); +} + +void +PrinterGfx::PSBinPath (const Point& rCurrent, Point& rOld, + pspath_t eType, sal_Int32& nColumn) +{ + sal_Char pPath[48]; + sal_Int32 nChar; + + // create the hex representation of the dx and dy path shift, store the field + // width as it is needed for the building the command + sal_Int32 nXPrec = getAlignedHexValueOf (rCurrent.X() - rOld.X(), pPath + 1); + sal_Int32 nYPrec = getAlignedHexValueOf (rCurrent.Y() - rOld.Y(), pPath + 1 + nXPrec); + pPath [ 1 + nXPrec + nYPrec ] = 0; + + // build the command, it is a char with bit represention 000cxxyy + // c represents the char, xx and yy repr. the field width of the dx and dy shift, + // dx and dy represent the number of bytes to read after the opcode + sal_Char cCmd = (eType == lineto ? (sal_Char)0x00 : (sal_Char)0x10); + switch (nYPrec) + { + case 2: break; + case 4: cCmd |= 0x01; break; + case 6: cCmd |= 0x02; break; + case 8: cCmd |= 0x03; break; + default: DBG_ERROR ("invalid x precision in binary path"); + } + switch (nXPrec) + { + case 2: break; + case 4: cCmd |= 0x04; break; + case 6: cCmd |= 0x08; break; + case 8: cCmd |= 0x0c; break; + default: DBG_ERROR ("invalid y precision in binary path"); + } + cCmd += 'A'; + pPath[0] = cCmd; + + // write the command to file, + // line breaking at column nMaxTextColumn (80) + nChar = 1 + nXPrec + nYPrec; + if ((nColumn + nChar) > nMaxTextColumn) + { + sal_Int32 nSegment = nMaxTextColumn - nColumn; + + WritePS (mpPageBody, pPath, nSegment); + WritePS (mpPageBody, "\n", 1); + WritePS (mpPageBody, pPath + nSegment, nChar - nSegment); + + nColumn = nChar - nSegment; + } + else + { + WritePS (mpPageBody, pPath, nChar); + + nColumn += nChar; + } + + rOld = rCurrent; +} + +void +PrinterGfx::PSScale (double fScaleX, double fScaleY) +{ + sal_Char pScale [48]; + sal_Int32 nChar = 0; + + nChar = psp::getValueOfDouble (pScale, fScaleX, 5); + nChar += psp::appendStr (" ", pScale + nChar); + nChar += psp::getValueOfDouble (pScale + nChar, fScaleY, 5); + nChar += psp::appendStr (" scale\n", pScale + nChar); + + WritePS (mpPageBody, pScale); +} + +/* psshowtext helper routines: draw an hex string for show/xshow */ +void +PrinterGfx::PSHexString (const sal_uChar* pString, sal_Int16 nLen) +{ + sal_Char pHexString [128]; + sal_Int32 nChar = 0; + + nChar = psp::appendStr ("<", pHexString); + for (int i = 0; i < nLen; i++) + { + if (nChar >= (nMaxTextColumn - 1)) + { + nChar += psp::appendStr ("\n", pHexString + nChar); + WritePS (mpPageBody, pHexString, nChar); + nChar = 0; + } + nChar += psp::getHexValueOf ((sal_Int32)pString[i], pHexString + nChar); + } + + nChar += psp::appendStr (">\n", pHexString + nChar); + WritePS (mpPageBody, pHexString, nChar); +} + +/* psshowtext helper routines: draw an array for xshow ps operator */ +void +PrinterGfx::PSDeltaArray (const sal_Int32 *pArray, sal_Int16 nEntries) +{ + sal_Char pPSArray [128]; + sal_Int32 nChar = 0; + + nChar = psp::appendStr ("[", pPSArray + nChar); + nChar += psp::getValueOf (pArray[0], pPSArray + nChar); + + for (int i = 1; i < nEntries; i++) + { + if (nChar >= (nMaxTextColumn - 1)) + { + nChar += psp::appendStr ("\n", pPSArray + nChar); + WritePS (mpPageBody, pPSArray, nChar); + nChar = 0; + } + + nChar += psp::appendStr (" ", pPSArray + nChar); + nChar += psp::getValueOf (pArray[i] - pArray[i-1], pPSArray + nChar); + } + + nChar += psp::appendStr (" 0]\n", pPSArray + nChar); + WritePS (mpPageBody, pPSArray); +} + +/* the DrawText equivalent, pDeltaArray may be NULL. For Type1 fonts or single byte + * fonts in general nBytes and nGlyphs is the same. For printer resident Composite + * fonts it may be different (these fonts may be SJIS encoded for example) */ +void +PrinterGfx::PSShowText (const sal_uChar* pStr, sal_Int16 nGlyphs, sal_Int16 nBytes, + const sal_Int32* pDeltaArray) +{ + PSSetColor (maTextColor); + PSSetColor (); + PSSetFont (); + // rotate the user coordinate system + if (mnTextAngle != 0) + { + PSGSave (); + PSRotate (mnTextAngle); + } + + sal_Char pBuffer[256]; + if( maVirtualStatus.mbArtBold ) + { + sal_Int32 nLW = maVirtualStatus.mnTextWidth; + if( nLW == 0 ) + nLW = maVirtualStatus.mnTextHeight; + else + nLW = nLW < maVirtualStatus.mnTextHeight ? nLW : maVirtualStatus.mnTextHeight; + psp::getValueOfDouble( pBuffer, (double)nLW / 30.0 ); + } + // dispatch to the drawing method + if (pDeltaArray == NULL) + { + PSHexString (pStr, nBytes); + + if( maVirtualStatus.mbArtBold ) + { + WritePS( mpPageBody, pBuffer ); + WritePS( mpPageBody, " bshow\n" ); + } + else + WritePS (mpPageBody, "show\n"); + } + else + { + PSHexString (pStr, nBytes); + PSDeltaArray (pDeltaArray, nGlyphs - 1); + if( maVirtualStatus.mbArtBold ) + { + WritePS( mpPageBody, pBuffer ); + WritePS( mpPageBody, " bxshow\n" ); + } + else + WritePS (mpPageBody, "xshow\n"); + } + + // restore the user coordinate system + if (mnTextAngle != 0) + PSGRestore (); +} + +void +PrinterGfx::PSComment( const sal_Char* pComment ) +{ + const sal_Char* pLast = pComment; + while( pComment && *pComment ) + { + while( *pComment && *pComment != '\n' && *pComment != '\r' ) + pComment++; + if( pComment - pLast > 1 ) + { + WritePS( mpPageBody, "% ", 2 ); + WritePS( mpPageBody, pLast, pComment - pLast ); + WritePS( mpPageBody, "\n", 1 ); + } + if( *pComment ) + pLast = ++pComment; + } +} + +sal_Bool +PrinterGfx::DrawEPS( const Rectangle& rBoundingBox, void* pPtr, sal_uInt32 nSize ) +{ + if( nSize == 0 ) + return sal_True; + if( ! mpPageBody ) + return sal_False; + + sal_Bool bSuccess = sal_False; + + // first search the BoundingBox of the EPS data + SvMemoryStream aStream( pPtr, nSize, STREAM_READ ); + aStream.Seek( STREAM_SEEK_TO_BEGIN ); + ByteString aLine; + + ByteString aDocTitle; + double fLeft = 0, fRight = 0, fTop = 0, fBottom = 0; + bool bEndComments = false; + while( ! aStream.IsEof() + && ( ( fLeft == 0 && fRight == 0 && fTop == 0 && fBottom == 0 ) || + ( aDocTitle.Len() == 0 && bEndComments == false ) ) + ) + { + aStream.ReadLine( aLine ); + if( aLine.Len() > 1 && aLine.GetChar( 0 ) == '%' ) + { + char cChar = aLine.GetChar(1); + if( cChar == '%' ) + { + if( aLine.CompareIgnoreCaseToAscii( "%%BoundingBox:", 14 ) == COMPARE_EQUAL ) + { + aLine = WhitespaceToSpace( aLine.GetToken( 1, ':' ) ); + if( aLine.Len() && aLine.Search( "atend" ) == STRING_NOTFOUND ) + { + fLeft = StringToDouble( GetCommandLineToken( 0, aLine ) ); + fBottom = StringToDouble( GetCommandLineToken( 1, aLine ) ); + fRight = StringToDouble( GetCommandLineToken( 2, aLine ) ); + fTop = StringToDouble( GetCommandLineToken( 3, aLine ) ); + } + } + else if( aLine.CompareIgnoreCaseToAscii( "%%Title:", 8 ) == COMPARE_EQUAL ) + aDocTitle = WhitespaceToSpace( aLine.Copy( 8 ) ); + else if( aLine.CompareIgnoreCaseToAscii( "%%EndComments", 13 ) == COMPARE_EQUAL ) + bEndComments = true; + } + else if( cChar == ' ' || cChar == '\t' || cChar == '\r' || cChar == '\n' ) + bEndComments = true; + } + else + bEndComments = true; + } + + static sal_uInt16 nEps = 0; + if( ! aDocTitle.Len() ) + aDocTitle = ByteString::CreateFromInt32( (sal_Int32)(nEps++) ); + + if( fLeft != fRight && fTop != fBottom ) + { + double fScaleX = (double)rBoundingBox.GetWidth()/(fRight-fLeft); + double fScaleY = -(double)rBoundingBox.GetHeight()/(fTop-fBottom); + Point aTranslatePoint( (int)(rBoundingBox.Left()-fLeft*fScaleX), + (int)(rBoundingBox.Bottom()+1-fBottom*fScaleY) ); + // prepare EPS + WritePS( mpPageBody, + "/b4_Inc_state save def\n" + "/dict_count countdictstack def\n" + "/op_count count 1 sub def\n" + "userdict begin\n" + "/showpage {} def\n" + "0 setgray 0 setlinecap 1 setlinewidth 0 setlinejoin\n" + "10 setmiterlimit [] 0 setdash newpath\n" + "/languagelevel where\n" + "{pop languagelevel\n" + "1 ne\n" + " {false setstrokeadjust false setoverprint\n" + " } if\n" + "}if\n" ); + // set up clip path and scale + BeginSetClipRegion( 1 ); + UnionClipRegion( rBoundingBox.Left(), rBoundingBox.Top(), rBoundingBox.GetWidth(), rBoundingBox.GetHeight() ); + EndSetClipRegion(); + PSTranslate( aTranslatePoint ); + PSScale( fScaleX, fScaleY ); + + // DSC requires BeginDocument + WritePS( mpPageBody, "%%BeginDocument: " ); + WritePS( mpPageBody, aDocTitle ); + WritePS( mpPageBody, "\n" ); + + // write the EPS data + sal_uInt64 nOutLength; + mpPageBody->write( pPtr, nSize, nOutLength ); + bSuccess = nOutLength == nSize; + + // corresponding EndDocument + if( ((char*)pPtr)[ nSize-1 ] != '\n' ) + WritePS( mpPageBody, "\n" ); + WritePS( mpPageBody, "%%EndDocument\n" ); + + // clean up EPS + WritePS( mpPageBody, + "count op_count sub {pop} repeat\n" + "countdictstack dict_count sub {end} repeat\n" + "b4_Inc_state restore\n" ); + } + return bSuccess; +} diff --git a/vcl/unx/generic/printergfx/glyphset.cxx b/vcl/unx/generic/printergfx/glyphset.cxx new file mode 100644 index 000000000000..9b0f5fb99c43 --- /dev/null +++ b/vcl/unx/generic/printergfx/glyphset.cxx @@ -0,0 +1,942 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include "glyphset.hxx" +#include "psputil.hxx" + +#include "sft.hxx" + +#include "printergfx.hxx" +#include "fontsubset.hxx" +#include "vcl/fontmanager.hxx" + +#include "osl/thread.h" + +#include "sal/alloca.h" + +#include "rtl/ustring.hxx" +#include "rtl/strbuf.hxx" + +#include <set> +#include <map> +#include <algorithm> + +using namespace vcl; +using namespace psp; +using namespace rtl; + +GlyphSet::GlyphSet () + : mnFontID (-1), + mbVertical (0), + mbUseFontEncoding (false) +{} + +GlyphSet::GlyphSet (sal_Int32 nFontID, sal_Bool bVertical) + : mnFontID (nFontID), + mbVertical (bVertical) +{ + PrintFontManager &rMgr = PrintFontManager::get(); + meBaseType = rMgr.getFontType (mnFontID); + maBaseName = OUStringToOString (rMgr.getPSName(mnFontID), + RTL_TEXTENCODING_ASCII_US); + mnBaseEncoding = rMgr.getFontEncoding(mnFontID); + mbUseFontEncoding = rMgr.getUseOnlyFontEncoding(mnFontID); +} + +GlyphSet::~GlyphSet () +{ + /* FIXME delete the glyphlist ??? */ +} + +sal_Int32 +GlyphSet::GetFontID () +{ + return mnFontID; +} + +fonttype::type +GlyphSet::GetFontType () +{ + return meBaseType; +} + +sal_Bool +GlyphSet::IsVertical () +{ + return mbVertical; +} + +sal_Bool +GlyphSet::SetFont (sal_Int32 nFontID, sal_Bool bVertical) +{ + if (mnFontID != -1) + return sal_False; + + mnFontID = nFontID; + mbVertical = bVertical; + + PrintFontManager &rMgr = PrintFontManager::get(); + meBaseType = rMgr.getFontType (mnFontID); + maBaseName = OUStringToOString (rMgr.getPSName(mnFontID), + RTL_TEXTENCODING_ASCII_US); + mnBaseEncoding = rMgr.getFontEncoding(mnFontID); + mbUseFontEncoding = rMgr.getUseOnlyFontEncoding(mnFontID); + + return sal_True; +} + +sal_Bool +GlyphSet::GetCharID ( + sal_Unicode nChar, + sal_uChar* nOutGlyphID, + sal_Int32* nOutGlyphSetID + ) +{ + return LookupCharID (nChar, nOutGlyphID, nOutGlyphSetID) + || AddCharID (nChar, nOutGlyphID, nOutGlyphSetID); +} + +sal_Bool +GlyphSet::GetGlyphID ( + sal_uInt32 nGlyph, + sal_Unicode nUnicode, + sal_uChar* nOutGlyphID, + sal_Int32* nOutGlyphSetID + ) +{ + return LookupGlyphID (nGlyph, nOutGlyphID, nOutGlyphSetID) + || AddGlyphID (nGlyph, nUnicode, nOutGlyphID, nOutGlyphSetID); +} + +sal_Bool +GlyphSet::LookupCharID ( + sal_Unicode nChar, + sal_uChar* nOutGlyphID, + sal_Int32* nOutGlyphSetID + ) +{ + char_list_t::iterator aGlyphSet; + sal_Int32 nGlyphSetID; + + // loop thru all the font subsets + for (aGlyphSet = maCharList.begin(), nGlyphSetID = 1; + aGlyphSet != maCharList.end(); + ++aGlyphSet, nGlyphSetID++) + { + // check every subset if it contains the queried unicode char + char_map_t::const_iterator aGlyph = (*aGlyphSet).find (nChar); + if (aGlyph != (*aGlyphSet).end()) + { + // success: found the unicode char, return the glyphid and the glyphsetid + *nOutGlyphSetID = nGlyphSetID; + *nOutGlyphID = (*aGlyph).second; + return sal_True; + } + } + + *nOutGlyphSetID = -1; + *nOutGlyphID = 0; + return sal_False; +} + +sal_Bool +GlyphSet::LookupGlyphID ( + sal_uInt32 nGlyph, + sal_uChar* nOutGlyphID, + sal_Int32* nOutGlyphSetID + ) +{ + glyph_list_t::iterator aGlyphSet; + sal_Int32 nGlyphSetID; + + // loop thru all the font subsets + for (aGlyphSet = maGlyphList.begin(), nGlyphSetID = 1; + aGlyphSet != maGlyphList.end(); + ++aGlyphSet, nGlyphSetID++) + { + // check every subset if it contains the queried unicode char + glyph_map_t::const_iterator aGlyph = (*aGlyphSet).find (nGlyph); + if (aGlyph != (*aGlyphSet).end()) + { + // success: found the glyph id, return the mapped glyphid and the glyphsetid + *nOutGlyphSetID = nGlyphSetID; + *nOutGlyphID = (*aGlyph).second; + return sal_True; + } + } + + *nOutGlyphSetID = -1; + *nOutGlyphID = 0; + return sal_False; +} + +sal_uChar +GlyphSet::GetAnsiMapping (sal_Unicode nUnicodeChar) +{ + static rtl_UnicodeToTextConverter aConverter = + rtl_createUnicodeToTextConverter(RTL_TEXTENCODING_MS_1252); + static rtl_UnicodeToTextContext aContext = + rtl_createUnicodeToTextContext( aConverter ); + + sal_Char nAnsiChar; + sal_uInt32 nCvtInfo; + sal_Size nCvtChars; + const sal_uInt32 nCvtFlags = RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR + | RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR; + + sal_Size nSize = rtl_convertUnicodeToText( aConverter, aContext, + &nUnicodeChar, 1, &nAnsiChar, 1, + nCvtFlags, &nCvtInfo, &nCvtChars ); + + return nSize == 1 ? (sal_uChar)nAnsiChar : (sal_uChar)0; +} + +sal_uChar +GlyphSet::GetSymbolMapping (sal_Unicode nUnicodeChar) +{ + if (0x0000 < nUnicodeChar && nUnicodeChar < 0x0100) + return (sal_uChar)nUnicodeChar; + if (0xf000 < nUnicodeChar && nUnicodeChar < 0xf100) + return (sal_uChar)nUnicodeChar; + + return 0; +} + +void +GlyphSet::AddNotdef (char_map_t &rCharMap) +{ + if (rCharMap.size() == 0) + rCharMap[0] = 0; +} + +void +GlyphSet::AddNotdef (glyph_map_t &rGlyphMap) +{ + if (rGlyphMap.size() == 0) + rGlyphMap[0] = 0; +} +sal_Bool +GlyphSet::AddCharID ( + sal_Unicode nChar, + sal_uChar* nOutGlyphID, + sal_Int32* nOutGlyphSetID + ) +{ + sal_uChar nMappedChar; + + // XXX important: avoid to reencode type1 symbol fonts + if (mnBaseEncoding == RTL_TEXTENCODING_SYMBOL) + nMappedChar = GetSymbolMapping (nChar); + else + nMappedChar = GetAnsiMapping (nChar); + + // create an empty glyphmap that is reserved for iso1252 encoded glyphs + // (or -- unencoded -- symbol glyphs) and a second map that takes any other + if (maCharList.empty()) + { + char_map_t aMap, aMapp; + + maCharList.push_back (aMap); + maCharList.push_back (aMapp); + } + // if the last map is full, create a new one + if ((!nMappedChar) && (maCharList.back().size() == 255)) + { + char_map_t aMap; + maCharList.push_back (aMap); + } + + // insert a new glyph in the font subset + if (nMappedChar) + { + // always put iso1252 chars into the first map, map them on itself + char_map_t& aGlyphSet = maCharList.front(); + AddNotdef (aGlyphSet); + + aGlyphSet [nChar] = nMappedChar; + *nOutGlyphSetID = 1; + *nOutGlyphID = nMappedChar; + } + else + { + // other chars are just appended to the list + char_map_t& aGlyphSet = maCharList.back(); + AddNotdef (aGlyphSet); + + int nSize = aGlyphSet.size(); + + aGlyphSet [nChar] = nSize; + *nOutGlyphSetID = maCharList.size(); + *nOutGlyphID = aGlyphSet [nChar]; + } + + return sal_True; +} + +sal_Bool +GlyphSet::AddGlyphID ( + sal_uInt32 nGlyph, + sal_Unicode nUnicode, + sal_uChar* nOutGlyphID, + sal_Int32* nOutGlyphSetID + ) +{ + sal_uChar nMappedChar; + + // XXX important: avoid to reencode type1 symbol fonts + if (mnBaseEncoding == RTL_TEXTENCODING_SYMBOL) + nMappedChar = GetSymbolMapping (nUnicode); + else + nMappedChar = GetAnsiMapping (nUnicode); + + // create an empty glyphmap that is reserved for iso1252 encoded glyphs + // (or -- unencoded -- symbol glyphs) and a second map that takes any other + if (maGlyphList.empty()) + { + glyph_map_t aMap, aMapp; + + maGlyphList.push_back (aMap); + maGlyphList.push_back (aMapp); + } + // if the last map is full, create a new one + if ((!nMappedChar) && (maGlyphList.back().size() == 255)) + { + glyph_map_t aMap; + maGlyphList.push_back (aMap); + } + + // insert a new glyph in the font subset + if (nMappedChar) + { + // always put iso1252 chars into the first map, map them on itself + glyph_map_t& aGlyphSet = maGlyphList.front(); + AddNotdef (aGlyphSet); + + aGlyphSet [nGlyph] = nMappedChar; + *nOutGlyphSetID = 1; + *nOutGlyphID = nMappedChar; + } + else + { + // other chars are just appended to the list + glyph_map_t& aGlyphSet = maGlyphList.back(); + AddNotdef (aGlyphSet); + + int nSize = aGlyphSet.size(); + + aGlyphSet [nGlyph] = nSize; + *nOutGlyphSetID = maGlyphList.size(); + *nOutGlyphID = aGlyphSet [nGlyph]; + } + + return sal_True; +} + +OString +GlyphSet::GetCharSetName (sal_Int32 nGlyphSetID) +{ + if (meBaseType == fonttype::TrueType) + { + OStringBuffer aSetName( maBaseName.getLength() + 32 ); + aSetName.append( maBaseName ); + aSetName.append( "FID" ); + aSetName.append( mnFontID ); + aSetName.append( mbVertical ? "VCSet" : "HCSet" ); + aSetName.append( nGlyphSetID ); + return aSetName.makeStringAndClear(); + } + else + /* (meBaseType == fonttype::Type1 || meBaseType == fonttype::Builtin) */ + { + return maBaseName; + } +} + +OString +GlyphSet::GetGlyphSetName (sal_Int32 nGlyphSetID) +{ + if (meBaseType == fonttype::TrueType) + { + OStringBuffer aSetName( maBaseName.getLength() + 32 ); + aSetName.append( maBaseName ); + aSetName.append( "FID" ); + aSetName.append( mnFontID ); + aSetName.append( mbVertical ? "VGSet" : "HGSet" ); + aSetName.append( nGlyphSetID ); + return aSetName.makeStringAndClear(); + } + else + /* (meBaseType == fonttype::Type1 || meBaseType == fonttype::Builtin) */ + { + return maBaseName; + } +} + +sal_Int32 +GlyphSet::GetGlyphSetEncoding (sal_Int32 nGlyphSetID) +{ + if (meBaseType == fonttype::TrueType) + return RTL_TEXTENCODING_DONTKNOW; + else + { + /* (meBaseType == fonttype::Type1 || meBaseType == fonttype::Builtin) */ + if (mnBaseEncoding == RTL_TEXTENCODING_SYMBOL) + return RTL_TEXTENCODING_SYMBOL; + else + return nGlyphSetID == 1 ? RTL_TEXTENCODING_MS_1252 + : RTL_TEXTENCODING_USER_START + nGlyphSetID; + } +} + +OString +GlyphSet::GetGlyphSetEncodingName (rtl_TextEncoding nEnc, const OString &rFontName) +{ + if ( nEnc == RTL_TEXTENCODING_MS_1252 + || nEnc == RTL_TEXTENCODING_ISO_8859_1) + { + return OString("ISO1252Encoding"); + } + else + if (nEnc >= RTL_TEXTENCODING_USER_START && nEnc <= RTL_TEXTENCODING_USER_END) + { + return rFontName + + OString("Enc") + + OString::valueOf ((sal_Int32)(nEnc - RTL_TEXTENCODING_USER_START)); + } + else + { + return OString(); + } +} + +OString +GlyphSet::GetGlyphSetEncodingName (sal_Int32 nGlyphSetID) +{ + return GetGlyphSetEncodingName (GetGlyphSetEncoding(nGlyphSetID), maBaseName); +} + +void +GlyphSet::PSDefineReencodedFont (osl::File* pOutFile, sal_Int32 nGlyphSetID) +{ + // only for ps fonts + if ((meBaseType != fonttype::Builtin) && (meBaseType != fonttype::Type1)) + return; + + sal_Char pEncodingVector [256]; + sal_Int32 nSize = 0; + + nSize += psp::appendStr ("(", pEncodingVector + nSize); + nSize += psp::appendStr (GetReencodedFontName(nGlyphSetID), + pEncodingVector + nSize); + nSize += psp::appendStr (") cvn (", pEncodingVector + nSize); + nSize += psp::appendStr (maBaseName.getStr(), + pEncodingVector + nSize); + nSize += psp::appendStr (") cvn ", pEncodingVector + nSize); + nSize += psp::appendStr (GetGlyphSetEncodingName(nGlyphSetID), + pEncodingVector + nSize); + nSize += psp::appendStr (" psp_definefont\n", + pEncodingVector + nSize); + + psp::WritePS (pOutFile, pEncodingVector); +} + +OString +GlyphSet::GetReencodedFontName (rtl_TextEncoding nEnc, const OString &rFontName) +{ + if ( nEnc == RTL_TEXTENCODING_MS_1252 + || nEnc == RTL_TEXTENCODING_ISO_8859_1) + { + return rFontName + + OString("-iso1252"); + } + else + if (nEnc >= RTL_TEXTENCODING_USER_START && nEnc <= RTL_TEXTENCODING_USER_END) + { + return rFontName + + OString("-enc") + + OString::valueOf ((sal_Int32)(nEnc - RTL_TEXTENCODING_USER_START)); + } + else + { + return OString(); + } +} + +OString +GlyphSet::GetReencodedFontName (sal_Int32 nGlyphSetID) +{ + return GetReencodedFontName (GetGlyphSetEncoding(nGlyphSetID), maBaseName); +} + +void GlyphSet::DrawGlyphs( + PrinterGfx& rGfx, + const Point& rPoint, + const sal_uInt32* pGlyphIds, + const sal_Unicode* pUnicodes, + sal_Int16 nLen, + const sal_Int32* pDeltaArray ) +{ + sal_uChar *pGlyphID = (sal_uChar*)alloca (nLen * sizeof(sal_uChar)); + sal_Int32 *pGlyphSetID = (sal_Int32*)alloca (nLen * sizeof(sal_Int32)); + std::set< sal_Int32 > aGlyphSet; + + // convert unicode to font glyph id and font subset + for (int nChar = 0; nChar < nLen; nChar++) + { + GetGlyphID (pGlyphIds[nChar], pUnicodes[nChar], pGlyphID + nChar, pGlyphSetID + nChar); + aGlyphSet.insert (pGlyphSetID[nChar]); + } + + // loop over all glyph sets to detect substrings that can be xshown together + // without changing the postscript font + sal_Int32 *pDeltaSubset = (sal_Int32*)alloca (nLen * sizeof(sal_Int32)); + sal_uChar *pGlyphSubset = (sal_uChar*)alloca (nLen * sizeof(sal_uChar)); + + std::set< sal_Int32 >::iterator aSet; + for (aSet = aGlyphSet.begin(); aSet != aGlyphSet.end(); ++aSet) + { + Point aPoint = rPoint; + sal_Int32 nOffset = 0; + sal_Int32 nGlyphs = 0; + sal_Int32 nChar; + + // get offset to first glyph + for (nChar = 0; (nChar < nLen) && (pGlyphSetID[nChar] != *aSet); nChar++) + { + nOffset = pDeltaArray [nChar]; + } + + // loop over all chars to extract those that share the current glyph set + for (nChar = 0; nChar < nLen; nChar++) + { + if (pGlyphSetID[nChar] == *aSet) + { + pGlyphSubset [nGlyphs] = pGlyphID [nChar]; + // the offset to the next glyph is determined by the glyph in + // front of the next glyph with the same glyphset id + // most often, this will be the current glyph + while ((nChar + 1) < nLen) + { + if (pGlyphSetID[nChar + 1] == *aSet) + break; + else + nChar += 1; + } + pDeltaSubset [nGlyphs] = pDeltaArray[nChar] - nOffset; + + nGlyphs += 1; + } + } + + // show the text using the PrinterGfx text api + aPoint.Move (nOffset, 0); + + OString aGlyphSetName(GetGlyphSetName(*aSet)); + rGfx.PSSetFont (aGlyphSetName, GetGlyphSetEncoding(*aSet)); + rGfx.PSMoveTo (aPoint); + rGfx.PSShowText (pGlyphSubset, nGlyphs, nGlyphs, nGlyphs > 1 ? pDeltaSubset : NULL); + } +} + +void +GlyphSet::DrawText (PrinterGfx &rGfx, const Point& rPoint, + const sal_Unicode* pStr, sal_Int16 nLen, const sal_Int32* pDeltaArray) +{ + // dispatch to the impl method + if (pDeltaArray == NULL) + ImplDrawText (rGfx, rPoint, pStr, nLen); + else + ImplDrawText (rGfx, rPoint, pStr, nLen, pDeltaArray); +} + +void +GlyphSet::ImplDrawText (PrinterGfx &rGfx, const Point& rPoint, + const sal_Unicode* pStr, sal_Int16 nLen) +{ + rGfx.PSMoveTo (rPoint); + + if( mbUseFontEncoding ) + { + OString aPSName( OUStringToOString( rGfx.GetFontMgr().getPSName( mnFontID ), RTL_TEXTENCODING_ISO_8859_1 ) ); + OString aBytes( OUStringToOString( OUString( pStr, nLen ), mnBaseEncoding ) ); + rGfx.PSSetFont( aPSName, mnBaseEncoding ); + rGfx.PSShowText( (const unsigned char*)aBytes.getStr(), nLen, aBytes.getLength() ); + return; + } + + int nChar; + sal_uChar *pGlyphID = (sal_uChar*)alloca (nLen * sizeof(sal_uChar)); + sal_Int32 *pGlyphSetID = (sal_Int32*)alloca (nLen * sizeof(sal_Int32)); + + // convert unicode to glyph id and char set (font subset) + for (nChar = 0; nChar < nLen; nChar++) + GetCharID (pStr[nChar], pGlyphID + nChar, pGlyphSetID + nChar); + + // loop over the string to draw subsequent pieces of chars + // with the same postscript font + for (nChar = 0; nChar < nLen; /* atend */) + { + sal_Int32 nGlyphSetID = pGlyphSetID [nChar]; + sal_Int32 nGlyphs = 1; + for (int nNextChar = nChar + 1; nNextChar < nLen; nNextChar++) + { + if (pGlyphSetID[nNextChar] == nGlyphSetID) + nGlyphs++; + else + break; + } + + // show the text using the PrinterGfx text api + OString aGlyphSetName(GetCharSetName(nGlyphSetID)); + rGfx.PSSetFont (aGlyphSetName, GetGlyphSetEncoding(nGlyphSetID)); + rGfx.PSShowText (pGlyphID + nChar, nGlyphs, nGlyphs); + + nChar += nGlyphs; + } +} + +void +GlyphSet::ImplDrawText (PrinterGfx &rGfx, const Point& rPoint, + const sal_Unicode* pStr, sal_Int16 nLen, const sal_Int32* pDeltaArray) +{ + if( mbUseFontEncoding ) + { + OString aPSName( OUStringToOString( rGfx.GetFontMgr().getPSName( mnFontID ), RTL_TEXTENCODING_ISO_8859_1 ) ); + OString aBytes( OUStringToOString( OUString( pStr, nLen ), mnBaseEncoding ) ); + rGfx.PSMoveTo( rPoint ); + rGfx.PSSetFont( aPSName, mnBaseEncoding ); + rGfx.PSShowText( (const unsigned char*)aBytes.getStr(), nLen, aBytes.getLength(), pDeltaArray ); + return; + } + + sal_uChar *pGlyphID = (sal_uChar*)alloca (nLen * sizeof(sal_uChar)); + sal_Int32 *pGlyphSetID = (sal_Int32*)alloca (nLen * sizeof(sal_Int32)); + std::set< sal_Int32 > aGlyphSet; + + // convert unicode to font glyph id and font subset + for (int nChar = 0; nChar < nLen; nChar++) + { + GetCharID (pStr[nChar], pGlyphID + nChar, pGlyphSetID + nChar); + aGlyphSet.insert (pGlyphSetID[nChar]); + } + + // loop over all glyph sets to detect substrings that can be xshown together + // without changing the postscript font + sal_Int32 *pDeltaSubset = (sal_Int32*)alloca (nLen * sizeof(sal_Int32)); + sal_uChar *pGlyphSubset = (sal_uChar*)alloca (nLen * sizeof(sal_uChar)); + + std::set< sal_Int32 >::iterator aSet; + for (aSet = aGlyphSet.begin(); aSet != aGlyphSet.end(); ++aSet) + { + Point aPoint = rPoint; + sal_Int32 nOffset = 0; + sal_Int32 nGlyphs = 0; + sal_Int32 nChar; + + // get offset to first glyph + for (nChar = 0; (nChar < nLen) && (pGlyphSetID[nChar] != *aSet); nChar++) + { + nOffset = pDeltaArray [nChar]; + } + + // loop over all chars to extract those that share the current glyph set + for (nChar = 0; nChar < nLen; nChar++) + { + if (pGlyphSetID[nChar] == *aSet) + { + pGlyphSubset [nGlyphs] = pGlyphID [nChar]; + // the offset to the next glyph is determined by the glyph in + // front of the next glyph with the same glyphset id + // most often, this will be the current glyph + while ((nChar + 1) < nLen) + { + if (pGlyphSetID[nChar + 1] == *aSet) + break; + else + nChar += 1; + } + pDeltaSubset [nGlyphs] = pDeltaArray[nChar] - nOffset; + + nGlyphs += 1; + } + } + + // show the text using the PrinterGfx text api + aPoint.Move (nOffset, 0); + + OString aGlyphSetName(GetCharSetName(*aSet)); + rGfx.PSSetFont (aGlyphSetName, GetGlyphSetEncoding(*aSet)); + rGfx.PSMoveTo (aPoint); + rGfx.PSShowText (pGlyphSubset, nGlyphs, nGlyphs, nGlyphs > 1 ? pDeltaSubset : NULL); + } +} + +sal_Bool +GlyphSet::PSUploadEncoding(osl::File* pOutFile, PrinterGfx &rGfx) +{ + // only for ps fonts + if ((meBaseType != fonttype::Builtin) && (meBaseType != fonttype::Type1)) + return sal_False; + if (mnBaseEncoding == RTL_TEXTENCODING_SYMBOL) + return sal_False; + + PrintFontManager &rMgr = rGfx.GetFontMgr(); + + // loop thru all the font subsets + sal_Int32 nGlyphSetID = 0; + char_list_t::iterator aGlyphSet; + for (aGlyphSet = maCharList.begin(); aGlyphSet != maCharList.end(); aGlyphSet++) + { + ++nGlyphSetID; + + if (nGlyphSetID == 1) // latin1 page uses global reencoding table + { + PSDefineReencodedFont (pOutFile, nGlyphSetID); + continue; + } + if ((*aGlyphSet).size() == 0) // empty set, doesn't need reencoding + { + continue; + } + + // create reencoding table + + sal_Char pEncodingVector [256]; + sal_Int32 nSize = 0; + + nSize += psp::appendStr ("/", + pEncodingVector + nSize); + nSize += psp::appendStr (GetGlyphSetEncodingName(nGlyphSetID), + pEncodingVector + nSize); + nSize += psp::appendStr (" [ ", + pEncodingVector + nSize); + + // need a list of glyphs, sorted by glyphid + typedef std::map< sal_uInt8, sal_Unicode > ps_mapping_t; + typedef ps_mapping_t::value_type ps_value_t; + ps_mapping_t aSortedGlyphSet; + + char_map_t::const_iterator aUnsortedGlyph; + for (aUnsortedGlyph = (*aGlyphSet).begin(); + aUnsortedGlyph != (*aGlyphSet).end(); + ++aUnsortedGlyph) + { + aSortedGlyphSet.insert(ps_value_t((*aUnsortedGlyph).second, + (*aUnsortedGlyph).first)); + } + + ps_mapping_t::const_iterator aSortedGlyph; + // loop thru all the glyphs in the subset + for (aSortedGlyph = (aSortedGlyphSet).begin(); + aSortedGlyph != (aSortedGlyphSet).end(); + ++aSortedGlyph) + { + nSize += psp::appendStr ("/", + pEncodingVector + nSize); + + std::list< OString > aName( rMgr.getAdobeNameFromUnicode((*aSortedGlyph).second) ); + + if( aName.begin() != aName.end() ) + nSize += psp::appendStr ( aName.front(), pEncodingVector + nSize); + else + nSize += psp::appendStr (".notdef", pEncodingVector + nSize ); + nSize += psp::appendStr (" ", pEncodingVector + nSize); + // flush line + if (nSize >= 70) + { + nSize += psp::appendStr ("\n", pEncodingVector + nSize); + psp::WritePS (pOutFile, pEncodingVector); + nSize = 0; + } + } + + nSize += psp::appendStr ("] def\n", pEncodingVector + nSize); + psp::WritePS (pOutFile, pEncodingVector); + + PSDefineReencodedFont (pOutFile, nGlyphSetID); + } + + return sal_True; +} + +struct EncEntry +{ + sal_uChar aEnc; + long aGID; + + EncEntry() : aEnc( 0 ), aGID( 0 ) {} + + bool operator<( const EncEntry& rRight ) const + { return aEnc < rRight.aEnc; } +}; + +static void CreatePSUploadableFont( TrueTypeFont* pSrcFont, FILE* pTmpFile, + const char* pGlyphSetName, int nGlyphCount, + /*const*/ sal_uInt16* pRequestedGlyphs, /*const*/ sal_uChar* pEncoding, + bool bAllowType42, bool /*bAllowCID*/ ) +{ + // match the font-subset to the printer capabilities + // TODO: allow CFF for capable printers + int nTargetMask = FontSubsetInfo::TYPE1_PFA | FontSubsetInfo::TYPE3_FONT; + if( bAllowType42 ) + nTargetMask |= FontSubsetInfo::TYPE42_FONT; + + std::vector< EncEntry > aSorted( nGlyphCount, EncEntry() ); + for( int i = 0; i < nGlyphCount; i++ ) + { + aSorted[i].aEnc = pEncoding[i]; + aSorted[i].aGID = pRequestedGlyphs[i]; + } + + std::stable_sort( aSorted.begin(), aSorted.end() ); + + std::vector< sal_uChar > aEncoding( nGlyphCount ); + std::vector< long > aRequestedGlyphs( nGlyphCount ); + + for( int i = 0; i < nGlyphCount; i++ ) + { + aEncoding[i] = aSorted[i].aEnc; + aRequestedGlyphs[i] = aSorted[i].aGID; + } + + FontSubsetInfo aInfo; + aInfo.LoadFont( pSrcFont ); + + aInfo.CreateFontSubset( nTargetMask, pTmpFile, pGlyphSetName, + &aRequestedGlyphs[0], &aEncoding[0], nGlyphCount, NULL ); +} + +sal_Bool +GlyphSet::PSUploadFont (osl::File& rOutFile, PrinterGfx &rGfx, bool bAllowType42, std::list< OString >& rSuppliedFonts ) +{ + // only for truetype fonts + if (meBaseType != fonttype::TrueType) + return sal_False; + + TrueTypeFont *pTTFont; + OString aTTFileName (rGfx.GetFontMgr().getFontFileSysPath(mnFontID)); + int nFace = rGfx.GetFontMgr().getFontFaceNumber(mnFontID); + sal_Int32 nSuccess = OpenTTFontFile(aTTFileName.getStr(), nFace < 0 ? 0 : nFace, &pTTFont); + if (nSuccess != SF_OK) + return sal_False; + FILE* pTmpFile = tmpfile(); + if (pTmpFile == NULL) + return sal_False; + + // array of unicode source characters + sal_Unicode pUChars[256]; + + // encoding vector maps character encoding to the ordinal number + // of the glyph in the output file + sal_uChar pEncoding[256]; + sal_uInt16 pTTGlyphMapping[256]; + const bool bAllowCID = false; // TODO: nPSLanguageLevel>=3 + + // loop thru all the font subsets + sal_Int32 nCharSetID; + char_list_t::iterator aCharSet; + for (aCharSet = maCharList.begin(), nCharSetID = 1; + aCharSet != maCharList.end(); + ++aCharSet, nCharSetID++) + { + if ((*aCharSet).size() == 0) + continue; + + // loop thru all the chars in the subset + char_map_t::const_iterator aChar; + sal_Int32 n = 0; + for (aChar = (*aCharSet).begin(); aChar != (*aCharSet).end(); aChar++) + { + pUChars [n] = (*aChar).first; + pEncoding [n] = (*aChar).second; + n++; + } + // create a mapping from the unicode chars to the char encoding in + // source TrueType font + MapString (pTTFont, pUChars, (*aCharSet).size(), pTTGlyphMapping, mbVertical); + + // create the current subset + OString aCharSetName = GetCharSetName(nCharSetID); + fprintf( pTmpFile, "%%%%BeginResource: font %s\n", aCharSetName.getStr() ); + CreatePSUploadableFont( pTTFont, pTmpFile, aCharSetName.getStr(), (*aCharSet).size(), + pTTGlyphMapping, pEncoding, bAllowType42, bAllowCID ); + fprintf( pTmpFile, "%%%%EndResource\n" ); + rSuppliedFonts.push_back( aCharSetName ); + } + + // loop thru all the font glyph subsets + sal_Int32 nGlyphSetID; + glyph_list_t::iterator aGlyphSet; + for (aGlyphSet = maGlyphList.begin(), nGlyphSetID = 1; + aGlyphSet != maGlyphList.end(); + ++aGlyphSet, nGlyphSetID++) + { + if ((*aGlyphSet).size() == 0) + continue; + + // loop thru all the glyphs in the subset + glyph_map_t::const_iterator aGlyph; + sal_Int32 n = 0; + for (aGlyph = (*aGlyphSet).begin(); aGlyph != (*aGlyphSet).end(); aGlyph++) + { + pTTGlyphMapping [n] = (*aGlyph).first; + pEncoding [n] = (*aGlyph).second; + n++; + } + + // create the current subset + OString aGlyphSetName = GetGlyphSetName(nGlyphSetID); + fprintf( pTmpFile, "%%%%BeginResource: font %s\n", aGlyphSetName.getStr() ); + CreatePSUploadableFont( pTTFont, pTmpFile, aGlyphSetName.getStr(), (*aGlyphSet).size(), + pTTGlyphMapping, pEncoding, bAllowType42, bAllowCID ); + fprintf( pTmpFile, "%%%%EndResource\n" ); + rSuppliedFonts.push_back( aGlyphSetName ); + } + + // copy the file into the page header + rewind(pTmpFile); + fflush(pTmpFile); + + sal_uChar pBuffer[0x2000]; + sal_uInt64 nIn; + sal_uInt64 nOut; + do + { + nIn = fread(pBuffer, 1, sizeof(pBuffer), pTmpFile); + rOutFile.write (pBuffer, nIn, nOut); + } + while ((nIn == nOut) && !feof(pTmpFile)); + + // cleanup + CloseTTFont (pTTFont); + fclose (pTmpFile); + + return sal_True; +} diff --git a/vcl/unx/generic/printergfx/glyphset.hxx b/vcl/unx/generic/printergfx/glyphset.hxx new file mode 100644 index 000000000000..320e8e071955 --- /dev/null +++ b/vcl/unx/generic/printergfx/glyphset.hxx @@ -0,0 +1,135 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _PSPRINT_GLYPHSET_HXX_ +#define _PSPRINT_GLYPHSET_HXX_ + +#include "vcl/fontmanager.hxx" + +#include "osl/file.hxx" + +#include "rtl/string.hxx" + +#include "tools/gen.hxx" + +#include <list> +#include <hash_map> + +namespace psp { + +class PrinterGfx; +class PrintFontManager; + +class GlyphSet +{ +private: + + sal_Int32 mnFontID; + sal_Bool mbVertical; + rtl::OString maBaseName; + fonttype::type meBaseType; + rtl_TextEncoding mnBaseEncoding; + bool mbUseFontEncoding; + + typedef std::hash_map< sal_Unicode, sal_uInt8 > char_map_t; + typedef std::list< char_map_t > char_list_t; + typedef std::hash_map< sal_uInt32, sal_uInt8 > glyph_map_t; + typedef std::list< glyph_map_t > glyph_list_t; + + char_list_t maCharList; + glyph_list_t maGlyphList; + + rtl::OString GetGlyphSetName (sal_Int32 nGlyphSetID); + rtl::OString GetCharSetName (sal_Int32 nGlyphSetID); + sal_Int32 GetGlyphSetEncoding (sal_Int32 nGlyphSetID); + rtl::OString GetGlyphSetEncodingName (sal_Int32 nGlyphSetID); + + rtl::OString GetReencodedFontName (sal_Int32 nGlyphSetID); + void PSDefineReencodedFont (osl::File* pOutFile, + sal_Int32 nGlyphSetID); + + sal_Bool GetCharID (sal_Unicode nChar, + sal_uChar* nOutGlyphID, sal_Int32* nOutGlyphSetID); + sal_Bool LookupCharID (sal_Unicode nChar, + sal_uChar* nOutGlyphID, sal_Int32* nOutGlyphSetID); + sal_Bool AddCharID (sal_Unicode nChar, + sal_uChar* nOutGlyphID, + sal_Int32* nOutGlyphSetID); + sal_Bool GetGlyphID (sal_uInt32 nGlyph, sal_Unicode nUnicode, + sal_uChar* nOutGlyphID, sal_Int32* nOutGlyphSetID); + sal_Bool LookupGlyphID (sal_uInt32 nGlyph, + sal_uChar* nOutGlyphID, sal_Int32* nOutGlyphSetID); + sal_Bool AddGlyphID (sal_uInt32 nGlyph, sal_Unicode nUnicode, + sal_uChar* nOutGlyphID, + sal_Int32* nOutGlyphSetID); + void AddNotdef (char_map_t &rCharMap); + void AddNotdef (glyph_map_t &rGlyphMap); + sal_uChar GetAnsiMapping (sal_Unicode nUnicodeChar); + sal_uChar GetSymbolMapping (sal_Unicode nUnicodeChar); + + void ImplDrawText (PrinterGfx &rGfx, const Point& rPoint, + const sal_Unicode* pStr, sal_Int16 nLen); + void ImplDrawText (PrinterGfx &rGfx, const Point& rPoint, + const sal_Unicode* pStr, sal_Int16 nLen, + const sal_Int32* pDeltaArray); + +public: + + GlyphSet (); + GlyphSet (sal_Int32 nFontID, sal_Bool bVertical); + ~GlyphSet (); + + sal_Int32 GetFontID (); + fonttype::type GetFontType (); + static rtl::OString + GetReencodedFontName (rtl_TextEncoding nEnc, + const rtl::OString &rFontName); + static rtl::OString + GetGlyphSetEncodingName (rtl_TextEncoding nEnc, + const rtl::OString &rFontName); + sal_Bool IsVertical (); + + sal_Bool SetFont (sal_Int32 nFontID, sal_Bool bVertical); + + void DrawText (PrinterGfx &rGfx, const Point& rPoint, + const sal_Unicode* pStr, sal_Int16 nLen, + const sal_Int32* pDeltaArray = NULL); + void DrawGlyphs (PrinterGfx& rGfx, + const Point& rPoint, + const sal_uInt32* pGlyphIds, + const sal_Unicode* pUnicodes, + sal_Int16 nLen, + const sal_Int32* pDeltaArray ); + sal_Bool PSUploadEncoding(osl::File* pOutFile, PrinterGfx &rGfx); + sal_Bool PSUploadFont (osl::File& rOutFile, PrinterGfx &rGfx, bool bAsType42, std::list< rtl::OString >& rSuppliedFonts ); +}; + + +} /* namespace psp */ + +#endif + diff --git a/vcl/unx/generic/printergfx/printerjob.cxx b/vcl/unx/generic/printergfx/printerjob.cxx new file mode 100644 index 000000000000..351d1af9b41d --- /dev/null +++ b/vcl/unx/generic/printergfx/printerjob.cxx @@ -0,0 +1,1204 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <pwd.h> + +#include "psputil.hxx" +#include "glyphset.hxx" + +#include "printerjob.hxx" +#include "printergfx.hxx" +#include "vcl/ppdparser.hxx" +#include "vcl/strhelper.hxx" +#include "vcl/printerinfomanager.hxx" + +#include "rtl/ustring.hxx" +#include "rtl/strbuf.hxx" +#include "rtl/ustrbuf.hxx" + +#include "osl/thread.h" +#include "sal/alloca.h" + +#include <algorithm> +#include <vector> + +using namespace psp; +using namespace rtl; + +// forward declaration + +#define nBLOCKSIZE 0x2000 + +namespace psp +{ + +sal_Bool +AppendPS (FILE* pDst, osl::File* pSrc, sal_uChar* pBuffer, + sal_uInt32 nBlockSize = nBLOCKSIZE) +{ + if ((pDst == NULL) || (pSrc == NULL)) + return sal_False; + + if (nBlockSize == 0) + nBlockSize = nBLOCKSIZE; + if (pBuffer == NULL) + pBuffer = (sal_uChar*)alloca (nBlockSize); + + pSrc->setPos (osl_Pos_Absolut, 0); + + sal_uInt64 nIn = 0; + sal_uInt64 nOut = 0; + do + { + pSrc->read (pBuffer, nBlockSize, nIn); + if (nIn > 0) + nOut = fwrite (pBuffer, 1, sal::static_int_cast<sal_uInt32>(nIn), pDst); + } + while ((nIn > 0) && (nIn == nOut)); + + return sal_True; +} + +} // namespace psp + +/* + * private convenience routines for file handling + */ + +osl::File* +PrinterJob::CreateSpoolFile (const rtl::OUString& rName, const rtl::OUString& rExtension) +{ + osl::File::RC nError = osl::File::E_None; + osl::File* pFile = NULL; + + rtl::OUString aFile = rName + rExtension; + rtl::OUString aFileURL; + nError = osl::File::getFileURLFromSystemPath( aFile, aFileURL ); + if (nError != osl::File::E_None) + return NULL; + aFileURL = maSpoolDirName + rtl::OUString::createFromAscii ("/") + aFileURL; + + pFile = new osl::File (aFileURL); + nError = pFile->open (OpenFlag_Read | OpenFlag_Write | OpenFlag_Create); + if (nError != osl::File::E_None) + { + delete pFile; + return NULL; + } + + pFile->setAttributes (aFileURL, + osl_File_Attribute_OwnWrite | osl_File_Attribute_OwnRead); + return pFile; +} + +/* + * public methods of PrinterJob: for use in PrinterGfx + */ + +void +PrinterJob::GetScale (double &rXScale, double &rYScale) const +{ + rXScale = mfXScale; + rYScale = mfYScale; +} + +sal_uInt16 +PrinterJob::GetDepth () const +{ + sal_Int32 nLevel = GetPostscriptLevel(); + sal_Bool bColor = IsColorPrinter (); + + return nLevel > 1 && bColor ? 24 : 8; +} + +sal_uInt16 +PrinterJob::GetPostscriptLevel (const JobData *pJobData) const +{ + sal_uInt16 nPSLevel = 2; + + if( pJobData == NULL ) + pJobData = &m_aLastJobData; + + if( pJobData->m_nPSLevel ) + nPSLevel = pJobData->m_nPSLevel; + else + if( pJobData->m_pParser ) + nPSLevel = pJobData->m_pParser->getLanguageLevel(); + + return nPSLevel; +} + +sal_Bool +PrinterJob::IsColorPrinter () const +{ + sal_Bool bColor = sal_False; + + if( m_aLastJobData.m_nColorDevice ) + bColor = m_aLastJobData.m_nColorDevice == -1 ? sal_False : sal_True; + else if( m_aLastJobData.m_pParser ) + bColor = m_aLastJobData.m_pParser->isColorDevice() ? sal_True : sal_False; + + return bColor; +} + +osl::File* +PrinterJob::GetDocumentHeader () +{ + return mpJobHeader; +} + +osl::File* +PrinterJob::GetDocumentTrailer () +{ + return mpJobTrailer; +} + +osl::File* +PrinterJob::GetCurrentPageHeader () +{ + return maHeaderList.back(); +} + +osl::File* +PrinterJob::GetCurrentPageBody () +{ + return maPageList.back(); +} + +/* + * public methods of PrinterJob: the actual job / spool handling + */ + +PrinterJob::PrinterJob () : + mpJobHeader( NULL ), + mpJobTrailer( NULL ), + m_bQuickJob( false ) +{ +} + +namespace psp +{ + +/* check whether the given name points to a directory which is + usable for the user */ +sal_Bool +existsTmpDir (const char* pName) +{ + struct stat aFileStatus; + + if (pName == NULL) + return sal_False; + if (stat(pName, &aFileStatus) != 0) + return sal_False; + if (! S_ISDIR(aFileStatus.st_mode)) + return sal_False; + + return access(pName, W_OK | R_OK) == 0 ? sal_True : sal_False; +} + +/* return the username in the given buffer */ +sal_Bool +getUserName (char* pName, int nSize) +{ + struct passwd *pPWEntry; + struct passwd aPWEntry; + sal_Char pPWBuffer[256]; + + sal_Bool bSuccess = sal_False; + +#ifdef FREEBSD + pPWEntry = getpwuid( getuid()); +#else + if (getpwuid_r(getuid(), &aPWEntry, pPWBuffer, sizeof(pPWBuffer), &pPWEntry) != 0) + pPWEntry = NULL; +#endif + + if (pPWEntry != NULL && pPWEntry->pw_name != NULL) + { + sal_Int32 nLen = strlen(pPWEntry->pw_name); + if (nLen > 0 && nLen < nSize) + { + memcpy (pName, pPWEntry->pw_name, nLen); + pName[nLen] = '\0'; + + bSuccess = sal_True; + } + } + + // wipe the passwd off the stack + memset (pPWBuffer, 0, sizeof(pPWBuffer)); + + return bSuccess; +} + +/* remove all our temporary files, uses external program "rm", since + osl functionality is inadequate */ +void +removeSpoolDir (const rtl::OUString& rSpoolDir) +{ + rtl::OUString aSysPath; + if( osl::File::E_None != osl::File::getSystemPathFromFileURL( rSpoolDir, aSysPath ) ) + { + // Conversion did not work, as this is quite a dangerous action, + // we should abort here .... + OSL_ENSURE( 0, "psprint: couldn't remove spool directory" ); + return; + } + rtl::OString aSysPathByte = + rtl::OUStringToOString (aSysPath, osl_getThreadTextEncoding()); + sal_Char pSystem [128]; + sal_Int32 nChar = 0; + + nChar = psp::appendStr ("rm -rf ", pSystem); + nChar += psp::appendStr (aSysPathByte.getStr(), pSystem + nChar); + + if (system (pSystem) == -1) + OSL_ENSURE( 0, "psprint: couldn't remove spool directory" ); +} + +/* creates a spool directory with a "pidgin random" value based on + current system time */ +rtl::OUString +createSpoolDir () +{ + TimeValue aCur; + osl_getSystemTime( &aCur ); + sal_Int32 nRand = aCur.Seconds ^ (aCur.Nanosec/1000); + + rtl::OUString aTmpDir; + osl_getTempDirURL( &aTmpDir.pData ); + + do + { + rtl::OUStringBuffer aDir( aTmpDir.getLength() + 16 ); + aDir.append( aTmpDir ); + aDir.appendAscii( "/psp" ); + aDir.append(nRand); + rtl::OUString aResult = aDir.makeStringAndClear(); + if( osl::Directory::create( aResult ) == osl::FileBase::E_None ) + { + osl::File::setAttributes( aResult, + osl_File_Attribute_OwnWrite + | osl_File_Attribute_OwnRead + | osl_File_Attribute_OwnExe ); + return aResult; + } + nRand++; + } while( nRand ); + return rtl::OUString(); +} + +} // namespace psp + +PrinterJob::~PrinterJob () +{ + std::list< osl::File* >::iterator pPage; + for (pPage = maPageList.begin(); pPage != maPageList.end(); pPage++) + { + //(*pPage)->remove(); + delete *pPage; + } + for (pPage = maHeaderList.begin(); pPage != maHeaderList.end(); pPage++) + { + //(*pPage)->remove(); + delete *pPage; + } + // mpJobHeader->remove(); + delete mpJobHeader; + // mpJobTrailer->remove(); + delete mpJobTrailer; + + // XXX should really call osl::remove routines + if( maSpoolDirName.getLength() ) + removeSpoolDir (maSpoolDirName); + + // osl::Directory::remove (maSpoolDirName); +} + +namespace psp +{ + +// get locale invariant, 7bit clean current local time string +sal_Char* +getLocalTime(sal_Char* pBuffer) +{ + time_t nTime = time (NULL); + struct tm aTime; + struct tm *pLocalTime = localtime_r (&nTime, &aTime); + + return asctime_r(pLocalTime, pBuffer); +} + +} + +static bool isAscii( const rtl::OUString& rStr ) +{ + const sal_Unicode* pStr = rStr; + sal_Int32 nLen = rStr.getLength(); + for( sal_Int32 i = 0; i < nLen; i++ ) + if( pStr[i] > 127 ) + return false; + return true; +} + +sal_Bool +PrinterJob::StartJob ( + const rtl::OUString& rFileName, + int nMode, + const rtl::OUString& rJobName, + const rtl::OUString& rAppName, + const JobData& rSetupData, + PrinterGfx* pGraphics, + bool bIsQuickJob + ) +{ + m_bQuickJob = bIsQuickJob; + mnMaxWidthPt = mnMaxHeightPt = 0; + mnLandscapes = mnPortraits = 0; + m_pGraphics = pGraphics; + InitPaperSize (rSetupData); + + // create file container for document header and trailer + maFileName = rFileName; + mnFileMode = nMode; + maSpoolDirName = createSpoolDir (); + maJobTitle = rJobName; + + rtl::OUString aExt = rtl::OUString::createFromAscii (".ps"); + mpJobHeader = CreateSpoolFile (rtl::OUString::createFromAscii("psp_head"), aExt); + mpJobTrailer = CreateSpoolFile (rtl::OUString::createFromAscii("psp_tail"), aExt); + if( ! (mpJobHeader && mpJobTrailer) ) // existing files are removed in destructor + return sal_False; + + // write document header according to Document Structuring Conventions (DSC) + WritePS (mpJobHeader, + "%!PS-Adobe-3.0\n" + "%%BoundingBox: (atend)\n" ); + + rtl::OUString aFilterWS; + + // Creator (this application) + aFilterWS = WhitespaceToSpace( rAppName, sal_False ); + WritePS (mpJobHeader, "%%Creator: ("); + WritePS (mpJobHeader, aFilterWS); + WritePS (mpJobHeader, ")\n"); + + // For (user name) + sal_Char pUserName[64]; + if (getUserName(pUserName, sizeof(pUserName))) + { + WritePS (mpJobHeader, "%%For: ("); + WritePS (mpJobHeader, pUserName); + WritePS (mpJobHeader, ")\n"); + } + + // Creation Date (locale independent local time) + sal_Char pCreationDate [256]; + WritePS (mpJobHeader, "%%CreationDate: ("); + getLocalTime(pCreationDate); + for( unsigned int i = 0; i < sizeof(pCreationDate)/sizeof(pCreationDate[0]); i++ ) + { + if( pCreationDate[i] == '\n' ) + { + pCreationDate[i] = 0; + break; + } + } + WritePS (mpJobHeader, pCreationDate ); + WritePS (mpJobHeader, ")\n"); + + // Document Title + /* #i74335# + * The title should be clean ascii; rJobName however may + * contain any Unicode character. So implement the following + * algorithm: + * use rJobName, if it contains only ascii + * use the filename, if it contains only ascii + * else omit %%Title + */ + aFilterWS = WhitespaceToSpace( rJobName, sal_False ); + rtl::OUString aTitle( aFilterWS ); + if( ! isAscii( aTitle ) ) + { + sal_Int32 nIndex = 0; + while( nIndex != -1 ) + aTitle = rFileName.getToken( 0, '/', nIndex ); + aTitle = WhitespaceToSpace( aTitle, sal_False ); + if( ! isAscii( aTitle ) ) + aTitle = rtl::OUString(); + } + + maJobTitle = aFilterWS; + if( aTitle.getLength() ) + { + WritePS (mpJobHeader, "%%Title: ("); + WritePS (mpJobHeader, aTitle); + WritePS (mpJobHeader, ")\n"); + } + + // Language Level + sal_Char pLevel[16]; + sal_Int32 nSz = getValueOf(GetPostscriptLevel(&rSetupData), pLevel); + pLevel[nSz++] = '\n'; + pLevel[nSz ] = '\0'; + WritePS (mpJobHeader, "%%LanguageLevel: "); + WritePS (mpJobHeader, pLevel); + + // Other + WritePS (mpJobHeader, "%%DocumentData: Clean7Bit\n"); + WritePS (mpJobHeader, "%%Pages: (atend)\n"); + WritePS (mpJobHeader, "%%Orientation: (atend)\n"); + WritePS (mpJobHeader, "%%PageOrder: Ascend\n"); + WritePS (mpJobHeader, "%%EndComments\n"); + + // write Prolog + writeProlog (mpJobHeader, rSetupData); + + // mark last job setup as not set + m_aLastJobData.m_pParser = NULL; + m_aLastJobData.m_aContext.setParser( NULL ); + + return sal_True; +} + +sal_Bool +PrinterJob::EndJob () +{ + // no pages ? that really means no print job + if( maPageList.empty() ) + return sal_False; + + // write document setup (done here because it + // includes the accumulated fonts + if( mpJobHeader ) + writeSetup( mpJobHeader, m_aDocumentJobData ); + m_pGraphics->OnEndJob(); + if( ! (mpJobHeader && mpJobTrailer) ) + return sal_False; + + // write document trailer according to Document Structuring Conventions (DSC) + rtl::OStringBuffer aTrailer(512); + aTrailer.append( "%%Trailer\n" ); + aTrailer.append( "%%BoundingBox: 0 0 " ); + aTrailer.append( (sal_Int32)mnMaxWidthPt ); + aTrailer.append( " " ); + aTrailer.append( (sal_Int32)mnMaxHeightPt ); + if( mnLandscapes > mnPortraits ) + aTrailer.append("\n%%Orientation: Landscape"); + else + aTrailer.append("\n%%Orientation: Portrait"); + aTrailer.append( "\n%%Pages: " ); + aTrailer.append( (sal_Int32)maPageList.size() ); + aTrailer.append( "\n%%EOF\n" ); + WritePS (mpJobTrailer, aTrailer.getStr()); + + /* + * spool the set of files to their final destination, this is U**X dependent + */ + + FILE* pDestFILE = NULL; + + /* create a destination either as file or as a pipe */ + sal_Bool bSpoolToFile = maFileName.getLength() > 0 ? sal_True : sal_False; + if (bSpoolToFile) + { + const rtl::OString aFileName = rtl::OUStringToOString (maFileName, + osl_getThreadTextEncoding()); + if( mnFileMode ) + { + int nFile = open( aFileName.getStr(), O_CREAT | O_EXCL | O_RDWR, mnFileMode ); + if( nFile != -1 ) + { + pDestFILE = fdopen( nFile, "w" ); + if( pDestFILE == NULL ) + { + close( nFile ); + unlink( aFileName.getStr() ); + return sal_False; + } + } + else + chmod( aFileName.getStr(), mnFileMode ); + } + if (pDestFILE == NULL) + pDestFILE = fopen (aFileName.getStr(), "w"); + + if (pDestFILE == NULL) + return sal_False; + } + else + { + PrinterInfoManager& rPrinterInfoManager = PrinterInfoManager::get (); + pDestFILE = rPrinterInfoManager.startSpool( m_aLastJobData.m_aPrinterName, m_bQuickJob ); + if (pDestFILE == NULL) + return sal_False; + } + + /* spool the document parts to the destination */ + + sal_uChar pBuffer[ nBLOCKSIZE ]; + + AppendPS (pDestFILE, mpJobHeader, pBuffer); + mpJobHeader->close(); + + sal_Bool bSuccess = sal_True; + std::list< osl::File* >::iterator pPageBody; + std::list< osl::File* >::iterator pPageHead; + for (pPageBody = maPageList.begin(), pPageHead = maHeaderList.begin(); + pPageBody != maPageList.end() && pPageHead != maHeaderList.end(); + pPageBody++, pPageHead++) + { + if( *pPageHead ) + { + osl::File::RC nError = (*pPageHead)->open(OpenFlag_Read); + if (nError == osl::File::E_None) + { + AppendPS (pDestFILE, *pPageHead, pBuffer); + (*pPageHead)->close(); + } + } + else + bSuccess = sal_False; + if( *pPageBody ) + { + osl::File::RC nError = (*pPageBody)->open(OpenFlag_Read); + if (nError == osl::File::E_None) + { + AppendPS (pDestFILE, *pPageBody, pBuffer); + (*pPageBody)->close(); + } + } + else + bSuccess = sal_False; + } + + AppendPS (pDestFILE, mpJobTrailer, pBuffer); + mpJobTrailer->close(); + + /* well done */ + + if (bSpoolToFile) + fclose (pDestFILE); + else + { + PrinterInfoManager& rPrinterInfoManager = PrinterInfoManager::get(); + if (0 == rPrinterInfoManager.endSpool( m_aLastJobData.m_aPrinterName, + maJobTitle, pDestFILE, m_aDocumentJobData, true )) + { + bSuccess = sal_False; + } + } + + return bSuccess; +} + +sal_Bool +PrinterJob::AbortJob () +{ + m_pGraphics->OnEndJob(); + return sal_False; +} + +void +PrinterJob::InitPaperSize (const JobData& rJobSetup) +{ + int nRes = rJobSetup.m_aContext.getRenderResolution (); + + String aPaper; + int nWidth, nHeight; + rJobSetup.m_aContext.getPageSize (aPaper, nWidth, nHeight); + + int nLeft = 0, nRight = 0, nUpper = 0, nLower = 0; + const PPDParser* pParser = rJobSetup.m_aContext.getParser(); + if (pParser != NULL) + pParser->getMargins (aPaper, nLeft, nRight, nUpper, nLower); + + mnResolution = nRes; + + mnWidthPt = nWidth; + mnHeightPt = nHeight; + + if( mnWidthPt > mnMaxWidthPt ) + mnMaxWidthPt = mnWidthPt; + if( mnHeightPt > mnMaxHeightPt ) + mnMaxHeightPt = mnHeightPt; + + mnLMarginPt = nLeft; + mnRMarginPt = nRight; + mnTMarginPt = nUpper; + mnBMarginPt = nLower; + + mfXScale = (double)72.0 / (double)mnResolution; + mfYScale = -1.0 * (double)72.0 / (double)mnResolution; +} + + +sal_Bool +PrinterJob::StartPage (const JobData& rJobSetup) +{ + InitPaperSize (rJobSetup); + + rtl::OUString aPageNo = rtl::OUString::valueOf ((sal_Int32)maPageList.size()+1); // sequential page number must start with 1 + rtl::OUString aExt = aPageNo + rtl::OUString::createFromAscii (".ps"); + + osl::File* pPageHeader = CreateSpoolFile ( + rtl::OUString::createFromAscii("psp_pghead"), aExt); + osl::File* pPageBody = CreateSpoolFile ( + rtl::OUString::createFromAscii("psp_pgbody"), aExt); + + maHeaderList.push_back (pPageHeader); + maPageList.push_back (pPageBody); + + if( ! (pPageHeader && pPageBody) ) + return sal_False; + + // write page header according to Document Structuring Conventions (DSC) + WritePS (pPageHeader, "%%Page: "); + WritePS (pPageHeader, aPageNo); + WritePS (pPageHeader, " "); + WritePS (pPageHeader, aPageNo); + WritePS (pPageHeader, "\n"); + + if( rJobSetup.m_eOrientation == orientation::Landscape ) + { + WritePS (pPageHeader, "%%PageOrientation: Landscape\n"); + mnLandscapes++; + } + else + { + WritePS (pPageHeader, "%%PageOrientation: Portrait\n"); + mnPortraits++; + } + + sal_Char pBBox [256]; + sal_Int32 nChar = 0; + + nChar = psp::appendStr ("%%PageBoundingBox: ", pBBox); + nChar += psp::getValueOf (mnLMarginPt, pBBox + nChar); + nChar += psp::appendStr (" ", pBBox + nChar); + nChar += psp::getValueOf (mnBMarginPt, pBBox + nChar); + nChar += psp::appendStr (" ", pBBox + nChar); + nChar += psp::getValueOf (mnWidthPt - mnRMarginPt, pBBox + nChar); + nChar += psp::appendStr (" ", pBBox + nChar); + nChar += psp::getValueOf (mnHeightPt - mnTMarginPt, pBBox + nChar); + nChar += psp::appendStr ("\n", pBBox + nChar); + + WritePS (pPageHeader, pBBox); + + /* #i7262# #i65491# write setup only before first page + * (to %%Begin(End)Setup, instead of %%Begin(End)PageSetup) + * don't do this in StartJob since the jobsetup there may be + * different. + */ + bool bWriteFeatures = true; + if( 1 == maPageList.size() ) + { + m_aDocumentJobData = rJobSetup; + bWriteFeatures = false; + } + + if ( writePageSetup( pPageHeader, rJobSetup, bWriteFeatures ) ) + { + m_aLastJobData = rJobSetup; + return true; + } + + return false; +} + +sal_Bool +PrinterJob::EndPage () +{ + m_pGraphics->OnEndPage(); + + osl::File* pPageHeader = maHeaderList.back(); + osl::File* pPageBody = maPageList.back(); + + if( ! (pPageBody && pPageHeader) ) + return sal_False; + + // copy page to paper and write page trailer according to DSC + + sal_Char pTrailer[256]; + sal_Int32 nChar = 0; + nChar = psp::appendStr ("grestore grestore\n", pTrailer); + nChar += psp::appendStr ("showpage\n", pTrailer + nChar); + nChar += psp::appendStr ("%%PageTrailer\n\n", pTrailer + nChar); + WritePS (pPageBody, pTrailer); + + // this page is done for now, close it to avoid having too many open fd's + + pPageHeader->close(); + pPageBody->close(); + + return sal_True; +} + +sal_uInt32 +PrinterJob::GetErrorCode () +{ + /* TODO */ + return 0; +} + +struct less_ppd_key : public ::std::binary_function<double, double, bool> +{ + bool operator()(const PPDKey* left, const PPDKey* right) + { return left->getOrderDependency() < right->getOrderDependency(); } +}; + +static bool writeFeature( osl::File* pFile, const PPDKey* pKey, const PPDValue* pValue, bool bUseIncluseFeature ) +{ + if( ! pKey || ! pValue ) + return true; + + OStringBuffer aFeature(256); + aFeature.append( "[{\n" ); + if( bUseIncluseFeature ) + aFeature.append( "%%IncludeFeature:" ); + else + aFeature.append( "%%BeginFeature:" ); + aFeature.append( " *" ); + aFeature.append( OUStringToOString( pKey->getKey(), RTL_TEXTENCODING_ASCII_US ) ); + aFeature.append( ' ' ); + aFeature.append( OUStringToOString( pValue->m_aOption, RTL_TEXTENCODING_ASCII_US ) ); + if( !bUseIncluseFeature ) + { + aFeature.append( '\n' ); + aFeature.append( OUStringToOString( pValue->m_aValue, RTL_TEXTENCODING_ASCII_US ) ); + aFeature.append( "\n%%EndFeature" ); + } + aFeature.append( "\n} stopped cleartomark\n" ); + sal_uInt64 nWritten = 0; + return pFile->write( aFeature.getStr(), aFeature.getLength(), nWritten ) + || nWritten != (sal_uInt64)aFeature.getLength() ? false : true; +} + +bool PrinterJob::writeFeatureList( osl::File* pFile, const JobData& rJob, bool bDocumentSetup ) +{ + bool bSuccess = true; + int i; + + // emit features ordered to OrderDependency + // ignore features that are set to default + + // sanity check + if( rJob.m_pParser == rJob.m_aContext.getParser() && + rJob.m_pParser && + ( m_aLastJobData.m_pParser == rJob.m_pParser || m_aLastJobData.m_pParser == NULL ) + ) + { + int nKeys = rJob.m_aContext.countValuesModified(); + ::std::vector< const PPDKey* > aKeys( nKeys ); + for( i = 0; i < nKeys; i++ ) + aKeys[i] = rJob.m_aContext.getModifiedKey( i ); + ::std::sort( aKeys.begin(), aKeys.end(), less_ppd_key() ); + + for( i = 0; i < nKeys && bSuccess; i++ ) + { + const PPDKey* pKey = aKeys[i]; + bool bEmit = false; + if( bDocumentSetup ) + { + if( pKey->getSetupType() == PPDKey::DocumentSetup ) + bEmit = true; + } + if( pKey->getSetupType() == PPDKey::PageSetup || + pKey->getSetupType() == PPDKey::AnySetup ) + bEmit = true; + if( bEmit ) + { + const PPDValue* pValue = rJob.m_aContext.getValue( pKey ); + if( pValue + && pValue->m_eType == eInvocation + && ( m_aLastJobData.m_pParser == NULL + || m_aLastJobData.m_aContext.getValue( pKey ) != pValue + || bDocumentSetup + ) + ) + { + // try to avoid PS level 2 feature commands if level is set to 1 + if( GetPostscriptLevel( &rJob ) == 1 ) + { + bool bHavePS2 = + ( pValue->m_aValue.SearchAscii( "<<" ) != STRING_NOTFOUND ) + || + ( pValue->m_aValue.SearchAscii( ">>" ) != STRING_NOTFOUND ); + if( bHavePS2 ) + continue; + } + bSuccess = writeFeature( pFile, pKey, pValue, PrinterInfoManager::get().getUseIncludeFeature() ); + } + } + } + } + else + bSuccess = false; + + return bSuccess; +} + +bool PrinterJob::writePageSetup( osl::File* pFile, const JobData& rJob, bool bWriteFeatures ) +{ + bool bSuccess = true; + + WritePS (pFile, "%%BeginPageSetup\n%\n"); + if ( bWriteFeatures ) + bSuccess = writeFeatureList( pFile, rJob, false ); + WritePS (pFile, "%%EndPageSetup\n"); + + sal_Char pTranslate [128]; + sal_Int32 nChar = 0; + + if( rJob.m_eOrientation == orientation::Portrait ) + { + nChar = psp::appendStr ("gsave\n[", pTranslate); + nChar += psp::getValueOfDouble ( pTranslate + nChar, mfXScale, 5); + nChar += psp::appendStr (" 0 0 ", pTranslate + nChar); + nChar += psp::getValueOfDouble ( pTranslate + nChar, mfYScale, 5); + nChar += psp::appendStr (" ", pTranslate + nChar); + nChar += psp::getValueOf (mnRMarginPt, pTranslate + nChar); + nChar += psp::appendStr (" ", pTranslate + nChar); + nChar += psp::getValueOf (mnHeightPt-mnTMarginPt, + pTranslate + nChar); + nChar += psp::appendStr ("] concat\ngsave\n", + pTranslate + nChar); + } + else + { + nChar = psp::appendStr ("gsave\n", pTranslate); + nChar += psp::appendStr ("[ 0 ", pTranslate + nChar); + nChar += psp::getValueOfDouble ( pTranslate + nChar, -mfYScale, 5); + nChar += psp::appendStr (" ", pTranslate + nChar); + nChar += psp::getValueOfDouble ( pTranslate + nChar, mfXScale, 5); + nChar += psp::appendStr (" 0 ", pTranslate + nChar ); + nChar += psp::getValueOfDouble ( pTranslate + nChar, mnLMarginPt, 5 ); + nChar += psp::appendStr (" ", pTranslate + nChar); + nChar += psp::getValueOf (mnBMarginPt, pTranslate + nChar ); + nChar += psp::appendStr ("] concat\ngsave\n", + pTranslate + nChar); + } + + WritePS (pFile, pTranslate); + + return bSuccess; +} + +void PrinterJob::writeJobPatch( osl::File* pFile, const JobData& rJobData ) +{ + if( ! PrinterInfoManager::get().getUseJobPatch() ) + return; + + const PPDKey* pKey = NULL; + + if( rJobData.m_pParser ) + pKey = rJobData.m_pParser->getKey( OUString( RTL_CONSTASCII_USTRINGPARAM( "JobPatchFile" ) ) ); + if( ! pKey ) + return; + + // order the patch files + // according to PPD spec the JobPatchFile options must be int + // and should be emitted in order + std::list< sal_Int32 > patch_order; + int nValueCount = pKey->countValues(); + for( int i = 0; i < nValueCount; i++ ) + { + const PPDValue* pVal = pKey->getValue( i ); + patch_order.push_back( pVal->m_aOption.ToInt32() ); + if( patch_order.back() == 0 && ! pVal->m_aOption.EqualsAscii( "0" ) ) + { + WritePS( pFile, "% Warning: left out JobPatchFile option \"" ); + OString aOption = OUStringToOString( pVal->m_aOption, RTL_TEXTENCODING_ASCII_US ); + WritePS( pFile, aOption.getStr() ); + WritePS( pFile, + "\"\n% as it violates the PPD spec;\n" + "% JobPatchFile options need to be numbered for ordering.\n" ); + } + } + + patch_order.sort(); + patch_order.unique(); + + while( patch_order.begin() != patch_order.end() ) + { + // note: this discards patch files not adhering to the "int" scheme + // as there won't be a value for them + writeFeature( pFile, pKey, pKey->getValue( OUString::valueOf( patch_order.front() ) ), false ); + patch_order.pop_front(); + } +} + +bool PrinterJob::writeProlog (osl::File* pFile, const JobData& rJobData ) +{ + WritePS( pFile, "%%BeginProlog\n" ); + + // JobPatchFile feature needs to be emitted at begin of prolog + writeJobPatch( pFile, rJobData ); + + static const sal_Char pProlog[] = { + "%%BeginResource: procset PSPrint-Prolog 1.0 0\n" + "/ISO1252Encoding [\n" + "/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef\n" + "/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef\n" + "/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef\n" + "/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef\n" + "/space /exclam /quotedbl /numbersign /dollar /percent /ampersand /quotesingle\n" + "/parenleft /parenright /asterisk /plus /comma /hyphen /period /slash\n" + "/zero /one /two /three /four /five /six /seven\n" + "/eight /nine /colon /semicolon /less /equal /greater /question\n" + "/at /A /B /C /D /E /F /G\n" + "/H /I /J /K /L /M /N /O\n" + "/P /Q /R /S /T /U /V /W\n" + "/X /Y /Z /bracketleft /backslash /bracketright /asciicircum /underscore\n" + "/grave /a /b /c /d /e /f /g\n" + "/h /i /j /k /l /m /n /o\n" + "/p /q /r /s /t /u /v /w\n" + "/x /y /z /braceleft /bar /braceright /asciitilde /unused\n" + "/Euro /unused /quotesinglbase /florin /quotedblbase /ellipsis /dagger /daggerdbl\n" + "/circumflex /perthousand /Scaron /guilsinglleft /OE /unused /Zcaron /unused\n" + "/unused /quoteleft /quoteright /quotedblleft /quotedblright /bullet /endash /emdash\n" + "/tilde /trademark /scaron /guilsinglright /oe /unused /zcaron /Ydieresis\n" + "/space /exclamdown /cent /sterling /currency /yen /brokenbar /section\n" + "/dieresis /copyright /ordfeminine /guillemotleft /logicalnot /hyphen /registered /macron\n" + "/degree /plusminus /twosuperior /threesuperior /acute /mu /paragraph /periodcentered\n" + "/cedilla /onesuperior /ordmasculine /guillemotright /onequarter /onehalf /threequarters /questiondown\n" + "/Agrave /Aacute /Acircumflex /Atilde /Adieresis /Aring /AE /Ccedilla\n" + "/Egrave /Eacute /Ecircumflex /Edieresis /Igrave /Iacute /Icircumflex /Idieresis\n" + "/Eth /Ntilde /Ograve /Oacute /Ocircumflex /Otilde /Odieresis /multiply\n" + "/Oslash /Ugrave /Uacute /Ucircumflex /Udieresis /Yacute /Thorn /germandbls\n" + "/agrave /aacute /acircumflex /atilde /adieresis /aring /ae /ccedilla\n" + "/egrave /eacute /ecircumflex /edieresis /igrave /iacute /icircumflex /idieresis\n" + "/eth /ntilde /ograve /oacute /ocircumflex /otilde /odieresis /divide\n" + "/oslash /ugrave /uacute /ucircumflex /udieresis /yacute /thorn /ydieresis] def\n" + "\n" + "/psp_definefont { exch dup findfont dup length dict begin { 1 index /FID ne\n" + "{ def } { pop pop } ifelse } forall /Encoding 3 -1 roll def\n" + "currentdict end exch pop definefont pop } def\n" + "\n" + "/pathdict dup 8 dict def load begin\n" + "/rcmd { { currentfile 1 string readstring pop 0 get dup 32 gt { exit }\n" + "{ pop } ifelse } loop dup 126 eq { pop exit } if 65 sub dup 16#3 and 1\n" + "add exch dup 16#C and -2 bitshift 16#3 and 1 add exch 16#10 and 16#10\n" + "eq 3 1 roll exch } def\n" + "/rhex { dup 1 sub exch currentfile exch string readhexstring pop dup 0\n" + "get dup 16#80 and 16#80 eq dup 3 1 roll { 16#7f and } if 2 index 0 3\n" + "-1 roll put 3 1 roll 0 0 1 5 -1 roll { 2 index exch get add 256 mul }\n" + "for 256 div exch pop exch { neg } if } def\n" + "/xcmd { rcmd exch rhex exch rhex exch 5 -1 roll add exch 4 -1 roll add\n" + "1 index 1 index 5 -1 roll { moveto } { lineto } ifelse } def end\n" + "/readpath { 0 0 pathdict begin { xcmd } loop end pop pop } def\n" + "\n" + "systemdict /languagelevel known not {\n" + "/xshow { exch dup length 0 1 3 -1 roll 1 sub { dup 3 index exch get\n" + "exch 2 index exch get 1 string dup 0 4 -1 roll put currentpoint 3 -1\n" + "roll show moveto 0 rmoveto } for pop pop } def\n" + "/rectangle { 4 -2 roll moveto 1 index 0 rlineto 0 exch rlineto neg 0\n" + "rlineto closepath } def\n" + "/rectfill { rectangle fill } def\n" + "/rectstroke { rectangle stroke } def } if\n" + "/bshow { currentlinewidth 3 1 roll currentpoint 3 index show moveto\n" + "setlinewidth false charpath stroke setlinewidth } def\n" + "/bxshow { currentlinewidth 4 1 roll setlinewidth exch dup length 1 sub\n" + "0 1 3 -1 roll { 1 string 2 index 2 index get 1 index exch 0 exch put dup\n" + "currentpoint 3 -1 roll show moveto currentpoint 3 -1 roll false charpath\n" + "stroke moveto 2 index exch get 0 rmoveto } for pop pop setlinewidth } def\n" + "\n" + "/psp_lzwfilter { currentfile /ASCII85Decode filter /LZWDecode filter } def\n" + "/psp_ascii85filter { currentfile /ASCII85Decode filter } def\n" + "/psp_lzwstring { psp_lzwfilter 1024 string readstring } def\n" + "/psp_ascii85string { psp_ascii85filter 1024 string readstring } def\n" + "/psp_imagedict {\n" + "/psp_bitspercomponent { 3 eq { 1 }{ 8 } ifelse } def\n" + "/psp_decodearray { [ [0 1 0 1 0 1] [0 255] [0 1] [0 255] ] exch get }\n" + "def 7 dict dup\n" + "/ImageType 1 put dup\n" + "/Width 7 -1 roll put dup\n" + "/Height 5 index put dup\n" + "/BitsPerComponent 4 index psp_bitspercomponent put dup\n" + "/Decode 5 -1 roll psp_decodearray put dup\n" + "/ImageMatrix [1 0 0 1 0 0] dup 5 8 -1 roll put put dup\n" + "/DataSource 4 -1 roll 1 eq { psp_lzwfilter } { psp_ascii85filter } ifelse put\n" + "} def\n" + "%%EndResource\n" + "%%EndProlog\n" + }; + static const sal_Char pSO52CompatProlog[] = { + "%%BeginResource: procset PSPrint-Prolog 1.0 0\n" + "/ISO1252Encoding [\n" + "/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef\n" + "/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef\n" + "/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef\n" + "/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef\n" + "/space /exclam /quotedbl /numbersign /dollar /percent /ampersand /quoteright\n" + "/parenleft /parenright /asterisk /plus /comma /minus /period /slash\n" + "/zero /one /two /three /four /five /six /seven\n" + "/eight /nine /colon /semicolon /less /equal /greater /question\n" + "/at /A /B /C /D /E /F /G\n" + "/H /I /J /K /L /M /N /O\n" + "/P /Q /R /S /T /U /V /W\n" + "/X /Y /Z /bracketleft /backslash /bracketright /asciicircum /underscore\n" + "/grave /a /b /c /d /e /f /g\n" + "/h /i /j /k /l /m /n /o\n" + "/p /q /r /s /t /u /v /w\n" + "/x /y /z /braceleft /bar /braceright /asciitilde /unused\n" + "/Euro /unused /quotesinglbase /florin /quotedblbase /ellipsis /dagger /daggerdbl\n" + "/circumflex /perthousand /Scaron /guilsinglleft /OE /unused /Zcaron /unused\n" + "/unused /quoteleft /quoteright /quotedblleft /quotedblright /bullet /endash /emdash\n" + "/tilde /trademark /scaron /guilsinglright /oe /unused /zcaron /Ydieresis\n" + "/space /exclamdown /cent /sterling /currency /yen /brokenbar /section\n" + "/dieresis /copyright /ordfeminine /guillemotleft /logicalnot /hyphen /registered /macron\n" + "/degree /plusminus /twosuperior /threesuperior /acute /mu /paragraph /periodcentered\n" + "/cedilla /onesuperior /ordmasculine /guillemotright /onequarter /onehalf /threequarters /questiondown\n" + "/Agrave /Aacute /Acircumflex /Atilde /Adieresis /Aring /AE /Ccedilla\n" + "/Egrave /Eacute /Ecircumflex /Edieresis /Igrave /Iacute /Icircumflex /Idieresis\n" + "/Eth /Ntilde /Ograve /Oacute /Ocircumflex /Otilde /Odieresis /multiply\n" + "/Oslash /Ugrave /Uacute /Ucircumflex /Udieresis /Yacute /Thorn /germandbls\n" + "/agrave /aacute /acircumflex /atilde /adieresis /aring /ae /ccedilla\n" + "/egrave /eacute /ecircumflex /edieresis /igrave /iacute /icircumflex /idieresis\n" + "/eth /ntilde /ograve /oacute /ocircumflex /otilde /odieresis /divide\n" + "/oslash /ugrave /uacute /ucircumflex /udieresis /yacute /thorn /ydieresis] def\n" + "\n" + "/psp_definefont { exch dup findfont dup length dict begin { 1 index /FID ne\n" + "{ def } { pop pop } ifelse } forall /Encoding 3 -1 roll def\n" + "currentdict end exch pop definefont pop } def\n" + "\n" + "/pathdict dup 8 dict def load begin\n" + "/rcmd { { currentfile 1 string readstring pop 0 get dup 32 gt { exit }\n" + "{ pop } ifelse } loop dup 126 eq { pop exit } if 65 sub dup 16#3 and 1\n" + "add exch dup 16#C and -2 bitshift 16#3 and 1 add exch 16#10 and 16#10\n" + "eq 3 1 roll exch } def\n" + "/rhex { dup 1 sub exch currentfile exch string readhexstring pop dup 0\n" + "get dup 16#80 and 16#80 eq dup 3 1 roll { 16#7f and } if 2 index 0 3\n" + "-1 roll put 3 1 roll 0 0 1 5 -1 roll { 2 index exch get add 256 mul }\n" + "for 256 div exch pop exch { neg } if } def\n" + "/xcmd { rcmd exch rhex exch rhex exch 5 -1 roll add exch 4 -1 roll add\n" + "1 index 1 index 5 -1 roll { moveto } { lineto } ifelse } def end\n" + "/readpath { 0 0 pathdict begin { xcmd } loop end pop pop } def\n" + "\n" + "systemdict /languagelevel known not {\n" + "/xshow { exch dup length 0 1 3 -1 roll 1 sub { dup 3 index exch get\n" + "exch 2 index exch get 1 string dup 0 4 -1 roll put currentpoint 3 -1\n" + "roll show moveto 0 rmoveto } for pop pop } def\n" + "/rectangle { 4 -2 roll moveto 1 index 0 rlineto 0 exch rlineto neg 0\n" + "rlineto closepath } def\n" + "/rectfill { rectangle fill } def\n" + "/rectstroke { rectangle stroke } def } if\n" + "/bshow { currentlinewidth 3 1 roll currentpoint 3 index show moveto\n" + "setlinewidth false charpath stroke setlinewidth } def\n" + "/bxshow { currentlinewidth 4 1 roll setlinewidth exch dup length 1 sub\n" + "0 1 3 -1 roll { 1 string 2 index 2 index get 1 index exch 0 exch put dup\n" + "currentpoint 3 -1 roll show moveto currentpoint 3 -1 roll false charpath\n" + "stroke moveto 2 index exch get 0 rmoveto } for pop pop setlinewidth } def\n" + "\n" + "/psp_lzwfilter { currentfile /ASCII85Decode filter /LZWDecode filter } def\n" + "/psp_ascii85filter { currentfile /ASCII85Decode filter } def\n" + "/psp_lzwstring { psp_lzwfilter 1024 string readstring } def\n" + "/psp_ascii85string { psp_ascii85filter 1024 string readstring } def\n" + "/psp_imagedict {\n" + "/psp_bitspercomponent { 3 eq { 1 }{ 8 } ifelse } def\n" + "/psp_decodearray { [ [0 1 0 1 0 1] [0 255] [0 1] [0 255] ] exch get }\n" + "def 7 dict dup\n" + "/ImageType 1 put dup\n" + "/Width 7 -1 roll put dup\n" + "/Height 5 index put dup\n" + "/BitsPerComponent 4 index psp_bitspercomponent put dup\n" + "/Decode 5 -1 roll psp_decodearray put dup\n" + "/ImageMatrix [1 0 0 1 0 0] dup 5 8 -1 roll put put dup\n" + "/DataSource 4 -1 roll 1 eq { psp_lzwfilter } { psp_ascii85filter } ifelse put\n" + "} def\n" + "%%EndResource\n" + "%%EndProlog\n" + }; + WritePS (pFile, m_pGraphics && m_pGraphics->getStrictSO52Compatibility() ? pSO52CompatProlog : pProlog); + + return true; +} + +bool PrinterJob::writeSetup( osl::File* pFile, const JobData& rJob ) +{ + WritePS (pFile, "%%BeginSetup\n%\n"); + + // download fonts + std::list< rtl::OString > aFonts[2]; + m_pGraphics->writeResources( pFile, aFonts[0], aFonts[1] ); + + for( int i = 0; i < 2; i++ ) + { + if( !aFonts[i].empty() ) + { + std::list< rtl::OString >::const_iterator it = aFonts[i].begin(); + rtl::OStringBuffer aLine( 256 ); + if( i == 0 ) + aLine.append( "%%DocumentSuppliedResources: font " ); + else + aLine.append( "%%DocumentNeededResources: font " ); + aLine.append( *it ); + aLine.append( "\n" ); + WritePS ( pFile, aLine.getStr() ); + while( (++it) != aFonts[i].end() ) + { + aLine.setLength(0); + aLine.append( "%%+ font " ); + aLine.append( *it ); + aLine.append( "\n" ); + WritePS ( pFile, aLine.getStr() ); + } + } + } + + bool bSuccess = true; + // in case of external print dialog the number of copies is prepended + // to the job, let us not complicate things by emitting our own copy count + bool bExternalDialog = PrinterInfoManager::get().checkFeatureToken( GetPrinterName(), "external_dialog" ); + if( ! bExternalDialog && rJob.m_nCopies > 1 ) + { + // setup code + ByteString aLine( "/#copies " ); + aLine += ByteString::CreateFromInt32( rJob.m_nCopies ); + aLine += " def\n"; + sal_uInt64 nWritten = 0; + bSuccess = pFile->write( aLine.GetBuffer(), aLine.Len(), nWritten ) + || nWritten != aLine.Len() ? false : true; + + if( bSuccess && GetPostscriptLevel( &rJob ) >= 2 ) + WritePS (pFile, "<< /NumCopies null /Policies << /NumCopies 1 >> >> setpagedevice\n" ); + } + + bool bFeatureSuccess = writeFeatureList( pFile, rJob, true ); + + WritePS (pFile, "%%EndSetup\n"); + + return bSuccess && bFeatureSuccess; +} diff --git a/vcl/unx/generic/printergfx/psheader.ps b/vcl/unx/generic/printergfx/psheader.ps new file mode 100644 index 000000000000..6a0e350d9ddc --- /dev/null +++ b/vcl/unx/generic/printergfx/psheader.ps @@ -0,0 +1,368 @@ +%************************************************************************* +% +% DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +% +% Copyright 2000, 2010 Oracle and/or its affiliates. +% +% OpenOffice.org - a multi-platform office productivity suite +% +% This file is part of OpenOffice.org. +% +% OpenOffice.org is free software: you can redistribute it and/or modify +% it under the terms of the GNU Lesser General Public License version 3 +% only, as published by the Free Software Foundation. +% +% OpenOffice.org is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU Lesser General Public License version 3 for more details +% (a copy is included in the LICENSE file that accompanied this code). +% +% You should have received a copy of the GNU Lesser General Public License +% version 3 along with OpenOffice.org. If not, see +% <http://www.openoffice.org/license.html> +% for a copy of the LGPLv3 License. +% +%************************************************************************* + +% +% +% readpath +% +% The intention of readpath is to save disk space since the vcl clip region routines +% produce a huge amount of lineto/moveto commands +% +% The principal idea is to maintain the current point on stack and to provide only deltas +% in the command. These deltas are added to the current point. The new point is used for +% the lineto and moveto command and saved on stack for the next command. +% +% pathdict implements binary/hex representation of lineto and moveto commands. +% The command consists of a 1byte opcode to switch between lineto and moveto and the size +% of the following delta-x and delta-y values. The opcode is read with /rcmd, the two +% coordinates are read with /rhex. The whole command is executed with /xcmd +% +% + +/pathdict dup 8 dict def load +begin + + % the command is of the bit format cxxyy + % with c=0 meaning lineto + % c=1 meaning moveto + % xx is a 2bit value for the number of bytes for x position + % yy is the same for y, values are off by one: 00 means 1; 11 means 4 ! + % the command has been added to 'A' to be always in the ascii character + % range. the command is followed by 2*xx + 2*yy hexchars. + % '~' denotes the special case of EOD + /rcmd { + { + currentfile 1 string readstring % s bool + pop % s + 0 get % s[0] + % --- check wether s[0] is CR, LF ... + dup 32 gt % s > ' ' ? then read on + { exit } + { pop } + ifelse + } + loop + + dup 126 eq { pop exit } if % -- Exit loop if cmd is '~' + 65 sub % cmd=s[0]-'A' + % -- Separate yy bits + dup 16#3 and 1 add % cmd yy + % -- Separate xx bits + exch % yy cmd + dup 16#C and -2 bitshift + 16#3 and 1 add exch % yy xx cmd + % -- Separate command bit + 16#10 and 16#10 eq % yy xx bool + 3 1 roll exch % bool xx yy + } def + + % length rhex -- reads a signed hex value of given length + % the left most bit of char 0 is considered as the sign (0 means '+', 1 means '-') + % the rest of the bits is considered to be the abs value. Please note that this + % does not match the C binary representation of integers + /rhex { + dup 1 sub exch % l-1 l + currentfile exch string readhexstring % l-1 substring[l] bool + pop + dup 0 get dup % l-1 s s[0] s[0] + % -- Extract the sign + 16#80 and 16#80 eq dup % l-1 s s[0] sign=- sign=- + % -- Mask out the sign bit and put value back + 3 1 roll % l-1 s sign=- s[0] sign=- + { 16#7f and } if % l-1 s sign=- +s[0] + 2 index 0 % l-1 s sign=- +s[0] s 0 + 3 -1 roll put % l-1 s sign=- s 0 +s[0] + % -- Read loop: add to prev sum, mul with 256 + 3 1 roll 0 % sign=- l-1 s Sum=0 + 0 1 5 -1 roll % sign=- s Sum=0 0 1 l-1 + { % sign=- s Sum idx + 2 index exch % sign=- s Sum s idx + get % sign=- s Sum s[idx] + add 256 mul % sign=- s Sum=(s[idx]+Sum)*256 + } + for + % -- mul was once too often, weave in the sign + 256 div % sign=- s Sum/256 + exch pop % sign=- Sum/256 + exch { neg } if % (sign=- ? -Sum : Sum) + } def + + % execute a single command, the former x and y position is already on stack + % only offsets are read from cmdstring + /xcmd { % x y + rcmd % x y bool wx wy + exch rhex % x y bool wy Dx + exch rhex % x y bool Dx Dy + exch 5 -1 roll % y bool Dy Dx x + add exch % y bool X Dy + 4 -1 roll add % bool X Y + 1 index 1 index % bool X Y X Y + 5 -1 roll % X Y X Y bool + { moveto } + { lineto } + ifelse % X Y + } def +end + +/readpath +{ + 0 0 % push initial-x initial-y + pathdict begin + { xcmd } loop + end + pop pop % pop final-x final-y +} def + +% +% +% if languagelevel is not in the systemdict then its level 1 interpreter: +% provide compatibility routines +% +% + +systemdict /languagelevel known not +{ + % string numarray xxshow - + % does only work for single byte fonts + /xshow { + exch dup % a s s + length 0 1 % a s l(s) 1 1 + 3 -1 roll 1 sub % a s 0 1 l(s)-1 + { % a s idx + dup % a s idx idx + % -- extract the delta offset + 3 index exch get % a s idx a[idx] + % -- extract the character + exch % a s a[idx] idx + 2 index exch get % a s a[idx] s[idx] + % -- create a tmp string for show + 1 string dup 0 % a s a[idx] s[idx] s1 s1 0 + 4 -1 roll % a s a[idx] s1 s1 0 s[idx] + put % a s a[idx] s1 + % -- store the current point + currentpoint 3 -1 roll % a s a[idx] x y s1 + % -- draw the character + show % a s a[idx] x y + % -- move to the offset + moveto 0 rmoveto % a s + } + for + pop pop % - + } def + + % x y width height rectfill + % x y width height rectshow + % in contrast to the languagelevel 2 operator + % they use and change the currentpath + /rectangle { + 4 -2 roll % width height x y + moveto % width height + 1 index 0 rlineto % width height % rmoveto(width, 0) + 0 exch rlineto % width % rmoveto(0, height) + neg 0 rlineto % - % rmoveto(-width, 0) + closepath + } def + + /rectfill { rectangle fill } def + /rectstroke { rectangle stroke } def +} +if + +% -- small test program +% 75 75 moveto /Times-Roman findfont 12 scalefont setfont +% <292a2b2c2d2e2f30313233343536373839> +% [5 5 6 6 6 6 6 6 6 6 6 6 7 7 7 7 5] xshow <21>[0] xshow +% showpage + +% +% +% shortcuts for image header with compression +% +% + +/psp_lzwfilter { + currentfile /ASCII85Decode filter /LZWDecode filter +} def +/psp_ascii85filter { + currentfile /ASCII85Decode filter +} def +/psp_lzwstring { + psp_lzwfilter 1024 string readstring +} def +/psp_ascii85string { + psp_ascii85filter 1024 string readstring +} def +/psp_imagedict { + /psp_bitspercomponent { + 3 eq + { 1 } + { 8 } + ifelse + } def + /psp_decodearray { + [ [0 1 0 1 0 1] [0 255] [0 1] [0 255] ] exch get + } def + + 7 dict dup + /ImageType 1 put dup + /Width 7 -1 roll put dup + /Height 5 index put dup + /BitsPerComponent 4 index + psp_bitspercomponent put dup + /Decode 5 -1 roll + psp_decodearray put dup + /ImageMatrix [1 0 0 1 0 0] dup + 5 8 -1 roll put put dup + /DataSource 4 -1 roll + 1 eq + { psp_lzwfilter } + { psp_ascii85filter } + ifelse put +} def + + +% +% +% font encoding and reencoding +% +% + +/ISO1252Encoding [ + /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef + /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef + /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef + /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef + /space /exclam /quotedbl /numbersign /dollar /percent /ampersand /quotesingle + /parenleft /parenright /asterisk /plus /comma /hyphen /period /slash + /zero /one /two /three /four /five /six /seven + /eight /nine /colon /semicolon /less /equal /greater /question + /at /A /B /C /D /E /F /G + /H /I /J /K /L /M /N /O + /P /Q /R /S /T /U /V /W + /X /Y /Z /bracketleft /backslash /bracketright /asciicircum /underscore + /grave /a /b /c /d /e /f /g + /h /i /j /k /l /m /n /o + /p /q /r /s /t /u /v /w + /x /y /z /braceleft /bar /braceright /asciitilde /unused + /Euro /unused /quotesinglbase /florin /quotedblbase /ellipsis /dagger /daggerdbl + /circumflex /perthousand /Scaron /guilsinglleft /OE /unused /Zcaron /unused + /unused /quoteleft /quoteright /quotedblleft /quotedblright /bullet /endash /emdash + /tilde /trademark /scaron /guilsinglright /oe /unused /zcaron /Ydieresis + /space /exclamdown /cent /sterling /currency /yen /brokenbar /section + /dieresis /copyright /ordfeminine /guillemotleft /logicalnot /hyphen /registered /macron + /degree /plusminus /twosuperior /threesuperior /acute /mu /paragraph /periodcentered + /cedilla /onesuperior /ordmasculine /guillemotright /onequarter /onehalf /threequarters /questiondown + /Agrave /Aacute /Acircumflex /Atilde /Adieresis /Aring /AE /Ccedilla + /Egrave /Eacute /Ecircumflex /Edieresis /Igrave /Iacute /Icircumflex /Idieresis + /Eth /Ntilde /Ograve /Oacute /Ocircumflex /Otilde /Odieresis /multiply + /Oslash /Ugrave /Uacute /Ucircumflex /Udieresis /Yacute /Thorn /germandbls + /agrave /aacute /acircumflex /atilde /adieresis /aring /ae /ccedilla + /egrave /eacute /ecircumflex /edieresis /igrave /iacute /icircumflex /idieresis + /eth /ntilde /ograve /oacute /ocircumflex /otilde /odieresis /divide + /oslash /ugrave /uacute /ucircumflex /udieresis /yacute /thorn /ydieresis +] def + +% /fontname /encoding psp_findfont +/psp_findfont { + exch dup % encoding fontname fontname + findfont % encoding fontname + dup length dict + begin + { + 1 index /FID ne + { def } + { pop pop } + ifelse + } forall + /Encoding 3 -1 roll def + currentdict + end + /psp_reencodedfont exch definefont +} def + +% bshow shows a text in artificial bold +% this is achieved by first showing the text +% then stroking its outline over it with +% the linewidth set to the second parameter +% usage: (string) num bshow + +/bshow { + currentlinewidth % save current linewidth + 3 1 roll % move it to the last stack position + currentpoint % save the current point + 3 index % copy the string to show + show % show it + moveto % move to the original coordinates again + setlinewidth % set the linewidth + false charpath % create the outline path of the shown string + stroke % and stroke it + setlinewidth % reset the stored linewidth +} def + +% bxshow shows a text with a delta array in artificial bold +% that is it does what bshow does for show +% usage: (string) [deltaarray] num bxshow + +/bxshow { + currentlinewidth % save linewidth + 4 1 roll % move it to the last stack position + setlinewidth % set the new linewidth + exch % exchange string and delta array + dup + length % get length of string + 1 sub % prepare parameters for {} for + 0 1 + 3 -1 roll + { + 1 string % create a string object length 1 + 2 index % get the text + 2 index % get charpos (for index variable) + get % have char value at charpos + 1 index % prepare string for put + exch + 0 + exch + put % put into string of length 1 + dup % duplicate the it + currentpoint % save current position + 3 -1 roll % prepare show + show % show the character + moveto % move back to beginning + currentpoint % save current position + 3 -1 roll % prepare outline path of character + false charpath + stroke % stroke it + moveto % move back + % now move to next point + 2 index % get advance array + exch % get charpos + get % get advance element + 0 rmoveto % advance current position + } for + pop pop % remove string and delta array + setlinewidth % restore linewidth +} def diff --git a/vcl/unx/generic/printergfx/psputil.cxx b/vcl/unx/generic/printergfx/psputil.cxx new file mode 100644 index 000000000000..370114be47cb --- /dev/null +++ b/vcl/unx/generic/printergfx/psputil.cxx @@ -0,0 +1,268 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include <string.h> + +#include "psputil.hxx" + +#include "tools/debug.hxx" + +namespace psp { + +/* + * string convenience routines + */ + +sal_Int32 +getHexValueOf (sal_Int32 nValue, sal_Char* pBuffer) +{ + const static sal_Char pHex [0x10] = { + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + + pBuffer[0] = pHex [(nValue & 0xF0) >> 4]; + pBuffer[1] = pHex [(nValue & 0x0F) ]; + + return 2; +} + +sal_Int32 +getAlignedHexValueOf (sal_Int32 nValue, sal_Char* pBuffer) +{ + // get sign + sal_Bool bNegative = nValue < 0; + nValue = bNegative ? -nValue : nValue; + + // get required buffer size, must be a multiple of two + sal_Int32 nPrecision; + if (nValue < 0x80) + nPrecision = 2; + else + if (nValue < 0x8000) + nPrecision = 4; + else + if (nValue < 0x800000) + nPrecision = 6; + else + nPrecision = 8; + + // convert the int into its hex representation, write it into the buffer + sal_Int32 nRet = nPrecision; + while (nPrecision) + { + nPrecision -= getHexValueOf (nValue % 256, pBuffer + nPrecision - 2 ); + nValue /= 256; + } + + // set sign bit + if (bNegative) + { + switch (pBuffer[0]) + { + case '0' : pBuffer[0] = '8'; break; + case '1' : pBuffer[0] = '9'; break; + case '2' : pBuffer[0] = 'A'; break; + case '3' : pBuffer[0] = 'B'; break; + case '4' : pBuffer[0] = 'C'; break; + case '5' : pBuffer[0] = 'D'; break; + case '6' : pBuffer[0] = 'E'; break; + case '7' : pBuffer[0] = 'F'; break; + default: DBG_ERROR("Already a signed value"); + } + } + + // report precision + return nRet; +} + + +sal_Int32 +getValueOf (sal_Int32 nValue, sal_Char* pBuffer) +{ + sal_Int32 nChar = 0; + if (nValue < 0) + { + pBuffer [nChar++] = '-'; + nValue *= -1; + } + else + if (nValue == 0) + { + pBuffer [nChar++] = '0'; + return nChar; + } + + sal_Char pInvBuffer [32]; + sal_Int32 nInvChar = 0; + while (nValue > 0) + { + pInvBuffer [nInvChar++] = '0' + nValue % 10; + nValue /= 10; + } + while (nInvChar > 0) + { + pBuffer [nChar++] = pInvBuffer [--nInvChar]; + } + + return nChar; +} + +sal_Int32 +appendStr (const sal_Char* pSrc, sal_Char* pDst) +{ + sal_Int32 nBytes = strlen (pSrc); + strncpy (pDst, pSrc, nBytes + 1); + + return nBytes; +} + +sal_Int32 +appendStr (const sal_Char* pSrc, sal_Char* pDst, sal_Int32 nBytes) +{ + strncpy (pDst, pSrc, nBytes); + pDst [nBytes] = '\0'; + return nBytes; +} + +/* + * copy strings to file + */ + +sal_Bool +WritePS (osl::File* pFile, const sal_Char* pString) +{ + sal_uInt64 nInLength = rtl_str_getLength (pString); + sal_uInt64 nOutLength = 0; + + if (nInLength > 0 && pFile) + pFile->write (pString, nInLength, nOutLength); + + return nInLength == nOutLength; +} + +sal_Bool +WritePS (osl::File* pFile, const sal_Char* pString, sal_uInt64 nInLength) +{ + sal_uInt64 nOutLength = 0; + + if (nInLength > 0 && pFile) + pFile->write (pString, nInLength, nOutLength); + + return nInLength == nOutLength; +} + +sal_Bool +WritePS (osl::File* pFile, const rtl::OString &rString) +{ + sal_uInt64 nInLength = rString.getLength(); + sal_uInt64 nOutLength = 0; + + if (nInLength > 0 && pFile) + pFile->write (rString, nInLength, nOutLength); + + return nInLength == nOutLength; +} + +sal_Bool +WritePS (osl::File* pFile, const rtl::OUString &rString) +{ + return WritePS (pFile, rtl::OUStringToOString(rString, RTL_TEXTENCODING_ASCII_US)); +} + +/* + * cache converter for use in postscript drawing routines + */ + +ConverterFactory::ConverterFactory() +{ +} + +ConverterFactory::~ConverterFactory () +{ + for( std::map< rtl_TextEncoding, rtl_UnicodeToTextConverter >::const_iterator it = m_aConverters.begin(); it != m_aConverters.end(); ++it ) + rtl_destroyUnicodeToTextConverter (it->second); +} + +rtl_UnicodeToTextConverter +ConverterFactory::Get (rtl_TextEncoding nEncoding) +{ + if (rtl_isOctetTextEncoding( nEncoding )) + { + std::map< rtl_TextEncoding, rtl_UnicodeToTextConverter >::const_iterator it = + m_aConverters.find( nEncoding ); + rtl_UnicodeToTextConverter aConverter; + if (it == m_aConverters.end()) + { + aConverter = rtl_createUnicodeToTextConverter (nEncoding); + m_aConverters[nEncoding] = aConverter; + } + else + aConverter = it->second; + return aConverter; + } + return NULL; +} + +// wrapper for rtl_convertUnicodeToText that handles the usual cases for +// textconversion in drawtext +sal_Size +ConverterFactory::Convert (const sal_Unicode *pText, int nTextLen, + sal_uChar *pBuffer, sal_Size nBufferSize, rtl_TextEncoding nEncoding) +{ + const sal_uInt32 nCvtFlags = RTL_UNICODETOTEXT_FLAGS_UNDEFINED_QUESTIONMARK + | RTL_UNICODETOTEXT_FLAGS_INVALID_QUESTIONMARK ; + sal_uInt32 nCvtInfo; + sal_Size nCvtChars; + + rtl_UnicodeToTextConverter aConverter = Get (nEncoding); + rtl_UnicodeToTextContext aContext = rtl_createUnicodeToTextContext (aConverter); + + sal_Size nSize = rtl_convertUnicodeToText (aConverter, aContext, + pText, nTextLen, (sal_Char*)pBuffer, nBufferSize, + nCvtFlags, &nCvtInfo, &nCvtChars); + + rtl_destroyUnicodeToTextContext (aConverter, aContext); + + return nSize; +} + +ConverterFactory* +GetConverterFactory () +{ + static ConverterFactory* pCvt = NULL; + + if (pCvt == NULL) + pCvt = new ConverterFactory; + + return pCvt; +} + + +} /* namespace psp */ diff --git a/vcl/unx/generic/printergfx/psputil.hxx b/vcl/unx/generic/printergfx/psputil.hxx new file mode 100644 index 000000000000..d4af41bb95ed --- /dev/null +++ b/vcl/unx/generic/printergfx/psputil.hxx @@ -0,0 +1,78 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _PSPRINT_PRINTERUTIL_HXX_ +#define _PSPRINT_PRINTERUTIL_HXX_ + +#include "osl/file.hxx" + +#include "rtl/ustring.hxx" +#include "rtl/string.hxx" +#include "rtl/tencinfo.h" +#include "rtl/textcvt.h" + +#include <map> + +namespace psp { + +/* + * string convenience routines + * sizeof(pBuffer) must be at least 2 Bytes, 0x00 <= nValue <= 0xFF, + * effective buffer of get*ValueOf() is NOT NULL-terminated + */ +sal_Int32 getHexValueOf (sal_Int32 nValue, sal_Char* pBuffer); +sal_Int32 getAlignedHexValueOf (sal_Int32 nValue, sal_Char* pBuffer); +sal_Int32 getValueOf (sal_Int32 nValue, sal_Char* pBuffer); +sal_Int32 appendStr (const sal_Char* pSrc, sal_Char* pDst); +sal_Int32 appendStr (const sal_Char* pSrc, sal_Char* pDst, sal_Int32 nBytes); + +sal_Bool WritePS (osl::File* pFile, const sal_Char* pString); +sal_Bool WritePS (osl::File* pFile, const sal_Char* pString, sal_uInt64 nInLength); +sal_Bool WritePS (osl::File* pFile, const rtl::OString &rString); +sal_Bool WritePS (osl::File* pFile, const rtl::OUString &rString); + +class ConverterFactory +{ + +public: + ConverterFactory(); + ~ConverterFactory(); + rtl_UnicodeToTextConverter Get (rtl_TextEncoding nEncoding); + sal_Size Convert (const sal_Unicode *pText, int nTextLen, + sal_uChar *pBuffer, sal_Size nBufferSize, + rtl_TextEncoding nEncoding); +private: + + std::map< rtl_TextEncoding, rtl_UnicodeToTextConverter > m_aConverters; +}; + +ConverterFactory* GetConverterFactory (); + +} /* namespace psp */ + +#endif /* _PSPRINT_PRINTERUTIL_HXX_ */ + diff --git a/vcl/unx/generic/printergfx/text_gfx.cxx b/vcl/unx/generic/printergfx/text_gfx.cxx new file mode 100644 index 000000000000..7934821d4d70 --- /dev/null +++ b/vcl/unx/generic/printergfx/text_gfx.cxx @@ -0,0 +1,862 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include <math.h> + +#include "psputil.hxx" +#include "glyphset.hxx" + +#include "printergfx.hxx" +#include "vcl/fontmanager.hxx" +#include "vcl/helper.hxx" + +#include "osl/thread.h" + +#include "sal/alloca.h" + +using namespace psp ; + +namespace psp { +/* + container for a font and its helper fonts: + 1st font is the font substitute e.g. helvetica substitutes arial on the printer + 2nd is the font itself + 3rd is a fallback font, usually a font with unicode glyph repertoir (e.g. andale) + symbol fonts (adobe-fontspecific) may need special glyphmapping + (symbol page vc. latin page) +*/ +class Font3 +{ + private: + + #define Font3Size 3 + + fontID mpFont [Font3Size]; + bool mbSymbol; + + public: + + fontID GetFont (int nIdx) const + { return nIdx < Font3Size ? mpFont[nIdx] : -1 ; } + bool IsSymbolFont () const + { return mbSymbol; } + + Font3 (const PrinterGfx &rGfx); + ~Font3 () {} +}; + +Font3::Font3(const PrinterGfx &rGfx) +{ + mpFont[0] = rGfx.getFontSubstitute(); + mpFont[1] = rGfx.GetFontID(); + mpFont[2] = rGfx.getFallbackID(); + // mpFont[2] = rGfx.GetFontID(); + + PrintFontManager &rMgr = PrintFontManager::get(); + mbSymbol = mpFont[1] != -1 ? + rMgr.getFontEncoding(mpFont[1]) == RTL_TEXTENCODING_SYMBOL : false; +} + +} // namespace psp + +static int getVerticalDeltaAngle( sal_Unicode nChar ) +{ + int nAngle = 0; + if( ( nChar >= 0x1100 && nChar < 0x11fa ) || + ( nChar >= 0x3000 && nChar < 0xfb00 ) || + ( nChar >= 0xfe20 && nChar < 0xfe70 ) || + ( nChar >= 0xff00 && nChar < 0xff64 ) + ) + { + /* #i52932# remember: + nChar == 0x2010 || nChar == 0x2015 + nChar == 0x2016 || nChar == 0x2026 + + are nAngle = 0 also, but already handled in the first if + */ + if( ( nChar >= 0x3008 && nChar < 0x3019 && nChar != 0x3012 ) || + nChar == 0xff3b || nChar == 0xff3d || + (nChar >= 0xff6b && nChar < 0xff64 ) || + nChar == 0xffe3 + ) + nAngle = 0; + else if( nChar == 0x30fc ) + nAngle = -900; + else + nAngle = 900; + } + return nAngle; +} + +void +PrinterGfx::PSUploadPS1Font (sal_Int32 nFontID) +{ + std::list< sal_Int32 >::iterator aFont; + // already in the document header ? + for (aFont = maPS1Font.begin(); aFont != maPS1Font.end(); ++aFont ) + if( nFontID == *aFont ) + return; + + // no occurrenc yet, mark for download + // add the fontid to the list + maPS1Font.push_back (nFontID); +} + +/* + * implement text handling printer routines, + */ + +sal_uInt16 +PrinterGfx::SetFont( + sal_Int32 nFontID, + sal_Int32 nHeight, + sal_Int32 nWidth, + sal_Int32 nAngle, + bool bVertical, + bool bArtItalic, + bool bArtBold + ) +{ + // font and encoding will be set by drawText again immediately + // before PSShowText + mnFontID = nFontID; + maVirtualStatus.maFont = rtl::OString(); + maVirtualStatus.maEncoding = RTL_TEXTENCODING_DONTKNOW; + maVirtualStatus.mnTextHeight = nHeight; + maVirtualStatus.mnTextWidth = nWidth; + maVirtualStatus.mbArtItalic = bArtItalic; + maVirtualStatus.mbArtBold = bArtBold; + mnTextAngle = nAngle; + mbTextVertical = bVertical; + + return 0; +} + +sal_uInt16 +PrinterGfx::SetFallbackFont ( sal_Int32 nFontID ) +{ + mnFallbackID = nFontID; + return 0; +} + +void PrinterGfx::drawGlyphs( + const Point& rPoint, + sal_uInt32* pGlyphIds, + sal_Unicode* pUnicodes, + sal_Int16 nLen, + sal_Int32* pDeltaArray + ) +{ + + // draw the string + // search for a glyph set matching the set font + std::list< GlyphSet >::iterator aIter; + for (aIter = maPS3Font.begin(); aIter != maPS3Font.end(); aIter++) + if ( ((*aIter).GetFontID() == mnFontID) + && ((*aIter).IsVertical() == mbTextVertical)) + { + (*aIter).DrawGlyphs (*this, rPoint, pGlyphIds, pUnicodes, nLen, pDeltaArray); + break; + } + + // not found ? create a new one + if (aIter == maPS3Font.end()) + { + maPS3Font.push_back (GlyphSet(mnFontID, mbTextVertical)); + maPS3Font.back().DrawGlyphs (*this, rPoint, pGlyphIds, pUnicodes, nLen, pDeltaArray); + } +} + +void PrinterGfx::DrawGlyphs( + const Point& rPoint, + sal_GlyphId* pGlyphIds, + sal_Unicode* pUnicodes, + sal_Int16 nLen, + sal_Int32* pDeltaArray + ) +{ + if( nLen <= 0 ) + return; + + if ( !mrFontMgr.isFontDownloadingAllowed( mnFontID ) ) + { + LicenseWarning(rPoint, pUnicodes, nLen, pDeltaArray); + return; + } + + if( mrFontMgr.getFontType( mnFontID ) != fonttype::TrueType ) + { + DrawText( rPoint, pUnicodes, nLen, pDeltaArray ); + return; + } + + // move and rotate the user coordinate system + // avoid the gsave/grestore for the simple cases since it allows + // reuse of the current font if it hasn't changed + sal_Int32 nCurrentTextAngle = mnTextAngle; + Point aPoint( rPoint ); + + if (nCurrentTextAngle != 0) + { + PSGSave (); + PSTranslate (rPoint); + PSRotate (nCurrentTextAngle); + mnTextAngle = 0; + aPoint = Point( 0, 0 ); + } + + if( mbTextVertical ) + { + // vertical glyphs can have an additional rotation ... sigh. + // so break up text in chunks of normal glyphs and print out + // specially rotated glyphs extra + sal_uInt32* pTempGlyphIds = (sal_uInt32*)alloca(sizeof(sal_Int32)*nLen); + sal_Int32* pTempDelta = (sal_Int32*)alloca(sizeof(sal_Int32)*nLen); + sal_Unicode* pTempUnicodes = (sal_Unicode*)alloca(sizeof(sal_Unicode)*nLen); + sal_Int16 nTempLen = 0; + sal_Int32 nTempFirstDelta = 0; + Point aRotPoint; + sal_Int32 nTextHeight = maVirtualStatus.mnTextHeight; + sal_Int32 nTextWidth = maVirtualStatus.mnTextWidth ? maVirtualStatus.mnTextWidth : maVirtualStatus.mnTextHeight; + sal_Int32 nAscend = mrFontMgr.getFontAscend( mnFontID ); + sal_Int32 nDescend = mrFontMgr.getFontDescend( mnFontID ); + + nDescend = nDescend * nTextHeight / 1000; + nAscend = nAscend * nTextHeight / 1000; + + for( sal_Int16 i = 0; i < nLen; i++ ) + { + const sal_GlyphId nRot = pGlyphIds[i] & GF_ROTMASK; + if( nRot == GF_NONE ) + { + pTempUnicodes[nTempLen] = pUnicodes[i]; + pTempGlyphIds[nTempLen] = pGlyphIds[i]; + if( nTempLen > 0 ) + pTempDelta[nTempLen-1] = pDeltaArray[i-1]-nTempFirstDelta; + else + { + // the first element in pDeltaArray shows + // the offset of the second character + // so if the first glyph is normal + // then we do not need to move the delta indices + // else we have to move them down by one and + // recalculate aPoint and all deltas + if( i != 0 ) + nTempFirstDelta = pDeltaArray[ i-1 ]; + } + nTempLen++; + } + else + { + sal_Int32 nOffset = i > 0 ? pDeltaArray[i-1] : 0; + sal_Int32 nRotAngle = 0; + switch( nRot ) + { + case GF_ROTR: + nRotAngle = 2700; + aRotPoint = Point( -nAscend*nTextWidth/nTextHeight, -nDescend*nTextWidth/nTextHeight - nOffset ); + break; + case GF_VERT: + nRotAngle = 1800; + aRotPoint = Point( -nOffset, (nAscend+nDescend) ); + break; + case GF_ROTL: + nRotAngle = 900; + aRotPoint = Point( -nDescend*nTextWidth/nTextHeight, nOffset + nAscend*nTextWidth/nTextHeight ); + break; + } + sal_GlyphId nRotGlyphId = pGlyphIds[i]; + sal_Unicode nRotUnicode = pUnicodes[i]; + sal_Int32 nRotDelta = 0; + + // transform matrix to new individual direction + PSGSave (); + GraphicsStatus aSaveStatus = maVirtualStatus; + if( nRot != 2 ) // switch font aspect + { + maVirtualStatus.mnTextWidth = nTextHeight; + maVirtualStatus.mnTextHeight = nTextWidth; + } + if( aPoint.X() || aPoint.Y() ) + PSTranslate( aPoint ); + PSRotate (nRotAngle); + // draw the rotated glyph + drawGlyphs( aRotPoint, &nRotGlyphId, &nRotUnicode, 1, &nRotDelta ); + + // restore previous state + maVirtualStatus = aSaveStatus; + PSGRestore(); + } + } + + pGlyphIds = pTempGlyphIds; + pUnicodes = pTempUnicodes; + pDeltaArray = pTempDelta; + nLen = nTempLen; + + aPoint.X() += nTempFirstDelta; + } + + if( nLen > 0 ) + drawGlyphs( aPoint, pGlyphIds, pUnicodes, nLen, pDeltaArray ); + + // restore the user coordinate system + if (nCurrentTextAngle != 0) + { + PSGRestore (); + mnTextAngle = nCurrentTextAngle; + } +} + +void +PrinterGfx::DrawText ( + const Point& rPoint, + const sal_Unicode* pStr, + sal_Int16 nLen, + const sal_Int32* pDeltaArray + ) +{ + fontID nRestoreFont = mnFontID; + + // setup font[substitutes] and map the string into the symbol area in case of + // symbol font + Font3 aFont(*this); + sal_Unicode *pEffectiveStr; + if ( aFont.IsSymbolFont() ) + { + pEffectiveStr = (sal_Unicode*)alloca(nLen * sizeof(pStr[0])); + for (int i = 0; i < nLen; i++) + pEffectiveStr[i] = pStr[i] < 256 ? pStr[i] + 0xF000 : pStr[i]; + } + else + { + pEffectiveStr = const_cast<sal_Unicode*>(pStr); + } + + fontID *pFontMap = (fontID*) alloca(nLen * sizeof(fontID)); + sal_Int32 *pCharWidth = (sal_Int32*) alloca(nLen * sizeof(sal_Int32)); + + for( int n = 0; n < nLen; n++ ) + { + CharacterMetric aBBox; + pFontMap[n] = getCharMetric (aFont, pEffectiveStr[n], &aBBox); + pCharWidth[n] = getCharWidth (mbTextVertical, pEffectiveStr[n], &aBBox); + } + + // setup a new delta array, use virtual resolution of 1000 + sal_Int32* pNewDeltaArray = (sal_Int32*)alloca( sizeof( sal_Int32 )*nLen ); + if ( pDeltaArray != 0) + { + for (int i = 0; i < nLen - 1; i++) + pNewDeltaArray[i] = 1000 * pDeltaArray[i]; + pNewDeltaArray[nLen - 1] = 0; + } + else + { + pNewDeltaArray[0] = pCharWidth[0]; + for (int i = 1; i < nLen; i++) + pNewDeltaArray[i] = pNewDeltaArray[i-1] + pCharWidth[i]; + } + + // move and rotate the user coordinate system + // avoid the gsave/grestore for the simple cases since it allows + // reuse of the current font if it hasn't changed + sal_Int32 nCurrentTextAngle = mnTextAngle; + sal_Int32 nCurrentPointX; + sal_Int32 nCurrentPointY; + + if (nCurrentTextAngle != 0) + { + PSGSave (); + PSTranslate (rPoint); + PSRotate (nCurrentTextAngle); + mnTextAngle = 0; + + nCurrentPointX = 0; + nCurrentPointY = 0; + } + else + { + nCurrentPointX = rPoint.X(); + nCurrentPointY = rPoint.Y(); + } + + // draw the string + sal_Int32 nDelta = 0; + for (int nTo = 0; nTo < nLen; ) + { + int nFrom = nTo; + fontID nFont = pFontMap[ nFrom ]; + + while ((nTo < nLen) && (nFont == pFontMap[nTo])) + { + pNewDeltaArray[ nTo ] = (sal_Int32)(((0.5 + pNewDeltaArray[ nTo ]) / 1000.0) - nDelta); + nTo++ ; + } + + SetFont( nFont, + maVirtualStatus.mnTextHeight, maVirtualStatus.mnTextWidth, + mnTextAngle, + mbTextVertical, + maVirtualStatus.mbArtItalic, + maVirtualStatus.mbArtBold + ); + + if (mbTextVertical) + { + drawVerticalizedText( + Point(nCurrentPointX + nDelta, nCurrentPointY), + pEffectiveStr + nFrom, nTo - nFrom, + pNewDeltaArray + nFrom ); + } + else + { + drawText( + Point(nCurrentPointX + nDelta, nCurrentPointY), + pEffectiveStr + nFrom, nTo - nFrom, + pDeltaArray == NULL ? NULL : pNewDeltaArray + nFrom ); + } + nDelta += pNewDeltaArray[ nTo - 1 ]; + } + + // restore the user coordinate system + if (nCurrentTextAngle != 0) + { + PSGRestore (); + mnTextAngle = nCurrentTextAngle; + } + + // restore the original font settings + SetFont( nRestoreFont, + maVirtualStatus.mnTextHeight, maVirtualStatus.mnTextWidth, + mnTextAngle, mbTextVertical, + maVirtualStatus.mbArtItalic, + maVirtualStatus.mbArtBold + ); +} + +void PrinterGfx::drawVerticalizedText( + const Point& rPoint, + const sal_Unicode* pStr, + sal_Int16 nLen, + const sal_Int32* pDeltaArray + ) +{ + sal_Int32* pDelta = (sal_Int32*)alloca( nLen * sizeof(sal_Int32) ); + + int nTextScale = maVirtualStatus.mnTextWidth ? maVirtualStatus.mnTextWidth : maVirtualStatus.mnTextHeight; + int nNormalAngle = mnTextAngle; + int nDeltaAngle, nLastPos = 0; + + double fSin = sin( -2.0*M_PI*nNormalAngle/3600 ); + double fCos = cos( -2.0*M_PI*nNormalAngle/3600 ); + + PrintFontManager &rMgr = PrintFontManager::get(); + PrintFontInfo aInfo; + rMgr.getFontInfo( mnFontID, aInfo ); + + bool* pGsubFlags = (bool*)alloca( nLen * sizeof(bool) ); + rMgr.hasVerticalSubstitutions( mnFontID, pStr, nLen, pGsubFlags ); + + Point aPoint( rPoint ); + for( int i = 0; i < nLen; ) + { + while( ( nDeltaAngle = getVerticalDeltaAngle( pStr[i] ) ) == 0 && i < nLen ) + i++; + if( i <= nLen && i > nLastPos ) + { + for( int n = nLastPos; n < i; n++ ) + pDelta[n] = pDeltaArray[n] - (aPoint.X() - rPoint.X() ); + + SetFont( mnFontID, + maVirtualStatus.mnTextHeight, maVirtualStatus.mnTextWidth, + nNormalAngle, mbTextVertical, + maVirtualStatus.mbArtItalic, + maVirtualStatus.mbArtBold ); + drawText( aPoint, pStr + nLastPos, i - nLastPos, pDelta + nLastPos ); + + aPoint.X() = (sal_Int32)(rPoint.X() + ((double)pDeltaArray[i-1] * fCos)); + aPoint.Y() = (sal_Int32)(rPoint.Y() + ((double)pDeltaArray[i-1] * fSin)); + } + if( i < nLen ) + { + int nOldWidth = maVirtualStatus.mnTextWidth; + int nOldHeight = maVirtualStatus.mnTextHeight; + SetFont( mnFontID, + nTextScale, + maVirtualStatus.mnTextHeight, + nNormalAngle + nDeltaAngle, + mbTextVertical, + maVirtualStatus.mbArtItalic, + maVirtualStatus.mbArtBold ); + + double nA = nTextScale * aInfo.m_nAscend / 1000.0; + double nD = nTextScale * aInfo.m_nDescend / 1000.0; + double fStretch = (double)maVirtualStatus.mnTextWidth / maVirtualStatus.mnTextHeight; + if( !pGsubFlags[i] ) + nD *= fStretch; + + Point aPos( aPoint ); + switch( nDeltaAngle ) + { + case +900: + aPos.X() += (sal_Int32)(+nA * fCos + nD * fSin); + aPos.Y() += (sal_Int32)(-nA * fSin + nD * fCos); + break; + case -900: + aPos.X() += (sal_Int32)(+nA * fSin + nD * fCos); + aPos.Y() += (sal_Int32)(-(nTextScale*fStretch - nD) * fCos); + break; + } + drawText( aPos, pStr+i, 1, NULL ); + if( i < nLen-1 && pDeltaArray ) + { + aPoint.X() = (sal_Int32)(rPoint.X() + ((double)pDeltaArray[i] * fCos)); + aPoint.Y() = (sal_Int32)(rPoint.Y() + ((double)pDeltaArray[i] * fSin)); + } + + // swap text width/height again + SetFont( mnFontID, + nOldHeight, + nOldWidth, + nNormalAngle, + mbTextVertical, + maVirtualStatus.mbArtItalic, + maVirtualStatus.mbArtBold ); + } + i++; + nLastPos = i; + } + mnTextAngle = nNormalAngle; +} + +void +PrinterGfx::LicenseWarning(const Point& rPoint, const sal_Unicode* pStr, + sal_Int16 nLen, const sal_Int32* pDeltaArray) +{ + // treat it like a builtin font in case a user has that font also in the + // printer. This is not so unlikely as it may seem; no print embedding + // licensed fonts are often used (or so they say) in companies: + // they are installed on displays and printers, but get not embedded in + // they are installed on displays and printers, but get not embedded in + // print files or documents because they are not licensed for use outside + // the company. + rtl::OString aMessage( "The font " ); + aMessage += rtl::OUStringToOString( mrFontMgr.getPSName(mnFontID), + RTL_TEXTENCODING_ASCII_US ); + aMessage += " could not be downloaded\nbecause its license does not allow for that"; + PSComment( aMessage.getStr() ); + + rtl::OString aFontName = rtl::OUStringToOString( + mrFontMgr.getPSName(mnFontID), + RTL_TEXTENCODING_ASCII_US); + PSSetFont (aFontName, RTL_TEXTENCODING_ISO_8859_1); + + sal_Size nSize = 4 * nLen; + sal_uChar* pBuffer = (sal_uChar*)alloca (nSize* sizeof(sal_uChar)); + + ConverterFactory* pCvt = GetConverterFactory (); + nSize = pCvt->Convert (pStr, nLen, pBuffer, nSize, RTL_TEXTENCODING_ISO_8859_1); + + PSMoveTo (rPoint); + PSShowText (pBuffer, nLen, nSize, pDeltaArray); +} + +void +PrinterGfx::drawText( + const Point& rPoint, + const sal_Unicode* pStr, + sal_Int16 nLen, + const sal_Int32* pDeltaArray + ) +{ + if (!(nLen > 0)) + return; + + fonttype::type eType = mrFontMgr.getFontType (mnFontID); + + if (eType == fonttype::Type1) + PSUploadPS1Font (mnFontID); + + if ( eType == fonttype::TrueType + && !mrFontMgr.isFontDownloadingAllowed(mnFontID)) + { + LicenseWarning(rPoint, pStr, nLen, pDeltaArray); + return; + } + + if( mrFontMgr.getUseOnlyFontEncoding( mnFontID ) ) + { + GlyphSet aGSet( mnFontID, mbTextVertical ); + aGSet.DrawText( *this, rPoint, pStr, nLen, pDeltaArray ); + return; + } + + // search for a glyph set matching the set font + std::list< GlyphSet >::iterator aIter; + for (aIter = maPS3Font.begin(); aIter != maPS3Font.end(); aIter++) + if ( ((*aIter).GetFontID() == mnFontID) + && ((*aIter).IsVertical() == mbTextVertical)) + { + (*aIter).DrawText (*this, rPoint, pStr, nLen, pDeltaArray); + break; + } + + // not found ? create a new one + if (aIter == maPS3Font.end()) + { + maPS3Font.push_back (GlyphSet(mnFontID, mbTextVertical)); + maPS3Font.back().DrawText (*this, rPoint, pStr, nLen, pDeltaArray); + } +} + +int +PrinterGfx::getCharWidth (sal_Bool b_vert, sal_Unicode n_char, CharacterMetric *p_bbox) +{ + b_vert = b_vert && (getVerticalDeltaAngle(n_char) != 0); + int w = b_vert ? p_bbox->height : p_bbox->width; + w *= maVirtualStatus.mnTextWidth ? maVirtualStatus.mnTextWidth : maVirtualStatus.mnTextHeight; + return w; +} + +fontID +PrinterGfx::getCharMetric (const Font3 &rFont, sal_Unicode n_char, CharacterMetric *p_bbox) +{ + p_bbox->width = -1; + p_bbox->height = -1; + + for (fontID n = 0; n < 3; n++) + { + fontID n_font = rFont.GetFont(n); + if (n_font != -1) + { + if( mbStrictSO52Compatibility ) + { + fonttype::type eType = mrFontMgr.getFontType( n_font ); + if( (eType == fonttype::Builtin || eType == fonttype::Type1) ) + { + // note: any character exchanged here MUST also be changed + // in the compatibility ISO encoding vector in the prolog + // in printerjob.cxx + sal_Unicode aRepl = 0; + if( n_char == 0x2d ) + aRepl = 0x2212; + else if( n_char == 0x27 ) + aRepl = 0x2019; + /* + additional characters that may need backwards compatibility: + ISO5589 StdEnc Unicode suggested n_char -> aRepl + 0264 0302 0x00B4 0x00B4 (acute) -> 0x2019 (quiteright) + 0246 - 0x00A6 0x00A6 (brokenbar) -> 0x007C (bar) + 0225 0267 0x0095 0x0095 () -> 0x2022 (bullet) + 0140 0301 0x0060 0x0060 (grave) -> ? + */ + if( aRepl ) + { + mrFontMgr.getMetrics( n_font, aRepl, aRepl, p_bbox ); + if (p_bbox->width >= 0 && p_bbox->height >= 0) + return n_font; + } + } + } + mrFontMgr.getMetrics( n_font, n_char, n_char, p_bbox ); + } + if (p_bbox->width >= 0 && p_bbox->height >= 0) + return n_font; + } + if (n_char != '?') + return getCharMetric (rFont, '?', p_bbox); + + return rFont.GetFont(0) != -1 ? rFont.GetFont(0) : rFont.GetFont(1); +} + +fontID +PrinterGfx::getFontSubstitute () const +{ + if( mpFontSubstitutes ) + { + ::std::hash_map< fontID, fontID >::const_iterator it = + mpFontSubstitutes->find( mnFontID ); + if( it != mpFontSubstitutes->end() ) + return it->second; + } + + return -1; +} + +sal_Int32 +PrinterGfx::GetCharWidth (sal_Unicode nFrom, sal_Unicode nTo, long *pWidthArray) +{ + Font3 aFont(*this); + if (aFont.IsSymbolFont() && (nFrom < 256) && (nTo < 256)) + { + nFrom += 0xF000; + nTo += 0xF000; + } + + for( int n = 0; n < (nTo - nFrom + 1); n++ ) + { + CharacterMetric aBBox; + getCharMetric (aFont, n + nFrom, &aBBox); + pWidthArray[n] = getCharWidth (mbTextVertical, n + nFrom, &aBBox); + } + + // returned metrics have postscript precision + return 1000; +} + +const ::std::list< KernPair >& PrinterGfx::getKernPairs( bool bVertical ) const +{ + /* + * Note: this is only a 80% solution: if a font is only + * partially substituted in a string due to missing glyphs + * the results may not be perfect; the more so the more the + * substitution differs from the original metricwise. But + * vcl only asks for KernPairs for each font once and NOT + * in a string context this is the best we can do. + * In future the kerning should be done on a per string basis. + */ + fontID nFont = mnFontID; + if( mpFontSubstitutes ) + { + ::std::hash_map< fontID, fontID >::const_iterator it = + mpFontSubstitutes->find( mnFontID ); + if( it != mpFontSubstitutes->end() ) + nFont = it->second; + } + return mrFontMgr.getKernPairs( nFont, bVertical ); +} + +/* + * advanced glyph handling + */ + +sal_Bool +PrinterGfx::GetGlyphBoundRect (sal_Unicode /*c*/, Rectangle& /*rOutRect*/) +{ + return 0; +} + +sal_uInt32 +PrinterGfx::GetGlyphOutline (sal_Unicode /*c*/, + sal_uInt16 **/*ppPolySizes*/, Point **/*ppPoints*/, sal_uInt8 **/*ppFlags*/) +{ + return 0; +} + +/* + * spool the converted truetype fonts to the page header after the page body is + * complete + * for Type1 fonts spool additional reencoding vectors that are necessary to access the + * whole font + */ + +void +PrinterGfx::OnEndPage () +{ +} + +void +PrinterGfx::OnEndJob () +{ + maPS3Font.clear(); + maPS1Font.clear(); +} + +void +PrinterGfx::writeResources( osl::File* pFile, std::list< rtl::OString >& rSuppliedFonts, std::list< rtl::OString >& rNeededFonts ) +{ + // write all type 1 fonts + std::list< sal_Int32 >::iterator aFont; + // already in the document header ? + for (aFont = maPS1Font.begin(); aFont != maPS1Font.end(); ++aFont) + { + const rtl::OString& rSysPath (mrFontMgr.getFontFileSysPath(*aFont) ); + rtl::OUString aUNCPath; + osl::File::getFileURLFromSystemPath (OStringToOUString (rSysPath, osl_getThreadTextEncoding()), aUNCPath); + osl::File aFontFile (aUNCPath); + + // provide the pfb or pfa font as a (pfa-)font resource + rtl::OString aPostScriptName = + rtl::OUStringToOString ( mrFontMgr.getPSName(*aFont), + RTL_TEXTENCODING_ASCII_US ); + + WritePS (pFile, "%%BeginResource: font "); + WritePS (pFile, aPostScriptName.getStr()); + WritePS (pFile, "\n"); + + osl::File::RC nError = aFontFile.open (OpenFlag_Read); + if (nError == osl::File::E_None) + { + convertPfbToPfa (aFontFile, *pFile); + aFontFile.close (); + + pFile->setPos(osl_Pos_Current, -1); + char lastchar = '\n'; + sal_uInt64 uBytes(1); + pFile->read((void *)(&lastchar), uBytes, uBytes); + if (lastchar != '\n') + WritePS (pFile, "\n"); + } + WritePS (pFile, "%%EndResource\n"); + rSuppliedFonts.push_back( aPostScriptName ); + } + + // write glyphsets and reencodings + std::list< GlyphSet >::iterator aIter; + for (aIter = maPS3Font.begin(); aIter != maPS3Font.end(); ++aIter) + { + if (aIter->GetFontType() == fonttype::TrueType) + { + aIter->PSUploadFont (*pFile, *this, mbUploadPS42Fonts ? true : false, rSuppliedFonts ); + } + else + // ( aIter->GetFontType() == fonttype::Type1 + // || aIter->GetFontType() == fonttype::Builtin ) + { + aIter->PSUploadEncoding (pFile, *this); + if( aIter->GetFontType() == fonttype::Builtin ) + rNeededFonts.push_back( + rtl::OUStringToOString( + mrFontMgr.getPSName( aIter->GetFontID() ), + RTL_TEXTENCODING_ASCII_US ) ); + } + } +} + +bool PrinterGfx::getStrictSO52Compatibility() const +{ + return mbStrictSO52Compatibility; +} + +void PrinterGfx::setStrictSO52Compatibility( bool bCompat) +{ + mbStrictSO52Compatibility = bCompat; +} |