/* -*- 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 <vcl/metaact.hxx>
#include <svtools/filter.hxx>
#include <svl/solar.hrc>
#include <svtools/fltcall.hxx>

#include <math.h>
#include <vcl/bmpacc.hxx>
#include <vcl/graph.hxx>
#include <vcl/gradient.hxx>
#include <vcl/hatch.hxx>
#include <vcl/metric.hxx>
#include <vcl/font.hxx>
#include <vcl/virdev.hxx>
#include <vcl/svapp.hxx>
#include <vcl/msgbox.hxx>
#include <vcl/gdimtf.hxx>
#include <vcl/rendergraphicrasterizer.hxx>

#include <tools/bigint.hxx>

#include <basegfx/polygon/b2dpolygon.hxx>
#include <basegfx/polygon/b2dpolypolygon.hxx>

//============================== PictWriter ===================================

struct PictWriterAttrStackMember {
    struct PictWriterAttrStackMember *  pSucc;
    Color                               aLineColor;
    Color                               aFillColor;
    RasterOp                            eRasterOp;
    Font                                aFont;
    MapMode                             aMapMode;
    Rectangle                           aClipRect;
};


enum PictDrawingMethod {
    PDM_FRAME, PDM_PAINT, PDM_ERASE, PDM_INVERT, PDM_FILL
};


struct PictPattern {
    sal_uInt32 nLo, nHi;
};

class PictWriter {

private:

    sal_Bool bStatus;
    sal_uLong nLastPercent; // Mit welcher Zahl pCallback zuletzt aufgerufen wurde.
    com::sun::star::uno::Reference< com::sun::star::task::XStatusIndicator > xStatusIndicator;

    SvStream * pPict;

    // Aktuelle Attribute im Quell-Metafile:
    Color       aLineColor;
    Color       aFillColor;
    RasterOp    eSrcRasterOp;
    Font        aSrcFont;
    MapMode     aSrcMapMode;
    MapMode     aTargetMapMode;
    Rectangle   aClipRect;
    PictWriterAttrStackMember * pAttrStack;

    // Aktuelle Attribute im Ziel-Metafile, und ob sie gueltig sind
    sal_Bool        bDstBkPatVisible;   sal_Bool bDstBkPatValid;
    sal_uInt8        nDstTxFace;            sal_Bool bDstTxFaceValid;
    RasterOp    eDstTxMode;         sal_Bool bDstTxModeValid;
    sal_uInt16      nDstPnSize;         sal_Bool bDstPnSizeValid;
    RasterOp    eDstPnMode;         sal_Bool bDstPnModeValid;
    PictPattern aDstPnPat;          sal_Bool bDstPnPatValid;
    sal_Bool        bDstFillPatVisible; sal_Bool bDstFillPatValid;
    sal_uInt16      nDstTxSize;         sal_Bool bDstTxSizeValid;
    Color       aDstFgCol;          sal_Bool bDstFgColValid;
    Color       aDstBkCol;          sal_Bool bDstBkColValid;
    Point       aDstPenPosition;    sal_Bool bDstPenPositionValid;
    Point       aDstTextPosition;   sal_Bool bDstTextPositionValid;
    String      aDstFontName; sal_uInt16 nDstFontNameId; sal_Bool bDstFontNameValid;

    sal_uLong nNumberOfActions;  // Anzahl der Actions im GDIMetafile
    sal_uLong nNumberOfBitmaps;  // Anzahl der Bitmaps
    sal_uLong nWrittenActions;   // Anzahl der bereits verarbeiteten Actions beim Schreiben der Opcodes
    sal_uLong nWrittenBitmaps;   // Anzahl der bereits geschriebenen Bitmaps
    sal_uLong nActBitmapPercent; // Wieviel Prozent die naechste Bitmap schon geschrieben ist.

    void MayCallback();
        // Berechnet anhand der obigen 5 Parameter eine Prozentzahl
        // und macht dann ggf. einen Callback. Setzt bStatus auf sal_False wenn User abbrechen
        // moechte.

    void CountActionsAndBitmaps(const GDIMetaFile & rMTF);
        // Zaehlt die Bitmaps und Actions (nNumberOfActions und nNumberOfBitmaps muessen
        // zu Anfang auf 0 gesetzt werden, weil diese Methode rekursiv ist)

    Polygon PolyPolygonToPolygon(const PolyPolygon & rPoly);
        // Macht aus einem PolyPolygon ein halbwegs vernuenftiges Polygon

    Rectangle MapRectangle( const Rectangle& rRect );
    void WritePoint(const Point & rPoint);
    void WriteSize(const Size & rSize);
    void WriteRGBColor(const Color & rColor);
    void WriteString( const String & rString );
    void WriteRectangle(const Rectangle & rRect);
    void WritePolygon(const Polygon & rPoly);
    void WriteArcAngles(const Rectangle & rRect, const Point & rStartPt, const Point & rEndPt);

    void ConvertLinePattern(PictPattern & rPat, sal_Bool bVisible) const;
    void ConvertFillPattern(PictPattern & rPat, sal_Bool bVisible) const;

    void WriteOpcode_TxFace(const Font & rFont);
    void WriteOpcode_TxMode(RasterOp eMode);
    void WriteOpcode_PnSize(sal_uInt16 nSize);
    void WriteOpcode_PnMode(RasterOp eMode);
    void WriteOpcode_PnLinePat(sal_Bool bVisible);
    void WriteOpcode_PnFillPat(sal_Bool bVisible);
    void WriteOpcode_OvSize(const Size & rSize);
    void WriteOpcode_TxSize(sal_uInt16 nSize);
    void WriteOpcode_RGBFgCol(const Color & rColor);
    void WriteOpcode_RGBBkCol(const Color & rColor);
    void WriteOpcode_Line(const Point & rLocPt, const Point & rNewPt);
    void WriteOpcode_LineFrom(const Point & rNewPt);
    void WriteOpcode_Text(const Point & rPoint, const String& rString, sal_Bool bDelta);
    void WriteOpcode_FontName(const Font & rFont);
    void WriteOpcode_ClipRect( const Rectangle& rRect );
    void WriteOpcode_Rect(PictDrawingMethod eMethod, const Rectangle & rRect);
    void WriteOpcode_SameRect(PictDrawingMethod eMethod);
    void WriteOpcode_RRect(PictDrawingMethod eMethod, const Rectangle & rRect);
    void WriteOpcode_SameRRect(PictDrawingMethod eMethod);
    void WriteOpcode_Oval(PictDrawingMethod eMethod, const Rectangle & rRect);
    void WriteOpcode_SameOval(PictDrawingMethod eMethod);
    void WriteOpcode_Arc(PictDrawingMethod eMethod, const Rectangle & rRect,
                         const Point & rStartPt, const Point & rEndPt);
    void WriteOpcode_SameArc(PictDrawingMethod eMethod, const Rectangle & rRect,
                             const Point & rStartPt, const Point & rEndPt);
    void WriteOpcode_Poly(PictDrawingMethod eMethod, const Polygon & rPoly);
    void WriteOpcode_BitsRect(const Point & rPoint, const Size & rSize, const Bitmap & rBitmap);
    void WriteOpcode_EndOfFile();

    void SetAttrForPaint();
    void SetAttrForFrame();
    void SetAttrForText();

    void WriteTextArray(Point & rPoint, const String& rString, const sal_Int32 * pDXAry);

    void HandleLineInfoPolyPolygons(const LineInfo& rInfo, const basegfx::B2DPolygon& rLinePolygon);
    void WriteOpcodes(const GDIMetaFile & rMTF);

    void WriteHeader(const GDIMetaFile & rMTF);
    void UpdateHeader();

public:

    sal_Bool WritePict( const GDIMetaFile & rMTF, SvStream & rTargetStream, FilterConfigItem* pFilterConfigItem );
};


//========================== Methoden von PictWriter ==========================


void PictWriter::MayCallback()
{
    if ( xStatusIndicator.is() )
    {
        sal_uLong nPercent;
        nPercent=((nWrittenBitmaps<<14)+(nActBitmapPercent<<14)/100+nWrittenActions)
                *100
                /((nNumberOfBitmaps<<14)+nNumberOfActions);

        if (nPercent>=nLastPercent+3)
        {
            nLastPercent=nPercent;
            if( nPercent<=100 )
                xStatusIndicator->setValue( nPercent );
        }
    }
}

void PictWriter::CountActionsAndBitmaps(const GDIMetaFile & rMTF)
{
    size_t              nAction, nActionCount;
    const MetaAction*   pMA;

    nActionCount = rMTF.GetActionSize();

    for (nAction=0; nAction < nActionCount; nAction++)
    {
        pMA = rMTF.GetAction( nAction );

        switch( pMA->GetType() )
        {
            case META_BMP_ACTION:
            case META_BMPSCALE_ACTION:
            case META_BMPSCALEPART_ACTION:
            case META_BMPEX_ACTION:
            case META_BMPEXSCALE_ACTION:
            case META_BMPEXSCALEPART_ACTION:
            case META_RENDERGRAPHIC_ACTION:
                nNumberOfBitmaps++;
            break;
        }

        nNumberOfActions++;
    }
}


Polygon PictWriter::PolyPolygonToPolygon(const PolyPolygon & rPolyPoly)
{
    sal_uInt16 nCount,nSize1,nSize2,np,i1,i2,i3,nBestIdx1,nBestIdx2;
    long nDistSqr,nBestDistSqr, nCountdownTests;
    Point aP1,aPRel;
    Polygon aPoly1, aPoly2, aPoly3;

    nCount=rPolyPoly.Count();
    if (nCount==0) return Polygon(0);

    aPoly1=rPolyPoly.GetObject(0);
    for (np=1; np<nCount; np++) {
        aPoly2=rPolyPoly.GetObject(np);

        //-----------------Folgendes verschmilzt aPoly1 und aPoly2 zu aPoly1-----------------

        nSize1=aPoly1.GetSize();
        nSize2=aPoly2.GetSize();

        // Zunaechst werden ein Punkt in aPoly1 (referenziert durch nBestIdx1) und ein
        // Punkt in aPoly2 (referenziert durch nBestIdx2) gesucht, die moeglichst dicht
        // beieinander liegen. Da dies mit quadratischem Aufwand einher geht, und somit
        // manche Bilder Ewigkeiten benoetigen, um exportiert zu werden, begrenzen wir
        // die Anzahl der Tests auf 1000, und brechen die Suche ggf. schon vorher ab.
        // Dadruch wird das Ergebnis nicht falsch, sondern eventuell nicht so schoen.
        nCountdownTests=1000;
        nBestDistSqr=0x7fffffff;
        nBestIdx1=0;
        nBestIdx2=0;
        for (i1=0; i1<nSize1; i1++) {
            aP1=aPoly1.GetPoint(i1);
            for (i2=0; i2<nSize2; i2++) {
                aPRel=aPoly2.GetPoint(i2); aPRel-=aP1;
                nDistSqr=aPRel.X()*aPRel.X()+aPRel.Y()*aPRel.Y();
                if (nDistSqr<nBestDistSqr) {
                    nBestIdx1=i1;
                    nBestIdx2=i2;
                    nBestDistSqr=nDistSqr;
                }
                if (nCountdownTests<=0) break;
                nCountdownTests--;
            }
            if (nCountdownTests<=0) break;
        }

        // Nun werden aPoly1 und aPoly2 zu einem Polygon aPoly3 (spaeter aPoly1) zusammengefuegt.
        // Die beiden Polygone werden verbunden durch zwei zusaetzliche Kanten zwischen den oben
        // gefundenen Punkten.
        aPoly3.Clear();
        aPoly3.SetSize(nSize1+nSize2+2);
        i3=0;
        for (i1=nBestIdx1; i1<nSize1;     i1++) aPoly3.SetPoint(aPoly1.GetPoint(i1),i3++);
        for (i1=0;         i1<=nBestIdx1; i1++) aPoly3.SetPoint(aPoly1.GetPoint(i1),i3++);
        for (i2=nBestIdx2; i2<nSize2;     i2++) aPoly3.SetPoint(aPoly2.GetPoint(i2),i3++);
        for (i2=0;         i2<=nBestIdx2; i2++) aPoly3.SetPoint(aPoly2.GetPoint(i2),i3++);

        aPoly1=aPoly3;

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

    }
    return aPoly1;
}


void PictWriter::WritePoint(const Point & rPoint)
{
    Point aPoint = OutputDevice::LogicToLogic( rPoint, aSrcMapMode, aTargetMapMode );
    *pPict << ((short)aPoint.Y()) << ((short)aPoint.X());
}


void PictWriter::WriteSize(const Size & rSize)
{
    OutputDevice::LogicToLogic( rSize, aSrcMapMode, aTargetMapMode ); // -Wall is this needed.
    *pPict << ((short)rSize.Height()) << ((short)rSize.Width());
}


void PictWriter::WriteRGBColor(const Color & rColor)
{
    const sal_uInt16 nR = ( (sal_uInt16) rColor.GetRed() << 8 ) | (sal_uInt16) rColor.GetRed();
    const sal_uInt16 nG = ( (sal_uInt16) rColor.GetGreen() << 8 ) | (sal_uInt16) rColor.GetGreen();
    const sal_uInt16 nB = ( (sal_uInt16) rColor.GetBlue() << 8 ) | (sal_uInt16) rColor.GetBlue();

    *pPict << nR << nG << nB;
}

void PictWriter::WriteString( const String & rString )
{
    rtl::OString aString(rtl::OUStringToOString(rString, osl_getThreadTextEncoding()));
    sal_Int32 nLen = aString.getLength();
    if ( nLen > 255 )
        nLen = 255;
    *pPict << ( (sal_uInt8)nLen );
    for (sal_Int32 i = 0; i < nLen; ++i)
        *pPict << aString[i];
}

Rectangle PictWriter::MapRectangle( const Rectangle& rRect )
{
    Point   aPoint = OutputDevice::LogicToLogic( rRect.TopLeft(), aSrcMapMode, aTargetMapMode );
    Size    aSize = OutputDevice::LogicToLogic( rRect.GetSize(), aSrcMapMode, aTargetMapMode );
    Rectangle aRect( aPoint, aSize );
    aRect.Justify();
    aRect.nBottom++;
    aRect.nRight++;
    return aRect;
}

void PictWriter::WriteRectangle(const Rectangle & rRect)
{
    Rectangle aRect( MapRectangle( rRect ) );
    *pPict  << (sal_Int16)aRect.Top() << (sal_Int16)aRect.Left()
            << (sal_Int16)aRect.Bottom() << (sal_Int16)aRect.Right();
}

void PictWriter::WritePolygon(const Polygon & rPoly)
{
    sal_uInt16 nDataSize,i,nSize;
    short nMinX = 0, nMinY = 0, nMaxX = 0, nMaxY = 0;
    short nx,ny;
    Polygon aPoly(rPoly);

    nSize=aPoly.GetSize();

    if (aPoly.GetPoint(0) != aPoly.GetPoint(nSize-1))
    {
        nSize++;
        aPoly.SetSize(nSize);
        aPoly.SetPoint(aPoly.GetPoint(0),nSize-1);
    }

    nDataSize=nSize*4+10;
    for (i=0; i<nSize; i++)
    {
        Point aPoint = OutputDevice::LogicToLogic( aPoly.GetPoint( i ),
                                                   aSrcMapMode,
                                                   aTargetMapMode );

        nx = (short) aPoint.X();
        ny = (short) aPoint.Y();

        if ( i==0 || nMinX>nx )
            nMinX=nx;
        if ( i==0 || nMinY>ny )
            nMinY=ny;
        if ( i==0 || nMaxX<nx )
            nMaxX=nx;
        if ( i==0 || nMaxY<ny )
            nMaxY=ny;
    }

    *pPict << nDataSize << nMinY << nMinX << nMaxY << nMaxX;

    for (i=0; i<nSize; i++)
        WritePoint( aPoly.GetPoint(i) );
}


void PictWriter::WriteArcAngles(const Rectangle & rRect, const Point & rStartPt, const Point & rEndPt)
{
    Point       aStartPt = OutputDevice::LogicToLogic( rStartPt,
                                                       aSrcMapMode,
                                                       aTargetMapMode );
    Point       aEndPt = OutputDevice::LogicToLogic( rEndPt,
                                                     aSrcMapMode,
                                                     aTargetMapMode );
    Rectangle   aRect( OutputDevice::LogicToLogic( rRect.TopLeft(),
                                                   aSrcMapMode,
                                                   aTargetMapMode ),
                       OutputDevice::LogicToLogic( rRect.GetSize(),
                                                   aSrcMapMode,
                                                   aTargetMapMode ) );
    Point aCenter;
    double fAngS, fAngE, fdx, fdy;
    short nStartAngle, nArcAngle;


    aCenter=Point( ( aRect.Left() + aRect.Right() ) >> 1,
                   ( aRect.Top() + aRect.Bottom() ) >> 1 );

    fdx=(double)(aStartPt.X()-aCenter.X());
    fdy=(double)(aStartPt.Y()-aCenter.Y());
    if ( fdx==0.0 && fdy==0.0 )
        fdx=1.0;
    fAngE=atan2(fdx,-fdy);

    fdx=(double)(aEndPt.X()-aCenter.X());
    fdy=(double)(aEndPt.Y()-aCenter.Y());
    if ( fdx==0.0 && fdy==0.0 )
        fdx=1.0;
    fAngS=atan2(fdx,-fdy);

    nStartAngle=(short)(fAngS*180.0/3.14159265359);
    nArcAngle=((short)(fAngE*180.0/3.14159265359))-nStartAngle;
    if (nArcAngle<0)
        nArcAngle += 360;
    *pPict << nStartAngle << nArcAngle;
}


void PictWriter::ConvertLinePattern(PictPattern & rPat, sal_Bool bVisible) const
{
    if( bVisible )
    {
        rPat.nHi=0xffffffff;
        rPat.nLo=0xffffffff;
    }
    else
    {
        rPat.nHi=0x00000000;
        rPat.nLo=0x00000000;
    }
}

void PictWriter::ConvertFillPattern(PictPattern & rPat, sal_Bool bVisible) const
{
    if( bVisible )
    {
        rPat.nHi=0xffffffff;
        rPat.nLo=0xffffffff;
    }
    else
    {
        rPat.nHi=0x00000000;
        rPat.nLo=0x00000000;
    }
}


void PictWriter::WriteOpcode_TxFace(const Font & rFont)
{
    sal_uInt8 nFace;
    FontWeight eWeight;

    nFace=0;
    eWeight=rFont.GetWeight();
    if (eWeight==WEIGHT_BOLD ||
        eWeight==WEIGHT_SEMIBOLD ||
        eWeight==WEIGHT_ULTRABOLD ||
        eWeight==WEIGHT_BLACK)                nFace|=0x01;
    if (rFont.GetItalic()!=ITALIC_NONE)       nFace|=0x02;
    if (rFont.GetUnderline()!=UNDERLINE_NONE) nFace|=0x04;
    if (rFont.IsOutline()==sal_True)              nFace|=0x08;
    if (rFont.IsShadow()==sal_True)               nFace|=0x10;

    if (bDstTxFaceValid==sal_False || nDstTxFace!=nFace) {
        *pPict << (sal_uInt16)0x0004 << nFace << (sal_uInt8)0;
        nDstTxFace=nFace;
        bDstTxFaceValid=sal_True;
    }
}


void PictWriter::WriteOpcode_TxMode(RasterOp eMode)
{
    sal_uInt16 nVal;

    if (bDstTxModeValid==sal_False || eDstTxMode!=eMode) {
        switch (eMode) {
            case ROP_INVERT: nVal=0x000c; break;
            case ROP_XOR:    nVal=0x000a; break;
            default:         nVal=0x0008;
        }
        *pPict << (sal_uInt16)0x0005 << nVal;
        eDstTxMode=eMode;
        bDstTxModeValid=sal_True;
    }
}


void PictWriter::WriteOpcode_PnSize(sal_uInt16 nSize)
{
    if (nSize==0) nSize=1;
    if (bDstPnSizeValid==sal_False || nDstPnSize!=nSize) {
        *pPict << (sal_uInt16)0x0007 << nSize << nSize;
        nDstPnSize=nSize;
        bDstPnSizeValid=sal_True;
    }
}


void PictWriter::WriteOpcode_PnMode(RasterOp eMode)
{
    sal_uInt16 nVal;

    if (bDstPnModeValid==sal_False || eDstPnMode!=eMode) {
        switch (eMode)
        {
            case ROP_INVERT: nVal=0x000c; break;
            case ROP_XOR:    nVal=0x000a; break;
            default:         nVal=0x0008;
        }
        *pPict << (sal_uInt16)0x0008 << nVal;
        eDstPnMode=eMode;
        bDstPnModeValid=sal_True;
    }
}


void PictWriter::WriteOpcode_PnLinePat(sal_Bool bVisible)
{
    PictPattern aPat;

    ConvertLinePattern(aPat,bVisible);
    if (bDstPnPatValid==sal_False || aDstPnPat.nHi!=aPat.nHi || aDstPnPat.nLo!=aPat.nLo) {
        *pPict << (sal_uInt16)0x0009 << aPat.nHi << aPat.nLo;
        aDstPnPat=aPat;
        bDstPnPatValid=sal_True;
    }
}


void PictWriter::WriteOpcode_PnFillPat(sal_Bool bVisible)
{
    PictPattern aPat;

    ConvertFillPattern(aPat,bVisible);
    if (bDstPnPatValid==sal_False || aDstPnPat.nHi!=aPat.nHi || aDstPnPat.nLo!=aPat.nLo) {
        *pPict << (sal_uInt16)0x0009 << aPat.nHi << aPat.nLo;
        aDstPnPat=aPat;
        bDstPnPatValid=sal_True;
    }
}


void PictWriter::WriteOpcode_OvSize(const Size & rSize)
{
    *pPict << (sal_uInt16)0x000b;
    WriteSize(rSize);
}


void PictWriter::WriteOpcode_TxSize(sal_uInt16 nSize)
{
    if (bDstTxSizeValid==sal_False || nDstTxSize!=nSize) {

        nDstTxSize = (sal_uInt16) OutputDevice::LogicToLogic( Size( 0, nSize ),
                                                          aSrcMapMode, aTargetMapMode ).Height();

        *pPict << (sal_uInt16)0x000d << nDstTxSize;
        bDstTxSizeValid=sal_True;
    }
}


void PictWriter::WriteOpcode_RGBFgCol(const Color & rColor)
{
    if (bDstFgColValid==sal_False || aDstFgCol!=rColor) {
        *pPict << (sal_uInt16)0x001a;
        WriteRGBColor(rColor);
        aDstFgCol=rColor;
        bDstFgColValid=sal_True;
    }
}


void PictWriter::WriteOpcode_RGBBkCol(const Color & rColor)
{
    if (bDstBkColValid==sal_False || aDstBkCol!=rColor) {
        *pPict << (sal_uInt16)0x001b;
        WriteRGBColor(rColor);
        aDstBkCol=rColor;
        bDstBkColValid=sal_True;
    }
}


void PictWriter::WriteOpcode_Line(const Point & rLocPt, const Point & rNewPt)
{
    Point aLocPt = OutputDevice::LogicToLogic( rLocPt,
                                               aSrcMapMode,
                                               aTargetMapMode );
    Point aNewPt = OutputDevice::LogicToLogic( rNewPt,
                                               aSrcMapMode,
                                               aTargetMapMode );
    long  dh,dv;

    dh=aNewPt.X()-aLocPt.X();
    dv=aNewPt.Y()-aLocPt.Y();
    if (dh<=127 && dh>=-128 && dv<=127 && dv>=-128)
    { // ShortLine
        *pPict << (sal_uInt16)0x0022;
        WritePoint(rLocPt);
        *pPict << (char)dh << (char)dv;
    }
    else
    {
        *pPict << (sal_uInt16)0x0020;
        WritePoint(rLocPt);
        WritePoint(rNewPt);
    }
    aDstPenPosition=rNewPt;
    bDstPenPositionValid=sal_True;
}


void PictWriter::WriteOpcode_LineFrom(const Point & rNewPt)
{
    Point aNewPt = OutputDevice::LogicToLogic( rNewPt,
                                               aSrcMapMode,
                                               aTargetMapMode );
    long  dh,dv;

    dh = aNewPt.X()-aDstPenPosition.X();
    dv = aNewPt.Y()-aDstPenPosition.Y();

    if (dh<=127 && dh>=-128 && dv<=127 && dv>=-128)
    { // ShortLine
        *pPict << (sal_uInt16)0x0023;
        *pPict << (char)dh << (char)dv;
    }
    else
    {
        *pPict << (sal_uInt16)0x0021;
        WritePoint(rNewPt);
    }
    aDstPenPosition=rNewPt;
    bDstPenPositionValid=sal_True;
}


void PictWriter::WriteOpcode_Text(const Point & rPoint, const String& rString, sal_Bool bDelta)
{
    Point aPoint = OutputDevice::LogicToLogic( rPoint,
                                               aSrcMapMode,
                                               aTargetMapMode );
    long  dh,dv;
    sal_uLong nPos;

    nPos = pPict->Tell();
    dh = aPoint.X()-aDstTextPosition.X();
    dv = aPoint.Y()-aDstTextPosition.Y();

    if (bDstTextPositionValid==sal_False || dh<0 || dh>255 || dv<0 || dv>0 || bDelta==sal_False)
    {
        *pPict << (sal_uInt16)0x0028;
        WritePoint(rPoint);
    }
    else if (dv==0)
    {
        *pPict << (sal_uInt16)0x0029 << (sal_uInt8)dh;
    }
    else if (dh==0)
    {
        *pPict << (sal_uInt16)0x002a << (sal_uInt8)dv;
    }
    else
    {
        *pPict << (sal_uInt16)0x002b << (sal_uInt8)dh << (sal_uInt8)dv;
    }

    WriteString( rString );
    if (((pPict->Tell()-nPos)&1)!=0)
        *pPict << (sal_uInt8)0;

    aDstTextPosition = aPoint;
    bDstTextPositionValid=sal_True;
}


void PictWriter::WriteOpcode_FontName(const Font & rFont)
{
    sal_uInt16 nDataLen,nFontId;

    switch (rFont.GetFamily()) {
        case FAMILY_MODERN:     nFontId=22; break;
        case FAMILY_ROMAN:      nFontId=20; break;
        case FAMILY_SWISS:      nFontId=21; break;
        default:                nFontId=1;
    }

    if (bDstFontNameValid==sal_False || nDstFontNameId!=nFontId || aDstFontName!=rFont.GetName())
    {
        rtl::OString aString(rtl::OUStringToOString(rFont.GetName(), osl_getThreadTextEncoding()));
        sal_uInt16 nFontNameLen = aString.getLength();
        if ( nFontNameLen )
        {
            nDataLen = 3 + nFontNameLen;
            *pPict << (sal_uInt16)0x002c << nDataLen << nFontId;
            WriteString( rFont.GetName() );
            if ( ( nFontNameLen & 1 ) == 0 )
                *pPict << (sal_uInt8)0;
        }
        *pPict << (sal_uInt16)0x0003 << nFontId;
        aDstFontName=rFont.GetName();
        nDstFontNameId=nFontId;
        bDstFontNameValid=sal_True;
    }
}

void PictWriter::WriteOpcode_ClipRect( const Rectangle& rRect )
{
    Rectangle aRect( MapRectangle( rRect ) );
    aRect.nBottom++;
    aRect.nRight++;
    *pPict  << (sal_uInt16)1    // opcode 1
            << (sal_uInt16)10   // data size
            << (sal_Int16)aRect.Top() << (sal_Int16)aRect.Left()
            << (sal_Int16)aRect.Bottom() << (sal_Int16)aRect.Right();
    aClipRect = aRect;
}

void PictWriter::WriteOpcode_Rect(PictDrawingMethod eMethod, const Rectangle & rRect)
{
    sal_uInt16 oc;
    switch (eMethod) {
        case PDM_FRAME:  oc=0x0030; break;
        case PDM_PAINT:  oc=0x0031; break;
        case PDM_ERASE:  oc=0x0032; break;
        case PDM_INVERT: oc=0x0033; break;
        case PDM_FILL:   oc=0x0034; break;
        default:         oc=0;      break;   // -Wall a default for oc...
    }
    *pPict << oc;
    WriteRectangle(rRect);
}


void PictWriter::WriteOpcode_SameRect(PictDrawingMethod eMethod)
{
    sal_uInt16 oc;
    switch (eMethod) {
        case PDM_FRAME:  oc=0x0038; break;
        case PDM_PAINT:  oc=0x0039; break;
        case PDM_ERASE:  oc=0x003a; break;
        case PDM_INVERT: oc=0x003b; break;
        case PDM_FILL:   oc=0x003c; break;
        default:         oc=0;      break;   // -Wall a default for oc...
    }
    *pPict << oc;
}


void PictWriter::WriteOpcode_RRect(PictDrawingMethod eMethod, const Rectangle & rRect)
{
    sal_uInt16 oc;
    switch (eMethod) {
        case PDM_FRAME:  oc=0x0040; break;
        case PDM_PAINT:  oc=0x0041; break;
        case PDM_ERASE:  oc=0x0042; break;
        case PDM_INVERT: oc=0x0043; break;
        case PDM_FILL:   oc=0x0044; break;
        default:         oc=0;      break;   // -Wall a default for oc...
    }
    *pPict << oc;
    WriteRectangle(rRect);
}


void PictWriter::WriteOpcode_SameRRect(PictDrawingMethod eMethod)
{
    sal_uInt16 oc;
    switch (eMethod) {
        case PDM_FRAME:  oc=0x0048; break;
        case PDM_PAINT:  oc=0x0049; break;
        case PDM_ERASE:  oc=0x004a; break;
        case PDM_INVERT: oc=0x004b; break;
        case PDM_FILL:   oc=0x004c; break;
        default:         oc=0;      break;   // -Wall a default for oc...
    }
    *pPict << oc;
}


void PictWriter::WriteOpcode_Oval(PictDrawingMethod eMethod, const Rectangle & rRect)
{
    sal_uInt16 oc;
    switch (eMethod) {
        case PDM_FRAME:  oc=0x0050; break;
        case PDM_PAINT:  oc=0x0051; break;
        case PDM_ERASE:  oc=0x0052; break;
        case PDM_INVERT: oc=0x0053; break;
        case PDM_FILL:   oc=0x0054; break;
        default:         oc=0;      break;   // -Wall a default for oc...
    }
    *pPict << oc;
    WriteRectangle(rRect);
}


void PictWriter::WriteOpcode_SameOval(PictDrawingMethod eMethod)
{
    sal_uInt16 oc;
    switch (eMethod) {
        case PDM_FRAME:  oc=0x0058; break;
        case PDM_PAINT:  oc=0x0059; break;
        case PDM_ERASE:  oc=0x005a; break;
        case PDM_INVERT: oc=0x005b; break;
        case PDM_FILL:   oc=0x005c; break;
        default:         oc=0;      break;   // -Wall a default for oc...
    }
    *pPict << oc;
}


void PictWriter::WriteOpcode_Arc(PictDrawingMethod eMethod, const Rectangle & rRect,
                                 const Point & rStartPt, const Point & rEndPt)
{
    sal_uInt16 oc;
    switch (eMethod) {
        case PDM_FRAME:  oc=0x0060; break;
        case PDM_PAINT:  oc=0x0061; break;
        case PDM_ERASE:  oc=0x0062; break;
        case PDM_INVERT: oc=0x0063; break;
        case PDM_FILL:   oc=0x0064; break;
        default:         oc=0;      break;   // -Wall a default for oc...
    }
    *pPict << oc;
    WriteRectangle(rRect);
    WriteArcAngles(rRect,rStartPt,rEndPt);
}


void PictWriter::WriteOpcode_SameArc(PictDrawingMethod eMethod, const Rectangle & rRect,
                                     const Point & rStartPt, const Point & rEndPt)
{
    sal_uInt16 oc;
    switch (eMethod) {
        case PDM_FRAME:  oc=0x0068; break;
        case PDM_PAINT:  oc=0x0069; break;
        case PDM_ERASE:  oc=0x006a; break;
        case PDM_INVERT: oc=0x006b; break;
        case PDM_FILL:   oc=0x006c; break;
        default:         oc=0;      break;   // -Wall a default for oc...
    }
    *pPict << oc;
    WriteArcAngles(rRect,rStartPt,rEndPt);
}


void PictWriter::WriteOpcode_Poly(PictDrawingMethod eMethod, const Polygon & rPoly)
{
    sal_uInt16 oc;

    if (rPoly.GetSize()<3) return;
    switch (eMethod) {
        case PDM_FRAME:  oc=0x0070; break;
        case PDM_PAINT:  oc=0x0071; break;
        case PDM_ERASE:  oc=0x0072; break;
        case PDM_INVERT: oc=0x0073; break;
        case PDM_FILL:   oc=0x0074; break;
        default:         oc=0;      break;   // -Wall a default for oc...
    }
    *pPict << oc;
    WritePolygon(rPoly);
}


void PictWriter::WriteOpcode_BitsRect(const Point & rPoint, const Size & rSize, const Bitmap & rBitmap)
{
    BitmapReadAccess*   pAcc = NULL;
    Bitmap              aBitmap( rBitmap );

    sal_uLong   nWidth, nHeight, nDstRowBytes, nx, nc, ny, nCount, nColTabSize, i;
    sal_uLong   nDstRowPos, nSrcRowBytes, nEqu3, nPos, nDstMapPos;
    sal_uInt16  nBitsPerPixel, nPackType;
    sal_uInt8   *pComp[4], *pPix, *pTemp;
    sal_uInt8    nEquData = 0;
    sal_uInt8    nFlagCounterByte, nRed, nGreen, nBlue;

    SetAttrForPaint();

    // temopraere Windows-BMP-Datei erzeugen:
    nActBitmapPercent=30;
    MayCallback();

    if ( bStatus == sal_False )
        return;
    if ( ( pAcc = aBitmap.AcquireReadAccess() ) == NULL )
        return;

    nBitsPerPixel = aBitmap.GetBitCount();

    // export code below only handles four discrete cases
    nBitsPerPixel =
        nBitsPerPixel <= 1 ? 1 : nBitsPerPixel <= 4 ? 4 : nBitsPerPixel <= 8 ? 8 : 24;

    nWidth = pAcc->Width();
    nHeight = pAcc->Height();

    // Wenn 24-Bit, dann den Opcode 'DirectBitsRect' erzeugen:
    if ( nBitsPerPixel == 24 )
    {

        // Anzahl Bytes einer (ungepackten) Zeile in Quelle und Ziel berechnen:
        nSrcRowBytes =( ( 3 * nWidth ) + 0x0003 ) & 0xfffc;
        nDstRowBytes = nWidth * 4;

        // Opcode und BaseAddr (?) schreiben:
        *pPict << (sal_uInt16)0x009a << (sal_uInt32)0x000000ff;

        // Normalerweise wollen wir den Packing-Type 4 (Run length encoding
        // for 32-Bit Pixels) erzeugen. Wenn aber RowBytes<8 gilt, sind die Daten
        // grundsaetzlich ungepackt, auch wenn der Packing-Type 4 angegeben ist,
        // was etwas komisch erscheint. Daher wollen wir in so einem Fall lieber
        // gleich den Packing-Type 1 (ungepackt) angeben:

        if ( nDstRowBytes < 8 )
            nPackType = 1;
        else
            nPackType = 4;

        // PixMap-Struktur schreiben:
        *pPict << (sal_uInt16)(nDstRowBytes|0x8000) // Bytes pro Zeile und dass es eine 'PixMap' ist
               << (sal_uInt16)0x0000                // Y1-Position der Bitmap in der Quelle
               << (sal_uInt16)0x0000                // X1-Position der Bitmap in der Quelle
               << (sal_uInt16)nHeight               // Y2-Position der Bitmap in der Quelle
               << (sal_uInt16)nWidth                // X2-Position der Bitmap in der Quelle
               << (sal_uInt16)0x0000                // Version
               << (sal_uInt16)nPackType             // Packing type
               << (sal_uInt32) 0x00000000            // Packing size (?)
               << (sal_uInt32) 0x00480000            // H-Res
               << (sal_uInt32) 0x00480000            // V-Res
               << (sal_uInt16)0x0010                // Pixel type (?)
               << (sal_uInt16)0x0020                // Pixel size: 32 bit
               << (sal_uInt16)0x0004                // CmpCount: 4 Komponenten
               << (sal_uInt16)0x0008                // CmpSize: 8 Bits
               << (sal_uInt32) 0x00000000            // PlaneBytes (?)
               << (sal_uInt32) 0x00000000            // (?)
               << (sal_uInt32) 0x00000000;           // (?)

        // Source-Rectangle schreiben:
        *pPict << (sal_uInt16)0x0000                // Y1-Position auf der Bitmap
               << (sal_uInt16)0x0000                // X1-Position auf der Bitmap
               << (sal_uInt16)nHeight               // Y2-Position auf der Bitmap
               << (sal_uInt16)nWidth;               // X2-Position auf der Bitmap

        // Destination-Rectangle schreiben:
        WritePoint( rPoint );
        WritePoint( Point( rPoint.X() + rSize.Width(), rPoint.Y() + rSize.Height() ) );

        // Transfer mode schreiben:
        *pPict << (sal_uInt16)0x0000; // (?)

        // Position der Map-Daten in Ziel merken:
        nDstMapPos=pPict->Tell();

        if ( nPackType == 1 )               // bei 24 bits nWidth == 1 !!
        {                                   // nicht packen
            for ( ny = 0; ny < nHeight; ny++ )
            {
                *pPict << (sal_uInt8)0;
                *pPict << (sal_uInt8)pAcc->GetPixel( ny, 0 ).GetRed();
                *pPict << (sal_uInt8)pAcc->GetPixel( ny, 0 ).GetGreen();
                *pPict << (sal_uInt8)pAcc->GetPixel( ny, 0 ).GetBlue();
                // Prozente zaehlen, Callback, Fehler pruefen:
                nActBitmapPercent = ( ny * 70 / nHeight ) + 30; // (30% machten schon das Schreiben der Win-BMP-Datei aus)
                MayCallback();
            }
        }
        else    // packen ( PackType == 4 )
        {
            // Speicher fuer Zeilen-Zwischen-Datenstruktur allozieren:
            for ( nc = 0; nc < 4; nc++ )
                pComp[ nc ] = new sal_uInt8[ nWidth ];

            // Schleife ueber Zeilen:
            for ( ny = 0; ny < nHeight; ny++ )
            {
                // Zeil ny der Quelle in die Zwischen-Datenstrktur einlesen:

                for ( nx = 0; nx < nWidth; nx++ )
                {
                    pComp[ 1 ][ nx ] = (sal_uInt8)pAcc->GetPixel( ny, nx ) .GetRed();
                    pComp[ 2 ][ nx ] = (sal_uInt8)pAcc->GetPixel( ny, nx ) .GetGreen();
                    pComp[ 3 ][ nx ] = (sal_uInt8)pAcc->GetPixel( ny, nx ) .GetBlue();
                    pComp[ 0 ][ nx ] = 0;
                }

                // Anfang der Zeile im Ziel merken:
                nDstRowPos = pPict->Tell();

                // ByteCount (das ist die Groesse der gepackten Zeile) zunaechst 0 (wird spaeter berichtigt):
                if ( nDstRowBytes > 250 )
                    *pPict << (sal_uInt16)0;
                else
                    *pPict << (sal_uInt8)0;

                // Schleife ueber Componenten:
                for ( nc = 0; nc < 4; nc++ )
                {
                    // Schleife ueber x:
                    nx = 0;
                    while ( nx < nWidth )
                    {
                        // Die Position von 3 gleichen Bytes suchen und in nEqu3 merken.
                        // wenn nicht gefunden, dann nEqu3=nWidth setzten.
                        // Wenn doch gefunden, dann in nEquData den Wert der Bytes merken.
                        nEqu3 = nx;
                        for (;;)
                        {
                            if ( nEqu3 + 2 >= nWidth )
                            {
                                nEqu3 = nWidth;
                                break;
                            }
                            nEquData = pComp[nc][nEqu3];
                            if ( nEquData == pComp[nc][nEqu3+1] && nEquData==pComp[nc][nEqu3+2] )
                                break;
                            nEqu3++;
                        }

                        // Die Daten von nx bis nEqu3 unkomprimiert schreiben (ggf. in mehreren Records):
                        while ( nEqu3 > nx )
                        {
                            nCount = nEqu3 - nx;
                            if ( nCount > 128 )
                                nCount=128;
                            nFlagCounterByte = (sal_uInt8)(nCount-1);
                            *pPict << nFlagCounterByte;
                            do
                            {
                                *pPict << pComp[nc][nx++];
                                nCount--;
                            }
                            while ( nCount > 0 );
                        }

                        // Jetzt einen Komprimierungs-Record erzeugen (falls oben mindestens 3
                        // gleiche Bytes gefunden):
                        if ( nx < nWidth )
                        {               // Hinweis: es gilt nx==nEqu3 (hoffentlich)
                            nCount=3;   // Drei Bytes sind gleich, wie weiter oben herausgefunden.
                                        // Pruefen, ob es weitere gleiche Bytes gibts (dabei Max.-Record-Groesse beachten):
                            while ( nx + nCount < nWidth && nCount < 128 )
                            {
                                if ( nEquData != pComp[ nc ][ nx + nCount ] )
                                    break;
                                nCount++;
                            }
                            // nCount gleiche Bytes komprimiert schreiben:
                            nFlagCounterByte = (sal_uInt8)( 1 - (long)nCount );
                            *pPict << nFlagCounterByte << nEquData;
                            nx += nCount;
                        }
                    }
                }
                // ByteCount berichtigen:
                nPos = pPict->Tell();
                pPict->Seek( nDstRowPos );
                if ( nDstRowBytes > 250 )
                    *pPict << ( (sal_uInt16)( nPos - nDstRowPos - 2 ) );
                else
                    *pPict << ( (sal_uInt8)( nPos - nDstRowPos - 1 ) );
                pPict->Seek( nPos );

                // Prozente zaehlen, Callback, Fehler pruefen:
                nActBitmapPercent = ( ny * 70 / nHeight ) + 30; // (30% machten schon das Schreiben der Win-BMP-Datei aus)
                MayCallback();
            }
            // Aufraeumen:
            for ( nc = 0; nc < 4; nc++ )
                delete pComp[ nc ];
        }
    }
    else
    {   // nicht 24-Bit also Opcode 'PackBitsRect' erzeugen:

        // Bei 1-Bit-Bildern ignorieren manche Import-Filter die Palette und nehmen statt
        // dessen die Vorder- und Hintergrundfarbe:
        if ( nBitsPerPixel == 1 )
        {
            WriteOpcode_RGBBkCol( pAcc->GetPaletteColor( 0 ) );
            WriteOpcode_RGBFgCol( pAcc->GetPaletteColor( 1 ) );
        }
        else
        {
            WriteOpcode_RGBBkCol( Color( COL_BLACK ) );
            WriteOpcode_RGBFgCol( Color( COL_WHITE ) );
        }

        // Anzahl Bytes einer (ungepackten) Zeile in Ziel und Quelle berechnen:
        nDstRowBytes = ( nWidth * nBitsPerPixel + 7 ) >> 3;
        nSrcRowBytes = ( nDstRowBytes + 3 ) & 0xfffffffc;

        // Opcode schreiben:
        *pPict << (sal_uInt16)0x0098;

        // Normalerweise wollen wir den Packing-Type 0 (default Packing) erzeugen.
        // Wenn aber RowBytes<8 gilt, sind die Daten grundsaetzlich ungepackt,
        // auch wenn der Packing-Type 0 angegeben ist, was etwas komisch erscheint.
        // Daher wollen wir in so einem Fall lieber gleich den Packing-Type 1 (ungepackt)
        // angeben:
        if ( nDstRowBytes < 8 )
            nPackType = 1;
        else
            nPackType = 0;

        // PixMap-Struktur schreiben:
        *pPict << (sal_uInt16)(nDstRowBytes|0x8000) // Bytes pro Zeile und dass es eine 'PixMap' ist
               << (sal_uInt16)0x0000                // Y1-Position der Bitmap in der Quelle
               << (sal_uInt16)0x0000                // X1-Position der Bitmap in der Quelle
               << (sal_uInt16)nHeight               // Y2-Position der Bitmap in der Quelle
               << (sal_uInt16)nWidth                // X2-Position der Bitmap in der Quelle
               << (sal_uInt16)0x0000                // Version
               << (sal_uInt16)nPackType             // Packing type
               << (sal_uInt32) 0x00000000            // Packing size (?)
               << (sal_uInt32) 0x00480000            // H-Res
               << (sal_uInt32) 0x00480000            // V-Res
               << (sal_uInt16)0x0000                // Pixel type (?)
               << (sal_uInt16)nBitsPerPixel         // Pixel size
               << (sal_uInt16)0x0001                // CmpCount: 1 Komponente
               << (sal_uInt16)nBitsPerPixel         // CmpSize
               << (sal_uInt32) 0x00000000            // PlaneBytes (?)
               << (sal_uInt32) 0x00000000            // (?)
               << (sal_uInt32) 0x00000000;           // (?)

        // Palette lesen und schreiben:
        nColTabSize = pAcc->GetPaletteEntryCount();
        *pPict << (sal_uInt32)0 << (sal_uInt16)0x8000 << (sal_uInt16)( nColTabSize - 1 );

        for ( i = 0; i < nColTabSize; i++ )
        {
            nRed = (sal_uInt8)pAcc->GetPaletteColor( (sal_uInt16)i ).GetRed();
            nGreen = (sal_uInt8)pAcc->GetPaletteColor( (sal_uInt16)i ).GetGreen();
            nBlue = (sal_uInt8)pAcc->GetPaletteColor( (sal_uInt16)i ).GetBlue();
            *pPict << (sal_uInt16)0 << nRed << nRed << nGreen << nGreen << nBlue << nBlue;
        }

        // Source-Rectangle schreiben:
        *pPict << (sal_uInt16)0 << (sal_uInt16)0 << (sal_uInt16)nHeight << (sal_uInt16)nWidth;

        // Destination-Rectangle schreiben:
        WritePoint( rPoint );
        WritePoint( Point( rPoint.X() + rSize.Width(), rPoint.Y() + rSize.Height() ) );

        // Transfer mode schreiben:
        *pPict << (sal_uInt16)0;            // (?)

        // Speicher fuer eine Zeile allozieren:
        pPix = new sal_uInt8[ nSrcRowBytes ];

        // Position der Map-Daten in Ziel merken:
        nDstMapPos=pPict->Tell();

        // Schleife ueber Zeilen:
        for ( ny = 0; ny < nHeight; ny++ )
        {

            // Zeile ny der Quelle in den Zwischenspeicher einlesen:

            switch ( nBitsPerPixel )
            {
                case 1 :
                    for ( pTemp = pPix, i = 0; i < nSrcRowBytes; i++ )
                        *pTemp++ = (sal_uInt8)0;
                    for ( i = 0; i < nWidth; i++ )
                        pPix[ ( i >> 3 ) ] |= (sal_uInt8)( pAcc->GetPixel( ny, i ) & 1 ) << ( ( i & 7 ) ^ 7 );
                    break;
                case 4 :
                    for ( pTemp = pPix, i = 0; i < nSrcRowBytes; i++ )
                        *pTemp++ = (sal_uInt8)0;
                    for ( i = 0; i < nWidth; i++ )
                        pPix[ ( i >> 1 ) ] |= (sal_uInt8)( pAcc->GetPixel( ny, i ) & 15 ) << ( ( i & 1 ) << 2 ) ;
                    break;
                case 8 :
                    for ( i = 0; i < nWidth; i++ )
                        pPix[ i ] = (sal_uInt8)pAcc->GetPixel( ny, i );
                    break;
            }

            if ( nPackType == 1 )
            {   // nicht packen
                pPict->Write( pPix, nDstRowBytes );
            }
            else
            {   // Packen (nPackType==0)

                // Anfang der Zeile im Ziel merken:
                nDstRowPos = pPict->Tell();

                // ByteCount (das ist die Groesse der gepackten Zeile) zunaechst 0 (wird spaeter berichtigt):
                if ( nDstRowBytes > 250 )
                    *pPict << (sal_uInt16)0;
                else
                    *pPict << (sal_uInt8)0;

                // Schleife ueber Bytes der Zeile:
                nx=0;
                while ( nx < nDstRowBytes && bStatus )
                {
                    // Die Position von 3 gleichen Bytes suchen und in nEqu3 merken.
                    // wenn nicht gefunden, dann nEqu3=nDstRowBytes setzten.
                    // Wenn doch gefunden, dann in nEquData den Wert der Bytes merken.
                    nEqu3 = nx;
                    for (;;)
                    {
                        if ( nEqu3 + 2 >= nDstRowBytes )
                        {
                            nEqu3 = nDstRowBytes;
                            break;
                        }
                        nEquData = pPix[ nEqu3 ];
                        if ( nEquData == pPix[ nEqu3 + 1 ] && nEquData == pPix[ nEqu3 + 2 ] )
                            break;
                        nEqu3++;
                    }

                    // Die Daten von nx bis nEqu3 unkomprimiert schreiben (ggf. in mehreren Records):
                    while ( nEqu3 > nx )
                    {
                        nCount = nEqu3 - nx;
                        if ( nCount > 128 )
                            nCount = 128;
                        nFlagCounterByte = (sal_uInt8)( nCount - 1 );
                        *pPict << nFlagCounterByte;
                        do
                        {
                            *pPict << pPix[ nx++ ];
                            nCount--;
                        } while ( nCount > 0 );
                    }

                    // Jetzt einen Komprimierungs-Record erzeugen (falls oben mindestens 3
                    // gleiche Bytes gefunden):
                    if ( nx < nDstRowBytes )
                    {   // Hinweis: es gilt nx==nEqu3 (hoffentlich)
                        nCount = 3; // Drei Bytes sind gleich, wie weiter oben herausgefunden.
                        // Pruefen, ob es weitere gleiche Bytes gibts (dabei Max.-Record-Groesse beachten):
                        while ( nx + nCount < nDstRowBytes && nCount < 128 )
                        {
                            if ( nEquData != pPix[ nx + nCount ] )
                                break;
                            nCount++;
                        }
                        // nCount gleiche Bytes komprimiert schreiben:
                        nFlagCounterByte = (sal_uInt8)( 1 - (long)nCount );
                        *pPict << nFlagCounterByte << nEquData;
                        nx += nCount;
                    }
                }

                // ByteCount berichtigen:
                nPos = pPict->Tell();
                pPict->Seek( nDstRowPos );
                if ( nDstRowBytes > 250 )
                    *pPict << ( (sal_uInt16)( nPos - nDstRowPos - 2 ) );
                else
                    *pPict << ( (sal_uInt8)( nPos - nDstRowPos - 1 ) );
                pPict->Seek( nPos );
            }

            // Prozente zaehlen, Callback, Fehler pruefen:
            nActBitmapPercent =( ny * 70 / nHeight ) + 30; // (30% machten schon das Schreiben der Win-BMP-Datei aus)
            MayCallback();
            if ( pPict->GetError() )
                bStatus = sal_False;
        }
        // Aufraeumen:
        delete[] pPix;
    }

    // Map-Daten muessen gerade Anzahl von Bytes sein:
    if ( ( ( pPict->Tell() - nDstMapPos ) & 1 ) != 0 )
        *pPict << (sal_uInt8)0;

    // Bitmaps zaehlen:
    nWrittenBitmaps++;
    nActBitmapPercent = 0;
    if ( pAcc )
        aBitmap.ReleaseAccess( pAcc );
}

void PictWriter::WriteOpcode_EndOfFile()
{
    *pPict << (sal_uInt16)0x00ff;
}


void PictWriter::SetAttrForPaint()
{
    WriteOpcode_PnMode(eSrcRasterOp);
    WriteOpcode_RGBFgCol(aFillColor);
    WriteOpcode_RGBBkCol(aFillColor);
    WriteOpcode_PnFillPat(aFillColor!=Color( COL_TRANSPARENT ));
}


void PictWriter::SetAttrForFrame()
{
    WriteOpcode_PnMode(eSrcRasterOp);
    WriteOpcode_PnSize(0);
    WriteOpcode_RGBFgCol(aLineColor);
    WriteOpcode_PnLinePat(aLineColor!=Color( COL_TRANSPARENT ));
}


void PictWriter::SetAttrForText()
{
    WriteOpcode_RGBFgCol(aSrcFont.GetColor());
    WriteOpcode_RGBBkCol(aSrcFont.GetFillColor());
    WriteOpcode_PnLinePat(sal_True);
    WriteOpcode_FontName(aSrcFont);
    WriteOpcode_TxSize((sal_uInt16)(aSrcFont.GetSize().Height()));
    WriteOpcode_TxMode(eSrcRasterOp);
    WriteOpcode_TxFace(aSrcFont);
}


void PictWriter::WriteTextArray(Point & rPoint, const String& rString, const sal_Int32 * pDXAry)
{
    sal_uInt16 i,nLen;
    sal_Unicode c;
    sal_Bool bDelta;
    Point aPt;

    if ( pDXAry == NULL )
        WriteOpcode_Text( rPoint, rString, sal_False );
    else
    {
        bDelta = sal_False;
        nLen = rString.Len();
        for ( i = 0; i < nLen; i++ )
        {
            c = rString.GetChar( i );
            if ( c && ( c != 0x20 ) )
            {
                aPt = rPoint;
                if ( i > 0 )
                    aPt.X() += pDXAry[ i - 1 ];

                WriteOpcode_Text( aPt, String( c ), bDelta );
                bDelta = sal_True;
            }
        }
    }
}

void PictWriter::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())
        {
            aLinePolyPolygon = aLinePolyPolygon.getDefaultAdaptiveSubdivision();
            const sal_uInt32 nPolyCount(aLinePolyPolygon.count());
            SetAttrForFrame();

            for(sal_uInt32 a(0); a < nPolyCount; a++)
            {
                const basegfx::B2DPolygon aCandidate(aLinePolyPolygon.getB2DPolygon(a));
                const sal_uInt32 nPointCount(aCandidate.count());

                if(nPointCount)
                {
                    const sal_uInt32 nEdgeCount(aCandidate.isClosed() ? nPointCount + 1 : nPointCount);
                    const basegfx::B2DPoint aCurr(aCandidate.getB2DPoint(0));
                    Point nCurr(basegfx::fround(aCurr.getX()), basegfx::fround(aCurr.getY()));

                    for(sal_uInt32 b(0); b < nEdgeCount; b++)
                    {
                        const sal_uInt32 nNextIndex((b + 1) % nPointCount);
                        const basegfx::B2DPoint aNext(aCandidate.getB2DPoint(nNextIndex));
                        const Point nNext(basegfx::fround(aNext.getX()), basegfx::fround(aNext.getY()));

                        WriteOpcode_Line(nCurr, nNext);
                        nCurr = nNext;
                    }
                }
            }
        }

        if(aFillPolyPolygon.count())
        {
            const Color aOldLineColor(aLineColor);
            const Color aOldFillColor(aFillColor);

            aLineColor = Color( COL_TRANSPARENT );
            aFillColor = aOldLineColor;
            SetAttrForPaint();

            for(sal_uInt32 a(0); a < aFillPolyPolygon.count(); a++)
            {
                const Polygon aPolygon(aFillPolyPolygon.getB2DPolygon(a).getDefaultAdaptiveSubdivision());
                WriteOpcode_Poly(PDM_PAINT, aPolygon);
            }

            aLineColor = aOldLineColor;
            aFillColor = aOldFillColor;
        }
    }
}

void PictWriter::WriteOpcodes( const GDIMetaFile & rMTF )
{
    size_t nA, nACount;
    const MetaAction* pMA;

    if( !bStatus)
        return;

    nACount = rMTF.GetActionSize();

    for (nA=0; nA < nACount; nA++)
    {
        pMA = rMTF.GetAction(nA);

        switch (pMA->GetType())
        {
            case META_PIXEL_ACTION:
            {
                const MetaPixelAction* pA = (const MetaPixelAction*) pMA;
                WriteOpcode_PnMode(eSrcRasterOp);
                WriteOpcode_PnSize(1);
                WriteOpcode_RGBFgCol(pA->GetColor());
                WriteOpcode_PnLinePat(sal_True);
                WriteOpcode_Line(pA->GetPoint(),pA->GetPoint());
            }
            break;

            case META_POINT_ACTION:
            {
                const MetaPointAction* pA = (const MetaPointAction*) pMA;

                if( aLineColor != Color( COL_TRANSPARENT ) )
                {
                    SetAttrForFrame();
                    WriteOpcode_Line( pA->GetPoint(),pA->GetPoint() );
                }
            }
            break;

            case META_LINE_ACTION:
            {
                const MetaLineAction* pA = (const MetaLineAction*) pMA;

                if( aLineColor != Color( COL_TRANSPARENT ) )
                {
                    if(pA->GetLineInfo().IsDefault())
                    {
                        SetAttrForFrame();
                        WriteOpcode_Line( pA->GetStartPoint(),pA->GetEndPoint() );
                    }
                    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()));
                        HandleLineInfoPolyPolygons(pA->GetLineInfo(), aPolygon);
                    }
                }
                break;
            }

            case META_RECT_ACTION:
            {
                const MetaRectAction* pA = (const MetaRectAction*) pMA;

                if (aFillColor!=Color( COL_TRANSPARENT ))
                {
                    SetAttrForPaint();
                    WriteOpcode_Rect(PDM_PAINT,pA->GetRect());
                    if (aLineColor!=Color( COL_TRANSPARENT ))
                    {
                        SetAttrForFrame();
                        WriteOpcode_SameRect(PDM_FRAME);
                    }
                }
                else if (aLineColor!=Color( COL_TRANSPARENT ))
                {
                    SetAttrForFrame();
                    WriteOpcode_Rect(PDM_FRAME,pA->GetRect());
                }
            }
            break;

            case META_ROUNDRECT_ACTION:
            {
                const MetaRoundRectAction* pA = (const MetaRoundRectAction*) pMA;

                WriteOpcode_OvSize( Size( pA->GetHorzRound(), pA->GetVertRound() ) );

                if (aFillColor!=Color( COL_TRANSPARENT ))
                {
                    SetAttrForPaint();
                    WriteOpcode_RRect(PDM_PAINT,pA->GetRect());
                    if (aLineColor!=Color( COL_TRANSPARENT ))
                    {
                        SetAttrForFrame();
                        WriteOpcode_SameRRect(PDM_FRAME);
                    }
                }
                else if (aLineColor!=Color( COL_TRANSPARENT ))
                {
                    SetAttrForFrame();
                    WriteOpcode_RRect(PDM_FRAME,pA->GetRect());
                }
            }
            break;

            case META_ELLIPSE_ACTION:
            {
                const MetaEllipseAction* pA = (const MetaEllipseAction*) pMA;

                if (aFillColor!=Color( COL_TRANSPARENT ))
                {
                    SetAttrForPaint();
                    WriteOpcode_Oval(PDM_PAINT,pA->GetRect());
                    if (aLineColor!=Color( COL_TRANSPARENT ))
                    {
                        SetAttrForFrame();
                        WriteOpcode_SameOval(PDM_FRAME);
                    }
                }
                else if (aLineColor!=Color( COL_TRANSPARENT ))
                {
                    SetAttrForFrame();
                    WriteOpcode_Oval(PDM_FRAME,pA->GetRect());
                }
            }
            break;

            case META_ARC_ACTION:
            {
                const MetaArcAction* pA = (const MetaArcAction*) pMA;

                if (aLineColor!=Color( COL_TRANSPARENT ))
                {
                    SetAttrForFrame();
                    WriteOpcode_Arc(PDM_FRAME,pA->GetRect(),pA->GetStartPoint(),pA->GetEndPoint());
                }
            }
            break;

            case META_PIE_ACTION:
            {
                const MetaPieAction* pA = (const MetaPieAction *) pMA;

                if (aFillColor!=Color( COL_TRANSPARENT ))
                {
                    SetAttrForPaint();
                    WriteOpcode_Arc(PDM_PAINT,pA->GetRect(),pA->GetStartPoint(),pA->GetEndPoint());

                    if (aLineColor!=Color( COL_TRANSPARENT ))
                    {
                        SetAttrForFrame();
                        WriteOpcode_SameArc(PDM_FRAME,pA->GetRect(),pA->GetStartPoint(),pA->GetEndPoint());
                    }
                }
                else if (aLineColor!=Color( COL_TRANSPARENT ))
                {
                    SetAttrForFrame();
                    WriteOpcode_Arc(PDM_FRAME,pA->GetRect(),pA->GetStartPoint(),pA->GetEndPoint());
                }

                if (aLineColor!=Color( COL_TRANSPARENT ))
                {
                    double fxc,fyc,fxr,fyr,fx1,fy1,fx2,fy2,l1,l2;

                    fxc=((double)(pA->GetRect().Left()+pA->GetRect().Right()))/2.0;
                    fyc=((double)(pA->GetRect().Top()+pA->GetRect().Bottom()))/2.0;
                    fxr=((double)pA->GetRect().GetWidth())/2.0;
                    fyr=((double)pA->GetRect().GetHeight())/2.0;
                    fx1=((double)pA->GetStartPoint().X())-fxc;
                    fy1=((double)pA->GetStartPoint().Y())-fyc;
                    fx2=((double)pA->GetEndPoint().X())-fxc;
                    fy2=((double)pA->GetEndPoint().Y())-fyc;
                    l1=sqrt(fx1*fx1+fy1*fy1);
                    l2=sqrt(fx2*fx2+fy2*fy2);

                    if (l1>0)
                    {
                        fx1=fx1/l1*fxr;
                        fy1=fy1/l1*fyr;
                    }

                    if (l2>0)
                    {
                        fx2=fx2/l2*fxr;
                        fy2=fy2/l2*fyr;
                    }
                    fx1+=fxc; fy1+=fyc; fx2+=fxc; fy2+=fyc;
                    WriteOpcode_Line(Point((long)(fx1+0.5),(long)(fy1+0.5)), Point((long)(fxc+0.5),(long)(fyc+0.5)));
                    WriteOpcode_LineFrom(Point((long)(fx2+0.5),(long)(fy2+0.5)));
                }
            }
            break;

            case META_CHORD_ACTION:
            {
//                OSL_FAIL( "Unsupported PICT-Action: META_CHORD_ACTION!" );
            }
            break;

            case META_POLYLINE_ACTION:
            {
                const MetaPolyLineAction* pA = (const MetaPolyLineAction*) pMA;

                if( aLineColor!=Color( COL_TRANSPARENT ) )
                {
                    const Polygon&  rPoly = pA->GetPolygon();

                    if( rPoly.GetSize() )
                    {
                        if(pA->GetLineInfo().IsDefault())
                        {
                            Polygon aSimplePoly;
                            if ( rPoly.HasFlags() )
                                rPoly.AdaptiveSubdivide( aSimplePoly );
                            else
                                aSimplePoly = rPoly;

                            const sal_uInt16    nSize = aSimplePoly.GetSize();
                            Point           aLast;

                            if ( nSize )
                            {
                                SetAttrForFrame();
                                aLast = aSimplePoly[0];

                                for ( sal_uInt16 i = 1; i < nSize; i++ )
                                {
                                    WriteOpcode_Line( aLast, aSimplePoly[i] );
                                    aLast = aSimplePoly[i];
                                }
                            }
                        }
                        else
                        {
                            // LineInfo used; handle Dash/Dot and fat lines
                            HandleLineInfoPolyPolygons(pA->GetLineInfo(), rPoly.getB2DPolygon());
                        }
                    }
                }
            }
            break;

            case META_POLYGON_ACTION:
            {
                const MetaPolygonAction* pA = (const MetaPolygonAction*) pMA;

                const Polygon& rPoly = pA->GetPolygon();

                Polygon aSimplePoly;
                if ( rPoly.HasFlags() )
                    rPoly.AdaptiveSubdivide( aSimplePoly );
                else
                    aSimplePoly = rPoly;

                if (aFillColor!=Color( COL_TRANSPARENT ))
                {
                    SetAttrForPaint();
                    WriteOpcode_Poly( PDM_PAINT, aSimplePoly );
                }
                if (aLineColor!=Color( COL_TRANSPARENT ))
                {
                    SetAttrForFrame();
                    WriteOpcode_Poly( PDM_FRAME, aSimplePoly );
                }
            }
            break;

            case META_POLYPOLYGON_ACTION:
            {
                const MetaPolyPolygonAction* pA = (const MetaPolyPolygonAction*) pMA;

                const PolyPolygon& rPolyPoly = pA->GetPolyPolygon();
                sal_uInt16 nPolyCount = rPolyPoly.Count();
                PolyPolygon aSimplePolyPoly( rPolyPoly );
                for ( sal_uInt16 i = 0; i < nPolyCount; i++ )
                {
                    if ( aSimplePolyPoly[ i ].HasFlags() )
                    {
                        Polygon aSimplePoly;
                        aSimplePolyPoly[ i ].AdaptiveSubdivide( aSimplePoly );
                        aSimplePolyPoly[ i ] = aSimplePoly;
                    }
                }
                if (aFillColor!=Color( COL_TRANSPARENT ))
                {
                    SetAttrForPaint();
                    WriteOpcode_Poly( PDM_PAINT, PolyPolygonToPolygon( aSimplePolyPoly ));
                }

                if (aLineColor!=Color( COL_TRANSPARENT ))
                {
                    sal_uInt16 nCount,i;
                    SetAttrForFrame();
                    nCount = aSimplePolyPoly.Count();
                    for ( i = 0; i < nCount; i++ )
                        WriteOpcode_Poly( PDM_FRAME, aSimplePolyPoly.GetObject( i ) );
                }
            }
            break;

            case META_TEXT_ACTION:
            {
                const MetaTextAction*   pA = (const MetaTextAction*) pMA;
                Point                   aPt( pA->GetPoint() );

                if ( aSrcFont.GetAlign() != ALIGN_BASELINE )
                {
                    VirtualDevice aVirDev;

                    if (aSrcFont.GetAlign()==ALIGN_TOP)
                        aPt.Y()+=(long)aVirDev.GetFontMetric(aSrcFont).GetAscent();
                    else
                        aPt.Y()-=(long)aVirDev.GetFontMetric(aSrcFont).GetDescent();
                }

                SetAttrForText();
                String aStr( pA->GetText(),pA->GetIndex(),pA->GetLen() );
                WriteOpcode_Text( aPt, aStr, sal_False );
            }
            break;

            case META_TEXTARRAY_ACTION:
            {
                const MetaTextArrayAction*  pA = (const MetaTextArrayAction*) pMA;
                Point                       aPt( pA->GetPoint() );

                if (aSrcFont.GetAlign()!=ALIGN_BASELINE)
                {
                    VirtualDevice aVirDev;

                    if (aSrcFont.GetAlign()==ALIGN_TOP)
                        aPt.Y()+=(long)aVirDev.GetFontMetric(aSrcFont).GetAscent();
                    else
                        aPt.Y()-=(long)aVirDev.GetFontMetric(aSrcFont).GetDescent();
                }
                SetAttrForText();
                String aStr( pA->GetText(),pA->GetIndex(),pA->GetLen() );
                WriteTextArray( aPt, aStr, pA->GetDXArray() );
                break;
            }

            case META_STRETCHTEXT_ACTION:
            {
                const MetaStretchTextAction*    pA = (const MetaStretchTextAction*) pMA;
                Point                           aPt( pA->GetPoint() );
                String                          aStr( pA->GetText(),pA->GetIndex(),pA->GetLen() );
                VirtualDevice                   aVirDev;
                sal_Int32*                      pDXAry = new sal_Int32[ aStr.Len() ];
                sal_Int32                       nNormSize( aVirDev.GetTextArray( aStr,pDXAry ) );
                sal_uInt16                          i;

                if (aSrcFont.GetAlign()!=ALIGN_BASELINE)
                {
                    if (aSrcFont.GetAlign()==ALIGN_TOP)
                        aPt.Y()+=(long)aVirDev.GetFontMetric(aSrcFont).GetAscent();
                    else
                        aPt.Y()-=(long)aVirDev.GetFontMetric(aSrcFont).GetDescent();
                }

                for ( i = 0; i < aStr.Len() - 1; i++ )
                    pDXAry[ i ] = pDXAry[ i ] * ( (long)pA->GetWidth() ) / nNormSize;

                SetAttrForText();
                WriteTextArray( aPt, aStr, pDXAry );
                delete[] pDXAry;
            }
            break;

            case META_TEXTRECT_ACTION:
            {
//                OSL_FAIL( "Unsupported PICT-Action: META_TEXTRECT_ACTION!" );
            }
            break;

            case META_BMP_ACTION:
            {
                const MetaBmpAction*    pA = (const MetaBmpAction*) pMA;
                const Bitmap            aBmp( pA->GetBitmap() );
                VirtualDevice           aVirDev;

                WriteOpcode_BitsRect( pA->GetPoint(), aVirDev.PixelToLogic( aBmp.GetSizePixel(), aSrcMapMode ), aBmp );
            }
            break;

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

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

                aBmp.Crop( Rectangle( pA->GetSrcPoint(), pA->GetSrcSize() ) );
                WriteOpcode_BitsRect( pA->GetDestPoint(), pA->GetDestSize(), aBmp );
            }
            break;

            case META_BMPEX_ACTION:
            {
                const MetaBmpExAction*  pA = (const MetaBmpExAction*) pMA;
                const Bitmap            aBmp( Graphic( pA->GetBitmapEx() ).GetBitmap() );
                VirtualDevice           aVirDev;

                WriteOpcode_BitsRect( pA->GetPoint(), aVirDev.PixelToLogic( aBmp.GetSizePixel(), aSrcMapMode ), aBmp );
            }
            break;

            case META_BMPEXSCALE_ACTION:
            {
                const MetaBmpExScaleAction* pA = (const MetaBmpExScaleAction*) pMA;
                const Bitmap                aBmp( Graphic( pA->GetBitmapEx() ).GetBitmap() );

                WriteOpcode_BitsRect( pA->GetPoint(), pA->GetSize(), aBmp );
            }
            break;

            case META_BMPEXSCALEPART_ACTION:
            {
                const MetaBmpExScalePartAction* pA = (const MetaBmpExScalePartAction*) pMA;
                Bitmap                          aBmp( Graphic( pA->GetBitmapEx() ).GetBitmap() );

                aBmp.Crop( Rectangle( pA->GetSrcPoint(), pA->GetSrcSize() ) );
                WriteOpcode_BitsRect( pA->GetDestPoint(), pA->GetDestSize(), aBmp );
            }
            break;

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

                size_t nCount = aGDIMetaFile.GetActionSize();
                for ( size_t i = 0; i < nCount; i++ )
                {
                    const MetaAction* pMetaAct = aGDIMetaFile.GetAction( i );
                    if ( pMetaAct->GetType() == META_BMPSCALE_ACTION )
                    {
                        const MetaBmpScaleAction* pBmpScaleAction = (const MetaBmpScaleAction*)pMetaAct;
                        WriteOpcode_BitsRect( pA->GetPoint(), pA->GetSize(), pBmpScaleAction->GetBitmap() );
                        break;
                    }
                }
            }
            break;

            case META_MASK_ACTION:
            case META_MASKSCALE_ACTION:
            case META_MASKSCALEPART_ACTION:
            {
//                OSL_FAIL( "Unsupported PICT-Action: META_MASK..._ACTION!" );
            }
            break;

            case META_GRADIENT_ACTION:
            {
                VirtualDevice               aVDev;
                GDIMetaFile                 aTmpMtf;
                const MetaGradientAction*   pA = (const MetaGradientAction*) pMA;

                aVDev.SetMapMode( aTargetMapMode );
                aVDev.AddGradientActions( pA->GetRect(), pA->GetGradient(), aTmpMtf );
                WriteOpcodes( aTmpMtf );
            }
            break;

            case META_HATCH_ACTION:
            {
                VirtualDevice           aVDev;
                GDIMetaFile             aTmpMtf;
                const MetaHatchAction*  pA = (const MetaHatchAction*) pMA;

                aVDev.SetMapMode( aTargetMapMode );
                aVDev.AddHatchActions( pA->GetPolyPolygon(), pA->GetHatch(), aTmpMtf );
                WriteOpcodes( aTmpMtf );
            }
            break;

            case META_WALLPAPER_ACTION:
            {
//                OSL_FAIL( "Unsupported PICT-Action: META_WALLPAPER_ACTION!" );
            }
            break;

            case META_CLIPREGION_ACTION:
            {
//                OSL_FAIL( "Unsupported PICT-Action: META_CLIPREGION_ACTION!" );
            }
            break;

            case META_ISECTRECTCLIPREGION_ACTION:
            {
                const MetaISectRectClipRegionAction* pA = (const MetaISectRectClipRegionAction*) pMA;
                WriteOpcode_ClipRect( pA->GetRect() );
            }
            break;

            case META_ISECTREGIONCLIPREGION_ACTION:
            {
//                OSL_FAIL( "Unsupported PICT-Action: META_ISECTREGIONCLIPREGION_ACTION!" );
            }
            break;

            case META_MOVECLIPREGION_ACTION:
            {
//                OSL_FAIL( "Unsupported PICT-Action: META_MOVECLIPREGION_ACTION!" );
            }
            break;

            case META_LINECOLOR_ACTION:
            {
                const MetaLineColorAction* pA = (const MetaLineColorAction*) pMA;

                if( pA->IsSetting() )
                    aLineColor = pA->GetColor();
                else
                    aLineColor = Color( COL_TRANSPARENT );
            }
            break;

            case META_FILLCOLOR_ACTION:
            {
                const MetaFillColorAction* pA = (const MetaFillColorAction*) pMA;

                if( pA->IsSetting() )
                    aFillColor = pA->GetColor();
                else
                    aFillColor = Color( COL_TRANSPARENT );
            }
            break;

            case META_TEXTCOLOR_ACTION:
            {
                const MetaTextColorAction* pA = (const MetaTextColorAction*) pMA;
                aSrcFont.SetColor( pA->GetColor() );
            }
            break;

            case META_TEXTFILLCOLOR_ACTION:
            {
                const MetaTextFillColorAction* pA = (const MetaTextFillColorAction*) pMA;

                if( pA->IsSetting() )
                    aSrcFont.SetFillColor( pA->GetColor() );
                else
                    aSrcFont.SetFillColor( Color( COL_TRANSPARENT ) );
            }
            break;

            case META_TEXTALIGN_ACTION:
            {
//                OSL_FAIL( "Unsupported PICT-Action: META_TEXTALIGN_ACTION!" );
            }
            break;

            case META_MAPMODE_ACTION:
            {
                const MetaMapModeAction* pA = (const MetaMapModeAction*) pMA;

                if (aSrcMapMode!=pA->GetMapMode())
                {
                    if( pA->GetMapMode().GetMapUnit() == MAP_RELATIVE )
                    {
                        MapMode aMM = pA->GetMapMode();
                        Fraction aScaleX = aMM.GetScaleX();
                        Fraction aScaleY = aMM.GetScaleY();

                        Point aOrigin = aSrcMapMode.GetOrigin();
                        BigInt aX( aOrigin.X() );
                        aX *= BigInt( aScaleX.GetDenominator() );
                        if( aOrigin.X() >= 0 )
                        {
                            if( aScaleX.GetNumerator() >= 0 )
                                aX += BigInt( aScaleX.GetNumerator()/2 );
                            else
                                aX -= BigInt( (aScaleX.GetNumerator()+1)/2 );
                        }
                        else
                        {
                            if( aScaleX.GetNumerator() >= 0 )
                                aX -= BigInt( (aScaleX.GetNumerator()-1)/2 );
                            else
                                aX += BigInt( aScaleX.GetNumerator()/2 );
                        }

                        aX /= BigInt( aScaleX.GetNumerator() );
                        aOrigin.X() = (long)aX + aMM.GetOrigin().X();
                        BigInt aY( aOrigin.Y() );
                        aY *= BigInt( aScaleY.GetDenominator() );

                        if( aOrigin.Y() >= 0 )
                        {
                            if( aScaleY.GetNumerator() >= 0 )
                                aY += BigInt( aScaleY.GetNumerator()/2 );
                            else
                                aY -= BigInt( (aScaleY.GetNumerator()+1)/2 );
                        }
                        else
                        {
                            if( aScaleY.GetNumerator() >= 0 )
                                aY -= BigInt( (aScaleY.GetNumerator()-1)/2 );
                            else
                                aY += BigInt( aScaleY.GetNumerator()/2 );
                        }

                        aY /= BigInt( aScaleY.GetNumerator() );
                        aOrigin.Y() = (long)aY + aMM.GetOrigin().Y();
                        aSrcMapMode.SetOrigin( aOrigin );

                        aScaleX *= aSrcMapMode.GetScaleX();
                        aScaleY *= aSrcMapMode.GetScaleY();
                        aSrcMapMode.SetScaleX( aScaleX );
                        aSrcMapMode.SetScaleY( aScaleY );
                    }
                    else
                        aSrcMapMode = pA->GetMapMode();
                }
            }
            break;

            case META_FONT_ACTION:
            {
                const MetaFontAction* pA = (const MetaFontAction*) pMA;
                aSrcFont=pA->GetFont();
            }
            break;

            case META_PUSH_ACTION:
            {
                PictWriterAttrStackMember * pAt = new PictWriterAttrStackMember;
                pAt->aLineColor=aLineColor;
                pAt->aFillColor=aFillColor;
                pAt->eRasterOp=eSrcRasterOp;
                pAt->aFont=aSrcFont;
                pAt->aMapMode=aSrcMapMode;
                pAt->aClipRect=aClipRect;
                pAt->pSucc=pAttrStack;
                pAttrStack=pAt;
            }
            break;

            case META_POP_ACTION:
            {
                PictWriterAttrStackMember* pAt=pAttrStack;

                if( pAt )
                {
                    aLineColor=pAt->aLineColor;
                    aFillColor=pAt->aFillColor;
                    eSrcRasterOp=pAt->eRasterOp;
                    aSrcFont=pAt->aFont;
                    aSrcMapMode=pAt->aMapMode;
                    if ( pAt->aClipRect != aClipRect )
                    {
                        Rectangle aRect( pAt->aClipRect );
                        *pPict  << (sal_uInt16)1    // opcode 1
                                << (sal_uInt16)10   // data size
                                << (sal_Int16)aRect.Top() << (sal_Int16)aRect.Left()
                                << (sal_Int16)aRect.Bottom() << (sal_Int16)aRect.Right();
                    }
                    aClipRect=pAt->aClipRect;
                    pAttrStack=pAt->pSucc;
                    delete pAt;
                }
            }
            break;

            case META_RASTEROP_ACTION:
            {
                const MetaRasterOpAction* pA = (const MetaRasterOpAction*) pMA;
                eSrcRasterOp=pA->GetRasterOp();
            }
            break;

            case META_TRANSPARENT_ACTION:
            {
                const PolyPolygon& rPolyPoly = ( (const MetaTransparentAction*) pMA )->GetPolyPolygon();

                if (aFillColor!=Color( COL_TRANSPARENT ))
                {
                    SetAttrForPaint();
                    WriteOpcode_Poly( PDM_PAINT, PolyPolygonToPolygon( rPolyPoly ) );
                }

                if (aLineColor!=Color( COL_TRANSPARENT ))
                {
                    SetAttrForFrame();
                    for( sal_uInt16 i = 0, nCount = rPolyPoly.Count(); i < nCount; i++ )
                        WriteOpcode_Poly( PDM_FRAME, rPolyPoly.GetObject( i ) );
                }
            }
            break;

            case META_FLOATTRANSPARENT_ACTION:
            {
                const MetaFloatTransparentAction* pA = (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() ? (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 );

                WriteOpcodes( aTmpMtf );
            }
            break;

            case( META_RENDERGRAPHIC_ACTION ):
            {
                const MetaRenderGraphicAction*          pA = (const MetaRenderGraphicAction*) pMA;
                const ::vcl::RenderGraphicRasterizer    aRasterizer( pA->GetRenderGraphic() );
                VirtualDevice                           aVirDev;
                const Bitmap                            aBmp( Graphic( aRasterizer.Rasterize(
                                                            aVirDev.LogicToPixel( pA->GetSize() ) ) ).GetBitmap() );

                WriteOpcode_BitsRect( pA->GetPoint(), pA->GetSize(), aBmp );
            }
            break;
        }

        nWrittenActions++;
        MayCallback();

        if (pPict->GetError())
        bStatus=sal_False;

        if (bStatus==sal_False)
        break;
    }
}


void PictWriter::WriteHeader(const GDIMetaFile & rMTF)
{
    sal_uInt16  i;
    Size aSize( rMTF.GetPrefSize() );
    Point aPoint;
    Rectangle   aRect( aPoint, aSize );

    // 512 Bytes "Muell" am Anfang:
    for (i=0;i<128;i++) *pPict << (sal_uInt32)0;

    // Lo-16-Bits der Groesse der Datei ohne die 512 Bytes Muell:
    *pPict << (sal_uInt16)0; // wird spaeter durch UpdateHeader() berichtigt

    // Das Bounding-Rectangle (y1,x1,y2,x2 !):
    WriteRectangle( aRect );

    // Version 2:
    *pPict << (sal_uInt32)0x001102ff;

    // Extended-Version-2-Header:
    *pPict << (sal_uInt16)0x0c00                            // Opcode
           << (sal_uInt16)0xfffe                            // Version (?)
           << (sal_uInt16)0x0000                            // Reserved
           << (sal_uInt32) 0x00480000                        // hRes
           << (sal_uInt32) 0x00480000;
    WriteRectangle( aRect );
    *pPict << (sal_uInt32)0x00000000;                        // Reserved

    // viele Import-Filter verlangen die Angabe eines
    // Clipping-Bereichs am Anfang

    WriteOpcode_ClipRect( aRect );
}


void PictWriter::UpdateHeader()
{
    sal_uLong nPos;

    // Lo-16-Bits der Groesse der Datei ohne die 512 Bytes Muell berichtigen:
    nPos=pPict->Tell();
    pPict->Seek(512);
    *pPict << (sal_uInt16)((nPos-512)&0x0000ffff);
    pPict->Seek(nPos);
}


sal_Bool PictWriter::WritePict(const GDIMetaFile & rMTF, SvStream & rTargetStream, FilterConfigItem* pFilterConfigItem )
{
    PictWriterAttrStackMember*  pAt;
    MapMode                     aMap72( MAP_INCH );
    Fraction                    aDPIFrac( 1, 72 );

    bStatus=sal_True;
    nLastPercent=0;

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

    pPict=&rTargetStream;
    pPict->SetNumberFormatInt(NUMBERFORMAT_INT_BIGENDIAN);

    aLineColor=Color( COL_BLACK );
    aFillColor=Color( COL_WHITE );
    eSrcRasterOp=ROP_OVERPAINT;
    aSrcFont=Font();
    aSrcMapMode = rMTF.GetPrefMapMode();

    aMap72.SetScaleX( aDPIFrac );
    aMap72.SetScaleY( aDPIFrac );
    aTargetMapMode = aMap72;

    pAttrStack=NULL;

    bDstBkPatValid=sal_False;
    bDstTxFaceValid=sal_False;
    bDstTxModeValid=sal_False;
    bDstPnSizeValid=sal_False;
    bDstPnModeValid=sal_False;
    bDstPnPatValid=sal_False;
    bDstFillPatValid=sal_False;
    bDstTxSizeValid=sal_False;
    bDstFgColValid=sal_False;
    bDstBkColValid=sal_False;
    bDstPenPositionValid=sal_False;
    bDstTextPositionValid=sal_False;
    bDstFontNameValid=sal_False;

    nNumberOfActions=0;
    nNumberOfBitmaps=0;
    nWrittenActions=0;
    nWrittenBitmaps=0;
    nActBitmapPercent=0;

    CountActionsAndBitmaps(rMTF);

    WriteHeader(rMTF);
    WriteOpcodes(rMTF);
    WriteOpcode_EndOfFile();
    UpdateHeader();

    while (pAttrStack!=NULL) {
        pAt=pAttrStack;
        pAttrStack=pAt->pSucc;
        delete pAt;
    }

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

    return bStatus;
}

//================== GraphicExport - die exportierte Funktion ================

extern "C" SAL_DLLPUBLIC_EXPORT sal_Bool __LOADONCALLAPI
GraphicExport(SvStream & rStream, Graphic & rGraphic, FilterConfigItem* pFilterConfigItem, sal_Bool)
{
    PictWriter      aPictWriter;

    if (rGraphic.GetType()==GRAPHIC_GDIMETAFILE)
    {
        GDIMetaFile aScaledMtf( rGraphic.GetGDIMetaFile() );

/*
        MapMode     aMap72( MAP_INCH );
        Fraction    aDPIFrac( 1, 72 );
        Size        aOldSize = aScaledMtf.GetPrefSize();

        aMap72.SetScaleX( aDPIFrac );
        aMap72.SetScaleY( aDPIFrac );

        Size aNewSize = OutputDevice::LogicToLogic( aOldSize,
                                                    aScaledMtf.GetPrefMapMode(),
                                                    aMap72 );

        aScaledMtf.Scale( Fraction( aNewSize.Width(), aOldSize.Width() ),
                          Fraction( aNewSize.Height(), aOldSize.Height() ) );
        aScaledMtf.SetPrefMapMode( aMap72 );
        aScaledMtf.SetPrefSize( aNewSize );
*/

        return aPictWriter.WritePict( aScaledMtf, rStream, pFilterConfigItem );
    }
    else
    {
        Bitmap aBmp=rGraphic.GetBitmap();
        GDIMetaFile aMTF;
        VirtualDevice aVirDev;

        aMTF.Record(&aVirDev);
        aVirDev.DrawBitmap(Point(),aBmp);
        aMTF.Stop();
        aMTF.SetPrefSize(aBmp.GetSizePixel());
        return aPictWriter.WritePict( aMTF, rStream, pFilterConfigItem );
    }
}

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