/* -*- 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 <filter/EpsWriter.hxx>
#include <tools/stream.hxx>
#include <tools/poly.hxx>
#include <tools/fract.hxx>
#include <tools/helpers.hxx>
#include <unotools/resmgr.hxx>
#include <vcl/svapp.hxx>
#include <vcl/metaact.hxx>
#include <vcl/graph.hxx>
#include <vcl/BitmapReadAccess.hxx>
#include <vcl/region.hxx>
#include <vcl/font.hxx>
#include <vcl/virdev.hxx>
#include <vcl/cvtgrf.hxx>
#include <vcl/gradient.hxx>
#include <unotools/configmgr.hxx>
#include <vcl/FilterConfigItem.hxx>
#include <vcl/graphictools.hxx>
#include <vcl/weld.hxx>
#include <strings.hrc>
#include <osl/diagnose.h>
#include <com/sun/star/task/XStatusIndicator.hpp>
#include <officecfg/Office/Common.hxx>

#include <cstdlib>
#include <memory>

using namespace ::com::sun::star::uno;

#define POSTSCRIPT_BOUNDINGSEARCH   0x1000  // we only try to get the BoundingBox
                                            // in the first 4096 bytes

#define EPS_PREVIEW_TIFF    1
#define EPS_PREVIEW_EPSI    2

#define PS_LINESIZE         70      // maximum number of characters a line in the output

// -----------------------------field-types------------------------------

namespace {

struct StackMember
{
    struct      StackMember * pSucc;
    Color       aGlobalCol;
    bool        bLineCol;
    Color       aLineCol;
    bool        bFillCol;
    Color       aFillCol;
    Color       aTextCol;
    bool        bTextFillCol;
    Color       aTextFillCol;
    Color       aBackgroundCol;
    vcl::Font   aFont;
    TextAlign   eTextAlign;

    double                      fLineWidth;
    double                      fMiterLimit;
    SvtGraphicStroke::CapType   eLineCap;
    SvtGraphicStroke::JoinType  eJoinType;
    SvtGraphicStroke::DashArray aDashArray;
};

struct PSLZWCTreeNode
{

    PSLZWCTreeNode*     pBrother;       // next node who has the same father
    PSLZWCTreeNode*     pFirstChild;    // first son
    sal_uInt16          nCode;          // The code for the string of pixel values, which arises if... <missing comment>
    sal_uInt16          nValue;         // the pixel value
};

enum NMode {PS_NONE = 0x00, PS_SPACE = 0x01, PS_RET = 0x02, PS_WRAP = 0x04}; // formatting mode: action which is inserted behind the output
inline NMode operator|(NMode a, NMode b)
{
    return static_cast<NMode>(static_cast<sal_uInt8>(a) | static_cast<sal_uInt8>(b));
}

class PSWriter
{
private:
    bool                mbStatus;
    bool                mbLevelWarning;     // if there any embedded eps file which was not exported
    sal_uInt32          mnLatestPush;       // offset to streamposition, where last push was done

    tools::Long                mnLevel;            // dialog options
    bool                mbGrayScale;
    bool                mbCompression;
    sal_Int32           mnPreview;
    sal_Int32           mnTextMode;

    SvStream*           mpPS;
    const GDIMetaFile*  pMTF;
    std::unique_ptr<GDIMetaFile>
                        pAMTF;              // only created if Graphics is not a Metafile
    ScopedVclPtrInstance<VirtualDevice>
                        pVDev;

    double              nBoundingX2;        // this represents the bounding box
    double              nBoundingY2;

    StackMember*        pGDIStack;
    sal_uInt32           mnCursorPos;        // current cursor position in output
    Color               aColor;             // current color which is used for output
    bool                bLineColor;
    Color               aLineColor;         // current GDIMetafile color settings
    bool                bFillColor;
    Color               aFillColor;
    Color               aTextColor;
    bool                bTextFillColor;
    Color               aTextFillColor;
    Color               aBackgroundColor;
    TextAlign           eTextAlign;

    double                      fLineWidth;
    double                      fMiterLimit;
    SvtGraphicStroke::CapType   eLineCap;
    SvtGraphicStroke::JoinType  eJoinType;
    SvtGraphicStroke::DashArray aDashArray;

    vcl::Font           maFont;
    vcl::Font           maLastFont;

    std::unique_ptr<PSLZWCTreeNode[]> pTable; // LZW compression data
    PSLZWCTreeNode*     pPrefix;            // the compression is as same as the TIFF compression
    sal_uInt16          nDataSize;
    sal_uInt16          nClearCode;
    sal_uInt16          nEOICode;
    sal_uInt16          nTableSize;
    sal_uInt16          nCodeSize;
    sal_uInt32          nOffset;
    sal_uInt32          dwShift;

    css::uno::Reference< css::task::XStatusIndicator > xStatusIndicator;

    void                ImplWriteProlog( const Graphic* pPreviewEPSI );
    void                ImplWriteEpilog();
    void                ImplWriteActions( const GDIMetaFile& rMtf, VirtualDevice& rVDev );

                        // this method makes LF's, space inserting and word wrapping as used in all nMode
                        // parameters
    inline void         ImplExecMode( NMode nMode );

                        // writes char[] + LF to stream
    inline void         ImplWriteLine( const char*, NMode nMode = PS_RET );

                        // writes ( nNumb / 10^nCount ) in ASCII format to stream
    void                ImplWriteF( sal_Int32 nNumb, sal_uInt8 nCount = 3, NMode nMode = PS_SPACE );

                        // writes a double in ASCII format to stream
    void                ImplWriteDouble( double );

                        // writes a long in ASCII format to stream
    void                ImplWriteLong( sal_Int32 nNumb, NMode nMode = PS_SPACE );

                        // writes a byte in ASCII format to stream
    void                ImplWriteByte( sal_uInt8 nNumb, NMode nMode = PS_SPACE );

                        // writes a byte in ASCII (hex) format to stream
    void                ImplWriteHexByte( sal_uInt8 nNumb, NMode nMode = PS_WRAP );

                        // writes nNumb as number from 0.000 till 1.000 in ASCII format to stream
    void                ImplWriteB1( sal_uInt8 nNumb );

    inline void         ImplWritePoint( const Point& );
    void                ImplMoveTo( const Point& );
    void                ImplLineTo( const Point&, NMode nMode = PS_SPACE );
    void                ImplCurveTo( const Point& rP1, const Point& rP2, const Point& rP3, NMode nMode );
    void                ImplTranslate( const double& fX, const double& fY );
    void                ImplScale( const double& fX, const double& fY );

    void                ImplAddPath( const tools::Polygon & rPolygon );
    void                ImplWriteLineInfo( double fLineWidth, double fMiterLimit, SvtGraphicStroke::CapType eLineCap,
                                    SvtGraphicStroke::JoinType eJoinType, SvtGraphicStroke::DashArray && rDashArray );
    void                ImplWriteLineInfo( const LineInfo& rLineInfo );
    void                ImplRect( const tools::Rectangle & rRectangle );
    void                ImplRectFill ( const tools::Rectangle & rRectangle );
    void                ImplWriteGradient( const tools::PolyPolygon& rPolyPoly, const Gradient& rGradient, VirtualDevice& rVDev );
    void                ImplIntersect( const tools::PolyPolygon& rPolyPoly );
    void                ImplPolyPoly( const tools::PolyPolygon & rPolyPolygon, bool bTextOutline = false );
    void                ImplPolyLine( const tools::Polygon & rPolygon );

    void                ImplSetClipRegion( vcl::Region const & rRegion );
    void                ImplBmp( Bitmap const *, Bitmap const *, const Point &, double nWidth, double nHeight );
    void                ImplText( const OUString& rUniString, const Point& rPos, KernArraySpan pDXArry, o3tl::span<const sal_Bool> pKashidaArry, sal_Int32 nWidth, VirtualDevice const & rVDev );
    void                ImplSetAttrForText( const Point & rPoint );
    void                ImplWriteCharacter( char );
    void                ImplWriteString( const OString&, VirtualDevice const & rVDev, KernArraySpan pDXArry, bool bStretch );
    void                ImplDefineFont( const char*, const char* );

    void                ImplClosePathDraw();
    void                ImplPathDraw();

    inline void         ImplWriteLineColor( NMode nMode );
    inline void         ImplWriteFillColor( NMode nMode );
    inline void         ImplWriteTextColor( NMode nMode );
    void                ImplWriteColor( NMode nMode );

    static double       ImplGetScaling( const MapMode& );
    void                ImplGetMapMode( const MapMode& );
    static bool         ImplGetBoundingBox( double* nNumb, sal_uInt8* pSource, sal_uInt32 nSize );
    static sal_uInt8*   ImplSearchEntry( sal_uInt8* pSource, sal_uInt8 const * pDest, sal_uInt32 nComp, sal_uInt32 nSize );
                        // LZW methods
    void                StartCompression();
    void                Compress( sal_uInt8 nSrc );
    void                EndCompression();
    inline void         WriteBits( sal_uInt16 nCode, sal_uInt16 nCodeLen );

public:
    bool            WritePS( const Graphic& rGraphic, SvStream& rTargetStream, FilterConfigItem* );
    PSWriter();
};

}

//========================== methods from PSWriter ==========================


PSWriter::PSWriter()
    : mbStatus(false)
    , mbLevelWarning(false)
    , mnLatestPush(0)
    , mnLevel(0)
    , mbGrayScale(false)
    , mbCompression(false)
    , mnPreview(0)
    , mnTextMode(0)
    , mpPS(nullptr)
    , pMTF(nullptr)
    , nBoundingX2(0)
    , nBoundingY2(0)
    , pGDIStack(nullptr)
    , mnCursorPos(0)
    , bLineColor(false)
    , bFillColor(false)
    , bTextFillColor(false)
    , eTextAlign()
    , fLineWidth(0)
    , fMiterLimit(0)
    , eLineCap()
    , eJoinType()
    , pPrefix(nullptr)
    , nDataSize(0)
    , nClearCode(0)
    , nEOICode(0)
    , nTableSize(0)
    , nCodeSize(0)
    , nOffset(0)
    , dwShift(0)
{
}

bool PSWriter::WritePS( const Graphic& rGraphic, SvStream& rTargetStream, FilterConfigItem* pFilterConfigItem )
{
    sal_uInt32 nStreamPosition = 0, nPSPosition = 0; // -Wall warning, unset, check

    mbStatus = true;
    mnPreview = 0;
    mbLevelWarning = false;
    mnLatestPush = 0xEFFFFFFE;

    if ( pFilterConfigItem )
    {
        xStatusIndicator = pFilterConfigItem->GetStatusIndicator();
        if ( xStatusIndicator.is() )
        {
            xStatusIndicator->start( OUString(), 100 );
        }
    }

    mpPS = &rTargetStream;
    mpPS->SetEndian( SvStreamEndian::LITTLE );

    // default values for the dialog options
    mnLevel = 2;
    mbGrayScale = false;
#ifdef UNX // don't compress by default on unix as ghostscript is unable to read LZW compressed eps
    mbCompression = false;
#else
    mbCompression = true;
#endif
    mnTextMode = 0;         // default0 : export glyph outlines

    // try to get the dialog selection
    if ( pFilterConfigItem )
    {
#ifdef UNX // don't put binary tiff preview ahead of postscript code by default on unix as ghostscript is unable to read it
        mnPreview = pFilterConfigItem->ReadInt32( "Preview", 0 );
#else
        mnPreview = pFilterConfigItem->ReadInt32( "Preview", 1 );
#endif
        mnLevel = pFilterConfigItem->ReadInt32( "Version", 2 );
        if ( mnLevel != 1 )
            mnLevel = 2;
        mbGrayScale = pFilterConfigItem->ReadInt32( "ColorFormat", 1 ) == 2;
#ifdef UNX // don't compress by default on unix as ghostscript is unable to read LZW compressed eps
        mbCompression = pFilterConfigItem->ReadInt32( "CompressionMode", 0 ) != 0;
#else
        mbCompression = pFilterConfigItem->ReadInt32( "CompressionMode", 1 ) == 1;
#endif
        mnTextMode = pFilterConfigItem->ReadInt32( "TextMode", 0 );
        if ( mnTextMode > 2 )
            mnTextMode = 0;
    }

    // compression is not available for Level 1
    if ( mnLevel == 1 )
    {
        mbGrayScale = true;
        mbCompression = false;
    }

    if ( mnPreview & EPS_PREVIEW_TIFF )
    {
        rTargetStream.WriteUInt32( 0xC6D3D0C5 );
        nStreamPosition = rTargetStream.Tell();
        rTargetStream.WriteUInt32( 0 ).WriteUInt32( 0 ).WriteUInt32( 0 ).WriteUInt32( 0 )
           .WriteUInt32( nStreamPosition + 26 ).WriteUInt32( 0 ).WriteUInt16( 0xffff );

        ErrCode nErrCode;
        if ( mbGrayScale )
        {
            BitmapEx aTempBitmapEx( rGraphic.GetBitmapEx() );
            aTempBitmapEx.Convert( BmpConversion::N8BitGreys );
            nErrCode = GraphicConverter::Export( rTargetStream, aTempBitmapEx, ConvertDataFormat::TIF ) ;
        }
        else
            nErrCode = GraphicConverter::Export( rTargetStream, rGraphic, ConvertDataFormat::TIF ) ;

        if ( nErrCode == ERRCODE_NONE )
        {
            nPSPosition = rTargetStream.TellEnd();
            rTargetStream.Seek( nStreamPosition + 20 );
            rTargetStream.WriteUInt32( nPSPosition - 30 );  // size of tiff gfx
            rTargetStream.Seek( nPSPosition );
        }
        else
        {
            mnPreview &=~ EPS_PREVIEW_TIFF;
            rTargetStream.Seek( nStreamPosition - 4 );
        }
    }

    // global default value setting
    StackMember*    pGS;

    if (rGraphic.GetType() == GraphicType::GdiMetafile)
        pMTF = &rGraphic.GetGDIMetaFile();
    else if (rGraphic.GetGDIMetaFile().GetActionSize())
    {
        pAMTF.reset( new GDIMetaFile( rGraphic.GetGDIMetaFile() ) );
        pMTF = pAMTF.get();
    }
    else
    {
        BitmapEx aBmp( rGraphic.GetBitmapEx() );
        pAMTF.reset( new GDIMetaFile );
        ScopedVclPtrInstance< VirtualDevice > pTmpVDev;
        pAMTF->Record( pTmpVDev );
        pTmpVDev->DrawBitmapEx( Point(), aBmp );
        pAMTF->Stop();
        pAMTF->SetPrefSize( aBmp.GetSizePixel() );
        pMTF = pAMTF.get();
    }
    pVDev->SetMapMode( pMTF->GetPrefMapMode() );
    nBoundingX2 = pMTF->GetPrefSize().Width();
    nBoundingY2 = pMTF->GetPrefSize().Height();

    pGDIStack = nullptr;
    aColor = COL_TRANSPARENT;
    bLineColor = true;
    aLineColor = COL_BLACK;
    bFillColor = true;
    aFillColor = COL_WHITE;
    bTextFillColor = true;
    aTextFillColor = COL_BLACK;
    fLineWidth = 1;
    fMiterLimit = 15; // use same limit as most graphic systems and basegfx
    eLineCap = SvtGraphicStroke::capButt;
    eJoinType = SvtGraphicStroke::joinMiter;
    aBackgroundColor = COL_WHITE;
    eTextAlign = ALIGN_BASELINE;

    if( pMTF->GetActionSize() )
    {
        ImplWriteProlog( ( mnPreview & EPS_PREVIEW_EPSI ) ? &rGraphic : nullptr );
        mnCursorPos = 0;
        ImplWriteActions( *pMTF, *pVDev );
        ImplWriteEpilog();
        if ( mnPreview & EPS_PREVIEW_TIFF )
        {
            sal_uInt32 nPosition = rTargetStream.Tell();
            rTargetStream.Seek( nStreamPosition );
            rTargetStream.WriteUInt32( nPSPosition );
            rTargetStream.WriteUInt32( nPosition - nPSPosition );
            rTargetStream.Seek( nPosition );
        }
        while( pGDIStack )
        {
            pGS=pGDIStack;
            pGDIStack=pGS->pSucc;
            delete pGS;
        }
    }
    else
        mbStatus = false;

    if ( mbStatus && mbLevelWarning && pFilterConfigItem )
    {
        std::locale loc = Translate::Create("flt");
        std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(nullptr,
                                                      VclMessageType::Info, VclButtonsType::Ok,
                                                      Translate::get(KEY_VERSION_CHECK, loc)));
        xInfoBox->run();
    }

    if ( xStatusIndicator.is() )
        xStatusIndicator->end();

    return mbStatus;
}

void PSWriter::ImplWriteProlog( const Graphic* pPreview )
{
    ImplWriteLine( "%!PS-Adobe-3.0 EPSF-3.0 " );
    mpPS->WriteCharPtr( "%%BoundingBox: " );                         // BoundingBox
    ImplWriteLong( 0 );
    ImplWriteLong( 0 );
    Size aSizePoint = OutputDevice::LogicToLogic( pMTF->GetPrefSize(),
                        pMTF->GetPrefMapMode(), MapMode(MapUnit::MapPoint));
    ImplWriteLong( aSizePoint.Width() );
    ImplWriteLong( aSizePoint.Height() ,PS_RET );
    ImplWriteLine( "%%Pages: 0" );
    OUString aCreator;
    OUString aCreatorOverride = officecfg::Office::Common::Save::Document::GeneratorOverride::get();
    if( !aCreatorOverride.isEmpty())
        aCreator = aCreatorOverride;
    else
        aCreator = "%%Creator: " + utl::ConfigManager::getProductName() + " " +
                   utl::ConfigManager::getProductVersion();
    ImplWriteLine( OUStringToOString( aCreator, RTL_TEXTENCODING_UTF8 ).getStr() );
    ImplWriteLine( "%%Title: none" );
    ImplWriteLine( "%%CreationDate: none" );

// defaults

    mpPS->WriteCharPtr( "%%LanguageLevel: " );                       // Language level
    ImplWriteLong( mnLevel, PS_RET );
    if ( !mbGrayScale && mnLevel == 1 )
        ImplWriteLine( "%%Extensions: CMYK" );          // CMYK extension is to set in color mode in level 1
    ImplWriteLine( "%%EndComments" );
    if ( pPreview && aSizePoint.Width() && aSizePoint.Height() )
    {
        Size aSizeBitmap( ( aSizePoint.Width() + 7 ) & ~7, aSizePoint.Height() );
        Bitmap aTmpBitmap( pPreview->GetBitmapEx().GetBitmap() );
        aTmpBitmap.Scale( aSizeBitmap, BmpScaleFlag::BestQuality );
        aTmpBitmap.Convert( BmpConversion::N1BitThreshold );
        BitmapReadAccess* pAcc = aTmpBitmap.AcquireReadAccess();
        if ( pAcc )
        {
            mpPS->WriteCharPtr( "%%BeginPreview: " );                    // BoundingBox
            ImplWriteLong( aSizeBitmap.Width() );
            ImplWriteLong( aSizeBitmap.Height() );
            mpPS->WriteCharPtr( "1 " );
            sal_Int32 nLines = aSizeBitmap.Width() / 312;
            if ( ( nLines * 312 ) != aSizeBitmap.Width() )
                nLines++;
            nLines *= aSizeBitmap.Height();
            ImplWriteLong( nLines );
            sal_Int32 nCount2, nCount = 4;
            const BitmapColor aBlack( pAcc->GetBestMatchingColor( COL_BLACK ) );
            for ( tools::Long nY = 0; nY < aSizeBitmap.Height(); nY++ )
            {
                nCount2 = 0;
                char nVal = 0;
                Scanline pScanline = pAcc->GetScanline( nY );
                for ( tools::Long nX = 0; nX < aSizeBitmap.Width(); nX++ )
                {
                    if ( !nCount2 )
                    {
                        ImplExecMode( PS_RET );
                        mpPS->WriteCharPtr( "%" );
                        nCount2 = 312;
                    }
                    nVal <<= 1;
                    if ( pAcc->GetPixelFromData( pScanline, nX ) == aBlack )
                        nVal |= 1;
                    if ( ! ( --nCount ) )
                    {
                        if ( nVal > 9 )
                            nVal += 'A' - 10;
                        else
                            nVal += '0';
                        mpPS->WriteChar( nVal );
                        nVal = 0;
                        nCount += 4;
                    }
                    nCount2--;
                }
            }
            Bitmap::ReleaseAccess( pAcc );
            ImplExecMode( PS_RET );
            ImplWriteLine( "%%EndPreview" );
        }
    }
    ImplWriteLine( "%%BeginProlog" );
    ImplWriteLine( "%%BeginResource: procset SDRes-Prolog 1.0 0" );

//  BEGIN EPSF
    ImplWriteLine( "/b4_inc_state save def\n/dict_count countdictstack def\n/op_count count 1 sub def\nuserdict begin" );
    ImplWriteLine( "0 setgray 0 setlinecap 1 setlinewidth 0 setlinejoin 10 setmiterlimit[] 0 setdash newpath" );
    ImplWriteLine( "/languagelevel where {pop languagelevel 1 ne {false setstrokeadjust false setoverprint} if} if" );

    ImplWriteLine( "/bdef {bind def} bind def" );       // the new operator bdef is created
    if ( mbGrayScale )
        ImplWriteLine( "/c {setgray} bdef" );
    else
        ImplWriteLine( "/c {setrgbcolor} bdef" );
    ImplWriteLine( "/l {neg lineto} bdef" );
    ImplWriteLine( "/rl {neg rlineto} bdef" );
    ImplWriteLine( "/lc {setlinecap} bdef" );
    ImplWriteLine( "/lj {setlinejoin} bdef" );
    ImplWriteLine( "/lw {setlinewidth} bdef" );
    ImplWriteLine( "/ml {setmiterlimit} bdef" );
    ImplWriteLine( "/ld {setdash} bdef" );
    ImplWriteLine( "/m {neg moveto} bdef" );
    ImplWriteLine( "/ct {6 2 roll neg 6 2 roll neg 6 2 roll neg curveto} bdef" );
    ImplWriteLine( "/r {rotate} bdef" );
    ImplWriteLine( "/t {neg translate} bdef" );
    ImplWriteLine( "/s {scale} bdef" );
    ImplWriteLine( "/sw {show} bdef" );
    ImplWriteLine( "/gs {gsave} bdef" );
    ImplWriteLine( "/gr {grestore} bdef" );

    ImplWriteLine( "/f {findfont dup length dict begin" );  // Setfont
    ImplWriteLine( "{1 index /FID ne {def} {pop pop} ifelse} forall /Encoding ISOLatin1Encoding def" );
    ImplWriteLine( "currentdict end /NFont exch definefont pop /NFont findfont} bdef" );

    ImplWriteLine( "/p {closepath} bdef" );
    ImplWriteLine( "/sf {scalefont setfont} bdef" );

    ImplWriteLine( "/ef {eofill}bdef"           );      // close path and fill
    ImplWriteLine( "/pc {closepath stroke}bdef" );      // close path and draw
    ImplWriteLine( "/ps {stroke}bdef" );                // draw current path
    ImplWriteLine( "/pum {matrix currentmatrix}bdef" ); // pushes the current matrix
    ImplWriteLine( "/pom {setmatrix}bdef" );            // pops the matrix
    ImplWriteLine( "/bs {/aString exch def /nXOfs exch def /nWidth exch def currentpoint nXOfs 0 rmoveto pum nWidth aString stringwidth pop div 1 scale aString show pom moveto} bdef" );
    ImplWriteLine( "%%EndResource" );
    ImplWriteLine( "%%EndProlog" );
    ImplWriteLine( "%%BeginSetup" );
    ImplWriteLine( "%%EndSetup" );
    ImplWriteLine( "%%Page: 1 1" );
    ImplWriteLine( "%%BeginPageSetup" );
    ImplWriteLine( "%%EndPageSetup" );
    ImplWriteLine( "pum" );
    ImplScale( static_cast<double>(aSizePoint.Width()) / static_cast<double>(pMTF->GetPrefSize().Width()), static_cast<double>(aSizePoint.Height()) / static_cast<double>(pMTF->GetPrefSize().Height()) );
    ImplWriteDouble( 0 );
    ImplWriteDouble( -pMTF->GetPrefSize().Height() );
    ImplWriteLine( "t" );
    ImplWriteLine( "/tm matrix currentmatrix def" );
}

void PSWriter::ImplWriteEpilog()
{
    ImplTranslate( 0, nBoundingY2 );
    ImplWriteLine( "pom" );
    ImplWriteLine( "count op_count sub {pop} repeat countdictstack dict_count sub {end} repeat b4_inc_state restore" );

    ImplWriteLine( "%%PageTrailer" );
    ImplWriteLine( "%%Trailer" );

    ImplWriteLine( "%%EOF" );
}

void PSWriter::ImplWriteActions( const GDIMetaFile& rMtf, VirtualDevice& rVDev )
{
    tools::PolyPolygon aFillPath;

    for( size_t nCurAction = 0, nCount = rMtf.GetActionSize(); nCurAction < nCount; nCurAction++ )
    {
        MetaAction* pMA = rMtf.GetAction( nCurAction );

        switch( pMA->GetType() )
        {
            case MetaActionType::NONE :
            break;

            case MetaActionType::PIXEL :
            {
                Color aOldLineColor( aLineColor );
                aLineColor = static_cast<const MetaPixelAction*>(pMA)->GetColor();
                ImplWriteLineColor( PS_SPACE );
                ImplMoveTo( static_cast<const MetaPixelAction*>(pMA)->GetPoint() );
                ImplLineTo( static_cast<const MetaPixelAction*>(pMA)->GetPoint() );
                ImplPathDraw();
                aLineColor = aOldLineColor;
            }
            break;

            case MetaActionType::POINT :
            {
                ImplWriteLineColor( PS_SPACE );
                ImplMoveTo( static_cast<const MetaPointAction*>(pMA)->GetPoint() );
                ImplLineTo( static_cast<const MetaPointAction*>(pMA)->GetPoint() );
                ImplPathDraw();
            }
            break;

            case MetaActionType::LINE :
            {
                const LineInfo& rLineInfo = static_cast<const MetaLineAction*>(pMA)->GetLineInfo();
                ImplWriteLineInfo( rLineInfo );
                if ( bLineColor )
                {
                    ImplWriteLineColor( PS_SPACE );
                    ImplMoveTo( static_cast<const MetaLineAction*>(pMA)->GetStartPoint() );
                    ImplLineTo( static_cast<const MetaLineAction*>(pMA )->GetEndPoint() );
                    ImplPathDraw();
                }
            }
            break;

            case MetaActionType::RECT :
            {
                ImplRect( static_cast<const MetaRectAction*>(pMA)->GetRect() );
            }
            break;

            case MetaActionType::ROUNDRECT :
                ImplRect( static_cast<const MetaRoundRectAction*>(pMA)->GetRect() );
            break;

            case MetaActionType::ELLIPSE :
            {
                tools::Rectangle   aRect = static_cast<const MetaEllipseAction*>(pMA)->GetRect();
                Point       aCenter = aRect.Center();
                tools::Polygon aPoly( aCenter, aRect.GetWidth() / 2, aRect.GetHeight() / 2 );
                tools::PolyPolygon aPolyPoly( aPoly );
                ImplPolyPoly( aPolyPoly );
            }
            break;

            case MetaActionType::ARC :
            {
                tools::Polygon aPoly( static_cast<const MetaArcAction*>(pMA)->GetRect(), static_cast<const MetaArcAction*>(pMA)->GetStartPoint(),
                    static_cast<const MetaArcAction*>(pMA)->GetEndPoint(), PolyStyle::Arc );
                tools::PolyPolygon aPolyPoly( aPoly );
                ImplPolyPoly( aPolyPoly );
            }
            break;

            case MetaActionType::PIE :
            {
                tools::Polygon aPoly( static_cast<const MetaPieAction*>(pMA)->GetRect(), static_cast<const MetaPieAction*>(pMA)->GetStartPoint(),
                    static_cast<const MetaPieAction*>(pMA)->GetEndPoint(), PolyStyle::Pie );
                tools::PolyPolygon aPolyPoly( aPoly );
                ImplPolyPoly( aPolyPoly );
            }
            break;

            case MetaActionType::CHORD :
            {
                tools::Polygon aPoly( static_cast<const MetaChordAction*>(pMA)->GetRect(), static_cast<const MetaChordAction*>(pMA)->GetStartPoint(),
                    static_cast<const MetaChordAction*>(pMA)->GetEndPoint(), PolyStyle::Chord );
                tools::PolyPolygon aPolyPoly( aPoly );
                ImplPolyPoly( aPolyPoly );
            }
            break;

            case MetaActionType::POLYLINE :
            {
                tools::Polygon aPoly( static_cast<const MetaPolyLineAction*>(pMA)->GetPolygon() );
                const LineInfo& rLineInfo = static_cast<const MetaPolyLineAction*>(pMA)->GetLineInfo();
                ImplWriteLineInfo( rLineInfo );

                if(basegfx::B2DLineJoin::NONE == rLineInfo.GetLineJoin()
                    && rLineInfo.GetWidth() > 1)
                {
                    // emulate B2DLineJoin::NONE by creating single edges
                    const sal_uInt16 nPoints(aPoly.GetSize());
                    const bool bCurve(aPoly.HasFlags());

                    for(sal_uInt16 a(0); a + 1 < nPoints; a++)
                    {
                        if(bCurve
                            && PolyFlags::Normal != aPoly.GetFlags(a + 1)
                            && a + 2 < nPoints
                            && PolyFlags::Normal != aPoly.GetFlags(a + 2)
                            && a + 3 < nPoints)
                        {
                            const tools::Polygon aSnippet(4,
                                aPoly.GetConstPointAry() + a,
                                aPoly.GetConstFlagAry() + a);
                            ImplPolyLine(aSnippet);
                            a += 2;
                        }
                        else
                        {
                            const tools::Polygon aSnippet(2,
                                aPoly.GetConstPointAry() + a);
                            ImplPolyLine(aSnippet);
                        }
                    }
                }
                else
                {
                    ImplPolyLine( aPoly );
                }
            }
            break;

            case MetaActionType::POLYGON :
            {
                tools::PolyPolygon aPolyPoly( static_cast<const MetaPolygonAction*>(pMA)->GetPolygon() );
                ImplPolyPoly( aPolyPoly );
            }
            break;

            case MetaActionType::POLYPOLYGON :
            {
                ImplPolyPoly( static_cast<const MetaPolyPolygonAction*>(pMA)->GetPolyPolygon() );
            }
            break;

            case MetaActionType::TEXT:
            {
                const MetaTextAction * pA = static_cast<const MetaTextAction*>(pMA);

                OUString  aUniStr = pA->GetText().copy( pA->GetIndex(), pA->GetLen() );
                Point     aPoint( pA->GetPoint() );

                ImplText( aUniStr, aPoint, {}, {}, 0, rVDev );
            }
            break;

            case MetaActionType::TEXTRECT:
            {
                OSL_FAIL( "Unsupported action: TextRect...Action!" );
            }
            break;

            case MetaActionType::STRETCHTEXT :
            {
                const MetaStretchTextAction* pA = static_cast<const MetaStretchTextAction*>(pMA);
                OUString  aUniStr = pA->GetText().copy( pA->GetIndex(), pA->GetLen() );
                Point     aPoint( pA->GetPoint() );

                ImplText( aUniStr, aPoint, {}, {}, pA->GetWidth(), rVDev );
            }
            break;

            case MetaActionType::TEXTARRAY:
            {
                const MetaTextArrayAction* pA = static_cast<const MetaTextArrayAction*>(pMA);
                OUString  aUniStr = pA->GetText().copy( pA->GetIndex(), pA->GetLen() );
                Point     aPoint( pA->GetPoint() );

                ImplText( aUniStr, aPoint, pA->GetDXArray(), pA->GetKashidaArray(), 0, rVDev );
            }
            break;

            case MetaActionType::BMP :
            {
                Bitmap aBitmap = static_cast<const MetaBmpAction*>(pMA)->GetBitmap();
                if ( mbGrayScale )
                    aBitmap.Convert( BmpConversion::N8BitGreys );
                Point aPoint = static_cast<const MetaBmpAction*>(pMA)->GetPoint();
                Size aSize( rVDev.PixelToLogic( aBitmap.GetSizePixel() ) );
                ImplBmp( &aBitmap, nullptr, aPoint, aSize.Width(), aSize.Height() );
            }
            break;

            case MetaActionType::BMPSCALE :
            {
                Bitmap aBitmap = static_cast<const MetaBmpScaleAction*>(pMA)->GetBitmap();
                if ( mbGrayScale )
                    aBitmap.Convert( BmpConversion::N8BitGreys );
                Point aPoint = static_cast<const MetaBmpScaleAction*>(pMA)->GetPoint();
                Size aSize = static_cast<const MetaBmpScaleAction*>(pMA)->GetSize();
                ImplBmp( &aBitmap, nullptr, aPoint, aSize.Width(), aSize.Height() );
            }
            break;

            case MetaActionType::BMPSCALEPART :
            {
                Bitmap  aBitmap( static_cast<const MetaBmpScalePartAction*>(pMA)->GetBitmap() );
                aBitmap.Crop( tools::Rectangle( static_cast<const MetaBmpScalePartAction*>(pMA)->GetSrcPoint(),
                    static_cast<const MetaBmpScalePartAction*>(pMA)->GetSrcSize() ) );
                if ( mbGrayScale )
                    aBitmap.Convert( BmpConversion::N8BitGreys );
                Point aPoint = static_cast<const MetaBmpScalePartAction*>(pMA)->GetDestPoint();
                Size aSize = static_cast<const MetaBmpScalePartAction*>(pMA)->GetDestSize();
                ImplBmp( &aBitmap, nullptr, aPoint, aSize.Width(), aSize.Height() );
            }
            break;

            case MetaActionType::BMPEX :
            {
                BitmapEx aBitmapEx( static_cast<MetaBmpExAction*>(pMA)->GetBitmapEx() );
                Bitmap aBitmap( aBitmapEx.GetBitmap() );
                if ( mbGrayScale )
                    aBitmap.Convert( BmpConversion::N8BitGreys );
                Bitmap aMask( aBitmapEx.GetAlpha() );
                Point aPoint( static_cast<const MetaBmpExAction*>(pMA)->GetPoint() );
                Size aSize( rVDev.PixelToLogic( aBitmap.GetSizePixel() ) );
                ImplBmp( &aBitmap, &aMask, aPoint, aSize.Width(), aSize.Height() );
            }
            break;

            case MetaActionType::BMPEXSCALE :
            {
                BitmapEx aBitmapEx( static_cast<MetaBmpExScaleAction*>(pMA)->GetBitmapEx() );
                Bitmap aBitmap( aBitmapEx.GetBitmap() );
                if ( mbGrayScale )
                    aBitmap.Convert( BmpConversion::N8BitGreys );
                Bitmap aMask( aBitmapEx.GetAlpha() );
                Point aPoint = static_cast<const MetaBmpExScaleAction*>(pMA)->GetPoint();
                Size aSize( static_cast<const MetaBmpExScaleAction*>(pMA)->GetSize() );
                ImplBmp( &aBitmap, &aMask, aPoint, aSize.Width(), aSize.Height() );
            }
            break;

            case MetaActionType::BMPEXSCALEPART :
            {
                BitmapEx    aBitmapEx( static_cast<const MetaBmpExScalePartAction*>(pMA)->GetBitmapEx() );
                aBitmapEx.Crop( tools::Rectangle( static_cast<const MetaBmpExScalePartAction*>(pMA)->GetSrcPoint(),
                    static_cast<const MetaBmpExScalePartAction*>(pMA)->GetSrcSize() ) );
                Bitmap      aBitmap( aBitmapEx.GetBitmap() );
                if ( mbGrayScale )
                    aBitmap.Convert( BmpConversion::N8BitGreys );
                Bitmap      aMask( aBitmapEx.GetAlpha() );
                Point aPoint = static_cast<const MetaBmpExScalePartAction*>(pMA)->GetDestPoint();
                Size aSize = static_cast<const MetaBmpExScalePartAction*>(pMA)->GetDestSize();
                ImplBmp( &aBitmap, &aMask, aPoint, aSize.Width(), aSize.Height() );
            }
            break;

            // Unsupported Actions
            case MetaActionType::MASK:
            case MetaActionType::MASKSCALE:
            case MetaActionType::MASKSCALEPART:
            {
                OSL_FAIL( "Unsupported action: MetaMask...Action!" );
            }
            break;

            case MetaActionType::GRADIENT :
            {
                tools::PolyPolygon aPolyPoly( static_cast<const MetaGradientAction*>(pMA)->GetRect() );
                ImplWriteGradient( aPolyPoly, static_cast<const MetaGradientAction*>(pMA)->GetGradient(), rVDev );
            }
            break;

            case MetaActionType::GRADIENTEX :
            {
                tools::PolyPolygon aPolyPoly( static_cast<const MetaGradientExAction*>(pMA)->GetPolyPolygon() );
                ImplWriteGradient( aPolyPoly, static_cast<const MetaGradientExAction*>(pMA)->GetGradient(), rVDev );
            }
            break;

            case MetaActionType::HATCH :
            {
                ScopedVclPtrInstance< VirtualDevice > l_pVirDev;
                GDIMetaFile     aTmpMtf;

                l_pVirDev->SetMapMode( rVDev.GetMapMode() );
                l_pVirDev->AddHatchActions( static_cast<const MetaHatchAction*>(pMA)->GetPolyPolygon(),
                                            static_cast<const MetaHatchAction*>(pMA)->GetHatch(), aTmpMtf );
                ImplWriteActions( aTmpMtf, rVDev );
            }
            break;

            case MetaActionType::WALLPAPER :
            {
                const MetaWallpaperAction* pA = static_cast<const MetaWallpaperAction*>(pMA);
                tools::Rectangle   aRect = pA->GetRect();
                const Wallpaper&   aWallpaper = pA->GetWallpaper();

                if ( aWallpaper.IsBitmap() )
                {
                    BitmapEx aBitmapEx = aWallpaper.GetBitmap();
                    Bitmap aBitmap( aBitmapEx.GetBitmap() );
                    if ( aBitmapEx.IsAlpha() )
                    {
                        if ( aWallpaper.IsGradient() )
                        {

                        // gradient action

                        }
                        Bitmap aMask( aBitmapEx.GetAlpha() );
                        ImplBmp( &aBitmap, &aMask, Point( aRect.Left(), aRect.Top() ), aRect.GetWidth(), aRect.GetHeight() );
                    }
                    else
                        ImplBmp( &aBitmap, nullptr, Point( aRect.Left(), aRect.Top() ), aRect.GetWidth(), aRect.GetHeight() );

                        // wallpaper Style

                }
                else if ( aWallpaper.IsGradient() )
                {

                // gradient action

                }
                else
                {
                    aColor = aWallpaper.GetColor();
                    ImplRectFill( aRect );
                }
            }
            break;

            case MetaActionType::ISECTRECTCLIPREGION:
            {
                const MetaISectRectClipRegionAction* pA = static_cast<const MetaISectRectClipRegionAction*>(pMA);
                vcl::Region aRegion( pA->GetRect() );
                ImplSetClipRegion( aRegion );
            }
            break;

            case MetaActionType::CLIPREGION:
            {
                const MetaClipRegionAction* pA = static_cast<const MetaClipRegionAction*>(pMA);
                const vcl::Region& aRegion( pA->GetRegion() );
                ImplSetClipRegion( aRegion );
            }
            break;

            case MetaActionType::ISECTREGIONCLIPREGION:
            {
                const MetaISectRegionClipRegionAction* pA = static_cast<const MetaISectRegionClipRegionAction*>(pMA);
                const vcl::Region& aRegion( pA->GetRegion() );
                ImplSetClipRegion( aRegion );
            }
            break;

            case MetaActionType::MOVECLIPREGION:
            {
                // TODO: Implement!
            }
            break;

            case MetaActionType::LINECOLOR :
            {
                if ( static_cast<const MetaLineColorAction*>(pMA)->IsSetting() )
                {
                    bLineColor = true;
                    aLineColor = static_cast<const MetaLineColorAction*>(pMA)->GetColor();
                }
                else
                    bLineColor = false;
            }
            break;

            case MetaActionType::FILLCOLOR :
            {
                if ( static_cast<const MetaFillColorAction*>(pMA)->IsSetting() )
                {
                    bFillColor = true;
                    aFillColor =  static_cast<const MetaFillColorAction*>(pMA)->GetColor();
                }
                else
                    bFillColor = false;
            }
            break;

            case MetaActionType::TEXTCOLOR :
            {
                aTextColor = static_cast<const MetaTextColorAction*>(pMA)->GetColor();
            }
            break;

            case MetaActionType::TEXTFILLCOLOR :
            {
                if ( static_cast<const MetaTextFillColorAction*>(pMA)->IsSetting() )
                {
                    bTextFillColor = true;
                    aTextFillColor = static_cast<const MetaTextFillColorAction*>(pMA)->GetColor();
                }
                else
                    bTextFillColor = false;
            }
            break;

            case MetaActionType::TEXTALIGN :
            {
                eTextAlign = static_cast<const MetaTextAlignAction*>(pMA)->GetTextAlign();
            }
            break;

            case MetaActionType::MAPMODE :
            {
                pMA->Execute( &rVDev );
                ImplGetMapMode( rVDev.GetMapMode() );
            }
            break;

            case MetaActionType::FONT :
            {
                maFont = static_cast<const MetaFontAction*>(pMA)->GetFont();
                rVDev.SetFont( maFont );
            }
            break;

            case MetaActionType::PUSH :
            {
                rVDev.Push(static_cast<const MetaPushAction*>(pMA)->GetFlags() );
                StackMember* pGS = new StackMember;
                pGS->pSucc = pGDIStack;
                pGDIStack = pGS;
                pGS->aDashArray = aDashArray;
                pGS->eJoinType = eJoinType;
                pGS->eLineCap = eLineCap;
                pGS->fLineWidth = fLineWidth;
                pGS->fMiterLimit = fMiterLimit;
                pGS->eTextAlign = eTextAlign;
                pGS->aGlobalCol = aColor;
                pGS->bLineCol = bLineColor;
                pGS->aLineCol = aLineColor;
                pGS->bFillCol = bFillColor;
                pGS->aFillCol = aFillColor;
                pGS->aTextCol = aTextColor;
                pGS->bTextFillCol = bTextFillColor;
                pGS->aTextFillCol = aTextFillColor;
                pGS->aBackgroundCol = aBackgroundColor;
                pGS->aFont = maFont;
                mnLatestPush = mpPS->Tell();
                ImplWriteLine( "gs" );
            }
            break;

            case MetaActionType::POP :
            {
                rVDev.Pop();
                if( pGDIStack )
                {
                    StackMember* pGS = pGDIStack;
                    pGDIStack = pGS->pSucc;
                    aDashArray = pGS->aDashArray;
                    eJoinType = pGS->eJoinType;
                    eLineCap = pGS->eLineCap;
                    fLineWidth = pGS->fLineWidth;
                    fMiterLimit = pGS->fMiterLimit;
                    eTextAlign = pGS->eTextAlign;
                    aColor = pGS->aGlobalCol;
                    bLineColor = pGS->bLineCol;
                    aLineColor = pGS->aLineCol;
                    bFillColor = pGS->bFillCol;
                    aFillColor = pGS->aFillCol;
                    aTextColor = pGS->aTextCol;
                    bTextFillColor = pGS->bTextFillCol;
                    aTextFillColor = pGS->aTextFillCol;
                    aBackgroundColor = pGS->aBackgroundCol;
                    maFont = pGS->aFont;
                    maLastFont = vcl::Font();                // set maLastFont != maFont -> so that
                    delete pGS;
                    sal_uInt32 nCurrentPos = mpPS->Tell();
                    if ( nCurrentPos - 3 == mnLatestPush )
                    {
                        mpPS->Seek( mnLatestPush );
                        ImplWriteLine( "  " );
                        mpPS->Seek( mnLatestPush );
                    }
                    else
                        ImplWriteLine( "gr" );
                }
            }
            break;

            case MetaActionType::EPS :
            {
                GfxLink aGfxLink = static_cast<const MetaEPSAction*>(pMA)->GetLink();
                const GDIMetaFile aSubstitute( static_cast<const MetaEPSAction*>(pMA)->GetSubstitute() );

                bool    bLevelConflict = false;
                sal_uInt8*  pSource = const_cast<sal_uInt8*>(aGfxLink.GetData());
                sal_uInt32   nSize = aGfxLink.GetDataSize();
                sal_uInt32 nParseThis = POSTSCRIPT_BOUNDINGSEARCH;
                if ( nSize < 64 )                       // assuming eps is larger than 64 bytes
                    pSource = nullptr;
                if ( nParseThis > nSize )
                    nParseThis = nSize;

                if ( pSource && ( mnLevel == 1 ) )
                {
                    sal_uInt8* pFound = ImplSearchEntry( pSource, reinterpret_cast<sal_uInt8 const *>("%%LanguageLevel:"), nParseThis - 10, 16 );
                    if ( pFound )
                    {
                        sal_uInt8   k, i = 10;
                        pFound += 16;
                        while ( --i )
                        {
                            k = *pFound++;
                            if ( ( k > '0' ) && ( k <= '9' ) )
                            {
                                if ( k != '1' )
                                {
                                    bLevelConflict = true;
                                    mbLevelWarning = true;
                                }
                                break;
                            }
                        }
                    }
                }
                if ( !bLevelConflict )
                {
                    double  nBoundingBox[4];
                    if ( pSource && ImplGetBoundingBox( nBoundingBox, pSource, nParseThis ) )
                    {
                        Point   aPoint = static_cast<const MetaEPSAction*>(pMA)->GetPoint();
                        Size    aSize = static_cast<const MetaEPSAction*>(pMA)->GetSize();

                        MapMode aMapMode( aSubstitute.GetPrefMapMode() );
                        Size aOutSize( OutputDevice::LogicToLogic( aSize, rVDev.GetMapMode(), aMapMode ) );
                        Point aOrigin( OutputDevice::LogicToLogic( aPoint, rVDev.GetMapMode(), aMapMode ) );
                        aOrigin.AdjustY(aOutSize.Height() );
                        aMapMode.SetOrigin( aOrigin );
                        aMapMode.SetScaleX( Fraction(aOutSize.Width() / ( nBoundingBox[ 2 ] - nBoundingBox[ 0 ] )) );
                        aMapMode.SetScaleY( Fraction(aOutSize.Height() / ( nBoundingBox[ 3 ] - nBoundingBox[ 1 ] )) );
                        ImplWriteLine( "gs" );
                        ImplGetMapMode( aMapMode );
                        ImplWriteLine( "%%BeginDocument:" );
                        mpPS->WriteBytes(pSource, aGfxLink.GetDataSize());
                        ImplWriteLine( "%%EndDocument\ngr" );
                    }
                }
            }
            break;

            case MetaActionType::Transparent:
            {
                // TODO: implement!
            }
            break;

            case MetaActionType::RASTEROP:
            {
                pMA->Execute( &rVDev );
            }
            break;

            case MetaActionType::FLOATTRANSPARENT:
            {
                const MetaFloatTransparentAction* pA = static_cast<const MetaFloatTransparentAction*>(pMA);

                GDIMetaFile     aTmpMtf( pA->GetGDIMetaFile() );
                Point           aSrcPt( aTmpMtf.GetPrefMapMode().GetOrigin() );
                const Size      aSrcSize( aTmpMtf.GetPrefSize() );
                const Point     aDestPt( pA->GetPoint() );
                const Size      aDestSize( pA->GetSize() );
                const double    fScaleX = aSrcSize.Width() ? static_cast<double>(aDestSize.Width()) / aSrcSize.Width() : 1.0;
                const double    fScaleY = aSrcSize.Height() ? static_cast<double>(aDestSize.Height()) / aSrcSize.Height() : 1.0;
                tools::Long            nMoveX, nMoveY;

                if( fScaleX != 1.0 || fScaleY != 1.0 )
                {
                    aTmpMtf.Scale( fScaleX, fScaleY );
                    aSrcPt.setX( FRound( aSrcPt.X() * fScaleX ) );
                    aSrcPt.setY( FRound( aSrcPt.Y() * fScaleY ) );
                }

                nMoveX = aDestPt.X() - aSrcPt.X();
                nMoveY = aDestPt.Y() - aSrcPt.Y();

                if( nMoveX || nMoveY )
                    aTmpMtf.Move( nMoveX, nMoveY );

                ImplWriteActions( aTmpMtf, rVDev );
            }
            break;

            case MetaActionType::COMMENT:
            {
                const MetaCommentAction* pA = static_cast<const MetaCommentAction*>(pMA);
                if ( pA->GetComment().equalsIgnoreAsciiCase("XGRAD_SEQ_BEGIN") )
                {
                    const MetaGradientExAction* pGradAction = nullptr;
                    while( ++nCurAction < nCount )
                    {
                        MetaAction* pAction = rMtf.GetAction( nCurAction );
                        if( pAction->GetType() == MetaActionType::GRADIENTEX )
                            pGradAction = static_cast<const MetaGradientExAction*>(pAction);
                        else if( ( pAction->GetType() == MetaActionType::COMMENT ) &&
                                 ( static_cast<const MetaCommentAction*>(pAction)->GetComment().equalsIgnoreAsciiCase("XGRAD_SEQ_END") ) )
                        {
                            break;
                        }
                    }

                    if( pGradAction )
                        ImplWriteGradient( pGradAction->GetPolyPolygon(), pGradAction->GetGradient(), rVDev );
                }
                else if ( pA->GetComment() == "XPATHFILL_SEQ_END" )
                {
                    if ( aFillPath.Count() )
                    {
                        aFillPath = tools::PolyPolygon();
                        ImplWriteLine( "gr" );
                    }
                }
                else
                {
                    const sal_uInt8* pData = pA->GetData();
                    if ( pData )
                    {
                        SvMemoryStream  aMemStm( const_cast<sal_uInt8 *>(pData), pA->GetDataSize(), StreamMode::READ );
                        bool        bSkipSequence = false;
                        OString sSeqEnd;

                        if( pA->GetComment() == "XPATHSTROKE_SEQ_BEGIN" )
                        {
                            sSeqEnd = "XPATHSTROKE_SEQ_END";
                            SvtGraphicStroke aStroke;
                            ReadSvtGraphicStroke( aMemStm, aStroke );

                            tools::Polygon aPath;
                            aStroke.getPath( aPath );

                            tools::PolyPolygon aStartArrow;
                            tools::PolyPolygon aEndArrow;
                            double fStrokeWidth( aStroke.getStrokeWidth() );
                            SvtGraphicStroke::JoinType eJT( aStroke.getJoinType() );
                            SvtGraphicStroke::DashArray l_aDashArray;

                            aStroke.getStartArrow( aStartArrow );
                            aStroke.getEndArrow( aEndArrow );
                            aStroke.getDashArray( l_aDashArray );

                            bSkipSequence = true;
                            if ( l_aDashArray.size() > 11 ) // ps dasharray limit is 11
                                bSkipSequence = false;
                            if ( aStartArrow.Count() || aEndArrow.Count() )
                                bSkipSequence = false;
                            if ( static_cast<sal_uInt32>(eJT) > 2 )
                                bSkipSequence = false;
                            if ( !l_aDashArray.empty() && ( fStrokeWidth != 0.0 ) )
                                bSkipSequence = false;
                            if ( bSkipSequence )
                            {
                                ImplWriteLineInfo( fStrokeWidth, aStroke.getMiterLimit(),
                                                    aStroke.getCapType(), eJT, std::move(l_aDashArray) );
                                ImplPolyLine( aPath );
                            }
                        }
                        else if (pA->GetComment() == "XPATHFILL_SEQ_BEGIN")
                        {
                            sSeqEnd = "XPATHFILL_SEQ_END";
                            SvtGraphicFill aFill;
                            ReadSvtGraphicFill( aMemStm, aFill );
                            switch( aFill.getFillType() )
                            {
                                case SvtGraphicFill::fillSolid :
                                {
                                    bSkipSequence = true;
                                    tools::PolyPolygon aPolyPoly;
                                    aFill.getPath( aPolyPoly );
                                    sal_uInt16 i, nPolyCount = aPolyPoly.Count();
                                    if ( nPolyCount )
                                    {
                                        aFillColor = aFill.getFillColor();
                                        ImplWriteFillColor( PS_SPACE );
                                        for ( i = 0; i < nPolyCount; )
                                        {
                                            ImplAddPath( aPolyPoly.GetObject( i ) );
                                            if ( ++i < nPolyCount )
                                            {
                                                mpPS->WriteCharPtr( "p" );
                                                mnCursorPos += 2;
                                                ImplExecMode( PS_RET );
                                            }
                                        }
                                        mpPS->WriteCharPtr( "p ef" );
                                        mnCursorPos += 4;
                                        ImplExecMode( PS_RET );
                                    }
                                }
                                break;

                                case SvtGraphicFill::fillTexture :
                                {
                                    aFill.getPath( aFillPath );

                                    /* normally an object filling is consisting of three MetaActions:
                                        MetaBitmapAction        using RasterOp xor,
                                        MetaPolyPolygonAction   using RasterOp rop_0
                                        MetaBitmapAction        using RasterOp xor

                                        Because RasterOps cannot been used in Postscript, we have to
                                        replace these actions. The MetaComment "XPATHFILL_SEQ_BEGIN" is
                                        providing the clippath of the object. The following loop is
                                        trying to find the bitmap that is matching the clippath, so that
                                        only one bitmap is exported, otherwise if the bitmap is not
                                        locatable, all metaactions are played normally.
                                    */
                                    sal_uInt32 nCommentStartAction = nCurAction;
                                    sal_uInt32 nBitmapCount = 0;
                                    sal_uInt32 nBitmapAction = 0;

                                    bool bOk = true;
                                    while( bOk && ( ++nCurAction < nCount ) )
                                    {
                                        MetaAction* pAction = rMtf.GetAction( nCurAction );
                                        switch( pAction->GetType() )
                                        {
                                            case MetaActionType::BMPSCALE :
                                            case MetaActionType::BMPSCALEPART :
                                            case MetaActionType::BMPEXSCALE :
                                            case MetaActionType::BMPEXSCALEPART :
                                            {
                                                nBitmapCount++;
                                                nBitmapAction = nCurAction;
                                            }
                                            break;
                                            case MetaActionType::COMMENT :
                                            {
                                                if (static_cast<const MetaCommentAction*>(pAction)->GetComment() == "XPATHFILL_SEQ_END")
                                                    bOk = false;
                                            }
                                            break;
                                            default: break;
                                        }
                                    }
                                    if( nBitmapCount == 2 )
                                    {
                                        ImplWriteLine( "gs" );
                                        ImplIntersect( aFillPath );
                                        GDIMetaFile aTempMtf;
                                        aTempMtf.AddAction( rMtf.GetAction( nBitmapAction )->Clone() );
                                        ImplWriteActions( aTempMtf, rVDev );
                                        ImplWriteLine( "gr" );
                                        aFillPath = tools::PolyPolygon();
                                    }
                                    else
                                        nCurAction = nCommentStartAction + 1;
                                }
                                break;

                                case SvtGraphicFill::fillGradient :
                                    aFill.getPath( aFillPath );
                                break;

                                case SvtGraphicFill::fillHatch :
                                break;
                            }
                            if ( aFillPath.Count() )
                            {
                                ImplWriteLine( "gs" );
                                ImplIntersect( aFillPath );
                            }
                        }
                        if ( bSkipSequence )
                        {
                            while( ++nCurAction < nCount )
                            {
                                pMA = rMtf.GetAction( nCurAction );
                                if ( pMA->GetType() == MetaActionType::COMMENT )
                                {
                                    OString sComment( static_cast<MetaCommentAction*>(pMA)->GetComment() );
                                    if ( sComment == sSeqEnd )
                                        break;
                                }
                            }
                        }
                    }
                }
            }
            break;
            default: break;
        }
    }
}

inline void PSWriter::ImplWritePoint( const Point& rPoint )
{
    ImplWriteDouble( rPoint.X() );
    ImplWriteDouble( rPoint.Y() );
}

void PSWriter::ImplMoveTo( const Point& rPoint )
{
    ImplWritePoint( rPoint );
    ImplWriteByte( 'm' );
    ImplExecMode( PS_SPACE );
}

void PSWriter::ImplLineTo( const Point& rPoint, NMode nMode )
{
    ImplWritePoint( rPoint );
    ImplWriteByte( 'l' );
    ImplExecMode( nMode );
}

void PSWriter::ImplCurveTo( const Point& rP1, const Point& rP2, const Point& rP3, NMode nMode )
{
    ImplWritePoint( rP1 );
    ImplWritePoint( rP2 );
    ImplWritePoint( rP3 );
    mpPS->WriteCharPtr( "ct " );
    ImplExecMode( nMode );
}

void PSWriter::ImplTranslate( const double& fX, const double& fY )
{
    ImplWriteDouble( fX );
    ImplWriteDouble( fY );
    ImplWriteByte( 't' );
    ImplExecMode( PS_RET );
}

void PSWriter::ImplScale( const double& fX, const double& fY )
{
    ImplWriteDouble( fX );
    ImplWriteDouble( fY );
    ImplWriteByte( 's' );
    ImplExecMode( PS_RET );
}

void PSWriter::ImplRect( const tools::Rectangle & rRect )
{
    if ( bFillColor )
        ImplRectFill( rRect );
    if ( bLineColor )
    {
        double nWidth = rRect.GetWidth();
        double nHeight = rRect.GetHeight();

        ImplWriteLineColor( PS_SPACE );
        ImplMoveTo( rRect.TopLeft() );
        ImplWriteDouble( nWidth );
        mpPS->WriteCharPtr( "0 rl 0 " );
        ImplWriteDouble( nHeight );
        mpPS->WriteCharPtr( "rl " );
        ImplWriteDouble( nWidth );
        mpPS->WriteCharPtr( "neg 0 rl " );
        ImplClosePathDraw();
    }
    mpPS->WriteUChar( 10 );
    mnCursorPos = 0;
}

void PSWriter::ImplRectFill( const tools::Rectangle & rRect )
{
    double nWidth = rRect.GetWidth();
    double nHeight = rRect.GetHeight();

    ImplWriteFillColor( PS_SPACE );
    ImplMoveTo( rRect.TopLeft() );
    ImplWriteDouble( nWidth );
    mpPS->WriteCharPtr( "0 rl 0 " );
    ImplWriteDouble( nHeight );
    mpPS->WriteCharPtr( "rl " );
    ImplWriteDouble( nWidth );
    mpPS->WriteCharPtr( "neg 0 rl ef " );
    mpPS->WriteCharPtr( "p ef" );
    mnCursorPos += 2;
    ImplExecMode( PS_RET );
}

void PSWriter::ImplAddPath( const tools::Polygon & rPolygon )
{
    sal_uInt16 nPointCount = rPolygon.GetSize();
    if ( nPointCount <= 1 )
        return;

    sal_uInt16 i = 1;
    ImplMoveTo( rPolygon.GetPoint( 0 ) );
    while ( i < nPointCount )
    {
        if ( ( rPolygon.GetFlags( i ) == PolyFlags::Control )
                && ( ( i + 2 ) < nPointCount )
                    && ( rPolygon.GetFlags( i + 1 ) == PolyFlags::Control )
                        && ( rPolygon.GetFlags( i + 2 ) != PolyFlags::Control ) )
        {
            ImplCurveTo( rPolygon[ i ], rPolygon[ i + 1 ], rPolygon[ i + 2 ], PS_WRAP );
            i += 3;
        }
        else
            ImplLineTo( rPolygon.GetPoint( i++ ), PS_SPACE | PS_WRAP );
    }
}

void PSWriter::ImplIntersect( const tools::PolyPolygon& rPolyPoly )
{
    sal_uInt16 i, nPolyCount = rPolyPoly.Count();
    for ( i = 0; i < nPolyCount; )
    {
        ImplAddPath( rPolyPoly.GetObject( i ) );
        if ( ++i < nPolyCount )
        {
            mpPS->WriteCharPtr( "p" );
            mnCursorPos += 2;
            ImplExecMode( PS_RET );
        }
    }
    ImplWriteLine( "eoclip newpath" );
}

void PSWriter::ImplWriteGradient( const tools::PolyPolygon& rPolyPoly, const Gradient& rGradient, VirtualDevice& rVDev )
{
    ScopedVclPtrInstance< VirtualDevice > l_pVDev;
    GDIMetaFile     aTmpMtf;
    l_pVDev->SetMapMode( rVDev.GetMapMode() );
    Gradient aGradient(rGradient);
    aGradient.AddGradientActions( rPolyPoly.GetBoundRect(), aTmpMtf );
    ImplWriteActions( aTmpMtf, rVDev );
}

void PSWriter::ImplPolyPoly( const tools::PolyPolygon & rPolyPoly, bool bTextOutline )
{
    sal_uInt16 i, nPolyCount = rPolyPoly.Count();
    if ( !nPolyCount )
        return;

    if ( bFillColor || bTextOutline )
    {
        if ( bTextOutline )
            ImplWriteTextColor( PS_SPACE );
        else
            ImplWriteFillColor( PS_SPACE );
        for ( i = 0; i < nPolyCount; )
        {
            ImplAddPath( rPolyPoly.GetObject( i ) );
            if ( ++i < nPolyCount )
            {
                mpPS->WriteCharPtr( "p" );
                mnCursorPos += 2;
                ImplExecMode( PS_RET );
            }
        }
        mpPS->WriteCharPtr( "p ef" );
        mnCursorPos += 4;
        ImplExecMode( PS_RET );
    }
    if ( bLineColor )
    {
        ImplWriteLineColor( PS_SPACE );
        for ( i = 0; i < nPolyCount; i++ )
            ImplAddPath( rPolyPoly.GetObject( i ) );
        ImplClosePathDraw();
    }
}

void PSWriter::ImplPolyLine( const tools::Polygon & rPoly )
{
    if ( !bLineColor )
        return;

    ImplWriteLineColor( PS_SPACE );
    sal_uInt16 i, nPointCount = rPoly.GetSize();
    if ( !nPointCount )
        return;

    if ( nPointCount > 1 )
    {
        ImplMoveTo( rPoly.GetPoint( 0 ) );
        i = 1;
        while ( i < nPointCount )
        {
            if ( ( rPoly.GetFlags( i ) == PolyFlags::Control )
                    && ( ( i + 2 ) < nPointCount )
                        && ( rPoly.GetFlags( i + 1 ) == PolyFlags::Control )
                            && ( rPoly.GetFlags( i + 2 ) != PolyFlags::Control ) )
            {
                ImplCurveTo( rPoly[ i ], rPoly[ i + 1 ], rPoly[ i + 2 ], PS_WRAP );
                i += 3;
            }
            else
                ImplLineTo( rPoly.GetPoint( i++ ), PS_SPACE | PS_WRAP );
        }
    }

    // #104645# explicitly close path if polygon is closed
    if( rPoly[ 0 ] == rPoly[ nPointCount-1 ] )
        ImplClosePathDraw();
    else
        ImplPathDraw();
}

void PSWriter::ImplSetClipRegion( vcl::Region const & rClipRegion )
{
    if ( rClipRegion.IsEmpty() )
        return;

    RectangleVector aRectangles;
    rClipRegion.GetRegionRectangles(aRectangles);

    for (auto const& rectangle : aRectangles)
    {
        double nX1(rectangle.Left());
        double nY1(rectangle.Top());
        double nX2(rectangle.Right());
        double nY2(rectangle.Bottom());

        ImplWriteDouble( nX1 );
        ImplWriteDouble( nY1 );
        ImplWriteByte( 'm' );
        ImplWriteDouble( nX2 );
        ImplWriteDouble( nY1 );
        ImplWriteByte( 'l' );
        ImplWriteDouble( nX2 );
        ImplWriteDouble( nY2 );
        ImplWriteByte( 'l' );
        ImplWriteDouble( nX1 );
        ImplWriteDouble( nY2 );
        ImplWriteByte( 'l' );
        ImplWriteDouble( nX1 );
        ImplWriteDouble( nY1 );
        ImplWriteByte( 'l', PS_SPACE | PS_WRAP );
    }

    ImplWriteLine( "eoclip newpath" );
}

// possible gfx formats:
//
// level 1: grayscale   8 bit
//          color      24 bit
//
// level 2: grayscale   8 bit
//          color       1(pal), 4(pal), 8(pal), 24 Bit
//

void PSWriter::ImplBmp( Bitmap const * pBitmap, Bitmap const * pMaskBitmap, const Point & rPoint, double nXWidth, double nYHeightOrg )
{
    if ( !pBitmap )
        return;

    sal_Int32   nHeightOrg = pBitmap->GetSizePixel().Height();
    sal_Int32   nHeightLeft = nHeightOrg;
    tools::Long    nWidth = pBitmap->GetSizePixel().Width();
    Point   aSourcePos( rPoint );

    while ( nHeightLeft )
    {
        Bitmap  aTileBitmap( *pBitmap );
        tools::Long    nHeight = nHeightLeft;
        double  nYHeight = nYHeightOrg;

        bool    bDoTrans = false;

        tools::Rectangle   aRect;
        vcl::Region      aRegion;

        if ( pMaskBitmap )
        {
            bDoTrans = true;
            while (true)
            {
                if ( mnLevel == 1 && nHeight > 10 )
                    nHeight = 8;
                aRect = tools::Rectangle( Point( 0, nHeightOrg - nHeightLeft ), Size( nWidth, nHeight ) );
                aRegion = pMaskBitmap->CreateRegion( COL_BLACK, aRect );

                if( mnLevel == 1 )
                {
                    RectangleVector aRectangleVector;
                    aRegion.GetRegionRectangles(aRectangleVector);

                    if ( aRectangleVector.size() * 5 > 1000 )
                    {
                        nHeight >>= 1;
                        if ( nHeight < 2 )
                            return;
                        continue;
                    }
                }
                break;
            }
        }
        if ( nHeight != nHeightOrg )
        {
            nYHeight = nYHeightOrg * nHeight / nHeightOrg;
            aTileBitmap.Crop( tools::Rectangle( Point( 0, nHeightOrg - nHeightLeft ), Size( nWidth, nHeight ) ) );
        }
        if ( bDoTrans )
        {
            ImplWriteLine( "gs\npum" );
            ImplTranslate( aSourcePos.X(), aSourcePos.Y() );
            ImplScale( nXWidth / nWidth,  nYHeight / nHeight );

            RectangleVector aRectangles;
            aRegion.GetRegionRectangles(aRectangles);
            const tools::Long nMoveVertical(nHeightLeft - nHeightOrg);

            for (auto & rectangle : aRectangles)
            {
                rectangle.Move(0, nMoveVertical);

                ImplWriteLong( rectangle.Left() );
                ImplWriteLong( rectangle.Top() );
                ImplWriteByte( 'm' );
                ImplWriteLong( rectangle.Right() + 1 );
                ImplWriteLong( rectangle.Top() );
                ImplWriteByte( 'l' );
                ImplWriteLong( rectangle.Right() + 1 );
                ImplWriteLong( rectangle.Bottom() + 1 );
                ImplWriteByte( 'l' );
                ImplWriteLong( rectangle.Left() );
                ImplWriteLong( rectangle.Bottom() + 1 );
                ImplWriteByte( 'l' );
                ImplWriteByte( 'p', PS_SPACE | PS_WRAP );
            }

            ImplWriteLine( "eoclip newpath" );
            ImplWriteLine( "pom" );
        }
        BitmapReadAccess* pAcc = aTileBitmap.AcquireReadAccess();

        if (!bDoTrans )
            ImplWriteLine( "pum" );

        ImplTranslate( aSourcePos.X(), aSourcePos.Y() + nYHeight );
        ImplScale( nXWidth, nYHeight );
        if ( mnLevel == 1 )                 // level 1 is always grayscale !!!
        {
            ImplWriteLong( nWidth );
            ImplWriteLong( nHeight );
            mpPS->WriteCharPtr( "8 [" );
            ImplWriteLong( nWidth );
            mpPS->WriteCharPtr( "0 0 " );
            ImplWriteLong( -nHeight );
            ImplWriteLong( 0 );
            ImplWriteLong( nHeight );
            ImplWriteLine( "]" );
            mpPS->WriteCharPtr( "{currentfile " );
            ImplWriteLong( nWidth );
            ImplWriteLine( "string readhexstring pop}" );
            ImplWriteLine( "image" );
            for ( tools::Long y = 0; y < nHeight; y++ )
            {
                Scanline pScanlineRead = pAcc->GetScanline( y );
                for ( tools::Long x = 0; x < nWidth; x++ )
                {
                    ImplWriteHexByte( pAcc->GetIndexFromData( pScanlineRead, x ) );
                }
            }
            mpPS->WriteUChar( 10 );
        }
        else    // Level 2
        {
            if ( mbGrayScale )
            {
                ImplWriteLine( "/DeviceGray setcolorspace" );
                ImplWriteLine( "<<" );
                ImplWriteLine( "/ImageType 1" );
                mpPS->WriteCharPtr( "/Width " );
                ImplWriteLong( nWidth, PS_RET );
                mpPS->WriteCharPtr( "/Height " );
                ImplWriteLong( nHeight, PS_RET );
                ImplWriteLine( "/BitsPerComponent 8" );
                ImplWriteLine( "/Decode[0 1]" );
                mpPS->WriteCharPtr( "/ImageMatrix[" );
                ImplWriteLong( nWidth );
                mpPS->WriteCharPtr( "0 0 " );
                ImplWriteLong( -nHeight );
                ImplWriteLong( 0 );
                ImplWriteLong( nHeight, PS_NONE );
                ImplWriteByte( ']', PS_RET );
                ImplWriteLine( "/DataSource currentfile" );
                ImplWriteLine( "/ASCIIHexDecode filter" );
                if ( mbCompression )
                    ImplWriteLine( "/LZWDecode filter" );
                ImplWriteLine( ">>" );
                ImplWriteLine( "image" );
                if ( mbCompression )
                {
                    StartCompression();
                    for ( tools::Long y = 0; y < nHeight; y++ )
                    {
                        Scanline pScanlineRead = pAcc->GetScanline( y );
                        for ( tools::Long x = 0; x < nWidth; x++ )
                        {
                            Compress( pAcc->GetIndexFromData( pScanlineRead, x ) );
                        }
                    }
                    EndCompression();
                }
                else
                {
                    for ( tools::Long y = 0; y < nHeight; y++ )
                    {
                        Scanline pScanlineRead = pAcc->GetScanline( y );
                        for ( tools::Long x = 0; x < nWidth; x++ )
                        {
                            ImplWriteHexByte( pAcc->GetIndexFromData( pScanlineRead, x ) );
                        }
                    }
                }
            }
            else
            {
                // have we to write a palette ?

                if ( pAcc->HasPalette() )
                {
                    ImplWriteLine( "[/Indexed /DeviceRGB " );
                    ImplWriteLong( pAcc->GetPaletteEntryCount() - 1, PS_RET );
                    ImplWriteByte( '<', PS_NONE );
                    for ( sal_uInt16 i = 0; i < pAcc->GetPaletteEntryCount(); i++ )
                    {
                        BitmapColor aBitmapColor = pAcc->GetPaletteColor( i );
                        ImplWriteHexByte( aBitmapColor.GetRed(), PS_NONE );
                        ImplWriteHexByte( aBitmapColor.GetGreen(), PS_NONE );
                        ImplWriteHexByte( aBitmapColor.GetBlue(), PS_SPACE | PS_WRAP );
                    }
                    ImplWriteByte( '>', PS_RET );

                    ImplWriteLine( "] setcolorspace" );
                    ImplWriteLine( "<<" );
                    ImplWriteLine( "/ImageType 1" );
                    mpPS->WriteCharPtr( "/Width " );
                    ImplWriteLong( nWidth, PS_RET );
                    mpPS->WriteCharPtr( "/Height " );
                    ImplWriteLong( nHeight, PS_RET );
                    ImplWriteLine( "/BitsPerComponent 8" );
                    ImplWriteLine( "/Decode[0 255]" );
                    mpPS->WriteCharPtr( "/ImageMatrix[" );
                    ImplWriteLong( nWidth );
                    mpPS->WriteCharPtr( "0 0 " );
                    ImplWriteLong( -nHeight );
                    ImplWriteLong( 0);
                    ImplWriteLong( nHeight, PS_NONE );
                    ImplWriteByte( ']', PS_RET );
                    ImplWriteLine( "/DataSource currentfile" );
                    ImplWriteLine( "/ASCIIHexDecode filter" );
                    if ( mbCompression )
                        ImplWriteLine( "/LZWDecode filter" );
                    ImplWriteLine( ">>" );
                    ImplWriteLine( "image" );
                    if ( mbCompression )
                    {
                        StartCompression();
                        for ( tools::Long y = 0; y < nHeight; y++ )
                        {
                            Scanline pScanlineRead = pAcc->GetScanline( y );
                            for ( tools::Long x = 0; x < nWidth; x++ )
                            {
                                Compress( pAcc->GetIndexFromData( pScanlineRead, x ) );
                            }
                        }
                        EndCompression();
                    }
                    else
                    {
                        for ( tools::Long y = 0; y < nHeight; y++ )
                        {
                            Scanline pScanlineRead = pAcc->GetScanline( y );
                            for ( tools::Long x = 0; x < nWidth; x++ )
                            {
                                ImplWriteHexByte( pAcc->GetIndexFromData( pScanlineRead, x ) );
                            }
                        }
                    }
                }
                else // 24 bit color
                {
                    ImplWriteLine( "/DeviceRGB setcolorspace" );
                    ImplWriteLine( "<<" );
                    ImplWriteLine( "/ImageType 1" );
                    mpPS->WriteCharPtr( "/Width " );
                    ImplWriteLong( nWidth, PS_RET );
                    mpPS->WriteCharPtr( "/Height " );
                    ImplWriteLong( nHeight, PS_RET );
                    ImplWriteLine( "/BitsPerComponent 8" );
                    ImplWriteLine( "/Decode[0 1 0 1 0 1]" );
                    mpPS->WriteCharPtr( "/ImageMatrix[" );
                    ImplWriteLong( nWidth );
                    mpPS->WriteCharPtr( "0 0 " );
                    ImplWriteLong( -nHeight );
                    ImplWriteLong( 0 );
                    ImplWriteLong( nHeight, PS_NONE );
                    ImplWriteByte( ']', PS_RET );
                    ImplWriteLine( "/DataSource currentfile" );
                    ImplWriteLine( "/ASCIIHexDecode filter" );
                    if ( mbCompression )
                        ImplWriteLine( "/LZWDecode filter" );
                    ImplWriteLine( ">>" );
                    ImplWriteLine( "image" );
                    if ( mbCompression )
                    {
                        StartCompression();
                        for ( tools::Long y = 0; y < nHeight; y++ )
                        {
                            Scanline pScanlineRead = pAcc->GetScanline( y );
                            for ( tools::Long x = 0; x < nWidth; x++ )
                            {
                                const BitmapColor aBitmapColor( pAcc->GetPixelFromData( pScanlineRead, x ) );
                                Compress( aBitmapColor.GetRed() );
                                Compress( aBitmapColor.GetGreen() );
                                Compress( aBitmapColor.GetBlue() );
                            }
                        }
                        EndCompression();
                    }
                    else
                    {
                        for ( tools::Long y = 0; y < nHeight; y++ )
                        {
                            Scanline pScanline = pAcc->GetScanline( y );
                            for ( tools::Long x = 0; x < nWidth; x++ )
                            {
                                const BitmapColor aBitmapColor( pAcc->GetPixelFromData( pScanline, x ) );
                                ImplWriteHexByte( aBitmapColor.GetRed() );
                                ImplWriteHexByte( aBitmapColor.GetGreen() );
                                ImplWriteHexByte( aBitmapColor.GetBlue() );
                            }
                        }
                    }
                }
            }
            ImplWriteLine( ">" );       // in Level 2 the dictionary needs to be closed (eod)
        }
        if ( bDoTrans )
            ImplWriteLine( "gr" );
        else
            ImplWriteLine( "pom" );

        Bitmap::ReleaseAccess( pAcc );
        nHeightLeft -= nHeight;
        if ( nHeightLeft )
        {
            nHeightLeft++;
            aSourcePos.setY( static_cast<tools::Long>( rPoint.Y() + ( nYHeightOrg * ( nHeightOrg - nHeightLeft ) ) / nHeightOrg ) );
        }
    }
}

void PSWriter::ImplWriteCharacter( char nChar )
{
    switch( nChar )
    {
        case '(' :
        case ')' :
        case '\\' :
            ImplWriteByte( sal_uInt8('\\'), PS_NONE );
    }
    ImplWriteByte( static_cast<sal_uInt8>(nChar), PS_NONE );
}

void PSWriter::ImplWriteString( const OString& rString, VirtualDevice const & rVDev, KernArraySpan pDXArry, bool bStretch )
{
    sal_Int32 nLen = rString.getLength();
    if ( !nLen )
        return;

    if ( !pDXArry.empty() )
    {
        double nx = 0;

        for (sal_Int32 i = 0; i < nLen; ++i)
        {
            if ( i > 0 )
                nx = pDXArry[ i - 1 ];
            ImplWriteDouble( bStretch ? nx : rVDev.GetTextWidth( OUString(rString[i]) ) );
            ImplWriteDouble( nx );
            ImplWriteLine( "(", PS_NONE );
            ImplWriteCharacter( rString[i] );
            ImplWriteLine( ") bs" );
        }
    }
    else
    {
        ImplWriteByte( '(', PS_NONE );
        for (sal_Int32 i = 0; i < nLen; ++i)
            ImplWriteCharacter( rString[i] );
        ImplWriteLine( ") sw" );
    }
}

void PSWriter::ImplText( const OUString& rUniString, const Point& rPos, KernArraySpan pDXArry, o3tl::span<const sal_Bool> pKashidaArry, sal_Int32 nWidth, VirtualDevice const & rVDev )
{
    if ( rUniString.isEmpty() )
        return;
    if ( mnTextMode == 0 )  // using glyph outlines
    {
        vcl::Font    aNotRotatedFont( maFont );
        aNotRotatedFont.SetOrientation( 0_deg10 );

        ScopedVclPtrInstance< VirtualDevice > pVirDev(DeviceFormat::DEFAULT);
        pVirDev->SetMapMode( rVDev.GetMapMode() );
        pVirDev->SetFont( aNotRotatedFont );
        pVirDev->SetTextAlign( eTextAlign );

        Degree10 nRotation = maFont.GetOrientation();
        tools::Polygon aPolyDummy( 1 );

        Point aPos( rPos );
        if ( nRotation )
        {
            aPolyDummy.SetPoint( aPos, 0 );
            aPolyDummy.Rotate( rPos, nRotation );
            aPos = aPolyDummy.GetPoint( 0 );
        }
        bool bOldLineColor = bLineColor;
        bLineColor = false;
        std::vector<tools::PolyPolygon> aPolyPolyVec;
        if ( pVirDev->GetTextOutlines( aPolyPolyVec, rUniString, 0, 0, -1, nWidth, pDXArry, pKashidaArry ) )
        {
            // always adjust text position to match baseline alignment
            ImplWriteLine( "pum" );
            ImplWriteDouble( aPos.X() );
            ImplWriteDouble( aPos.Y() );
            ImplWriteLine( "t" );
            if ( nRotation )
            {
                ImplWriteF( nRotation.get(), 1 );
                mpPS->WriteCharPtr( "r " );
            }
            for (auto const& elem : aPolyPolyVec)
                ImplPolyPoly( elem, true );
            ImplWriteLine( "pom" );
        }
        bLineColor = bOldLineColor;
    }
    else if ( ( mnTextMode == 1 ) || ( mnTextMode == 2 ) )  // normal text output
    {
        if ( mnTextMode == 2 )  // forcing output one complete text packet, by
            pDXArry = {};       // ignoring the kerning array
        ImplSetAttrForText( rPos );
        OString aStr(OUStringToOString(rUniString,
            maFont.GetCharSet()));
        ImplWriteString( aStr, rVDev, pDXArry, nWidth != 0 );
        if ( maFont.GetOrientation() )
            ImplWriteLine( "gr" );
    }
}

void PSWriter::ImplSetAttrForText( const Point& rPoint )
{
    Point aPoint( rPoint );

    Degree10 nRotation = maFont.GetOrientation();
    ImplWriteTextColor(PS_RET);

    Size aSize = maFont.GetFontSize();

    if ( maLastFont != maFont )
    {
        if ( maFont.GetPitch() == PITCH_FIXED )         // a little bit font selection
            ImplDefineFont( "Courier", "Oblique" );
        else if ( maFont.GetCharSet() == RTL_TEXTENCODING_SYMBOL )
            ImplWriteLine( "/Symbol findfont" );
        else if ( maFont.GetFamilyType() == FAMILY_SWISS )
            ImplDefineFont( "Helvetica", "Oblique" );
        else
            ImplDefineFont( "Times", "Italic" );

        maLastFont = maFont;
        aSize = maFont.GetFontSize();
        ImplWriteDouble( aSize.Height() );
        mpPS->WriteCharPtr( "sf " );
    }
    if ( eTextAlign != ALIGN_BASELINE )
    {                                                       // PostScript does not know about FontAlignment
        if ( eTextAlign == ALIGN_TOP )                      // -> so I assume that
            aPoint.AdjustY( aSize.Height() * 4 / 5 );       // the area under the baseline
        else if ( eTextAlign == ALIGN_BOTTOM )              // is about 20% of the font size
            aPoint.AdjustY( -( aSize.Height() / 5 ) );
    }
    ImplMoveTo( aPoint );
    if ( nRotation )
    {
        mpPS->WriteCharPtr( "gs " );
        ImplWriteF( nRotation.get(), 1 );
        mpPS->WriteCharPtr( "r " );
    }
}

void PSWriter::ImplDefineFont( const char* pOriginalName, const char* pItalic )
{
    mpPS->WriteUChar( '/' );             //convert the font pOriginalName using ISOLatin1Encoding
    mpPS->WriteCharPtr( pOriginalName );
    switch ( maFont.GetWeight() )
    {
        case WEIGHT_SEMIBOLD :
        case WEIGHT_BOLD :
        case WEIGHT_ULTRABOLD :
        case WEIGHT_BLACK :
            mpPS->WriteCharPtr( "-Bold" );
            if ( maFont.GetItalic() != ITALIC_NONE )
                mpPS->WriteCharPtr( pItalic );
            break;
        default:
            if ( maFont.GetItalic() != ITALIC_NONE )
                mpPS->WriteCharPtr( pItalic );
            break;
    }
    ImplWriteLine( " f" );
}

void PSWriter::ImplClosePathDraw()
{
    mpPS->WriteCharPtr( "pc" );
    mnCursorPos += 2;
    ImplExecMode( PS_RET );
}

void PSWriter::ImplPathDraw()
{
    mpPS->WriteCharPtr( "ps" );
    mnCursorPos += 2;
    ImplExecMode( PS_RET );
}


inline void PSWriter::ImplWriteLineColor( NMode nMode )
{
    if ( aColor != aLineColor )
    {
        aColor = aLineColor;
        ImplWriteColor( nMode );
    }
}

inline void PSWriter::ImplWriteFillColor( NMode nMode )
{
    if ( aColor != aFillColor )
    {
        aColor = aFillColor;
        ImplWriteColor( nMode );
    }
}

inline void PSWriter::ImplWriteTextColor( NMode nMode )
{
    if ( aColor != aTextColor )
    {
        aColor = aTextColor;
        ImplWriteColor( nMode );
    }
}

void PSWriter::ImplWriteColor( NMode nMode )
{
    if ( mbGrayScale )
    {
        // writes the Color (grayscale) as a Number from 0.000 up to 1.000

        ImplWriteF( 1000 * ( aColor.GetRed() * 77 + aColor.GetGreen() * 151 +
            aColor.GetBlue() * 28 + 1 ) / 65536, 3, nMode );
    }
    else
    {
        ImplWriteB1 ( aColor.GetRed() );
        ImplWriteB1 ( aColor.GetGreen() );
        ImplWriteB1 ( aColor.GetBlue() );
    }
    mpPS->WriteCharPtr( "c" );                               // ( c is defined as setrgbcolor or setgray )
    ImplExecMode( nMode );
}

void PSWriter::ImplGetMapMode( const MapMode& rMapMode )
{
    ImplWriteLine( "tm setmatrix" );
    double fMul = ImplGetScaling(rMapMode);
    double fScaleX = static_cast<double>(rMapMode.GetScaleX()) * fMul;
    double fScaleY = static_cast<double>(rMapMode.GetScaleY()) * fMul;
    ImplTranslate( rMapMode.GetOrigin().X() * fScaleX, rMapMode.GetOrigin().Y() * fScaleY );
    ImplScale( fScaleX, fScaleY );
}

inline void PSWriter::ImplExecMode( NMode nMode )
{
    if ( nMode & PS_WRAP )
    {
        if ( mnCursorPos >= PS_LINESIZE )
        {
            mnCursorPos = 0;
            mpPS->WriteUChar( 0xa );
            return;
        }
    }
    if ( nMode & PS_SPACE )
    {
            mpPS->WriteUChar( 32 );
            mnCursorPos++;
    }
    if ( nMode & PS_RET )
    {
        mpPS->WriteUChar( 0xa );
        mnCursorPos = 0;
    }
}

inline void PSWriter::ImplWriteLine( const char* pString, NMode nMode )
{
    sal_uInt32 i = 0;
    while ( pString[ i ] )
    {
        mpPS->WriteUChar( pString[ i++ ] );
    }
    mnCursorPos += i;
    ImplExecMode( nMode );
}

double PSWriter::ImplGetScaling( const MapMode& rMapMode )
{
    double nMul;
    switch (rMapMode.GetMapUnit())
    {
        case MapUnit::MapPixel :
        case MapUnit::MapSysFont :
        case MapUnit::MapAppFont :

        case MapUnit::Map100thMM :
            nMul = 1;
            break;
        case MapUnit::Map10thMM :
            nMul = 10;
            break;
        case MapUnit::MapMM :
            nMul = 100;
            break;
        case MapUnit::MapCM :
            nMul = 1000;
            break;
        case MapUnit::Map1000thInch :
            nMul = 2.54;
            break;
        case MapUnit::Map100thInch :
            nMul = 25.4;
            break;
        case MapUnit::Map10thInch :
            nMul = 254;
            break;
        case MapUnit::MapInch :
            nMul = 2540;
            break;
        case MapUnit::MapTwip :
            nMul = 1.76388889;
            break;
        case MapUnit::MapPoint :
            nMul = 35.27777778;
            break;
        default:
            nMul = 1.0;
            break;
    }
    return nMul;
}


void PSWriter::ImplWriteLineInfo( double fLWidth, double fMLimit,
                                  SvtGraphicStroke::CapType eLCap,
                                  SvtGraphicStroke::JoinType eJoin,
                                  SvtGraphicStroke::DashArray && rLDash )
{
    if ( fLineWidth != fLWidth )
    {
        fLineWidth = fLWidth;
        ImplWriteDouble( fLineWidth );
        ImplWriteLine( "lw", PS_SPACE );
    }
    if ( eLineCap != eLCap )
    {
        eLineCap = eLCap;
        ImplWriteLong( static_cast<sal_Int32>(eLineCap) );
        ImplWriteLine( "lc", PS_SPACE );
    }
    if ( eJoinType != eJoin )
    {
        eJoinType = eJoin;
        ImplWriteLong( static_cast<sal_Int32>(eJoinType) );
        ImplWriteLine( "lj", PS_SPACE );
    }
    if ( eJoinType == SvtGraphicStroke::joinMiter )
    {
        if ( fMiterLimit != fMLimit )
        {
            fMiterLimit = fMLimit;
            ImplWriteDouble( fMiterLimit );
            ImplWriteLine( "ml", PS_SPACE );
        }
    }
    if ( aDashArray != rLDash )
    {
        aDashArray = std::move(rLDash);
        sal_uInt32 j, i = aDashArray.size();
        ImplWriteLine( "[", PS_SPACE );
        for ( j = 0; j < i; j++ )
            ImplWriteDouble( aDashArray[ j ] );
        ImplWriteLine( "] 0 ld" );
    }
}

void PSWriter::ImplWriteLineInfo( const LineInfo& rLineInfo )
{
    std::vector< double > l_aDashArray;
    if ( rLineInfo.GetStyle() == LineStyle::Dash )
        l_aDashArray = rLineInfo.GetDotDashArray();
    const double fLWidth(( ( rLineInfo.GetWidth() + 1 ) + ( rLineInfo.GetWidth() + 1 ) ) * 0.5);
    SvtGraphicStroke::JoinType aJoinType(SvtGraphicStroke::joinMiter);
    SvtGraphicStroke::CapType aCapType(SvtGraphicStroke::capButt);

    switch(rLineInfo.GetLineJoin())
    {
        case basegfx::B2DLineJoin::NONE:
            // do NOT use SvtGraphicStroke::joinNone here
            // since it will be written as numerical value directly
            // and is NOT a valid EPS value
            break;
        case basegfx::B2DLineJoin::Miter:
            aJoinType = SvtGraphicStroke::joinMiter;
            break;
        case basegfx::B2DLineJoin::Bevel:
            aJoinType = SvtGraphicStroke::joinBevel;
            break;
        case basegfx::B2DLineJoin::Round:
            aJoinType = SvtGraphicStroke::joinRound;
            break;
    }
    switch(rLineInfo.GetLineCap())
    {
        default: /* css::drawing::LineCap_BUTT */
        {
            aCapType = SvtGraphicStroke::capButt;
            break;
        }
        case css::drawing::LineCap_ROUND:
        {
            aCapType = SvtGraphicStroke::capRound;
            break;
        }
        case css::drawing::LineCap_SQUARE:
        {
            aCapType = SvtGraphicStroke::capSquare;
            break;
        }
    }

    ImplWriteLineInfo( fLWidth, fMiterLimit, aCapType, aJoinType, std::move(l_aDashArray) );
}

void PSWriter::ImplWriteLong(sal_Int32 nNumber, NMode nMode)
{
    const OString aNumber(OString::number(nNumber));
    mnCursorPos += aNumber.getLength();
    mpPS->WriteOString( aNumber );
    ImplExecMode(nMode);
}

void PSWriter::ImplWriteDouble( double fNumber )
{
    sal_Int32   nPTemp = static_cast<sal_Int32>(fNumber);
    sal_Int32   nATemp = std::abs( static_cast<sal_Int32>( ( fNumber - nPTemp ) * 100000 ) );

    if ( !nPTemp && nATemp && ( fNumber < 0.0 ) )
        mpPS->WriteChar( '-' );

    const OString aNumber1(OString::number(nPTemp));
    mpPS->WriteOString( aNumber1 );
    mnCursorPos += aNumber1.getLength();

    if ( nATemp )
    {
        int zCount = 0;
        mpPS->WriteUChar( '.' );
        mnCursorPos++;
        const OString aNumber2(OString::number(nATemp));

        sal_Int16 n, nLen = aNumber2.getLength();
        if ( nLen < 8 )
        {
            mnCursorPos += 6 - nLen;
            for ( n = 0; n < ( 5 - nLen ); n++ )
            {
                mpPS->WriteUChar( '0' );
            }
        }
        mnCursorPos += nLen;
        for ( n = 0; n < nLen; n++ )
        {
            mpPS->WriteChar( aNumber2[n] );
            zCount--;
            if ( aNumber2[n] != '0' )
                zCount = 0;
        }
        if ( zCount )
            mpPS->SeekRel( zCount );
    }
    ImplExecMode( PS_SPACE );
}

/// Writes the number to stream: nNumber / ( 10^nCount )
void PSWriter::ImplWriteF( sal_Int32 nNumber, sal_uInt8 nCount, NMode nMode )
{
    if ( nNumber < 0 )
    {
        mpPS->WriteUChar( '-' );
        nNumber = -nNumber;
        mnCursorPos++;
    }
    const OString aScaleFactor(OString::number(nNumber));
    sal_uInt32 nLen = aScaleFactor.getLength();
    sal_Int32 const nStSize = (nCount + 1) - nLen;
    static_assert(sizeof(nStSize) == sizeof((nCount + 1) - nLen)); // tdf#134667
    if ( nStSize >= 1 )
    {
        mpPS->WriteUChar( '0' );
        mnCursorPos++;
    }
    if ( nStSize >= 2 )
    {
        mpPS->WriteUChar( '.' );
        for (sal_Int32 i = 1; i < nStSize; ++i)
        {
            mpPS->WriteUChar( '0' );
            mnCursorPos++;
        }
    }
    mnCursorPos += nLen;
    for( sal_uInt32 n = 0; n < nLen; n++  )
    {
        if ( n == nLen - nCount )
        {
            mpPS->WriteUChar( '.' );
            mnCursorPos++;
        }
        mpPS->WriteChar( aScaleFactor[n] );
    }
    ImplExecMode( nMode );
}

void PSWriter::ImplWriteByte( sal_uInt8 nNumb, NMode nMode )
{
    mpPS->WriteUChar( nNumb );
    mnCursorPos++;
    ImplExecMode( nMode );
}

void PSWriter::ImplWriteHexByte( sal_uInt8 nNumb, NMode nMode )
{
    if ( ( nNumb >> 4 ) > 9 )
        mpPS->WriteUChar( ( nNumb >> 4 ) + 'A' - 10 );
    else
        mpPS->WriteUChar( ( nNumb >> 4 ) + '0' );

    if ( ( nNumb & 0xf ) > 9 )
        mpPS->WriteUChar( ( nNumb & 0xf ) + 'A' - 10 );
    else
        mpPS->WriteUChar( ( nNumb & 0xf ) + '0' );
    mnCursorPos += 2;
    ImplExecMode( nMode );
}

// writes the sal_uInt8 nNumb as a Number from 0.000 up to 1.000

void PSWriter::ImplWriteB1( sal_uInt8 nNumb )
{
    ImplWriteF( 1000 * ( nNumb + 1 ) / 256  );
}

inline void PSWriter::WriteBits( sal_uInt16 nCode, sal_uInt16 nCodeLen )
{
    dwShift |= ( nCode << ( nOffset - nCodeLen ) );
    nOffset -= nCodeLen;
    while ( nOffset < 24 )
    {
        ImplWriteHexByte( static_cast<sal_uInt8>( dwShift >> 24 ) );
        dwShift <<= 8;
        nOffset += 8;
    }
    if ( nCode == 257 && nOffset != 32 )
        ImplWriteHexByte( static_cast<sal_uInt8>( dwShift >> 24 ) );
}

void PSWriter::StartCompression()
{
    sal_uInt16 i;
    nDataSize = 8;

    nClearCode = 1 << nDataSize;
    nEOICode = nClearCode + 1;
    nTableSize = nEOICode + 1;
    nCodeSize = nDataSize + 1;

    nOffset = 32;                       // number of free unused in dwShift
    dwShift = 0;

    pTable.reset(new PSLZWCTreeNode[ 4096 ]);

    for ( i = 0; i < 4096; i++ )
    {
        pTable[ i ].pBrother = pTable[ i ].pFirstChild = nullptr;
        pTable[ i ].nCode = i;
        pTable[ i ].nValue = static_cast<sal_uInt8>( i );
    }
    pPrefix = nullptr;
    WriteBits( nClearCode, nCodeSize );
}

void PSWriter::Compress( sal_uInt8 nCompThis )
{
    PSLZWCTreeNode*     p;
    sal_uInt16              i;
    sal_uInt8               nV;

    if( !pPrefix )
    {
        pPrefix = pTable.get() + nCompThis;
    }
    else
    {
        nV = nCompThis;
        for( p = pPrefix->pFirstChild; p != nullptr; p = p->pBrother )
        {
            if ( p->nValue == nV )
                break;
        }

        if( p )
            pPrefix = p;
        else
        {
            WriteBits( pPrefix->nCode, nCodeSize );

            if ( nTableSize == 409 )
            {
                WriteBits( nClearCode, nCodeSize );

                for ( i = 0; i < nClearCode; i++ )
                    pTable[ i ].pFirstChild = nullptr;

                nCodeSize = nDataSize + 1;
                nTableSize = nEOICode + 1;
            }
            else
            {
                if( nTableSize == static_cast<sal_uInt16>( ( 1 << nCodeSize ) - 1 ) )
                    nCodeSize++;

                p = pTable.get() + ( nTableSize++ );
                p->pBrother = pPrefix->pFirstChild;
                pPrefix->pFirstChild = p;
                p->nValue = nV;
                p->pFirstChild = nullptr;
            }

            pPrefix = pTable.get() + nV;
        }
    }
}

void PSWriter::EndCompression()
{
    if( pPrefix )
        WriteBits( pPrefix->nCode, nCodeSize );

    WriteBits( nEOICode, nCodeSize );
    pTable.reset();
}

sal_uInt8* PSWriter::ImplSearchEntry( sal_uInt8* pSource, sal_uInt8 const * pDest, sal_uInt32 nComp, sal_uInt32 nSize )
{
    while ( nComp-- >= nSize )
    {
        sal_uInt64 i;
        for ( i = 0; i < nSize; i++ )
        {
            if ( ( pSource[i]&~0x20 ) != ( pDest[i]&~0x20 ) )
                break;
        }
        if ( i == nSize )
            return pSource;
        pSource++;
    }
    return nullptr;
}

bool PSWriter::ImplGetBoundingBox( double* nNumb, sal_uInt8* pSource, sal_uInt32 nSize )
{
    bool    bRetValue = false;
    sal_uInt32   nBytesRead;

    if ( nSize < 256 )      // we assume that the file is greater than 256 bytes
        return false;

    if ( nSize < POSTSCRIPT_BOUNDINGSEARCH )
        nBytesRead = nSize;
    else
        nBytesRead = POSTSCRIPT_BOUNDINGSEARCH;

    sal_uInt8* pDest = ImplSearchEntry( pSource, reinterpret_cast<sal_uInt8 const *>("%%BoundingBox:"), nBytesRead, 14 );
    if ( pDest )
    {
        int     nSecurityCount = 100;   // only 100 bytes following the bounding box will be checked
        nNumb[0] = nNumb[1] = nNumb[2] = nNumb[3] = 0;
        pDest += 14;
        for ( int i = 0; ( i < 4 ) && nSecurityCount; i++ )
        {
            int     nDivision = 1;
            bool    bDivision = false;
            bool    bNegative = false;
            bool    bValid = true;

            while ( ( --nSecurityCount ) && ( ( *pDest == ' ' ) || ( *pDest == 0x9 ) ) )
                pDest++;
            sal_uInt8 nByte = *pDest;
            while ( nSecurityCount && ( nByte != ' ' ) && ( nByte != 0x9 ) && ( nByte != 0xd ) && ( nByte != 0xa ) )
            {
                switch ( nByte )
                {
                    case '.' :
                        if ( bDivision )
                            bValid = false;
                        else
                            bDivision = true;
                        break;
                    case '-' :
                        bNegative = true;
                        break;
                    default :
                        if ( ( nByte < '0' ) || ( nByte > '9' ) )
                            nSecurityCount = 1;     // error parsing the bounding box values
                        else if ( bValid )
                        {
                            if ( bDivision )
                                nDivision*=10;
                            nNumb[i] *= 10;
                            nNumb[i] += nByte - '0';
                        }
                        break;
                }
                nSecurityCount--;
                nByte = *(++pDest);
            }
            if ( bNegative )
                nNumb[i] = -nNumb[i];
            if ( bDivision && ( nDivision != 1 ) )
                nNumb[i] /= nDivision;
        }
        if ( nSecurityCount)
            bRetValue = true;
    }
    return bRetValue;
}

//================== GraphicExport - the exported function ===================

bool ExportEpsGraphic(SvStream & rStream, const Graphic & rGraphic, FilterConfigItem* pFilterConfigItem)
{
    PSWriter aPSWriter;
    return aPSWriter.WritePS(rGraphic, rStream, pFilterConfigItem);
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
