/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2000, 2010 Oracle and/or its affiliates.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/


#include "emfwr.hxx"
#include <rtl/strbuf.hxx>
#include <vcl/salbtype.hxx>
#include <basegfx/polygon/b2dpolygon.hxx>
#include <basegfx/polygon/b2dpolypolygon.hxx>
#include <vcl/lineinfo.hxx>
#include <vcl/rendergraphicrasterizer.hxx>

// -----------
// - Defines -
// -----------

#define WIN_EMR_HEADER                      1
#define WIN_EMR_POLYBEZIER                  2
#define WIN_EMR_POLYGON                     3
#define WIN_EMR_POLYLINE                    4
#define WIN_EMR_POLYBEZIERTO                5
#define WIN_EMR_POLYLINETO                  6
#define WIN_EMR_POLYPOLYLINE                7
#define WIN_EMR_POLYPOLYGON                 8
#define WIN_EMR_SETWINDOWEXTEX              9
#define WIN_EMR_SETWINDOWORGEX              10
#define WIN_EMR_SETVIEWPORTEXTEX            11
#define WIN_EMR_SETVIEWPORTORGEX            12
#define WIN_EMR_SETBRUSHORGEX               13
#define WIN_EMR_EOF                         14
#define WIN_EMR_SETPIXELV                   15
#define WIN_EMR_SETMAPPERFLAGS              16
#define WIN_EMR_SETMAPMODE                  17
#define WIN_EMR_SETBKMODE                   18
#define WIN_EMR_SETPOLYFILLMODE             19
#define WIN_EMR_SETROP2                     20
#define WIN_EMR_SETSTRETCHBLTMODE           21
#define WIN_EMR_SETTEXTALIGN                22
#define WIN_EMR_SETCOLORADJUSTMENT          23
#define WIN_EMR_SETTEXTCOLOR                24
#define WIN_EMR_SETBKCOLOR                  25
#define WIN_EMR_OFFSETCLIPRGN               26
#define WIN_EMR_MOVETOEX                    27
#define WIN_EMR_SETMETARGN                  28
#define WIN_EMR_EXCLUDECLIPRECT             29
#define WIN_EMR_INTERSECTCLIPRECT           30
#define WIN_EMR_SCALEVIEWPORTEXTEX          31
#define WIN_EMR_SCALEWINDOWEXTEX            32
#define WIN_EMR_SAVEDC                      33
#define WIN_EMR_RESTOREDC                   34
#define WIN_EMR_SETWORLDTRANSFORM           35
#define WIN_EMR_MODIFYWORLDTRANSFORM        36
#define WIN_EMR_SELECTOBJECT                37
#define WIN_EMR_CREATEPEN                   38
#define WIN_EMR_CREATEBRUSHINDIRECT         39
#define WIN_EMR_DELETEOBJECT                40
#define WIN_EMR_ANGLEARC                    41
#define WIN_EMR_ELLIPSE                     42
#define WIN_EMR_RECTANGLE                   43
#define WIN_EMR_ROUNDRECT                   44
#define WIN_EMR_ARC                         45
#define WIN_EMR_CHORD                       46
#define WIN_EMR_PIE                         47
#define WIN_EMR_SELECTPALETTE               48
#define WIN_EMR_CREATEPALETTE               49
#define WIN_EMR_SETPALETTEENTRIES           50
#define WIN_EMR_RESIZEPALETTE               51
#define WIN_EMR_REALIZEPALETTE              52
#define WIN_EMR_EXTFLOODFILL                53
#define WIN_EMR_LINETO                      54
#define WIN_EMR_ARCTO                       55
#define WIN_EMR_POLYDRAW                    56
#define WIN_EMR_SETARCDIRECTION             57
#define WIN_EMR_SETMITERLIMIT               58
#define WIN_EMR_BEGINPATH                   59
#define WIN_EMR_ENDPATH                     60
#define WIN_EMR_CLOSEFIGURE                 61
#define WIN_EMR_FILLPATH                    62
#define WIN_EMR_STROKEANDFILLPATH           63
#define WIN_EMR_STROKEPATH                  64
#define WIN_EMR_FLATTENPATH                 65
#define WIN_EMR_WIDENPATH                   66
#define WIN_EMR_SELECTCLIPPATH              67
#define WIN_EMR_ABORTPATH                   68

#define WIN_EMR_GDICOMMENT                  70
#define WIN_EMR_FILLRGN                     71
#define WIN_EMR_FRAMERGN                    72
#define WIN_EMR_INVERTRGN                   73
#define WIN_EMR_PAINTRGN                    74
#define WIN_EMR_EXTSELECTCLIPRGN            75
#define WIN_EMR_BITBLT                      76
#define WIN_EMR_STRETCHBLT                  77
#define WIN_EMR_MASKBLT                     78
#define WIN_EMR_PLGBLT                      79
#define WIN_EMR_SETDIBITSTODEVICE           80
#define WIN_EMR_STRETCHDIBITS               81
#define WIN_EMR_EXTCREATEFONTINDIRECTW      82
#define WIN_EMR_EXTTEXTOUTA                 83
#define WIN_EMR_EXTTEXTOUTW                 84
#define WIN_EMR_POLYBEZIER16                85
#define WIN_EMR_POLYGON16                   86
#define WIN_EMR_POLYLINE16                  87
#define WIN_EMR_POLYBEZIERTO16              88
#define WIN_EMR_POLYLINETO16                89
#define WIN_EMR_POLYPOLYLINE16              90
#define WIN_EMR_POLYPOLYGON16               91
#define WIN_EMR_POLYDRAW16                  92
#define WIN_EMR_CREATEMONOBRUSH             93
#define WIN_EMR_CREATEDIBPATTERNBRUSHPT     94
#define WIN_EMR_EXTCREATEPEN                95
#define WIN_EMR_POLYTEXTOUTA                96
#define WIN_EMR_POLYTEXTOUTW                97

#define WIN_SRCCOPY                         0x00CC0020L
#define WIN_SRCPAINT                        0x00EE0086L
#define WIN_SRCAND                          0x008800C6L
#define WIN_SRCINVERT                       0x00660046L

#define HANDLE_INVALID                      0xffffffff
#define MAXHANDLES                          65000

#define LINE_SELECT                         0x00000001
#define FILL_SELECT                         0x00000002
#define TEXT_SELECT                         0x00000004

/* Text Alignment Options */
#define TA_NOUPDATECP                       0
#define TA_UPDATECP                         1

#define TA_LEFT                             0
#define TA_RIGHT                            2
#define TA_CENTER                           6

#define TA_TOP                              0
#define TA_BOTTOM                           8
#define TA_BASELINE                         24
#define TA_RTLREADING                       256
#define TA_MASK     (TA_BASELINE+TA_CENTER+TA_UPDATECP+TA_RTLREADING)

#define MM_ANISOTROPIC                      8

// -------------
// - EMFWriter -
// -------------

sal_Bool EMFWriter::WriteEMF( const GDIMetaFile& rMtf, FilterConfigItem* pFilterConfigItem )
{
    const sal_uLong nHeaderPos = m_rStm.Tell();

    mpHandlesUsed = new sal_Bool[ MAXHANDLES ];
    memset( mpHandlesUsed, 0, MAXHANDLES * sizeof( sal_Bool ) );
    mnHorTextAlign = mnHandleCount = mnLastPercent = mnRecordPos = mnRecordCount = 0;
    mnLineHandle = mnFillHandle = mnTextHandle = HANDLE_INVALID;
    mbRecordOpen = sal_False;

    maVDev.EnableOutput( sal_False );
    maVDev.SetMapMode( rMtf.GetPrefMapMode() );
    mpFilterConfigItem = pFilterConfigItem;

    // don't work with pixel as destination map mode -> higher resolution preferrable
    maDestMapMode.SetMapUnit( MAP_100TH_MM );

    const Size aMtfSizePix( maVDev.LogicToPixel( rMtf.GetPrefSize(), rMtf.GetPrefMapMode() ) );
    const Size aMtfSizeLog( maVDev.LogicToLogic( rMtf.GetPrefSize(), rMtf.GetPrefMapMode(), MAP_100TH_MM ) );

    // seek over header
    // use [MS-EMF 2.2.11] HeaderExtension2 Object, otherwise resulting EMF cannot be converted with GetWinMetaFileBits()
    m_rStm.SeekRel( 108 );

    // write initial values

    // set 100th mm map mode in EMF
    ImplBeginRecord( WIN_EMR_SETMAPMODE );
    m_rStm << (sal_Int32) MM_ANISOTROPIC;
    ImplEndRecord();

    ImplBeginRecord( WIN_EMR_SETVIEWPORTEXTEX );
    m_rStm << (sal_Int32) maVDev.ImplGetDPIX() << (sal_Int32) maVDev.ImplGetDPIY();
    ImplEndRecord();

    ImplBeginRecord( WIN_EMR_SETWINDOWEXTEX );
    m_rStm << (sal_Int32) 2540 << (sal_Int32) 2540;
    ImplEndRecord();

    ImplBeginRecord( WIN_EMR_SETVIEWPORTORGEX );
    m_rStm << (sal_Int32) 0 << (sal_Int32) 0;
    ImplEndRecord();

    ImplBeginRecord( WIN_EMR_SETWINDOWORGEX );
    m_rStm << (sal_Int32) 0 << (sal_Int32) 0;
    ImplEndRecord();

    ImplWriteRasterOp( ROP_OVERPAINT );

    ImplBeginRecord( WIN_EMR_SETBKMODE );
    m_rStm << (sal_uInt32) 1; // TRANSPARENT
    ImplEndRecord();

    // write emf data
    ImplWrite( rMtf );

    ImplBeginRecord( WIN_EMR_EOF );
    m_rStm<< (sal_uInt32)0      // nPalEntries
            << (sal_uInt32)0x10     // offPalEntries
            << (sal_uInt32)0x14;    // nSizeLast
    ImplEndRecord();


    // write header
    const sal_uLong nEndPos = m_rStm.Tell(); m_rStm.Seek( nHeaderPos );

    m_rStm << (sal_uInt32) 0x00000001 << (sal_uInt32) 108   //use [MS-EMF 2.2.11] HeaderExtension2 Object
             << (sal_Int32) 0 << (sal_Int32) 0 << (sal_Int32) ( aMtfSizePix.Width() - 1 ) << (sal_Int32) ( aMtfSizePix.Height() - 1 )
             << (sal_Int32) 0 << (sal_Int32) 0 << (sal_Int32) ( aMtfSizeLog.Width() - 1 ) << (sal_Int32) ( aMtfSizeLog.Height() - 1 )
             << (sal_uInt32) 0x464d4520 << (sal_uInt32) 0x10000 << (sal_uInt32) ( nEndPos - nHeaderPos )
             << (sal_uInt32) mnRecordCount << (sal_uInt16) ( mnHandleCount + 1 ) << (sal_uInt16) 0 << (sal_uInt32) 0 << (sal_uInt32) 0 << (sal_uInt32) 0
             << (sal_Int32) aMtfSizePix.Width() << (sal_Int32) aMtfSizePix.Height()
             << (sal_Int32) ( aMtfSizeLog.Width() / 100 ) << (sal_Int32) ( aMtfSizeLog.Height() / 100 )
             << (sal_uInt32) 0 << (sal_uInt32) 0 << (sal_uInt32) 0
             << (sal_Int32) (  aMtfSizeLog.Width() * 10 ) << (sal_Int32) ( aMtfSizeLog.Height() * 10 ); //use [MS-EMF 2.2.11] HeaderExtension2 Object

    m_rStm.Seek( nEndPos );
    delete[] mpHandlesUsed;

    return( m_rStm.GetError() == ERRCODE_NONE );
}

// -----------------------------------------------------------------------------

sal_uLong EMFWriter::ImplAcquireHandle()
{
    sal_uLong nHandle = HANDLE_INVALID;

    for( sal_uLong i = 0; i < MAXHANDLES && ( HANDLE_INVALID == nHandle ); i++ )
    {
        if( !mpHandlesUsed[ i ] )
        {
            mpHandlesUsed[ i ] = sal_True;

            if( ( nHandle = i ) == mnHandleCount )
                mnHandleCount++;
        }
    }

    DBG_ASSERT( nHandle != HANDLE_INVALID, "No more handles available" );
    return( nHandle != HANDLE_INVALID ? nHandle + 1 : HANDLE_INVALID );
}

// -----------------------------------------------------------------------------

void EMFWriter::ImplReleaseHandle( sal_uLong nHandle )
{
    DBG_ASSERT( nHandle && ( nHandle < MAXHANDLES ), "Handle out of range" );
    mpHandlesUsed[ nHandle - 1 ] = sal_False;
}

// -----------------------------------------------------------------------------

void EMFWriter::ImplBeginRecord( sal_uInt32 nType )
{
    DBG_ASSERT( !mbRecordOpen, "Another record is already opened!" );

    if( !mbRecordOpen )
    {
        mbRecordOpen = sal_True;
        mnRecordPos = m_rStm.Tell();

        m_rStm << nType;
        m_rStm.SeekRel( 4 );
    }
}

// -----------------------------------------------------------------------------

void EMFWriter::ImplEndRecord()
{
    DBG_ASSERT( mbRecordOpen, "Record was not opened!" );

    if( mbRecordOpen )
    {
        sal_Int32 nFillBytes, nActPos = m_rStm.Tell();
        m_rStm.Seek( mnRecordPos + 4 );
        nFillBytes = nActPos - mnRecordPos;
        nFillBytes += 3;    // each record has to be dword aligned
        nFillBytes ^= 3;
        nFillBytes &= 3;
        m_rStm << (sal_uInt32)( ( nActPos - mnRecordPos ) + nFillBytes );
        m_rStm.Seek( nActPos );
        while( nFillBytes-- )
            m_rStm << (sal_uInt8)0;
        mnRecordCount++;
        mbRecordOpen = sal_False;
    }
}

// -----------------------------------------------------------------------------

sal_Bool EMFWriter::ImplPrepareHandleSelect( sal_uInt32& rHandle, sal_uLong nSelectType )
{
    if( rHandle != HANDLE_INVALID )
    {
        sal_uInt32 nStockObject = 0x80000000;

        if( LINE_SELECT == nSelectType )
            nStockObject |= 0x00000007;
        else if( FILL_SELECT == nSelectType )
            nStockObject |= 0x00000001;
        else if( TEXT_SELECT == nSelectType )
            nStockObject |= 0x0000000a;

        // select stock object first
        ImplBeginRecord( WIN_EMR_SELECTOBJECT );
        m_rStm << nStockObject;
        ImplEndRecord();

        // destroy handle of created object
        ImplBeginRecord( WIN_EMR_DELETEOBJECT );
        m_rStm << rHandle;
        ImplEndRecord();

        // mark handle as free
        ImplReleaseHandle( rHandle );
    }

    rHandle = ImplAcquireHandle();

    return( HANDLE_INVALID != rHandle );
}

// -----------------------------------------------------------------------------

void EMFWriter::ImplCheckLineAttr()
{
    if( mbLineChanged && ImplPrepareHandleSelect( mnLineHandle, LINE_SELECT ) )
    {
        sal_uInt32 nStyle = maVDev.IsLineColor() ? 0 : 5;
        sal_uInt32 nWidth = 0, nHeight = 0;

        ImplBeginRecord( WIN_EMR_CREATEPEN );
        m_rStm << mnLineHandle << nStyle << nWidth << nHeight;
        ImplWriteColor( maVDev.GetLineColor() );
        ImplEndRecord();

        ImplBeginRecord( WIN_EMR_SELECTOBJECT );
        m_rStm << mnLineHandle;
        ImplEndRecord();
    }
}

// -----------------------------------------------------------------------------

void EMFWriter::ImplCheckFillAttr()
{
    if( mbFillChanged && ImplPrepareHandleSelect( mnFillHandle, FILL_SELECT ) )
    {
        sal_uInt32 nStyle = maVDev.IsFillColor() ? 0 : 1;
        sal_uInt32 nPatternStyle = 0;

        ImplBeginRecord( WIN_EMR_CREATEBRUSHINDIRECT );
        m_rStm << mnFillHandle << nStyle;
        ImplWriteColor( maVDev.GetFillColor() );
        m_rStm << nPatternStyle;
        ImplEndRecord();

        ImplBeginRecord( WIN_EMR_SELECTOBJECT );
        m_rStm << mnFillHandle;
        ImplEndRecord();
    }
}

// -----------------------------------------------------------------------------

void EMFWriter::ImplCheckTextAttr()
{
    if( mbTextChanged && ImplPrepareHandleSelect( mnTextHandle, TEXT_SELECT ) )
    {
        const Font&     rFont = maVDev.GetFont();
        String          aFontName( rFont.GetName() );
        sal_Int32       nWeight;
        sal_uInt16      i;
        sal_uInt8       nPitchAndFamily;

        ImplBeginRecord( WIN_EMR_EXTCREATEFONTINDIRECTW );
        m_rStm << mnTextHandle;
        ImplWriteExtent( -rFont.GetSize().Height() );
        ImplWriteExtent( rFont.GetSize().Width() );
        m_rStm << (sal_Int32) rFont.GetOrientation() << (sal_Int32) rFont.GetOrientation();

        switch( rFont.GetWeight() )
        {
            case WEIGHT_THIN:       nWeight = 100; break;
            case WEIGHT_ULTRALIGHT: nWeight = 200; break;
            case WEIGHT_LIGHT:      nWeight = 300; break;
            case WEIGHT_SEMILIGHT:  nWeight = 300; break;
            case WEIGHT_NORMAL:     nWeight = 400; break;
            case WEIGHT_MEDIUM:     nWeight = 500; break;
            case WEIGHT_SEMIBOLD:   nWeight = 600; break;
            case WEIGHT_BOLD:       nWeight = 700; break;
            case WEIGHT_ULTRABOLD:  nWeight = 800; break;
            case WEIGHT_BLACK:      nWeight = 900; break;
            default:                nWeight = 0; break;
        }

        m_rStm << nWeight;
        m_rStm << (sal_uInt8) ( ( ITALIC_NONE == rFont.GetItalic() ) ? 0 : 1 );
        m_rStm << (sal_uInt8) ( ( UNDERLINE_NONE == rFont.GetUnderline() ) ? 0 : 1 );
        m_rStm << (sal_uInt8) ( ( STRIKEOUT_NONE == rFont.GetStrikeout() ) ? 0 : 1 );
        m_rStm << (sal_uInt8) ( ( RTL_TEXTENCODING_SYMBOL == rFont.GetCharSet() ) ? 2 : 0 );
        m_rStm << (sal_uInt8) 0 << (sal_uInt8) 0 << (sal_uInt8) 0;

        switch( rFont.GetPitch() )
        {
            case PITCH_FIXED:    nPitchAndFamily = 0x01; break;
            case PITCH_VARIABLE: nPitchAndFamily = 0x02; break;
            default:             nPitchAndFamily = 0x00; break;
        }

        switch( rFont.GetFamily() )
        {
            case FAMILY_DECORATIVE: nPitchAndFamily |= 0x50; break;
            case FAMILY_MODERN:     nPitchAndFamily |= 0x30; break;
            case FAMILY_ROMAN:      nPitchAndFamily |= 0x10; break;
            case FAMILY_SCRIPT:     nPitchAndFamily |= 0x40; break;
            case FAMILY_SWISS:      nPitchAndFamily |= 0x20; break;
            default: break;
        }

        m_rStm << nPitchAndFamily;

        for( i = 0; i < 32; i++ )
            m_rStm << (sal_Unicode) ( ( i < aFontName.Len() ) ? aFontName.GetChar( i ) : 0 );

        // dummy elfFullName
        for( i = 0; i < 64; i++ )
            m_rStm << (sal_Unicode) 0;

        // dummy elfStyle
        for( i = 0; i < 32; i++ )
            m_rStm << (sal_Unicode) 0;

        // dummy elfVersion, elfStyleSize, elfMatch, elfReserved
        m_rStm << (sal_uInt32) 0 << (sal_uInt32) 0 << (sal_uInt32) 0 << (sal_uInt32) 0 ;

        // dummy elfVendorId
        m_rStm << (sal_uInt32) 0;

        // dummy elfCulture
        m_rStm << (sal_uInt32) 0;

        // dummy elfPanose
        m_rStm << (sal_uInt8) 0 << (sal_uInt8) 0 << (sal_uInt8) 0 << (sal_uInt8) 0 << (sal_uInt8) 0 << (sal_uInt8) 0 << (sal_uInt8) 0 << (sal_uInt8) 0 << (sal_uInt8) 0 << (sal_uInt8) 0;

        // fill record to get a record size divideable by 4
        m_rStm << (sal_uInt16) 0;

        ImplEndRecord();

        // TextAlign
        sal_uInt32 nTextAlign;

        switch( rFont.GetAlign() )
        {
            case ALIGN_TOP:    nTextAlign = TA_TOP; break;
            case ALIGN_BOTTOM: nTextAlign = TA_BOTTOM; break;
            default:           nTextAlign = TA_BASELINE; break;
        }
        nTextAlign |= mnHorTextAlign;

        ImplBeginRecord( WIN_EMR_SETTEXTALIGN );
        m_rStm << nTextAlign;
        ImplEndRecord();

        // Text color
        ImplBeginRecord( WIN_EMR_SETTEXTCOLOR );
        ImplWriteColor( maVDev.GetTextColor() );
        ImplEndRecord();

        ImplBeginRecord( WIN_EMR_SELECTOBJECT );
        m_rStm << mnTextHandle;
        ImplEndRecord();
    }
}

// -----------------------------------------------------------------------------

void EMFWriter::ImplWriteColor( const Color& rColor )
{
    sal_uInt32 nCol = rColor.GetRed();

    nCol |= ( (sal_uInt32) rColor.GetGreen() ) << 8;
    nCol |= ( (sal_uInt32) rColor.GetBlue() ) << 16;

    m_rStm << nCol;
}

// -----------------------------------------------------------------------------

void EMFWriter::ImplWriteRasterOp( RasterOp eRop )
{
    sal_uInt32 nROP2;

    switch( eRop )
    {
        case ROP_INVERT: nROP2 = 6; break;
        case ROP_XOR:    nROP2 = 7; break;
        default:         nROP2 = 13;break;
    }

    ImplBeginRecord( WIN_EMR_SETROP2 );
    m_rStm << nROP2;
    ImplEndRecord();
}

// -----------------------------------------------------------------------------

void EMFWriter::ImplWriteExtent( long nExtent )
{
    nExtent = maVDev.LogicToLogic( Size( nExtent, 0 ), maVDev.GetMapMode(), maDestMapMode ).Width();
    m_rStm << (sal_Int32) nExtent;
}

// -----------------------------------------------------------------------------

void EMFWriter::ImplWritePoint( const Point& rPoint )
{
    const Point aPoint( maVDev.LogicToLogic( rPoint, maVDev.GetMapMode(), maDestMapMode ));
     m_rStm << (sal_Int32) aPoint.X() << (sal_Int32) aPoint.Y();
}

// -----------------------------------------------------------------------------

void EMFWriter::ImplWriteSize( const Size& rSize)
{
    const Size aSize( maVDev.LogicToLogic( rSize, maVDev.GetMapMode(), maDestMapMode ));
     m_rStm << (sal_Int32) aSize.Width() << (sal_Int32) aSize.Height();
}

// -----------------------------------------------------------------------------

void EMFWriter::ImplWriteRect( const Rectangle& rRect )
{
    const Rectangle aRect( maVDev.LogicToLogic ( rRect, maVDev.GetMapMode(), maDestMapMode ));
    m_rStm
        << static_cast<sal_Int32>(aRect.Left())
        << static_cast<sal_Int32>(aRect.Top())
        << static_cast<sal_Int32>(aRect.Right())
        << static_cast<sal_Int32>(aRect.Bottom());
}

// -----------------------------------------------------------------------------

void EMFWriter::ImplWritePolygonRecord( const Polygon& rPoly, sal_Bool bClose )
{
    if( rPoly.GetSize() )
    {
        if( rPoly.HasFlags() )
            ImplWritePath( rPoly, bClose );
        else
        {
            if( bClose )
                ImplCheckFillAttr();

            ImplCheckLineAttr();

            ImplBeginRecord( bClose ? WIN_EMR_POLYGON : WIN_EMR_POLYLINE );
            ImplWriteRect( rPoly.GetBoundRect() );
            m_rStm << (sal_uInt32) rPoly.GetSize();

            for( sal_uInt16 i = 0; i < rPoly.GetSize(); i++ )
                ImplWritePoint( rPoly[ i ] );

            ImplEndRecord();
        }
    }
}

// -----------------------------------------------------------------------------

void EMFWriter::ImplWritePolyPolygonRecord( const PolyPolygon& rPolyPoly )
{
    sal_uInt16 n, i, nPolyCount = rPolyPoly.Count();

    if( nPolyCount )
    {
        if( 1 == nPolyCount )
            ImplWritePolygonRecord( rPolyPoly[ 0 ], sal_True );
        else
        {
            sal_Bool    bHasFlags = sal_False;
            sal_uInt32  nTotalPoints = 0;

            for( i = 0; i < nPolyCount; i++ )
            {
                nTotalPoints += rPolyPoly[ i ].GetSize();
                if ( rPolyPoly[ i ].HasFlags() )
                    bHasFlags = sal_True;
            }
            if( nTotalPoints )
            {
                if ( bHasFlags )
                    ImplWritePath( rPolyPoly, sal_True );
                else
                {
                    ImplCheckFillAttr();
                    ImplCheckLineAttr();

                    ImplBeginRecord( WIN_EMR_POLYPOLYGON );
                    ImplWriteRect( rPolyPoly.GetBoundRect() );
                    m_rStm << (sal_uInt32)nPolyCount << nTotalPoints;

                    for( i = 0; i < nPolyCount; i++ )
                        m_rStm << (sal_uInt32)rPolyPoly[ i ].GetSize();

                    for( i = 0; i < nPolyCount; i++ )
                    {
                        const Polygon& rPoly = rPolyPoly[ i ];

                        for( n = 0; n < rPoly.GetSize(); n++ )
                            ImplWritePoint( rPoly[ n ] );
                    }
                    ImplEndRecord();
                }
            }
        }
    }
}

// -----------------------------------------------------------------------------

void EMFWriter::ImplWritePath( const PolyPolygon& rPolyPoly, sal_Bool bClosed )
{
    if ( bClosed )
        ImplCheckFillAttr();
    ImplCheckLineAttr();

    ImplBeginRecord( WIN_EMR_BEGINPATH );
    ImplEndRecord();

    sal_uInt16 i, n, o, nPolyCount = rPolyPoly.Count();
    for ( i = 0; i < nPolyCount; i++ )
    {
        n = 0;
        const Polygon& rPoly = rPolyPoly[ i ];
        while ( n < rPoly.GetSize() )
        {
            if( n == 0 )
            {
                ImplBeginRecord( WIN_EMR_MOVETOEX );
                ImplWritePoint( rPoly[ 0 ] );
                ImplEndRecord();
                n++;
                continue;
            }

            sal_uInt16 nBezPoints = 0;

            while ( ( ( nBezPoints + n + 2 ) < rPoly.GetSize() ) && ( rPoly.GetFlags( nBezPoints + n ) == POLY_CONTROL ) )
                nBezPoints += 3;

            if ( nBezPoints )
            {
                ImplBeginRecord( WIN_EMR_POLYBEZIERTO );
                Polygon aNewPoly( nBezPoints + 1 );
                aNewPoly[ 0 ] = rPoly[ n - 1 ];
                for ( o = 0; o < nBezPoints; o++ )
                    aNewPoly[ o + 1 ] = rPoly[ n + o ];
                ImplWriteRect( aNewPoly.GetBoundRect() );
                m_rStm << (sal_uInt32)nBezPoints;
                for( o = 1; o < aNewPoly.GetSize(); o++ )
                    ImplWritePoint( aNewPoly[ o ] );
                ImplEndRecord();
                n = n + nBezPoints;
            }
            else
            {
                sal_uInt16 nPoints = 1;
                while( ( nPoints + n ) < rPoly.GetSize() && ( rPoly.GetFlags( nPoints + n ) != POLY_CONTROL ) )
                    nPoints++;

                if ( nPoints > 1 )
                {
                    ImplBeginRecord( WIN_EMR_POLYLINETO );
                    Polygon aNewPoly( nPoints + 1 );
                    aNewPoly[ 0 ] = rPoly[ n - 1];
                    for ( o = 1; o <= nPoints; o++ )
                        aNewPoly[ o ] = rPoly[ n - 1 + o ];
                    ImplWriteRect( aNewPoly.GetBoundRect() );
                    m_rStm << (sal_uInt32)( nPoints );
                    for( o = 1; o < aNewPoly.GetSize(); o++ )
                        ImplWritePoint( aNewPoly[ o ] );
                    ImplEndRecord();
                }
                else
                {
                    ImplBeginRecord( WIN_EMR_LINETO );
                    ImplWritePoint( rPoly[ n ] );
                    ImplEndRecord();
                }
                n = n + nPoints;
            }
            if ( bClosed && ( n == rPoly.GetSize() ) )
            {
                ImplBeginRecord( WIN_EMR_CLOSEFIGURE );
                ImplEndRecord();
            }
        }
    }
    ImplBeginRecord( WIN_EMR_ENDPATH );
    ImplEndRecord();
    ImplBeginRecord( bClosed ? WIN_EMR_FILLPATH : WIN_EMR_STROKEPATH );
    ImplWriteRect( rPolyPoly.GetBoundRect() );
    ImplEndRecord();
}

// -----------------------------------------------------------------------------

void EMFWriter::ImplWriteBmpRecord( const Bitmap& rBmp, const Point& rPt,
                                    const Size& rSz, sal_uInt32 nROP )
{
    if( !!rBmp )
    {
        SvMemoryStream  aMemStm( 65535, 65535 );
        const Size      aBmpSizePixel( rBmp.GetSizePixel() );

        ImplBeginRecord( WIN_EMR_STRETCHDIBITS );
        ImplWriteRect( Rectangle( rPt, rSz ) );
        ImplWritePoint( rPt );
        m_rStm << (sal_Int32) 0 << (sal_Int32) 0 << (sal_Int32) aBmpSizePixel.Width() << (sal_Int32) aBmpSizePixel.Height();

        // write offset positions and sizes later
        const sal_uLong nOffPos = m_rStm.Tell();
        m_rStm.SeekRel( 16 );

        m_rStm << (sal_uInt32) 0 << sal_Int32( ( ROP_XOR == maVDev.GetRasterOp() && WIN_SRCCOPY == nROP ) ? WIN_SRCINVERT : nROP );
        ImplWriteSize( rSz );

        rBmp.Write( aMemStm, sal_True, sal_False );

        sal_uInt32  nDIBSize = aMemStm.Tell(), nHeaderSize, nCompression, nColsUsed, nPalCount, nImageSize;
        sal_uInt16  nBitCount;

        // get DIB parameters
        aMemStm.Seek( 0 );
        aMemStm >> nHeaderSize;
        aMemStm.SeekRel( 10 );
        aMemStm >> nBitCount >> nCompression >> nImageSize;
        aMemStm.SeekRel( 8 );
        aMemStm >> nColsUsed;

        nPalCount = ( nBitCount <= 8 ) ? ( nColsUsed ? nColsUsed : ( 1 << (sal_uInt32) nBitCount ) ) :
                                         ( ( 3 == nCompression ) ? 12 : 0 );

        m_rStm.Write( aMemStm.GetData(), nDIBSize );

        const sal_uLong nEndPos = m_rStm.Tell();
        m_rStm.Seek( nOffPos );
        m_rStm << (sal_uInt32) 80 << (sal_uInt32)( nHeaderSize + ( nPalCount << 2 ) );
        m_rStm << (sal_uInt32)( 80 + ( nHeaderSize + ( nPalCount << 2 ) ) ) << nImageSize;
        m_rStm.Seek( nEndPos );

        ImplEndRecord();
    }
}

// -----------------------------------------------------------------------------

void EMFWriter::ImplWriteTextRecord( const Point& rPos, const String rText, const sal_Int32* pDXArray, sal_uInt32 nWidth )
{
    xub_StrLen nLen = rText.Len(), i;

    if( nLen )
    {
        sal_uInt32  nNormWidth;
        sal_Int32*  pOwnArray;
        sal_Int32*  pDX;

        // get text sizes
        if( pDXArray )
        {
            pOwnArray = NULL;
            nNormWidth = maVDev.GetTextWidth( rText );
            pDX = (sal_Int32*) pDXArray;
        }
        else
        {
            pOwnArray = new sal_Int32[ nLen ];
            nNormWidth = maVDev.GetTextArray( rText, pOwnArray );
            pDX = pOwnArray;
        }

        if( nLen > 1 )
        {
            nNormWidth = pDX[ nLen - 2 ] + maVDev.GetTextWidth( rText.GetChar( nLen - 1 ) );

            if( nWidth && nNormWidth && ( nWidth != nNormWidth ) )
            {
                const double fFactor = (double) nWidth / nNormWidth;

                for( i = 0; i < ( nLen - 1 ); i++ )
                    pDX[ i ] = FRound( pDX[ i ] * fFactor );
            }
        }

        // write text record
        ImplBeginRecord( WIN_EMR_EXTTEXTOUTW );

        ImplWriteRect( Rectangle( rPos, Size( nNormWidth, maVDev.GetTextHeight() ) ) );
        m_rStm << (sal_uInt32)1;
        m_rStm << (sal_Int32) 0 << (sal_Int32) 0;
        ImplWritePoint( rPos );
        m_rStm << (sal_uInt32) nLen << (sal_uInt32) 76 << (sal_uInt32) 2;
        m_rStm << (sal_Int32) 0 << (sal_Int32) 0 << (sal_Int32) 0 << (sal_Int32) 0;
        m_rStm << (sal_uInt32) ( 76 + ( nLen << 1 ) + ( (nLen & 1 ) ? 2 : 0 ) );

        // write text
        for( i = 0; i < nLen; i++ )
            m_rStm << (sal_Unicode)rText.GetChar( i );

        // padding word
        if( nLen & 1 )
            m_rStm << (sal_uInt16) 0;

        // write DX array
        ImplWriteExtent( pDX[ 0 ] );

        if( nLen > 1 )
        {
            for( i = 1; i < ( nLen - 1 ); i++ )
                ImplWriteExtent( pDX[ i ] - pDX[ i - 1 ] );

            ImplWriteExtent( pDX[ nLen - 2 ] / ( nLen - 1 ) );
        }

        ImplEndRecord();
        delete[] pOwnArray;
    }
}

// -----------------------------------------------------------------------------

void EMFWriter::Impl_handleLineInfoPolyPolygons(const LineInfo& rInfo, const basegfx::B2DPolygon& rLinePolygon)
{
    if(rLinePolygon.count())
    {
        basegfx::B2DPolyPolygon aLinePolyPolygon(rLinePolygon);
        basegfx::B2DPolyPolygon aFillPolyPolygon;

        rInfo.applyToB2DPolyPolygon(aLinePolyPolygon, aFillPolyPolygon);

        if(aLinePolyPolygon.count())
        {
            for(sal_uInt32 a(0); a < aLinePolyPolygon.count(); a++)
            {
                const basegfx::B2DPolygon aCandidate(aLinePolyPolygon.getB2DPolygon(a));
                ImplWritePolygonRecord( Polygon(aCandidate), sal_False );
            }
        }

        if(aFillPolyPolygon.count())
        {
            const Color aOldLineColor(maVDev.GetLineColor());
            const Color aOldFillColor(maVDev.GetFillColor());

            maVDev.SetLineColor();
            maVDev.SetFillColor(aOldLineColor);

            for(sal_uInt32 a(0); a < aFillPolyPolygon.count(); a++)
            {
                const Polygon aPolygon(aFillPolyPolygon.getB2DPolygon(a));
                ImplWritePolyPolygonRecord(PolyPolygon(Polygon(aPolygon)));
            }

            maVDev.SetLineColor(aOldLineColor);
            maVDev.SetFillColor(aOldFillColor);
        }
    }
}

// -----------------------------------------------------------------------------

void EMFWriter::ImplWrite( const GDIMetaFile& rMtf )
{
    for( size_t j = 0, nActionCount = rMtf.GetActionSize(); j < nActionCount; j++ )
    {
        const MetaAction*   pAction = rMtf.GetAction( j );
        const sal_uInt16        nType = pAction->GetType();

        switch( nType )
        {
            case( META_PIXEL_ACTION ):
            {
                const MetaPixelAction* pA = (const MetaPixelAction*) pAction;

                ImplCheckLineAttr();
                ImplBeginRecord( WIN_EMR_SETPIXELV );
                ImplWritePoint( pA->GetPoint() );
                ImplWriteColor( pA->GetColor() );
                ImplEndRecord();
            }
            break;

            case( META_POINT_ACTION ):
            {
                if( maVDev.IsLineColor() )
                {
                    const MetaPointAction* pA = (const MetaPointAction*) pAction;

                    ImplCheckLineAttr();
                    ImplBeginRecord( WIN_EMR_SETPIXELV );
                    ImplWritePoint( pA->GetPoint() );
                    ImplWriteColor( maVDev.GetLineColor() );
                    ImplEndRecord();
                }
            }
            break;

            case( META_LINE_ACTION ):
            {
                if( maVDev.IsLineColor() )
                {
                    const MetaLineAction* pA = (const MetaLineAction*) pAction;

                    if(pA->GetLineInfo().IsDefault())
                    {
                        ImplCheckLineAttr();

                        ImplBeginRecord( WIN_EMR_MOVETOEX );
                        ImplWritePoint( pA->GetStartPoint() );
                        ImplEndRecord();

                        ImplBeginRecord( WIN_EMR_LINETO );
                        ImplWritePoint( pA->GetEndPoint() );
                        ImplEndRecord();

                        ImplBeginRecord( WIN_EMR_SETPIXELV );
                        ImplWritePoint( pA->GetEndPoint() );
                        ImplWriteColor( maVDev.GetLineColor() );
                        ImplEndRecord();
                    }
                    else
                    {
                        // LineInfo used; handle Dash/Dot and fat lines
                        basegfx::B2DPolygon aPolygon;
                        aPolygon.append(basegfx::B2DPoint(pA->GetStartPoint().X(), pA->GetStartPoint().Y()));
                        aPolygon.append(basegfx::B2DPoint(pA->GetEndPoint().X(), pA->GetEndPoint().Y()));
                        Impl_handleLineInfoPolyPolygons(pA->GetLineInfo(), aPolygon);
                    }
                }
            }
            break;

            case( META_RECT_ACTION ):
            {
                if( maVDev.IsLineColor() || maVDev.IsFillColor() )
                {
                    const MetaRectAction* pA = (const MetaRectAction*) pAction;

                    ImplCheckFillAttr();
                    ImplCheckLineAttr();

                    ImplBeginRecord( WIN_EMR_RECTANGLE );
                    ImplWriteRect( pA->GetRect() );
                    ImplEndRecord();
                }
            }
            break;

            case( META_ROUNDRECT_ACTION ):
            {
                if( maVDev.IsLineColor() || maVDev.IsFillColor() )
                {
                    const MetaRoundRectAction* pA = (const MetaRoundRectAction*) pAction;

                    ImplCheckFillAttr();
                    ImplCheckLineAttr();

                    ImplBeginRecord( WIN_EMR_ROUNDRECT );
                    ImplWriteRect( pA->GetRect() );
                    ImplWriteSize( Size( pA->GetHorzRound(), pA->GetVertRound() ) );
                    ImplEndRecord();
                }
            }
            break;

            case( META_ELLIPSE_ACTION ):
            {
                if( maVDev.IsLineColor() || maVDev.IsFillColor() )
                {
                    const MetaEllipseAction* pA = (const MetaEllipseAction*) pAction;

                    ImplCheckFillAttr();
                    ImplCheckLineAttr();

                    ImplBeginRecord( WIN_EMR_ELLIPSE );
                    ImplWriteRect( pA->GetRect() );
                    ImplEndRecord();
                }
            }
            break;

            case( META_ARC_ACTION ):
            case( META_PIE_ACTION ):
            case( META_CHORD_ACTION ):
            case( META_POLYGON_ACTION ):
            {
                if( maVDev.IsLineColor() || maVDev.IsFillColor() )
                {
                    Polygon aPoly;

                    switch( nType )
                    {
                        case( META_ARC_ACTION ):
                        {
                            const MetaArcAction* pA = (const MetaArcAction*) pAction;
                            aPoly = Polygon( pA->GetRect(), pA->GetStartPoint(), pA->GetEndPoint(), POLY_ARC );
                        }
                        break;

                        case( META_PIE_ACTION ):
                        {
                            const MetaPieAction* pA = (const MetaPieAction*) pAction;
                            aPoly = Polygon( pA->GetRect(), pA->GetStartPoint(), pA->GetEndPoint(), POLY_PIE );
                        }
                        break;

                        case( META_CHORD_ACTION ):
                        {
                            const MetaChordAction* pA = (const MetaChordAction*) pAction;
                            aPoly = Polygon( pA->GetRect(), pA->GetStartPoint(), pA->GetEndPoint(), POLY_CHORD );
                        }
                        break;

                        case( META_POLYGON_ACTION ):
                            aPoly = ( (const MetaPolygonAction*) pAction )->GetPolygon();
                        break;
                    }

                    ImplWritePolygonRecord( aPoly, nType != META_ARC_ACTION );
                }
            }
            break;

            case( META_POLYLINE_ACTION ):
            {
                if( maVDev.IsLineColor() )
                {
                    const MetaPolyLineAction*   pA = (const MetaPolyLineAction*) pAction;
                    const Polygon&              rPoly = pA->GetPolygon();

                    if( rPoly.GetSize() )
                    {
                        if(pA->GetLineInfo().IsDefault())
                        {
                            ImplWritePolygonRecord( rPoly, sal_False );
                        }
                        else
                        {
                            // LineInfo used; handle Dash/Dot and fat lines
                            Impl_handleLineInfoPolyPolygons(pA->GetLineInfo(), rPoly.getB2DPolygon());
                        }
                    }
                }
            }
            break;

            case( META_POLYPOLYGON_ACTION ):
            {
                if( maVDev.IsLineColor() || maVDev.IsFillColor() )
                    ImplWritePolyPolygonRecord( ( (const MetaPolyPolygonAction*) pAction )->GetPolyPolygon() );
            }
            break;

            case( META_GRADIENT_ACTION ):
            {
                const MetaGradientAction*   pA = (const MetaGradientAction*) pAction;
                GDIMetaFile                 aTmpMtf;

                maVDev.AddGradientActions( pA->GetRect(), pA->GetGradient(), aTmpMtf );
                ImplWrite( aTmpMtf );
            }
            break;

            case META_HATCH_ACTION:
            {
                const MetaHatchAction*  pA = (const MetaHatchAction*) pAction;
                GDIMetaFile             aTmpMtf;

                maVDev.AddHatchActions( pA->GetPolyPolygon(), pA->GetHatch(), aTmpMtf );
                ImplWrite( aTmpMtf );
            }
            break;

            case META_TRANSPARENT_ACTION:
            {
                ImplCheckFillAttr();
                ImplCheckLineAttr();
                ImplWritePolyPolygonRecord( ( (MetaTransparentAction*) pAction )->GetPolyPolygon() );
            }
            break;

            case META_FLOATTRANSPARENT_ACTION:
            {
                const MetaFloatTransparentAction* pA = (const MetaFloatTransparentAction*) pAction;

                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() ? (double) aDestSize.Width() / aSrcSize.Width() : 1.0;
                const double    fScaleY = aSrcSize.Height() ? (double) aDestSize.Height() / aSrcSize.Height() : 1.0;
                long            nMoveX, nMoveY;

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

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

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

                ImplCheckFillAttr();
                ImplCheckLineAttr();
                ImplCheckTextAttr();
                ImplWrite( aTmpMtf );
            }
            break;

            case( META_EPS_ACTION ):
            {
                const MetaEPSAction*    pA = (const MetaEPSAction*) pAction;
                const GDIMetaFile       aSubstitute( pA->GetSubstitute() );

                for( size_t i = 0, nCount = aSubstitute.GetActionSize(); i < nCount; i++ )
                {
                    const MetaAction* pSubstAct = aSubstitute.GetAction( i );
                    if( pSubstAct->GetType() == META_BMPSCALE_ACTION )
                    {
                        maVDev.Push( PUSH_ALL );
                        ImplBeginRecord( WIN_EMR_SAVEDC );
                        ImplEndRecord();

                        MapMode aMapMode( aSubstitute.GetPrefMapMode() );
                        Size aOutSize( maVDev.LogicToLogic( pA->GetSize(), maVDev.GetMapMode(), aMapMode ) );
                        aMapMode.SetScaleX( Fraction( aOutSize.Width(), aSubstitute.GetPrefSize().Width() ) );
                        aMapMode.SetScaleY( Fraction( aOutSize.Height(), aSubstitute.GetPrefSize().Height() ) );
                        aMapMode.SetOrigin( maVDev.LogicToLogic( pA->GetPoint(), maVDev.GetMapMode(), aMapMode ) );
                        maVDev.SetMapMode( aMapMode );
                        ImplWrite( aSubstitute );

                        maVDev.Pop();
                        ImplBeginRecord( WIN_EMR_RESTOREDC );
                        m_rStm << (sal_Int32) -1;
                        ImplEndRecord();
                        break;
                    }
                }
            }
            break;

            case META_BMP_ACTION:
            {
                const MetaBmpAction* pA = (const MetaBmpAction *) pAction;
                ImplWriteBmpRecord( pA->GetBitmap(), pA->GetPoint(), maVDev.PixelToLogic( pA->GetBitmap().GetSizePixel() ), WIN_SRCCOPY );
            }
            break;

            case META_BMPSCALE_ACTION:
            {
                const MetaBmpScaleAction* pA = (const MetaBmpScaleAction*) pAction;
                ImplWriteBmpRecord( pA->GetBitmap(), pA->GetPoint(), pA->GetSize(), WIN_SRCCOPY );
            }
            break;

            case META_BMPSCALEPART_ACTION:
            {
                const MetaBmpScalePartAction*   pA = (const MetaBmpScalePartAction*) pAction;
                Bitmap                          aTmp( pA->GetBitmap() );

                if( aTmp.Crop( Rectangle( pA->GetSrcPoint(), pA->GetSrcSize() ) ) )
                    ImplWriteBmpRecord( aTmp, pA->GetDestPoint(), pA->GetDestSize(), WIN_SRCCOPY );
            }
            break;

            case META_BMPEX_ACTION:
            {
                const MetaBmpExAction*  pA = (const MetaBmpExAction *) pAction;
                Bitmap                  aBmp( pA->GetBitmapEx().GetBitmap() );
                Bitmap                  aMsk( pA->GetBitmapEx().GetMask() );

                if( !!aMsk )
                {
                    aBmp.Replace( aMsk, COL_WHITE );
                    aMsk.Invert();
                    ImplWriteBmpRecord( aMsk, pA->GetPoint(), maVDev.PixelToLogic( aMsk.GetSizePixel() ), WIN_SRCPAINT );
                    ImplWriteBmpRecord( aBmp, pA->GetPoint(), maVDev.PixelToLogic( aBmp.GetSizePixel() ), WIN_SRCAND );
                }
                else
                    ImplWriteBmpRecord( aBmp, pA->GetPoint(), aBmp.GetSizePixel(), WIN_SRCCOPY );
            }
            break;

            case META_BMPEXSCALE_ACTION:
            {
                const MetaBmpExScaleAction* pA = (const MetaBmpExScaleAction*) pAction;
                Bitmap                      aBmp( pA->GetBitmapEx().GetBitmap() );
                Bitmap                      aMsk( pA->GetBitmapEx().GetMask() );

                if( !!aMsk )
                {
                    aBmp.Replace( aMsk, COL_WHITE );
                    aMsk.Invert();
                    ImplWriteBmpRecord( aMsk, pA->GetPoint(), pA->GetSize(), WIN_SRCPAINT );
                    ImplWriteBmpRecord( aBmp, pA->GetPoint(), pA->GetSize(), WIN_SRCAND );
                }
                else
                    ImplWriteBmpRecord( aBmp, pA->GetPoint(), pA->GetSize(), WIN_SRCCOPY );
            }
            break;

            case META_BMPEXSCALEPART_ACTION:
            {
                const MetaBmpExScalePartAction* pA = (const MetaBmpExScalePartAction*) pAction;
                BitmapEx                        aBmpEx( pA->GetBitmapEx() );
                aBmpEx.Crop( Rectangle( pA->GetSrcPoint(), pA->GetSrcSize() ) );
                Bitmap                          aBmp( aBmpEx.GetBitmap() );
                Bitmap                          aMsk( aBmpEx.GetMask() );

                if( !!aMsk )
                {
                    aBmp.Replace( aMsk, COL_WHITE );
                    aMsk.Invert();
                    ImplWriteBmpRecord( aMsk, pA->GetDestPoint(), pA->GetDestSize(), WIN_SRCPAINT );
                    ImplWriteBmpRecord( aBmp, pA->GetDestPoint(), pA->GetDestSize(), WIN_SRCAND );
                }
                else
                    ImplWriteBmpRecord( aBmp, pA->GetDestPoint(), pA->GetDestSize(), WIN_SRCCOPY );
            }
            break;

            case META_TEXT_ACTION:
            {
                const MetaTextAction*   pA = (const MetaTextAction*) pAction;
                const String            aText( pA->GetText(), pA->GetIndex(), pA->GetLen() );

                ImplCheckTextAttr();
                ImplWriteTextRecord( pA->GetPoint(), aText, NULL, 0 );
            }
            break;

            case META_TEXTRECT_ACTION:
            {
                const MetaTextRectAction*   pA = (const MetaTextRectAction*) pAction;
                const String                aText( pA->GetText() );

                ImplCheckTextAttr();
                ImplWriteTextRecord( pA->GetRect().TopLeft(), aText, NULL, 0 );
            }
            break;

            case META_TEXTARRAY_ACTION:
            {
                const MetaTextArrayAction*  pA = (const MetaTextArrayAction*) pAction;
                const String                aText( pA->GetText(), pA->GetIndex(), pA->GetLen() );

                ImplCheckTextAttr();
                ImplWriteTextRecord( pA->GetPoint(), aText, pA->GetDXArray(), 0 );
            }
            break;

            case META_STRETCHTEXT_ACTION:
            {
                const MetaStretchTextAction*    pA = (const MetaStretchTextAction*) pAction;
                const String                    aText( pA->GetText(), pA->GetIndex(), pA->GetLen() );

                ImplCheckTextAttr();
                ImplWriteTextRecord( pA->GetPoint(), aText, NULL, pA->GetWidth() );
            }
            break;

            case( META_LINECOLOR_ACTION ):
            {
                ( (MetaAction*) pAction )->Execute( &maVDev );
                mbLineChanged = sal_True;
            }
            break;

            case( META_FILLCOLOR_ACTION ):
            {
                ( (MetaAction*) pAction )->Execute( &maVDev );
                mbFillChanged = sal_True;
            }
            break;

            case( META_TEXTCOLOR_ACTION ):
            case( META_TEXTLINECOLOR_ACTION ):
            case( META_TEXTFILLCOLOR_ACTION ):
            case( META_TEXTALIGN_ACTION ):
            case( META_FONT_ACTION ):
            {
                ( (MetaAction*) pAction )->Execute( &maVDev );
                mbTextChanged = sal_True;
            }
            break;

            case( META_ISECTRECTCLIPREGION_ACTION ):
            {
                ( (MetaAction*) pAction )->Execute( &maVDev );

                ImplBeginRecord( WIN_EMR_INTERSECTCLIPRECT );
                ImplWriteRect( ( (MetaISectRectClipRegionAction*) pAction )->GetRect() );
                ImplEndRecord();
            }
            break;

            case( META_CLIPREGION_ACTION ):
            case( META_ISECTREGIONCLIPREGION_ACTION ):
            case( META_MOVECLIPREGION_ACTION ):
            {
                ( (MetaAction*) pAction )->Execute( &maVDev );
            }
            break;

            case( META_REFPOINT_ACTION ):
            case( META_MAPMODE_ACTION ):
                ( (MetaAction*) pAction )->Execute( &maVDev );
            break;

            case( META_PUSH_ACTION ):
            {
                ( (MetaAction*) pAction )->Execute( &maVDev );

                ImplBeginRecord( WIN_EMR_SAVEDC );
                ImplEndRecord();
            }
            break;

            case( META_POP_ACTION ):
            {
                ( (MetaAction*) pAction )->Execute( &maVDev );

                ImplBeginRecord( WIN_EMR_RESTOREDC );
                m_rStm << (sal_Int32) -1;
                ImplEndRecord();

                ImplWriteRasterOp( maVDev.GetRasterOp() );
                mbLineChanged = mbFillChanged = mbTextChanged = sal_True;
            }
            break;

            case( META_RASTEROP_ACTION ):
            {
                ( (MetaAction*) pAction )->Execute( &maVDev );
                ImplWriteRasterOp( ( (MetaRasterOpAction*) pAction )->GetRasterOp() );
            }
            break;

            case( META_LAYOUTMODE_ACTION ):
            {
                sal_uInt32 nLayoutMode = ( (MetaLayoutModeAction*) pAction )->GetLayoutMode();
                mnHorTextAlign = 0;
                if (nLayoutMode & TEXT_LAYOUT_BIDI_RTL)
                {
                    mnHorTextAlign = TA_RIGHT | TA_RTLREADING;
                }
                if (nLayoutMode & TEXT_LAYOUT_TEXTORIGIN_RIGHT)
                    mnHorTextAlign |= TA_RIGHT;
                else if (nLayoutMode & TEXT_LAYOUT_TEXTORIGIN_LEFT)
                    mnHorTextAlign &= ~TA_RIGHT;
                break;
            }

            case( META_MASK_ACTION ):
            case( META_MASKSCALE_ACTION ):
            case( META_MASKSCALEPART_ACTION ):
            case( META_WALLPAPER_ACTION ):
            case( META_TEXTLINE_ACTION ):
            case( META_COMMENT_ACTION ):
            case( META_GRADIENTEX_ACTION ):
            {
                // !!! >>> we don't want to support these actions
            }
            break;

            case( META_RENDERGRAPHIC_ACTION ):
            {
                const MetaRenderGraphicAction*          pA = (const MetaRenderGraphicAction*) pAction;
                const ::vcl::RenderGraphicRasterizer    aRasterizer( pA->GetRenderGraphic() );
                const BitmapEx                          aBmpEx( aRasterizer.Rasterize( maVDev.LogicToPixel( pA->GetSize() ) ) );
                Bitmap                                  aBmp( aBmpEx.GetBitmap() );
                Bitmap                                  aMsk( aBmpEx.GetMask() );

                if( !!aMsk )
                {
                    aBmp.Replace( aMsk, COL_WHITE );
                    aMsk.Invert();
                    ImplWriteBmpRecord( aMsk, pA->GetPoint(), pA->GetSize(), WIN_SRCPAINT );
                    ImplWriteBmpRecord( aBmp, pA->GetPoint(), pA->GetSize(), WIN_SRCAND );
                }
                else
                    ImplWriteBmpRecord( aBmp, pA->GetPoint(), pA->GetSize(), WIN_SRCCOPY );
            }
            break;

            default:
                OSL_FAIL(rtl::OStringBuffer(RTL_CONSTASCII_STRINGPARAM(
                    "EMFWriter::ImplWriteActions: unsupported MetaAction #" )).
                     append(static_cast<sal_Int32>(nType)).getStr());
            break;
        }
    }
}

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