tdf#156230: Drop freshly unused PrinterJob and PrinterGfx

Change-Id: I5149fb447e76044e7f4bfdd9a79ee3b454859375
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/155065
Tested-by: Jenkins
Reviewed-by: خالد حسني <khaled@libreoffice.org>
diff --git a/vcl/Library_vcl.mk b/vcl/Library_vcl.mk
index 6293e292..c29801f 100644
--- a/vcl/Library_vcl.mk
+++ b/vcl/Library_vcl.mk
@@ -559,15 +559,9 @@ vcl_headless_freetype_code=\
    vcl/unx/generic/fontmanager/fontconfig \
    vcl/unx/generic/fontmanager/fontmanager \
    vcl/unx/generic/fontmanager/helper \
    vcl/unx/generic/print/bitmap_gfx \
    vcl/unx/generic/print/common_gfx \
    vcl/unx/generic/print/glyphset \
    vcl/unx/generic/print/printerjob \
    vcl/unx/generic/print/psputil \
    vcl/unx/generic/print/genpspgraphics \
    vcl/unx/generic/print/genprnpsp \
    vcl/unx/generic/print/prtsetup \
    vcl/unx/generic/print/text_gfx \
    vcl/unx/generic/printer/jobdata \
    vcl/unx/generic/printer/ppdparser \

diff --git a/vcl/inc/unx/printergfx.hxx b/vcl/inc/unx/printergfx.hxx
deleted file mode 100644
index 0c89ef4..0000000
--- a/vcl/inc/unx/printergfx.hxx
+++ /dev/null
@@ -1,346 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */

#ifndef INCLUDED_VCL_INC_GENERIC_PRINTERGFX_HXX
#define INCLUDED_VCL_INC_GENERIC_PRINTERGFX_HXX

#include <osl/file.hxx>
#include <tools/gen.hxx>
#include <vcl/dllapi.h>

#include <impglyphitem.hxx>

#include <list>
#include <vector>

enum class PolyFlags : sal_uInt8;

namespace psp {

struct JobData;

/*
 * lightweight container to handle RGB values
 */

class PrinterColor
{
public:

    enum class ColorSpace { eInvalid, eRGB };

private:

    sal_uInt8       mnRed;
    sal_uInt8       mnGreen;
    sal_uInt8       mnBlue;
    ColorSpace      meColorspace;

public:

    PrinterColor()
        : mnRed(0)
        , mnGreen(0)
        , mnBlue(0)
        , meColorspace(ColorSpace::eInvalid)
    {}
    PrinterColor (sal_uInt16 nRed, sal_uInt16 nGreen,
                  sal_uInt16 nBlue) :
            mnRed   (nRed),
            mnGreen (nGreen),
            mnBlue  (nBlue),
            meColorspace (ColorSpace::eRGB)
    {}
    PrinterColor (sal_uInt32 nRGB) :
            mnRed   ((nRGB & 0x00ff0000) >> 16),
            mnGreen ((nRGB & 0x0000ff00) >>  8),
            mnBlue  ((nRGB & 0x000000ff)      ),
            meColorspace (ColorSpace::eRGB)
    {}

    bool        Is () const
    { return meColorspace != ColorSpace::eInvalid; }

    sal_uInt16      GetRed () const
    { return mnRed; }
    sal_uInt16      GetGreen () const
    { return mnGreen; }
    sal_uInt16      GetBlue () const
    { return mnBlue; }
    bool        operator== (const PrinterColor& aColor) const
    {
        return aColor.Is() && Is()
            && mnRed   == aColor.mnRed
            && mnGreen == aColor.mnGreen
            && mnBlue  == aColor.mnBlue;
    }
    bool        operator!= (const PrinterColor& aColor) const
    { return ! (aColor==*this); }

    PrinterColor&   operator= (sal_uInt32 nRGB)
    {
        meColorspace = ColorSpace::eRGB;
        mnBlue  = (nRGB & 0x000000ff);
        mnGreen = (nRGB & 0x0000ff00) >>  8;
        mnRed   = (nRGB & 0x00ff0000) >> 16;

        return *this;
    }
};

class GlyphSet;
class PrinterJob;
class PrintFontManager;
struct CharacterMetric;

/*
 * Bitmap Interface, this has to be filled with your actual bitmap implementation
 * sample implementations can be found in:
 *      psprint/workben/cui/pspdem.cxx
 *      vcl/unx/source/gdi/salgdi2.cxx
 */

class PrinterBmp
{
public:

    virtual             ~PrinterBmp ()  = 0;
    virtual sal_uInt32  GetPaletteColor (sal_uInt32 nIdx) const = 0;
    virtual sal_uInt32  GetPaletteEntryCount ()           const = 0;
    virtual sal_uInt32  GetPixelRGB  (sal_uInt32 nRow, sal_uInt32 nColumn) const = 0;
    virtual sal_uInt8   GetPixelGray (sal_uInt32 nRow, sal_uInt32 nColumn) const = 0;
    virtual sal_uInt8   GetPixelIdx  (sal_uInt32 nRow, sal_uInt32 nColumn) const = 0;
    virtual sal_uInt32  GetDepth ()     const = 0;
};

enum class ImageType {
    TrueColorImage,
    MonochromeImage,
    PaletteImage,
    GrayScaleImage
};

/*
 * printer raster operations
 */

struct GraphicsStatus
{
    OString        maFont;
    rtl_TextEncoding    maEncoding;
    bool                mbArtItalic;
    bool                mbArtBold;
    sal_Int32           mnTextHeight;
    sal_Int32           mnTextWidth;
    PrinterColor        maColor;
    double             mfLineWidth;

    GraphicsStatus();
};

class PrinterGfx
{
private:

    /* common settings */

    double          mfScaleX;
    double          mfScaleY;

    sal_uInt32      mnDpi;
    sal_uInt16      mnDepth;

    sal_uInt16      mnPSLevel;
    bool        mbColor;
    bool        mbUploadPS42Fonts;

    osl::File*      mpPageBody;

    /* text/font related data, for a type1 font it has to be checked
       whether this font has already been downloaded. A TrueType font
       will be converted into one or more Type3 fonts, containing glyphs
       in no particular order. In addition to the existence of the
       glyph in one of the subfonts, the mapping from unicode to the
       glyph has to be remembered */

    std::vector< GlyphSet > maPS3Font;

    sal_Int32       mnFontID;
    Degree10        mnTextAngle;
    bool            mbTextVertical;
    PrintFontManager& mrFontMgr;

    /* bitmap drawing implementation */

    void    DrawPS1GrayImage      (const PrinterBmp& rBitmap, const tools::Rectangle& rArea);
    void    writePS2ImageHeader   (const tools::Rectangle& rArea, psp::ImageType nType);
    void    writePS2Colorspace    (const PrinterBmp& rBitmap, psp::ImageType nType);
    void    DrawPS2GrayImage      (const PrinterBmp& rBitmap, const tools::Rectangle& rArea);
    void    DrawPS2PaletteImage   (const PrinterBmp& rBitmap, const tools::Rectangle& rArea);
    void    DrawPS2TrueColorImage (const PrinterBmp& rBitmap, const tools::Rectangle& rArea);
    void    DrawPS2MonoImage      (const PrinterBmp& rBitmap, const tools::Rectangle& rArea);

    /* clip region */

    std::list< tools::Rectangle > maClipRegion;
    bool JoinVerticalClipRectangles( std::list< tools::Rectangle >::iterator& it,
                                         Point& aOldPoint, sal_Int32& nColumn );

    /* color settings */
    PrinterColor    maFillColor;
    PrinterColor    maTextColor;
    PrinterColor    maLineColor;

    /* graphics state */
    GraphicsStatus                  maVirtualStatus;
    std::list< GraphicsStatus >     maGraphicsStack;
    GraphicsStatus& currentState() { return maGraphicsStack.front(); }

public:
    /* graphics status update */
    void            PSSetColor ();
    void            PSSetLineWidth ();
    void            PSSetFont ();

    /* graphics status functions */
    void            PSSetColor (const PrinterColor& rColor)
    { maVirtualStatus.maColor = rColor; }

    void            PSSetFont (const OString& rName,
                               rtl_TextEncoding nEncoding)
    { maVirtualStatus.maFont = rName; maVirtualStatus.maEncoding = nEncoding; }

    /* graphics status stack */
    void            PSGSave ();
    void            PSGRestore ();

    /* PS helpers */
    enum pspath_t { moveto = 0, lineto = 1 };
    void            PSBinLineTo (const Point& rCurrent, Point& rOld,
                                 sal_Int32& nColumn);
    void            PSBinMoveTo (const Point& rCurrent, Point& rOld,
                                 sal_Int32& nColumn);
    void            PSBinStartPath ();
    void            PSBinEndPath ();
    void            PSBinCurrentPath (sal_uInt32 nPoints, const Point* pPath);
    void            PSBinPath (const Point& rCurrent, Point& rOld,
                               pspath_t eType, sal_Int32& nColumn);

    void            PSRotate (Degree10 nAngle);
    void            PSTranslate (const Point& rPoint);
    void            PSMoveTo (const Point& rPoint);
    void            PSScale (double fScaleX, double fScaleY);
    void            PSLineTo(const Point& rPoint );
    void            PSPointOp (const Point& rPoint, const char* pOperator);
    void            PSHexString (const unsigned char* pString, sal_Int16 nLen);
    void            PSShowGlyph (const unsigned char nGlyphId);

    void            OnEndJob ();
    void            writeResources( osl::File* pFile, std::vector< OString >& rSuppliedFonts );
    PrintFontManager& GetFontMgr () { return mrFontMgr; }

    void            drawGlyph(const Point& rPoint,
                              sal_GlyphId aGlyphId);
public:
    PrinterGfx();
    ~PrinterGfx();
    void            Init (PrinterJob &rPrinterSpec);
    void            Init (const JobData& rData);
    void            Clear();

    // query depth
    sal_uInt16      GetBitCount () const { return mnDepth;}

    // clip region
    void            ResetClipRegion ();
    void            BeginSetClipRegion();
    void            UnionClipRegion (sal_Int32 nX, sal_Int32 nY,
                                     sal_Int32 nDX, sal_Int32 nDY);
    void            EndSetClipRegion ();

    // set xy color
    void            SetLineColor (const PrinterColor& rLineColor = PrinterColor())
    { maLineColor = rLineColor; }
    void            SetFillColor (const PrinterColor& rFillColor = PrinterColor())
    { maFillColor = rFillColor; }

    // drawing primitives
    void            DrawPixel (const Point& rPoint, const PrinterColor& rPixelColor);
    void            DrawPixel (const Point& rPoint)
    { DrawPixel (rPoint, maLineColor); }
    void            DrawLine  (const Point& rFrom, const Point& rTo);
    void            DrawRect  (const tools::Rectangle& rRectangle);
    void            DrawPolyLine (sal_uInt32 nPoints, const Point* pPath );
    void            DrawPolygon  (sal_uInt32 nPoints, const Point* pPath);
    void            DrawPolyPolygon (sal_uInt32 nPoly,
                                     const sal_uInt32 *pPolygonSize,
                                     const Point** pPolygonList);
    void            DrawPolyLineBezier (sal_uInt32 nPoints,
                                     const Point* pPath,
                                     const PolyFlags* pFlgAry );
    void            DrawPolygonBezier  (sal_uInt32 nPoints,
                                     const Point* pPath,
                                     const PolyFlags* pFlgAry);
    void            DrawPolyPolygonBezier  (sal_uInt32 nPoly,
                                     const sal_uInt32* pPoints,
                                     const Point* const* pPtAry,
                                     const PolyFlags* const* pFlgAry);

    // eps
    bool        DrawEPS ( const tools::Rectangle& rBoundingBox, void* pPtr, sal_uInt32 nSize);

    // image drawing
    void            DrawBitmap (const tools::Rectangle& rDest, const tools::Rectangle& rSrc,
                                const PrinterBmp& rBitmap);

    // font and text handling
    void            SetFont (
                             sal_Int32 nFontID,
                             sal_Int32 nPointHeight,
                             sal_Int32 nPointWidth,
                             Degree10 nAngle,
                             bool bVertical,
                             bool bArtItalic,
                             bool bArtBold
                             );
    sal_Int32       GetFontID () const
    { return mnFontID; }
    bool            GetFontVertical() const
    { return mbTextVertical; }
    sal_Int32       GetFontHeight () const
    { return maVirtualStatus.mnTextHeight; }
    sal_Int32       GetFontWidth () const
    { return maVirtualStatus.mnTextWidth; }
    bool            GetArtificialItalic() const
    { return maVirtualStatus.mbArtItalic; }
    bool            GetArtificialBold() const
    { return maVirtualStatus.mbArtBold; }
    void            SetTextColor (PrinterColor const & rTextColor)
    { maTextColor = rTextColor; }

    void            DrawGlyph(const Point& rPoint,
                              const GlyphItem& rGlyph);

};

} /* namespace psp */

#endif // INCLUDED_VCL_INC_GENERIC_PRINTERGFX_HXX

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/inc/unx/printerjob.hxx b/vcl/inc/unx/printerjob.hxx
deleted file mode 100644
index 47d1c0a..0000000
--- a/vcl/inc/unx/printerjob.hxx
+++ /dev/null
@@ -1,129 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */

#ifndef INCLUDED_VCL_INC_GENERIC_PRINTERJOB_HXX
#define INCLUDED_VCL_INC_GENERIC_PRINTERJOB_HXX

#include <jobdata.hxx>
#include <osl/file.hxx>

#include <string_view>
#include <vector>

namespace psp {

class PrinterGfx;

class PrinterJob
{
private:
    OUString           maSpoolDirName;
    OUString           maFileName; // empty: spool to command, else spool to named file
    OUString           maJobTitle;
    int                     mnFileMode;

    std::unique_ptr<osl::File> mpJobHeader;
    std::unique_ptr<osl::File> mpJobTrailer;

    std::vector< std::unique_ptr<osl::File> > maPageVector;
    std::vector< std::unique_ptr<osl::File> > maHeaderVector;

    JobData                 m_aDocumentJobData;
    JobData                 m_aLastJobData;
    PrinterGfx*             m_pGraphics;

    sal_uInt32      mnResolution;

    sal_uInt32      mnWidthPt;
    sal_uInt32      mnHeightPt;
    sal_uInt32      mnMaxWidthPt;
    sal_uInt32      mnMaxHeightPt;

    int mnLandscapes;
    int mnPortraits;

    sal_uInt32      mnLMarginPt;
    sal_uInt32      mnRMarginPt;
    sal_uInt32      mnTMarginPt;
    sal_uInt32      mnBMarginPt;

    double          mfXScale;
    double          mfYScale;

    bool            m_bQuickJob;

private:
    std::unique_ptr<osl::File> CreateSpoolFile (std::u16string_view rName,
                                     std::u16string_view rExtension) const;
    void            InitPaperSize (const JobData& rJobSetup);

    bool            writeFeatureList( osl::File* pFile, const JobData&, bool bDocumentSetup ) const;
    bool            writeSetup( osl::File* pFile, const JobData& );
    bool            writePageSetup( osl::File* pFile, const JobData&, bool bWriteFeatures );
    static void     writeJobPatch( osl::File* File, const JobData& );
    static void     writeProlog (osl::File* pFile, const JobData& );

public:             // for usage in PrinterGfx
    sal_uInt32      GetResolution () const { return mnResolution; }
    void            GetScale (double &rXScale, double &rYScale) const;
    sal_uInt16      GetDepth () const;
    sal_uInt16      GetPostscriptLevel (const JobData *pJobData = nullptr) const;
    bool        IsColorPrinter () const;

    osl::File*      GetCurrentPageBody ();

    const OUString& GetPrinterName() const { return m_aLastJobData.m_aPrinterName; }

public:
    PrinterJob ();
    ~PrinterJob ();

    /*  rFileName: if length is greater than 0 save resulting PostScript
     *  to named file.
     *  nMode: only meaningful when saving to file: if nonzero, try
     *  to impose the mode on the resulting file's inode; for nonexistent
     *  files use open, for existent files try a chmod
     *  rJobName: text to appear in the %%Title comment
     *  rAppName: text to appear in the %%Creator comment
     *  rSetupData: JobData that apply to this job
     *  pGraphics: the graphics used to print this job;
     *             this graphics must live until EndJob() has returned
     *  bIsQuickJob: the job was started as "direct print" meaning
     *               the quick command for spooling should be used instead
     *               of the normal command
     */
    bool        StartJob (const OUString& rFileName,
                              int nMode,
                              const OUString& rJobName,
                              std::u16string_view rAppName,
                              const JobData& rSetupData,
                              PrinterGfx* pGraphics,
                              bool bIsQuickJob
                              );
    bool        EndJob ();

    void        StartPage (const JobData& rJobSetup);
    void        EndPage ();
};

}  // namespace psp

#endif // INCLUDED_VCL_INC_GENERIC_PRINTERJOB_HXX

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/unx/generic/print/bitmap_gfx.cxx b/vcl/unx/generic/print/bitmap_gfx.cxx
deleted file mode 100644
index 2d86497..0000000
--- a/vcl/unx/generic/print/bitmap_gfx.cxx
+++ /dev/null
@@ -1,674 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */

#include <array>
#include <memory>
#include "psputil.hxx"

#include <unx/printergfx.hxx>

namespace psp {

const sal_uInt32 nLineLength = 80;
const sal_uInt32 nBufferSize = 16384;

/*
 *
 * Bitmap compression / Hex encoding / Ascii85 Encoding
 *
 */

PrinterBmp::~PrinterBmp()
{
}

/* HexEncoder */

namespace {

class HexEncoder
{
private:

    osl::File*      mpFile;
    sal_uInt32      mnColumn;
    sal_uInt32      mnOffset;
    OStringBuffer   mpFileBuffer;

public:

    explicit        HexEncoder (osl::File* pFile);
                   ~HexEncoder ();
    void            WriteAscii (sal_uInt8 nByte);
    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);
    mnColumn += nOff;
    mnOffset += nOff;

    if (mnColumn >= nLineLength)
    {
        mnOffset += psp::appendStr ("\n", mpFileBuffer);
        mnColumn = 0;
    }
    if (mnOffset >= nBufferSize)
        FlushLine ();
}

void
HexEncoder::EncodeByte (sal_uInt8 nByte)
{
    WriteAscii (nByte);
}

void
HexEncoder::FlushLine ()
{
    if (mnOffset > 0)
    {
        WritePS (mpFile, mpFileBuffer.makeStringAndClear());
        mnOffset = 0;
    }
}

/* Ascii85 encoder, is abi compatible with HexEncoder but writes a ~> to
   indicate end of data EOD */

namespace {

class Ascii85Encoder
{
private:

    osl::File*      mpFile;
    sal_uInt32      mnByte;
    sal_uInt8       mpByteBuffer[4];

    sal_uInt32      mnColumn;
    sal_uInt32      mnOffset;
    OStringBuffer   mpFileBuffer;

    inline void     PutByte (sal_uInt8 nByte);
    inline void     PutEOD ();
    void            ConvertToAscii85 ();
    void            FlushLine ();

public:

    explicit        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 ()
{
    // Add (4 - mnByte) zero padding bytes:
    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.append('z');

        mnOffset += 1;
        mnColumn += 1;
    }
    else
    {
        /* real ascii85 encoding */

        // Of the up to 5 characters to be generated, do not generate the last (4 - mnByte) ones
        // that correspond to the (4 - mnByte) zero padding bytes added to the input:

        auto const pos = mpFileBuffer.getLength();
        if (mnByte == 4) {
            mpFileBuffer.insert(pos, char((nByteValue % 85) + 33));
        }
        nByteValue /= 85;
        if (mnByte >= 3) {
            mpFileBuffer.insert(pos, char((nByteValue % 85) + 33));
        }
        nByteValue /= 85;
        if (mnByte >= 2) {
            mpFileBuffer.insert(pos, char((nByteValue % 85) + 33));
        }
        nByteValue /= 85;
        mpFileBuffer.insert(pos, char((nByteValue % 85) + 33));
        nByteValue /= 85;
        mpFileBuffer.insert(pos, char((nByteValue % 85) + 33));

        mnColumn += (mnByte + 1);
        mnOffset += (mnByte + 1);

        /* insert a newline if necessary */
        if (mnColumn > nLineLength)
        {
            sal_uInt32 nEolOff = mnColumn - nLineLength;
            auto const posNl = pos + (mnByte + 1) - nEolOff;

            mpFileBuffer.insert(posNl, '\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);
        mnColumn = 0;
    }
    if (mnOffset >= nBufferSize)
        FlushLine ();
}

void
Ascii85Encoder::EncodeByte (sal_uInt8 nByte)
{
    WriteAscii (nByte);
}

void
Ascii85Encoder::FlushLine ()
{
    if (mnOffset > 0)
    {
        WritePS (mpFile, mpFileBuffer.makeStringAndClear());
        mnOffset = 0;
    }
}

Ascii85Encoder::~Ascii85Encoder ()
{
    if (mnByte > 0)
        ConvertToAscii85 ();
    if (mnOffset > 0)
        FlushLine ();
    PutEOD ();
}

/* LZW encoder */

namespace {

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
    };

    std::array<LZWCTreeNode, 4096>
                    maTable;    // LZW compression data
    LZWCTreeNode*   mpPrefix;   // the compression is as same as the TIFF compression
    static constexpr sal_uInt16 gnDataSize = 8;
    static constexpr sal_uInt16 gnClearCode = 1 << gnDataSize;
    static constexpr sal_uInt16 gnEOICode = gnClearCode + 1;
    sal_uInt16      mnTableSize;
    sal_uInt16      mnCodeSize;
    sal_uInt32      mnOffset;
    sal_uInt32      mdwShift;

    void            WriteBits (sal_uInt16 nCode, sal_uInt16 nCodeLen);

public:

    explicit LZWEncoder (osl::File* pOutputFile);
    virtual ~LZWEncoder () override;

    virtual void    EncodeByte (sal_uInt8 nByte) override;
};

}

LZWEncoder::LZWEncoder(osl::File* pOutputFile) :
        Ascii85Encoder (pOutputFile),
        maTable{{}},
        mpPrefix(nullptr),
        mnTableSize(gnEOICode + 1),
        mnCodeSize(gnDataSize + 1),
        mnOffset(32),       // free bits in dwShift
        mdwShift(0)
{
    for (sal_uInt32 i = 0; i < 4096; i++)
    {
        maTable[i].mpBrother    = nullptr;
        maTable[i].mpFirstChild = nullptr;
        maTable[i].mnCode       = i;
        maTable[i].mnValue      = static_cast<sal_uInt8>(maTable[i].mnCode);
    }

    WriteBits( gnClearCode, mnCodeSize );
}

LZWEncoder::~LZWEncoder()
{
    if (mpPrefix)
        WriteBits (mpPrefix->mnCode, mnCodeSize);

    WriteBits (gnEOICode, mnCodeSize);
}

void
LZWEncoder::WriteBits (sal_uInt16 nCode, sal_uInt16 nCodeLen)
{
    mdwShift |= (nCode << (mnOffset - nCodeLen));
    mnOffset -= nCodeLen;
    while (mnOffset < 24)
    {
        WriteAscii (static_cast<sal_uInt8>(mdwShift >> 24));
        mdwShift <<= 8;
        mnOffset += 8;
    }
    if (nCode == 257 && mnOffset != 32)
        WriteAscii (static_cast<sal_uInt8>(mdwShift >> 24));
}

void
LZWEncoder::EncodeByte (sal_uInt8 nByte )
{
    LZWCTreeNode*   p;
    sal_uInt16      i;
    sal_uInt8       nV;

    if (!mpPrefix)
    {
        mpPrefix = maTable.data() + nByte;
    }
    else
    {
        nV = nByte;
        for (p = mpPrefix->mpFirstChild; p != nullptr; p = p->mpBrother)
        {
            if (p->mnValue == nV)
                break;
        }

        if (p != nullptr)
        {
            mpPrefix = p;
        }
        else
        {
            WriteBits (mpPrefix->mnCode, mnCodeSize);

            if (mnTableSize == 409)
            {
                WriteBits (gnClearCode, mnCodeSize);

                for (i = 0; i < gnClearCode; i++)
                    maTable[i].mpFirstChild = nullptr;

                mnCodeSize = gnDataSize + 1;
                mnTableSize = gnEOICode + 1;
            }
            else
            {
                if(mnTableSize == static_cast<sal_uInt16>((1 << mnCodeSize) - 1))
                    mnCodeSize++;

                p = maTable.data() + (mnTableSize++);
                p->mpBrother = mpPrefix->mpFirstChild;
                mpPrefix->mpFirstChild = p;
                p->mnValue = nV;
                p->mpFirstChild = nullptr;
            }

            mpPrefix = maTable.data() + nV;
        }
    }
}

/*
 *
 * bitmap handling routines
 *
 */

void
PrinterGfx::DrawBitmap (const tools::Rectangle& rDest, const tools::Rectangle& rSrc,
                        const PrinterBmp& rBitmap)
{
    double fScaleX = static_cast<double>(rDest.GetWidth());
    double fScaleY = static_cast<double>(rDest.GetHeight());
    if(rSrc.GetWidth() > 0)
    {
        fScaleX = static_cast<double>(rDest.GetWidth()) / static_cast<double>(rSrc.GetWidth());
    }
    if(rSrc.GetHeight() > 0)
    {
        fScaleY = static_cast<double>(rDest.GetHeight()) / static_cast<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 ();
}

/*
 *
 * Implementation: PS Level 1
 *
 */

void
PrinterGfx::DrawPS1GrayImage (const PrinterBmp& rBitmap, const tools::Rectangle& rArea)
{
    sal_uInt32 nWidth  = rArea.GetWidth();
    sal_uInt32 nHeight = rArea.GetHeight();

    OStringBuffer pGrayImage;

    // image header
    psp::getValueOf (nWidth,                           pGrayImage);
    psp::appendStr  (" ",                              pGrayImage);
    psp::getValueOf (nHeight,                          pGrayImage);
    psp::appendStr  (" 8 ",                            pGrayImage);
    psp::appendStr  ("[ 1 0 0 1 0 ",                   pGrayImage);
    psp::getValueOf (nHeight,                          pGrayImage);
    psp::appendStr  ("]",                              pGrayImage);
    psp::appendStr  (" {currentfile ",                 pGrayImage);
    psp::getValueOf (nWidth,                           pGrayImage);
    psp::appendStr  (" string readhexstring pop}\n",   pGrayImage);
    psp::appendStr  ("image\n",                        pGrayImage);

    WritePS (mpPageBody, pGrayImage.makeStringAndClear());

    // image body
    HexEncoder aEncoder(mpPageBody);

    for (tools::Long nRow = rArea.Top(); nRow <= rArea.Bottom(); nRow++)
    {
        for (tools::Long nColumn = rArea.Left(); nColumn <= rArea.Right(); nColumn++)
        {
            unsigned char nByte = rBitmap.GetPixelGray (nRow, nColumn);
            aEncoder.EncodeByte (nByte);
        }
    }

    WritePS (mpPageBody, "\n");
}

/*
 *
 * Implementation: PS Level 2
 *
 */

void
PrinterGfx::writePS2ImageHeader (const tools::Rectangle& rArea, psp::ImageType nType)
{
    OStringBuffer pImage;

    sal_Int32 nDictType = 0;
    switch (nType)
    {
        case psp::ImageType::TrueColorImage:  nDictType = 0; break;
        case psp::ImageType::PaletteImage:    nDictType = 1; break;
        case psp::ImageType::GrayScaleImage:  nDictType = 2; break;
        case psp::ImageType::MonochromeImage: nDictType = 3; break;
        default: break;
    }

    psp::getValueOf (rArea.GetWidth(),  pImage);
    psp::appendStr  (" ",               pImage);
    psp::getValueOf (rArea.GetHeight(), pImage);
    psp::appendStr  (" ",               pImage);
    psp::getValueOf (nDictType,         pImage);
    psp::appendStr  (" ",               pImage);
    psp::getValueOf (sal_Int32(1),      pImage); // nCompressType
    psp::appendStr  (" psp_imagedict image\n", pImage);

    WritePS (mpPageBody, pImage.makeStringAndClear());
}

void
PrinterGfx::writePS2Colorspace(const PrinterBmp& rBitmap, psp::ImageType nType)
{
    switch (nType)
    {
        case psp::ImageType::GrayScaleImage:

            WritePS (mpPageBody, "/DeviceGray setcolorspace\n");
            break;

        case psp::ImageType::TrueColorImage:

            WritePS (mpPageBody, "/DeviceRGB setcolorspace\n");
            break;

        case psp::ImageType::MonochromeImage:
        case psp::ImageType::PaletteImage:
        {

            OStringBuffer pImage;

            const sal_uInt32 nSize = rBitmap.GetPaletteEntryCount();

            psp::appendStr ("[/Indexed /DeviceRGB ", pImage);
            psp::getValueOf (nSize - 1, pImage);
            psp::appendStr ("\npsp_lzwstring\n", pImage);
            WritePS (mpPageBody, pImage.makeStringAndClear());

            LZWEncoder aEncoder(mpPageBody);
            for (sal_uInt32 i = 0; i < nSize; i++)
            {
                PrinterColor aColor = rBitmap.GetPaletteColor(i);

                aEncoder.EncodeByte (aColor.GetRed());
                aEncoder.EncodeByte (aColor.GetGreen());
                aEncoder.EncodeByte (aColor.GetBlue());
            }

            WritePS (mpPageBody, "pop ] setcolorspace\n");
        }
        break;
        default: break;
    }
}

void
PrinterGfx::DrawPS2GrayImage (const PrinterBmp& rBitmap, const tools::Rectangle& rArea)
{
    writePS2Colorspace(rBitmap, psp::ImageType::GrayScaleImage);
    writePS2ImageHeader(rArea, psp::ImageType::GrayScaleImage);

    LZWEncoder aEncoder(mpPageBody);

    for (tools::Long nRow = rArea.Top(); nRow <= rArea.Bottom(); nRow++)
    {
        for (tools::Long nColumn = rArea.Left(); nColumn <= rArea.Right(); nColumn++)
        {
            unsigned char nByte = rBitmap.GetPixelGray (nRow, nColumn);
            aEncoder.EncodeByte (nByte);
        }
    }
}

void
PrinterGfx::DrawPS2MonoImage (const PrinterBmp& rBitmap, const tools::Rectangle& rArea)
{
    writePS2Colorspace(rBitmap, psp::ImageType::MonochromeImage);
    writePS2ImageHeader(rArea, psp::ImageType::MonochromeImage);

    LZWEncoder aEncoder(mpPageBody);

    for (tools::Long nRow = rArea.Top(); nRow <= rArea.Bottom(); nRow++)
    {
        tools::Long      nBitPos = 0;
        unsigned char nByte   = 0;

        for (tools::Long nColumn = rArea.Left(); nColumn <= rArea.Right(); nColumn++)
        {
            unsigned char nBit   = rBitmap.GetPixelIdx (nRow, nColumn);
            nByte |= nBit << (7 - nBitPos);

            if (++nBitPos == 8)
            {
                aEncoder.EncodeByte (nByte);
                nBitPos = 0;
                nByte   = 0;
            }
        }
        // keep the row byte aligned
        if (nBitPos != 0)
            aEncoder.EncodeByte (nByte);
    }
}

void
PrinterGfx::DrawPS2PaletteImage (const PrinterBmp& rBitmap, const tools::Rectangle& rArea)
{
    writePS2Colorspace(rBitmap, psp::ImageType::PaletteImage);
    writePS2ImageHeader(rArea, psp::ImageType::PaletteImage);

    LZWEncoder aEncoder(mpPageBody);

    for (tools::Long nRow = rArea.Top(); nRow <= rArea.Bottom(); nRow++)
    {
        for (tools::Long nColumn = rArea.Left(); nColumn <= rArea.Right(); nColumn++)
        {
            unsigned char nByte = rBitmap.GetPixelIdx (nRow, nColumn);
            aEncoder.EncodeByte (nByte);
        }
    }
}

void
PrinterGfx::DrawPS2TrueColorImage (const PrinterBmp& rBitmap, const tools::Rectangle& rArea)
{
    writePS2Colorspace(rBitmap, psp::ImageType::TrueColorImage);
    writePS2ImageHeader(rArea, psp::ImageType::TrueColorImage);

    LZWEncoder aEncoder(mpPageBody);

    for (tools::Long nRow = rArea.Top(); nRow <= rArea.Bottom(); nRow++)
    {
        for (tools::Long nColumn = rArea.Left(); nColumn <= rArea.Right(); nColumn++)
        {
            PrinterColor aColor = rBitmap.GetPixelRGB (nRow, nColumn);
            aEncoder.EncodeByte (aColor.GetRed());
            aEncoder.EncodeByte (aColor.GetGreen());
            aEncoder.EncodeByte (aColor.GetBlue());
        }
    }
}

} /* namespace psp */

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/unx/generic/print/common_gfx.cxx b/vcl/unx/generic/print/common_gfx.cxx
deleted file mode 100644
index aba50ec..0000000
--- a/vcl/unx/generic/print/common_gfx.cxx
+++ /dev/null
@@ -1,1152 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */

#include <sal/config.h>

#include "psputil.hxx"
#include "glyphset.hxx"

#include <unx/printergfx.hxx>
#include <unx/printerjob.hxx>
#include <unx/fontmanager.hxx>
#include <strhelper.hxx>
#include <printerinfomanager.hxx>

#include <tools/color.hxx>
#include <tools/poly.hxx>
#include <tools/stream.hxx>
#include <o3tl/string_view.hxx>

using namespace psp ;

const sal_Int32 nMaxTextColumn = 80;

GraphicsStatus::GraphicsStatus() :
        maEncoding(RTL_TEXTENCODING_DONTKNOW),
        mbArtItalic( false ),
        mbArtBold( false ),
        mnTextHeight( 0 ),
        mnTextWidth( 0 ),
        mfLineWidth( -1 )
{
}

/*
 * non graphics routines
 */

void
PrinterGfx::Init (PrinterJob &rPrinterJob)
{
    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() ) );
    mbUploadPS42Fonts = rInfo.m_pParser && rInfo.m_pParser->isType42Capable();
}

void
PrinterGfx::Init (const JobData& rData)
{
    mpPageBody      = nullptr;
    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 ) : ( rData.m_pParser == nullptr || rData.m_pParser->isColorDevice() );
    int nRes = rData.m_aContext.getRenderResolution();
    mnDpi           = nRes;
    mfScaleX        = 72.0 / static_cast<double>(mnDpi);
    mfScaleY        = 72.0 / static_cast<double>(mnDpi);
    const PrinterInfo& rInfo( PrinterInfoManager::get().getPrinterInfo( rData.m_aPrinterName ) );
    mbUploadPS42Fonts = rInfo.m_pParser && rInfo.m_pParser->isType42Capable();
}


PrinterGfx::PrinterGfx()
    : mfScaleX(0.0)
    , mfScaleY(0.0)
    , mnDpi(0)
    , mnDepth(0)
    , mnPSLevel(0)
    , mbColor(false)
    , mbUploadPS42Fonts(false)
    , mpPageBody(nullptr)
    , mnFontID(0)
    , mnTextAngle(0)
    , mbTextVertical(false)
    , mrFontMgr(PrintFontManager::get())
    , maFillColor(0xff,0,0)
    , maTextColor(0,0,0)
    , maLineColor(0, 0xff, 0)
{
    maVirtualStatus.mfLineWidth = 1.0;
    maVirtualStatus.mnTextHeight = 12;
    maVirtualStatus.mnTextWidth = 0;

    maGraphicsStack.emplace_back( );
}

PrinterGfx::~PrinterGfx()
{
}

void
PrinterGfx::Clear()
{
    mpPageBody                      = nullptr;
    mnFontID                        = 0;
    maVirtualStatus                 = GraphicsStatus();
    maVirtualStatus.mnTextHeight    = 12;
    maVirtualStatus.mnTextWidth     = 0;
    maVirtualStatus.mfLineWidth     = 1.0;
    mbTextVertical                  = false;
    maLineColor                     = PrinterColor();
    maFillColor                     = PrinterColor();
    maTextColor                     = PrinterColor();
    mnDpi                           = 300;
    mnDepth                         = 24;
    mnPSLevel                       = 2;
    mbColor                         = true;
    mnTextAngle                     = 0_deg10;

    maClipRegion.clear();
    maGraphicsStack.clear();
    maGraphicsStack.emplace_back( );
}

/*
 * clip region handling
 */

void
PrinterGfx::ResetClipRegion()
{
    maClipRegion.clear();
    PSGRestore ();
    PSGSave (); // get "clean" clippath
}

void
PrinterGfx::BeginSetClipRegion()
{
    maClipRegion.clear();
}

void
PrinterGfx::UnionClipRegion (sal_Int32 nX,sal_Int32 nY,sal_Int32 nDX,sal_Int32 nDY)
{
    if( nDX && nDY )
        maClipRegion.emplace_back(Point(nX,nY ), Size(nDX,nDY));
}

bool
PrinterGfx::JoinVerticalClipRectangles( std::list< tools::Rectangle >::iterator& it,
                                        Point& rOldPoint, sal_Int32& rColumn )
{
    bool bSuccess = false;

    std::list< tools::Rectangle >::iterator tempit, nextit;
    nextit = it;
    ++nextit;
    std::list< Point > leftside, rightside;

    tools::Rectangle aLastRect( *it );
    leftside.emplace_back( it->Left(), it->Top() );
    rightside.emplace_back( 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                           ||
                    std::abs( aLastRect.Left() - nextit->Left() ) > 2        ||
                    std::abs( aLastRect.Right() - nextit->Right() ) > 2
                    )
                {
                    leftside.emplace_back( aLastRect.Left(), aLastRect.Bottom()+1 );
                    rightside.emplace_back( 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.emplace_back( aLastRect.Left(), aLastRect.Bottom()+1 );
        rightside.emplace_back( aLastRect.Right()+1, aLastRect.Bottom()+1 );

        // cool, we can concatenate rectangles
        const int nDX = -65536, nDY = 65536;
        int nNewDX = 0, nNewDY = 0;

        Point aLastPoint = leftside.front();
        PSBinMoveTo (aLastPoint, rOldPoint, rColumn);
        leftside.pop_front();
        while( !leftside.empty() )
        {
            Point aPoint (leftside.front());
            leftside.pop_front();
            // may have been the last one
            if( !leftside.empty() )
            {
                nNewDX = aPoint.X() - aLastPoint.X();
                nNewDY = aPoint.Y() - aLastPoint.Y();
                if( nNewDX != 0 &&
                    static_cast<double>(nNewDY)/static_cast<double>(nNewDX) == double(nDY)/double(nDX) )
                    continue;
            }
            PSBinLineTo (aPoint, rOldPoint, rColumn);
            aLastPoint = aPoint;
        }

        aLastPoint = rightside.back();
        PSBinLineTo (aLastPoint, rOldPoint, rColumn);
        rightside.pop_back();
        while( !rightside.empty() )
        {
            Point aPoint (rightside.back());
            rightside.pop_back();
            if( !rightside.empty() )
            {
                nNewDX = aPoint.X() - aLastPoint.X();
                nNewDY = aPoint.Y() - aLastPoint.Y();
                if( nNewDX != 0 &&
                    static_cast<double>(nNewDY)/static_cast<double>(nNewDX) == double(nDY)/double(nDX) )
                    continue;
            }
            PSBinLineTo (aPoint, rOldPoint, rColumn);
        }

        tempit = it;
        ++tempit;
        maClipRegion.erase( it );
        it = tempit;
        bSuccess = true;
    }
    return bSuccess;
}

void
PrinterGfx::EndSetClipRegion()
{
    PSGRestore ();
    PSGSave (); // get "clean" clippath

    PSBinStartPath ();
    Point aOldPoint (0, 0);
    sal_Int32 nColumn = 0;

    std::list< tools::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 (Point( it->Left()-1, it->Top()-1),      aOldPoint, nColumn );
            PSBinLineTo (Point( it->Left()-1, it->Bottom()+1 ),  aOldPoint, nColumn );
            PSBinLineTo (Point( it->Right()+1, it->Bottom()+1 ), aOldPoint, nColumn );
            PSBinLineTo (Point( it->Right()+1, it->Top()-1 ),    aOldPoint, nColumn );
            ++it;
        }
    }

    PSBinEndPath ();

    WritePS (mpPageBody, "closepath clip newpath\n");
    maClipRegion.clear();
}

/*
 * draw graphic primitives
 */

void
PrinterGfx::DrawRect (const tools::Rectangle& rRectangle )
{
    OStringBuffer pRect;

    psp::getValueOf (rRectangle.Left(),      pRect);
    psp::appendStr (" ",                     pRect);
    psp::getValueOf (rRectangle.Top(),       pRect);
    psp::appendStr (" ",                     pRect);
    psp::getValueOf (rRectangle.GetWidth(),  pRect);
    psp::appendStr (" ",                     pRect);
    psp::getValueOf (rRectangle.GetHeight(), pRect);
    psp::appendStr (" ",                     pRect);
    auto const rect = pRect.makeStringAndClear();

    if( maFillColor.Is() )
    {
        PSSetColor (maFillColor);
        PSSetColor ();
        WritePS (mpPageBody, rect);
        WritePS (mpPageBody, "rectfill\n");
    }
    if( maLineColor.Is() )
    {
        PSSetColor (maLineColor);
        PSSetColor ();
        PSSetLineWidth ();
        WritePS (mpPageBody, rect);
        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 <= 0 || (pPath == nullptr) || !(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 PolyFlags* pFlgAry)
{
    const sal_uInt32 nBezString= 1024;
    char pString[nBezString];

    if ( nPoints <= 1 || !maLineColor.Is() || !pPath )
        return;

    PSSetColor (maLineColor);
    PSSetColor ();
    PSSetLineWidth ();

    snprintf(pString, nBezString, "%" SAL_PRIdINT64 " %" SAL_PRIdINT64 " moveto\n", sal_Int64(pPath[0].X()), sal_Int64(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] != PolyFlags::Control) //If the next point is a PolyFlags::Normal, we're drawing a line
        {
            snprintf(pString, nBezString, "%" SAL_PRIdINT64 " %" SAL_PRIdINT64 " lineto\n", sal_Int64(pPath[i].X()), sal_Int64(pPath[i].Y()));
            i++;
        }
        else //Otherwise we're drawing a spline
        {
            if (i+2 >= nPoints)
                return; //Error: wrong sequence of control/normal points somehow
            if ((pFlgAry[i] == PolyFlags::Control) && (pFlgAry[i+1] == PolyFlags::Control) &&
                (pFlgAry[i+2] != PolyFlags::Control))
            {
                snprintf(pString, nBezString, "%" SAL_PRIdINT64 " %" SAL_PRIdINT64 " %" SAL_PRIdINT64 " %" SAL_PRIdINT64 " %" SAL_PRIdINT64 " %" SAL_PRIdINT64 " curveto\n",
                         sal_Int64(pPath[i].X()), sal_Int64(pPath[i].Y()),
                         sal_Int64(pPath[i+1].X()), sal_Int64(pPath[i+1].Y()),
                         sal_Int64(pPath[i+2].X()), sal_Int64(pPath[i+2].Y()));
            }
            else
            {
                OSL_FAIL( "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 PolyFlags* pFlgAry)
{
    const sal_uInt32 nBezString = 1024;
    char pString[nBezString];
    // premature end of operation
    if (nPoints <= 0 || (pPath == nullptr) || !(maFillColor.Is() || maLineColor.Is()))
        return;

    snprintf(pString, nBezString, "%" SAL_PRIdINT64 " %" SAL_PRIdINT64 " moveto\n", sal_Int64(pPath[0].X()), sal_Int64(pPath[0].Y()));
    WritePS(mpPageBody, pString); //Move to the starting point for the PolyPolygon
    for (unsigned int i=1; i < nPoints;)
    {
        if (pFlgAry[i] != PolyFlags::Control)
        {
            snprintf(pString, nBezString, "%" SAL_PRIdINT64 " %" SAL_PRIdINT64 " lineto\n",
                sal_Int64(pPath[i].X()), sal_Int64(pPath[i].Y()));
            WritePS(mpPageBody, pString);
            i++;
        }
        else
        {
            if (i+2 >= nPoints)
                return; //Error: wrong sequence of control/normal points somehow
            if ((pFlgAry[i] == PolyFlags::Control) && (pFlgAry[i+1] == PolyFlags::Control) &&
                    (pFlgAry[i+2] != PolyFlags::Control))
            {
                snprintf(pString, nBezString, "%" SAL_PRIdINT64 " %" SAL_PRIdINT64 " %" SAL_PRIdINT64 " %" SAL_PRIdINT64 " %" SAL_PRIdINT64 " %" SAL_PRIdINT64 " curveto\n",
                        sal_Int64(pPath[i].X()), sal_Int64(pPath[i].Y()),
                        sal_Int64(pPath[i+1].X()), sal_Int64(pPath[i+1].Y()),
                        sal_Int64(pPath[i+2].X()), sal_Int64(pPath[i+2].Y()));
                WritePS(mpPageBody, pString);
            }
            else
            {
                OSL_FAIL( "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 PolyFlags* const* pFlgAry)
{
    const sal_uInt32 nBezString = 1024;
    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];
        // sanity check
        if( nPoints == 0 || pPtAry[i] == nullptr )
            continue;

        snprintf(pString, nBezString, "%" SAL_PRIdINT64 " %" SAL_PRIdINT64 " moveto\n",
            sal_Int64(pPtAry[i][0].X()), sal_Int64(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] != PolyFlags::Control)
            {
                snprintf(pString, nBezString, "%" SAL_PRIdINT64 " %" SAL_PRIdINT64 " lineto\n",
                    sal_Int64(pPtAry[i][j].X()), sal_Int64(pPtAry[i][j].Y()));
                WritePS(mpPageBody, pString);
                j++;
            }
            else
            {
                if (j+2 >= nPoints)
                    break; //Error: wrong sequence of control/normal points somehow
                if ((pFlgAry[i][j] == PolyFlags::Control) && (pFlgAry[i][j+1] == PolyFlags::Control) && (pFlgAry[i][j+2] != PolyFlags::Control))
                {
                    snprintf(pString, nBezString, "%" SAL_PRIdINT64 " %" SAL_PRIdINT64 " %" SAL_PRIdINT64 " %" SAL_PRIdINT64 " %" SAL_PRIdINT64 " %" SAL_PRIdINT64 " curveto\n",
                            sal_Int64(pPtAry[i][j].X()), sal_Int64(pPtAry[i][j].Y()),
                            sal_Int64(pPtAry[i][j+1].X()), sal_Int64(pPtAry[i][j+1].Y()),
                            sal_Int64(pPtAry[i][j+2].X()), sal_Int64(pPtAry[i][j+2].Y()));
                    WritePS(mpPageBody, pString);
                }
                else
                {
                    OSL_FAIL( "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.empty() )
        aNewState = maGraphicsStack.front();
    maGraphicsStack.push_front( aNewState );
}

void
PrinterGfx::PSGRestore ()
{
    WritePS (mpPageBody, "grestore\n" );
    if( maGraphicsStack.empty() )
        WritePS (mpPageBody, "Error: too many grestores\n" );
    else
        maGraphicsStack.pop_front();
}

void
PrinterGfx::PSSetLineWidth ()
{
    if( currentState().mfLineWidth != maVirtualStatus.mfLineWidth )
    {
        OStringBuffer pBuffer;

        currentState().mfLineWidth = maVirtualStatus.mfLineWidth;
        psp::getValueOfDouble (pBuffer, maVirtualStatus.mfLineWidth, 5);
        psp::appendStr (" setlinewidth\n", pBuffer);
        WritePS (mpPageBody, pBuffer.makeStringAndClear());
    }
}

void
PrinterGfx::PSSetColor ()
{
    PrinterColor& rColor( maVirtualStatus.maColor );

    if( currentState().maColor == rColor )
        return;

    currentState().maColor = rColor;

    OStringBuffer pBuffer;

    if( mbColor )
    {
        psp::getValueOfDouble (pBuffer,
                                        static_cast<double>(rColor.GetRed()) / 255.0, 5);
        psp::appendStr (" ", pBuffer);
        psp::getValueOfDouble (pBuffer,
                                        static_cast<double>(rColor.GetGreen()) / 255.0, 5);
        psp::appendStr (" ", pBuffer);
        psp::getValueOfDouble (pBuffer,
                                        static_cast<double>(rColor.GetBlue()) / 255.0, 5);
        psp::appendStr (" setrgbcolor\n", pBuffer );
    }
    else
    {
        Color aColor( rColor.GetRed(), rColor.GetGreen(), rColor.GetBlue() );
        sal_uInt8 nCol = aColor.GetLuminance();
        psp::getValueOfDouble( pBuffer, static_cast<double>(nCol) / 255.0, 5 );
        psp::appendStr( " setgray\n", pBuffer );
    }

    WritePS (mpPageBody, pBuffer.makeStringAndClear());
}

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)
        )
        return;

    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;

    OStringBuffer pSetFont;

    // 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)
       )
    {
        OString aReencodedFont =
                    psp::GlyphSet::GetReencodedFontName (rCurrent.maEncoding,
                                                            rCurrent.maFont);

        psp::appendStr  ("(",          pSetFont);
        psp::appendStr  (aReencodedFont.getStr(),
                                                pSetFont);
        psp::appendStr  (") cvn findfont ",
                                                pSetFont);
    }
    else
    // tt based fonts mustn't reencode, the encoding is implied by the fontname
    // same for symbol type1 fonts, don't try to touch them
    {
        psp::appendStr  ("(",          pSetFont);
        psp::appendStr  (rCurrent.maFont.getStr(),
                                                pSetFont);
        psp::appendStr  (") cvn findfont ",
                                                pSetFont);
    }

    if( ! rCurrent.mbArtItalic )
    {
        psp::getValueOf (nTextWidth,   pSetFont);
        psp::appendStr  (" ",          pSetFont);
        psp::getValueOf (-nTextHeight, pSetFont);
        psp::appendStr  (" matrix scale makefont setfont\n", pSetFont);
    }
    else // skew 15 degrees to right
    {
        psp::appendStr  ( " [",        pSetFont);
        psp::getValueOf (nTextWidth,   pSetFont);
        psp::appendStr  (" 0 ",        pSetFont);
        psp::getValueOfDouble (pSetFont, 0.27*static_cast<double>(nTextWidth), 3 );
        psp::appendStr  ( " ",         pSetFont);
        psp::getValueOf (-nTextHeight, pSetFont);

        psp::appendStr  (" 0 0] makefont setfont\n", pSetFont);
    }

    WritePS (mpPageBody, pSetFont.makeStringAndClear());

}

void
PrinterGfx::PSRotate (Degree10 nAngle)
{
    sal_Int32 nPostScriptAngle = -nAngle.get();
    while( nPostScriptAngle < 0 )
        nPostScriptAngle += 3600;

    if (nPostScriptAngle == 0)
        return;

    sal_Int32 nFullAngle  = nPostScriptAngle / 10;
    sal_Int32 nTenthAngle = nPostScriptAngle % 10;

    OStringBuffer pRotate;

    psp::getValueOf (nFullAngle,  pRotate);
    psp::appendStr (".",          pRotate);
    psp::getValueOf (nTenthAngle, pRotate);
    psp::appendStr (" rotate\n",  pRotate);

    WritePS (mpPageBody, pRotate.makeStringAndClear());
}

void
PrinterGfx::PSPointOp (const Point& rPoint, const char* pOperator)
{
    OStringBuffer pPSCommand;

    psp::getValueOf (rPoint.X(), pPSCommand);
    psp::appendStr  (" ",        pPSCommand);
    psp::getValueOf (rPoint.Y(), pPSCommand);
    psp::appendStr  (" ",        pPSCommand);
    psp::appendStr  (pOperator,  pPSCommand);
    psp::appendStr  ("\n",       pPSCommand);

    WritePS (mpPageBody, pPSCommand.makeStringAndClear());
}

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");
}

/* 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)
{
    OStringBuffer pPath;
    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);
    sal_Int32 nYPrec = getAlignedHexValueOf (rCurrent.Y() - rOld.Y(), pPath);

    // build the command, it is a char with bit representation 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
    char cCmd = (eType == lineto ? char(0x00) : char(0x10));
    switch (nYPrec)
    {
        case 2: break;
        case 4: cCmd |= 0x01;   break;
        case 6: cCmd |= 0x02;   break;
        case 8: cCmd |= 0x03;   break;
        default:    OSL_FAIL("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:    OSL_FAIL("invalid y precision in binary path");
    }
    cCmd += 'A';
    pPath.insert(0, cCmd);
    auto const path = pPath.makeStringAndClear();

    // 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, path.copy(0, nSegment));
        WritePS (mpPageBody, "\n", 1);
        WritePS (mpPageBody, path.copy(nSegment));

        nColumn  = nChar - nSegment;
    }
    else
    {
        WritePS (mpPageBody, path);

        nColumn += nChar;
    }

    rOld = rCurrent;
}

void
PrinterGfx::PSScale (double fScaleX, double fScaleY)
{
    OStringBuffer pScale;

    psp::getValueOfDouble (pScale, fScaleX, 5);
    psp::appendStr        (" ", pScale);
    psp::getValueOfDouble (pScale, fScaleY, 5);
    psp::appendStr        (" scale\n", pScale);

    WritePS (mpPageBody, pScale.makeStringAndClear());
}

/* psshowtext helper routines: draw an hex string for show/xshow */
void
PrinterGfx::PSHexString (const unsigned char* pString, sal_Int16 nLen)
{
    OStringBuffer pHexString;
    sal_Int32 nChar = psp::appendStr ("<", pHexString);
    for (int i = 0; i < nLen; i++)
    {
        if (nChar >= (nMaxTextColumn - 1))
        {
            psp::appendStr ("\n", pHexString);
            WritePS (mpPageBody, pHexString.makeStringAndClear());
            nChar = 0;
        }
        nChar += psp::getHexValueOf (static_cast<sal_Int32>(pString[i]), pHexString);
    }

    psp::appendStr (">\n", pHexString);
    WritePS (mpPageBody, pHexString.makeStringAndClear());
}

void
PrinterGfx::PSShowGlyph (const unsigned char nGlyphId)
{
    PSSetColor (maTextColor);
    PSSetColor ();
    PSSetFont  ();
    // rotate the user coordinate system
    if (mnTextAngle)
    {
        PSGSave ();
        PSRotate (mnTextAngle);
    }

    char pBuffer[256];
    if( maVirtualStatus.mbArtBold )
    {
        sal_Int32 nLW = maVirtualStatus.mnTextWidth;
        if( nLW == 0 )
            nLW = maVirtualStatus.mnTextHeight;
        else
            nLW = std::min(nLW, maVirtualStatus.mnTextHeight);
        psp::getValueOfDouble( pBuffer, static_cast<double>(nLW) / 30.0 );
    }

    // dispatch to the drawing method
    PSHexString (&nGlyphId, 1);

    if( maVirtualStatus.mbArtBold )
    {
        WritePS( mpPageBody, pBuffer );
        WritePS( mpPageBody, " bshow\n" );
    }
    else
        WritePS (mpPageBody, "show\n");

    // restore the user coordinate system
    if (mnTextAngle)
        PSGRestore ();
}

bool
PrinterGfx::DrawEPS( const tools::Rectangle& rBoundingBox, void* pPtr, sal_uInt32 nSize )
{
    if( nSize == 0 )
        return true;
    if( ! mpPageBody )
        return false;

    bool bSuccess = false;

    // first search the BoundingBox of the EPS data
    SvMemoryStream aStream( pPtr, nSize, StreamMode::READ );
    aStream.Seek( STREAM_SEEK_TO_BEGIN );
    OString aLine;

    OString aDocTitle;
    double fLeft = 0, fRight = 0, fTop = 0, fBottom = 0;
    bool bEndComments = false;
    while( ! aStream.eof()
           && ( ( fLeft == 0 && fRight == 0 && fTop == 0 && fBottom == 0 ) ||
                ( aDocTitle.isEmpty() && !bEndComments ) )
           )
    {
        aStream.ReadLine( aLine );
        if( aLine.getLength() > 1 && aLine[0] == '%' )
        {
            char cChar = aLine[1];
            if( cChar == '%' )
            {
                if( aLine.matchIgnoreAsciiCase( "%%BoundingBox:" ) )
                {
                    aLine = WhitespaceToSpace( o3tl::getToken(aLine, 1, ':') );
                    if( !aLine.isEmpty() && aLine.indexOf( "atend" ) == -1 )
                    {
                        fLeft   = StringToDouble( GetCommandLineToken( 0, aLine ) );
                        fBottom = StringToDouble( GetCommandLineToken( 1, aLine ) );
                        fRight  = StringToDouble( GetCommandLineToken( 2, aLine ) );
                        fTop    = StringToDouble( GetCommandLineToken( 3, aLine ) );
                    }
                }
                else if( aLine.matchIgnoreAsciiCase( "%%Title:" ) )
                    aDocTitle = WhitespaceToSpace( aLine.subView( 8 ) );
                else if( aLine.matchIgnoreAsciiCase( "%%EndComments" ) )
                    bEndComments = true;
            }
            else if( cChar == ' ' || cChar == '\t' || cChar == '\r' || cChar == '\n' )
                bEndComments = true;
        }
        else
            bEndComments = true;
    }

    static sal_uInt16 nEps = 0;
    if( aDocTitle.isEmpty() )
        aDocTitle = OString::number(nEps++);

    if( fLeft != fRight && fTop != fBottom )
    {
        double fScaleX = static_cast<double>(rBoundingBox.GetWidth())/(fRight-fLeft);
        double fScaleY = -static_cast<double>(rBoundingBox.GetHeight())/(fTop-fBottom);
        Point aTranslatePoint( static_cast<int>(rBoundingBox.Left()-fLeft*fScaleX),
                               static_cast<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();
        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( static_cast<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;
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/unx/generic/print/glyphset.cxx b/vcl/unx/generic/print/glyphset.cxx
deleted file mode 100644
index 283558a..0000000
--- a/vcl/unx/generic/print/glyphset.cxx
+++ /dev/null
@@ -1,297 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */

#include "glyphset.hxx"

#include <sft.hxx>

#include <unx/printergfx.hxx>
#include <fontsubset.hxx>
#include <unx/fontmanager.hxx>

#include <tools/gen.hxx>

#include <osl/thread.h>

#include <rtl/ustring.hxx>
#include <rtl/strbuf.hxx>

#include <unotools/tempfile.hxx>

#include <algorithm>

using namespace vcl;
using namespace psp;

GlyphSet::GlyphSet (sal_Int32 nFontID, bool bVertical)
        : mnFontID (nFontID),
          mbVertical (bVertical)
{
    PrintFontManager &rMgr = PrintFontManager::get();
    maBaseName          = OUStringToOString (rMgr.getPSName(mnFontID),
                                           RTL_TEXTENCODING_ASCII_US);
}

void
GlyphSet::GetGlyphID (
                      sal_GlyphId nGlyph,
                      unsigned char* nOutGlyphID,
                      sal_Int32* nOutGlyphSetID
                     )
{
    if (!LookupGlyphID(nGlyph, nOutGlyphID, nOutGlyphSetID))
        AddGlyphID(nGlyph, nOutGlyphID, nOutGlyphSetID);
}

bool
GlyphSet::LookupGlyphID (
                        sal_GlyphId nGlyph,
                        unsigned char* nOutGlyphID,
                        sal_Int32* nOutGlyphSetID
                        )
{
    sal_Int32             nGlyphSetID = 1;

    // loop through all the font subsets
    for (auto const& glyph : maGlyphList)
    {
        // check every subset if it contains the queried unicode char
        glyph_map_t::const_iterator aGlyph = glyph.find (nGlyph);
        if (aGlyph != glyph.end())
        {
            // success: found the glyph id, return the mapped glyphid and the glyphsetid
            *nOutGlyphSetID = nGlyphSetID;
            *nOutGlyphID    = aGlyph->second;
            return true;
        }
        ++nGlyphSetID;
    }

    *nOutGlyphSetID = -1;
    *nOutGlyphID    =  0;
    return false;
}

void
GlyphSet::AddNotdef (glyph_map_t &rGlyphMap)
{
    if (rGlyphMap.empty())
        rGlyphMap[0] = 0;
}

void
GlyphSet::AddGlyphID (
                     sal_GlyphId nGlyph,
                     unsigned char* nOutGlyphID,
                     sal_Int32* nOutGlyphSetID
                     )
{
    // create an empty glyphmap that is reserved for 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 (maGlyphList.back().size() == 255)
    {
        glyph_map_t aMap;
        maGlyphList.push_back (aMap);
    }

    glyph_map_t& aGlyphSet = maGlyphList.back();
    AddNotdef (aGlyphSet);

    int nSize         = aGlyphSet.size();

    aGlyphSet [nGlyph] = nSize;
    *nOutGlyphSetID   = maGlyphList.size();
    *nOutGlyphID      = aGlyphSet [nGlyph];
}

OString
GlyphSet::GetGlyphSetName (sal_Int32 nGlyphSetID)
{
    OStringBuffer aSetName( maBaseName.getLength() + 32 );
    aSetName.append( maBaseName
        + "FID" +
        OString::number( mnFontID ) );
    aSetName.append( mbVertical ? "VGSet" : "HGSet" );
    aSetName.append( nGlyphSetID );
    return aSetName.makeStringAndClear();
}

OString
GlyphSet::GetReencodedFontName (rtl_TextEncoding nEnc, std::string_view rFontName)
{
    if (   nEnc == RTL_TEXTENCODING_MS_1252
        || nEnc == RTL_TEXTENCODING_ISO_8859_1)
    {
        return OString::Concat(rFontName) + "-iso1252";
    }
    else
    if (nEnc >= RTL_TEXTENCODING_USER_START && nEnc <= RTL_TEXTENCODING_USER_END)
    {
        return OString::Concat(rFontName)
               + "-enc"
               + OString::number(nEnc - RTL_TEXTENCODING_USER_START);
    }
    else
    {
        return OString();
    }
}

void GlyphSet::DrawGlyph(PrinterGfx& rGfx,
                         const Point& rPoint,
                         const sal_GlyphId nGlyphId)
{
    unsigned char nGlyphID;
    sal_Int32 nGlyphSetID;

    // convert to font glyph id and font subset
    GetGlyphID (nGlyphId, &nGlyphID, &nGlyphSetID);

    OString aGlyphSetName = GetGlyphSetName(nGlyphSetID);

    rGfx.PSSetFont  (aGlyphSetName, RTL_TEXTENCODING_DONTKNOW);
    rGfx.PSMoveTo   (rPoint);
    rGfx.PSShowGlyph(nGlyphID);
}

namespace {

struct EncEntry
{
    unsigned char  aEnc;
    tools::Long       aGID;

    EncEntry() : aEnc( 0 ), aGID( 0 ) {}

    bool operator<( const EncEntry& rRight ) const
    { return aEnc < rRight.aEnc; }
};

}

static void CreatePSUploadableFont( TrueTypeFont* pSrcFont, SvStream* pTmpFile,
    const char* pGlyphSetName, int nGlyphCount,
    /*const*/ const sal_uInt16* pRequestedGlyphs, /*const*/ const unsigned char* pEncoding,
    bool bAllowType42 )
{
    // match the font-subset to the printer capabilities
     // TODO: allow CFF for capable printers
    FontType nTargetMask = FontType::TYPE1_PFA | FontType::TYPE3_FONT;
    if( bAllowType42 )
        nTargetMask |= FontType::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< unsigned char > aEncoding( nGlyphCount );
    std::vector< sal_GlyphId > 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.data(), aEncoding.data(), nGlyphCount );
}

void
GlyphSet::PSUploadFont (osl::File& rOutFile, PrinterGfx &rGfx, bool bAllowType42, std::vector< OString >& rSuppliedFonts )
{
    TrueTypeFont *pTTFont;
    OString aTTFileName (rGfx.GetFontMgr().getFontFileSysPath(mnFontID));
    int nFace = rGfx.GetFontMgr().getFontFaceNumber(mnFontID);
    SFErrCodes nSuccess = OpenTTFontFile(aTTFileName.getStr(), nFace, &pTTFont);
    if (nSuccess != SFErrCodes::Ok)
        return;

    utl::TempFileFast aTmpFile;
    SvStream* pTmpFile = aTmpFile.GetStream(StreamMode::READWRITE);

    // encoding vector maps character encoding to the ordinal number
    // of the glyph in the output file
    unsigned char  pEncoding[256];
    sal_uInt16 pTTGlyphMapping[256];

    // loop through all the font glyph subsets
    sal_Int32 nGlyphSetID = 1;
    for (auto const& glyph : maGlyphList)
    {
        if (glyph.empty())
        {
            ++nGlyphSetID;
            continue;
        }

        // loop through all the glyphs in the subset
        sal_Int32 n = 0;
        for (auto const& elem : glyph)
        {
            pTTGlyphMapping [n] = elem.first;
            pEncoding       [n] = elem.second;
            n++;
        }

        // create the current subset
        OString aGlyphSetName = GetGlyphSetName(nGlyphSetID);
        pTmpFile->WriteOString( Concat2View(OString::Concat("%%BeginResource: font ")+ aGlyphSetName + "\n") );
        CreatePSUploadableFont( pTTFont, pTmpFile, aGlyphSetName.getStr(), glyph.size(),
                                pTTGlyphMapping, pEncoding, bAllowType42 );
        pTmpFile->WriteOString("%%EndResource\n" );
        rSuppliedFonts.push_back( aGlyphSetName );
        ++nGlyphSetID;
    }

    // copy the file into the page header
    pTmpFile->Seek(0);
    pTmpFile->Flush();

    unsigned char  pBuffer[0x2000];
    sal_uInt64 nIn;
    sal_uInt64 nOut;
    do
    {
        nIn = pTmpFile->ReadBytes(pBuffer, sizeof(pBuffer));
        rOutFile.write (pBuffer, nIn, nOut);
    }
    while ((nIn == nOut) && !pTmpFile->eof());

    // cleanup
    CloseTTFont (pTTFont);
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/unx/generic/print/glyphset.hxx b/vcl/unx/generic/print/glyphset.hxx
deleted file mode 100644
index db7fe72..0000000
--- a/vcl/unx/generic/print/glyphset.hxx
+++ /dev/null
@@ -1,81 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */

#pragma once

#include <osl/file.hxx>

#include <rtl/string.hxx>

#include <glyphid.hxx>

#include <string_view>
#include <vector>
#include <unordered_map>

class Point;

namespace psp {

class PrinterGfx;
class PrintFontManager;

class GlyphSet
{
private:

    sal_Int32           mnFontID;
    bool                mbVertical;
    OString             maBaseName;

    typedef std::unordered_map< sal_GlyphId, sal_uInt8 > glyph_map_t;
    std::vector< glyph_map_t > maGlyphList;

    OString     GetGlyphSetName (sal_Int32 nGlyphSetID);

    void        GetGlyphID (sal_GlyphId nGlyphId,
                                unsigned char* nOutGlyphID, sal_Int32* nOutGlyphSetID);
    bool        LookupGlyphID (sal_GlyphId nGlyphId,
                                   unsigned char* nOutGlyphID, sal_Int32* nOutGlyphSetID);
    void        AddGlyphID (sal_GlyphId nGlyphId,
                                unsigned char* nOutGlyphID,
                                sal_Int32* nOutGlyphSetID);
    static void     AddNotdef (glyph_map_t &rGlyphMap);

public:

    GlyphSet (sal_Int32 nFontID, bool bVertical);
    /* FIXME delete the glyphlist in ~GlyphSet ??? */

    sal_Int32       GetFontID () const { return mnFontID;}
    static OString
    GetReencodedFontName (rtl_TextEncoding nEnc,
                          std::string_view rFontName);

    bool            IsVertical () const { return mbVertical;}

    void            DrawGlyph (PrinterGfx& rGfx,
                               const Point& rPoint,
                               const sal_GlyphId nGlyphId);
    void        PSUploadFont (osl::File& rOutFile, PrinterGfx &rGfx, bool bAsType42, std::vector< OString >& rSuppliedFonts );
};

} /* namespace psp */

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/unx/generic/print/printerjob.cxx b/vcl/unx/generic/print/printerjob.cxx
deleted file mode 100644
index 5bfe6b7..0000000
--- a/vcl/unx/generic/print/printerjob.cxx
+++ /dev/null
@@ -1,969 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */

#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#include "psputil.hxx"

#include <unx/printerjob.hxx>
#include <unx/printergfx.hxx>
#include <ppdparser.hxx>
#include <strhelper.hxx>
#include <printerinfomanager.hxx>

#include <rtl/ustring.hxx>
#include <rtl/strbuf.hxx>
#include <rtl/ustrbuf.hxx>

#include <osl/thread.h>
#include <osl/security.hxx>

#include <algorithm>
#include <cstddef>
#include <deque>
#include <vector>

using namespace psp;

#define nBLOCKSIZE 0x2000

namespace psp
{

static bool
AppendPS (FILE* pDst, osl::File* pSrc, unsigned char* pBuffer)
{
    assert(pBuffer);
    if ((pDst == nullptr) || (pSrc == nullptr))
        return false;

    if (pSrc->setPos(osl_Pos_Absolut, 0) != osl::FileBase::E_None)
        return false;

    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 true;
}

} // namespace psp

/*
 * private convenience routines for file handling
 */

std::unique_ptr<osl::File>
PrinterJob::CreateSpoolFile (std::u16string_view rName, std::u16string_view rExtension) const
{
    OUString aFile = OUString::Concat(rName) + rExtension;
    OUString aFileURL;
    osl::File::RC nError = osl::File::getFileURLFromSystemPath( aFile, aFileURL );
    if (nError != osl::File::E_None)
        return nullptr;
    aFileURL = maSpoolDirName + "/" + aFileURL;

    std::unique_ptr<osl::File> pFile( new osl::File (aFileURL) );
    nError = pFile->open (osl_File_OpenFlag_Read | osl_File_OpenFlag_Write | osl_File_OpenFlag_Create);
    if (nError != osl::File::E_None)
    {
        return nullptr;
    }

    osl::File::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();
    bool  bColor = IsColorPrinter ();

    return nLevel > 1 && bColor ? 24 : 8;
}

sal_uInt16
PrinterJob::GetPostscriptLevel (const JobData *pJobData) const
{
    sal_uInt16 nPSLevel = 2;

    if( pJobData == nullptr )
        pJobData = &m_aLastJobData;

    if( pJobData->m_nPSLevel )
        nPSLevel = pJobData->m_nPSLevel;
    else
        if( pJobData->m_pParser )
            nPSLevel = pJobData->m_pParser->getLanguageLevel();

    return nPSLevel;
}

bool
PrinterJob::IsColorPrinter () const
{
    bool bColor = false;

    if( m_aLastJobData.m_nColorDevice )
        bColor = m_aLastJobData.m_nColorDevice != -1;
    else if( m_aLastJobData.m_pParser )
        bColor = m_aLastJobData.m_pParser->isColorDevice();

    return bColor;
}

osl::File*
PrinterJob::GetCurrentPageBody ()
{
    return maPageVector.back().get();
}

/*
 * public methods of PrinterJob: the actual job / spool handling
 */
PrinterJob::PrinterJob()
    : mnFileMode(0)
    , m_pGraphics(nullptr)
    , mnResolution(96)
    , mnWidthPt(0)
    , mnHeightPt(0)
    , mnMaxWidthPt(0)
    , mnMaxHeightPt(0)
    , mnLandscapes(0)
    , mnPortraits(0)
    , mnLMarginPt(0)
    , mnRMarginPt(0)
    , mnTMarginPt(0)
    , mnBMarginPt(0)
    , mfXScale(1)
    , mfYScale(1)
    , m_bQuickJob(false)
{
}

/* remove all our temporary files, uses external program "rm", since
   osl functionality is inadequate */
static void
removeSpoolDir (const OUString& rSpoolDir)
{
    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_FAIL( "psprint: couldn't remove spool directory" );
        return;
    }
    OString aSysPathByte =
        OUStringToOString (aSysPath, osl_getThreadTextEncoding());
    if (system (OString("rm -rf " + aSysPathByte).getStr()) == -1)
        OSL_FAIL( "psprint: couldn't remove spool directory" );
}

/* creates a spool directory with a "pidgin random" value based on
   current system time */
static OUString
createSpoolDir ()
{
    TimeValue aCur;
    osl_getSystemTime( &aCur );
    sal_Int32 nRand = aCur.Seconds ^ (aCur.Nanosec/1000);

    OUString aTmpDir;
    osl_getTempDirURL( &aTmpDir.pData );

    do
    {
        OUString aDir = aTmpDir + "/psp" + OUString::number(nRand);
        if( osl::Directory::create( aDir ) == osl::FileBase::E_None )
        {
            osl::File::setAttributes( aDir,
                                        osl_File_Attribute_OwnWrite
                                      | osl_File_Attribute_OwnRead
                                      | osl_File_Attribute_OwnExe );
            return aDir;
        }
        nRand++;
    } while( nRand );
    return OUString();
}

PrinterJob::~PrinterJob ()
{
    maPageVector.clear();
    maHeaderVector.clear();

    // mpJobHeader->remove();
    mpJobHeader.reset();
    // mpJobTrailer->remove();
    mpJobTrailer.reset();

    // XXX should really call osl::remove routines
    if( !maSpoolDirName.isEmpty() )
        removeSpoolDir (maSpoolDirName);

    // osl::Directory::remove (maSpoolDirName);
}

static void WriteLocalTimePS( osl::File *rFile )
{
    TimeValue aStartTime, tLocal;
    oslDateTime date_time;
    if (osl_getSystemTime( &aStartTime ) &&
        osl_getLocalTimeFromSystemTime( &aStartTime, &tLocal ) &&
        osl_getDateTimeFromTimeValue( &tLocal, &date_time ))
    {
        char ar[ 256 ];
        snprintf(
            ar, sizeof (ar),
            "%04d-%02d-%02d %02d:%02d:%02d ",
            date_time.Year, date_time.Month, date_time.Day,
            date_time.Hours, date_time.Minutes, date_time.Seconds );
        WritePS( rFile, ar );
    }
    else
        WritePS( rFile, "Unknown-Time" );
}

static bool isAscii( std::u16string_view rStr )
{
    size_t nLen = rStr.size();
    for( size_t i = 0; i < nLen; i++ )
        if( rStr[i] > 127 )
            return false;
    return true;
}

bool
PrinterJob::StartJob (
                      const OUString& rFileName,
                      int nMode,
                      const OUString& rJobName,
                      std::u16string_view 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;

    OUString aExt(".ps");
    mpJobHeader  = CreateSpoolFile (u"psp_head", aExt);
    mpJobTrailer = CreateSpoolFile (u"psp_tail", aExt);
    if( ! (mpJobHeader && mpJobTrailer) ) // existing files are removed in destructor
        return false;

    // write document header according to Document Structuring Conventions (DSC)
    WritePS (mpJobHeader.get(),
             "%!PS-Adobe-3.0\n"
             "%%BoundingBox: (atend)\n" );

    // Creator (this application)
    OUString aFilterWS = WhitespaceToSpace( rAppName, false );
    WritePS (mpJobHeader.get(), "%%Creator: (");
    WritePS (mpJobHeader.get(), aFilterWS);
    WritePS (mpJobHeader.get(), ")\n");

    // For (user name)
    osl::Security aSecurity;
    OUString aUserName;
    if( aSecurity.getUserName( aUserName ) )
    {
        WritePS (mpJobHeader.get(), "%%For: (");
        WritePS (mpJobHeader.get(), aUserName);
        WritePS (mpJobHeader.get(), ")\n");
    }

    // Creation Date (locale independent local time)
    WritePS (mpJobHeader.get(), "%%CreationDate: (");
    WriteLocalTimePS (mpJobHeader.get());
    WritePS (mpJobHeader.get(), ")\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, false );
    OUString aTitle( aFilterWS );
    if( ! isAscii( aTitle ) )
    {
        aTitle = WhitespaceToSpace( rFileName.subView(rFileName.lastIndexOf('/')+1), false );
        if( ! isAscii( aTitle ) )
            aTitle.clear();
    }

    maJobTitle = aFilterWS;
    if( !aTitle.isEmpty() )
    {
        WritePS (mpJobHeader.get(), "%%Title: (");
        WritePS (mpJobHeader.get(), aTitle);
        WritePS (mpJobHeader.get(), ")\n");
    }

    // Language Level
    OStringBuffer pLevel;
    getValueOf(GetPostscriptLevel(&rSetupData), pLevel);
    pLevel.append('\n');
    WritePS (mpJobHeader.get(), "%%LanguageLevel: ");
    WritePS (mpJobHeader.get(), pLevel.makeStringAndClear());

    // Other
    WritePS (mpJobHeader.get(), "%%DocumentData: Clean7Bit\n");
    WritePS (mpJobHeader.get(), "%%Pages: (atend)\n");
    WritePS (mpJobHeader.get(), "%%Orientation: (atend)\n");
    WritePS (mpJobHeader.get(), "%%PageOrder: Ascend\n");
    WritePS (mpJobHeader.get(), "%%EndComments\n");

    // write Prolog
    writeProlog (mpJobHeader.get(), rSetupData);

    // mark last job setup as not set
    m_aLastJobData.m_pParser = nullptr;
    m_aLastJobData.m_aContext.setParser( nullptr );

    return true;
}

bool
PrinterJob::EndJob()
{
    // no pages ? that really means no print job
    if( maPageVector.empty() )
        return false;

    // write document setup (done here because it
    // includes the accumulated fonts
    if( mpJobHeader )
        writeSetup( mpJobHeader.get(), m_aDocumentJobData );
    m_pGraphics->OnEndJob();
    if( ! (mpJobHeader && mpJobTrailer) )
        return false;

    // write document trailer according to Document Structuring Conventions (DSC)
    OStringBuffer aTrailer(512);
    aTrailer.append( "%%Trailer\n"
        "%%BoundingBox: 0 0 "
        + OString::number( static_cast<sal_Int32>(mnMaxWidthPt) )
        + " "
        + OString::number( static_cast<sal_Int32>(mnMaxHeightPt) ) );
    if( mnLandscapes > mnPortraits )
        aTrailer.append("\n%%Orientation: Landscape");
    else
        aTrailer.append("\n%%Orientation: Portrait");
    aTrailer.append( "\n%%Pages: "
        + OString::number( static_cast<sal_Int32>(maPageVector.size()) )
        + "\n%%EOF\n" );
    WritePS (mpJobTrailer.get(), aTrailer.getStr());

    /*
     * spool the set of files to their final destination, this is U**X dependent
     */

    FILE* pDestFILE = nullptr;

    /* create a destination either as file or as a pipe */
    bool bSpoolToFile = !maFileName.isEmpty();
    if (bSpoolToFile)
    {
        const OString aFileName = 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 == nullptr )
                {
                    close( nFile );
                    unlink( aFileName.getStr() );
                    return false;
                }
            }
            else
            {
                (void)chmod( aFileName.getStr(), mnFileMode );
            }
        }
        if (pDestFILE == nullptr)
            pDestFILE = fopen (aFileName.getStr(), "w");

        if (pDestFILE == nullptr)
            return false;
    }
    else
    {
        PrinterInfoManager& rPrinterInfoManager = PrinterInfoManager::get ();
        pDestFILE = rPrinterInfoManager.startSpool( m_aLastJobData.m_aPrinterName, m_bQuickJob );
        if (pDestFILE == nullptr)
            return false;
    }

    /* spool the document parts to the destination */

    unsigned char pBuffer[ nBLOCKSIZE ];

    AppendPS (pDestFILE, mpJobHeader.get(), pBuffer);
    mpJobHeader->close();

    bool bSuccess = true;
    std::vector< std::unique_ptr<osl::File> >::iterator pPageBody;
    std::vector< std::unique_ptr<osl::File> >::iterator pPageHead;
    for (pPageBody  = maPageVector.begin(), pPageHead  = maHeaderVector.begin();
         pPageBody != maPageVector.end() && pPageHead != maHeaderVector.end();
         ++pPageBody, ++pPageHead)
    {
        if( *pPageHead )
        {
            osl::File::RC nError = (*pPageHead)->open(osl_File_OpenFlag_Read);
            if (nError == osl::File::E_None)
            {
                AppendPS (pDestFILE, pPageHead->get(), pBuffer);
                (*pPageHead)->close();
            }
        }
        else
            bSuccess = false;
        if( *pPageBody )
        {
            osl::File::RC nError = (*pPageBody)->open(osl_File_OpenFlag_Read);
            if (nError == osl::File::E_None)
            {
                AppendPS (pDestFILE, pPageBody->get(), pBuffer);
                (*pPageBody)->close();
            }
        }
        else
            bSuccess = false;
    }

    AppendPS (pDestFILE, mpJobTrailer.get(), pBuffer);
    mpJobTrailer->close();

    /* well done */

    if (bSpoolToFile)
        fclose (pDestFILE);
    else
    {
        PrinterInfoManager& rPrinterInfoManager = PrinterInfoManager::get();
        if (!rPrinterInfoManager.endSpool( m_aLastJobData.m_aPrinterName,
            maJobTitle, pDestFILE, m_aDocumentJobData, true, OUString()))
        {
            bSuccess = false;
        }
    }

    return bSuccess;
}

void
PrinterJob::InitPaperSize (const JobData& rJobSetup)
{
    int nRes = rJobSetup.m_aContext.getRenderResolution ();

    OUString 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 != nullptr)
        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        = 72.0 / static_cast<double>(mnResolution);
    mfYScale        = -1.0 * 72.0 / static_cast<double>(mnResolution);
}

void
PrinterJob::StartPage (const JobData& rJobSetup)
{
    InitPaperSize (rJobSetup);

    OUString aPageNo = OUString::number (static_cast<sal_Int32>(maPageVector.size())+1); // sequential page number must start with 1
    OUString aExt    = aPageNo + ".ps";

    maHeaderVector.push_back( CreateSpoolFile ( u"psp_pghead", aExt) );
    maPageVector.push_back( CreateSpoolFile ( u"psp_pgbody", aExt) );

    osl::File* pPageHeader = maHeaderVector.back().get();
    osl::File* pPageBody   = maPageVector.back().get();

    if( ! (pPageHeader && pPageBody) )
        return;

    // 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++;
    }

    OStringBuffer pBBox;

    psp::appendStr  ("%%PageBoundingBox: ",    pBBox);
    psp::getValueOf (mnLMarginPt,              pBBox);
    psp::appendStr  (" ",                      pBBox);
    psp::getValueOf (mnBMarginPt,              pBBox);
    psp::appendStr  (" ",                      pBBox);
    psp::getValueOf (mnWidthPt  - mnRMarginPt, pBBox);
    psp::appendStr  (" ",                      pBBox);
    psp::getValueOf (mnHeightPt - mnTMarginPt, pBBox);
    psp::appendStr  ("\n",                     pBBox);

    WritePS (pPageHeader, pBBox.makeStringAndClear());

    /* #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 == maPageVector.size() )
    {
        m_aDocumentJobData = rJobSetup;
        bWriteFeatures = false;
    }

    if ( writePageSetup( pPageHeader, rJobSetup, bWriteFeatures ) )
    {
        m_aLastJobData = rJobSetup;
    }
}

void
PrinterJob::EndPage ()
{
    osl::File* pPageHeader = maHeaderVector.back().get();
    osl::File* pPageBody   = maPageVector.back().get();

    if( ! (pPageBody && pPageHeader) )
        return;

    // copy page to paper and write page trailer according to DSC

    OStringBuffer pTrailer;
    psp::appendStr ("grestore grestore\n", pTrailer);
    psp::appendStr ("showpage\n",          pTrailer);
    psp::appendStr ("%%PageTrailer\n\n",   pTrailer);
    WritePS (pPageBody, pTrailer.makeStringAndClear());

    // this page is done for now, close it to avoid having too many open fd's

    pPageHeader->close();
    pPageBody->close();
}

namespace {

struct less_ppd_key
{
    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( " *"
        + OUStringToOString( pKey->getKey(), RTL_TEXTENCODING_ASCII_US )
        + " "
        + OUStringToOString( pValue->m_aOption, RTL_TEXTENCODING_ASCII_US ) );
    if( !bUseIncluseFeature )
    {
        aFeature.append( "\n"
            + OUStringToOString( pValue->m_aValue, RTL_TEXTENCODING_ASCII_US )
            + "\n%%EndFeature" );
    }
    aFeature.append( "\n} stopped cleartomark\n" );
    sal_uInt64 nWritten = 0;
    return !(pFile->write( aFeature.getStr(), aFeature.getLength(), nWritten )
        || nWritten != static_cast<sal_uInt64>(aFeature.getLength()));
}

bool PrinterJob::writeFeatureList( osl::File* pFile, const JobData& rJob, bool bDocumentSetup ) const
{
    bool bSuccess = true;

    // 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 == nullptr )
        )
    {
        std::size_t i;
        std::size_t 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::SetupType::DocumentSetup )
                    bEmit = true;
            }
            if( pKey->getSetupType()    == PPDKey::SetupType::PageSetup        ||
                pKey->getSetupType()    == PPDKey::SetupType::AnySetup )
                bEmit = true;
            if( bEmit )
            {
                const PPDValue* pValue = rJob.m_aContext.getValue( pKey );
                if( pValue
                    && pValue->m_eType == eInvocation
                    && ( m_aLastJobData.m_pParser == nullptr
                         || 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.indexOf( "<<" ) != -1 )
                            ||
                            ( pValue->m_aValue.indexOf( ">>" ) != -1 );
                        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");

    OStringBuffer pTranslate;

    if( rJob.m_eOrientation == orientation::Portrait )
    {
        psp::appendStr  ("gsave\n[",   pTranslate);
        psp::getValueOfDouble (        pTranslate, mfXScale, 5);
        psp::appendStr  (" 0 0 ",      pTranslate);
        psp::getValueOfDouble (        pTranslate, mfYScale, 5);
        psp::appendStr  (" ",          pTranslate);
        psp::getValueOf (mnRMarginPt,  pTranslate);
        psp::appendStr  (" ",          pTranslate);
        psp::getValueOf (mnHeightPt-mnTMarginPt,
                                  pTranslate);
        psp::appendStr  ("] concat\ngsave\n",
                                  pTranslate);
    }
    else
    {
        psp::appendStr  ("gsave\n",    pTranslate);
        psp::appendStr  ("[ 0 ",       pTranslate);
        psp::getValueOfDouble (        pTranslate, -mfYScale, 5);
        psp::appendStr  (" ",          pTranslate);
        psp::getValueOfDouble (        pTranslate, mfXScale, 5);
        psp::appendStr  (" 0 ",        pTranslate );
        psp::getValueOfDouble (        pTranslate, mnLMarginPt, 5 );
        psp::appendStr  (" ",          pTranslate);
        psp::getValueOf (mnBMarginPt,  pTranslate );
        psp::appendStr ("] concat\ngsave\n",
                                 pTranslate);
    }

    WritePS (pFile, pTranslate.makeStringAndClear());

    return bSuccess;
}

void PrinterJob::writeJobPatch( osl::File* pFile, const JobData& rJobData )
{
    if( ! PrinterInfoManager::get().getUseJobPatch() )
        return;

    const PPDKey* pKey = nullptr;

    if( rJobData.m_pParser )
        pKey = rJobData.m_pParser->getKey( "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::deque< 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 != "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" );
        }
    }

    std::sort(patch_order.begin(), patch_order.end());
    patch_order.erase(std::unique(patch_order.begin(), patch_order.end()), patch_order.end());

    for (auto const& elem : patch_order)
    {
        // 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::number(elem) ), false );
    }
}

void 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 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"
    };
    WritePS (pFile, pProlog);
}

bool PrinterJob::writeSetup( osl::File* pFile, const JobData& rJob )
{
    WritePS (pFile, "%%BeginSetup\n%\n");

    // download fonts
    std::vector< OString > aFonts;
    m_pGraphics->writeResources( pFile, aFonts );

    if( !aFonts.empty() )
    {
        std::vector< OString >::const_iterator it = aFonts.begin();
        OStringBuffer aLine( 256 );
        aLine.append( "%%DocumentSuppliedResources: font " + (*it) + "\n" );
        WritePS ( pFile, aLine.getStr() );
        while( (++it) != aFonts.end() )
        {
            aLine.setLength(0);
            aLine.append( "%%+ font " + (*it) + "\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
        OString aLine = "/#copies " +
                OString::number(static_cast<sal_Int32>(rJob.m_nCopies)) +
                " def\n";
        sal_uInt64 nWritten = 0;
        bSuccess = !(pFile->write(aLine.getStr(), aLine.getLength(), nWritten)
            || nWritten != static_cast<sal_uInt64>(aLine.getLength()));

        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;
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/unx/generic/print/psputil.cxx b/vcl/unx/generic/print/psputil.cxx
deleted file mode 100644
index b483713..0000000
--- a/vcl/unx/generic/print/psputil.cxx
+++ /dev/null
@@ -1,184 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */

#include <string.h>
#include "psputil.hxx"

namespace psp {

/*
 * string convenience routines
 */

sal_Int32
getHexValueOf (sal_Int32 nValue, OStringBuffer& pBuffer)
{
    const static char pHex [0x10] = {
        '0', '1', '2', '3', '4', '5', '6', '7',
        '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };

    pBuffer.append(pHex [(nValue & 0xF0) >> 4]);
    pBuffer.append(pHex [(nValue & 0x0F)     ]);

    return 2;
}

sal_Int32
getAlignedHexValueOf (sal_Int32 nValue, OStringBuffer& pBuffer)
{
    // get sign
    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;
    auto const start = pBuffer.getLength();
    while (nPrecision)
    {
        OStringBuffer scratch;
        nPrecision -= getHexValueOf (nValue % 256, scratch );
        pBuffer.insert(start, scratch);
        nValue /= 256;
    }

    // set sign bit
    if (bNegative)
    {
        switch (pBuffer[start])
        {
            case '0' : pBuffer[start] = '8'; break;
            case '1' : pBuffer[start] = '9'; break;
            case '2' : pBuffer[start] = 'A'; break;
            case '3' : pBuffer[start] = 'B'; break;
            case '4' : pBuffer[start] = 'C'; break;
            case '5' : pBuffer[start] = 'D'; break;
            case '6' : pBuffer[start] = 'E'; break;
            case '7' : pBuffer[start] = 'F'; break;
            default: OSL_FAIL("Already a signed value");
        }
    }

    // report precision
    return nRet;
}

sal_Int32
getValueOf (sal_Int32 nValue, OStringBuffer& pBuffer)
{
    sal_Int32 nChar = 0;
    if (nValue < 0)
    {
        pBuffer.append('-');
        ++nChar;
        nValue *= -1;
    }
    else
        if (nValue == 0)
        {
            pBuffer.append('0');
            ++nChar;
            return nChar;
        }

    char  pInvBuffer [32];
    sal_Int32 nInvChar = 0;
    while (nValue > 0)
    {
        pInvBuffer [nInvChar++] = '0' + nValue % 10;
        nValue /= 10;
    }
    while (nInvChar > 0)
    {
        pBuffer.append(pInvBuffer [--nInvChar]);
        ++nChar;
    }

    return nChar;
}

sal_Int32
appendStr (const char* pSrc, OStringBuffer& pDst)
{
    sal_Int32 nBytes = strlen (pSrc);
    pDst.append(pSrc, nBytes);

    return nBytes;
}

/*
 * copy strings to file
 */

bool
WritePS (osl::File* pFile, const 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;
}

bool
WritePS (osl::File* pFile, const char* pString, sal_uInt64 nInLength)
{
    sal_uInt64 nOutLength = 0;

    if (nInLength > 0 && pFile)
        pFile->write (pString, nInLength, nOutLength);

    return nInLength == nOutLength;
}

bool
WritePS (osl::File* pFile, const OString &rString)
{
    sal_uInt64 nInLength = rString.getLength();
    sal_uInt64 nOutLength = 0;

    if (nInLength > 0 && pFile)
        pFile->write (rString.getStr(), nInLength, nOutLength);

    return nInLength == nOutLength;
}

bool
WritePS (osl::File* pFile, std::u16string_view rString)
{
    return WritePS (pFile, OUStringToOString(rString, RTL_TEXTENCODING_ASCII_US));
}

} /* namespace psp */

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/unx/generic/print/psputil.hxx b/vcl/unx/generic/print/psputil.hxx
deleted file mode 100644
index e5ae180..0000000
--- a/vcl/unx/generic/print/psputil.hxx
+++ /dev/null
@@ -1,55 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */

#pragma once

#include <sal/config.h>

#include <string_view>

#include <osl/file.hxx>

#include <rtl/math.hxx>
#include <rtl/ustring.hxx>
#include <rtl/strbuf.hxx>
#include <rtl/string.hxx>

namespace psp {

/*
 *  string convenience routines
 */
sal_Int32   getHexValueOf (sal_Int32 nValue, OStringBuffer& pBuffer);
sal_Int32   getAlignedHexValueOf (sal_Int32 nValue, OStringBuffer& pBuffer);
sal_Int32   getValueOf    (sal_Int32 nValue, OStringBuffer& pBuffer);
sal_Int32   appendStr     (const char* pSrc, OStringBuffer& pDst);

inline void getValueOfDouble( OStringBuffer& pBuffer, double f, int nPrecision = 0)
{
    pBuffer.append(rtl::math::doubleToString( f, rtl_math_StringFormat_G, nPrecision, '.', true ));
}

bool    WritePS (osl::File* pFile, const char* pString);
bool    WritePS (osl::File* pFile, const char* pString, sal_uInt64 nInLength);
bool    WritePS (osl::File* pFile, const OString &rString);
bool    WritePS (osl::File* pFile, std::u16string_view rString);

}  /* namespace psp */

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/unx/generic/print/text_gfx.cxx b/vcl/unx/generic/print/text_gfx.cxx
deleted file mode 100644
index d847004..0000000
--- a/vcl/unx/generic/print/text_gfx.cxx
+++ /dev/null
@@ -1,158 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */

#include "psputil.hxx"
#include "glyphset.hxx"

#include <unx/printergfx.hxx>
#include <unx/fontmanager.hxx>

using namespace psp ;

/*
 * implement text handling printer routines,
 */

void PrinterGfx::SetFont(
                    sal_Int32 nFontID,
                    sal_Int32 nHeight,
                    sal_Int32 nWidth,
                    Degree10 nAngle,
                    bool bVertical,
                    bool bArtItalic,
                    bool bArtBold
                    )
{
    // font and encoding will be set by drawText again immediately
    // before PSShowText
    mnFontID                          = nFontID;
    maVirtualStatus.maFont.clear();
    maVirtualStatus.maEncoding        = RTL_TEXTENCODING_DONTKNOW;
    maVirtualStatus.mnTextHeight      = nHeight;
    maVirtualStatus.mnTextWidth       = nWidth;
    maVirtualStatus.mbArtItalic       = bArtItalic;
    maVirtualStatus.mbArtBold         = bArtBold;
    mnTextAngle                       = nAngle;
    mbTextVertical                    = bVertical;
}

void PrinterGfx::drawGlyph(const Point& rPoint,
                           sal_GlyphId aGlyphId)
{

    // draw the string
    // search for a glyph set matching the set font
    bool bGlyphFound = false;
    for (auto & elem : maPS3Font)
        if ( (elem.GetFontID()  == mnFontID)
             && (elem.IsVertical() == mbTextVertical))
        {
            elem.DrawGlyph (*this, rPoint, aGlyphId);
            bGlyphFound = true;
            break;
        }

    // not found ? create a new one
    if (!bGlyphFound)
    {
        maPS3Font.emplace_back(mnFontID, mbTextVertical);
        maPS3Font.back().DrawGlyph (*this, rPoint, aGlyphId);
    }
}

void PrinterGfx::DrawGlyph(const Point& rPoint,
                           const GlyphItem& rGlyph)
{
    // 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
    Degree10 nCurrentTextAngle = mnTextAngle;
    Point aPoint( rPoint );

    if (nCurrentTextAngle)
    {
        PSGSave ();
        PSTranslate (rPoint);
        PSRotate (nCurrentTextAngle);
        mnTextAngle = 0_deg10;
        aPoint = Point( 0, 0 );
    }

    if (mbTextVertical && rGlyph.IsVertical())
    {
        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;

        Point aRotPoint( -nDescend*nTextWidth/nTextHeight, nAscend*nTextWidth/nTextHeight );

        // transform matrix to new individual direction
        PSGSave ();
        GraphicsStatus aSaveStatus = maVirtualStatus;
        // switch font aspect
        maVirtualStatus.mnTextWidth = nTextHeight;
        maVirtualStatus.mnTextHeight = nTextWidth;
        if( aPoint.X() || aPoint.Y() )
            PSTranslate( aPoint );
        PSRotate (900_deg10);
        // draw the rotated glyph
        drawGlyph(aRotPoint, rGlyph.glyphId());

        // restore previous state
        maVirtualStatus = aSaveStatus;
        PSGRestore();
    }
    else
        drawGlyph(aPoint, rGlyph.glyphId());

    // restore the user coordinate system
    if (nCurrentTextAngle)
    {
        PSGRestore ();
        mnTextAngle = nCurrentTextAngle;
    }
}

/*
 * 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::OnEndJob ()
{
    maPS3Font.clear();
}

void
PrinterGfx::writeResources( osl::File* pFile, std::vector< OString >& rSuppliedFonts )
{
    // write glyphsets and reencodings
    for (auto & PS3Font : maPS3Font)
    {
        PS3Font.PSUploadFont (*pFile, *this, mbUploadPS42Fonts, rSuppliedFonts );
    }
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */