/* -*- 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 "docxattributeoutput.hxx"
#include "docxexport.hxx"
#include "docxexportfilter.hxx"
#include "docxfootnotes.hxx"
#include "writerwordglue.hxx"
#include "wrtww8.hxx"
#include "ww8par.hxx"
#include "fmtcntnt.hxx"
#include "fmtsrnd.hxx"
#include "fchrfmt.hxx"
#include "tgrditem.hxx"
#include "fmtruby.hxx"
#include "charfmt.hxx"
#include "breakit.hxx"

#include <comphelper/string.hxx>
#include <oox/token/tokens.hxx>
#include <oox/export/drawingml.hxx>
#include <oox/export/utils.hxx>
#include <oox/mathml/export.hxx>

#include <i18npool/mslangid.hxx>

#include <hintids.hxx>

#include <svl/poolitem.hxx>

#include <editeng/fontitem.hxx>
#include <editeng/tstpitem.hxx>
#include <editeng/adjitem.hxx>
#include <editeng/spltitem.hxx>
#include <editeng/widwitem.hxx>
#include <editeng/lspcitem.hxx>
#include <editeng/keepitem.hxx>
#include <editeng/shaditem.hxx>
#include <editeng/brshitem.hxx>
#include <editeng/postitem.hxx>
#include <editeng/wghtitem.hxx>
#include <editeng/kernitem.hxx>
#include <editeng/crsditem.hxx>
#include <editeng/cmapitem.hxx>
#include <editeng/wrlmitem.hxx>
#include <editeng/udlnitem.hxx>
#include <editeng/langitem.hxx>
#include <editeng/escpitem.hxx>
#include <editeng/fhgtitem.hxx>
#include <editeng/colritem.hxx>
#include <editeng/hyznitem.hxx>
#include <editeng/brkitem.hxx>
#include <editeng/lrspitem.hxx>
#include <editeng/ulspitem.hxx>
#include <editeng/boxitem.hxx>
#include <editeng/cntritem.hxx>
#include <editeng/shdditem.hxx>
#include <editeng/akrnitem.hxx>
#include <editeng/pbinitem.hxx>
#include <editeng/emphitem.hxx>
#include <editeng/twolinesitem.hxx>
#include <editeng/charscaleitem.hxx>
#include <editeng/charrotateitem.hxx>
#include <editeng/charreliefitem.hxx>
#include <editeng/paravertalignitem.hxx>
#include <editeng/pgrditem.hxx>
#include <editeng/frmdiritem.hxx>
#include <editeng/blnkitem.hxx>
#include <editeng/charhiddenitem.hxx>
#include <editeng/opaqitem.hxx>
#include <editeng/editobj.hxx>
#include <svx/svdmodel.hxx>
#include <svx/svdobj.hxx>
#include <sfx2/sfxbasemodel.hxx>

#include <anchoredobject.hxx>
#include <docufld.hxx>
#include <flddropdown.hxx>
#include <format.hxx>
#include <fmtanchr.hxx>
#include <fmtclds.hxx>
#include <fmtinfmt.hxx>
#include <fmtfld.hxx>
#include <fmtfsize.hxx>
#include <fmtftn.hxx>
#include <fmtrowsplt.hxx>
#include <fmtline.hxx>
#include <frmfmt.hxx>
#include <frmatr.hxx>
#include <ftninfo.hxx>
#include <htmltbl.hxx>
#include <lineinfo.hxx>
#include <ndgrf.hxx>
#include <ndole.hxx>
#include <ndtxt.hxx>
#include <node.hxx>
#include <pagedesc.hxx>
#include <paratr.hxx>
#include <swmodule.hxx>
#include <swtable.hxx>
#include <txtftn.hxx>
#include <txtinet.hxx>
#include <numrule.hxx>

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

#include <tools/color.hxx>

#include <com/sun/star/i18n/ScriptType.hdl>
#include <com/sun/star/drawing/XShape.hpp>
#include <com/sun/star/frame/XModel.hpp>
#include <com/sun/star/chart2/XChartDocument.hpp>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/container/XNamed.hpp>
#include <IMark.hxx>

#if OSL_DEBUG_LEVEL > 1
#include <stdio.h>
#endif

using ::editeng::SvxBorderLine;
using rtl::OString;
using rtl::OStringBuffer;
using rtl::OUString;
using rtl::OUStringBuffer;
using rtl::OUStringToOString;

using namespace oox;
using namespace docx;
using namespace sax_fastparser;
using namespace nsSwDocInfoSubType;
using namespace nsFieldFlags;
using namespace sw::util;
using namespace ::com::sun::star;

class FFDataWriterHelper
{
    ::sax_fastparser::FSHelperPtr m_pSerializer;
    void writeCommonStart( const rtl::OUString& rName )
    {
        m_pSerializer->startElementNS( XML_w, XML_ffData, FSEND );
        m_pSerializer->singleElementNS( XML_w, XML_name,
            FSNS( XML_w, XML_val ), OUStringToOString( rName, RTL_TEXTENCODING_UTF8 ).getStr(),
            FSEND );
        m_pSerializer->singleElementNS( XML_w, XML_enabled, FSEND );
        m_pSerializer->singleElementNS( XML_w, XML_calcOnExit,
            FSNS( XML_w, XML_val ),
            "0", FSEND );
    }
    void writeFinish()
    {
        m_pSerializer->endElementNS( XML_w, XML_ffData );
    }
public:
    FFDataWriterHelper( const ::sax_fastparser::FSHelperPtr pSerializer ) : m_pSerializer( pSerializer ){}
    void WriteFormCheckbox( const rtl::OUString& rName, const rtl::OUString& rDefault, bool bChecked )
    {
       writeCommonStart( rName );
       // Checkbox specific bits
       m_pSerializer->startElementNS( XML_w, XML_checkBox, FSEND );
       // currently hardcoding autosize
       // #TODO check if this defaulted
       m_pSerializer->startElementNS( XML_w, XML_sizeAuto, FSEND );
       m_pSerializer->endElementNS( XML_w, XML_sizeAuto );
       if ( !rDefault.isEmpty() )
       {
           m_pSerializer->singleElementNS( XML_w, XML_default,
               FSNS( XML_w, XML_val ),
                   rtl::OUStringToOString( rDefault, RTL_TEXTENCODING_UTF8 ).getStr(), FSEND );
       }
       if ( bChecked )
            m_pSerializer->singleElementNS( XML_w, XML_checked, FSEND );
        m_pSerializer->endElementNS( XML_w, XML_checkBox );
       writeFinish();
    }
    void WriteFormText(  const rtl::OUString& rName, const rtl::OUString& rDefault )
    {
       writeCommonStart( rName );
       if ( !rDefault.isEmpty() )
       {
           m_pSerializer->startElementNS( XML_w, XML_textInput, FSEND );
           m_pSerializer->singleElementNS( XML_w, XML_default,
               FSNS( XML_w, XML_val ),
               rtl::OUStringToOString( rDefault, RTL_TEXTENCODING_UTF8 ).getStr(), FSEND );
           m_pSerializer->endElementNS( XML_w, XML_textInput );
       }
       writeFinish();
    }
};

class FieldMarkParamsHelper
{
    const sw::mark::IFieldmark& mrFieldmark;
    public:
    FieldMarkParamsHelper( const sw::mark::IFieldmark& rFieldmark ) : mrFieldmark( rFieldmark ) {}
    rtl::OUString getName() { return mrFieldmark.GetName(); }
    template < typename T >
    bool extractParam( const rtl::OUString& rKey, T& rResult )
    {
        bool bResult = false;
        if ( mrFieldmark.GetParameters() )
        {
            sw::mark::IFieldmark::parameter_map_t::const_iterator it = mrFieldmark.GetParameters()->find( rKey );
            if ( it != mrFieldmark.GetParameters()->end() )
                bResult = ( it->second >>= rResult );
        }
        return bResult;
    }
};
void DocxAttributeOutput::RTLAndCJKState( bool bIsRTL, sal_uInt16 /*nScript*/ )
{
    if (bIsRTL)
        m_pSerializer->singleElementNS( XML_w, XML_rtl, FSNS( XML_w, XML_val ), "true", FSEND );
}

void DocxAttributeOutput::StartParagraph( ww8::WW8TableNodeInfo::Pointer_t pTextNodeInfo )
{
    if ( m_nColBreakStatus == COLBRK_POSTPONE )
        m_nColBreakStatus = COLBRK_WRITE;

    // Output table/table row/table cell starts if needed
    if ( pTextNodeInfo.get() )
    {
        sal_uInt32 nRow = pTextNodeInfo->getRow();
        sal_uInt32 nCell = pTextNodeInfo->getCell();

        // New cell/row?
        if ( m_nTableDepth > 0 && !m_bTableCellOpen )
        {
            ww8::WW8TableNodeInfoInner::Pointer_t pDeepInner( pTextNodeInfo->getInnerForDepth( m_nTableDepth ) );
            if ( pDeepInner->getCell() == 0 )
                StartTableRow( pDeepInner );

            StartTableCell( pDeepInner );
        }

        if ( nRow == 0 && nCell == 0 )
        {
            // Do we have to start the table?
            // [If we are at the rigth depth already, it means that we
            // continue the table cell]
            sal_uInt32 nCurrentDepth = pTextNodeInfo->getDepth();

            if ( nCurrentDepth > m_nTableDepth )
            {
                // Start all the tables that begin here
                for ( sal_uInt32 nDepth = m_nTableDepth + 1; nDepth <= pTextNodeInfo->getDepth(); ++nDepth )
                {
                    ww8::WW8TableNodeInfoInner::Pointer_t pInner( pTextNodeInfo->getInnerForDepth( nDepth ) );

                    StartTable( pInner );
                    StartTableRow( pInner );
                    StartTableCell( pInner );
                }

                m_nTableDepth = nCurrentDepth;
            }
        }
    }

    m_pSerializer->startElementNS( XML_w, XML_p, FSEND );

    // postpone the output of the run (we get it before the paragraph
    // properties, but must write it after them)
    m_pSerializer->mark();

    // no section break in this paragraph yet; can be set in SectionBreak()
    m_pSectionInfo.reset();

    m_bParagraphOpened = true;
}

void DocxAttributeOutput::EndParagraph( ww8::WW8TableNodeInfoInner::Pointer_t pTextNodeInfoInner )
{
    // write the paragraph properties + the run, already in the correct order
    m_pSerializer->mergeTopMarks();
    m_pSerializer->endElementNS( XML_w, XML_p );

    // Check for end of cell, rows, tables here
    FinishTableRowCell( pTextNodeInfoInner );

    m_bParagraphOpened = false;

    // Write the anchored frame if any
    if ( m_pParentFrame )
    {
        sw::Frame *pParentFrame = m_pParentFrame;
        m_pParentFrame = NULL;

        const SwFrmFmt& rFrmFmt = pParentFrame->GetFrmFmt( );
        const SwNodeIndex* pNodeIndex = rFrmFmt.GetCntnt().GetCntntIdx();

        sal_uLong nStt = pNodeIndex ? pNodeIndex->GetIndex()+1                  : 0;
        sal_uLong nEnd = pNodeIndex ? pNodeIndex->GetNode().EndOfSectionIndex() : 0;

        m_rExport.SaveData( nStt, nEnd );

        m_rExport.mpParentFrame = pParentFrame;

        m_rExport.WriteText( );

        m_rExport.RestoreData();

        delete pParentFrame;
    }
}

void DocxAttributeOutput::FinishTableRowCell( ww8::WW8TableNodeInfoInner::Pointer_t pInner, bool bForceEmptyParagraph )
{
    if ( pInner.get() )
    {
        // Where are we in the table
        sal_uInt32 nRow = pInner->getRow( );

        const SwTable *pTable = pInner->getTable( );
        const SwTableLines& rLines = pTable->GetTabLines( );
        sal_uInt16 nLinesCount = rLines.Count( );
        // HACK
        // msoffice seems to have an internal limitation of 63 columns for tables
        // and refuses to load .docx with more, even though the spec seems to allow that;
        // so simply if there are more columns, don't close the last one msoffice will handle
        // and merge the contents of the remaining ones into it (since we don't close the cell
        // here, following ones will not be opened)
        bool limitWorkaround = ( pInner->getCell() >= 62 && !pInner->isEndOfLine());

        if ( pInner->isEndOfCell() && !limitWorkaround )
        {
            if ( bForceEmptyParagraph )
                m_pSerializer->singleElementNS( XML_w, XML_p, FSEND );

            EndTableCell();
        }

        // This is a line end
        if ( pInner->isEndOfLine() )
            EndTableRow();

        // This is the end of the table
        if ( pInner->isEndOfLine( ) && ( nRow + 1 ) == nLinesCount )
            EndTable();
    }
}

void DocxAttributeOutput::EmptyParagraph()
{
    m_pSerializer->singleElementNS( XML_w, XML_p, FSEND );
}

void DocxAttributeOutput::StartParagraphProperties( const SwTxtNode& rNode )
{
    // output page/section breaks
    // Writer can have them at the beginning of a paragraph, or at the end, but
    // in docx, we have to output them in the paragraph properties of the last
    // paragraph in a section.  To get it right, we have to switch to the next
    // paragraph, and detect the section breaks there.
    SwNodeIndex aNextIndex( rNode, 1 );
    if ( aNextIndex.GetNode().IsTxtNode() )
    {
        const SwTxtNode* pTxtNode = static_cast< SwTxtNode* >( &aNextIndex.GetNode() );
        m_rExport.OutputSectionBreaks( pTxtNode->GetpSwAttrSet(), *pTxtNode );
    }
    else if ( aNextIndex.GetNode().IsTableNode() )
    {
        const SwTableNode* pTableNode = static_cast< SwTableNode* >( &aNextIndex.GetNode() );
        const SwFrmFmt *pFmt = pTableNode->GetTable().GetFrmFmt();
        m_rExport.OutputSectionBreaks( &(pFmt->GetAttrSet()), *pTableNode );
    }

    m_pSerializer->mark( );

    m_pSerializer->startElementNS( XML_w, XML_pPr, FSEND );

    // and output the section break now (if it appeared)
    if ( m_pSectionInfo )
    {
        m_rExport.SectionProperties( *m_pSectionInfo );
        m_pSectionInfo.reset();
    }

    InitCollectedParagraphProperties();
}

void DocxAttributeOutput::InitCollectedParagraphProperties()
{
    m_pParagraphSpacingAttrList = NULL;

    // Write the elements in the spec order
    static const sal_Int32 aOrder[] =
    {
        FSNS( XML_w, XML_pStyle ),
        FSNS( XML_w, XML_keepNext ),
        FSNS( XML_w, XML_keepLines ),
        FSNS( XML_w, XML_pageBreakBefore ),
        FSNS( XML_w, XML_framePr ),
        FSNS( XML_w, XML_widowControl ),
        FSNS( XML_w, XML_numPr ),
        FSNS( XML_w, XML_suppressLineNumbers ),
        FSNS( XML_w, XML_pBdr ),
        FSNS( XML_w, XML_shd ),
        FSNS( XML_w, XML_tabs ),
        FSNS( XML_w, XML_suppressAutoHyphens ),
        FSNS( XML_w, XML_kinsoku ),
        FSNS( XML_w, XML_wordWrap ),
        FSNS( XML_w, XML_overflowPunct ),
        FSNS( XML_w, XML_topLinePunct ),
        FSNS( XML_w, XML_autoSpaceDE ),
        FSNS( XML_w, XML_autoSpaceDN ),
        FSNS( XML_w, XML_bidi ),
        FSNS( XML_w, XML_adjustRightInd ),
        FSNS( XML_w, XML_snapToGrid ),
        FSNS( XML_w, XML_spacing ),
        FSNS( XML_w, XML_ind ),
        FSNS( XML_w, XML_contextualSpacing ),
        FSNS( XML_w, XML_mirrorIndents ),
        FSNS( XML_w, XML_suppressOverlap ),
        FSNS( XML_w, XML_jc ),
        FSNS( XML_w, XML_textDirection ),
        FSNS( XML_w, XML_textAlignment ),
        FSNS( XML_w, XML_textboxTightWrap ),
        FSNS( XML_w, XML_outlineLvl ),
        FSNS( XML_w, XML_divId ),
        FSNS( XML_w, XML_cnfStyle ),
        FSNS( XML_w, XML_rPr ),
        FSNS( XML_w, XML_sectPr ),
        FSNS( XML_w, XML_pPrChange )
    };

    // postpone the output so that we can later [in EndParagraphProperties()]
    // prepend the properties before the run
    sal_Int32 len = sizeof ( aOrder ) / sizeof( sal_Int32 );
    uno::Sequence< sal_Int32 > aSeqOrder( len );
    for ( sal_Int32 i = 0; i < len; i++ )
        aSeqOrder[i] = aOrder[i];

    m_pSerializer->mark( aSeqOrder );
}

void DocxAttributeOutput::WriteCollectedParagraphProperties()
{
    if ( m_pFlyAttrList )
    {
        XFastAttributeListRef xAttrList( m_pFlyAttrList );
        m_pFlyAttrList = NULL;

        m_pSerializer->singleElementNS( XML_w, XML_framePr, xAttrList );
    }

    if ( m_pParagraphSpacingAttrList )
    {
        XFastAttributeListRef xAttrList( m_pParagraphSpacingAttrList );
        m_pParagraphSpacingAttrList = NULL;

        m_pSerializer->singleElementNS( XML_w, XML_spacing, xAttrList );
    }

    // Merge the marks for the ordered elements
    m_pSerializer->mergeTopMarks( );
}

void DocxAttributeOutput::EndParagraphProperties()
{
    WriteCollectedParagraphProperties();

    m_pSerializer->endElementNS( XML_w, XML_pPr );

    if ( m_nColBreakStatus == COLBRK_WRITE )
    {
        m_pSerializer->startElementNS( XML_w, XML_r, FSEND );
        m_pSerializer->singleElementNS( XML_w, XML_br,
                FSNS( XML_w, XML_type ), "column", FSEND );
        m_pSerializer->endElementNS( XML_w, XML_r );

        m_nColBreakStatus = COLBRK_NONE;
    }

    // merge the properties _before_ the run (strictly speaking, just
    // after the start of the paragraph)
    m_pSerializer->mergeTopMarks( sax_fastparser::MERGE_MARKS_PREPEND );
}

void DocxAttributeOutput::StartRun( const SwRedlineData* pRedlineData, bool /*bSingleEmptyRun*/ )
{
    // if there is some redlining in the document, output it
    StartRedline( pRedlineData );

    // postpone the output of the start of a run (there are elements that need
    // to be written before the start of the run, but we learn which they are
    // _inside_ of the run)
    m_pSerializer->mark(); // let's call it "postponed run start"

    // postpone the output of the text (we get it before the run properties,
    // but must write it after them)
    m_pSerializer->mark(); // let's call it "postponed text"
}

void DocxAttributeOutput::EndRun()
{
    // Write field starts
    for ( std::vector<FieldInfos>::iterator pIt = m_Fields.begin(); pIt != m_Fields.end(); )
    {
        // Add the fields starts for all but hyperlinks and TOCs
        if ( pIt->bOpen && pIt->pField )
        {
            StartField_Impl( *pIt );

            // Remove the field from the stack if only the start has to be written
            // Unknown fields sould be removed too
            if ( !pIt->bClose || ( pIt->eType == ww::eUNKNOWN ) )
            {
                pIt = m_Fields.erase( pIt );
                continue;
            }
        }
        ++pIt;
    }

    // write the run properties + the text, already in the correct order
    m_pSerializer->mergeTopMarks(); // merges with "postponed text", see above

    // level down, to be able to prepend the actual run start attribute (just
    // before "postponed run start")
    m_pSerializer->mark(); // let's call it "actual run start"

    if ( m_closeHyperlinkInPreviousRun )
    {
        if ( m_startedHyperlink )
        {
            m_pSerializer->endElementNS( XML_w, XML_hyperlink );
            m_startedHyperlink = false;
        }
        m_closeHyperlinkInPreviousRun = false;
    }

    // Write the hyperlink and toc fields starts
    for ( std::vector<FieldInfos>::iterator pIt = m_Fields.begin(); pIt != m_Fields.end(); )
    {
        // Add the fields starts for hyperlinks, TOCs and index marks
        if ( pIt->bOpen && !pIt->pField )
        {
            StartField_Impl( *pIt, sal_True );

            // Remove the field if no end needs to be written
            if ( !pIt->bClose ) {
                pIt = m_Fields.erase( pIt );
                continue;
            }
        }
        ++pIt;
    }

    // Start the hyperlink after the fields separators or we would generate invalid file
    if ( m_pHyperlinkAttrList )
    {
        XFastAttributeListRef xAttrList ( m_pHyperlinkAttrList );

        m_pSerializer->startElementNS( XML_w, XML_hyperlink, xAttrList );
        m_pHyperlinkAttrList = NULL;
        m_startedHyperlink = true;
    }

    DoWriteBookmarks( );

    m_pSerializer->startElementNS( XML_w, XML_r, FSEND );
    m_pSerializer->mergeTopMarks( sax_fastparser::MERGE_MARKS_PREPEND ); // merges with "postponed run start", see above

    // write the run start + the run content
    m_pSerializer->mergeTopMarks(); // merges the "actual run start"

    // append the actual run end
    m_pSerializer->endElementNS( XML_w, XML_r );

    WritePostponedMath();

    if ( m_closeHyperlinkInThisRun )
    {
        if ( m_startedHyperlink )
        {
            m_pSerializer->endElementNS( XML_w, XML_hyperlink );
            m_startedHyperlink = false;
        }
        m_closeHyperlinkInThisRun = false;
    }

    while ( m_Fields.begin() != m_Fields.end() )
    {
        EndField_Impl( m_Fields.front( ) );
        m_Fields.erase( m_Fields.begin( ) );
    }

    // if there is some redlining in the document, output it
    EndRedline();
}

void DocxAttributeOutput::DoWriteBookmarks()
{
    // Write the start bookmarks
    for ( std::vector< OString >::const_iterator it = m_rMarksStart.begin(), end = m_rMarksStart.end();
          it != end; ++it )
    {
        const OString& rName = *it;

        // Output the bookmark
        sal_uInt16 nId = m_nNextMarkId++;
        m_rOpenedMarksIds[rName] = nId;
        m_pSerializer->singleElementNS( XML_w, XML_bookmarkStart,
            FSNS( XML_w, XML_id ), OString::valueOf( sal_Int32( nId ) ).getStr(  ),
            FSNS( XML_w, XML_name ), rName.getStr(),
            FSEND );
    }
    m_rMarksStart.clear();

    // export the end bookmarks
    for ( std::vector< OString >::const_iterator it = m_rMarksEnd.begin(), end = m_rMarksEnd.end();
          it != end; ++it )
    {
        const OString& rName = *it;

        // Get the id of the bookmark
        std::map< OString, sal_uInt16 >::iterator pPos = m_rOpenedMarksIds.find( rName );
        if ( pPos != m_rOpenedMarksIds.end(  ) )
        {
            sal_uInt16 nId = ( *pPos ).second;
            m_pSerializer->singleElementNS( XML_w, XML_bookmarkEnd,
                FSNS( XML_w, XML_id ), OString::valueOf( sal_Int32( nId ) ).getStr(  ),
                FSEND );
            m_rOpenedMarksIds.erase( rName );
        }
    }
    m_rMarksEnd.clear();
}

void DocxAttributeOutput::WriteFFData(  const FieldInfos& rInfos )
{
    const ::sw::mark::IFieldmark& rFieldmark = *rInfos.pFieldmark;
    if ( rInfos.eType == ww::eFORMDROPDOWN )
    {
        uno::Sequence< ::rtl::OUString> vListEntries;
        rtl::OUString sName, sHelp, sToolTip, sSelected;

        FieldMarkParamsHelper params( rFieldmark );
        params.extractParam( ODF_FORMDROPDOWN_LISTENTRY, vListEntries );
        sName = params.getName();
        sal_Int32 nSelectedIndex = 0;

        if ( params.extractParam( ODF_FORMDROPDOWN_RESULT, nSelectedIndex ) )
        {
            if (nSelectedIndex < vListEntries.getLength() )
                sSelected = vListEntries[ nSelectedIndex ];
        }

        GetExport().DoComboBox( sName, sHelp, sToolTip, sSelected, vListEntries );
    }
    else if ( rInfos.eType == ww::eFORMCHECKBOX )
    {
        rtl::OUString sName, sDefault;
        bool bChecked = false;

        FieldMarkParamsHelper params( rFieldmark );
        params.extractParam( ODF_FORMCHECKBOX_NAME, sName );

        const sw::mark::ICheckboxFieldmark* pCheckboxFm = dynamic_cast<const sw::mark::ICheckboxFieldmark*>(&rFieldmark);
        if ( pCheckboxFm && pCheckboxFm->IsChecked() )
            bChecked = true;

        FFDataWriterHelper ffdataOut( m_pSerializer );
        ffdataOut.WriteFormCheckbox( sName, rtl::OUString(), bChecked );
    }
    else if ( rInfos.eType == ww::eFORMTEXT )
    {
        FieldMarkParamsHelper params( rFieldmark );
        FFDataWriterHelper ffdataOut( m_pSerializer );
        ffdataOut.WriteFormText( params.getName(), rtl::OUString() );
    }
}

void DocxAttributeOutput::StartField_Impl( FieldInfos& rInfos, sal_Bool bWriteRun )
{
    if ( rInfos.pField && rInfos.eType == ww::eUNKNOWN )
    {
        // Expand unsupported fields
        RunText( rInfos.pField->GetFieldName() );
    }
    else if ( rInfos.eType != ww::eNONE ) // HYPERLINK fields are just commands
    {
        if ( bWriteRun )
            m_pSerializer->startElementNS( XML_w, XML_r, FSEND );

        if ( rInfos.eType == ww::eFORMDROPDOWN )
        {
                m_pSerializer->startElementNS( XML_w, XML_fldChar,
                    FSNS( XML_w, XML_fldCharType ), "begin",
                    FSEND );
                if ( rInfos.pFieldmark && !rInfos.pField )
                    WriteFFData(  rInfos );
                if ( rInfos.pField )
                {
                    const SwDropDownField& rFld2 = *(SwDropDownField*)rInfos.pField;
                    uno::Sequence<rtl::OUString> aItems =
                        rFld2.GetItemSequence();
                    GetExport().DoComboBox(rFld2.GetName(),
                               rFld2.GetHelp(),
                               rFld2.GetToolTip(),
                               rFld2.GetSelectedItem(), aItems);
                }
                m_pSerializer->endElementNS( XML_w, XML_fldChar );

                if ( bWriteRun )
                    m_pSerializer->endElementNS( XML_w, XML_r );
                if ( !rInfos.pField )
                    CmdField_Impl( rInfos );

        }
        else
        {
            // Write the field start
            m_pSerializer->startElementNS( XML_w, XML_fldChar,
                FSNS( XML_w, XML_fldCharType ), "begin",
                FSEND );

            if ( rInfos.pFieldmark )
                WriteFFData(  rInfos );

            m_pSerializer->endElementNS( XML_w, XML_fldChar );

            if ( bWriteRun )
                m_pSerializer->endElementNS( XML_w, XML_r );

            // The hyperlinks fields can't be expanded: the value is
            // normally in the text run
            if ( !rInfos.pField )
                CmdField_Impl( rInfos );
        }
    }
}

void DocxAttributeOutput::DoWriteCmd( String& rCmd )
{
    // Write the Field command
    m_pSerializer->startElementNS( XML_w, XML_instrText, FSEND );
    m_pSerializer->writeEscaped( OUString( rCmd ) );
    m_pSerializer->endElementNS( XML_w, XML_instrText );

}

void DocxAttributeOutput::CmdField_Impl( FieldInfos& rInfos )
{
    m_pSerializer->startElementNS( XML_w, XML_r, FSEND );
    xub_StrLen nNbToken = comphelper::string::getTokenCount(rInfos.sCmd, '\t');

    for ( xub_StrLen i = 0; i < nNbToken; i++ )
    {
        String sToken = rInfos.sCmd.GetToken( i, '\t' );
        if ( rInfos.eType ==  ww::eCREATEDATE
          || rInfos.eType ==  ww::eSAVEDATE
          || rInfos.eType ==  ww::ePRINTDATE
          || rInfos.eType ==  ww::eDATE
          || rInfos.eType ==  ww::eTIME )
        {
           sToken.SearchAndReplaceAll( String( "NNNN" ), String( "dddd"  ) );
           sToken.SearchAndReplaceAll( String( "NN" ), String( "ddd"  ) );
        }
        // Write the Field command
        DoWriteCmd( sToken );

        // Replace tabs by </instrText><tab/><instrText>
        if ( i < ( nNbToken - 1 ) )
            RunText( String::CreateFromAscii( "\t" ) );
    }

    m_pSerializer->endElementNS( XML_w, XML_r );

    // Write the Field separator
    m_pSerializer->startElementNS( XML_w, XML_r, FSEND );
    m_pSerializer->singleElementNS( XML_w, XML_fldChar,
          FSNS( XML_w, XML_fldCharType ), "separate",
          FSEND );
    m_pSerializer->endElementNS( XML_w, XML_r );
}

void DocxAttributeOutput::EndField_Impl( FieldInfos& rInfos )
{
    // The command has to be written before for the hyperlinks
    if ( rInfos.pField )
    {
        CmdField_Impl( rInfos );
    }

    // Write the bookmark start if any
    OUString aBkmName( m_sFieldBkm );
    if ( !aBkmName.isEmpty() )
    {
        m_pSerializer->singleElementNS( XML_w, XML_bookmarkStart,
               FSNS( XML_w, XML_id ), OString::valueOf( sal_Int32( m_nNextMarkId ) ).getStr( ),
               FSNS( XML_w, XML_name ), OUStringToOString( aBkmName, RTL_TEXTENCODING_UTF8 ).getStr( ),
               FSEND );
    }

    if (rInfos.pField ) // For hyperlinks and TOX
    {
        // Write the Field latest value
        m_pSerializer->startElementNS( XML_w, XML_r, FSEND );

        String sExpand( rInfos.pField->ExpandField( true ) );
        // newlines embedded in fields are 0x0B in MSO and 0x0A for us
        sExpand.SearchAndReplaceAll( 0x0A, 0x0B );
        RunText( sExpand );

        m_pSerializer->endElementNS( XML_w, XML_r );
    }

    // Write the bookmark end if any
    if ( !aBkmName.isEmpty() )
    {
        m_pSerializer->singleElementNS( XML_w, XML_bookmarkEnd,
               FSNS( XML_w, XML_id ), OString::valueOf( sal_Int32( m_nNextMarkId ) ).getStr( ),
               FSEND );

        m_nNextMarkId++;
    }

    // Write the Field end
    if ( rInfos.bClose  )
    {
        m_pSerializer->startElementNS( XML_w, XML_r, FSEND );
        m_pSerializer->singleElementNS( XML_w, XML_fldChar,
              FSNS( XML_w, XML_fldCharType ), "end",
              FSEND );
        m_pSerializer->endElementNS( XML_w, XML_r );
    }
    // Write the ref field if a bookmark had to be set and the field
    // should be visible
    if ( rInfos.pField )
    {
        sal_uInt16 nSubType = rInfos.pField->GetSubType( );
        bool bIsSetField = rInfos.pField->GetTyp( )->Which( ) == RES_SETEXPFLD;
        bool bShowRef = ( !bIsSetField || ( nSubType & nsSwExtendedSubType::SUB_INVISIBLE ) ) ? false : true;

        if ( ( m_sFieldBkm.Len( ) > 0 ) && bShowRef )
        {
            // Write the field beginning
            m_pSerializer->startElementNS( XML_w, XML_r, FSEND );
            m_pSerializer->singleElementNS( XML_w, XML_fldChar,
                FSNS( XML_w, XML_fldCharType ), "begin",
                FSEND );
            m_pSerializer->endElementNS( XML_w, XML_r );

            rInfos.sCmd = FieldString( ww::eREF );
            rInfos.sCmd.APPEND_CONST_ASC( "\"" );
            rInfos.sCmd += m_sFieldBkm;
            rInfos.sCmd.APPEND_CONST_ASC( "\" " );

            // Clean the field bookmark data to avoid infinite loop
            m_sFieldBkm = String( );

            // Write the end of the field
            EndField_Impl( rInfos );
        }
    }
}

void DocxAttributeOutput::StartRunProperties()
{
    // postpone the output so that we can later [in EndRunProperties()]
    // prepend the properties before the text
    m_pSerializer->mark();

    m_pSerializer->startElementNS( XML_w, XML_rPr, FSEND );

    InitCollectedRunProperties();

    OSL_ASSERT( m_postponedGraphic == NULL );
    m_postponedGraphic = new std::list< PostponedGraphic >;
}

void DocxAttributeOutput::InitCollectedRunProperties()
{
    m_pFontsAttrList = NULL;
    m_pEastAsianLayoutAttrList = NULL;
    m_pCharLangAttrList = NULL;

    // Write the elements in the spec order
    static const sal_Int32 aOrder[] =
    {
        FSNS( XML_w, XML_rStyle ),
        FSNS( XML_w, XML_rFonts ),
        FSNS( XML_w, XML_b ),
        FSNS( XML_w, XML_bCs ),
        FSNS( XML_w, XML_i ),
        FSNS( XML_w, XML_iCs ),
        FSNS( XML_w, XML_caps ),
        FSNS( XML_w, XML_smallCaps ),
        FSNS( XML_w, XML_strike ),
        FSNS( XML_w, XML_dstrike ),
        FSNS( XML_w, XML_outline ),
        FSNS( XML_w, XML_shadow ),
        FSNS( XML_w, XML_emboss ),
        FSNS( XML_w, XML_imprint ),
        FSNS( XML_w, XML_noProof ),
        FSNS( XML_w, XML_snapToGrid ),
        FSNS( XML_w, XML_vanish ),
        FSNS( XML_w, XML_webHidden ),
        FSNS( XML_w, XML_color ),
        FSNS( XML_w, XML_spacing ),
        FSNS( XML_w, XML_w ),
        FSNS( XML_w, XML_kern ),
        FSNS( XML_w, XML_position ),
        FSNS( XML_w, XML_sz ),
        FSNS( XML_w, XML_szCs ),
        FSNS( XML_w, XML_highlight ),
        FSNS( XML_w, XML_u ),
        FSNS( XML_w, XML_effect ),
        FSNS( XML_w, XML_bdr ),
        FSNS( XML_w, XML_shd ),
        FSNS( XML_w, XML_fitText ),
        FSNS( XML_w, XML_vertAlign ),
        FSNS( XML_w, XML_rtl ),
        FSNS( XML_w, XML_cs ),
        FSNS( XML_w, XML_em ),
        FSNS( XML_w, XML_lang ),
        FSNS( XML_w, XML_eastAsianLayout ),
        FSNS( XML_w, XML_specVanish ),
        FSNS( XML_w, XML_oMath ),
        FSNS( XML_w, XML_rPrChange )
    };

    // postpone the output so that we can later [in EndParagraphProperties()]
    // prepend the properties before the run
    sal_Int32 len = sizeof ( aOrder ) / sizeof( sal_Int32 );
    uno::Sequence< sal_Int32 > aSeqOrder( len );
    for ( sal_Int32 i = 0; i < len; i++ )
        aSeqOrder[i] = aOrder[i];

    m_pSerializer->mark( aSeqOrder );

}

void DocxAttributeOutput::WriteCollectedRunProperties()
{
    // Write all differed properties
    if ( m_pFontsAttrList )
    {
        XFastAttributeListRef xAttrList( m_pFontsAttrList );
        m_pFontsAttrList = NULL;

        m_pSerializer->singleElementNS( XML_w, XML_rFonts, xAttrList );
    }

    if ( m_pEastAsianLayoutAttrList )
    {
        XFastAttributeListRef xAttrList( m_pEastAsianLayoutAttrList );
        m_pEastAsianLayoutAttrList = NULL;

        m_pSerializer->singleElementNS( XML_w, XML_eastAsianLayout, xAttrList );
    }

    if ( m_pCharLangAttrList )
    {
        XFastAttributeListRef xAttrList( m_pCharLangAttrList );
        m_pCharLangAttrList = NULL;

        m_pSerializer->singleElementNS( XML_w, XML_lang, xAttrList );
    }

    // Merge the marks for the ordered elements
    m_pSerializer->mergeTopMarks();
}

void DocxAttributeOutput::EndRunProperties( const SwRedlineData* /*pRedlineData*/ )
{
    WriteCollectedRunProperties();

    m_pSerializer->endElementNS( XML_w, XML_rPr );

    // write footnotes/endnotes if we have any
    FootnoteEndnoteReference();

    WritePostponedGraphic();

    // merge the properties _before_ the run text (strictly speaking, just
    // after the start of the run)
    m_pSerializer->mergeTopMarks( sax_fastparser::MERGE_MARKS_PREPEND );
}

void DocxAttributeOutput::WritePostponedGraphic()
{
    for( std::list< PostponedGraphic >::const_iterator it = m_postponedGraphic->begin();
         it != m_postponedGraphic->end();
         ++it )
        FlyFrameGraphic( it->grfNode, it->size );
    delete m_postponedGraphic;
    m_postponedGraphic = NULL;
}

void DocxAttributeOutput::FootnoteEndnoteRefTag()
{
    if( m_footnoteEndnoteRefTag == 0 )
        return;
    m_pSerializer->singleElementNS( XML_w, m_footnoteEndnoteRefTag, FSEND );
    m_footnoteEndnoteRefTag = 0;
}

/** Output sal_Unicode* as a run text (<t>the text</t>).

    When bMove is true, update rBegin to point _after_ the end of the text +
    1, meaning that it skips one character after the text.  This is to make
    the switch in DocxAttributeOutput::RunText() nicer ;-)
 */
static void impl_WriteRunText( FSHelperPtr pSerializer, sal_Int32 nTextToken,
        const sal_Unicode* &rBegin, const sal_Unicode* pEnd, bool bMove = true )
{
    const sal_Unicode *pBegin = rBegin;

    // skip one character after the end
    if ( bMove )
        rBegin = pEnd + 1;

    if ( pBegin >= pEnd )
        return; // we want to write at least one character

    // we have to add 'preserve' when starting/ending with space
    if ( *pBegin == sal_Unicode( ' ' ) || *( pEnd - 1 ) == sal_Unicode( ' ' ) )
    {
        pSerializer->startElementNS( XML_w, nTextToken, FSNS( XML_xml, XML_space ), "preserve", FSEND );
    }
    else
        pSerializer->startElementNS( XML_w, nTextToken, FSEND );

    pSerializer->writeEscaped( OUString( pBegin, pEnd - pBegin ) );

    pSerializer->endElementNS( XML_w, nTextToken );
}

void DocxAttributeOutput::RunText( const String& rText, rtl_TextEncoding /*eCharSet*/ )
{
    if( m_closeHyperlinkInThisRun )
    {
        m_closeHyperlinkInPreviousRun = true;
        m_closeHyperlinkInThisRun = false;
    }
    OUString aText( rText );

    // one text can be split into more <w:t>blah</w:t>'s by line breaks etc.
    const sal_Unicode *pBegin = aText.getStr();
    const sal_Unicode *pEnd = pBegin + aText.getLength();

    // the text run is usually XML_t, with the exception of the deleted text
    sal_Int32 nTextToken = XML_t;
    if ( m_pRedlineData && m_pRedlineData->GetType() == nsRedlineType_t::REDLINE_DELETE )
        nTextToken = XML_delText;

    for ( const sal_Unicode *pIt = pBegin; pIt < pEnd; ++pIt )
    {
        switch ( *pIt )
        {
            case 0x09: // tab
                impl_WriteRunText( m_pSerializer, nTextToken, pBegin, pIt );
                m_pSerializer->singleElementNS( XML_w, XML_tab, FSEND );
                break;
            case 0x0b: // line break
                impl_WriteRunText( m_pSerializer, nTextToken, pBegin, pIt );
                m_pSerializer->singleElementNS( XML_w, XML_br, FSEND );
                break;
            default:
                if ( *pIt < 0x0020 ) // filter out the control codes
                {
                    impl_WriteRunText( m_pSerializer, nTextToken, pBegin, pIt );
                    OSL_TRACE( "Ignored control code %x in a text run.", *pIt );
                }
                break;
        }
    }

    impl_WriteRunText( m_pSerializer, nTextToken, pBegin, pEnd, false );
}

void DocxAttributeOutput::RawText( const String& /*rText*/, bool /*bForceUnicode*/, rtl_TextEncoding /*eCharSet*/ )
{
    OSL_TRACE("TODO DocxAttributeOutput::RawText( const String& rText, bool bForceUnicode, rtl_TextEncoding eCharSet )" );
}

void DocxAttributeOutput::StartRuby( const SwTxtNode& rNode, xub_StrLen nPos, const SwFmtRuby& rRuby )
{
    OSL_TRACE("TODO DocxAttributeOutput::StartRuby( const SwTxtNode& rNode, const SwFmtRuby& rRuby )" );
    m_pSerializer->startElementNS( XML_w, XML_ruby, FSEND );
    m_pSerializer->startElementNS( XML_w, XML_rubyPr, FSEND );
    // hps
    // hpsBaseText
    // hpsRaise
    // lid
    lang::Locale aLocale( SwBreakIt::Get()->GetLocale(
                rNode.GetLang( nPos ) ) );
    OUString sLang( aLocale.Language );
    if ( !aLocale.Country.isEmpty() )
        sLang += OUString( "-" ) + OUString( aLocale.Country );
    m_pSerializer->singleElementNS( XML_w, XML_lid,
            FSNS( XML_w, XML_val ),
            OUStringToOString( sLang, RTL_TEXTENCODING_UTF8 ).getStr( ), FSEND );

    OString sAlign ( "center" );
    switch ( rRuby.GetAdjustment( ) )
    {
        case 0:
            sAlign = OString( "left" );
            break;
        case 1:
            // Defaults to center
            break;
        case 2:
            sAlign = OString( "right" );
            break;
        case 3:
            sAlign = OString( "distributeLetter" );
            break;
        case 4:
            sAlign = OString( "distributeSpace" );
            break;
        default:
            break;
    }
    m_pSerializer->singleElementNS( XML_w, XML_rubyAlign,
            FSNS( XML_w, XML_val ), sAlign.getStr(), FSEND );
    m_pSerializer->endElementNS( XML_w, XML_rubyPr );

    m_pSerializer->startElementNS( XML_w, XML_rt, FSEND );
    StartRun( NULL );
    StartRunProperties( );
    SwWW8AttrIter aAttrIt( m_rExport, rNode );
    aAttrIt.OutAttr( nPos, true );
    sal_uInt16 nStyle = m_rExport.GetId( *rRuby.GetTxtRuby()->GetCharFmt() );
    OString aStyleId( "style" );
    aStyleId += OString::valueOf( sal_Int32( nStyle ) );
    m_pSerializer->singleElementNS( XML_w, XML_rStyle,
            FSNS( XML_w, XML_val ), aStyleId.getStr(), FSEND );
    EndRunProperties( NULL );
    RunText( rRuby.GetText( ) );
    EndRun( );
    m_pSerializer->endElementNS( XML_w, XML_rt );

    m_pSerializer->startElementNS( XML_w, XML_rubyBase, FSEND );
    StartRun( NULL );
}

void DocxAttributeOutput::EndRuby()
{
    OSL_TRACE( "TODO DocxAttributeOutput::EndRuby()" );
    EndRun( );
    m_pSerializer->endElementNS( XML_w, XML_rubyBase );
    m_pSerializer->endElementNS( XML_w, XML_ruby );
}

bool DocxAttributeOutput::AnalyzeURL( const String& rUrl, const String& rTarget, String* pLinkURL, String* pMark )
{
    bool bBookMarkOnly = AttributeOutputBase::AnalyzeURL( rUrl, rTarget, pLinkURL, pMark );

    String sURL = *pLinkURL;
    String sMark = *pMark;

    bool bOutputField = sMark.Len();

    if ( bOutputField )
    {
        if ( bBookMarkOnly )
            sURL = FieldString( ww::eHYPERLINK );
        else
        {
            String sFld( FieldString( ww::eHYPERLINK ) );
            sFld.APPEND_CONST_ASC( "\"" );
            sURL.Insert( sFld, 0 );
            sURL += '\"';
        }

        if ( sMark.Len() )
            ( ( sURL.APPEND_CONST_ASC( " \\l \"" ) ) += sMark ) += '\"';

        if ( rTarget.Len() )
            ( sURL.APPEND_CONST_ASC( " \\n " ) ) += rTarget;
    }

    *pLinkURL = sURL;
    *pMark = sMark;

    return bBookMarkOnly;
}

bool DocxAttributeOutput::StartURL( const String& rUrl, const String& rTarget )
{
    String sMark;
    String sUrl;

    bool bBookmarkOnly = AnalyzeURL( rUrl, rTarget, &sUrl, &sMark );

    if ( sMark.Len() && !bBookmarkOnly )
    {
        m_rExport.OutputField( NULL, ww::eHYPERLINK, sUrl );
    }
    else
    {
        // Output a hyperlink XML element
        m_pHyperlinkAttrList = m_pSerializer->createAttrList();

        if ( !bBookmarkOnly )
        {
            OUString osUrl( sUrl );

            OString sId = rtl::OUStringToOString( GetExport().GetFilter().addRelation( m_pSerializer->getOutputStream(),
                        S( "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink" ),
                        osUrl, true ), RTL_TEXTENCODING_UTF8 );

            m_pHyperlinkAttrList->add( FSNS( XML_r, XML_id), sId.getStr());
        }
        else
            m_pHyperlinkAttrList->add( FSNS( XML_w, XML_anchor ),
                    OUStringToOString( OUString( sMark ), RTL_TEXTENCODING_UTF8 ).getStr( ) );

        OUString sTarget( rTarget );
        if ( !sTarget.isEmpty() )
        {
            OString soTarget = OUStringToOString( sTarget, RTL_TEXTENCODING_UTF8 );
            m_pHyperlinkAttrList->add(FSNS( XML_w, XML_tgtFrame ), soTarget.getStr());
        }
    }

    return true;
}

bool DocxAttributeOutput::EndURL()
{
    m_closeHyperlinkInThisRun = true;
    return true;
}

void DocxAttributeOutput::FieldVanish( const String& rTxt, ww::eField eType )
{
    WriteField_Impl( NULL, eType, rTxt, WRITEFIELD_ALL );
}

void DocxAttributeOutput::Redline( const SwRedlineData* /*pRedline*/ )
{
    OSL_TRACE( "TODO DocxAttributeOutput::Redline( const SwRedlineData* pRedline )" );
}

/// Append the number as 2-digit when less than 10.
static void impl_AppendTwoDigits( OStringBuffer &rBuffer, sal_Int32 nNum )
{
    if ( nNum < 0 || nNum > 99 )
    {
        rBuffer.append( "00" );
        return;
    }

    if ( nNum < 10 )
        rBuffer.append( '0' );

    rBuffer.append( nNum );
}

/** Convert DateTime to xsd::dateTime string.

I guess there must be an implementation of this somewhere in OOo, but I failed
to find it, unfortunately :-(
*/
static OString impl_DateTimeToOString( const DateTime& rDateTime )
{
    DateTime aInUTC( rDateTime );
// HACK: this is correct according to the spec, but MSOffice believes everybody lives
// in UTC+0 when reading it back
//    aInUTC.ConvertToUTC();

    OStringBuffer aBuffer( 25 );
    aBuffer.append( sal_Int32( aInUTC.GetYear() ) );
    aBuffer.append( '-' );

    impl_AppendTwoDigits( aBuffer, aInUTC.GetMonth() );
    aBuffer.append( '-' );

    impl_AppendTwoDigits( aBuffer, aInUTC.GetDay() );
    aBuffer.append( 'T' );

    impl_AppendTwoDigits( aBuffer, aInUTC.GetHour() );
    aBuffer.append( ':' );

    impl_AppendTwoDigits( aBuffer, aInUTC.GetMin() );
    aBuffer.append( ':' );

    impl_AppendTwoDigits( aBuffer, aInUTC.GetSec() );
    aBuffer.append( 'Z' ); // we are in UTC

    return aBuffer.makeStringAndClear();
}

void DocxAttributeOutput::StartRedline( const SwRedlineData* pRedlineData )
{
    m_pRedlineData = pRedlineData;

    if ( !m_pRedlineData )
        return;

    // FIXME check if it's necessary to travel over the Next()'s in pRedlineData

    OString aId( OString::valueOf( m_nRedlineId++ ) );

    const String &rAuthor( SW_MOD()->GetRedlineAuthor( pRedlineData->GetAuthor() ) );
    OString aAuthor( OUStringToOString( rAuthor, RTL_TEXTENCODING_UTF8 ) );

    OString aDate( impl_DateTimeToOString( pRedlineData->GetTimeStamp() ) );

    switch ( pRedlineData->GetType() )
    {
        case nsRedlineType_t::REDLINE_INSERT:
            m_pSerializer->startElementNS( XML_w, XML_ins,
                    FSNS( XML_w, XML_id ), aId.getStr(),
                    FSNS( XML_w, XML_author ), aAuthor.getStr(),
                    FSNS( XML_w, XML_date ), aDate.getStr(),
                    FSEND );
            break;

        case nsRedlineType_t::REDLINE_DELETE:
            m_pSerializer->startElementNS( XML_w, XML_del,
                    FSNS( XML_w, XML_id ), aId.getStr(),
                    FSNS( XML_w, XML_author ), aAuthor.getStr(),
                    FSNS( XML_w, XML_date ), aDate.getStr(),
                    FSEND );
            break;

        case nsRedlineType_t::REDLINE_FORMAT:
            OSL_TRACE( "TODO DocxAttributeOutput::StartRedline()" );
        default:
            break;
    }
}

void DocxAttributeOutput::EndRedline()
{
    if ( !m_pRedlineData )
        return;

    switch ( m_pRedlineData->GetType() )
    {
        case nsRedlineType_t::REDLINE_INSERT:
            m_pSerializer->endElementNS( XML_w, XML_ins );
            break;

        case nsRedlineType_t::REDLINE_DELETE:
            m_pSerializer->endElementNS( XML_w, XML_del );
            break;

        case nsRedlineType_t::REDLINE_FORMAT:
            OSL_TRACE( "TODO DocxAttributeOutput::EndRedline()" );
            break;
        default:
            break;
    }

    m_pRedlineData = NULL;
}

void DocxAttributeOutput::FormatDrop( const SwTxtNode& /*rNode*/, const SwFmtDrop& /*rSwFmtDrop*/, sal_uInt16 /*nStyle*/, ww8::WW8TableNodeInfo::Pointer_t /*pTextNodeInfo*/, ww8::WW8TableNodeInfoInner::Pointer_t )
{
    OSL_TRACE( "TODO DocxAttributeOutput::FormatDrop( const SwTxtNode& rNode, const SwFmtDrop& rSwFmtDrop, sal_uInt16 nStyle )" );
}

void DocxAttributeOutput::ParagraphStyle( sal_uInt16 nStyle )
{
    OString aStyleId( "style" );
    aStyleId += OString::valueOf( sal_Int32( nStyle ) );

    m_pSerializer->singleElementNS( XML_w, XML_pStyle, FSNS( XML_w, XML_val ), aStyleId.getStr(), FSEND );
}

static OString impl_ConvertColor( const Color &rColor )
{
    OString color( "auto" );
    if ( rColor.GetColor() != COL_AUTO )
    {
        const char pHexDigits[] = "0123456789ABCDEF";
        char pBuffer[] = "000000";

        pBuffer[0] = pHexDigits[ ( rColor.GetRed()   >> 4 ) & 0x0F ];
        pBuffer[1] = pHexDigits[   rColor.GetRed()          & 0x0F ];
        pBuffer[2] = pHexDigits[ ( rColor.GetGreen() >> 4 ) & 0x0F ];
        pBuffer[3] = pHexDigits[   rColor.GetGreen()        & 0x0F ];
        pBuffer[4] = pHexDigits[ ( rColor.GetBlue()  >> 4 ) & 0x0F ];
        pBuffer[5] = pHexDigits[   rColor.GetBlue()         & 0x0F ];

        color = OString( pBuffer );
    }
    return color;
}

static void impl_borderLine( FSHelperPtr pSerializer, sal_Int32 elementToken, const SvxBorderLine* pBorderLine, sal_uInt16 nDist )
{
    FastAttributeList* pAttr = pSerializer->createAttrList();


    // Compute val attribute value
    // Can be one of:
    //      single, double,
    //      basicWideOutline, basicWideInline
    // OOXml also supports those types of borders, but we'll try to play with the first ones.
    //      thickThinMediumGap, thickThinLargeGap, thickThinSmallGap
    //      thinThickLargeGap, thinThickMediumGap, thinThickSmallGap
    const char* pVal = "none";
    if ( !pBorderLine->isEmpty( ) )
    {
        switch (pBorderLine->GetBorderLineStyle())
        {
            case table::BorderLineStyle::SOLID:
                pVal = ( sal_Char* )"single";
                break;
            case table::BorderLineStyle::DOTTED:
                pVal = ( sal_Char* )"dotted";
                break;
            case table::BorderLineStyle::DASHED:
                pVal = ( sal_Char* )"dashed";
                break;
            case table::BorderLineStyle::DOUBLE:
                pVal = ( sal_Char* )"double";
                break;
            case table::BorderLineStyle::THINTHICK_SMALLGAP:
                pVal = ( sal_Char* )"thinThickSmallGap";
                break;
            case table::BorderLineStyle::THINTHICK_MEDIUMGAP:
                pVal = ( sal_Char* )"thinThickMediumGap";
                break;
            case table::BorderLineStyle::THINTHICK_LARGEGAP:
                pVal = ( sal_Char* )"thinThickLargeGap";
                break;
            case table::BorderLineStyle::THICKTHIN_SMALLGAP:
                pVal = ( sal_Char* )"thickThinSmallGap";
                break;
            case table::BorderLineStyle::THICKTHIN_MEDIUMGAP:
                pVal = ( sal_Char* )"thickThinMediumGap";
                break;
            case table::BorderLineStyle::THICKTHIN_LARGEGAP:
                pVal = ( sal_Char* )"thickThinLargeGap";
                break;
            case table::BorderLineStyle::EMBOSSED:
                pVal = ( sal_Char* )"threeDEmboss";
                break;
            case table::BorderLineStyle::ENGRAVED:
                pVal = ( sal_Char* )"threeDEngrave";
                break;
            case table::BorderLineStyle::OUTSET:
                pVal = ( sal_Char* )"outset";
                break;
            case table::BorderLineStyle::INSET:
                pVal = ( sal_Char* )"inset";
                break;
            case table::BorderLineStyle::NONE:
            default:
                break;
        }
    }

    pAttr->add( FSNS( XML_w, XML_val ), OString( pVal ) );

    if ( !pBorderLine->isEmpty() )
    {
        // Compute the sz attribute

        double const fConverted( ::editeng::ConvertBorderWidthToWord(
                pBorderLine->GetBorderLineStyle(), pBorderLine->GetWidth()));
        // The unit is the 8th of point
        sal_Int32 nWidth = sal_Int32( fConverted / 2.5 );
        sal_uInt16 nMinWidth = 2;
        sal_uInt16 nMaxWidth = 96;

        if ( nWidth > nMaxWidth )
            nWidth = nMaxWidth;
        else if ( nWidth < nMinWidth )
            nWidth = nMinWidth;

        pAttr->add( FSNS( XML_w, XML_sz ), OString::valueOf( sal_Int32( nWidth ) ) );

        // Get the distance (in pt)
        pAttr->add( FSNS( XML_w, XML_space ), OString::valueOf( sal_Int32( nDist / 20 ) ) );

        // Get the color code as an RRGGBB hex value
        OString sColor( impl_ConvertColor( pBorderLine->GetColor( ) ) );
        pAttr->add( FSNS( XML_w, XML_color ), sColor );
    }

    XFastAttributeListRef xAttrs( pAttr );
    pSerializer->singleElementNS( XML_w, elementToken, xAttrs );
}

static void impl_pageBorders( FSHelperPtr pSerializer, const SvxBoxItem& rBox, bool bUseStartEnd = false )
{
    static const sal_uInt16 aBorders[] =
    {
        BOX_LINE_TOP, BOX_LINE_LEFT, BOX_LINE_BOTTOM, BOX_LINE_RIGHT
    };

    const sal_Int32 aXmlElements[] =
    {
        XML_top,
        bUseStartEnd ? XML_start : XML_left,
        XML_bottom,
        bUseStartEnd ? XML_end : XML_right
    };
    const sal_uInt16* pBrd = aBorders;
    for( int i = 0; i < 4; ++i, ++pBrd )
    {
        const SvxBorderLine* pLn = rBox.GetLine( *pBrd );
        if ( pLn )
            impl_borderLine( pSerializer, aXmlElements[i], pLn, 0 );
    }
}

void DocxAttributeOutput::TableCellProperties( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )
{
    m_pSerializer->startElementNS( XML_w, XML_tcPr, FSEND );

    const SwTableBox *pTblBox = pTableTextNodeInfoInner->getTableBox( );

    bool bEcma = GetExport().GetFilter().getVersion( ) == oox::core::ECMA_DIALECT;

    // Cell prefered width
    SwTwips nWidth = GetGridCols( pTableTextNodeInfoInner )->at( pTableTextNodeInfoInner->getCell() );
    if ( pTableTextNodeInfoInner->getCell() )
        nWidth = nWidth - GetGridCols( pTableTextNodeInfoInner )->at( pTableTextNodeInfoInner->getCell() - 1 );
    m_pSerializer->singleElementNS( XML_w, XML_tcW,
           FSNS( XML_w, XML_w ), OString::valueOf( sal_Int32( nWidth ) ).getStr( ),
           FSNS( XML_w, XML_type ), "dxa",
           FSEND );

    // Horizontal spans
    const SwWriteTableRows& aRows = m_pTableWrt->GetRows( );
    SwWriteTableRow *pRow = aRows[ pTableTextNodeInfoInner->getRow( ) ];
    const SwWriteTableCell *pCell = &pRow->GetCells( )[ pTableTextNodeInfoInner->getCell( ) ];

    sal_uInt16 nColSpan = pCell->GetColSpan();
    if ( nColSpan > 1 )
        m_pSerializer->singleElementNS( XML_w, XML_gridSpan,
                FSNS( XML_w, XML_val ), OString::valueOf( sal_Int32( nColSpan ) ).getStr(),
                FSEND );

    // Vertical merges
    long vSpan = pTblBox->getRowSpan( );
    if ( vSpan > 1 )
    {
        m_pSerializer->singleElementNS( XML_w, XML_vMerge,
                FSNS( XML_w, XML_val ), "restart",
                FSEND );
    }
    else if ( vSpan < 0 )
    {
        m_pSerializer->singleElementNS( XML_w, XML_vMerge,
                FSNS( XML_w, XML_val ), "continue",
                FSEND );
    }

    // The cell borders
    m_pSerializer->startElementNS( XML_w, XML_tcBorders, FSEND );
    SwFrmFmt *pFmt = pTblBox->GetFrmFmt( );
    impl_pageBorders( m_pSerializer, pFmt->GetBox( ), !bEcma );
    m_pSerializer->endElementNS( XML_w, XML_tcBorders );

    TableBackgrounds( pTableTextNodeInfoInner );

    // Cell margins
    m_pSerializer->startElementNS( XML_w, XML_tcMar, FSEND );
    const SvxBoxItem& rBox = pFmt->GetBox( );
    static const sal_uInt16 aBorders[] =
    {
        BOX_LINE_TOP, BOX_LINE_LEFT, BOX_LINE_BOTTOM, BOX_LINE_RIGHT
    };

    const sal_Int32 aXmlElements[] =
    {
        XML_top,
        bEcma ? XML_left : XML_start,
        XML_bottom,
        bEcma ? XML_right : XML_end
    };
    const sal_uInt16* pBrd = aBorders;
    for( int i = 0; i < 4; ++i, ++pBrd )
    {
        sal_Int32 nDist = sal_Int32( rBox.GetDistance( *pBrd ) );
        m_pSerializer->singleElementNS( XML_w, aXmlElements[i],
               FSNS( XML_w, XML_w ), OString::valueOf( nDist ).getStr( ),
               FSNS( XML_w, XML_type ), "dxa",
               FSEND );
    }

    m_pSerializer->endElementNS( XML_w, XML_tcMar );

    TableVerticalCell( pTableTextNodeInfoInner );

    m_pSerializer->endElementNS( XML_w, XML_tcPr );
}

void DocxAttributeOutput::InitTableHelper( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )
{
    sal_uInt32 nPageSize = 0;
    bool bRelBoxSize = false;

    // Create the SwWriteTable instance to use col spans (and maybe other infos)
    GetTablePageSize( pTableTextNodeInfoInner.get(), nPageSize, bRelBoxSize );

    const SwTable* pTable = pTableTextNodeInfoInner->getTable( );
    const SwFrmFmt *pFmt = pTable->GetFrmFmt( );
    SwTwips nTblSz = pFmt->GetFrmSize( ).GetWidth( );

    const SwHTMLTableLayout *pLayout = pTable->GetHTMLTableLayout();
    if( pLayout && pLayout->IsExportable() )
        m_pTableWrt = new SwWriteTable( pLayout );
    else
        m_pTableWrt = new SwWriteTable( pTable->GetTabLines(), (sal_uInt16)nPageSize,
                (sal_uInt16)nTblSz, false);
}

void DocxAttributeOutput::StartTable( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )
{
    m_pSerializer->startElementNS( XML_w, XML_tbl, FSEND );

    InitTableHelper( pTableTextNodeInfoInner );
    TableDefinition( pTableTextNodeInfoInner );
}

void DocxAttributeOutput::EndTable()
{
    m_pSerializer->endElementNS( XML_w, XML_tbl );

    if ( m_nTableDepth > 0 )
        --m_nTableDepth;

    // We closed the table; if it is a nested table, the cell that contains it
    // still continues
    m_bTableCellOpen = true;

    // Cleans the table helper
    delete m_pTableWrt, m_pTableWrt = NULL;
}

void DocxAttributeOutput::StartTableRow( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )
{
    m_pSerializer->startElementNS( XML_w, XML_tr, FSEND );

    // Output the row properties
    m_pSerializer->startElementNS( XML_w, XML_trPr, FSEND );

    // Header row: tblHeader
    const SwTable *pTable = pTableTextNodeInfoInner->getTable( );
    if ( pTable->GetRowsToRepeat( ) > pTableTextNodeInfoInner->getRow( ) )
        m_pSerializer->singleElementNS( XML_w, XML_tblHeader,
               FSNS( XML_w, XML_val ), "true",
               FSEND );

    TableHeight( pTableTextNodeInfoInner );
    TableCanSplit( pTableTextNodeInfoInner );

    m_pSerializer->endElementNS( XML_w, XML_trPr );
}

void DocxAttributeOutput::EndTableRow( )
{
    m_pSerializer->endElementNS( XML_w, XML_tr );
}

void DocxAttributeOutput::StartTableCell( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )
{
    if ( !m_pTableWrt )
        InitTableHelper( pTableTextNodeInfoInner );

    m_pSerializer->startElementNS( XML_w, XML_tc, FSEND );

    // Write the cell properties here
    TableCellProperties( pTableTextNodeInfoInner );

    m_bTableCellOpen = true;
}

void DocxAttributeOutput::EndTableCell( )
{
    m_pSerializer->endElementNS( XML_w, XML_tc );

    m_bTableCellOpen = false;
}

void DocxAttributeOutput::TableInfoCell( ww8::WW8TableNodeInfoInner::Pointer_t /*pTableTextNodeInfoInner*/ )
{
}

void DocxAttributeOutput::TableInfoRow( ww8::WW8TableNodeInfoInner::Pointer_t /*pTableTextNodeInfo*/ )
{
}

void DocxAttributeOutput::TableDefinition( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )
{
    bool bEcma = GetExport().GetFilter().getVersion( ) == oox::core::ECMA_DIALECT;

    // Write the table properties
    m_pSerializer->startElementNS( XML_w, XML_tblPr, FSEND );

    static const sal_Int32 aOrder[] =
    {
        FSNS( XML_w, XML_tblStyle ),
        FSNS( XML_w, XML_tblpPr ),
        FSNS( XML_w, XML_tblOverlap ),
        FSNS( XML_w, XML_bidiVisual ),
        FSNS( XML_w, XML_tblStyleRowBandSize ),
        FSNS( XML_w, XML_tblStyleColBandSize ),
        FSNS( XML_w, XML_tblW ),
        FSNS( XML_w, XML_jc ),
        FSNS( XML_w, XML_tblCellSpacing ),
        FSNS( XML_w, XML_tblInd ),
        FSNS( XML_w, XML_tblBorders ),
        FSNS( XML_w, XML_shd ),
        FSNS( XML_w, XML_tblLayout ),
        FSNS( XML_w, XML_tblCellMar ),
        FSNS( XML_w, XML_tblLook ),
        FSNS( XML_w, XML_tblPrChange )
    };

    // postpone the output so that we can later []
    // prepend the properties before the run
    sal_Int32 len = sizeof ( aOrder ) / sizeof( sal_Int32 );
    uno::Sequence< sal_Int32 > aSeqOrder( len );
    for ( sal_Int32 i = 0; i < len; i++ )
        aSeqOrder[i] = aOrder[i];

    m_pSerializer->mark( aSeqOrder );

    sal_uInt32 nPageSize = 0;
    bool bRelBoxSize = false;

    // Create the SwWriteTable instance to use col spans (and maybe other infos)
    GetTablePageSize( pTableTextNodeInfoInner.get(), nPageSize, bRelBoxSize );

    // Output the table prefered width
    if ( nPageSize != 0 )
        m_pSerializer->singleElementNS( XML_w, XML_tblW,
                FSNS( XML_w, XML_w ), OString::valueOf( sal_Int32( nPageSize ) ).getStr( ),
                FSNS( XML_w, XML_type ), "dxa",
                FSEND );

    // Output the table alignement
    const SwTable *pTable = pTableTextNodeInfoInner->getTable();
    SwFrmFmt *pTblFmt = pTable->GetFrmFmt( );
    const char* pJcVal;
    sal_Int32 nIndent = 0;
    switch ( pTblFmt->GetHoriOrient( ).GetHoriOrient( ) )
    {
        case text::HoriOrientation::CENTER:
            pJcVal = "center";
            break;
        case text::HoriOrientation::RIGHT:
            if ( bEcma )
                pJcVal = "right";
            else
                pJcVal = "end";
            break;
        default:
        case text::HoriOrientation::NONE:
        case text::HoriOrientation::LEFT_AND_WIDTH:
        {
            if ( bEcma )
                pJcVal = "left";
            else
                pJcVal = "start";
            nIndent = sal_Int32( pTblFmt->GetLRSpace( ).GetLeft( ) );
            break;
        }
    }
    m_pSerializer->singleElementNS( XML_w, XML_jc,
            FSNS( XML_w, XML_val ), pJcVal,
            FSEND );

    // Output the table borders
    TableDefaultBorders( pTableTextNodeInfoInner );
    TableBidi( pTableTextNodeInfoInner );

    // Table indent
    if ( nIndent != 0 )
        m_pSerializer->singleElementNS( XML_w, XML_tblInd,
                FSNS( XML_w, XML_w ), OString::valueOf( nIndent ).getStr( ),
                FSNS( XML_w, XML_type ), "dxa",
                FSEND );

    // Merge the marks for the ordered elements
    m_pSerializer->mergeTopMarks( );

    m_pSerializer->endElementNS( XML_w, XML_tblPr );

    // Write the table grid infos
    m_pSerializer->startElementNS( XML_w, XML_tblGrid, FSEND );
    sal_Int32 nPrv = 0;
    ww8::GridColsPtr pGridCols = GetGridCols( pTableTextNodeInfoInner );
    for ( ww8::GridCols::const_iterator it = pGridCols->begin(); it != pGridCols->end(); ++it )
    {
        sal_Int32 nWidth  =  sal_Int32( *it ) - nPrv;
        m_pSerializer->singleElementNS( XML_w, XML_gridCol,
               FSNS( XML_w, XML_w ), OString::valueOf( nWidth ).getStr( ),
               FSEND );
        nPrv = sal_Int32( *it );
    }

    m_pSerializer->endElementNS( XML_w, XML_tblGrid );
}

void DocxAttributeOutput::TableDefaultBorders( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )
{
    const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox();
    const SwFrmFmt * pFrmFmt = pTabBox->GetFrmFmt();

    bool bEcma = GetExport().GetFilter().getVersion( ) == oox::core::ECMA_DIALECT;

    // the defaults of the table are taken from the top-left cell
    m_pSerializer->startElementNS( XML_w, XML_tblBorders, FSEND );
    impl_pageBorders( m_pSerializer, pFrmFmt->GetBox( ), !bEcma );
    m_pSerializer->endElementNS( XML_w, XML_tblBorders );
}

void DocxAttributeOutput::TableBackgrounds( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )
{
    const SwTableBox *pTblBox = pTableTextNodeInfoInner->getTableBox( );
    const SwFrmFmt *pFmt = pTblBox->GetFrmFmt( );
    const SfxPoolItem *pI = NULL;

    Color aColor;
    if ( SFX_ITEM_ON == pFmt->GetAttrSet().GetItemState( RES_BACKGROUND, false, &pI ) )
        aColor = dynamic_cast<const SvxBrushItem *>(pI)->GetColor();
    else
        aColor = COL_AUTO;

    OString sColor = impl_ConvertColor( aColor );
    m_pSerializer->singleElementNS( XML_w, XML_shd,
            FSNS( XML_w, XML_fill ), sColor.getStr( ),
            FSNS( XML_w, XML_val ), "clear",
            FSEND );
}

void DocxAttributeOutput::TableHeight( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )
{
    const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox();
    const SwTableLine * pTabLine = pTabBox->GetUpper();
    const SwFrmFmt * pLineFmt = pTabLine->GetFrmFmt();

    const SwFmtFrmSize& rLSz = pLineFmt->GetFrmSize();
    if ( ATT_VAR_SIZE != rLSz.GetHeightSizeType() && rLSz.GetHeight() )
    {
        sal_Int32 nHeight = rLSz.GetHeight();
        const char *pRule = NULL;

        switch ( rLSz.GetHeightSizeType() )
        {
            case ATT_FIX_SIZE: pRule = "exact"; break;
            case ATT_MIN_SIZE: pRule = "atLeast"; break;
            default:           break;
        }

        if ( pRule )
            m_pSerializer->singleElementNS( XML_w, XML_trHeight,
                    FSNS( XML_w, XML_val ), OString::valueOf( nHeight ).getStr( ),
                    FSNS( XML_w, XML_hRule ), pRule,
                    FSEND );
    }
}

void DocxAttributeOutput::TableCanSplit( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )
{
    const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox();
    const SwTableLine * pTabLine = pTabBox->GetUpper();
    const SwFrmFmt * pLineFmt = pTabLine->GetFrmFmt();

    const SwFmtRowSplit& rSplittable = pLineFmt->GetRowSplit( );
    const char* pCantSplit = ( !rSplittable.GetValue( ) ) ? "true" : "false";

    m_pSerializer->singleElementNS( XML_w, XML_cantSplit,
           FSNS( XML_w, XML_val ), pCantSplit,
           FSEND );
}

void DocxAttributeOutput::TableBidi( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )
{
    const SwTable * pTable = pTableTextNodeInfoInner->getTable();
    const SwFrmFmt * pFrmFmt = pTable->GetFrmFmt();

    if ( m_rExport.TrueFrameDirection( *pFrmFmt ) == FRMDIR_HORI_RIGHT_TOP )
    {
        m_pSerializer->singleElementNS( XML_w, XML_bidiVisual,
                FSNS( XML_w, XML_val ), "true",
                FSEND );
    }
}

void DocxAttributeOutput::TableVerticalCell( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )
{
    const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox();
    const SwFrmFmt *pFrmFmt = pTabBox->GetFrmFmt( );

    if ( FRMDIR_VERT_TOP_RIGHT == m_rExport.TrueFrameDirection( *pFrmFmt ) )
        m_pSerializer->singleElementNS( XML_w, XML_textDirection,
               FSNS( XML_w, XML_val ), "tbRl",
               FSEND );

    const SwWriteTableRows& aRows = m_pTableWrt->GetRows( );
    SwWriteTableRow *pRow = aRows[ pTableTextNodeInfoInner->getRow( ) ];
    const SwWriteTableCell *pCell = &pRow->GetCells( )[ pTableTextNodeInfoInner->getCell( ) ];
    switch( pCell->GetVertOri())
    {
        case text::VertOrientation::TOP:
            break;
        case text::VertOrientation::CENTER:
            m_pSerializer->singleElementNS( XML_w, XML_vAlign,
                FSNS( XML_w, XML_val ), "center", FSEND );
            break;
        case text::VertOrientation::BOTTOM:
            m_pSerializer->singleElementNS( XML_w, XML_vAlign,
                FSNS( XML_w, XML_val ), "bottom", FSEND );
            break;
    }
}

void DocxAttributeOutput::TableNodeInfo( ww8::WW8TableNodeInfo::Pointer_t /*pNodeInfo*/ )
{
    OSL_TRACE( "TODO: DocxAttributeOutput::TableNodeInfo( ww8::WW8TableNodeInfo::Pointer_t pNodeInfo )" );
}

void DocxAttributeOutput::TableNodeInfoInner( ww8::WW8TableNodeInfoInner::Pointer_t pNodeInfoInner )
{
    // This is called when the nested table ends in a cell, and there's no
    // paragraph benhind that; so we must check for the ends of cell, rows,
    // tables
    // ['true' to write an empty paragraph, MS Word insists on that]
    FinishTableRowCell( pNodeInfoInner, true );
}

void DocxAttributeOutput::TableOrientation( ww8::WW8TableNodeInfoInner::Pointer_t /*pTableTextNodeInfoInner*/ )
{
    OSL_TRACE( "TODO: DocxAttributeOutput::TableOrientation( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )" );
}

void DocxAttributeOutput::TableSpacing( ww8::WW8TableNodeInfoInner::Pointer_t /*pTableTextNodeInfoInner*/ )
{
#if OSL_DEBUG_LEVEL > 1
    fprintf( stderr, "TODO: DocxAttributeOutput::TableSpacing( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )\n" );
#endif
}

void DocxAttributeOutput::TableRowEnd( sal_uInt32 /*nDepth*/ )
{
    OSL_TRACE( "TODO: DocxAttributeOutput::TableRowEnd( sal_uInt32 nDepth = 1 )" );
}

void DocxAttributeOutput::StartStyles()
{
    m_pSerializer->startElementNS( XML_w, XML_styles,
            FSNS( XML_xmlns, XML_w ), "http://schemas.openxmlformats.org/wordprocessingml/2006/main",
            FSEND );
}

void DocxAttributeOutput::EndStyles( sal_uInt16 /*nNumberOfStyles*/ )
{
    m_pSerializer->endElementNS( XML_w, XML_styles );
}

void DocxAttributeOutput::DefaultStyle( sal_uInt16 nStyle )
{
    // are these the values of enum ww::sti (see ../inc/wwstyles.hxx)?
#if OSL_DEBUG_LEVEL > 1
    OSL_TRACE( "TODO DocxAttributeOutput::DefaultStyle( sal_uInt16 nStyle )- %d", nStyle );
#else
    (void) nStyle; // to quiet the warning
#endif
}

void DocxAttributeOutput::FlyFrameGraphic( const SwGrfNode* pGrfNode, const Size& rSize, const SwFlyFrmFmt* pOLEFrmFmt, SwOLENode* pOLENode )
{
    OSL_TRACE( "TODO DocxAttributeOutput::FlyFrameGraphic( const SwGrfNode* pGrfNode, const Size& rSize, const SwFlyFrmFmt* pOLEFrmFmt, SwOLENode* pOLENode ) - some stuff still missing" );
    const SwFrmFmt* pFrmFmt = pGrfNode ? pGrfNode->GetFlyFmt() : pOLEFrmFmt;
    // create the relation ID
    OString aRelId;
    sal_Int32 nImageType;
    if ( pGrfNode && pGrfNode->IsLinkedFile() )
    {
        // linked image, just create the relation
        String aFileName;
        pGrfNode->GetFileFilterNms( &aFileName, 0 );

        // TODO Convert the file name to relative for better interoperability

        aRelId = m_rExport.AddRelation(
                    S( "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image" ),
                    OUString( aFileName ) );

        nImageType = XML_link;
    }
    else
    {
        // inline, we also have to write the image itself
        Graphic* pGraphic = 0;
        if (pGrfNode)
            pGraphic = &const_cast< Graphic& >( pGrfNode->GetGrf() );
        else
            pGraphic = pOLENode->GetGraphic();

        m_rDrawingML.SetFS( m_pSerializer ); // to be sure that we write to the right stream
        OUString aImageId = m_rDrawingML.WriteImage( *pGraphic );

        aRelId = OUStringToOString( aImageId, RTL_TEXTENCODING_UTF8 );

        nImageType = XML_embed;
    }

    if ( aRelId.isEmpty() )
        return;

    m_pSerializer->startElementNS( XML_w, XML_drawing,
            FSEND );
    bool isAnchor = pFrmFmt->GetAnchor().GetAnchorId() != FLY_AS_CHAR;
    if( isAnchor )
    {
        ::sax_fastparser::FastAttributeList* attrList = m_pSerializer->createAttrList();
        attrList->add( XML_behindDoc, pFrmFmt->GetOpaque().GetValue() ? "0" : "1" );
        attrList->add( XML_distT, "0" );
        attrList->add( XML_distB, "0" );
        attrList->add( XML_distL, "0" );
        attrList->add( XML_distR, "0" );
        attrList->add( XML_simplePos, "0" );
        attrList->add( XML_locked, "0" );
        attrList->add( XML_layoutInCell, "1" );
        attrList->add( XML_allowOverlap, "1" ); // TODO
        if( const SdrObject* pObj = pFrmFmt->FindRealSdrObject())
            attrList->add( XML_relativeHeight, OString::valueOf( sal_Int32( pObj->GetOrdNum())));
        m_pSerializer->startElementNS( XML_wp, XML_anchor, XFastAttributeListRef( attrList ));
        m_pSerializer->singleElementNS( XML_wp, XML_simplePos, XML_x, "0", XML_y, "0", FSEND ); // required, unused
        const char* relativeFromH;
        const char* relativeFromV;
        switch( pFrmFmt->GetAnchor().GetAnchorId())
        {
            case FLY_AT_PAGE:
                relativeFromV = relativeFromH = "page";
                break;
            case FLY_AT_PARA:
                relativeFromH = "column";
                relativeFromV = "paragraph";
                break;
            case FLY_AT_CHAR:
            default:
                relativeFromH = "character";
                relativeFromV = "line";
                break;
        };
        Point pos( 0, 0 );
        if( const SwFlyFrmFmt* flyfmt = dynamic_cast<const SwFlyFrmFmt*>(pFrmFmt)) // TODO is always true?
            pos = flyfmt->GetAnchoredObj()->GetCurrRelPos();
        OString x( OString::valueOf( TwipsToEMU( pos.X())));
        OString y( OString::valueOf( TwipsToEMU( pos.Y())));
        m_pSerializer->startElementNS( XML_wp, XML_positionH, XML_relativeFrom, relativeFromH, FSEND );
        m_pSerializer->startElementNS( XML_wp, XML_posOffset, FSEND );
        m_pSerializer->write( x.getStr() );
        m_pSerializer->endElementNS( XML_wp, XML_posOffset );
        m_pSerializer->endElementNS( XML_wp, XML_positionH );
        m_pSerializer->startElementNS( XML_wp, XML_positionV, XML_relativeFrom, relativeFromV, FSEND );
        m_pSerializer->startElementNS( XML_wp, XML_posOffset, FSEND );
        m_pSerializer->write( y.getStr() );
        m_pSerializer->endElementNS( XML_wp, XML_posOffset );
        m_pSerializer->endElementNS( XML_wp, XML_positionV );
    }
    else
    {
        m_pSerializer->startElementNS( XML_wp, XML_inline,
                XML_distT, "0", XML_distB, "0", XML_distL, "0", XML_distR, "0",
                FSEND );
    }
    // now the common parts
    // extent of the image
    OString aWidth( OString::valueOf( TwipsToEMU( rSize.Width() ) ) );
    OString aHeight( OString::valueOf( TwipsToEMU( rSize.Height() ) ) );
    m_pSerializer->singleElementNS( XML_wp, XML_extent,
            XML_cx, aWidth.getStr(),
            XML_cy, aHeight.getStr(),
            FSEND );
    // TODO - the right effectExtent, extent including the effect
    m_pSerializer->singleElementNS( XML_wp, XML_effectExtent,
            XML_l, "0", XML_t, "0", XML_r, "0", XML_b, "0",
            FSEND );

    if( isAnchor )
    {
        switch( pFrmFmt->GetSurround().GetValue())
        {
            case SURROUND_NONE:
                m_pSerializer->singleElementNS( XML_wp, XML_wrapTopAndBottom, FSEND );
                break;
            case SURROUND_THROUGHT:
                m_pSerializer->singleElementNS( XML_wp, XML_wrapNone, FSEND );
                break;
            case SURROUND_PARALLEL:
                m_pSerializer->singleElementNS( XML_wp, XML_wrapSquare,
                    XML_wrapText, "bothSides", FSEND );
                break;
            case SURROUND_IDEAL:
            default:
                m_pSerializer->singleElementNS( XML_wp, XML_wrapSquare,
                    XML_wrapText, "largest", FSEND );
                break;
        }
    }
    // picture description (used for pic:cNvPr later too)
    ::sax_fastparser::FastAttributeList* docPrattrList = m_pSerializer->createAttrList();
    docPrattrList->add( XML_id, OString::valueOf( sal_Int32( m_anchorId++ )).getStr());
    docPrattrList->add( XML_name, "Picture" );
    docPrattrList->add( XML_descr, OUStringToOString( pGrfNode ? pGrfNode->GetDescription() : pOLEFrmFmt->GetObjDescription(), RTL_TEXTENCODING_UTF8 ).getStr());
    if( GetExport().GetFilter().getVersion( ) != oox::core::ECMA_DIALECT )
        docPrattrList->add( XML_title, OUStringToOString( pGrfNode ? pGrfNode->GetTitle() : pOLEFrmFmt->GetObjTitle(), RTL_TEXTENCODING_UTF8 ).getStr());
    XFastAttributeListRef docPrAttrListRef( docPrattrList );
    m_pSerializer->startElementNS( XML_wp, XML_docPr, docPrAttrListRef );
    // TODO hyperlink
    // m_pSerializer->singleElementNS( XML_a, XML_hlinkClick,
    //         FSNS( XML_xmlns, XML_a ), "http://schemas.openxmlformats.org/drawingml/2006/main",
    //         FSNS( XML_r, XML_id ), "rId4",
    //         FSEND );
    m_pSerializer->endElementNS( XML_wp, XML_docPr );

    m_pSerializer->startElementNS( XML_wp, XML_cNvGraphicFramePr,
            FSEND );
    // TODO change aspect?
    m_pSerializer->singleElementNS( XML_a, XML_graphicFrameLocks,
            FSNS( XML_xmlns, XML_a ), "http://schemas.openxmlformats.org/drawingml/2006/main",
            XML_noChangeAspect, "1",
            FSEND );
    m_pSerializer->endElementNS( XML_wp, XML_cNvGraphicFramePr );

    m_pSerializer->startElementNS( XML_a, XML_graphic,
            FSNS( XML_xmlns, XML_a ), "http://schemas.openxmlformats.org/drawingml/2006/main",
            FSEND );
    m_pSerializer->startElementNS( XML_a, XML_graphicData,
            XML_uri, "http://schemas.openxmlformats.org/drawingml/2006/picture",
            FSEND );

    m_pSerializer->startElementNS( XML_pic, XML_pic,
            FSNS( XML_xmlns, XML_pic ), "http://schemas.openxmlformats.org/drawingml/2006/picture",
            FSEND );

    m_pSerializer->startElementNS( XML_pic, XML_nvPicPr,
            FSEND );
    // It seems pic:cNvpr and wp:docPr are pretty much the same thing with the same attributes
    m_pSerializer->startElementNS( XML_pic, XML_cNvPr, docPrAttrListRef );

    // TODO hyperlink
    // m_pSerializer->singleElementNS( XML_a, XML_hlinkClick,
    //     FSNS( XML_r, XML_id ), "rId4",
    //     FSEND );
    m_pSerializer->endElementNS( XML_pic, XML_cNvPr );

    m_pSerializer->startElementNS( XML_pic, XML_cNvPicPr,
            FSEND );
    // TODO change aspect?
    m_pSerializer->singleElementNS( XML_a, XML_picLocks,
            XML_noChangeAspect, "1", XML_noChangeArrowheads, "1",
            FSEND );
    m_pSerializer->endElementNS( XML_pic, XML_cNvPicPr );
    m_pSerializer->endElementNS( XML_pic, XML_nvPicPr );

    // the actual picture
    m_pSerializer->startElementNS( XML_pic, XML_blipFill,
            FSEND );
    m_pSerializer->singleElementNS( XML_a, XML_blip,
            FSNS( XML_r, nImageType ), aRelId.getStr(),
            FSEND );
    m_pSerializer->singleElementNS( XML_a, XML_srcRect,
            FSEND );
    m_pSerializer->startElementNS( XML_a, XML_stretch,
            FSEND );
    m_pSerializer->singleElementNS( XML_a, XML_fillRect,
            FSEND );
    m_pSerializer->endElementNS( XML_a, XML_stretch );
    m_pSerializer->endElementNS( XML_pic, XML_blipFill );

    // TODO setup the right values below
    m_pSerializer->startElementNS( XML_pic, XML_spPr,
            XML_bwMode, "auto",
            FSEND );
    m_pSerializer->startElementNS( XML_a, XML_xfrm,
            FSEND );
    m_pSerializer->singleElementNS( XML_a, XML_off,
            XML_x, "0", XML_y, "0",
            FSEND );
    m_pSerializer->singleElementNS( XML_a, XML_ext,
            XML_cx, aWidth.getStr(),
            XML_cy, aHeight.getStr(),
            FSEND );
    m_pSerializer->endElementNS( XML_a, XML_xfrm );
    m_pSerializer->startElementNS( XML_a, XML_prstGeom,
            XML_prst, "rect",
            FSEND );
    m_pSerializer->singleElementNS( XML_a, XML_avLst,
            FSEND );
    m_pSerializer->endElementNS( XML_a, XML_prstGeom );
    m_pSerializer->singleElementNS( XML_a, XML_noFill,
            FSEND );
    m_pSerializer->startElementNS( XML_a, XML_ln,
            XML_w, "9525",
            FSEND );
    m_pSerializer->singleElementNS( XML_a, XML_noFill,
            FSEND );
    m_pSerializer->singleElementNS( XML_a, XML_miter,
            XML_lim, "800000",
            FSEND );
    m_pSerializer->singleElementNS( XML_a, XML_headEnd,
            FSEND );
    m_pSerializer->singleElementNS( XML_a, XML_tailEnd,
            FSEND );
    m_pSerializer->endElementNS( XML_a, XML_ln );
    m_pSerializer->endElementNS( XML_pic, XML_spPr );

    m_pSerializer->endElementNS( XML_pic, XML_pic );

    m_pSerializer->endElementNS( XML_a, XML_graphicData );
    m_pSerializer->endElementNS( XML_a, XML_graphic );
    m_pSerializer->endElementNS( XML_wp, isAnchor ? XML_anchor : XML_inline );

    m_pSerializer->endElementNS( XML_w, XML_drawing );
}

void DocxAttributeOutput::WriteOLE2Obj( const SdrObject* pSdrObj, SwOLENode& rOLENode, const Size& rSize, const SwFlyFrmFmt* pFlyFrmFmt )
{
    if( WriteOLEChart( pSdrObj, rSize ))
        return;
    if( WriteOLEMath( pSdrObj, rOLENode, rSize ))
        return;
    // Then we fall back to just export the object as a graphic.
    FlyFrameGraphic( 0, rSize, pFlyFrmFmt, &rOLENode );
}

bool DocxAttributeOutput::WriteOLEChart( const SdrObject* pSdrObj, const Size& rSize )
{
    uno::Reference< chart2::XChartDocument > xChartDoc;
    uno::Reference< drawing::XShape > xShape( ((SdrObject*)pSdrObj)->getUnoShape(), uno::UNO_QUERY );
    if( xShape.is() )
    {
        uno::Reference< beans::XPropertySet > xPropSet( xShape, uno::UNO_QUERY );
        if( xPropSet.is() )
            xChartDoc.set( xPropSet->getPropertyValue( "Model" ), uno::UNO_QUERY );
    }

    if( xChartDoc.is() )
    {
        OSL_TRACE("DocxAttributeOutput::WriteOLE2Obj: export chart ");
        m_pSerializer->startElementNS( XML_w, XML_drawing,
            FSEND );
        m_pSerializer->startElementNS( XML_wp, XML_inline,
            XML_distT, "0", XML_distB, "0", XML_distL, "0", XML_distR, "0",
            FSEND );

        OString aWidth( OString::valueOf( TwipsToEMU( rSize.Width() ) ) );
        OString aHeight( OString::valueOf( TwipsToEMU( rSize.Height() ) ) );
        m_pSerializer->singleElementNS( XML_wp, XML_extent,
            XML_cx, aWidth.getStr(),
            XML_cy, aHeight.getStr(),
            FSEND );
        // TODO - the right effectExtent, extent including the effect
        m_pSerializer->singleElementNS( XML_wp, XML_effectExtent,
            XML_l, "0", XML_t, "0", XML_r, "0", XML_b, "0",
            FSEND );

        // should get the unique id
        sal_Int32 nID = 1;
        OUString sName("Object 1");
        uno::Reference< container::XNamed > xNamed( xShape, uno::UNO_QUERY );
        if( xNamed.is() )
            sName = xNamed->getName();

        m_pSerializer->singleElementNS( XML_wp, XML_docPr,
            XML_id, I32S( nID ),
            XML_name, USS( sName ),
            FSEND );

        m_pSerializer->singleElementNS( XML_wp, XML_cNvGraphicFramePr,
            FSEND );

        m_pSerializer->startElementNS( XML_a, XML_graphic,
            FSNS( XML_xmlns, XML_a ), "http://schemas.openxmlformats.org/drawingml/2006/main",
            FSEND );

        m_pSerializer->startElementNS( XML_a, XML_graphicData,
            XML_uri, "http://schemas.openxmlformats.org/drawingml/2006/chart",
            FSEND );

        OString aRelId;
        static sal_Int32 nChartCount = 0;
        nChartCount++;
        uno::Reference< frame::XModel > xModel( xChartDoc, uno::UNO_QUERY );
        aRelId = m_rExport.OutputChart( xModel, nChartCount );

        m_pSerializer->singleElementNS( XML_c, XML_chart,
            FSNS( XML_xmlns, XML_c ), "http://schemas.openxmlformats.org/drawingml/2006/chart",
            FSNS( XML_xmlns, XML_r ), "http://schemas.openxmlformats.org/officeDocument/2006/relationships",
            FSNS( XML_r, XML_id ), aRelId.getStr(),
            FSEND );

        m_pSerializer->endElementNS( XML_a, XML_graphicData );
        m_pSerializer->endElementNS( XML_a, XML_graphic );
        m_pSerializer->endElementNS( XML_wp, XML_inline );
        m_pSerializer->endElementNS( XML_w, XML_drawing );

        return true;
    }
    return false;
}

bool DocxAttributeOutput::WriteOLEMath( const SdrObject*, const SwOLENode& rOLENode, const Size& )
{
    uno::Reference < embed::XEmbeddedObject > xObj(const_cast<SwOLENode&>(rOLENode).GetOLEObj().GetOleRef());
    sal_Int64 nAspect = rOLENode.GetAspect();
    svt::EmbeddedObjectRef aObjRef( xObj, nAspect );
    SvGlobalName aObjName(aObjRef->getClassID());

    if( !SotExchange::IsMath(aObjName) )
        return false;
    assert( m_postponedMath == NULL ); // make it a list if there can be more inside one run
    m_postponedMath = &rOLENode;
    return true;
}

void DocxAttributeOutput::WritePostponedMath()
{
    if( m_postponedMath == NULL )
        return;
    uno::Reference < embed::XEmbeddedObject > xObj(const_cast<SwOLENode*>(m_postponedMath)->GetOLEObj().GetOleRef());
    uno::Reference< uno::XInterface > xInterface( xObj->getComponent(), uno::UNO_QUERY );
// gcc4.4 (and 4.3 and possibly older) have a problem with dynamic_cast directly to the target class,
// so help it with an intermediate cast. I'm not sure what exactly the problem is, seems to be unrelated
// to RTLD_GLOBAL, so most probably a gcc bug.
    oox::FormulaExportBase* formulaexport = dynamic_cast<oox::FormulaExportBase*>(dynamic_cast<SfxBaseModel*>(xInterface.get()));
    assert( formulaexport != NULL );
    formulaexport->writeFormulaOoxml( m_pSerializer, GetExport().GetFilter().getVersion());
    m_postponedMath = NULL;
}

void DocxAttributeOutput::OutputFlyFrame_Impl( const sw::Frame &rFrame, const Point& /*rNdTopLeft*/ )
{
    m_pSerializer->mark();

    switch ( rFrame.GetWriterType() )
    {
        case sw::Frame::eGraphic:
            {
                const SwNode *pNode = rFrame.GetContent();
                const SwGrfNode *pGrfNode = pNode ? pNode->GetGrfNode() : 0;
                if ( pGrfNode )
                {
                    if( m_postponedGraphic == NULL )
                        FlyFrameGraphic( pGrfNode, rFrame.GetLayoutSize() );
                    else // we are writting out attributes, but w:drawing should not be inside w:rPr,
                    {    // so write it out later
                        m_postponedGraphic->push_back( PostponedGraphic( pGrfNode, rFrame.GetLayoutSize()));
                    }
                }
            }
            break;
        case sw::Frame::eDrawing:
            {
                const SdrObject* pSdrObj = rFrame.GetFrmFmt().FindRealSdrObject();
                if ( pSdrObj )
                {
                    bool bSwapInPage = false;
                    if ( !pSdrObj->GetPage() )
                    {
                        if ( SdrModel* pModel = m_rExport.pDoc->GetDrawModel() )
                        {
                            if ( SdrPage *pPage = pModel->GetPage( 0 ) )
                            {
                                bSwapInPage = true;
                                const_cast< SdrObject* >( pSdrObj )->SetPage( pPage );
                            }
                        }
                    }

                    m_pSerializer->startElementNS( XML_w, XML_pict,
                            FSEND );

                    m_rExport.VMLExporter().AddSdrObject( *pSdrObj );

                    m_pSerializer->endElementNS( XML_w, XML_pict );

                    if ( bSwapInPage )
                        const_cast< SdrObject* >( pSdrObj )->SetPage( 0 );
                }
            }
            break;
        case sw::Frame::eTxtBox:
            {
                // The frame output is postponed to the end of the anchor paragraph
                m_pParentFrame = new sw::Frame(rFrame);
            }
            break;
        case sw::Frame::eOle:
            {
                const SwFrmFmt &rFrmFmt = rFrame.GetFrmFmt();
                const SdrObject *pSdrObj = rFrmFmt.FindRealSdrObject();
                if ( pSdrObj )
                {
                    SwNodeIndex aIdx(*rFrmFmt.GetCntnt().GetCntntIdx(), 1);
                    SwOLENode& rOLENd = *aIdx.GetNode().GetOLENode();
                    WriteOLE2Obj( pSdrObj, rOLENd, rFrame.GetLayoutSize(), dynamic_cast<const SwFlyFrmFmt*>( &rFrmFmt ));
                }
            }
            break;
        default:
            OSL_TRACE( "TODO DocxAttributeOutput::OutputFlyFrame_Impl( const sw::Frame& rFrame, const Point& rNdTopLeft ) - frame type '%s'\n",
                    rFrame.GetWriterType() == sw::Frame::eTxtBox? "eTxtBox":
                    ( rFrame.GetWriterType() == sw::Frame::eOle? "eOle":
                      ( rFrame.GetWriterType() == sw::Frame::eFormControl? "eFormControl": "???" ) ) );
            break;
    }

    m_pSerializer->mergeTopMarks( sax_fastparser::MERGE_MARKS_POSTPONE );
}

void DocxAttributeOutput::WriteOutliner(const OutlinerParaObject& rParaObj)
{
    const EditTextObject& rEditObj = rParaObj.GetTextObject();
    MSWord_SdrAttrIter aAttrIter( m_rExport, rEditObj, TXT_HFTXTBOX );

    sal_uInt16 nPara = rEditObj.GetParagraphCount();

    m_pSerializer->startElementNS( XML_w, XML_textbox, FSEND );
    m_pSerializer->startElementNS( XML_w, XML_txbxContent, FSEND );
    for (sal_uInt16 n = 0; n < nPara; ++n)
    {
        if( n )
            aAttrIter.NextPara( n );

        String aStr( rEditObj.GetText( n ));
        xub_StrLen nAktPos = 0;
        xub_StrLen nEnd = aStr.Len();

        m_pSerializer->startElementNS( XML_w, XML_p, FSEND );
        do {
            xub_StrLen nNextAttr = aAttrIter.WhereNext();
            if( nNextAttr > nEnd )
                nNextAttr = nEnd;

            m_pSerializer->startElementNS( XML_w, XML_r, FSEND );
            bool bTxtAtr = aAttrIter.IsTxtAttr( nAktPos );
            if( !bTxtAtr )
            {
                String aOut( aStr.Copy( nAktPos, nNextAttr - nAktPos ) );
                RunText(aOut);
            }

            m_pSerializer->endElementNS( XML_w, XML_r );

            nAktPos = nNextAttr;
            aAttrIter.NextPos();
        }
        while( nAktPos < nEnd );
        m_pSerializer->endElementNS( XML_w, XML_p );
    }
    m_pSerializer->endElementNS( XML_w, XML_txbxContent );
    m_pSerializer->endElementNS( XML_w, XML_textbox );
}

oox::drawingml::DrawingML& DocxAttributeOutput::GetDrawingML()
{
    return m_rDrawingML;
}

void DocxAttributeOutput::StartStyle( const String& rName, bool bPapFmt,
        sal_uInt16 nBase, sal_uInt16 nNext, sal_uInt16 /*nWwId*/, sal_uInt16 nId, bool bAutoUpdate )
{
    OString aStyle( "style" );

    m_pSerializer->startElementNS( XML_w, XML_style,
            FSNS( XML_w, XML_type ), bPapFmt? "paragraph": "character", // FIXME is this correct?
            FSNS( XML_w, XML_styleId ), ( aStyle + OString::valueOf( sal_Int32( nId ) ) ).getStr(),
            FSEND );

    m_pSerializer->singleElementNS( XML_w, XML_name,
            FSNS( XML_w, XML_val ), OUStringToOString( OUString( rName ), RTL_TEXTENCODING_UTF8 ).getStr(),
            FSEND );

    if ( nBase != 0x0FFF )
    {
        m_pSerializer->singleElementNS( XML_w, XML_basedOn,
                FSNS( XML_w, XML_val ), ( aStyle + OString::valueOf( sal_Int32( nBase ) ) ).getStr(),
                FSEND );
    }

    m_pSerializer->singleElementNS( XML_w, XML_next,
            FSNS( XML_w, XML_val ), ( aStyle + OString::valueOf( sal_Int32( nNext ) ) ).getStr(),
            FSEND );

    if ( bAutoUpdate )
        m_pSerializer->singleElementNS( XML_w, XML_autoRedefine, FSEND );
}

void DocxAttributeOutput::EndStyle()
{
    m_pSerializer->endElementNS( XML_w, XML_style );
}

void DocxAttributeOutput::StartStyleProperties( bool bParProp, sal_uInt16 /*nStyle*/ )
{
    if ( bParProp )
    {
        m_pSerializer->startElementNS( XML_w, XML_pPr, FSEND );
        InitCollectedParagraphProperties();
    }
    else
    {
        m_pSerializer->startElementNS( XML_w, XML_rPr, FSEND );
        InitCollectedRunProperties();
    }
}

void DocxAttributeOutput::EndStyleProperties( bool bParProp )
{
    if ( bParProp )
    {
        WriteCollectedParagraphProperties();
        m_pSerializer->endElementNS( XML_w, XML_pPr );
    }
    else
    {
        WriteCollectedRunProperties();
        m_pSerializer->endElementNS( XML_w, XML_rPr );
    }
}

void DocxAttributeOutput::OutlineNumbering( sal_uInt8 nLvl, const SwNumFmt& /*rNFmt*/, const SwFmt& /*rFmt*/ )
{
    if ( nLvl >= WW8ListManager::nMaxLevel )
        nLvl = WW8ListManager::nMaxLevel - 1;

    m_pSerializer->singleElementNS( XML_w, XML_outlineLvl,
            FSNS( XML_w, XML_val ), OString::valueOf( sal_Int32( nLvl ) ).getStr( ),
            FSEND );
}

void DocxAttributeOutput::PageBreakBefore( bool bBreak )
{
    if ( bBreak )
        m_pSerializer->singleElementNS( XML_w, XML_pageBreakBefore, FSEND );
    else
        m_pSerializer->singleElementNS( XML_w, XML_pageBreakBefore,
                FSNS( XML_w, XML_val ), "false",
                FSEND );
}

void DocxAttributeOutput::SectionBreak( sal_uInt8 nC, const WW8_SepInfo* pSectionInfo )
{
    switch ( nC )
    {
        case msword::ColumnBreak:
            // The column break should be output in the next paragraph...
            m_nColBreakStatus = COLBRK_POSTPONE;
            break;
        case msword::PageBreak:
            if ( pSectionInfo )
            {
                if ( !m_bParagraphOpened )
                {
                    // Create a dummy paragraph if needed
                    m_pSerializer->startElementNS( XML_w, XML_p, FSEND );
                    m_pSerializer->startElementNS( XML_w, XML_pPr, FSEND );

                    m_rExport.SectionProperties( *pSectionInfo );

                    m_pSerializer->endElementNS( XML_w, XML_pPr );
                    m_pSerializer->endElementNS( XML_w, XML_p );
                }
                else
                {
                    // postpone the output of this; it has to be done inside the
                    // paragraph properties, so remember it until then
                    m_pSectionInfo.reset( new WW8_SepInfo( *pSectionInfo ));
                }
            }
            else
            {
                m_pSerializer->startElementNS( XML_w, XML_r, FSEND );
                m_pSerializer->singleElementNS( XML_w, XML_br,
                        FSNS( XML_w, XML_type ), "page", FSEND );
                m_pSerializer->endElementNS( XML_w, XML_r );
            }
            break;
        default:
            OSL_TRACE( "Unknown section break to write: %d", nC );
            break;
    }
}

void DocxAttributeOutput::StartSection()
{
    m_pSerializer->startElementNS( XML_w, XML_sectPr, FSEND );
    m_bOpenedSectPr = true;

    // Write the elements in the spec order
    static const sal_Int32 aOrder[] =
    {
        FSNS( XML_w, XML_headerReference ),
        FSNS( XML_w, XML_footerReference ),
        FSNS( XML_w, XML_footnotePr ),
        FSNS( XML_w, XML_endnotePr ),
        FSNS( XML_w, XML_type ),
        FSNS( XML_w, XML_pgSz ),
        FSNS( XML_w, XML_pgMar ),
        FSNS( XML_w, XML_paperSrc ),
        FSNS( XML_w, XML_pgBorders ),
        FSNS( XML_w, XML_lnNumType ),
        FSNS( XML_w, XML_pgNumType ),
        FSNS( XML_w, XML_cols ),
        FSNS( XML_w, XML_formProt ),
        FSNS( XML_w, XML_vAlign ),
        FSNS( XML_w, XML_noEndnote ),
        FSNS( XML_w, XML_titlePg ),
        FSNS( XML_w, XML_textDirection ),
        FSNS( XML_w, XML_bidi ),
        FSNS( XML_w, XML_rtlGutter ),
        FSNS( XML_w, XML_docGrid ),
        FSNS( XML_w, XML_printerSettings ),
        FSNS( XML_w, XML_sectPrChange )
    };

    // postpone the output so that we can later [in EndParagraphProperties()]
    // prepend the properties before the run
    sal_Int32 len = sizeof ( aOrder ) / sizeof( sal_Int32 );
    uno::Sequence< sal_Int32 > aSeqOrder( len );
    for ( sal_Int32 i = 0; i < len; i++ )
        aSeqOrder[i] = aOrder[i];

    m_pSerializer->mark( aSeqOrder );
}

void DocxAttributeOutput::EndSection()
{
    // Write the section properties
    if ( m_pSectionSpacingAttrList )
    {
        XFastAttributeListRef xAttrList( m_pSectionSpacingAttrList );
        m_pSectionSpacingAttrList = NULL;

        m_pSerializer->singleElementNS( XML_w, XML_pgMar, xAttrList );
    }

    // Order the elements
    m_pSerializer->mergeTopMarks( );

    m_pSerializer->endElementNS( XML_w, XML_sectPr );
    m_bOpenedSectPr = false;
}

void DocxAttributeOutput::SectionFormProtection( bool bProtected )
{
    if ( bProtected )
        m_pSerializer->singleElementNS( XML_w, XML_formProt, FSEND );
    else
        m_pSerializer->singleElementNS( XML_w, XML_formProt,
                FSNS( XML_w, XML_val ), "false", FSEND );
}

void DocxAttributeOutput::SectionLineNumbering( sal_uLong nRestartNo, const SwLineNumberInfo& rLnNumInfo )
{
    FastAttributeList* pAttr = m_pSerializer->createAttrList();
    pAttr->add( FSNS( XML_w, XML_countBy ), OString::valueOf(static_cast<sal_Int32>(rLnNumInfo.GetCountBy())).getStr());
    pAttr->add( FSNS( XML_w, XML_restart ), rLnNumInfo.IsRestartEachPage() ? "newPage" : "continuous" );
    if( rLnNumInfo.GetPosFromLeft())
        pAttr->add( FSNS( XML_w, XML_distance ), OString::valueOf(static_cast<sal_Int32>(rLnNumInfo.GetPosFromLeft())).getStr());
    if( nRestartNo )
        pAttr->add( FSNS( XML_w, XML_start ), OString::valueOf( long( nRestartNo )).getStr());
    XFastAttributeListRef xAttrs( pAttr );
    m_pSerializer->singleElementNS( XML_w, XML_lnNumType, xAttrs );
}

void DocxAttributeOutput::SectionTitlePage()
{
    m_pSerializer->singleElementNS( XML_w, XML_titlePg, FSEND );
}

void DocxAttributeOutput::SectionPageBorders( const SwFrmFmt* pFmt, const SwFrmFmt* /*pFirstPageFmt*/ )
{
    // Output the margins

    const SvxBoxItem& rBox = pFmt->GetBox( );

    const SvxBorderLine* pBottom = rBox.GetBottom( );
    const SvxBorderLine* pTop = rBox.GetTop( );
    const SvxBorderLine* pLeft = rBox.GetLeft( );
    const SvxBorderLine* pRight = rBox.GetRight( );

    if ( pBottom || pTop || pLeft || pRight )
    {
        // All distances are relative to the text margins
        m_pSerializer->startElementNS( XML_w, XML_pgBorders,
               FSNS( XML_w, XML_display ), "allPages",
               FSNS( XML_w, XML_offsetFrom ), "text",
               FSEND );

        m_pSerializer->mark();

        m_pSerializer->endElementNS( XML_w, XML_pgBorders );
        m_pSerializer->mark();
    }
}

void DocxAttributeOutput::SectionBiDi( bool bBiDi )
{
    if ( bBiDi )
        m_pSerializer->singleElementNS( XML_w, XML_bidi, FSEND );
}

static OString impl_NumberingType( sal_uInt16 nNumberingType )
{
    OString aType;

    switch ( nNumberingType )
    {
        case SVX_NUM_CHARS_UPPER_LETTER:
        case SVX_NUM_CHARS_UPPER_LETTER_N:  aType = "upperLetter"; break;
        case SVX_NUM_CHARS_LOWER_LETTER:
        case SVX_NUM_CHARS_LOWER_LETTER_N:  aType = "lowerLetter"; break;
        case SVX_NUM_ROMAN_UPPER:           aType = "upperRoman";  break;
        case SVX_NUM_ROMAN_LOWER:           aType = "lowerRoman";  break;

        case SVX_NUM_ARABIC:                aType = "decimal";     break;

        case SVX_NUM_BITMAP:
        case SVX_NUM_CHAR_SPECIAL:          aType = "bullet";      break;

        default:                            aType = "none";        break;
    }

    return aType;
}

void DocxAttributeOutput::SectionPageNumbering( sal_uInt16 nNumType, sal_uInt16 nPageRestartNumber )
{
    // FIXME Not called properly with page styles like "First Page"

    FastAttributeList* pAttr = m_pSerializer->createAttrList();

    // 0 means no restart: then don't output that attribute if 0
    if ( nPageRestartNumber > 0 )
       pAttr->add( FSNS( XML_w, XML_start ), OString::valueOf( sal_Int32( nPageRestartNumber ) ) );

    // nNumType corresponds to w:fmt. See WW8Export::GetNumId() for more precisions
    OString aFmt( impl_NumberingType( nNumType ) );
    if ( !aFmt.isEmpty() )
        pAttr->add( FSNS( XML_w, XML_fmt ), aFmt.getStr() );

    XFastAttributeListRef xAttrs( pAttr );
    m_pSerializer->singleElementNS( XML_w, XML_pgNumType, xAttrs );

    // see 2.6.12 pgNumType (Page Numbering Settings)
    OSL_TRACE( "TODO DocxAttributeOutput::SectionPageNumbering()" );
}

void DocxAttributeOutput::SectionType( sal_uInt8 nBreakCode )
{
    /*  break code:   0 No break, 1 New column
        2 New page, 3 Even page, 4 Odd page
        */
    const char* pType = NULL;
    switch ( nBreakCode )
    {
        case 1:  pType = "nextColumn"; break;
        case 2:  pType = "nextPage";   break;
        case 3:  pType = "evenPage";   break;
        case 4:  pType = "oddPage";    break;
        default: pType = "continuous"; break;
    }

    if ( pType )
        m_pSerializer->singleElementNS( XML_w, XML_type,
                FSNS( XML_w, XML_val ), pType,
                FSEND );
}

void DocxAttributeOutput::StartFont( const String& rFamilyName ) const
{
    m_pSerializer->startElementNS( XML_w, XML_font,
            FSNS( XML_w, XML_name ), OUStringToOString( OUString( rFamilyName ), RTL_TEXTENCODING_UTF8 ).getStr(),
            FSEND );
}

void DocxAttributeOutput::EndFont() const
{
    m_pSerializer->endElementNS( XML_w, XML_font );
}

void DocxAttributeOutput::FontAlternateName( const String& rName ) const
{
    m_pSerializer->singleElementNS( XML_w, XML_altName,
            FSNS( XML_w, XML_val ), OUStringToOString( OUString( rName ), RTL_TEXTENCODING_UTF8 ).getStr(),
            FSEND );
}

void DocxAttributeOutput::FontCharset( sal_uInt8 nCharSet, rtl_TextEncoding nEncoding ) const
{
    FastAttributeList* pAttr = m_pSerializer->createAttrList();

    OString aCharSet( OString::valueOf( sal_Int32( nCharSet ), 16 ) );
    if ( aCharSet.getLength() == 1 )
        aCharSet = OString( "0" ) + aCharSet;
    pAttr->add( FSNS( XML_w, XML_val ), aCharSet.getStr());

    if( GetExport().GetFilter().getVersion( ) != oox::core::ECMA_DIALECT )
    {
        if( const char* charset = rtl_getMimeCharsetFromTextEncoding( nEncoding ))
            pAttr->add( FSNS( XML_w, XML_characterSet ), charset );
    }

    m_pSerializer->singleElementNS( XML_w, XML_charset, XFastAttributeListRef( pAttr ));
}

void DocxAttributeOutput::FontFamilyType( FontFamily eFamily ) const
{
    const char *pFamily = NULL;
    switch ( eFamily )
    {
        case FAMILY_ROMAN:      pFamily = "roman"; break;
        case FAMILY_SWISS:      pFamily = "swiss"; break;
        case FAMILY_MODERN:     pFamily = "modern"; break;
        case FAMILY_SCRIPT:     pFamily = "script"; break;
        case FAMILY_DECORATIVE: pFamily = "decorative"; break;
        default:                pFamily = "auto"; break; // no font family
    }

    if ( pFamily )
        m_pSerializer->singleElementNS( XML_w, XML_family,
                FSNS( XML_w, XML_val ), pFamily,
                FSEND );
}

void DocxAttributeOutput::FontPitchType( FontPitch ePitch ) const
{
    const char *pPitch = NULL;
    switch ( ePitch )
    {
        case PITCH_VARIABLE: pPitch = "variable"; break;
        case PITCH_FIXED:    pPitch = "fixed"; break;
        default:             pPitch = "default"; break; // no info about the pitch
    }

    if ( pPitch )
        m_pSerializer->singleElementNS( XML_w, XML_pitch,
                FSNS( XML_w, XML_val ), pPitch,
                FSEND );
}

void DocxAttributeOutput::NumberingDefinition( sal_uInt16 nId, const SwNumRule &rRule )
{
    // nId is the same both for abstract numbering definition as well as the
    // numbering definition itself
    // TODO check that this is actually true & fix if not ;-)
    OString aId( OString::valueOf( sal_Int32( nId ) ) );

    m_pSerializer->startElementNS( XML_w, XML_num,
            FSNS( XML_w, XML_numId ), aId.getStr(),
            FSEND );

    m_pSerializer->singleElementNS( XML_w, XML_abstractNumId,
            FSNS( XML_w, XML_val ), aId.getStr(),
            FSEND );

#if OSL_DEBUG_LEVEL > 1
    // TODO ww8 version writes this, anything to do about it here?
    if ( rRule.IsContinusNum() )
        OSL_TRACE( "TODO DocxAttributeOutput::NumberingDefinition()" );
#else
    (void) rRule; // to quiet the warning...
#endif

    m_pSerializer->endElementNS( XML_w, XML_num );
}

void DocxAttributeOutput::StartAbstractNumbering( sal_uInt16 nId )
{
    m_pSerializer->startElementNS( XML_w, XML_abstractNum,
            FSNS( XML_w, XML_abstractNumId ), OString::valueOf( sal_Int32( nId ) ).getStr(),
            FSEND );
}

void DocxAttributeOutput::EndAbstractNumbering()
{
    m_pSerializer->endElementNS( XML_w, XML_abstractNum );
}

void DocxAttributeOutput::NumberingLevel( sal_uInt8 nLevel,
        sal_uInt16 nStart,
        sal_uInt16 nNumberingType,
        SvxAdjust eAdjust,
        const sal_uInt8 * /*pNumLvlPos*/,
        sal_uInt8 nFollow,
        const wwFont *pFont,
        const SfxItemSet *pOutSet,
        sal_Int16 nIndentAt,
        sal_Int16 nFirstLineIndex,
        sal_Int16 nListTabPos,
        const String &rNumberingString )
{
    m_pSerializer->startElementNS( XML_w, XML_lvl,
            FSNS( XML_w, XML_ilvl ), OString::valueOf( sal_Int32( nLevel ) ).getStr(),
            FSEND );

    // start with the nStart value
    m_pSerializer->singleElementNS( XML_w, XML_start,
            FSNS( XML_w, XML_val ), OString::valueOf( sal_Int32( nStart ) ).getStr(),
            FSEND );

    // format
    OString aFmt( impl_NumberingType( nNumberingType ) );

    if ( !aFmt.isEmpty() )
        m_pSerializer->singleElementNS( XML_w, XML_numFmt,
                FSNS( XML_w, XML_val ), aFmt.getStr(),
                FSEND );

    // suffix
    const char *pSuffix = NULL;
    switch ( nFollow )
    {
        case 1:  pSuffix = "space";   break;
        case 2:  pSuffix = "nothing"; break;
        default: /*pSuffix = "tab";*/ break;
    }
    if ( pSuffix )
        m_pSerializer->singleElementNS( XML_w, XML_suff,
                FSNS( XML_w, XML_val ), pSuffix,
                FSEND );

    // text
    OUString aText( rNumberingString );
    OUStringBuffer aBuffer( aText.getLength() + WW8ListManager::nMaxLevel );

    const sal_Unicode *pPrev = aText.getStr();
    const sal_Unicode *pIt = aText.getStr();
    while ( pIt < aText.getStr() + aText.getLength() )
    {
        // convert the level values to %NUMBER form
        // (we don't use pNumLvlPos at all)
        // FIXME so far we support the ww8 limit of levels only
        if ( *pIt < sal_Unicode( WW8ListManager::nMaxLevel ) )
        {
            aBuffer.append( pPrev, pIt - pPrev );
            aBuffer.append( '%' );
            aBuffer.append( OUString::valueOf( sal_Int32( *pIt ) + 1 ) );

            pPrev = pIt + 1;
        }
        ++pIt;
    }
    if ( pPrev < pIt )
        aBuffer.append( pPrev, pIt - pPrev );

    m_pSerializer->singleElementNS( XML_w, XML_lvlText,
            FSNS( XML_w, XML_val ), OUStringToOString( aBuffer.makeStringAndClear(), RTL_TEXTENCODING_UTF8 ).getStr(),
            FSEND );

    // justification
    const char *pJc;
    bool ecmaDialect = ( m_rExport.GetFilter().getVersion() == oox::core::ECMA_DIALECT );
    switch ( eAdjust )
    {
        case SVX_ADJUST_CENTER: pJc = "center"; break;
        case SVX_ADJUST_RIGHT:  pJc = !ecmaDialect ? "end" : "right";  break;
        default:                pJc = !ecmaDialect ? "start" : "left";   break;
    }
    m_pSerializer->singleElementNS( XML_w, XML_lvlJc,
            FSNS( XML_w, XML_val ), pJc,
            FSEND );

    // indentation
    m_pSerializer->startElementNS( XML_w, XML_pPr, FSEND );
    if( nListTabPos != 0 )
    {
        m_pSerializer->startElementNS( XML_w, XML_tabs, FSEND );
        m_pSerializer->singleElementNS( XML_w, XML_tab,
                FSNS( XML_w, XML_val ), "num",
                FSNS( XML_w, XML_pos ), OString::valueOf( static_cast<sal_Int32>(nListTabPos) ).getStr(),
                FSEND );
        m_pSerializer->endElementNS( XML_w, XML_tabs );
    }

    sal_Int32 nToken = ecmaDialect ? XML_left : XML_start;
    m_pSerializer->singleElementNS( XML_w, XML_ind,
            FSNS( XML_w, nToken ), OString::valueOf( sal_Int32( nIndentAt ) ).getStr(),
            FSNS( XML_w, XML_hanging ), OString::valueOf( sal_Int32( -nFirstLineIndex ) ).getStr(),
            FSEND );
    m_pSerializer->endElementNS( XML_w, XML_pPr );

    // font
    if ( pOutSet )
    {
        m_pSerializer->startElementNS( XML_w, XML_rPr, FSEND );

        if ( pFont )
        {
            GetExport().GetId( *pFont ); // ensure font info is written to fontTable.xml
            OString aFamilyName( OUStringToOString( OUString( pFont->GetFamilyName() ), RTL_TEXTENCODING_UTF8 ) );
            m_pSerializer->singleElementNS( XML_w, XML_rFonts,
                    FSNS( XML_w, XML_ascii ), aFamilyName.getStr(),
                    FSNS( XML_w, XML_hAnsi ), aFamilyName.getStr(),
                    FSNS( XML_w, XML_cs ), aFamilyName.getStr(),
                    FSNS( XML_w, XML_hint ), "default",
                    FSEND );
        }
        m_rExport.OutputItemSet( *pOutSet, false, true, i18n::ScriptType::LATIN, m_rExport.mbExportModeRTF );

        m_pSerializer->endElementNS( XML_w, XML_rPr );
    }

    // TODO anything to do about nListTabPos?

    m_pSerializer->endElementNS( XML_w, XML_lvl );
}

void DocxAttributeOutput::CharCaseMap( const SvxCaseMapItem& rCaseMap )
{
    switch ( rCaseMap.GetValue() )
    {
        case SVX_CASEMAP_KAPITAELCHEN:
            m_pSerializer->singleElementNS( XML_w, XML_smallCaps, FSEND );
            break;
        case SVX_CASEMAP_VERSALIEN:
            m_pSerializer->singleElementNS( XML_w, XML_caps, FSEND );
            break;
        default: // Something that ooxml does not support
            m_pSerializer->singleElementNS( XML_w, XML_smallCaps, FSNS( XML_w, XML_val ), "false", FSEND );
            m_pSerializer->singleElementNS( XML_w, XML_caps, FSNS( XML_w, XML_val ), "false", FSEND );
            break;
    }
}

void DocxAttributeOutput::CharColor( const SvxColorItem& rColor )
{
    const Color aColor( rColor.GetValue() );
    OString aColorString;

    aColorString = impl_ConvertColor( aColor );

    m_pSerializer->singleElementNS( XML_w, XML_color,
            FSNS( XML_w, XML_val ), aColorString.getStr(), FSEND );
}

void DocxAttributeOutput::CharContour( const SvxContourItem& rContour )
{
    if ( rContour.GetValue() )
        m_pSerializer->singleElementNS( XML_w, XML_outline, FSEND );
    else
        m_pSerializer->singleElementNS( XML_w, XML_outline, FSNS( XML_w, XML_val ), "false", FSEND );
}

void DocxAttributeOutput::CharCrossedOut( const SvxCrossedOutItem& rCrossedOut )
{
    switch ( rCrossedOut.GetStrikeout() )
    {
        case STRIKEOUT_DOUBLE:
            m_pSerializer->singleElementNS( XML_w, XML_dstrike, FSEND );
            break;
        case STRIKEOUT_NONE:
            m_pSerializer->singleElementNS( XML_w, XML_dstrike, FSNS( XML_w, XML_val ), "false", FSEND );
            m_pSerializer->singleElementNS( XML_w, XML_strike, FSNS( XML_w, XML_val ), "false", FSEND );
            break;
        default:
            m_pSerializer->singleElementNS( XML_w, XML_strike, FSEND );
            break;
    }
}

void DocxAttributeOutput::CharEscapement( const SvxEscapementItem& rEscapement )
{
    OString sIss;
    short nEsc = rEscapement.GetEsc(), nProp = rEscapement.GetProp();
    if ( !nEsc )
    {
        sIss = OString( "baseline" );
        nEsc = 0;
        nProp = 100;
    }
    else if ( DFLT_ESC_PROP == nProp )
    {
        if ( DFLT_ESC_SUB == nEsc || DFLT_ESC_AUTO_SUB == nEsc )
            sIss = OString( "subscript" );
        else if ( DFLT_ESC_SUPER == nEsc || DFLT_ESC_AUTO_SUPER == nEsc )
            sIss = OString( "superscript" );
    }

    if ( !sIss.isEmpty() )
        m_pSerializer->singleElementNS( XML_w, XML_vertAlign,
           FSNS( XML_w, XML_val ), sIss.getStr(), FSEND );

    if ( sIss.isEmpty() || sIss.match( OString( "baseline" ) ) )
    {
        long nHeight = ((SvxFontHeightItem&)m_rExport.GetItem(
                                    RES_CHRATR_FONTSIZE )).GetHeight();
        OString sPos = OString::valueOf( ( nHeight * nEsc + 500 ) / 1000 );
        m_pSerializer->singleElementNS( XML_w, XML_position,
                FSNS( XML_w, XML_val ), sPos.getStr( ), FSEND );

        if( 100 != nProp || sIss.match( OString( "baseline" ) ) )
        {
            OString sSize = OString::valueOf( ( nHeight * nProp + 500 ) / 1000 );
                m_pSerializer->singleElementNS( XML_w, XML_sz,
                    FSNS( XML_w, XML_val ), sSize.getStr( ), FSEND );
        }
    }
}

void DocxAttributeOutput::CharFont( const SvxFontItem& rFont)
{
    if (!m_pFontsAttrList)
        m_pFontsAttrList = m_pSerializer->createAttrList();
    OUString sFontName(rFont.GetFamilyName());
    OString sFontNameUtf8 = OUStringToOString(sFontName, RTL_TEXTENCODING_UTF8);
    m_pFontsAttrList->add(FSNS(XML_w, XML_ascii), sFontNameUtf8);
    m_pFontsAttrList->add(FSNS(XML_w, XML_hAnsi), sFontNameUtf8);
}

void DocxAttributeOutput::CharFontSize( const SvxFontHeightItem& rFontSize)
{
    OString fontSize = OString::valueOf( sal_Int32( ( rFontSize.GetHeight() + 5 ) / 10 ) );

    switch ( rFontSize.Which() )
    {
        case RES_CHRATR_FONTSIZE:
        case RES_CHRATR_CJK_FONTSIZE:
            m_pSerializer->singleElementNS( XML_w, XML_sz, FSNS( XML_w, XML_val ), fontSize.getStr(), FSEND );
            break;
        case RES_CHRATR_CTL_FONTSIZE:
            m_pSerializer->singleElementNS( XML_w, XML_szCs, FSNS( XML_w, XML_val ), fontSize.getStr(), FSEND );
            break;
    }
}

void DocxAttributeOutput::CharKerning( const SvxKerningItem& rKerning )
{
    OString aKerning = OString::valueOf( ( sal_Int32 ) rKerning.GetValue() );
    m_pSerializer->singleElementNS( XML_w, XML_spacing, FSNS(XML_w, XML_val), aKerning.getStr(), FSEND );
}

void DocxAttributeOutput::CharLanguage( const SvxLanguageItem& rLanguage )
{
    if (!m_pCharLangAttrList)
        m_pCharLangAttrList = m_pSerializer->createAttrList();

    ::com::sun::star::lang::Locale xLocale= MsLangId::convertLanguageToLocale( rLanguage.GetLanguage( ) );
    OString sLanguage = OUStringToOString(xLocale.Language, RTL_TEXTENCODING_UTF8);
    OString sCountry = OUStringToOString(xLocale.Country, RTL_TEXTENCODING_UTF8);

    OString aLanguageCode = sLanguage + "-" + sCountry;

    switch ( rLanguage.Which() )
    {
        case RES_CHRATR_LANGUAGE:
            m_pCharLangAttrList->add(FSNS(XML_w, XML_val), aLanguageCode);
            break;
        case RES_CHRATR_CJK_LANGUAGE:
            m_pCharLangAttrList->add(FSNS(XML_w, XML_eastAsia), aLanguageCode);
            break;
        case RES_CHRATR_CTL_LANGUAGE:
            m_pCharLangAttrList->add(FSNS(XML_w, XML_bidi), aLanguageCode);
            break;
    }
}

void DocxAttributeOutput::CharPosture( const SvxPostureItem& rPosture )
{
    if ( rPosture.GetPosture() != ITALIC_NONE )
        m_pSerializer->singleElementNS( XML_w, XML_i, FSEND );
    else
        m_pSerializer->singleElementNS( XML_w, XML_i, FSNS( XML_w, XML_val ), "false", FSEND );
}

void DocxAttributeOutput::CharShadow( const SvxShadowedItem& rShadow )
{
    if ( rShadow.GetValue() )
        m_pSerializer->singleElementNS( XML_w, XML_shadow, FSEND );
    else
        m_pSerializer->singleElementNS( XML_w, XML_shadow, FSNS( XML_w, XML_val ), "false", FSEND );
}

void DocxAttributeOutput::CharUnderline( const SvxUnderlineItem& rUnderline )
{
    const char *pUnderline;

    switch ( rUnderline.GetLineStyle() )
    {
        case UNDERLINE_SINGLE:         pUnderline = "single";          break;
        case UNDERLINE_BOLD:           pUnderline = "thick";           break;
        case UNDERLINE_DOUBLE:         pUnderline = "double";          break;
        case UNDERLINE_DOTTED:         pUnderline = "dotted";          break;
        case UNDERLINE_DASH:           pUnderline = "dash";            break;
        case UNDERLINE_DASHDOT:        pUnderline = "dotDash";         break;
        case UNDERLINE_DASHDOTDOT:     pUnderline = "dotDotDash";      break;
        case UNDERLINE_WAVE:           pUnderline = "wave";            break;
        case UNDERLINE_BOLDDOTTED:     pUnderline = "dottedHeavy";     break;
        case UNDERLINE_BOLDDASH:       pUnderline = "dashedHeavy";     break;
        case UNDERLINE_LONGDASH:       pUnderline = "dashLongHeavy";   break;
        case UNDERLINE_BOLDLONGDASH:   pUnderline = "dashLongHeavy";   break;
        case UNDERLINE_BOLDDASHDOT:    pUnderline = "dashDotHeavy";    break;
        case UNDERLINE_BOLDDASHDOTDOT: pUnderline = "dashDotDotHeavy"; break;
        case UNDERLINE_BOLDWAVE:       pUnderline = "wavyHeavy";       break;
        case UNDERLINE_DOUBLEWAVE:     pUnderline = "wavyDouble";      break;
        case UNDERLINE_NONE:           // fall through
        default:                       pUnderline = "none";            break;
    }

    m_pSerializer->singleElementNS( XML_w, XML_u, FSNS( XML_w, XML_val ), pUnderline, FSEND );
}

void DocxAttributeOutput::CharWeight( const SvxWeightItem& rWeight )
{
    if ( rWeight.GetWeight() == WEIGHT_BOLD )
        m_pSerializer->singleElementNS( XML_w, XML_b, FSEND );
    else
        m_pSerializer->singleElementNS( XML_w, XML_b, FSNS( XML_w, XML_val ), "false", FSEND );
}

void DocxAttributeOutput::CharAutoKern( const SvxAutoKernItem& )
{
    OSL_TRACE( "TODO DocxAttributeOutput::CharAutoKern()" );
}

void DocxAttributeOutput::CharAnimatedText( const SvxBlinkItem& rBlink )
{
    if ( rBlink.GetValue() )
        m_pSerializer->singleElementNS(XML_w, XML_effect, FSNS( XML_w, XML_val ), "blinkBackground", FSEND );
    else
        m_pSerializer->singleElementNS(XML_w, XML_effect, FSNS( XML_w, XML_val ), "none", FSEND );
}

void DocxAttributeOutput::CharBackground( const SvxBrushItem& rBrush )
{
    m_pSerializer->singleElementNS( XML_w, XML_shd,
            FSNS( XML_w, XML_fill ), impl_ConvertColor( rBrush.GetColor() ).getStr(),
            FSNS( XML_w, XML_val ), "clear",
            FSEND );
}

void DocxAttributeOutput::CharFontCJK( const SvxFontItem& rFont )
{
    if (!m_pFontsAttrList)
        m_pFontsAttrList = m_pSerializer->createAttrList();
    OUString sFontName(rFont.GetFamilyName());
    OString sFontNameUtf8 = OUStringToOString(sFontName, RTL_TEXTENCODING_UTF8);
    m_pFontsAttrList->add(FSNS(XML_w, XML_eastAsia), sFontNameUtf8);
}

void DocxAttributeOutput::CharPostureCJK( const SvxPostureItem& rPosture )
{
    if ( rPosture.GetPosture() != ITALIC_NONE )
        m_pSerializer->singleElementNS( XML_w, XML_i, FSEND );
    else
        m_pSerializer->singleElementNS( XML_w, XML_i, FSNS( XML_w, XML_val ), "false", FSEND );
}

void DocxAttributeOutput::CharWeightCJK( const SvxWeightItem& rWeight )
{
    if ( rWeight.GetWeight() == WEIGHT_BOLD )
        m_pSerializer->singleElementNS( XML_w, XML_b, FSEND );
    else
        m_pSerializer->singleElementNS( XML_w, XML_b, FSNS( XML_w, XML_val ), "false", FSEND );
}

void DocxAttributeOutput::CharFontCTL( const SvxFontItem& rFont )
{
    if (!m_pFontsAttrList)
        m_pFontsAttrList = m_pSerializer->createAttrList();
    OUString sFontName(rFont.GetFamilyName());
    OString sFontNameUtf8 = OUStringToOString(sFontName, RTL_TEXTENCODING_UTF8);
    m_pFontsAttrList->add(FSNS(XML_w, XML_cs), sFontNameUtf8);

}

void DocxAttributeOutput::CharPostureCTL( const SvxPostureItem& rPosture)
{
    if ( rPosture.GetPosture() != ITALIC_NONE )
        m_pSerializer->singleElementNS( XML_w, XML_iCs, FSEND );
    else
        m_pSerializer->singleElementNS( XML_w, XML_iCs, FSNS( XML_w, XML_val ), "false", FSEND );
}

void DocxAttributeOutput::CharWeightCTL( const SvxWeightItem& rWeight )
{
    if ( rWeight.GetWeight() == WEIGHT_BOLD )
        m_pSerializer->singleElementNS( XML_w, XML_bCs, FSEND );
    else
        m_pSerializer->singleElementNS( XML_w, XML_bCs, FSNS( XML_w, XML_val ), "false", FSEND );
}

void DocxAttributeOutput::CharRotate( const SvxCharRotateItem& rRotate)
{
    if ( !rRotate.GetValue() )
        return;

    if (!m_pEastAsianLayoutAttrList)
        m_pEastAsianLayoutAttrList = m_pSerializer->createAttrList();
    OString sTrue((sal_Char *)"true");
    m_pEastAsianLayoutAttrList->add(FSNS(XML_w, XML_vert), sTrue);

    if (rRotate.IsFitToLine())
        m_pEastAsianLayoutAttrList->add(FSNS(XML_w, XML_vertCompress), sTrue);
}

void DocxAttributeOutput::CharEmphasisMark( const SvxEmphasisMarkItem& rEmphasisMark )
{
    const char *pEmphasis;

    switch ( rEmphasisMark.GetValue() )
    {
        case EMPHASISMARK_NONE:         pEmphasis = "none";     break;
        case EMPHASISMARK_SIDE_DOTS:    pEmphasis = "dot";      break;
        case EMPHASISMARK_CIRCLE_ABOVE: pEmphasis = "circle";   break;
        case EMPHASISMARK_DOTS_BELOW:   pEmphasis = "underDot"; break;
        default:                        pEmphasis = "comma";    break;
    }

    m_pSerializer->singleElementNS( XML_w, XML_em, FSNS( XML_w, XML_val ), pEmphasis, FSEND );
}

void DocxAttributeOutput::CharTwoLines( const SvxTwoLinesItem& rTwoLines )
{
    if ( !rTwoLines.GetValue() )
        return;

    if (!m_pEastAsianLayoutAttrList)
        m_pEastAsianLayoutAttrList = m_pSerializer->createAttrList();
    OString sTrue((sal_Char *)"true");
    m_pEastAsianLayoutAttrList->add(FSNS(XML_w, XML_combine), sTrue);

    sal_Unicode cStart = rTwoLines.GetStartBracket();
    sal_Unicode cEnd = rTwoLines.GetEndBracket();

    if (!cStart && !cEnd)
        return;

    OString sBracket;
    if ((cStart == '{') || (cEnd == '}'))
        sBracket = (sal_Char *)"curly";
    else if ((cStart == '<') || (cEnd == '>'))
        sBracket = (sal_Char *)"angle";
    else if ((cStart == '[') || (cEnd == ']'))
        sBracket = (sal_Char *)"square";
    else
        sBracket = (sal_Char *)"round";
    m_pEastAsianLayoutAttrList->add(FSNS(XML_w, XML_combineBrackets), sBracket);
}

void DocxAttributeOutput::CharScaleWidth( const SvxCharScaleWidthItem& rScaleWidth )
{
    m_pSerializer->singleElementNS( XML_w, XML_w,
            FSNS( XML_w, XML_val ), rtl::OString::valueOf( sal_Int32( rScaleWidth.GetValue() ) ).getStr(), FSEND );
}

void DocxAttributeOutput::CharRelief( const SvxCharReliefItem& rRelief )
{
    switch ( rRelief.GetValue() )
    {
        case RELIEF_EMBOSSED:
            m_pSerializer->singleElementNS( XML_w, XML_emboss, FSEND );
            break;
        case RELIEF_ENGRAVED:
            m_pSerializer->singleElementNS( XML_w, XML_imprint, FSEND );
            break;
        default:
            m_pSerializer->singleElementNS( XML_w, XML_emboss, FSNS( XML_w, XML_val ), "false", FSEND );
            m_pSerializer->singleElementNS( XML_w, XML_imprint, FSNS( XML_w, XML_val ), "false", FSEND );
            break;
    }
}

void DocxAttributeOutput::CharHidden( const SvxCharHiddenItem& rHidden )
{
    if ( rHidden.GetValue() )
        m_pSerializer->singleElementNS( XML_w, XML_vanish, FSEND );
    else
        m_pSerializer->singleElementNS( XML_w, XML_vanish, FSNS( XML_w, XML_val ), "false", FSEND );
}

void DocxAttributeOutput::TextINetFormat( const SwFmtINetFmt& rLink )
{
    const SwTxtINetFmt* pINetFmt = rLink.GetTxtINetFmt();
    const SwCharFmt* pCharFmt = pINetFmt->GetCharFmt();

    OString aStyleId( "style" );
    aStyleId += OString::valueOf( sal_Int32( m_rExport.GetId( *pCharFmt ) ) );

    m_pSerializer->singleElementNS( XML_w, XML_rStyle, FSNS( XML_w, XML_val ), aStyleId.getStr(), FSEND );
}

void DocxAttributeOutput::TextCharFormat( const SwFmtCharFmt& rCharFmt )
{
    OString aStyleId( "style" );
    aStyleId += OString::valueOf( sal_Int32( m_rExport.GetId( *rCharFmt.GetCharFmt() ) ) );

    m_pSerializer->singleElementNS( XML_w, XML_rStyle, FSNS( XML_w, XML_val ), aStyleId.getStr(), FSEND );
}

void DocxAttributeOutput::RefField( const SwField&  rFld, const String& rRef )
{
    sal_uInt16 nType = rFld.GetTyp( )->Which( );
    if ( nType == RES_GETEXPFLD )
    {
        String sCmd = FieldString( ww::eREF );
        sCmd.APPEND_CONST_ASC( "\"" );
        sCmd += rRef;
        sCmd.APPEND_CONST_ASC( "\" " );

        m_rExport.OutputField( &rFld, ww::eREF, sCmd );
    }

    // There is nothing to do here for the set fields
}

void DocxAttributeOutput::HiddenField( const SwField& /*rFld*/ )
{
    OSL_TRACE( "TODO DocxAttributeOutput::HiddenField()" );
}

void DocxAttributeOutput::PostitField( const SwField* pFld )
{
    assert( dynamic_cast< const SwPostItField* >( pFld ));
    m_postitFields.push_back( static_cast< const SwPostItField* >( pFld ));
}

void DocxAttributeOutput::WritePostitFieldReference()
{
    while( m_postitFieldsMaxId < m_postitFields.size())
    {
        OString idstr = OString::valueOf( sal_Int32( m_postitFieldsMaxId ));
        m_pSerializer->singleElementNS( XML_w, XML_commentReference, FSNS( XML_w, XML_id ), idstr.getStr(), FSEND );
        ++m_postitFieldsMaxId;
    }
}

void DocxAttributeOutput::WritePostitFields()
{
    for( unsigned int i = 0;
         i < m_postitFields.size();
         ++i )
    {
        OString idstr = OString::valueOf( sal_Int32( i ));
        const SwPostItField* f = m_postitFields[ i ];
        m_pSerializer->startElementNS( XML_w, XML_comment, FSNS( XML_w, XML_id ), idstr.getStr(),
            FSNS( XML_w, XML_author ), rtl::OUStringToOString( f->GetPar1(), RTL_TEXTENCODING_UTF8 ).getStr(),
            FSNS( XML_w, XML_date ), impl_DateTimeToOString(f->GetDateTime()).getStr(), FSEND );
        // Check for the text object existing, it seems that it can be NULL when saving a newly created
        // comment without giving focus back to the main document. As GetTxt() is empty in that case as well,
        // that is probably a bug in the Writer core.
        if( f->GetTextObject() != NULL )
            GetExport().WriteOutliner( *f->GetTextObject(), TXT_ATN );
        m_pSerializer->endElementNS( XML_w, XML_comment );
    }
}

bool DocxAttributeOutput::DropdownField( const SwField* pFld )
{
    bool bExpand = false;

    ww::eField eType = ww::eFORMDROPDOWN;
    String sCmd = FieldString( eType  );
    GetExport( ).OutputField( pFld, eType, sCmd );

    return bExpand;
}

void DocxAttributeOutput::SetField( const SwField& rFld, ww::eField eType, const String& rCmd )
{
    // field bookmarks are handled in the EndRun method
    GetExport().OutputField(&rFld, eType, rCmd );
}

void DocxAttributeOutput::WriteExpand( const SwField* pFld )
{
    // Will be written in the next End Run
    String sCmd;
    m_rExport.OutputField( pFld, ww::eUNKNOWN, sCmd );
}

void DocxAttributeOutput::WriteField_Impl( const SwField* pFld, ww::eField eType, const String& rFldCmd, sal_uInt8 nMode )
{
    struct FieldInfos infos;
    infos.pField = pFld;
    infos.sCmd = rFldCmd;
    infos.eType = eType;
    infos.bClose = WRITEFIELD_CLOSE & nMode;
    infos.bOpen = WRITEFIELD_START & nMode;
    m_Fields.push_back( infos );

    if ( pFld )
    {
        sal_uInt16 nType = pFld->GetTyp( )->Which( );
        sal_uInt16 nSubType = pFld->GetSubType();

        // TODO Any other field types here ?
        if ( ( nType == RES_SETEXPFLD ) && ( nSubType & nsSwGetSetExpType::GSE_STRING ) )
        {
            const SwSetExpField *pSet = ( const SwSetExpField* )( pFld );
            m_sFieldBkm = pSet->GetPar1( );
        }
        else if ( nType == RES_DROPDOWN )
        {
            const SwDropDownField* pDropDown = ( const SwDropDownField* )( pFld );
            m_sFieldBkm = pDropDown->GetName( );
        }
    }
}

void DocxAttributeOutput::WriteFormData_Impl( const ::sw::mark::IFieldmark& rFieldmark )
{
    if ( !m_Fields.empty() )
        m_Fields.begin()->pFieldmark = &rFieldmark;
}

void DocxAttributeOutput::WriteBookmarks_Impl( std::vector< OUString >& rStarts,
        std::vector< OUString >& rEnds )
{
    for ( std::vector< OUString >::const_iterator it = rStarts.begin(), end = rStarts.end(); it != end; ++it )
    {
        OString rName = OUStringToOString( *it, RTL_TEXTENCODING_UTF8 ).getStr( );
        m_rMarksStart.push_back( rName );
    }
    rStarts.clear();

    for ( std::vector< OUString >::const_iterator it = rEnds.begin(), end = rEnds.end(); it != end; ++it )
    {
        OString rName = OUStringToOString( *it, RTL_TEXTENCODING_UTF8 ).getStr( );
        m_rMarksEnd.push_back( rName );
    }
    rEnds.clear();
}

void DocxAttributeOutput::TextFootnote_Impl( const SwFmtFtn& rFootnote )
{
    const SwEndNoteInfo& rInfo = rFootnote.IsEndNote()?
        m_rExport.pDoc->GetEndNoteInfo(): m_rExport.pDoc->GetFtnInfo();

    // footnote/endnote run properties
    const SwCharFmt* pCharFmt = rInfo.GetAnchorCharFmt( *m_rExport.pDoc );

    OString aStyleId( "style" );
    aStyleId += OString::valueOf( sal_Int32( m_rExport.GetId( *pCharFmt ) ) );

    m_pSerializer->singleElementNS( XML_w, XML_rStyle, FSNS( XML_w, XML_val ), aStyleId.getStr(), FSEND );

    // remember the footnote/endnote to
    // 1) write the footnoteReference/endnoteReference in EndRunProperties()
    // 2) be able to dump them all to footnotes.xml/endnotes.xml
    if ( !rFootnote.IsEndNote() )
        m_pFootnotesList->add( rFootnote );
    else
        m_pEndnotesList->add( rFootnote );
}

void DocxAttributeOutput::FootnoteEndnoteReference()
{
    sal_Int32 nId;
    const SwFmtFtn *pFootnote = m_pFootnotesList->getCurrent( nId );

    // both cannot be set at the same time - if they are, it's a bug
    if ( !pFootnote )
        pFootnote = m_pEndnotesList->getCurrent( nId );

    if ( !pFootnote )
        return;

    sal_Int32 nToken = pFootnote->IsEndNote()? XML_endnoteReference: XML_footnoteReference;

    // write it
    if ( pFootnote->GetNumStr().Len() == 0 )
    {
        // autonumbered
        m_pSerializer->singleElementNS( XML_w, nToken,
                FSNS( XML_w, XML_id ), ::rtl::OString::valueOf( nId ).getStr(),
                FSEND );
    }
    else
    {
        // not autonumbered
        m_pSerializer->singleElementNS( XML_w, nToken,
                FSNS( XML_w, XML_customMarkFollows ), "1",
                FSNS( XML_w, XML_id ), ::rtl::OString::valueOf( nId ).getStr(),
                FSEND );

        RunText( pFootnote->GetNumStr() );
    }
}

void DocxAttributeOutput::FootnotesEndnotes( bool bFootnotes )
{
    const FootnotesVector& rVector = bFootnotes? m_pFootnotesList->getVector(): m_pEndnotesList->getVector();

    sal_Int32 nBody = bFootnotes? XML_footnotes: XML_endnotes;
    sal_Int32 nItem = bFootnotes? XML_footnote:  XML_endnote;

    m_pSerializer->startElementNS( XML_w, nBody,
            FSNS( XML_xmlns, XML_w ), "http://schemas.openxmlformats.org/wordprocessingml/2006/main",
            FSNS( XML_xmlns, XML_r ), "http://schemas.openxmlformats.org/officeDocument/2006/relationships",
            FSEND );

    sal_Int32 nIndex = 0;

    // separator
    m_pSerializer->startElementNS( XML_w, nItem,
            FSNS( XML_w, XML_id ), OString::valueOf( nIndex++ ).getStr(),
            FSNS( XML_w, XML_type ), "separator",
            FSEND );
    m_pSerializer->startElementNS( XML_w, XML_p, FSEND );
    m_pSerializer->startElementNS( XML_w, XML_r, FSEND );
    m_pSerializer->singleElementNS( XML_w, XML_separator, FSEND );
    m_pSerializer->endElementNS( XML_w, XML_r );
    m_pSerializer->endElementNS( XML_w, XML_p );
    m_pSerializer->endElementNS( XML_w, nItem );

    // separator
    m_pSerializer->startElementNS( XML_w, nItem,
            FSNS( XML_w, XML_id ), OString::valueOf( nIndex++ ).getStr(),
            FSNS( XML_w, XML_type ), "continuationSeparator",
            FSEND );
    m_pSerializer->startElementNS( XML_w, XML_p, FSEND );
    m_pSerializer->startElementNS( XML_w, XML_r, FSEND );
    m_pSerializer->singleElementNS( XML_w, XML_continuationSeparator, FSEND );
    m_pSerializer->endElementNS( XML_w, XML_r );
    m_pSerializer->endElementNS( XML_w, XML_p );
    m_pSerializer->endElementNS( XML_w, nItem );

    // if new special ones are added, update also WriteFootnoteEndnotePr()

    // footnotes/endnotes themselves
    for ( FootnotesVector::const_iterator i = rVector.begin(); i != rVector.end(); ++i, ++nIndex )
    {
        m_pSerializer->startElementNS( XML_w, nItem,
                FSNS( XML_w, XML_id ), OString::valueOf( nIndex ).getStr(),
                FSEND );

        const SwNodeIndex* pIndex = (*i)->GetTxtFtn()->GetStartNode();
        // tag required at the start of each footnote/endnote
        m_footnoteEndnoteRefTag = bFootnotes ? XML_footnoteRef : XML_endnoteRef;

        m_rExport.WriteSpecialText( pIndex->GetIndex() + 1,
                pIndex->GetNode().EndOfSectionIndex(),
                bFootnotes? TXT_FTN: TXT_EDN );

        m_pSerializer->endElementNS( XML_w, nItem );
    }

    m_pSerializer->endElementNS( XML_w, nBody );

}

void DocxAttributeOutput::WriteFootnoteEndnotePr( ::sax_fastparser::FSHelperPtr fs, int tag,
    const SwEndNoteInfo& info, int listtag )
{
    fs->startElementNS( XML_w, tag, FSEND );
    const char* fmt = NULL;
    switch( info.aFmt.GetNumberingType())
    {
        case SVX_NUM_CHARS_UPPER_LETTER_N: // fall through, map to upper letters
        case SVX_NUM_CHARS_UPPER_LETTER:
            fmt = "upperLetter";
            break;
        case SVX_NUM_CHARS_LOWER_LETTER_N: // fall through, map to lower letters
        case SVX_NUM_CHARS_LOWER_LETTER:
            fmt = "lowerLetter";
            break;
        case SVX_NUM_ROMAN_UPPER:
            fmt = "upperRoman";
            break;
        case SVX_NUM_ROMAN_LOWER:
            fmt = "lowerRoman";
            break;
        case SVX_NUM_ARABIC:
            fmt = "decimal";
            break;
        case SVX_NUM_NUMBER_NONE:
            fmt = "none";
            break;
        case SVX_NUM_CHAR_SPECIAL:
            fmt = "bullet";
            break;
        case SVX_NUM_PAGEDESC:
        case SVX_NUM_BITMAP:
        default:
            break; // no format
    }
    if( fmt != NULL )
        fs->singleElementNS( XML_w, XML_numFmt, FSNS( XML_w, XML_val ), fmt, FSEND );
    if( info.nFtnOffset != 0 )
        fs->singleElementNS( XML_w, XML_numStart, FSNS( XML_w, XML_val ),
            rtl::OString::valueOf( sal_Int32( info.nFtnOffset + 1 )).getStr(), FSEND );
    if( listtag != 0 ) // we are writting to settings.xml, write also special footnote/endnote list
    { // there are currently only two hardcoded ones ( see FootnotesEndnotes())
        fs->singleElementNS( XML_w, listtag, FSNS( XML_w, XML_id ), "0", FSEND );
        fs->singleElementNS( XML_w, listtag, FSNS( XML_w, XML_id ), "1", FSEND );
    }
    fs->endElementNS( XML_w, tag );
}

void DocxAttributeOutput::SectFootnoteEndnotePr()
{
    if( HasFootnotes())
        WriteFootnoteEndnotePr( m_pSerializer, XML_footnotePr, m_rExport.pDoc->GetFtnInfo(), 0 );
    if( HasEndnotes())
        WriteFootnoteEndnotePr( m_pSerializer, XML_endnotePr, m_rExport.pDoc->GetEndNoteInfo(), 0 );
}

void DocxAttributeOutput::ParaLineSpacing_Impl( short nSpace, short nMulti )
{
    if ( !m_pParagraphSpacingAttrList )
        m_pParagraphSpacingAttrList = m_pSerializer->createAttrList();

    if ( nSpace < 0 )
    {
        m_pParagraphSpacingAttrList->add( FSNS( XML_w, XML_lineRule ), "exact" );
        m_pParagraphSpacingAttrList->add( FSNS( XML_w, XML_line ), OString::valueOf( sal_Int32( -nSpace ) ) );
    }
    else if( nMulti )
    {
        m_pParagraphSpacingAttrList->add( FSNS( XML_w, XML_lineRule ), "auto" );
        m_pParagraphSpacingAttrList->add( FSNS( XML_w, XML_line ), OString::valueOf( sal_Int32( nSpace ) ) );
    }
    else if ( nSpace > 0 )
    {
        m_pParagraphSpacingAttrList->add( FSNS( XML_w, XML_lineRule ), "atLeast" );
        m_pParagraphSpacingAttrList->add( FSNS( XML_w, XML_line ), OString::valueOf( sal_Int32( nSpace ) ) );
    }
    else
        m_pParagraphSpacingAttrList->add( FSNS( XML_w, XML_lineRule ), "auto" );
}

void DocxAttributeOutput::ParaAdjust( const SvxAdjustItem& rAdjust )
{
    const char *pAdjustString;

    bool bEcma = GetExport().GetFilter().getVersion( ) == oox::core::ECMA_DIALECT;

    const SfxItemSet* pItems = GetExport().GetCurItemSet();
    const SvxFrameDirectionItem* rFrameDir = static_cast< const SvxFrameDirectionItem* >( pItems->GetItem( RES_FRAMEDIR ) );

    short nDir = FRMDIR_ENVIRONMENT;
    if( rFrameDir != NULL )
        nDir = rFrameDir->GetValue();
    if ( nDir == FRMDIR_ENVIRONMENT )
        nDir = GetExport( ).GetDefaultFrameDirection( );
    bool bRtl = ( nDir == FRMDIR_HORI_RIGHT_TOP );

    switch ( rAdjust.GetAdjust() )
    {
        case SVX_ADJUST_LEFT:
            if ( bEcma )
                pAdjustString = "left";
            else if ( bRtl )
                pAdjustString = "end";
            else
                pAdjustString = "start";
            break;
        case SVX_ADJUST_RIGHT:
            if ( bEcma )
                pAdjustString = "right";
            else if ( bRtl )
                pAdjustString = "start";
            else
                pAdjustString = "end";
            break;
        case SVX_ADJUST_BLOCKLINE:
        case SVX_ADJUST_BLOCK:
            pAdjustString = "both";
            break;
        case SVX_ADJUST_CENTER:
            pAdjustString = "center";
            break;
        default:
            return; // not supported attribute
    }
    m_pSerializer->singleElementNS( XML_w, XML_jc, FSNS( XML_w, XML_val ), pAdjustString, FSEND );
}

void DocxAttributeOutput::ParaSplit( const SvxFmtSplitItem& rSplit )
{
    if (rSplit.GetValue())
        m_pSerializer->singleElementNS( XML_w, XML_keepLines, FSNS( XML_w, XML_val ), "false", FSEND );
    else
        m_pSerializer->singleElementNS( XML_w, XML_keepLines, FSEND );
}

void DocxAttributeOutput::ParaWidows( const SvxWidowsItem& rWidows )
{
    if (rWidows.GetValue())
        m_pSerializer->singleElementNS( XML_w, XML_widowControl, FSEND );
    else
        m_pSerializer->singleElementNS( XML_w, XML_widowControl, FSNS( XML_w, XML_val ), "false", FSEND );
}

static void impl_WriteTabElement( FSHelperPtr pSerializer,
        const SvxTabStop& rTab, long nCurrentLeft )
{
    FastAttributeList *pTabElementAttrList = pSerializer->createAttrList();

    switch (rTab.GetAdjustment())
    {
    case SVX_TAB_ADJUST_RIGHT:
        pTabElementAttrList->add( FSNS( XML_w, XML_val ), OString( (sal_Char *)"right") );
        break;
    case SVX_TAB_ADJUST_DECIMAL:
        pTabElementAttrList->add( FSNS( XML_w, XML_val ), OString( (sal_Char *)"decimal") );
        break;
    case SVX_TAB_ADJUST_CENTER:
        pTabElementAttrList->add( FSNS( XML_w, XML_val ), OString( (sal_Char *)"center") );
        break;
    case SVX_TAB_ADJUST_DEFAULT:
    case SVX_TAB_ADJUST_LEFT:
    default:
        pTabElementAttrList->add( FSNS( XML_w, XML_val ), OString( (sal_Char *)"left") );
        break;
    }

    pTabElementAttrList->add( FSNS( XML_w, XML_pos ), OString::valueOf( rTab.GetTabPos() + nCurrentLeft ) );

    sal_Unicode cFillChar = rTab.GetFill();

    if (sal_Unicode('.') == cFillChar )
        pTabElementAttrList->add( FSNS( XML_w, XML_leader ), OString( (sal_Char *) "dot" ) );
    else if ( sal_Unicode('-') == cFillChar )
        pTabElementAttrList->add( FSNS( XML_w, XML_leader ), OString( (sal_Char *) "hyphen" ) );
    else if ( sal_Unicode(0xB7) == cFillChar ) // middle dot
        pTabElementAttrList->add( FSNS( XML_w, XML_leader ), OString( (sal_Char *) "middleDot" ) );
    else if ( sal_Unicode('_') == cFillChar )
        pTabElementAttrList->add( FSNS( XML_w, XML_leader ), OString( (sal_Char *) "underscore" ) );
    else
        pTabElementAttrList->add( FSNS( XML_w, XML_leader ), OString( (sal_Char *) "none" ) );

    pSerializer->singleElementNS( XML_w, XML_tab, pTabElementAttrList );
}

void DocxAttributeOutput::ParaTabStop( const SvxTabStopItem& rTabStop )
{
    const SfxPoolItem* pLR = m_rExport.HasItem( RES_LR_SPACE );
    long nCurrentLeft = pLR ? ((const SvxLRSpaceItem*)pLR)->GetTxtLeft() : 0;

    m_pSerializer->startElementNS( XML_w, XML_tabs, FSEND );

    sal_uInt16 nCount = rTabStop.Count();
    for (sal_uInt16 i = 0; i < nCount; i++ )
    {
        if( rTabStop[i].GetAdjustment() != SVX_TAB_ADJUST_DEFAULT )
            impl_WriteTabElement( m_pSerializer, rTabStop[i], nCurrentLeft );
        else
            GetExport().setDefaultTabStop( rTabStop[i].GetTabPos());
    }

    m_pSerializer->endElementNS( XML_w, XML_tabs );
}

void DocxAttributeOutput::ParaHyphenZone( const SvxHyphenZoneItem& rHyphenZone )
{
    m_pSerializer->singleElementNS( XML_w, XML_suppressAutoHyphens,
            FSNS( XML_w, XML_val ), rHyphenZone.IsHyphen( ) ? "false" : "true" ,
            FSEND );
}

void DocxAttributeOutput::ParaNumRule_Impl( const SwTxtNode* /*pTxtNd*/, sal_Int32 nLvl, sal_Int32 nNumId )
{
    if ( USHRT_MAX != nNumId && 0 != nNumId )
    {
        m_pSerializer->startElementNS( XML_w, XML_numPr, FSEND );
        m_pSerializer->singleElementNS( XML_w, XML_ilvl, FSNS( XML_w, XML_val ), OString::valueOf( sal_Int32( nLvl )).getStr(), FSEND );
        m_pSerializer->singleElementNS( XML_w, XML_numId, FSNS( XML_w, XML_val ), OString::valueOf( sal_Int32( nNumId )).getStr(), FSEND );
        m_pSerializer->endElementNS( XML_w, XML_numPr );
    }
}

void DocxAttributeOutput::ParaScriptSpace( const SfxBoolItem& rScriptSpace )
{
    sal_uInt16 nXmlElement = 0;

    switch ( rScriptSpace.Which( ) )
    {
        case RES_PARATR_SCRIPTSPACE:
            nXmlElement = XML_autoSpaceDE;
            break;
        case RES_PARATR_HANGINGPUNCTUATION:
            nXmlElement = XML_overflowPunct;
            break;
        case RES_PARATR_FORBIDDEN_RULES:
            nXmlElement = XML_kinsoku;
            break;
    }

    if ( nXmlElement )
    {
        m_pSerializer->singleElementNS( XML_w, nXmlElement,
               FSNS( XML_w, XML_val ), rScriptSpace.GetValue( ) ? "true": "false",
               FSEND );
    }
}

void DocxAttributeOutput::ParaVerticalAlign( const SvxParaVertAlignItem& rAlign )
{
    const char *pAlignString;

    switch ( rAlign.GetValue() )
    {
        case SvxParaVertAlignItem::BASELINE:
            pAlignString = "baseline";
            break;
        case SvxParaVertAlignItem::TOP:
            pAlignString = "top";
            break;
        case SvxParaVertAlignItem::CENTER:
            pAlignString = "center";
            break;
        case SvxParaVertAlignItem::BOTTOM:
            pAlignString = "bottom";
            break;
        case SvxParaVertAlignItem::AUTOMATIC:
            pAlignString = "auto";
            break;
        default:
            return; // not supported attribute
    }
    m_pSerializer->singleElementNS( XML_w, XML_textAlignment, FSNS( XML_w, XML_val ), pAlignString, FSEND );
}

void DocxAttributeOutput::ParaSnapToGrid( const SvxParaGridItem& rGrid )
{
    m_pSerializer->singleElementNS( XML_w, XML_snapToGrid,
            FSNS( XML_w, XML_val ), rGrid.GetValue( ) ? "true": "false",
            FSEND );
}

void DocxAttributeOutput::FormatFrameSize( const SwFmtFrmSize& rSize )
{
    if ( m_rExport.bOutFlyFrmAttrs )
    {
        if ( !m_pFlyAttrList )
            m_pFlyAttrList = m_pSerializer->createAttrList( );

        if ( rSize.GetWidth() && rSize.GetWidthSizeType() == ATT_FIX_SIZE )
        {
            m_pFlyAttrList->add( FSNS( XML_w, XML_w ), OString::valueOf( rSize.GetWidth( ) ) );
        }

        if ( rSize.GetHeight() )
        {
            OString sRule( "exact" );
            if ( rSize.GetHeightSizeType() == ATT_MIN_SIZE )
                sRule = OString( "atLeast" );
            m_pFlyAttrList->add( FSNS( XML_w, XML_hRule ), sRule );
            m_pFlyAttrList->add( FSNS( XML_w, XML_h ), OString::valueOf( rSize.GetHeight( ) ) );
        }
    }
    else if ( m_rExport.bOutPageDescs )
    {
        FastAttributeList *attrList = m_pSerializer->createAttrList( );
        if ( m_rExport.pAktPageDesc->GetLandscape( ) )
            attrList->add( FSNS( XML_w, XML_orient ), "landscape" );

        attrList->add( FSNS( XML_w, XML_w ), OString::valueOf( rSize.GetWidth( ) ) );
        attrList->add( FSNS( XML_w, XML_h ), OString::valueOf( rSize.GetHeight( ) ) );

        XFastAttributeListRef xAttrList( attrList );
        attrList = NULL;

        m_pSerializer->singleElementNS( XML_w, XML_pgSz, xAttrList );
    }
}

void DocxAttributeOutput::FormatPaperBin( const SvxPaperBinItem& )
{
    OSL_TRACE( "TODO DocxAttributeOutput::FormatPaperBin()" );
}

void DocxAttributeOutput::FormatLRSpace( const SvxLRSpaceItem& rLRSpace )
{
    bool bEcma = m_rExport.GetFilter().getVersion( ) == oox::core::ECMA_DIALECT;

    if ( m_rExport.bOutFlyFrmAttrs )
    {
        if ( !m_pFlyAttrList )
            m_pFlyAttrList = m_pSerializer->createAttrList();

        m_pFlyAttrList->add( FSNS( XML_w, XML_hSpace ),
                OString::valueOf(
                    sal_Int32( ( rLRSpace.GetLeft() + rLRSpace.GetRight() ) / 2 ) ) );
    }
    else if ( m_rExport.bOutPageDescs )
    {
        if ( !m_pSectionSpacingAttrList )
            m_pSectionSpacingAttrList = m_pSerializer->createAttrList();

        sal_uInt16 nLDist, nRDist;
        const SfxPoolItem* pItem = m_rExport.HasItem( RES_BOX );
        if ( pItem )
        {
            nRDist = ((SvxBoxItem*)pItem)->CalcLineSpace( BOX_LINE_LEFT );
            nLDist = ((SvxBoxItem*)pItem)->CalcLineSpace( BOX_LINE_RIGHT );
        }
        else
            nLDist = nRDist = 0;
        nLDist = nLDist + (sal_uInt16)rLRSpace.GetLeft();
        nRDist = nRDist + (sal_uInt16)rLRSpace.GetRight();

        m_pSectionSpacingAttrList->add( FSNS( XML_w, XML_left ), OString::valueOf( sal_Int32( nLDist ) ) );
        m_pSectionSpacingAttrList->add( FSNS( XML_w, XML_right ), OString::valueOf( sal_Int32( nRDist ) ) );
    }
    else
    {
        FastAttributeList *pLRSpaceAttrList = m_pSerializer->createAttrList();

        pLRSpaceAttrList->add( FSNS( XML_w, ( bEcma ? XML_left : XML_start ) ), OString::valueOf( (sal_Int32) rLRSpace.GetTxtLeft() ) );
        pLRSpaceAttrList->add( FSNS( XML_w, ( bEcma ? XML_right : XML_end ) ), OString::valueOf( (sal_Int32) rLRSpace.GetRight() ) );

        sal_Int32 nFirstLineAdjustment = rLRSpace.GetTxtFirstLineOfst();
        if (nFirstLineAdjustment > 0)
            pLRSpaceAttrList->add( FSNS( XML_w, XML_firstLine ), OString::valueOf( nFirstLineAdjustment ) );
        else
            pLRSpaceAttrList->add( FSNS( XML_w, XML_hanging ), OString::valueOf( - nFirstLineAdjustment ) );
        m_pSerializer->singleElementNS( XML_w, XML_ind, pLRSpaceAttrList );
    }
}

void DocxAttributeOutput::FormatULSpace( const SvxULSpaceItem& rULSpace )
{

    if ( m_rExport.bOutFlyFrmAttrs )
    {
        if ( !m_pFlyAttrList )
            m_pFlyAttrList = m_pSerializer->createAttrList();

        m_pFlyAttrList->add( FSNS( XML_w, XML_vSpace ),
                OString::valueOf(
                    sal_Int32( ( rULSpace.GetLower() + rULSpace.GetUpper() ) / 2 ) ) );
    }
    else if (m_rExport.bOutPageDescs )
    {
        OSL_ENSURE( m_rExport.GetCurItemSet(), "Impossible" );
        if ( !m_rExport.GetCurItemSet() )
            return;

        if ( !m_pSectionSpacingAttrList )
            m_pSectionSpacingAttrList = m_pSerializer->createAttrList();

        HdFtDistanceGlue aDistances( *m_rExport.GetCurItemSet() );

        sal_Int32 nHeader = 0;
        if ( aDistances.HasHeader() )
            nHeader = sal_Int32( aDistances.dyaHdrTop );
        m_pSectionSpacingAttrList->add( FSNS( XML_w, XML_header ), OString::valueOf( nHeader ) );

        // Page top
        m_pSectionSpacingAttrList->add( FSNS( XML_w, XML_top ),
                OString::valueOf( sal_Int32( aDistances.dyaTop ) ) );

        sal_Int32 nFooter = 0;
        if ( aDistances.HasFooter() )
            nFooter = sal_Int32( aDistances.dyaHdrBottom );
        m_pSectionSpacingAttrList->add( FSNS( XML_w, XML_footer ), OString::valueOf( nFooter ) );

        // Page Bottom
        m_pSectionSpacingAttrList->add( FSNS( XML_w, XML_bottom ),
                OString::valueOf( sal_Int32( aDistances.dyaBottom ) ) );

        // FIXME Page Gutter is not handled ATM, setting to 0 as it's mandatory for OOXML
        m_pSectionSpacingAttrList->add( FSNS( XML_w, XML_gutter ),
                OString::valueOf( sal_Int32( 0 ) ) );

    }
    else
    {
        if ( !m_pParagraphSpacingAttrList )
            m_pParagraphSpacingAttrList = m_pSerializer->createAttrList();
        m_pParagraphSpacingAttrList->add( FSNS( XML_w, XML_before ),
                OString::valueOf( (sal_Int32)rULSpace.GetUpper() ) );
        m_pParagraphSpacingAttrList->add( FSNS( XML_w, XML_after ),
                OString::valueOf( (sal_Int32)rULSpace.GetLower() ) );
        if (rULSpace.GetContext())
            m_pSerializer->singleElementNS( XML_w, XML_contextualSpacing, FSEND );
        else
            m_pSerializer->singleElementNS( XML_w, XML_contextualSpacing, FSNS( XML_w, XML_val ), "false", FSEND );
    }
}

void DocxAttributeOutput::FormatSurround( const SwFmtSurround& rSurround )
{
    if ( m_rExport.bOutFlyFrmAttrs )
    {
        if ( !m_pFlyAttrList )
            m_pFlyAttrList = m_pSerializer->createAttrList();

        OString sWrap( "auto" );
        switch ( rSurround.GetSurround( ) )
        {
            case SURROUND_NONE:
                sWrap = OString( "none" );
                break;
            case SURROUND_THROUGHT:
                sWrap = OString( "through" );
                break;
            case SURROUND_IDEAL:
            case SURROUND_PARALLEL:
            case SURROUND_LEFT:
            case SURROUND_RIGHT:
            default:
                sWrap = OString( "around" );
        }

        m_pFlyAttrList->add( FSNS( XML_w, XML_wrap ), sWrap );
    }
}

void DocxAttributeOutput::FormatVertOrientation( const SwFmtVertOrient& rFlyVert )
{
    if ( m_rExport.bOutFlyFrmAttrs )
    {
        if ( !m_pFlyAttrList )
            m_pFlyAttrList = m_pSerializer->createAttrList();

        OString sAlign;
        switch( rFlyVert.GetVertOrient() )
        {
            case text::VertOrientation::NONE:
                break;
            case text::VertOrientation::CENTER:
            case text::VertOrientation::LINE_CENTER:
                sAlign = OString( "center" );
                break;
            case text::VertOrientation::BOTTOM:
            case text::VertOrientation::LINE_BOTTOM:
                sAlign = OString( "bottom" );
                break;
            case text::VertOrientation::TOP:
            case text::VertOrientation::LINE_TOP:
            default:
                sAlign = OString( "top" );
                break;
        }

        if ( !sAlign.isEmpty() )
            m_pFlyAttrList->add( FSNS( XML_w, XML_yAlign ), sAlign );
        else
            m_pFlyAttrList->add( FSNS( XML_w, XML_y ),
                OString::valueOf( sal_Int32( rFlyVert.GetPos() ) ) );

        OString sVAnchor( "page" );
        switch ( rFlyVert.GetRelationOrient( ) )
        {
            case text::RelOrientation::CHAR:
            case text::RelOrientation::PRINT_AREA:
            case text::RelOrientation::TEXT_LINE:
                sVAnchor = OString( "column" );
                break;
            case text::RelOrientation::FRAME:
            case text::RelOrientation::PAGE_LEFT:
            case text::RelOrientation::PAGE_RIGHT:
            case text::RelOrientation::FRAME_LEFT:
            case text::RelOrientation::FRAME_RIGHT:
                sVAnchor = OString( "margin" );
                break;
            case text::RelOrientation::PAGE_FRAME:
            case text::RelOrientation::PAGE_PRINT_AREA:
            default:
                break;
        }

        m_pFlyAttrList->add( FSNS( XML_w, XML_vAnchor ), sVAnchor );
    }
}

void DocxAttributeOutput::FormatHorizOrientation( const SwFmtHoriOrient& rFlyHori )
{
    if ( m_rExport.bOutFlyFrmAttrs )
    {
        if ( !m_pFlyAttrList )
            m_pFlyAttrList = m_pSerializer->createAttrList();

        OString sAlign;
        switch( rFlyHori.GetHoriOrient() )
        {
            case text::HoriOrientation::NONE:
                break;
            case text::HoriOrientation::LEFT:
                sAlign = OString( rFlyHori.IsPosToggle( ) ? "inside" : "left" );
                break;
            case text::HoriOrientation::RIGHT:
                sAlign = OString( rFlyHori.IsPosToggle( ) ? "outside" : "right" );
                break;
            case text::HoriOrientation::CENTER:
            case text::HoriOrientation::FULL: // FULL only for tables
            default:
                sAlign = OString( "center" );
                break;
        }

        if ( !sAlign.isEmpty() )
            m_pFlyAttrList->add( FSNS( XML_w, XML_xAlign ), sAlign );
        else
            m_pFlyAttrList->add( FSNS( XML_w, XML_x ),
                OString::valueOf( sal_Int32( rFlyHori.GetPos() ) ) );

        OString sHAnchor( "page" );
        switch ( rFlyHori.GetRelationOrient( ) )
        {
            case text::RelOrientation::CHAR:
            case text::RelOrientation::PRINT_AREA:
                sHAnchor = OString( "text" );
                break;
            case text::RelOrientation::FRAME:
            case text::RelOrientation::PAGE_LEFT:
            case text::RelOrientation::PAGE_RIGHT:
            case text::RelOrientation::FRAME_LEFT:
            case text::RelOrientation::FRAME_RIGHT:
                sHAnchor = OString( "margin" );
                break;
            case text::RelOrientation::PAGE_FRAME:
            case text::RelOrientation::PAGE_PRINT_AREA:
            default:
                break;
        }

        m_pFlyAttrList->add( FSNS( XML_w, XML_hAnchor ), sHAnchor );
    }
}

void DocxAttributeOutput::FormatAnchor( const SwFmtAnchor& )
{
    // Fly frames: anchors here aren't matching the anchors in docx
}

void DocxAttributeOutput::FormatBackground( const SvxBrushItem& rBrush )
{
    if ( !m_rExport.bOutPageDescs )
    {
        OString sColor = impl_ConvertColor( rBrush.GetColor( ) );
        m_pSerializer->singleElementNS( XML_w, XML_shd,
                FSNS( XML_w, XML_fill ), sColor.getStr( ),
                FSNS( XML_w, XML_val ), "clear",
                FSEND );
    }
}

void DocxAttributeOutput::FormatBox( const SvxBoxItem& rBox )
{

    if ( !m_bOpenedSectPr )
    {
        // Normally open the borders tag for paragraphs
        m_pSerializer->startElementNS( XML_w, XML_pBdr, FSEND );
    }

    impl_pageBorders( m_pSerializer, rBox );

    if ( m_bOpenedSectPr )
    {
        // Special handling for pgBorder
        m_pSerializer->mergeTopMarks( sax_fastparser::MERGE_MARKS_PREPEND );
        m_pSerializer->mergeTopMarks( );
    }
    else
    {
        // Normally close the borders tag for paragraphs
        m_pSerializer->endElementNS( XML_w, XML_pBdr );
    }
}

void DocxAttributeOutput::FormatColumns_Impl( sal_uInt16 nCols, const SwFmtCol& rCol, bool bEven, SwTwips nPageSize )
{
    // Get the columns attributes
    FastAttributeList *pColsAttrList = m_pSerializer->createAttrList();

    pColsAttrList->add( FSNS( XML_w, XML_num ),
            OString::valueOf( sal_Int32( nCols ) ). getStr( ) );

    const char* pEquals = "false";
    if ( bEven )
    {
        sal_uInt16 nWidth = rCol.GetGutterWidth( true );
        pColsAttrList->add( FSNS( XML_w, XML_space ),
               OString::valueOf( sal_Int32( nWidth ) ).getStr( ) );

        pEquals = "true";
    }

    pColsAttrList->add( FSNS( XML_w, XML_equalWidth ), pEquals );

    bool bHasSep = COLADJ_NONE == rCol.GetLineAdj( );
    pColsAttrList->add( FSNS( XML_w, XML_sep ), bHasSep ? "true" : "false" );

    // Write the element
    m_pSerializer->startElementNS( XML_w, XML_cols, pColsAttrList );

    // Write the columns width if non-equals
    const SwColumns & rColumns = rCol.GetColumns(  );
    if ( !bEven )
    {
        for ( sal_uInt16 n = 0; n < nCols; ++n )
        {
            FastAttributeList *pColAttrList = m_pSerializer->createAttrList();
            sal_uInt16 nWidth = rCol.CalcPrtColWidth( n, ( sal_uInt16 ) nPageSize );
            pColAttrList->add( FSNS( XML_w, XML_w ),
                    OString::valueOf( sal_Int32( nWidth ) ).getStr( ) );

            if ( n + 1 != nCols )
            {
                sal_uInt16 nSpacing = rColumns[n].GetRight( ) + rColumns[n + 1].GetLeft( );
                pColAttrList->add( FSNS( XML_w, XML_space ),
                    OString::valueOf( sal_Int32( nSpacing ) ).getStr( ) );
            }

            m_pSerializer->singleElementNS( XML_w, XML_col, pColAttrList );
        }
    }

    m_pSerializer->endElementNS( XML_w, XML_cols );
}

void DocxAttributeOutput::FormatKeep( const SvxFmtKeepItem& )
{
    m_pSerializer->singleElementNS( XML_w, XML_keepNext, FSEND );
}

void DocxAttributeOutput::FormatTextGrid( const SwTextGridItem& rGrid )
{
    FastAttributeList *pGridAttrList = m_pSerializer->createAttrList();

    OString sGridType;
    switch ( rGrid.GetGridType( ) )
    {
        default:
        case GRID_NONE:
            sGridType = OString( "default" );
            break;
        case GRID_LINES_ONLY:
            sGridType = OString( "lines" );
            break;
        case GRID_LINES_CHARS:
            if ( rGrid.IsSnapToChars( ) )
                sGridType = OString( "snapToChars" );
            else
                sGridType = OString( "linesAndChars" );
            break;
    }
    pGridAttrList->add( FSNS( XML_w, XML_type ), sGridType.getStr( ) );

    sal_uInt16 nHeight = rGrid.GetBaseHeight() + rGrid.GetRubyHeight();
    pGridAttrList->add( FSNS( XML_w, XML_linePitch ),
            OString::valueOf( sal_Int32( nHeight ) ).getStr( ) );

    pGridAttrList->add( FSNS( XML_w, XML_charSpace ),
            OString::valueOf( sal_Int32( GridCharacterPitch( rGrid ) ) ).getStr( ) );

    m_pSerializer->singleElementNS( XML_w, XML_docGrid, pGridAttrList );
}

void DocxAttributeOutput::FormatLineNumbering( const SwFmtLineNumber& rNumbering )
{
    if ( !rNumbering.IsCount( ) )
        m_pSerializer->singleElementNS( XML_w, XML_suppressLineNumbers, FSEND );
}

void DocxAttributeOutput::FormatFrameDirection( const SvxFrameDirectionItem& rDirection )
{
    OString sTextFlow;
    bool bBiDi = false;
    short nDir = rDirection.GetValue();

    if ( nDir == FRMDIR_ENVIRONMENT )
        nDir = GetExport( ).GetDefaultFrameDirection( );

    switch ( nDir )
    {
        default:
        case FRMDIR_HORI_LEFT_TOP:
            sTextFlow = OString( "lrTb" );
            break;
        case FRMDIR_HORI_RIGHT_TOP:
            sTextFlow = OString( "lrTb" );
            bBiDi = true;
            break;
        case FRMDIR_VERT_TOP_LEFT: // many things but not this one
        case FRMDIR_VERT_TOP_RIGHT:
            sTextFlow = OString( "tbRl" );
            break;
    }

    if ( m_rExport.bOutPageDescs )
    {
        m_pSerializer->singleElementNS( XML_w, XML_textDirection,
               FSNS( XML_w, XML_val ), sTextFlow.getStr( ),
               FSEND );
        if ( bBiDi )
            m_pSerializer->singleElementNS( XML_w, XML_bidi, FSEND );
    }
    else if ( !m_rExport.bOutFlyFrmAttrs )
    {
        if ( bBiDi )
            m_pSerializer->singleElementNS( XML_w, XML_bidi, FSEND );
    }
}

DocxAttributeOutput::DocxAttributeOutput( DocxExport &rExport, FSHelperPtr pSerializer, oox::drawingml::DrawingML* pDrawingML )
    : m_rExport( rExport ),
      m_pSerializer( pSerializer ),
      m_rDrawingML( *pDrawingML ),
      m_pFontsAttrList( NULL ),
      m_pEastAsianLayoutAttrList( NULL ),
      m_pCharLangAttrList( NULL ),
      m_pSectionSpacingAttrList( NULL ),
      m_pParagraphSpacingAttrList( NULL ),
      m_pHyperlinkAttrList( NULL ),
      m_pFlyAttrList( NULL ),
      m_pFootnotesList( new ::docx::FootnotesList() ),
      m_pEndnotesList( new ::docx::FootnotesList() ),
      m_footnoteEndnoteRefTag( 0 ),
      m_pSectionInfo( NULL ),
      m_pRedlineData( NULL ),
      m_nRedlineId( 0 ),
      m_bOpenedSectPr( false ),
      m_sFieldBkm( ),
      m_nNextMarkId( 0 ),
      m_pTableWrt( NULL ),
      m_bTableCellOpen( false ),
      m_nTableDepth( 0 ),
      m_bParagraphOpened( false ),
      m_nColBreakStatus( COLBRK_NONE ),
      m_pParentFrame( NULL ),
      m_closeHyperlinkInThisRun( false ),
      m_closeHyperlinkInPreviousRun( false ),
      m_startedHyperlink( false ),
      m_postponedGraphic( NULL ),
      m_postponedMath( NULL ),
      m_postitFieldsMaxId( 0 ),
      m_anchorId( 0 )
{
}

DocxAttributeOutput::~DocxAttributeOutput()
{
    delete m_pFontsAttrList, m_pFontsAttrList = NULL;
    delete m_pEastAsianLayoutAttrList, m_pEastAsianLayoutAttrList = NULL;
    delete m_pCharLangAttrList, m_pCharLangAttrList = NULL;
    delete m_pSectionSpacingAttrList, m_pSectionSpacingAttrList = NULL;
    delete m_pParagraphSpacingAttrList, m_pParagraphSpacingAttrList = NULL;
    delete m_pHyperlinkAttrList, m_pHyperlinkAttrList = NULL;
    delete m_pFlyAttrList, m_pFlyAttrList = NULL;

    delete m_pFootnotesList, m_pFootnotesList = NULL;
    delete m_pEndnotesList, m_pEndnotesList = NULL;

    delete m_pTableWrt, m_pTableWrt = NULL;
    delete m_pParentFrame;
}

DocxExport& DocxAttributeOutput::GetExport()
{
    return m_rExport;
}

bool DocxAttributeOutput::HasFootnotes() const
{
    return !m_pFootnotesList->isEmpty();
}

bool DocxAttributeOutput::HasEndnotes() const
{
    return !m_pEndnotesList->isEmpty();
}

bool DocxAttributeOutput::HasPostitFields() const
{
    return !m_postitFields.empty();
}

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