/* -*- 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 "tokens.hxx"
#include "oox/core/xmlfilterbase.hxx"
#include "oox/export/drawingml.hxx"
#include "oox/export/utils.hxx"

#include <cstdio>
#include <com/sun/star/awt/CharSet.hpp>
#include <com/sun/star/awt/FontDescriptor.hpp>
#include <com/sun/star/awt/FontSlant.hpp>
#include <com/sun/star/awt/FontWeight.hpp>
#include <com/sun/star/awt/FontUnderline.hpp>
#include <com/sun/star/awt/Gradient.hpp>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/beans/XPropertyState.hpp>
#include <com/sun/star/beans/Property.hpp>
#include <com/sun/star/beans/XPropertySetInfo.hpp>
#include <com/sun/star/container/XEnumerationAccess.hpp>
#include <com/sun/star/container/XIndexAccess.hpp>
#include <com/sun/star/drawing/BitmapMode.hpp>
#include <com/sun/star/drawing/EnhancedCustomShapeAdjustmentValue.hpp>
#include <com/sun/star/drawing/LineDash.hpp>
#include <com/sun/star/drawing/LineJoint.hpp>
#include <com/sun/star/drawing/LineStyle.hpp>
#include <com/sun/star/drawing/TextHorizontalAdjust.hpp>
#include <com/sun/star/drawing/TextVerticalAdjust.hpp>
#include <com/sun/star/drawing/XShape.hpp>
#include <com/sun/star/drawing/FillStyle.hpp>
#include <com/sun/star/drawing/BitmapMode.hpp>
#include <com/sun/star/i18n/ScriptType.hpp>
#include <com/sun/star/io/XOutputStream.hpp>
#include <com/sun/star/style/LineSpacing.hpp>
#include <com/sun/star/style/LineSpacingMode.hpp>
#include <com/sun/star/style/ParagraphAdjust.hpp>
#include <com/sun/star/text/WritingMode.hpp>
#include <com/sun/star/text/XText.hpp>
#include <com/sun/star/text/XTextContent.hpp>
#include <com/sun/star/text/XTextField.hpp>
#include <com/sun/star/text/XTextRange.hpp>
#include <tools/stream.hxx>
#include <tools/string.hxx>
#include <vcl/cvtgrf.hxx>
#include <unotools/fontcvt.hxx>
#include <unotools/fontdefs.hxx>
#include <vcl/graph.hxx>
#include <svtools/grfmgr.hxx>
#include <rtl/strbuf.hxx>
#include <sfx2/app.hxx>
#include <svl/languageoptions.hxx>
#include <filter/msfilter/escherex.hxx>
#include <editeng/svxenum.hxx>

using namespace ::com::sun::star;
using namespace ::com::sun::star::beans;
using namespace ::com::sun::star::drawing;
using namespace ::com::sun::star::i18n;
using namespace ::com::sun::star::style;
using namespace ::com::sun::star::text;
using namespace ::com::sun::star::uno;
using ::com::sun::star::beans::PropertyState;
using ::com::sun::star::beans::PropertyValue;
using ::com::sun::star::beans::XPropertySet;
using ::com::sun::star::beans::XPropertyState;
using ::com::sun::star::container::XEnumeration;
using ::com::sun::star::container::XEnumerationAccess;
using ::com::sun::star::container::XIndexAccess;
using ::com::sun::star::io::XOutputStream;
using ::com::sun::star::style::LineSpacing;
using ::com::sun::star::text::XText;
using ::com::sun::star::text::XTextContent;
using ::com::sun::star::text::XTextField;
using ::com::sun::star::text::XTextRange;
using ::rtl::OString;
using ::rtl::OStringBuffer;
using ::rtl::OUString;
using ::rtl::OUStringBuffer;
using ::sax_fastparser::FSHelperPtr;

DBG(extern void dump_pset(Reference< XPropertySet > rXPropSet));

namespace oox {
namespace drawingml {

#define GETA(propName) \
    GetProperty( rXPropSet, String( RTL_CONSTASCII_USTRINGPARAM( #propName ) ) )

#define GETAD(propName) \
    ( GetPropertyAndState( rXPropSet, rXPropState, String( RTL_CONSTASCII_USTRINGPARAM( #propName ) ), eState ) && eState == beans::PropertyState_DIRECT_VALUE )

#define GET(variable, propName) \
    if ( GETA(propName) ) \
        mAny >>= variable;
DBG(
void lcl_dump_pset(Reference< XPropertySet > rXPropSet)
{
    Reference< XPropertySetInfo > info = rXPropSet->getPropertySetInfo ();
    Sequence< beans::Property > props = info->getProperties ();

    for (int i=0; i < props.getLength (); i++) {
        OString name = OUStringToOString( props [i].Name, RTL_TEXTENCODING_UTF8);
        fprintf (stderr,"%30s = ", name.getStr() );

    try {
        Any value = rXPropSet->getPropertyValue( props [i].Name );

        OUString strValue;
        sal_Int32 intValue;
        bool boolValue;
    LineSpacing spacing;
//         RectanglePoint pointValue;

        if( value >>= strValue )
            fprintf (stderr,"\"%s\"\n", USS( strValue ) );
        else if( value >>= intValue )
            fprintf (stderr,"%d            (hex: %x)\n", intValue, intValue);
        else if( value >>= boolValue )
            fprintf (stderr,"%d            (bool)\n", boolValue);
    else if( value >>= spacing ) {
        fprintf (stderr, "mode: %d value: %d\n", spacing.Mode, spacing.Height);
    }
//         else if( value >>= pointValue )
//             fprintf (stderr,"%d            (RectanglePoint)\n", pointValue);
        else
            fprintf (stderr,"???           <unhandled type>\n");
    } catch(Exception e) {
        fprintf (stderr,"unable to get '%s' value\n", USS(props [i].Name));
    }
    }
}
);

// not thread safe
int DrawingML::mnImageCounter = 1;

void DrawingML::ResetCounters()
{
    mnImageCounter = 1;
}

bool DrawingML::GetProperty( Reference< XPropertySet > rXPropSet, String aName )
{
    bool bRetValue = false;

    try {
        mAny = rXPropSet->getPropertyValue( aName );
        if ( mAny.hasValue() )
            bRetValue = true;
    } catch( Exception& ) { /* printf ("exception when trying to get value of property: %s\n", ST(aName)); */ }

    return bRetValue;
}

bool DrawingML::GetPropertyAndState( Reference< XPropertySet > rXPropSet, Reference< XPropertyState > rXPropState, String aName, PropertyState& eState )
{
    bool bRetValue = false;

    try {
        mAny = rXPropSet->getPropertyValue( aName );
        if ( mAny.hasValue() ) {
            bRetValue = true;
            eState = rXPropState->getPropertyState( aName );
        }
    } catch( Exception& ) { /* printf ("exception when trying to get value of property: %s\n", ST(aName)); */ }

    return bRetValue;
}

void DrawingML::WriteColor( sal_uInt32 nColor )
{
    OString sColor = OString::valueOf( ( sal_Int64 ) nColor, 16 );
    if( sColor.getLength() < 6 ) {
        OStringBuffer sBuf( "0" );
        int remains = 5 - sColor.getLength();

        while( remains > 0 ) {
            sBuf.append( "0" );
            remains--;
        }

        sBuf.append( sColor );

        sColor = sBuf.getStr();
    }
    mpFS->singleElementNS( XML_a, XML_srgbClr, XML_val, sColor.getStr(), FSEND );
}

void DrawingML::WriteSolidFill( sal_uInt32 nColor )
{
    mpFS->startElementNS( XML_a, XML_solidFill, FSEND );
    WriteColor( nColor );
    mpFS->endElementNS( XML_a, XML_solidFill );
}

void DrawingML::WriteSolidFill( Reference< XPropertySet > rXPropSet )
{
    if ( GetProperty( rXPropSet, S( "FillColor" ) ) )
        WriteSolidFill( *((sal_uInt32*) mAny.getValue()) & 0xffffff );
}

void DrawingML::WriteGradientStop( sal_uInt16 nStop, sal_uInt32 nColor )
{
    mpFS->startElementNS( XML_a, XML_gs,
                          XML_pos, I32S( nStop * 1000 ),
                          FSEND );
    WriteColor( nColor );
    mpFS->endElementNS( XML_a, XML_gs );
}

sal_uInt32 DrawingML::ColorWithIntensity( sal_uInt32 nColor, sal_uInt32 nIntensity )
{
    return ( ( ( nColor & 0xff ) * nIntensity ) / 100 )
        | ( ( ( ( ( nColor & 0xff00 ) >> 8 ) * nIntensity ) / 100 ) << 8 )
        | ( ( ( ( ( nColor & 0xff0000 ) >> 8 ) * nIntensity ) / 100 ) << 8 );
}

void DrawingML::WriteGradientFill( Reference< XPropertySet > rXPropSet )
{
    awt::Gradient aGradient;
    if( GETA( FillGradient ) ) {
        aGradient = *static_cast< const awt::Gradient* >( mAny.getValue() );

        mpFS->startElementNS( XML_a, XML_gradFill, FSEND );

        switch( aGradient.Style ) {
            default:
            case GradientStyle_LINEAR:
                mpFS->startElementNS( XML_a, XML_gsLst, FSEND );
                WriteGradientStop( 0, ColorWithIntensity( aGradient.StartColor, aGradient.StartIntensity ) );
                WriteGradientStop( 100, ColorWithIntensity( aGradient.EndColor, aGradient.EndIntensity ) );
                mpFS->endElementNS( XML_a, XML_gsLst );
                mpFS->singleElementNS( XML_a, XML_lin,
                                       XML_ang, I32S( ( ( ( 3600 - aGradient.Angle + 900 ) * 6000 ) % 21600000 ) ),
                                       FSEND );
                break;

            case GradientStyle_AXIAL:
                mpFS->startElementNS( XML_a, XML_gsLst, FSEND );
                WriteGradientStop( 0, ColorWithIntensity( aGradient.EndColor, aGradient.EndIntensity ) );
                WriteGradientStop( 50, ColorWithIntensity( aGradient.StartColor, aGradient.StartIntensity ) );
                WriteGradientStop( 100, ColorWithIntensity( aGradient.EndColor, aGradient.EndIntensity ) );
                mpFS->endElementNS( XML_a, XML_gsLst );
                mpFS->singleElementNS( XML_a, XML_lin,
                                       XML_ang, I32S( ( ( ( 3600 - aGradient.Angle + 900 ) * 6000 ) % 21600000 ) ),
                                       FSEND );
                break;

                /* I don't see how to apply transformation to gradients, so
                 * elliptical will end as radial and square as
                 * rectangular. also position offsets are not applied */
            case GradientStyle_RADIAL:
            case GradientStyle_ELLIPTICAL:
            case GradientStyle_RECT:
            case GradientStyle_SQUARE:
                mpFS->startElementNS( XML_a, XML_gsLst, FSEND );
                WriteGradientStop( 0, ColorWithIntensity( aGradient.EndColor, aGradient.EndIntensity ) );
                WriteGradientStop( 100, ColorWithIntensity( aGradient.StartColor, aGradient.StartIntensity ) );
                mpFS->endElementNS( XML_a, XML_gsLst );
                mpFS->singleElementNS( XML_a, XML_path,
                                       XML_path, ( aGradient.Style == awt::GradientStyle_RADIAL || aGradient.Style == awt::GradientStyle_ELLIPTICAL ) ? "circle" : "rect",
                                       FSEND );
                break;
        }

        mpFS->endElementNS( XML_a, XML_gradFill );
    }

}

void DrawingML::WriteLineArrow( Reference< XPropertySet > rXPropSet, sal_Bool bLineStart )
{
    ESCHER_LineEnd eLineEnd;
    sal_Int32 nArrowLength;
    sal_Int32 nArrowWidth;

    if ( EscherPropertyContainer::GetLineArrow( bLineStart, rXPropSet, eLineEnd, nArrowLength, nArrowWidth ) ) {
        const char* len;
        const char* type;
        const char* width;

        switch( nArrowLength ) {
            case ESCHER_LineShortArrow:
                len = "sm";
                break;
            default:
            case ESCHER_LineMediumLenArrow:
                len = "med";
                break;
            case ESCHER_LineLongArrow:
                len = "lg";
                break;
        }

        switch( eLineEnd ) {
            default:
            case ESCHER_LineNoEnd:
                type = "none";
                break;
            case ESCHER_LineArrowEnd:
                type = "triangle";
                break;
            case ESCHER_LineArrowStealthEnd:
                type = "stealth";
                break;
            case ESCHER_LineArrowDiamondEnd:
                type = "diamond";
                break;
            case ESCHER_LineArrowOvalEnd:
                type = "oval";
                break;
            case ESCHER_LineArrowOpenEnd:
                type = "arrow";
                break;
        }

        switch( nArrowWidth ) {
            case ESCHER_LineNarrowArrow:
                width = "sm";
                break;
            default:
            case ESCHER_LineMediumWidthArrow:
                width = "med";
                break;
            case ESCHER_LineWideArrow:
                width = "lg";
                break;
        }

        mpFS->singleElementNS( XML_a, bLineStart ? XML_headEnd : XML_tailEnd,
                               XML_len, len,
                               XML_type, type,
                               XML_w, width,
                               FSEND );
    }
}

void DrawingML::WriteOutline( Reference< XPropertySet > rXPropSet )
{
    drawing::LineStyle aLineStyle( drawing::LineStyle_NONE );

    GET( aLineStyle, LineStyle );

    if( aLineStyle == drawing::LineStyle_NONE )
        return;

    sal_uInt32 nLineWidth = 0;
    sal_uInt32 nColor = 0;
    sal_Bool bColorSet = FALSE;
    const char* cap = NULL;
    drawing::LineDash aLineDash;
    sal_Bool bDashSet = FALSE;

    GET( nLineWidth, LineWidth );

    switch( aLineStyle ) {
        case drawing::LineStyle_DASH:
            if( GETA( LineDash ) ) {
                aLineDash = *(drawing::LineDash*) mAny.getValue();
                bDashSet = TRUE;
                if( aLineDash.Style == DashStyle_ROUND || aLineDash.Style == DashStyle_ROUNDRELATIVE )
                    cap = "rnd";

                DBG(printf("dash dots: %d dashes: %d dotlen: %d dashlen: %d distance: %d\n",
                            int( aLineDash.Dots ), int( aLineDash.Dashes ), int( aLineDash.DotLen ), int( aLineDash.DashLen ), int( aLineDash.Distance )));
            }
            /* fallthru intended */
        case drawing::LineStyle_SOLID:
        default:
            if ( GETA( LineColor ) ) {
                nColor = *((sal_uInt32*) mAny.getValue()) & 0xffffff;
                bColorSet = TRUE;
            }
            break;
    }

    mpFS->startElementNS( XML_a, XML_ln,
                          XML_cap, cap,
                          XML_w, nLineWidth > 1 ? I64S( MM100toEMU( nLineWidth ) ) : NULL,
                          FSEND );
    if( bColorSet )
        WriteSolidFill( nColor );

    if( bDashSet ) {
        mpFS->startElementNS( XML_a, XML_custDash, FSEND );
        int i;
        for( i = 0; i < aLineDash.Dots; i ++ )
            mpFS->singleElementNS( XML_a, XML_ds,
                                   XML_d, aLineDash.DotLen ? I64S( aLineDash.DotLen*1000 ) : "100000",
                                   XML_sp, I64S( aLineDash.Distance*1000 ),
                                   FSEND );
        for( i = 0; i < aLineDash.Dashes; i ++ )
            mpFS->singleElementNS( XML_a, XML_ds,
                                   XML_d, aLineDash.DashLen ? I64S( aLineDash.DashLen*1000 ) : "100000",
                                   XML_sp, I64S( aLineDash.Distance*1000 ),
                                   FSEND );
        mpFS->endElementNS( XML_a, XML_custDash );
    }

    if( nLineWidth > 1 && GETA( LineJoint ) ) {
        LineJoint eLineJoint;

        mAny >>= eLineJoint;
        switch( eLineJoint ) {
            case LineJoint_NONE:
            case LineJoint_MIDDLE:
            case LineJoint_BEVEL:
                mpFS->singleElementNS( XML_a, XML_bevel, FSEND );
                break;
            default:
            case LineJoint_MITER:
                mpFS->singleElementNS( XML_a, XML_miter, FSEND );
                break;
            case LineJoint_ROUND:
                mpFS->singleElementNS( XML_a, XML_round, FSEND );
                break;
        }
    }

    WriteLineArrow( rXPropSet, sal_True );
    WriteLineArrow( rXPropSet, sal_False );

    mpFS->endElementNS( XML_a, XML_ln );
}

OUString DrawingML::WriteImage( const OUString& rURL )
{
    ByteString aURLBS( UniString( rURL ), RTL_TEXTENCODING_UTF8 );

    const char aURLBegin[] = "vnd.sun.star.GraphicObject:";
    int index = aURLBS.Search( aURLBegin );

    if ( index != STRING_NOTFOUND ) {
        DBG(printf ("begin: %ld %s\n", long( sizeof( aURLBegin ) ), USS( rURL ) + sizeof( aURLBegin ) - 1 ));
        aURLBS.Erase( 0, sizeof( aURLBegin ) - 1 );
        Graphic aGraphic = GraphicObject( aURLBS ).GetTransformedGraphic ();

        return WriteImage( aGraphic );
    } else {
        // add link to relations
    }

    return OUString();
}

OUString DrawingML::WriteImage( const Graphic& rGraphic )
{
    GfxLink aLink = rGraphic.GetLink ();
    OUString sMediaType;
    const char* sExtension = NULL;
    OUString sRelId;

    SvMemoryStream aStream;
    const void* aData = aLink.GetData();
    sal_Size nDataSize = aLink.GetDataSize();

    switch ( aLink.GetType() ) {
        case GFX_LINK_TYPE_NATIVE_GIF:
            sMediaType = US( "image/gif" );
            sExtension = ".gif";
            break;
        case GFX_LINK_TYPE_NATIVE_JPG:
            sMediaType = US( "image/jpeg" );
            sExtension = ".jpeg";
            break;
        case GFX_LINK_TYPE_NATIVE_PNG:
            sMediaType = US( "image/png" );
            sExtension = ".png";
            break;
        case GFX_LINK_TYPE_NATIVE_TIF:
            sMediaType = US( "image/tiff" );
            sExtension = ".tiff";
            break;
        case GFX_LINK_TYPE_NATIVE_WMF:
            sMediaType = US( "image/x-wmf" );
            sExtension = ".wmf";
            break;
        case GFX_LINK_TYPE_NATIVE_MET:
            sMediaType = US( "image/x-met" );
            sExtension = ".met";
            break;
        case GFX_LINK_TYPE_NATIVE_PCT:
            sMediaType = US( "image/x-pict" );
            sExtension = ".pct";
            break;
        default: {
            GraphicType aType = rGraphic.GetType();
            if ( aType == GRAPHIC_BITMAP ) {
                GraphicConverter::Export( aStream, rGraphic, CVT_PNG );
                sMediaType = US( "image/png" );
                sExtension = ".png";
            } else if ( aType == GRAPHIC_GDIMETAFILE ) {
                GraphicConverter::Export( aStream, rGraphic, CVT_EMF );
                sMediaType = US( "image/x-emf" );
                sExtension = ".emf";
            } else {
                OSL_TRACE( "unhandled graphic type" );
                break;
            }

            aData = aStream.GetData();
            nDataSize = aStream.GetEndOfData();
            break;
            }
    }

    const char *pComponent = NULL;
    switch ( meDocumentType )
    {
        case DOCUMENT_DOCX: pComponent = "word"; break;
        case DOCUMENT_PPTX: pComponent = "ppt"; break;
        case DOCUMENT_XLSX: pComponent = "xl"; break;
    }

    Reference< XOutputStream > xOutStream = mpFB->openFragmentStream( OUStringBuffer()
                                                                      .appendAscii( pComponent )
                                                                      .appendAscii( "/media/image" )
                                                                      .append( (sal_Int32) mnImageCounter )
                                                                      .appendAscii( sExtension )
                                                                      .makeStringAndClear(),
                                                                      sMediaType );
    xOutStream->writeBytes( Sequence< sal_Int8 >( (const sal_Int8*) aData, nDataSize ) );
    xOutStream->closeOutput();

    const char *pImagePrefix = NULL;
    switch ( meDocumentType )
    {
        case DOCUMENT_DOCX:
            pImagePrefix = "media/image";
            break;
        case DOCUMENT_PPTX:
        case DOCUMENT_XLSX:
            pImagePrefix = "../media/image";
            break;
    }

    sRelId = mpFB->addRelation( mpFS->getOutputStream(),
                                US( "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image" ),
                                OUStringBuffer()
                                .appendAscii( pImagePrefix )
                                .append( (sal_Int32) mnImageCounter ++ )
                                .appendAscii( sExtension )
                                .makeStringAndClear() );

    return sRelId;
}

OUString DrawingML::WriteBlip( Reference< XPropertySet > rXPropSet, OUString& rURL )
{
        OUString sRelId = WriteImage( rURL );
    sal_Int16 nBright = 0;
    sal_Int32 nContrast = 0;

    GET( nBright, AdjustLuminance );
    GET( nContrast, AdjustContrast );

        mpFS->startElementNS( XML_a, XML_blip,
                  FSNS( XML_r, XML_embed), OUStringToOString( sRelId, RTL_TEXTENCODING_UTF8 ).getStr(),
                  FSEND );
    if( nBright || nContrast )
        mpFS->singleElementNS( XML_a, XML_lum,
                   XML_bright, nBright ? I32S( nBright*1000 ) : NULL,
                   XML_contrast, nContrast ? I32S( nContrast*1000 ) : NULL,
                   FSEND );

    mpFS->endElementNS( XML_a, XML_blip );

    return sRelId;
}

void DrawingML::WriteBlipMode( Reference< XPropertySet > rXPropSet )
{
    BitmapMode eBitmapMode( BitmapMode_NO_REPEAT );
    if (GetProperty( rXPropSet, S( "FillBitmapMode" ) ) )
        mAny >>= eBitmapMode;

    DBG(printf("fill bitmap mode: %d\n", eBitmapMode));

    switch (eBitmapMode) {
    case BitmapMode_REPEAT:
        mpFS->singleElementNS( XML_a, XML_tile, FSEND );
        break;
    default:
        ;
    }
}

void DrawingML::WriteBlipFill( Reference< XPropertySet > rXPropSet, String sURLPropName )
{
    WriteBlipFill( rXPropSet, sURLPropName, XML_a );
}

void DrawingML::WriteBlipFill( Reference< XPropertySet > rXPropSet, String sURLPropName, sal_Int32 nXmlNamespace )
{
    if ( GetProperty( rXPropSet, sURLPropName ) ) {
        OUString aURL;
        mAny >>= aURL;

        DBG(printf ("URL: %s\n", OUStringToOString( aURL, RTL_TEXTENCODING_UTF8 ).getStr() ));

        if( !aURL.getLength() )
            return;

        mpFS->startElementNS( nXmlNamespace , XML_blipFill, FSEND );

        WriteBlip( rXPropSet, aURL );

        if( sURLPropName == S( "FillBitmapURL" ) )
            WriteBlipMode( rXPropSet );
        else if( GetProperty( rXPropSet, S( "FillBitmapStretch" ) ) ) {
                bool bStretch = false;
                mAny >>= bStretch;

                if( bStretch )
                    WriteStretch();
        }

        mpFS->endElementNS( nXmlNamespace, XML_blipFill );
    }
}

void DrawingML::WriteStretch()
{
    mpFS->startElementNS( XML_a, XML_stretch, FSEND );
    mpFS->singleElementNS( XML_a, XML_fillRect, FSEND );
    mpFS->endElementNS( XML_a, XML_stretch );
}

void DrawingML::WriteTransformation( const Rectangle& rRect,
        sal_Int32 nXmlNamespace, sal_Bool bFlipH, sal_Bool bFlipV, sal_Int32 nRotation )
{
    mpFS->startElementNS( nXmlNamespace, XML_xfrm,
                          XML_flipH, bFlipH ? "1" : NULL,
                          XML_flipV, bFlipV ? "1" : NULL,
                          XML_rot, nRotation ? I32S( nRotation ) : NULL,
                          FSEND );

    mpFS->singleElementNS( XML_a, XML_off, XML_x, IS( MM100toEMU( rRect.Left() ) ), XML_y, IS( MM100toEMU( rRect.Top() ) ), FSEND );
    mpFS->singleElementNS( XML_a, XML_ext, XML_cx, IS( MM100toEMU( rRect.GetWidth() ) ), XML_cy, IS( MM100toEMU( rRect.GetHeight() ) ), FSEND );

    mpFS->endElementNS( nXmlNamespace, XML_xfrm );
}

void DrawingML::WriteShapeTransformation( Reference< XShape > rXShape, sal_Int32 nXmlNamespace, sal_Bool bFlipH, sal_Bool bFlipV, sal_Int32 nRotation )
{
    DBG(printf( "write shape transformation\n" ));

    awt::Point aPos = rXShape->getPosition();
    awt::Size aSize = rXShape->getSize();

    WriteTransformation( Rectangle( Point( aPos.X, aPos.Y ), Size( aSize.Width, aSize.Height ) ), nXmlNamespace, bFlipH, bFlipV, nRotation );
}

void DrawingML::WriteRunProperties( Reference< XPropertySet > rRun, sal_Bool bIsField )
{
    Reference< XPropertySet > rXPropSet( rRun, UNO_QUERY );
    Reference< XPropertyState > rXPropState( rRun, UNO_QUERY );
    OUString usLanguage;
    PropertyState eState;
    sal_Int16 nScriptType = SvtLanguageOptions::GetScriptTypeOfLanguage( Application::GetSettings().GetLanguage() );
    sal_Bool bComplex = ( nScriptType == ScriptType::COMPLEX );
    const char* bold = NULL;
    const char* italic = NULL;
    const char* underline = NULL;
    sal_Int32 nSize = 1800;

    if( GETAD( CharHeight ) )
        nSize = (sal_Int32) (100*(*((float*) mAny.getValue())));

    if ( ( bComplex && GETAD( CharWeightComplex ) ) || GETAD( CharWeight ) )
        if ( *((float*) mAny.getValue()) >= awt::FontWeight::SEMIBOLD )
            bold = "1";

    if ( ( bComplex && GETAD( CharPostureComplex ) ) || GETAD( CharPosture ) )
        switch ( *((awt::FontSlant*) mAny.getValue()) )
        {
            case awt::FontSlant_OBLIQUE :
            case awt::FontSlant_ITALIC :
                italic = "1";
                break;
            default:
                break;
        }

    if ( GETAD( CharUnderline ) )
        switch ( *((sal_Int16*) mAny.getValue()) )
        {
            case awt::FontUnderline::SINGLE :
                underline = "sng";
                break;
            case awt::FontUnderline::DOUBLE :
                underline = "dbl";
                break;
            case awt::FontUnderline::DOTTED :
                underline = "dotted";
                break;
            case awt::FontUnderline::DASH :
                underline = "dash";
                break;
            case awt::FontUnderline::LONGDASH :
                underline = "dashLong";
                break;
            case awt::FontUnderline::DASHDOT :
                underline = "dotDash";
                break;
            case awt::FontUnderline::DASHDOTDOT :
                underline = "dotDotDash";
                break;
            case awt::FontUnderline::WAVE :
                underline = "wavy";
                break;
            case awt::FontUnderline::DOUBLEWAVE :
                underline = "wavyDbl";
                break;
            case awt::FontUnderline::BOLD :
                underline = "heavy";
                break;
            case awt::FontUnderline::BOLDDOTTED :
                underline = "dottedHeavy";
                break;
            case awt::FontUnderline::BOLDDASH :
                underline = "dashHeavy";
                break;
            case awt::FontUnderline::BOLDLONGDASH :
                underline = "dashLongHeavy";
                break;
            case awt::FontUnderline::BOLDDASHDOT :
                underline = "dotDashHeavy";
                break;
            case awt::FontUnderline::BOLDDASHDOTDOT :
                underline = "dotDotDashHeavy";
                break;
            case awt::FontUnderline::BOLDWAVE :
                underline = "wavyHeavy";
                break;
        }

    if( GETA( CharLocale ) ) {
        com::sun::star::lang::Locale eLocale;
        mAny >>= eLocale;

        OUStringBuffer usLanguageBuffer = eLocale.Language;
        if( eLocale.Country.getLength() ) {
            usLanguageBuffer.appendAscii( "-" );
            usLanguageBuffer.append( eLocale.Country );
        }

        if( usLanguageBuffer.getLength() )
            usLanguage = usLanguageBuffer.makeStringAndClear();
    }

    mpFS->startElementNS( XML_a, XML_rPr,
                          XML_b, bold,
                          XML_i, italic,
                          XML_lang, usLanguage.getLength() ? USS( usLanguage ) : NULL,
                          XML_sz, nSize == 1800 ? NULL : IS( nSize ),
                          XML_u, underline,
                          FSEND );

    // mso doesn't like text color to be placed after typeface
    if( GETAD( CharColor ) ) {
        sal_uInt32 color = *((sal_uInt32*) mAny.getValue());
        DBG(printf("run color: %x auto: %x\n", static_cast<unsigned int>( color ), static_cast<unsigned int>( COL_AUTO )));

        if( color == COL_AUTO ) { // nCharColor depends to the background color
            sal_Bool bIsDark = sal_False;
            GET( bIsDark, IsBackgroundDark );
            color = bIsDark ? 0xffffff : 0x000000;
        }
        color &= 0xffffff;

        // TODO: special handle embossed/engraved

        WriteSolidFill( color );
    }

    if( GETAD( CharFontName ) ) {
        const char* typeface = NULL;
        const char* pitch = NULL;
        const char* charset = NULL;
        OUString usTypeface, usPitch, usCharset;

        mAny >>= usTypeface;
        String aSubstName( GetSubsFontName( usTypeface, SUBSFONT_ONLYONE | SUBSFONT_MS ) );
        if( aSubstName.Len() )
            typeface = ST( aSubstName );
        else
            typeface = USS( usTypeface );

        mpFS->singleElementNS( XML_a, XML_latin,
                               XML_typeface, typeface,
                               XML_pitchFamily, pitch,
                               XML_charset, charset,
                               FSEND );
    }

    if( ( bComplex && GETAD( CharFontNameComplex ) ) || ( !bComplex && GETAD( CharFontNameAsian ) ) ) {
        const char* typeface = NULL;
        const char* pitch = NULL;
        const char* charset = NULL;
        OUString usTypeface, usPitch, usCharset;

        mAny >>= usTypeface;
        String aSubstName( GetSubsFontName( usTypeface, SUBSFONT_ONLYONE | SUBSFONT_MS ) );
        if( aSubstName.Len() )
            typeface = ST( aSubstName );
        else
            typeface = USS( usTypeface );

        mpFS->singleElementNS( XML_a, bComplex ? XML_cs : XML_ea,
                               XML_typeface, typeface,
                               XML_pitchFamily, pitch,
                               XML_charset, charset,
                               FSEND );
    }

    if( bIsField ) {
        Reference< XTextField > rXTextField;
        GET( rXTextField, TextField );
        if( rXTextField.is() )
            rXPropSet.set( rXTextField, UNO_QUERY );
    }

    // field properties starts here
    if( GETA( URL ) ) {
    OUString sURL;

    mAny >>= sURL;
    if( sURL.getLength() ) {
        OUString sRelId = mpFB->addRelation( mpFS->getOutputStream(),
                              US( "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink" ),
                              sURL, US( "External" ) );

        mpFS->singleElementNS( XML_a, XML_hlinkClick,
                   FSNS( XML_r,XML_id ), USS( sRelId ),
                   FSEND );
    }
    }

    mpFS->endElementNS( XML_a, XML_rPr );
}

const char* DrawingML::GetFieldType( ::com::sun::star::uno::Reference< ::com::sun::star::text::XTextRange > rRun, sal_Bool& bIsField )
{
    const char* sType = NULL;
    Reference< XPropertySet > rXPropSet( rRun, UNO_QUERY );
    String aFieldType;

    if( GETA( TextPortionType ) ) {
        aFieldType = String( *(::rtl::OUString*)mAny.getValue() );
        DBG(printf ("field type: %s\n", ST(aFieldType) ));
    }

    if( aFieldType == S( "TextField" ) ) {
        Reference< XTextField > rXTextField;
        GET( rXTextField, TextField );
        if( rXTextField.is() ) {
        bIsField = sal_True;
            rXPropSet.set( rXTextField, UNO_QUERY );
            if( rXPropSet.is() ) {
                String aFieldKind( rXTextField->getPresentation( TRUE ) );
                DBG(printf ("field kind: %s\n", ST(aFieldKind) ));
                if( aFieldKind == S( "Page" ) ) {
                    return "slidenum";
                }
        // else if( aFieldKind == S( "URL" ) ) {
        // do not return here
        // and make URL field text run with hyperlink property later
        // }
            }
        }
    }

    return sType;
}

void DrawingML::GetUUID( OStringBuffer& rBuffer )
{
    Sequence< sal_uInt8 > aSeq( 16 );
    static char cDigits[17] = "0123456789ABCDEF";
    rtl_createUuid( (sal_uInt8*)aSeq.getArray(), 0, sal_True );
    int i;

    rBuffer.append( '{' );
    for( i = 0; i < 4; i++ ) {
        rBuffer.append( cDigits[ aSeq[i] >> 4 ] );
        rBuffer.append( cDigits[ aSeq[i] && 0xf ] );
    }
    rBuffer.append( '-' );
    for( ; i < 6; i++ ) {
        rBuffer.append( cDigits[ aSeq[i] >> 4 ] );
        rBuffer.append( cDigits[ aSeq[i] && 0xf ] );
    }
    rBuffer.append( '-' );
    for( ; i < 8; i++ ) {
        rBuffer.append( cDigits[ aSeq[i] >> 4 ] );
        rBuffer.append( cDigits[ aSeq[i] && 0xf ] );
    }
    rBuffer.append( '-' );
    for( ; i < 10; i++ ) {
        rBuffer.append( cDigits[ aSeq[i] >> 4 ] );
        rBuffer.append( cDigits[ aSeq[i] && 0xf ] );
    }
    rBuffer.append( '-' );
    for( ; i < 16; i++ ) {
        rBuffer.append( cDigits[ aSeq[i] >> 4 ] );
        rBuffer.append( cDigits[ aSeq[i] && 0xf ] );
    }
    rBuffer.append( '}' );
}

void DrawingML::WriteRun( Reference< XTextRange > rRun )
{
    const char* sFieldType;
    sal_Bool bIsField = sal_False;
    OUString sText = rRun->getString();

    if( sText.getLength() < 1)
        return;

    if( ( sFieldType = GetFieldType( rRun, bIsField ) ) ) {
        OStringBuffer sUUID(39);

        GetUUID( sUUID );
        mpFS->startElementNS( XML_a, XML_fld,
                              XML_id, sUUID.getStr(),
                              XML_type, sFieldType,
                              FSEND );
    } else
        mpFS->startElementNS( XML_a, XML_r, FSEND );

    Reference< XPropertySet > xPropSet( rRun, uno::UNO_QUERY );
    WriteRunProperties( xPropSet, bIsField );

    mpFS->startElementNS( XML_a, XML_t, FSEND );
    mpFS->writeEscaped( sText );
    mpFS->endElementNS( XML_a, XML_t );

    if( sFieldType )
        mpFS->endElementNS( XML_a, XML_fld );
    else
        mpFS->endElementNS( XML_a, XML_r );
}

#define AUTONUM(x) \
                        if( bPBoth ) \
                            pAutoNumType = #x "ParenBoth"; \
                        else if( bPBehind ) \
                            pAutoNumType = #x "ParenR"; \
                        else if( bSDot ) \
                            pAutoNumType = #x "Period";

inline static const char* GetAutoNumType( sal_Int16 nNumberingType, bool bSDot, bool bPBehind, bool bPBoth )
{
    const char* pAutoNumType = NULL;

    switch( (SvxExtNumType)nNumberingType )
        {
        case SVX_NUM_CHARS_UPPER_LETTER_N :
        case SVX_NUM_CHARS_UPPER_LETTER :
            AUTONUM( alphaUc );
            break;
        case SVX_NUM_CHARS_LOWER_LETTER_N :
        case SVX_NUM_CHARS_LOWER_LETTER :
            AUTONUM( alphaLc );
            break;
        case SVX_NUM_ROMAN_UPPER :
            AUTONUM( romanUc );
            break;
        case SVX_NUM_ROMAN_LOWER :
            AUTONUM( romanLc );
            break;
        case SVX_NUM_ARABIC :
            AUTONUM( arabic )
            else
                pAutoNumType = "arabicPlain";
                        break;
        default:
            break;
        }

    return pAutoNumType;
}

void DrawingML::WriteParagraphNumbering( Reference< XPropertySet > rXPropSet, sal_Int16 nLevel )
{
    if( nLevel >= 0 && GETA( NumberingRules ) )
    {
        Reference< XIndexAccess > rXIndexAccess;

        if ( ( mAny >>= rXIndexAccess ) && nLevel < rXIndexAccess->getCount() )
        {
            DBG(printf ("numbering rules\n"));

            Sequence< PropertyValue > aPropertySequence;
            rXIndexAccess->getByIndex( nLevel ) >>= aPropertySequence;

            const PropertyValue* pPropValue = aPropertySequence.getArray();

            sal_Int32 nPropertyCount = aPropertySequence.getLength();

            if ( nPropertyCount ) {

                sal_Int16 nNumberingType = -1;
                bool bSDot = false;
                bool bPBehind = false;
                bool bPBoth = false;
                sal_Unicode aBulletChar = 0x2022; // a bullet
                awt::FontDescriptor aFontDesc;
                bool bHasFontDesc = false;
                OUString aGraphicURL;
                sal_Int16 nBulletRelSize = 0;

                for ( sal_Int32 i = 0; i < nPropertyCount; i++ ) {
                    const void* pValue = pPropValue[ i ].Value.getValue();
                    if ( pValue ) {
                        OUString aPropName( pPropValue[ i ].Name );
                        DBG(printf ("pro name: %s\n", OUStringToOString( aPropName, RTL_TEXTENCODING_UTF8 ).getStr()));
                        if ( aPropName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "NumberingType" ) ) )
                            nNumberingType = *( (sal_Int16*)pValue );
                        else if ( aPropName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "Prefix" ) ) ) {
                            if( *(OUString*)pValue == US( ")" ) )
                                bPBoth = true;
                        } else if  ( aPropName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "Suffix" ) ) ) {
                            if( *(OUString*)pValue == US( "." ) )
                                bSDot = true;
                            else if( *(OUString*)pValue == US( ")" ) )
                                bPBehind = true;
                        } else if ( aPropName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "BulletChar" ) ) )
                        {
                            aBulletChar = String ( *( (String*)pValue ) ).GetChar( 0 );
                            //printf ("bullet char: %d\n", aBulletChar.getStr());
                        }
                        else if ( aPropName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "BulletFont" ) ) )
                        {
                            aFontDesc = *( (awt::FontDescriptor*)pValue );
                            bHasFontDesc = true;

                            // Our numbullet dialog has set the wrong textencoding for our "StarSymbol" font,
                            // instead of a Unicode encoding the encoding RTL_TEXTENCODING_SYMBOL was used.
                            // Because there might exist a lot of damaged documemts I added this two lines
                            // which fixes the bullet problem for the export.
                            if ( aFontDesc.Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "StarSymbol" ) ) )
                                aFontDesc.CharSet = RTL_TEXTENCODING_MS_1252;

                        } else if ( aPropName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "BulletRelSize" ) ) ) {
                            nBulletRelSize = *( (sal_Int16*)pValue );
                        } else if ( aPropName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "GraphicURL" ) ) ) {
                            aGraphicURL = ( *(OUString*)pValue );
                            DBG(printf ("graphic url: %s\n", OUStringToOString( aGraphicURL, RTL_TEXTENCODING_UTF8 ).getStr()));
                        } else if ( aPropName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "GraphicSize" ) ) )
                        {
                            if ( pPropValue[ i ].Value.getValueType() == ::getCppuType( (awt::Size*)0) )
                            {
                                // don't cast awt::Size to Size as on 64-bits they are not the same.
                                ::com::sun::star::awt::Size aSize;
                                pPropValue[ i ].Value >>= aSize;
                                //aBuGraSize.nA = aSize.Width;
                                //aBuGraSize.nB = aSize.Height;
                                DBG(printf("graphic size: %dx%d\n", int( aSize.Width ), int( aSize.Height )));
                            }
                        }
                    }
                }

                const char* pAutoNumType = GetAutoNumType( nNumberingType, bSDot, bPBehind, bPBoth );

                if( nLevel >= 0 ) {
                    if( aGraphicURL.getLength() > 0 ) {
                        OUString sRelId = WriteImage( aGraphicURL );

                        mpFS->startElementNS( XML_a, XML_buBlip, FSEND );
                        mpFS->singleElementNS( XML_a, XML_blip, FSNS( XML_r, XML_embed ), USS( sRelId ), FSEND );
                        mpFS->endElementNS( XML_a, XML_buBlip );
                    } else {
                        if( nBulletRelSize && nBulletRelSize != 100 )
                            mpFS->singleElementNS( XML_a, XML_buSzPct,
                                                   XML_val, IS( 1000*( (sal_Int32)nBulletRelSize ) ), FSEND );
                        if( bHasFontDesc )
                            mpFS->singleElementNS( XML_a, XML_buFont,
                                                   XML_typeface, OUStringToOString( aFontDesc.Name, RTL_TEXTENCODING_UTF8 ).getStr(),
                                                   XML_charset, (aFontDesc.CharSet == awt::CharSet::SYMBOL) ? "2" : NULL,
                                                   FSEND );

                        if( pAutoNumType )
                            mpFS->singleElementNS( XML_a, XML_buAutoNum, XML_type, pAutoNumType, FSEND );
                        else {
                            aBulletChar = SubstituteBullet( aBulletChar, aFontDesc );
                            mpFS->singleElementNS( XML_a, XML_buChar, XML_char, USS( OUString( aBulletChar ) ), FSEND );
                        }
                    }
                }
            }
        }
    }
}

const char* DrawingML::GetAlignment( sal_Int32 nAlignment )
{
    const char* sAlignment = NULL;

    switch( nAlignment ) {
        case style::ParagraphAdjust_CENTER:
            sAlignment = "ctr";
            break;
        case style::ParagraphAdjust_RIGHT:
            sAlignment = "r";
            break;
        case style::ParagraphAdjust_BLOCK:
            sAlignment = "just";
            break;
        default:
            ;
    }

    return sAlignment;
}

void DrawingML::WriteLinespacing( LineSpacing& rSpacing )
{
    if( rSpacing.Mode == LineSpacingMode::PROP )
        mpFS->singleElementNS( XML_a, XML_spcPct,
                               XML_val, I32S( ((sal_Int32)rSpacing.Height)*1000 ),
                               FSEND );
    else
        mpFS->singleElementNS( XML_a, XML_spcPts,
                               XML_val, I32S( rSpacing.Height ),
                               FSEND );
}

void DrawingML::WriteParagraphProperties( Reference< XTextContent > rParagraph )
{
    Reference< XPropertySet > rXPropSet( rParagraph, UNO_QUERY );
    Reference< XPropertyState > rXPropState( rParagraph, UNO_QUERY );
    PropertyState eState;

    if( !rXPropSet.is() || !rXPropState.is() )
        return;

    //OSL_TRACE("write paragraph properties pset");
    //DBG(lcl_dump_pset(rXPropSet));

    sal_Int16 nLevel = -1;
    GET( nLevel, NumberingLevel );

    sal_Int32 nLeftMargin = 0;
    // fix coordinates
    //GET( nLeftMargin, ParaLeftMargin );

    sal_Int16 nAlignment( style::ParagraphAdjust_LEFT );
    GET( nAlignment, ParaAdjust );

    sal_Bool bHasLinespacing = sal_False;
    LineSpacing aLineSpacing;
    if( GETAD( ParaLineSpacing ) )
        bHasLinespacing = ( mAny >>= aLineSpacing );

    if( nLevel != -1
        || nLeftMargin > 0
        || nAlignment != style::ParagraphAdjust_LEFT
        || bHasLinespacing ) {
        mpFS->startElementNS( XML_a, XML_pPr,
                              XML_lvl, nLevel > 0 ? I32S( nLevel ) : NULL,
                              XML_marL, nLeftMargin > 0 ? IS( nLeftMargin ) : NULL,
                              XML_algn, GetAlignment( nAlignment ),
                              FSEND );

        if( bHasLinespacing ) {
            mpFS->startElementNS( XML_a, XML_lnSpc, FSEND );
            WriteLinespacing( aLineSpacing );
            mpFS->endElementNS( XML_a, XML_lnSpc );
        }

        WriteParagraphNumbering( rXPropSet, nLevel );

        mpFS->endElementNS( XML_a, XML_pPr );
    }
}

void DrawingML::WriteParagraph( Reference< XTextContent > rParagraph )
{
    Reference< XEnumerationAccess > access( rParagraph, UNO_QUERY );
    if( !access.is() )
        return;

    Reference< XEnumeration > enumeration( access->createEnumeration() );
    if( !enumeration.is() )
        return;

    mpFS->startElementNS( XML_a, XML_p, FSEND );

    sal_Bool bPropertiesWritten = FALSE;
    while( enumeration->hasMoreElements() ) {
        Reference< XTextRange > run;
        Any any ( enumeration->nextElement() );

        if (any >>= run) {
            if( !bPropertiesWritten && run->getString().getLength() ) {
                WriteParagraphProperties( rParagraph );
                bPropertiesWritten = TRUE;
            }
            WriteRun( run );
        }
    }
    mpFS->singleElementNS( XML_a, XML_endParaRPr, FSEND );

    mpFS->endElementNS( XML_a, XML_p );
}

void DrawingML::WriteText( Reference< XShape > rXShape  )
{
    Reference< XText > xXText( rXShape, UNO_QUERY );
    Reference< XPropertySet > rXPropSet( rXShape, UNO_QUERY );

    if( !xXText.is() )
        return;

#define DEFLRINS 254
#define DEFTBINS 127
    sal_Int32 nLeft, nRight, nTop, nBottom;
    nLeft = nRight = DEFLRINS;
    nTop = nBottom = DEFTBINS;

    // top inset looks a bit different compared to ppt export
    // check if something related doesn't work as expected
    GET( nLeft, TextLeftDistance );
    GET( nRight, TextRightDistance );
    GET( nTop, TextUpperDistance );
    GET( nBottom, TextLowerDistance );

    TextVerticalAdjust eVerticalAlignment( TextVerticalAdjust_TOP );
    const char* sVerticalAlignment = NULL;
    GET( eVerticalAlignment, TextVerticalAdjust );
    switch( eVerticalAlignment ) {
        case TextVerticalAdjust_BOTTOM:
            sVerticalAlignment = "b";
            break;
        case TextVerticalAdjust_CENTER:
            sVerticalAlignment = "ctr";
            break;
        case TextVerticalAdjust_TOP:
        default:
            ;
    }

    const char* sWritingMode = NULL;
    sal_Bool bVertical = sal_False;
    if( GETA( TextWritingMode ) ) {
        WritingMode eMode;

        if( ( mAny >>= eMode ) && eMode == WritingMode_TB_RL ) {
            sWritingMode = "vert";
            bVertical = sal_True;
        }
    }

    TextHorizontalAdjust eHorizontalAlignment( TextHorizontalAdjust_CENTER );
    bool bHorizontalCenter = false;
    GET( eHorizontalAlignment, TextHorizontalAdjust );
    if( eHorizontalAlignment == TextHorizontalAdjust_CENTER )
        bHorizontalCenter = true;
    else if( bVertical && eHorizontalAlignment == TextHorizontalAdjust_LEFT )
        sVerticalAlignment = "b";

    sal_Bool bHasWrap = FALSE;
    sal_Bool bWrap = FALSE;
    if( GETA( TextWordWrap ) ) {
        mAny >>= bWrap;
        bHasWrap = TRUE;
        //DBG(printf("wrap: %d\n", bWrap));
    }

    mpFS->singleElementNS( XML_a, XML_bodyPr,
                           XML_wrap, bHasWrap && !bWrap ? "none" : NULL,
                           XML_lIns, (nLeft != DEFLRINS) ? IS( MM100toEMU( nLeft ) ) : NULL,
                           XML_rIns, (nRight != DEFLRINS) ? IS( MM100toEMU( nRight ) ) : NULL,
                           XML_tIns, (nTop != DEFTBINS) ? IS( MM100toEMU( nTop ) ) : NULL,
                           XML_bIns, (nBottom != DEFTBINS) ? IS( MM100toEMU( nBottom ) ) : NULL,
                           XML_anchor, sVerticalAlignment,
                           XML_anchorCtr, bHorizontalCenter ? "1" : NULL,
                           XML_vert, sWritingMode,
                           FSEND );

    Reference< XEnumerationAccess > access( xXText, UNO_QUERY );
    if( !access.is() )
        return;

    Reference< XEnumeration > enumeration( access->createEnumeration() );
    if( !enumeration.is() )
        return;

    while( enumeration->hasMoreElements() ) {
        Reference< XTextContent > paragraph;
        Any any ( enumeration->nextElement() );

        if( any >>= paragraph)
            WriteParagraph( paragraph );
    }

}

void DrawingML::WritePresetShape( const char* pShape )
{
    mpFS->startElementNS( XML_a, XML_prstGeom,
                          XML_prst, pShape,
                          FSEND );
    mpFS->singleElementNS( XML_a, XML_avLst, FSEND );
    mpFS->endElementNS(  XML_a, XML_prstGeom );
}

void DrawingML::WritePresetShape( const char* pShape, MSO_SPT eShapeType, sal_Bool bPredefinedHandlesUsed, sal_Int32 nAdjustmentsWhichNeedsToBeConverted, const PropertyValue& rProp )
{
    mpFS->startElementNS( XML_a, XML_prstGeom,
                          XML_prst, pShape,
                          FSEND );
    mpFS->startElementNS( XML_a, XML_avLst, FSEND );

    Sequence< drawing::EnhancedCustomShapeAdjustmentValue > aAdjustmentSeq;
    if ( ( rProp.Value >>= aAdjustmentSeq )
         && eShapeType != mso_sptActionButtonForwardNext  // we have adjustments values for these type of shape, but MSO doesn't like them
         && eShapeType != mso_sptActionButtonBackPrevious // so they are now disabled
        ) {
        DBG(printf("adj seq len: %d\n", int( aAdjustmentSeq.getLength() )));
        if ( bPredefinedHandlesUsed )
            EscherPropertyContainer::LookForPolarHandles( eShapeType, nAdjustmentsWhichNeedsToBeConverted );

        sal_Int32 nValue, nLength = aAdjustmentSeq.getLength();
        for( sal_Int32 i=0; i < nLength; i++ )
            if( EscherPropertyContainer::GetAdjustmentValue( aAdjustmentSeq[ i ], i, nAdjustmentsWhichNeedsToBeConverted, nValue ) )
                mpFS->singleElementNS( XML_a, XML_gd,
                                       XML_name, nLength > 1 ? ( OString( "adj" ) + OString::valueOf( i + 1 ) ).getStr() : "adj",
                                       XML_fmla, (OString("val ") + OString::valueOf( nValue )).getStr(),
                                       FSEND );
    }

    mpFS->endElementNS( XML_a, XML_avLst );
    mpFS->endElementNS(  XML_a, XML_prstGeom );
}

void DrawingML::WritePolyPolygon( const PolyPolygon& rPolyPolygon )
{
    if( rPolyPolygon.Count() < 1 )
        return;

    mpFS->startElementNS( XML_a, XML_custGeom, FSEND );
    mpFS->singleElementNS( XML_a, XML_avLst, FSEND );
    mpFS->singleElementNS( XML_a, XML_gdLst, FSEND );
    mpFS->singleElementNS( XML_a, XML_ahLst, FSEND );
    mpFS->singleElementNS( XML_a, XML_rect,
                           XML_l, "0",
                           XML_t, "0",
                           XML_r, "r",
                           XML_b, "b",
                           FSEND );

    mpFS->startElementNS( XML_a, XML_pathLst, FSEND );

    for( USHORT i = 0; i < rPolyPolygon.Count(); i ++ ) {

        const Polygon& rPoly = rPolyPolygon[ i ];
        Rectangle aRect( rPoly.GetBoundRect() );
        sal_Bool bBezier = FALSE;

        mpFS->startElementNS( XML_a, XML_path,
                              XML_w, I64S( aRect.GetWidth() ),
                              XML_h, I64S( aRect.GetHeight() ),
                              FSEND );

        if( rPoly.GetSize() > 0 )
        {
            mpFS->startElementNS( XML_a, XML_moveTo, FSEND );

            mpFS->singleElementNS( XML_a, XML_pt,
                                   XML_x, I64S( rPoly[ 0 ].X() - aRect.Left() ),
                                   XML_y, I64S( rPoly[ 0 ].Y() - aRect.Top() ),
                                   FSEND );

            mpFS->endElementNS( XML_a, XML_moveTo );
        }

        for( USHORT j = 1; j < rPoly.GetSize(); j ++ )
        {
            enum PolyFlags flags = rPoly.GetFlags(j);
            if( flags == POLY_CONTROL && !bBezier )
            {
                mpFS->startElementNS( XML_a, XML_cubicBezTo, FSEND );
                bBezier = TRUE;
            }
            else if( flags == POLY_NORMAL && !bBezier )
                mpFS->startElementNS( XML_a, XML_lnTo, FSEND );

            mpFS->singleElementNS( XML_a, XML_pt,
                                   XML_x, I64S( rPoly[j].X() - aRect.Left() ),
                                   XML_y, I64S( rPoly[j].Y() - aRect.Top() ),
                                   FSEND );

            if( ( flags == POLY_NORMAL || flags == POLY_SYMMTR ) && bBezier )
            {
                mpFS->endElementNS( XML_a, XML_cubicBezTo );
                bBezier = FALSE;
            }
            else if( flags == POLY_NORMAL && !bBezier )
                mpFS->endElementNS( XML_a, XML_lnTo );
            else if( bBezier && ( j % 3 ) == 0 )
            {
                // //a:cubicBezTo can only contain 3 //a:pt elements, so we
                // need to break things up...
                mpFS->endElementNS( XML_a, XML_cubicBezTo );
                mpFS->startElementNS( XML_a, XML_cubicBezTo, FSEND );
            }
//             switch( rPoly.GetFlags(j) ) {
//                 case POLY_NORMAL:
//                     DBG(printf("normal\n"));
//                     break;
//                 case POLY_SMOOTH:
//                     DBG(printf("smooth\n"));
//                     break;
//                 case POLY_CONTROL:
//                     DBG(printf("control\n"));
//                     break;
//                 case POLY_SYMMTR:
//                     DBG(printf("symmtr\n"));
//                         break;
//             }
//             DBG(printf("point %ld %ld\n", rPoly[j].X() - aRect.Left(), rPoly[j].Y() - aRect.Top()));
        }

        mpFS->endElementNS( XML_a, XML_path );
    }

    mpFS->endElementNS( XML_a, XML_pathLst );

    mpFS->endElementNS(  XML_a, XML_custGeom );
}

void DrawingML::WriteConnectorConnections( EscherConnectorListEntry& rConnectorEntry, sal_Int32 nStartID, sal_Int32 nEndID )
{
    if( nStartID != -1 )
        mpFS->singleElementNS( XML_a, XML_stCxn,
                               XML_id, I32S( nStartID ),
                               XML_idx, I64S( rConnectorEntry.GetConnectorRule( TRUE ) ),
                               FSEND );
    if( nEndID != -1 )
        mpFS->singleElementNS( XML_a, XML_endCxn,
                               XML_id, I32S( nEndID ),
                               XML_idx, I64S( rConnectorEntry.GetConnectorRule( FALSE ) ),
                               FSEND );
}

// from sw/source/filter/ww8/wrtw8num.cxx for default bullets to export to MS intact
static void lcl_SubstituteBullet(String& rNumStr, rtl_TextEncoding& rChrSet, String& rFontName)
{
    sal_Unicode cChar = rNumStr.GetChar(0);
    StarSymbolToMSMultiFont *pConvert = CreateStarSymbolToMSMultiFont();
    String sFont = pConvert->ConvertChar(cChar);
    delete pConvert;
    if (sFont.Len())
    {
        rNumStr = static_cast< sal_Unicode >(cChar | 0xF000);
        rFontName = sFont;
        rChrSet = RTL_TEXTENCODING_SYMBOL;
    }
    else if ( (rNumStr.GetChar(0) < 0xE000 || rNumStr.GetChar(0) > 0xF8FF) )
    {
        /*
           Ok we can't fit into a known windows unicode font, but
           we are not in the private area, so we are a
           standardized symbol, so turn off the symbol bit and
           let words own font substitution kick in
           */
        rChrSet = RTL_TEXTENCODING_UNICODE;
        rFontName = ::GetFontToken(rFontName, 0);
    }
    else
    {
        /*
           Well we don't have an available substition, and we're
           in our private area, so give up and show a standard
           bullet symbol
           */
        rFontName.AssignAscii(RTL_CONSTASCII_STRINGPARAM("Wingdings"));
        rNumStr = static_cast< sal_Unicode >(0x6C);
    }
}

sal_Unicode DrawingML::SubstituteBullet( sal_Unicode cBulletId, ::com::sun::star::awt::FontDescriptor& rFontDesc )
{
    String sNumStr = cBulletId;

    if ( rFontDesc.Name.equalsIgnoreAsciiCaseAscii("starsymbol") ||
         rFontDesc.Name.equalsIgnoreAsciiCaseAscii("opensymbol") )  {
        String sFontName = rFontDesc.Name;
        rtl_TextEncoding aCharSet = rFontDesc.CharSet;

        lcl_SubstituteBullet( sNumStr, aCharSet, sFontName );

        rFontDesc.Name = sFontName;
        rFontDesc.CharSet = aCharSet;
    }

    return sNumStr.GetChar( 0 );
}

sax_fastparser::FSHelperPtr DrawingML::CreateOutputStream (
    const OUString& sFullStream,
    const OUString& sRelativeStream,
    const Reference< XOutputStream >& xParentRelation,
    const char* sContentType,
    const char* sRelationshipType,
    ::rtl::OUString* pRelationshipId )
{
    OUString sRelationshipId;
    if (xParentRelation.is())
        sRelationshipId = GetFB()->addRelation( xParentRelation, OUString::createFromAscii( sRelationshipType), sRelativeStream );
    else
        sRelationshipId = GetFB()->addRelation( OUString::createFromAscii( sRelationshipType ), sRelativeStream );

    if( pRelationshipId )
        *pRelationshipId = sRelationshipId;

    sax_fastparser::FSHelperPtr p = GetFB()->openFragmentStreamWithSerializer( sFullStream, OUString::createFromAscii( sContentType ) );

    return p;
}

void DrawingML::WriteFill( Reference< XPropertySet > xPropSet )
{
    if ( !GetProperty( xPropSet, S( "FillStyle" ) ) )
        return;
    FillStyle aFillStyle( FillStyle_NONE );
    xPropSet->getPropertyValue( S( "FillStyle" ) ) >>= aFillStyle;

    if( aFillStyle == FillStyle_BITMAP )
    {
        //DBG(printf ("FillStyle_BITMAP properties\n"));
        //DBG(dump_pset(rXPropSet));
    }

    if( aFillStyle == FillStyle_NONE ||
        aFillStyle == FillStyle_HATCH )
        return;

    switch( aFillStyle )
    {
    case ::com::sun::star::drawing::FillStyle_SOLID :
        WriteSolidFill( xPropSet );
        break;
    case ::com::sun::star::drawing::FillStyle_GRADIENT :
        WriteGradientFill( xPropSet );
        break;
    case ::com::sun::star::drawing::FillStyle_BITMAP :
        WriteBlipFill( xPropSet, S( "FillBitmapURL" ) );
        break;
    default:
        ;
    }

    return;
}

}
}

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