Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  impedit4.cxx   Sprache: C

 
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */



#include <svl/srchitem.hxx>
#include <editeng/adjustitem.hxx>
#include <editeng/cmapitem.hxx>
#include <editeng/lspcitem.hxx>
#include <editeng/tstpitem.hxx>

#include "eertfpar.hxx"
#include <editeng/editeng.hxx>
#include "impedit.hxx"
#include <editeng/editview.hxx>
#include "eehtml.hxx"
#include "editobj2.hxx"
#include <i18nlangtag/lang.h>
#include <sal/log.hxx>
#include <o3tl/safeint.hxx>
#include <osl/diagnose.h>
#include <osl/thread.h>

#include <editxml.hxx>

#include <editeng/autokernitem.hxx>
#include <editeng/contouritem.hxx>
#include <editeng/colritem.hxx>
#include <editeng/crossedoutitem.hxx>
#include <editeng/escapementitem.hxx>
#include <editeng/fhgtitem.hxx>
#include <editeng/fontitem.hxx>
#include <editeng/kernitem.hxx>
#include <editeng/lrspitem.hxx>
#include <editeng/postitem.hxx>
#include <editeng/shdditem.hxx>
#include <editeng/udlnitem.hxx>
#include <editeng/ulspitem.hxx>
#include <editeng/wghtitem.hxx>
#include <editeng/langitem.hxx>
#include <editeng/charreliefitem.hxx>
#include <editeng/frmdiritem.hxx>
#include <editeng/emphasismarkitem.hxx>
#include "textconv.hxx"
#include <rtl/tencinfo.h>
#include <svtools/htmlout.hxx>
#include <svtools/rtfout.hxx>
#include <tools/stream.hxx>
#include <edtspell.hxx>
#include <editeng/unolingu.hxx>
#include <com/sun/star/linguistic2/XThesaurus.hpp>
#include <com/sun/star/i18n/ScriptType.hpp>
#include <com/sun/star/i18n/WordType.hpp>
#include <unotools/transliterationwrapper.hxx>
#include <unotools/textsearch.hxx>
#include <comphelper/processfactory.hxx>
#include <vcl/help.hxx>
#include <vcl/metric.hxx>
#include <svtools/rtfkeywd.hxx>
#include <editeng/edtdlg.hxx>

#include <cstddef>
#include <memory>
#include <unordered_map>
#include <vector>
#include <set>

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


ErrCode ImpEditEngine::Read( SvStream& rInput, const OUString& rBaseURL, EETextFormat eFormat, SvKeyValueIterator* pHTTPHeaderAttrs /* = NULL */ )
{
    bool bUndoEnabled = IsUndoEnabled();
    EnableUndo(false);
    SetText(OUString());
    EditPaM aPaM(maEditDoc.GetStartPaM());
    Read(rInput, rBaseURL, eFormat, EditSelection(aPaM, aPaM), pHTTPHeaderAttrs);
    EnableUndo(bUndoEnabled);
    return rInput.GetError();
}

EditPaM ImpEditEngine::Read(SvStream& rInput, const OUString& rBaseURL, EETextFormat eFormat, const EditSelection& rSel, SvKeyValueIterator* pHTTPHeaderAttrs)
{
    bool _bUpdate = SetUpdateLayout( false );
    EditPaM aPaM;
    if ( eFormat == EETextFormat::Text )
        aPaM = ReadText( rInput, rSel );
    else if ( eFormat == EETextFormat::Rtf )
        aPaM = ReadRTF( rInput, rSel );
    else if ( eFormat == EETextFormat::Xml )
        aPaM = ReadXML( rInput, rSel );
    else if ( eFormat == EETextFormat::Html )
        aPaM = ReadHTML( rInput, rBaseURL, rSel, pHTTPHeaderAttrs );
    else
    {
        OSL_FAIL( "Read: Unknown Format" );
    }

    FormatFullDoc();        // perhaps a simple format is enough?
    SetUpdateLayout( _bUpdate );

    return aPaM;
}

EditPaM ImpEditEngine::ReadText( SvStream& rInput, EditSelection aSel )
{
    if ( aSel.HasRange() )
        aSel = ImpDeleteSelection( aSel );
    EditPaM aPaM = aSel.Max();

    OUString aTmpStr;
    bool bDone = rInput.ReadByteStringLine( aTmpStr, rInput.GetStreamCharSet() );
    while ( bDone )
    {
        aPaM = ImpInsertText( EditSelection( aPaM, aPaM ), aTmpStr );
        aPaM = ImpInsertParaBreak( aPaM );
        bDone = rInput.ReadByteStringLine( aTmpStr, rInput.GetStreamCharSet() );
    }
    return aPaM;
}

EditPaM ImpEditEngine::ReadXML( SvStream& rInput, EditSelection aSel )
{
    if ( aSel.HasRange() )
        aSel = ImpDeleteSelection( aSel );

    ESelection aESel = CreateESel( aSel );

    return ::SvxReadXML( *GetEditEnginePtr(), rInput, aESel );
}

EditPaM ImpEditEngine::ReadRTF( SvStream& rInput, EditSelection aSel )
{
    if ( aSel.HasRange() )
        aSel = ImpDeleteSelection( aSel );

    // The SvRTF parser expects the Which-mapping passed on in the pool, not
    // dependent on a secondary.
    SfxItemPool* pPool = &maEditDoc.GetItemPool();
    while (pPool->GetSecondaryPool() && pPool->GetName() != "EditEngineItemPool")
    {
        pPool = pPool->GetSecondaryPool();
    }

    assert(pPool && "ReadRTF: no EditEnginePool!");

    DBG_ASSERT(pPool->GetName() == "EditEngineItemPool",
        "ReadRTF: wrong EditEnginePool!");

    EditRTFParserRef xPrsr = new EditRTFParser(rInput, aSel, *pPool, mpEditEngine);
    SvParserState eState = xPrsr->CallParser();
    if ( ( eState != SvParserState::Accepted ) && ( !rInput.GetError() ) )
    {
        rInput.SetError( EE_READWRITE_WRONGFORMAT );
        return aSel.Min();
    }
    return xPrsr->GetCurPaM();
}

EditPaM ImpEditEngine::ReadHTML( SvStream& rInput, const OUString& rBaseURL, EditSelection aSel, SvKeyValueIterator* pHTTPHeaderAttrs )
{
    if ( aSel.HasRange() )
        aSel = ImpDeleteSelection( aSel );

    EditHTMLParserRef xPrsr = new EditHTMLParser( rInput, rBaseURL, pHTTPHeaderAttrs );
    SvParserState eState = xPrsr->CallParser(mpEditEngine, aSel.Max());
    if ( ( eState != SvParserState::Accepted ) && ( !rInput.GetError() ) )
    {
        rInput.SetError( EE_READWRITE_WRONGFORMAT );
        return aSel.Min();
    }
    return xPrsr->GetCurSelection().Max();
}

void ImpEditEngine::Write( SvStream& rOutput, EETextFormat eFormat )
{
    EditPaM aStartPaM(maEditDoc.GetStartPaM());
    EditPaM aEndPaM(maEditDoc.GetEndPaM());
    Write(rOutput, eFormat, EditSelection(aStartPaM, aEndPaM));
}

void ImpEditEngine::Write(SvStream& rOutput, EETextFormat eFormat, const EditSelection& rSel)
{
    if ( !rOutput.IsWritable() )
        rOutput.SetError( SVSTREAM_WRITE_ERROR );

    if ( rOutput.GetError() )
        return;

    if ( eFormat == EETextFormat::Text )
        WriteText( rOutput, rSel );
    else if ( eFormat == EETextFormat::Rtf )
        WriteRTF( rOutput, rSel, /*bClipboard=*/false );
    else if ( eFormat == EETextFormat::Xml )
        WriteXML( rOutput, rSel );
    else if ( eFormat == EETextFormat::Html )
        ;
    else
    {
        OSL_FAIL( "Write: Unknown Format" );
    }
}

ErrCode ImpEditEngine::WriteText( SvStream& rOutput, EditSelection aSel )
{
    sal_Int32 nStartNode, nEndNode;
    bool bRange = aSel.HasRange();
    if ( bRange )
    {
        aSel.Adjust( maEditDoc );
        nStartNode = maEditDoc.GetPos( aSel.Min().GetNode() );
        nEndNode = maEditDoc.GetPos( aSel.Max().GetNode() );
    }
    else
    {
        nStartNode = 0;
        nEndNode = maEditDoc.Count()-1;
    }

    // iterate over the paragraphs ...
    for ( sal_Int32 nNode = nStartNode; nNode <= nEndNode; nNode++  )
    {
        ContentNode* pNode = maEditDoc.GetObject( nNode );
        assert(pNode && "Node not found: Search&Replace");

        sal_Int32 nStartPos = 0;
        sal_Int32 nEndPos = pNode->Len();
        if ( bRange )
        {
            if ( nNode == nStartNode )
                nStartPos = aSel.Min().GetIndex();
            if ( nNode == nEndNode ) // can also be == nStart!
                nEndPos = aSel.Max().GetIndex();
        }
        OUString aTmpStr = EditDoc::GetParaAsString( pNode, nStartPos, nEndPos );
        rOutput.WriteByteStringLine( aTmpStr, rOutput.GetStreamCharSet() );
    }

    return rOutput.GetError();
}

bool ImpEditEngine::WriteItemListAsRTF( ItemList& rLst, SvStream& rOutput, sal_Int32 nPara, sal_Int32 nPos,
                        const std::vector<std::unique_ptr<SvxFontItem>>& rFontTable, SvxColorList& rColorList )
{
    const SfxPoolItem* pAttrItem = rLst.First();
    while ( pAttrItem )
    {
        WriteItemAsRTF( *pAttrItem, rOutput, nPara, nPos,rFontTable, rColorList );
        pAttrItem = rLst.Next();
    }
    return rLst.Count() != 0;
}

static void lcl_FindValidAttribs( ItemList& rLst, ContentNode* pNode, sal_Int32 nIndex, sal_uInt16 nScriptType )
{
    std::size_t nAttr = 0;
    EditCharAttrib* pAttr = GetAttrib( pNode->GetCharAttribs().GetAttribs(), nAttr );
    while ( pAttr && ( pAttr->GetStart() <= nIndex ) )
    {
        // Start is checked in while ...
        if ( pAttr->GetEnd() > nIndex )
        {
            if ( IsScriptItemValid( pAttr->GetItem()->Which(), nScriptType ) )
                rLst.Insert( pAttr->GetItem() );
        }
        nAttr++;
        pAttr = GetAttrib( pNode->GetCharAttribs().GetAttribs(), nAttr );
    }
}

void ImpEditEngine::WriteXML(SvStream& rOutput, const EditSelection& rSel)
{
    ESelection aESel = CreateESel(rSel);

    SvxWriteXML( *GetEditEnginePtr(), rOutput, aESel );
}

static size_t GetFontIndex(const SfxPoolItem& rItem,
                           const std::vector<std::unique_ptr<SvxFontItem>>& rFontTable)
{
    for (size_t i = 0; i < rFontTable.size(); ++i)
        if (*rFontTable[i] == rItem)
            return i;
    return 0;
}

ErrCode ImpEditEngine::WriteRTF( SvStream& rOutput, EditSelection aSel, bool bClipboard )
{
    assert( IsUpdateLayout() && "WriteRTF for UpdateMode = sal_False!" );
    CheckIdleFormatter();

    sal_Int32 nStartNode, nEndNode;
    aSel.Adjust( maEditDoc );

    nStartNode = maEditDoc.GetPos( aSel.Min().GetNode() );
    nEndNode = maEditDoc.GetPos( aSel.Max().GetNode() );

    // RTF header ...
    rOutput.WriteChar( '{' ) ;

    rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_RTF );

    rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_ANSI );
    rtl_TextEncoding eDestEnc = RTL_TEXTENCODING_MS_1252;

    // Generate and write out Font table  ...
    std::vector<std::unique_ptr<SvxFontItem>> aFontTable;
    // default font must be up front, so DEF font in RTF
    aFontTable.emplace_back( new SvxFontItem( maEditDoc.GetItemPool().GetUserOrPoolDefaultItem( EE_CHAR_FONTINFO ) ) );
    aFontTable.emplace_back( new SvxFontItem( maEditDoc.GetItemPool().GetUserOrPoolDefaultItem( EE_CHAR_FONTINFO_CJK ) ) );
    aFontTable.emplace_back( new SvxFontItem( maEditDoc.GetItemPool().GetUserOrPoolDefaultItem( EE_CHAR_FONTINFO_CTL ) ) );
    for (auto nWhich : { EE_CHAR_FONTINFO, EE_CHAR_FONTINFO_CJK, EE_CHAR_FONTINFO_CTL })
    {
        for (const SfxPoolItem* pItem : maEditDoc.GetItemPool().GetItemSurrogates(nWhich))
        {
            SvxFontItem const*const pFontItem = static_cast<const SvxFontItem*>(pItem);
            bool bAlreadyExist = false;
            const size_t nTestMax = nWhich == EE_CHAR_FONTINFO ? 1 : aFontTable.size();
            for ( size_t nTest = 0; !bAlreadyExist && ( nTest < nTestMax ); nTest++ )
            {
                bAlreadyExist = *aFontTable[ nTest ] == *pFontItem;
            }

            if ( !bAlreadyExist )
                aFontTable.emplace_back( new SvxFontItem( *pFontItem ) );
        }
    }

    rOutput << endl;
    rOutput.WriteChar( '{' ).WriteOString( OOO_STRING_SVTOOLS_RTF_FONTTBL );
    for ( std::vector<SvxFontItem*>::size_type j = 0; j < aFontTable.size(); j++ )
    {
        SvxFontItem* pFontItem = aFontTable[ j ].get();
        rOutput.WriteChar( '{' );
        rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_F );
        rOutput.WriteNumberAsString( j );
        switch ( pFontItem->GetFamily()  )
        {
            case FAMILY_DONTKNOW:       rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_FNIL );
                                        break;
            case FAMILY_DECORATIVE:     rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_FDECOR );
                                        break;
            case FAMILY_MODERN:         rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_FMODERN );
                                        break;
            case FAMILY_ROMAN:          rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_FROMAN );
                                        break;
            case FAMILY_SCRIPT:         rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_FSCRIPT );
                                        break;
            case FAMILY_SWISS:          rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_FSWISS );
                                        break;
            default:
                break;
        }
        rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_FPRQ );
        sal_uInt16 nVal = 0;
        switch( pFontItem->GetPitch() )
        {
            case PITCH_FIXED:       nVal = 1;       break;
            case PITCH_VARIABLE:    nVal = 2;       break;
            default:
                break;
        }
        rOutput.WriteNumberAsString( nVal );

        rtl_TextEncoding eChrSet = pFontItem->GetCharSet();
        // tdf#47679 OpenSymbol is not encoded in Symbol Encoding
        if (IsOpenSymbol(pFontItem->GetFamilyName()))
        {
            SAL_WARN_IF(eChrSet == RTL_TEXTENCODING_SYMBOL, "editeng""OpenSymbol should not have charset of RTL_TEXTENCODING_SYMBOL in new documents");
            eChrSet = RTL_TEXTENCODING_UTF8;
        }
        DBG_ASSERT( eChrSet != 9, "SystemCharSet?!" );
        if( RTL_TEXTENCODING_DONTKNOW == eChrSet )
            eChrSet = osl_getThreadTextEncoding();
        rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_FCHARSET );
        rOutput.WriteNumberAsString( rtl_getBestWindowsCharsetFromTextEncoding( eChrSet ) );

        rOutput.WriteChar( ' ' );
        RTFOutFuncs::Out_String( rOutput, pFontItem->GetFamilyName(), eDestEnc );
        rOutput.WriteOString( ";}" );
    }
    rOutput.WriteChar( '}' );
    rOutput << endl;

    // Write out ColorList  ...
    SvxColorList aColorList;
    // COL_AUTO should be the default color, always put it first
    aColorList.emplace_back(COL_AUTO);
    SvxColorItem const& rDefault(maEditDoc.GetItemPool().GetUserOrPoolDefaultItem(EE_CHAR_COLOR));
    if (rDefault.GetValue() != COL_AUTO) // is the default always AUTO?
    {
        aColorList.push_back(rDefault.GetValue());
    }
    for (const SfxPoolItem* pItem : maEditDoc.GetItemPool().GetItemSurrogates(EE_CHAR_COLOR))
    {
        auto pColorItem(dynamic_cast<SvxColorItem const*>(pItem));
        if (pColorItem && pColorItem->GetValue() != COL_AUTO) // may be null!
        {
            aColorList.push_back(pColorItem->GetValue());
        }
    }

    rOutput.WriteChar( '{' ).WriteOString( OOO_STRING_SVTOOLS_RTF_COLORTBL );
    for ( SvxColorList::size_type j = 0; j < aColorList.size(); j++ )
    {
        Color const color = aColorList[j];
        if (color != COL_AUTO) // auto is represented by "empty" element
        {
            rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_RED );
            rOutput.WriteNumberAsString( color.GetRed() );
            rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_GREEN );
            rOutput.WriteNumberAsString( color.GetGreen() );
            rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_BLUE );
            rOutput.WriteNumberAsString( color.GetBlue() );
        }
        rOutput.WriteChar( ';' );
    }
    rOutput.WriteChar( '}' );
    rOutput << endl;

    std::unordered_map<SfxStyleSheetBase*, sal_uInt32> aStyleSheetToIdMap;
    // StyleSheets...
    if ( GetStyleSheetPool() )
    {
        // Collect used paragraph styles when copying to the clipboard.
        std::set<SfxStyleSheetBase*> aUsedParagraphStyles;
        if (bClipboard)
        {
            for (sal_Int32 nNode = nStartNode; nNode <= nEndNode; nNode++)
            {
                ContentNode* pNode = maEditDoc.GetObject(nNode);
                if (!pNode)
                {
                    continue;
                }

                SfxStyleSheet* pParaStyle = pNode->GetStyleSheet();
                if (!pParaStyle)
                {
                    continue;
                }

                aUsedParagraphStyles.insert(pParaStyle);

                // Collect parents of the style recursively.
                OUString aParent = pParaStyle->GetParent();
                while (!aParent.isEmpty())
                {
                    auto pParent = static_cast<SfxStyleSheet*>(
                        GetStyleSheetPool()->Find(aParent, pParaStyle->GetFamily()));
                    if (!pParent)
                    {
                        break;
                    }

                    aUsedParagraphStyles.insert(pParent);
                    aParent = pParent->GetParent();
                }

                // Collect follows of the style recursively.
                OUString aFollow = pParaStyle->GetFollow();
                while (!aFollow.isEmpty())
                {
                    auto pFollow = static_cast<SfxStyleSheet*>(
                        GetStyleSheetPool()->Find(aFollow, pParaStyle->GetFamily()));
                    if (!pFollow)
                    {
                        break;
                    }

                    auto it = aUsedParagraphStyles.insert(pFollow);
                    // A style is fine to have itself as a follow.
                    if (!it.second)
                    {
                        // No insertion happened, so this is visited already.
                        break;
                    }
                    aFollow = pFollow->GetFollow();
                }
            }
        }

        std::shared_ptr<SfxStyleSheetIterator> aSSSIterator = std::make_shared<SfxStyleSheetIterator>(GetStyleSheetPool(),
                SfxStyleFamily::All);
        // fill aStyleSheetToIdMap
        sal_uInt32 nId = 1;
        for ( SfxStyleSheetBase* pStyle = aSSSIterator->First(); pStyle;
                                 pStyle = aSSSIterator->Next() )
        {
            if (bClipboard && !aUsedParagraphStyles.contains(pStyle))
            {
                // Don't include unused paragraph styles in the clipboard case.
                continue;
            }
            aStyleSheetToIdMap[pStyle] = nId;
            nId++;
        }

        if ( aSSSIterator->Count() )
        {

            sal_uInt32 nStyle = 0;
            rOutput.WriteChar( '{' ).WriteOString( OOO_STRING_SVTOOLS_RTF_STYLESHEET );

            for ( SfxStyleSheetBase* pStyle = aSSSIterator->First(); pStyle;
                                     pStyle = aSSSIterator->Next() )
            {
                if (bClipboard && !aUsedParagraphStyles.contains(pStyle))
                {
                    // Don't write unused paragraph styles in the clipboard case.
                    continue;
                }

                rOutput << endl;
                rOutput.WriteChar( '{' ).WriteOString( OOO_STRING_SVTOOLS_RTF_S );
                sal_uInt32 nNumber = nStyle + 1;
                rOutput.WriteNumberAsString( nNumber );

                // Attribute, also from Parent!
                for ( sal_uInt16 nParAttr = EE_PARA_START; nParAttr <= EE_CHAR_END; nParAttr++ )
                {
                    if ( pStyle->GetItemSet().GetItemState( nParAttr ) == SfxItemState::SET )
                    {
                        const SfxPoolItem& rItem = pStyle->GetItemSet().Get( nParAttr );
                        WriteItemAsRTF( rItem, rOutput, 0, 0, aFontTable, aColorList );
                    }
                }

                // Parent ... (only if necessary)
                if ( !pStyle->GetParent().isEmpty() && ( pStyle->GetParent() != pStyle->GetName() ) )
                {
                    SfxStyleSheet* pParent = static_cast<SfxStyleSheet*>(GetStyleSheetPool()->Find( pStyle->GetParent(), pStyle->GetFamily() ));
                    DBG_ASSERT( pParent, "Parent not found!" );
                    rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_SBASEDON );
                    auto iter = aStyleSheetToIdMap.find(pParent);
                    assert(iter != aStyleSheetToIdMap.end());
                    nNumber = iter->second;
                    rOutput.WriteNumberAsString( nNumber );
                }

                // Next Style... (more)
                // we assume that we have only SfxStyleSheet in the pool
                SfxStyleSheet* pNext = static_cast<SfxStyleSheet*>(pStyle);
                if ( !pStyle->GetFollow().isEmpty() && ( pStyle->GetFollow() != pStyle->GetName() ) )
                    pNext = static_cast<SfxStyleSheet*>(GetStyleSheetPool()->Find( pStyle->GetFollow(), pStyle->GetFamily() ));

                DBG_ASSERT( pNext, "Next not found!" );
                rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_SNEXT );
                auto iter = aStyleSheetToIdMap.find(pNext);
                assert(iter != aStyleSheetToIdMap.end());
                nNumber = iter->second;
                rOutput.WriteNumberAsString( nNumber );

                // Name of the template...
                rOutput.WriteOString( " " );
                RTFOutFuncs::Out_String( rOutput, pStyle->GetName(), eDestEnc );
                rOutput.WriteOString( ";}" );
                nStyle++;
            }
            rOutput.WriteChar( '}' );
            rOutput << endl;
        }
    }

    // Write the pool defaults in advance ...
    rOutput.WriteChar( '{' ).WriteOString( OOO_STRING_SVTOOLS_RTF_IGNORE ).WriteOString( "\\EditEnginePoolDefaults" );
    for ( sal_uInt16 nPoolDefItem = EE_PARA_START; nPoolDefItem <= EE_CHAR_END; nPoolDefItem++)
    {
        const SfxPoolItem& rItem = maEditDoc.GetItemPool().GetUserOrPoolDefaultItem( nPoolDefItem );
        WriteItemAsRTF( rItem, rOutput, 0, 0, aFontTable, aColorList );
    }
    rOutput.WriteChar( '}' ) << endl;

    // DefTab:
    MapMode aTwpMode( MapUnit::MapTwip );
    sal_uInt16 nDefTabTwps = static_cast<sal_uInt16>(GetRefDevice()->LogicToLogic(
                                        Point( maEditDoc.GetDefTab(), 0 ),
                                        &GetRefMapMode(), &aTwpMode ).X());
    rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_DEFTAB );
    rOutput.WriteNumberAsString( nDefTabTwps );
    rOutput << endl;

    // iterate over the paragraphs ...
    rOutput.WriteChar( '{' ) << endl;
    for ( sal_Int32 nNode = nStartNode; nNode <= nEndNode; nNode++  )
    {
        ContentNode* pNode = maEditDoc.GetObject( nNode );
        DBG_ASSERT( pNode, "Node not found: Search&Replace" );

        // The paragraph attributes in advance ...
        bool bAttr = false;

        // Template?
        if ( pNode->GetStyleSheet() )
        {
            // Number of template
            rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_S );
            auto iter = aStyleSheetToIdMap.find(pNode->GetStyleSheet());
            assert(iter != aStyleSheetToIdMap.end());
            sal_uInt32 nNumber = iter->second;
            rOutput.WriteNumberAsString( nNumber );

            // All Attribute
            // Attribute, also from Parent!
            for ( sal_uInt16 nParAttr = EE_PARA_START; nParAttr <= EE_CHAR_END; nParAttr++ )
            {
                if ( pNode->GetStyleSheet()->GetItemSet().GetItemState( nParAttr ) == SfxItemState::SET )
                {
                    const SfxPoolItem& rItem = pNode->GetStyleSheet()->GetItemSet().Get( nParAttr );
                    WriteItemAsRTF( rItem, rOutput, nNode, 0, aFontTable, aColorList );
                    bAttr = true;
                }
            }
        }

        for ( sal_uInt16 nParAttr = EE_PARA_START; nParAttr <= EE_CHAR_END; nParAttr++ )
        {
            // Now where stylesheet processing, only hard paragraph attributes!
            if ( pNode->GetContentAttribs().GetItems().GetItemState( nParAttr ) == SfxItemState::SET )
            {
                const SfxPoolItem& rItem = pNode->GetContentAttribs().GetItems().Get( nParAttr );
                WriteItemAsRTF( rItem, rOutput, nNode, 0, aFontTable, aColorList );
                bAttr = true;
            }
        }
        if ( bAttr )
            rOutput.WriteChar( ' ' ); // Separator

        ItemList aAttribItems;
        ParaPortion* pParaPortion = FindParaPortion( pNode );
        DBG_ASSERT( pParaPortion, "Portion not found: WriteRTF" );

        sal_Int32 nIndex = 0;
        sal_Int32 nStartPos = 0;
        sal_Int32 nEndPos = pNode->Len();
        sal_Int32 nStartPortion = 0;
        sal_Int32 nEndPortion = pParaPortion->GetTextPortions().Count() - 1;
        bool bFinishPortion = false;
        sal_Int32 nPortionStart;

        if ( nNode == nStartNode )
        {
            nStartPos = aSel.Min().GetIndex();
            nStartPortion = pParaPortion->GetTextPortions().FindPortion( nStartPos, nPortionStart );
            if ( nStartPos != 0 )
            {
                aAttribItems.Clear();
                lcl_FindValidAttribs( aAttribItems, pNode, nStartPos, GetI18NScriptType( EditPaM( pNode, 0 ) ) );
                if ( aAttribItems.Count() )
                {
                    // These attributes may not apply to the entire paragraph:
                    rOutput.WriteChar( '{' );
                    WriteItemListAsRTF( aAttribItems, rOutput, nNode, nStartPos, aFontTable, aColorList );
                    bFinishPortion = true;
                }
                aAttribItems.Clear();
            }
        }
        if ( nNode == nEndNode ) // can also be == nStart!
        {
            nEndPos = aSel.Max().GetIndex();
            nEndPortion = pParaPortion->GetTextPortions().FindPortion( nEndPos, nPortionStart );
        }

        const EditCharAttrib* pNextFeature = pNode->GetCharAttribs().FindFeature(nIndex);
        // start at 0, so the index is right ...
        for ( sal_Int32 n = 0; n <= nEndPortion; n++ )
        {
            const TextPortion& rTextPortion = pParaPortion->GetTextPortions()[n];
            if ( n < nStartPortion )
            {
                nIndex = nIndex + rTextPortion.GetLen();
                continue;
            }

            if ( pNextFeature && ( pNextFeature->GetStart() == nIndex ) && ( pNextFeature->GetItem()->Which() != EE_FEATURE_FIELD ) )
            {
                WriteItemAsRTF( *pNextFeature->GetItem(), rOutput, nNode, nIndex, aFontTable, aColorList );
                pNextFeature = pNode->GetCharAttribs().FindFeature( pNextFeature->GetStart() + 1 );
            }
            else
            {
                aAttribItems.Clear();
                sal_uInt16 nScriptTypeI18N = GetI18NScriptType( EditPaM( pNode, nIndex+1 ) );
                SvtScriptType nScriptType = SvtLanguageOptions::FromI18NToSvtScriptType(nScriptTypeI18N);
                rtl_TextEncoding actEncoding = eDestEnc;
                if ( !n || IsScriptChange( EditPaM( pNode, nIndex ) ) )
                {
                    SfxItemSet aAttribs = GetAttribs( nNode, nIndex+1, nIndex+1 );
                    auto& item = aAttribs.Get(GetScriptItemId(EE_CHAR_FONTINFO, nScriptType));
                    aAttribItems.Insert(&item);
                    // The actual encoding that RTF uses for the portion is defined by the font
                    if (auto i = GetFontIndex(item, aFontTable);
                        i < aFontTable.size()
                        && aFontTable[i]->GetCharSet() != RTL_TEXTENCODING_DONTKNOW)
                        actEncoding = aFontTable[i]->GetCharSet();
                    aAttribItems.Insert( &aAttribs.Get( GetScriptItemId( EE_CHAR_FONTHEIGHT, nScriptType ) ) );
                    aAttribItems.Insert( &aAttribs.Get( GetScriptItemId( EE_CHAR_WEIGHT, nScriptType ) ) );
                    aAttribItems.Insert( &aAttribs.Get( GetScriptItemId( EE_CHAR_ITALIC, nScriptType ) ) );
                    aAttribItems.Insert( &aAttribs.Get( GetScriptItemId( EE_CHAR_LANGUAGE, nScriptType ) ) );

                    // tdf#119192: Write these font attributes at paragraph scope, so they can be
                    // reused across multiple runs.
                    WriteItemListAsRTF(aAttribItems, rOutput, nNode, nIndex, aFontTable,
                                       aColorList);
                    aAttribItems.Clear();
                }
                // Insert hard attribs AFTER CJK attribs...
                lcl_FindValidAttribs( aAttribItems, pNode, nIndex, nScriptTypeI18N );

                rOutput.WriteChar( '{' );
                if ( WriteItemListAsRTF( aAttribItems, rOutput, nNode, nIndex, aFontTable, aColorList ) )
                    rOutput.WriteChar( ' ' );

                sal_Int32 nS = nIndex;
                sal_Int32 nE = nIndex + rTextPortion.GetLen();
                if ( n == nStartPortion )
                    nS = nStartPos;
                if ( n == nEndPortion )
                    nE = nEndPos;

                OUString aRTFStr = EditDoc::GetParaAsString( pNode, nS, nE);
                RTFOutFuncs::Out_String(rOutput, aRTFStr, actEncoding);
                rOutput.WriteChar( '}' );
            }
            if ( bFinishPortion )
            {
                rOutput.WriteChar( '}' );
                bFinishPortion = false;
            }

            nIndex = nIndex + rTextPortion.GetLen();
        }

        rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_PAR ).WriteOString( OOO_STRING_SVTOOLS_RTF_PARD ).WriteOString( OOO_STRING_SVTOOLS_RTF_PLAIN );
        rOutput << endl;
    }
    // RTF-trailer ...
    rOutput.WriteOString( "}}" );    // 1xparentheses paragraphs, 1xparentheses RTF document

    aFontTable.clear();

    return rOutput.GetError();
}


void ImpEditEngine::WriteItemAsRTF( const SfxPoolItem& rItem, SvStream& rOutput, sal_Int32 nPara, sal_Int32 nPos,
                            const std::vector<std::unique_ptr<SvxFontItem>>& rFontTable, SvxColorList& rColorList )
{
    sal_uInt16 nWhich = rItem.Which();
    switch ( nWhich )
    {
        case EE_PARA_WRITINGDIR:
        {
            const SvxFrameDirectionItem& rWritingMode = static_cast<const SvxFrameDirectionItem&>(rItem);
            if ( rWritingMode.GetValue() == SvxFrameDirection::Horizontal_RL_TB )
                rOutput.WriteOString( "\\rtlpar" );
            else
                rOutput.WriteOString( "\\ltrpar" );
        }
        break;
        case EE_PARA_OUTLLEVEL:
        {
            sal_Int32 nLevel = static_cast<const SfxInt16Item&>(rItem).GetValue();
            if( nLevel >= 0 )
            {
                rOutput.WriteOString( "\\level" );
                rOutput.WriteNumberAsString( nLevel );
            }
        }
        break;
        case EE_PARA_OUTLLRSPACE:
        case EE_PARA_LRSPACE:
        {
            rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_FI );
            sal_Int32 nTxtFirst
                = static_cast<const SvxLRSpaceItem&>(rItem).ResolveTextFirstLineOffset({});
            nTxtFirst = LogicToTwips( nTxtFirst );
            rOutput.WriteNumberAsString( nTxtFirst );
            rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_LI );
            sal_uInt32 nTxtLeft = static_cast<const SvxLRSpaceItem&>(rItem).ResolveTextLeft({});
            nTxtLeft = static_cast<sal_uInt32>(LogicToTwips( nTxtLeft ));
            rOutput.WriteNumberAsString( nTxtLeft );
            rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_RI );
            sal_uInt32 nTxtRight = static_cast<const SvxLRSpaceItem&>(rItem).ResolveRight({});
            nTxtRight = LogicToTwips( nTxtRight);
            rOutput.WriteNumberAsString( nTxtRight );
        }
        break;
        case EE_PARA_ULSPACE:
        {
            rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_SB );
            sal_uInt32 nUpper = static_cast<const SvxULSpaceItem&>(rItem).GetUpper();
            nUpper = static_cast<sal_uInt32>(LogicToTwips( nUpper ));
            rOutput.WriteNumberAsString( nUpper );
            rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_SA );
            sal_uInt32 nLower = static_cast<const SvxULSpaceItem&>(rItem).GetLower();
            nLower = LogicToTwips( nLower );
            rOutput.WriteNumberAsString( nLower );
        }
        break;
        case EE_PARA_SBL:
        {
            rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_SL );
            sal_Int32 nVal = static_cast<const SvxLineSpacingItem&>(rItem).GetLineHeight();
            char cMult = '0';
            if ( static_cast<const SvxLineSpacingItem&>(rItem).GetInterLineSpaceRule() == SvxInterLineSpaceRule::Prop )
            {
                // From where do I get the value now?
                // The SwRTF parser is based on a 240 Font!
                nVal = static_cast<const SvxLineSpacingItem&>(rItem).GetPropLineSpace();
                nVal *= 240;
                nVal /= 100;
                cMult = '1';
            }
            rOutput.WriteNumberAsString( nVal );
            rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_SLMULT ).WriteChar( cMult );
        }
        break;
        case EE_PARA_JUST:
        {
            SvxAdjust eJustification = static_cast<const SvxAdjustItem&>(rItem).GetAdjust();
            switch ( eJustification )
            {
                case SvxAdjust::Center: rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_QC );
                                        break;
                case SvxAdjust::Right:  rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_QR );
                                        break;
                default:                rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_QL );
                                        break;
            }
        }
        break;
        case EE_PARA_TABS:
        {
            const SvxTabStopItem& rTabs = static_cast<const SvxTabStopItem&>(rItem);
            for ( sal_uInt16 i = 0; i < rTabs.Count(); i++ )
            {
                const SvxTabStop& rTab = rTabs[i];
                rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_TX );
                rOutput.WriteNumberAsString( LogicToTwips( rTab.GetTabPos() ) );
            }
        }
        break;
        case EE_CHAR_COLOR:
        {
            SvxColorList::const_iterator const iter = std::find(
                    rColorList.begin(), rColorList.end(),
                    static_cast<SvxColorItem const&>(rItem).GetValue());
            assert(iter != rColorList.end());
            sal_uInt32 const n = iter - rColorList.begin();
            rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_CF );
            rOutput.WriteNumberAsString( n );
        }
        break;
        case EE_CHAR_FONTINFO:
        case EE_CHAR_FONTINFO_CJK:
        case EE_CHAR_FONTINFO_CTL:
        {
            rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_F );
            rOutput.WriteNumberAsString(GetFontIndex(rItem, rFontTable));
        }
        break;
        case EE_CHAR_FONTHEIGHT:
        case EE_CHAR_FONTHEIGHT_CJK:
        case EE_CHAR_FONTHEIGHT_CTL:
        {
            rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_FS );
            sal_Int32 nHeight = static_cast<const SvxFontHeightItem&>(rItem).GetHeight();
            nHeight = LogicToTwips( nHeight );
            // Twips => HalfPoints
            nHeight /= 10;
            rOutput.WriteNumberAsString( nHeight );
        }
        break;
        case EE_CHAR_WEIGHT:
        case EE_CHAR_WEIGHT_CJK:
        case EE_CHAR_WEIGHT_CTL:
        {
            FontWeight e = static_cast<const SvxWeightItem&>(rItem).GetWeight();
            switch ( e )
            {
                case WEIGHT_BOLD:   rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_B );                break;
                default:            rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_B ).WriteChar( '0' );     break;
            }
        }
        break;
        case EE_CHAR_UNDERLINE:
        {
            // Must underlined if in WordLineMode, but the information is
            // missing here
            FontLineStyle e = static_cast<const SvxUnderlineItem&>(rItem).GetLineStyle();
            switch ( e )
            {
                case LINESTYLE_NONE:    rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_ULNONE );       break;
                case LINESTYLE_SINGLE:  rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_UL );       break;
                case LINESTYLE_DOUBLE:  rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_ULDB );     break;
                case LINESTYLE_DOTTED:  rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_ULD );      break;
                default:
                    break;
            }
        }
        break;
        case EE_CHAR_OVERLINE:
        {
            FontLineStyle e = static_cast<const SvxOverlineItem&>(rItem).GetLineStyle();
            switch ( e )
            {
                case LINESTYLE_NONE:    rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_OLNONE );       break;
                case LINESTYLE_SINGLE:  rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_OL );       break;
                case LINESTYLE_DOUBLE:  rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_OLDB );     break;
                case LINESTYLE_DOTTED:  rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_OLD );      break;
                default:
                    break;
            }
        }
        break;
        case EE_CHAR_STRIKEOUT:
        {
            FontStrikeout e = static_cast<const SvxCrossedOutItem&>(rItem).GetStrikeout();
            switch ( e )
            {
                case STRIKEOUT_SINGLE:
                case STRIKEOUT_DOUBLE:  rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_STRIKE );       break;
                case STRIKEOUT_NONE:    rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_STRIKE ).WriteChar'0' );    break;
                default:
                    break;
            }
        }
        break;
        case EE_CHAR_ITALIC:
        case EE_CHAR_ITALIC_CJK:
        case EE_CHAR_ITALIC_CTL:
        {
            FontItalic e = static_cast<const SvxPostureItem&>(rItem).GetPosture();
            switch ( e )
            {
                case ITALIC_OBLIQUE:
                case ITALIC_NORMAL: rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_I );        break;
                case ITALIC_NONE:   rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_I ).WriteChar( '0' ); break;
                default:
                    break;
            }
        }
        break;
        case EE_CHAR_OUTLINE:
        {
            rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_OUTL );
            if ( !static_cast<const SvxContourItem&>(rItem).GetValue() )
                rOutput.WriteChar( '0' );
        }
        break;
        case EE_CHAR_RELIEF:
        {
            FontRelief nRelief = static_cast<const SvxCharReliefItem&>(rItem).GetValue();
            if ( nRelief == FontRelief::Embossed )
                rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_EMBO );
            if ( nRelief == FontRelief::Engraved )
                rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_IMPR );
        }
        break;
        case EE_CHAR_EMPHASISMARK:
        {
            FontEmphasisMark nMark = static_cast<const SvxEmphasisMarkItem&>(rItem).GetEmphasisMark();
            if ( nMark == FontEmphasisMark::NONE )
                rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_ACCNONE );
            else if ( nMark == (FontEmphasisMark::Accent | FontEmphasisMark::PosAbove) )
                rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_ACCCOMMA );
            else
                rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_ACCDOT );
        }
        break;
        case EE_CHAR_SHADOW:
        {
            rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_SHAD );
            if ( !static_cast<const SvxShadowedItem&>(rItem).GetValue() )
                rOutput.WriteChar( '0' );
        }
        break;
        case EE_FEATURE_TAB:
        {
            rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_TAB );
        }
        break;
        case EE_FEATURE_LINEBR:
        {
            rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_LINE );
        }
        break;
        case EE_CHAR_KERNING:
        {
            rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_EXPNDTW );
            rOutput.WriteNumberAsString( LogicToTwips(
                static_cast<const SvxKerningItem&>(rItem).GetValue() ) );
        }
        break;
        case EE_CHAR_PAIRKERNING:
        {
            rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_KERNING );
            rOutput.WriteNumberAsString( static_cast<const SvxAutoKernItem&>(rItem).GetValue() ? 1 : 0 );
        }
        break;
        case EE_CHAR_ESCAPEMENT:
        {
            SvxFont aFont;
            ContentNode* pNode = maEditDoc.GetObject( nPara );
            SeekCursor( pNode, nPos, aFont );
            MapMode aPntMode( MapUnit::MapPoint );
            tools::Long nFontHeight = GetRefDevice()->LogicToLogic(
                    aFont.GetFontSize(), &GetRefMapMode(), &aPntMode ).Height();
            nFontHeight *=2;    // Half Points
            sal_uInt16 const nProp = static_cast<const SvxEscapementItem&>(rItem).GetProportionalHeight();
            sal_uInt16 nProp100 = nProp*100;    // For SWG-Token Prop in 100th percent.
            short nEsc = static_cast<const SvxEscapementItem&>(rItem).GetEsc();
            const FontMetric aFontMetric = GetRefDevice()->GetFontMetric();
            double fFontHeight = aFontMetric.GetAscent() + aFontMetric.GetDescent();
            double fAutoAscent = .8;
            double fAutoDescent = .2;
            if ( fFontHeight )
            {
                fAutoAscent = aFontMetric.GetAscent() / fFontHeight;
                fAutoDescent = aFontMetric.GetDescent() / fFontHeight;
            }
            if ( nEsc == DFLT_ESC_AUTO_SUPER )
            {
                nEsc =  fAutoAscent * (100 - nProp);
                nProp100++; // A 1 afterwards means 'automatic'.
            }
            else if ( nEsc == DFLT_ESC_AUTO_SUB )
            {
                nEsc =  fAutoDescent * -(100 - nProp);
                nProp100++;
            }
            // SWG:
            if ( nEsc )
            {
                rOutput.WriteOString( "{\\*\\updnprop" ).WriteNumberAsString(
                    nProp100 ).WriteChar( '}' );
            }
            tools::Long nUpDown = nFontHeight * std::abs( nEsc ) / 100;
            if ( nEsc < 0 )
                rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_DN );
            else if ( nEsc > 0 )
                rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_UP );
            rOutput.WriteNumberAsString(nUpDown);
        }
        break;
        case EE_CHAR_CASEMAP:
        {
            const SvxCaseMapItem& rCaseMap = static_cast<const SvxCaseMapItem&>(rItem);
            switch (rCaseMap.GetValue())
            {
                case SvxCaseMap::SmallCaps:
                    rOutput.WriteOString(OOO_STRING_SVTOOLS_RTF_SCAPS);
                    break;
                case SvxCaseMap::Uppercase:
                    rOutput.WriteOString(OOO_STRING_SVTOOLS_RTF_CAPS);
                    break;
                default// Something that rtf does not support
                    rOutput.WriteOString(OOO_STRING_SVTOOLS_RTF_SCAPS);
                    rOutput.WriteNumberAsString(0);
                    rOutput.WriteOString(OOO_STRING_SVTOOLS_RTF_CAPS);
                    rOutput.WriteNumberAsString(0);
                    break;
            }
        }
        break;
    }
}

// Currently not good enough to be used for a ::Write of EETextFormat::Html, it
// only supports hyperlinks over plain text
OString ImpEditEngine::GetSimpleHtml() const
{
    OStringBuffer aOutput;

    sal_Int32 nStartNode = 0;
    sal_Int32 nEndNode = maEditDoc.Count()-1;

    // iterate over the paragraphs ...
    for (sal_Int32 nNode = nStartNode; nNode <= nEndNode; nNode++)
    {
        const ContentNode* pNode = maEditDoc.GetObject( nNode );

        const ParaPortion* pParaPortion = FindParaPortion( pNode );

        sal_Int32 nIndex = 0;
        sal_Int32 nEndPortion = pParaPortion->GetTextPortions().Count() - 1;

        OStringBuffer aPara;
        for (sal_Int32 n = 0; n <= nEndPortion; n++)
        {
            const TextPortion& rTextPortion = pParaPortion->GetTextPortions()[n];

            const SvxURLField* pURLField = nullptr;
            if ( rTextPortion.GetKind() == PortionKind::FIELD )
            {
                const EditCharAttrib* pAttr = pNode->GetCharAttribs().FindFeature(nIndex);
                const SvxFieldItem* pFieldItem = dynamic_cast<const SvxFieldItem*>(pAttr->GetItem());
                if( pFieldItem )
                {
                    const SvxFieldData* pFieldData = pFieldItem->GetField();
                    pURLField = dynamic_cast<const SvxURLField*>(pFieldData);
                }
            }

            OUString aRTFStr = EditDoc::GetParaAsString(pNode, nIndex, nIndex + rTextPortion.GetLen());
            if (pURLField)
                aPara.append("" + HTMLOutFuncs::ConvertStringToHTML(pURLField->GetURL()) + "\">");

            aPara.append(HTMLOutFuncs::ConvertStringToHTML(aRTFStr));

            if (pURLField)
                aPara.append("
"
);

            nIndex = nIndex + rTextPortion.GetLen();
        }

        if (aPara.isEmpty())
        {
            if (nEndNode == 0) // only one empty blank line
                break;
            aPara.append("
"
);
        }
        aOutput.append("
" + aPara + "
"
);
    }

    return aOutput.makeStringAndClear();
}

std::unique_ptr<EditTextObject> ImpEditEngine::GetEmptyTextObject()
{
    EditSelection aEmptySel;
    aEmptySel.Min() = maEditDoc.GetStartPaM();
    aEmptySel.Max() = maEditDoc.GetStartPaM();

    return CreateTextObject( aEmptySel );
}

std::unique_ptr<EditTextObject> ImpEditEngine::CreateTextObject( sal_Int32 nPara, sal_Int32 nParas )
{
    DBG_ASSERT(0 <= nPara && nPara < maEditDoc.Count(), "CreateTextObject: Startpara out of Range");
    DBG_ASSERT(nParas <= maEditDoc.Count() - nPara, "CreateTextObject: Endpara out of Range");

    ContentNode* pStartNode = maEditDoc.GetObject(nPara);
    ContentNode* pEndNode = maEditDoc.GetObject(nPara + nParas - 1);
    DBG_ASSERT( pStartNode, "Start-Paragraph does not exist: CreateTextObject" );
    DBG_ASSERT( pEndNode, "End-Paragraph does not exist: CreateTextObject" );

    if ( pStartNode && pEndNode )
    {
        EditSelection aTmpSel;
        aTmpSel.Min() = EditPaM( pStartNode, 0 );
        aTmpSel.Max() = EditPaM( pEndNode, pEndNode->Len() );
        return CreateTextObject(aTmpSel);
    }
    return nullptr;
}

std::unique_ptr<EditTextObject> ImpEditEngine::CreateTextObject()
{
    EditSelection aCompleteSelection;
    aCompleteSelection.Min() = maEditDoc.GetStartPaM();
    aCompleteSelection.Max() = maEditDoc.GetEndPaM();

    return CreateTextObject( aCompleteSelection );
}

std::unique_ptr<EditTextObject> ImpEditEngine::CreateTextObject(const EditSelection&&nbsp;rSel)
{
    return CreateTextObject(rSel, GetEditTextObjectPool(), maStatus.AllowBigObjects(), mnBigTextObjectStart);
}

std::unique_ptr<EditTextObject> ImpEditEngine::CreateTextObject( EditSelection aSel, SfxItemPool* pPool, bool bAllowBigObjects, sal_Int32 nBigObjectStart )
{
    sal_Int32 nStartNode, nEndNode;
    sal_Int32 nTextPortions = 0;

    aSel.Adjust( maEditDoc );
    nStartNode = maEditDoc.GetPos( aSel.Min().GetNode() );
    nEndNode = maEditDoc.GetPos( aSel.Max().GetNode() );

    bool bOnlyFullParagraphs = !( aSel.Min().GetIndex() ||
        ( aSel.Max().GetIndex() < aSel.Max().GetNode()->Len() ) );

    // Templates are not saved!
    // (Only the name and family, template itself must be in App!)

    const MapUnit eMapUnit = maEditDoc.GetItemPool().GetMetric(DEF_METRIC);
    auto pTxtObj(std::make_unique<EditTextObjectImpl>(pPool, eMapUnit, GetVertical(), GetRotation(),
                                                      GetItemScriptType(aSel)));

    // iterate over the paragraphs ...
    sal_Int32 nNode;
    for ( nNode = nStartNode; nNode <= nEndNode; nNode++  )
    {
        ContentNode* pNode = maEditDoc.GetObject( nNode );
        DBG_ASSERT( pNode, "Node not found: Search&Replace" );

        if ( bOnlyFullParagraphs )
        {
            nTextPortions += GetParaPortions().getRef(nNode).GetTextPortions().Count();
        }

        sal_Int32 nStartPos = 0;
        sal_Int32 nEndPos = pNode->Len();

        bool bEmptyPara = nEndPos == 0;

        if ( ( nNode == nStartNode ) && !bOnlyFullParagraphs )
            nStartPos = aSel.Min().GetIndex();
        if ( ( nNode == nEndNode ) && !bOnlyFullParagraphs )
            nEndPos = aSel.Max().GetIndex();


        ContentInfo *pC = pTxtObj->CreateAndInsertContent();

        // The paragraph attributes ...
        pC->GetParaAttribs().Set( pNode->GetContentAttribs().GetItems() );

        // The StyleSheet...
        if ( pNode->GetStyleSheet() )
        {
            pC->SetStyle(pNode->GetStyleSheet()->GetName());
            pC->SetFamily(pNode->GetStyleSheet()->GetFamily());
        }

        // The Text...
        pC->SetText(pNode->Copy(nStartPos, nEndPos-nStartPos));
        auto& rCAttriblist = pC->GetCharAttribs();

        // and the Attribute...
        std::size_t nAttr = 0;
        EditCharAttrib* pAttr = GetAttrib( pNode->GetCharAttribs().GetAttribs(), nAttr );
        rCAttriblist.reserve(rCAttriblist.size() + pNode->GetCharAttribs().GetAttribs().size());
        while ( pAttr )
        {
            // In a blank paragraph keep the attributes!
            if ( bEmptyPara ||
                 ( ( pAttr->GetEnd() > nStartPos ) && ( pAttr->GetStart() < nEndPos ) ) )
            {
                XEditAttribute aX = pTxtObj->CreateAttrib(*pAttr->GetItem(), pAttr->GetStart(), pAttr->GetEnd());
                // Possibly Correct ...
                if ( ( nNode == nStartNode ) && ( nStartPos != 0 ) )
                {
                    aX.GetStart() = ( aX.GetStart() > nStartPos ) ? aX.GetStart()-nStartPos : 0;
                    aX.GetEnd() = aX.GetEnd() - nStartPos;

                }
                if ( nNode == nEndNode )
                {
                    if ( aX.GetEnd() > (nEndPos-nStartPos) )
                        aX.GetEnd() = nEndPos-nStartPos;
                }
                DBG_ASSERT( aX.GetEnd() <= (nEndPos-nStartPos), "CreateTextObject: Attribute too long!" );
                if ( aX.GetLen() || bEmptyPara )
                    rCAttriblist.push_back(std::move(aX));
            }
            nAttr++;
            pAttr = GetAttrib( pNode->GetCharAttribs().GetAttribs(), nAttr );
        }

        // If possible online spelling
        if ( bAllowBigObjects && bOnlyFullParagraphs && pNode->GetWrongList() )
            pC->SetWrongList( pNode->GetWrongList()->Clone() );

    }

    // Remember the portions info in case of large text objects:
    // sleeper set up when Olli paragraphs not hacked!
    if ( bAllowBigObjects && bOnlyFullParagraphs && IsFormatted() && IsUpdateLayout() && ( nTextPortions >= nBigObjectStart ) )
    {
        XParaPortionList* pXList = new XParaPortionList(GetRefDevice(), GetColumnWidth(maPaperSize),
            maScalingParameters.fFontX, maScalingParameters.fFontY,
            maScalingParameters.fSpacingX, maScalingParameters.fSpacingY);
        pTxtObj->SetPortionInfo(std::unique_ptr<XParaPortionList>(pXList));
        for ( nNode = nStartNode; nNode <= nEndNode; nNode++  )
        {
            ParaPortion const& rParaPortion = GetParaPortions().getRef(nNode);
            XParaPortion* pX = new XParaPortion;
            pXList->push_back(pX);

            pX->nHeight = rParaPortion.GetHeight();
            pX->nFirstLineOffset = rParaPortion.GetFirstLineOffset();

            // The TextPortions
            sal_uInt16 nCount = rParaPortion.GetTextPortions().Count();
            sal_uInt16 n;
            for ( n = 0; n < nCount; n++ )
            {
                const TextPortion& rTextPortion = rParaPortion.GetTextPortions()[n];
                TextPortion* pNew = new TextPortion( rTextPortion );
                pX->aTextPortions.Append(pNew);
            }

            // The lines
            nCount = rParaPortion.GetLines().Count();
            for ( n = 0; n < nCount; n++ )
            {
                const EditLine& rLine = rParaPortion.GetLines()[n];
                pX->aLines.Append(std::unique_ptr<EditLine>(rLine.Clone()));
            }
#ifdef DBG_UTIL
            sal_uInt16 nTest;
            int nTPLen = 0, nTxtLen = 0;
            for (nTest = rParaPortion.GetTextPortions().Count(); nTest;)
                nTPLen += rParaPortion.GetTextPortions()[--nTest].GetLen();
            for (nTest = rParaPortion.GetLines().Count(); nTest; )
                nTxtLen += rParaPortion.GetLines()[--nTest].GetLen();
            DBG_ASSERT(nTPLen == rParaPortion.GetNode()->Len() && nTxtLen == rParaPortion.GetNode()->Len(), "CreateBinTextObject: ParaPortion not completely formatted!");
#endif
        }
    }
    return pTxtObj;
}

void ImpEditEngine::SetText( const EditTextObject& rTextObject )
{
    // Since setting a text object is not undo-able!
    ResetUndoManager();
    bool _bUpdate = IsUpdateLayout();
    bool _bUndo = IsUndoEnabled();

    SetText( OUString() );
    EditPaM aPaM = maEditDoc.GetStartPaM();

    SetUpdateLayout( false );
    EnableUndo( false );

    InsertText( rTextObject, EditSelection( aPaM, aPaM ) );
    SetVertical(rTextObject.GetVertical());
    SetRotation(rTextObject.GetRotation());

    DBG_ASSERT( !HasUndoManager() || !GetUndoManager().GetUndoActionCount(), "From where comes the Undo in SetText ?!" );
    SetUpdateLayout( _bUpdate );
    EnableUndo( _bUndo );
}

EditSelection ImpEditEngine::InsertText( const EditTextObject& rTextObject, EditSelection aSel )
{
    aSel.Adjust( maEditDoc );
    if ( aSel.HasRange() )
        aSel = ImpDeleteSelection( aSel );
    EditSelection aNewSel = InsertTextObject( rTextObject, aSel.Max() );
    return aNewSel;
}

EditSelection ImpEditEngine::InsertTextObject( const EditTextObject& rTextObject, EditPaM aPaM )
{
    // Optimize: No getPos undFindParaportion, instead calculate index!
    EditSelection aSel( aPaM, aPaM );
    DBG_ASSERT( !aSel.DbgIsBuggy( maEditDoc ), "InsertBibTextObject: Selection broken!(1)" );

    bool bUsePortionInfo = false;
    const EditTextObjectImpl& rTextObjectImpl = toImpl(rTextObject);
    XParaPortionList* pPortionInfo = rTextObjectImpl.GetPortionInfo();

    if (pPortionInfo && ( static_cast<tools::Long>(pPortionInfo->GetPaperWidth()) == GetColumnWidth(maPaperSize))
            && pPortionInfo->GetRefMapMode() == GetRefDevice()->GetMapMode()
            && pPortionInfo->getFontScaleX() == maScalingParameters.fFontX
            && pPortionInfo->getFontScaleY() == maScalingParameters.fFontY
            && pPortionInfo->getSpacingScaleX() == maScalingParameters.fSpacingX
            && pPortionInfo->getSpacingScaleY() == maScalingParameters.fSpacingY)
    {
        if ( (pPortionInfo->GetRefDevPtr() == GetRefDevice()) ||
             (pPortionInfo->RefDevIsVirtual() && GetRefDevice()->IsVirtual()) )
            bUsePortionInfo = true;
    }

    bool bConvertMetricOfItems = false;
    MapUnit eSourceUnit = MapUnit(), eDestUnit = MapUnit();
    if (rTextObjectImpl.HasMetric())
    {
        eSourceUnit = rTextObjectImpl.GetMetric();
        eDestUnit = maEditDoc.GetItemPool().GetMetric( DEF_METRIC );
        if ( eSourceUnit != eDestUnit )
            bConvertMetricOfItems = true;
    }

    // Before, paragraph count was of type sal_uInt16 so if nContents exceeded
    // 0xFFFF this wouldn't have worked anyway, given that nPara is used to
    // number paragraphs and is fearlessly incremented.
    sal_Int32 nContents = static_cast<sal_Int32>(rTextObjectImpl.GetContents().size());
    SAL_WARN_IF( nContents < 0, "editeng""ImpEditEngine::InsertTextObject - contents overflow " << nContents);
    sal_Int32 nPara = maEditDoc.GetPos( aPaM.GetNode() );

    for (sal_Int32 n = 0; n < nContents; ++n, ++nPara)
    {
        const ContentInfo* pC = rTextObjectImpl.GetContents()[n].get();
        bool bNewContent = aPaM.GetNode()->Len() == 0;
        const sal_Int32 nStartPos = aPaM.GetIndex();

        aPaM = ImpFastInsertText( aPaM, pC->GetText() );

        ParaPortion* pPortion = FindParaPortion( aPaM.GetNode() );
        DBG_ASSERT( pPortion, "Blind Portion in FastInsertText" );
        pPortion->MarkInvalid( nStartPos, pC->GetText().getLength() );

        // Character attributes ...
        bool bAllreadyHasAttribs = aPaM.GetNode()->GetCharAttribs().Count() != 0;
        size_t nNewAttribs = pC->GetCharAttribs().size();
        if ( nNewAttribs )
        {
            bool bUpdateFields = false;
            for (size_t nAttr = 0; nAttr < nNewAttribs; ++nAttr)
            {
                const XEditAttribute& rX = pC->GetCharAttribs()[nAttr];
                // Can happen when paragraphs > 16K, it is simply wrapped.
                    //TODO! Still true, still needed?
                if ( rX.GetEnd() <= aPaM.GetNode()->Len() )
                {
                    if ( !bAllreadyHasAttribs || rX.IsFeature() )
                    {
                        // Normal attributes then go faster ...
                        // Features shall not be inserted through
                        // EditDoc:: InsertAttrib, using FastInsertText they are
                        // already in the flow
                        DBG_ASSERT( rX.GetEnd() <= aPaM.GetNode()->Len(), "InsertBinTextObject: Attribute too large!" );
                        EditCharAttrib* pAttr;
                        if ( !bConvertMetricOfItems )
                            pAttr = MakeCharAttrib( maEditDoc.GetItemPool(), *(rX.GetItem()), rX.GetStart()+nStartPos, rX.GetEnd()+nStartPos );
                        else
                        {
                            std::unique_ptr<SfxPoolItem> pNew(rX.GetItem()->Clone());
                            ConvertItem( pNew, eSourceUnit, eDestUnit );
                            pAttr = MakeCharAttrib( maEditDoc.GetItemPool(), *pNew, rX.GetStart()+nStartPos, rX.GetEnd()+nStartPos );
                        }
                        DBG_ASSERT( pAttr->GetEnd() <= aPaM.GetNode()->Len(), "InsertBinTextObject: Attribute does not fit! (1)" );
                        aPaM.GetNode()->GetCharAttribs().InsertAttrib( pAttr );
                        if ( pAttr->Which() == EE_FEATURE_FIELD )
                            bUpdateFields = true;
                    }
                    else
                    {
                        DBG_ASSERT( rX.GetEnd()+nStartPos <= aPaM.GetNode()->Len(), "InsertBinTextObject: Attribute does not fit! (2)" );
                        // Tabs and other Features can not be inserted through InsertAttrib:
                        maEditDoc.InsertAttrib( aPaM.GetNode(), rX.GetStart()+nStartPos, rX.GetEnd()+nStartPos, *rX.GetItem() );
                    }
                }
            }
            if ( bUpdateFields )
                UpdateFields();

            // Otherwise, quick format => no attributes!
            pPortion->MarkSelectionInvalid( nStartPos );
        }

#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
        CharAttribList::DbgCheckAttribs(aPaM.GetNode()->GetCharAttribs());
#endif

        bool bParaAttribs = false;
        if ( bNewContent || ( ( n > 0 ) && ( n < (nContents-1) ) ) )
        {
            // only style and ParaAttribs when new paragraph, or
            // completely internal ...
            bParaAttribs = pC->GetParaAttribs().Count() != 0;
            if ( GetStyleSheetPool() && pC->GetStyle().getLength() )
            {
                SfxStyleSheet* pStyle = static_cast<SfxStyleSheet*>(GetStyleSheetPool()->Find( pC->GetStyle(), pC->GetFamily() ));
                DBG_ASSERT( pStyle, "InsertBinTextObject - Style not found!" );
                SetStyleSheet( nPara, pStyle );
            }
            if ( !bConvertMetricOfItems )
                SetParaAttribs( maEditDoc.GetPos( aPaM.GetNode() ), pC->GetParaAttribs() );
            else
            {
                SfxItemSet aAttribs( GetEmptyItemSet() );
                ConvertAndPutItems( aAttribs, pC->GetParaAttribs(), &eSourceUnit, &eDestUnit );
                SetParaAttribs( maEditDoc.GetPos( aPaM.GetNode() ), aAttribs );
            }
            if ( bNewContent && bUsePortionInfo )
            {
                const XParaPortion& rXP = (*pPortionInfo)[n];
                ParaPortion* pParaPortion = GetParaPortions().SafeGetObject(nPara);
                DBG_ASSERT( pParaPortion, "InsertBinTextObject: ParaPortion?" );
                pParaPortion->mnHeight = rXP.nHeight;
                pParaPortion->mnFirstLineOffset = rXP.nFirstLineOffset;
                pParaPortion->mbForceRepaint = true;
                pParaPortion->SetValid();   // Do not format

                // The Text Portions
                pParaPortion->GetTextPortions().Reset();
                sal_uInt16 nCount = rXP.aTextPortions.Count();
                for ( sal_uInt16 _n = 0; _n < nCount; _n++ )
                {
                    const TextPortion& rTextPortion = rXP.aTextPortions[_n];
                    TextPortion* pNew = new TextPortion( rTextPortion );
                    pParaPortion->GetTextPortions().Append(pNew);
                }

                // The lines
                pParaPortion->GetLines().Reset();
                nCount = rXP.aLines.Count();
                for ( sal_uInt16 m = 0; m < nCount; m++ )
                {
                    const EditLine& rLine = rXP.aLines[m];
                    EditLine* pNew = rLine.Clone();
                    pNew->SetInvalid(); // Paint again!
                    pParaPortion->GetLines().Append(std::unique_ptr<EditLine>(pNew));
                }
#ifdef DBG_UTIL
                sal_uInt16 nTest;
                int nTPLen = 0, nTxtLen = 0;
                for ( nTest = pParaPortion->GetTextPortions().Count(); nTest; )
                    nTPLen += pParaPortion->GetTextPortions()[--nTest].GetLen();
                for ( nTest = pParaPortion->GetLines().Count(); nTest; )
                    nTxtLen += pParaPortion->GetLines()[--nTest].GetLen();
                DBG_ASSERT( ( nTPLen == pParaPortion->GetNode()->Len() ) && ( nTxtLen == pParaPortion->GetNode()->Len() ), "InsertTextObject: ParaPortion not completely formatted!" );
#endif
            }
        }
        if ( !bParaAttribs ) // DefFont is not calculated for FastInsertParagraph
        {
            aPaM.GetNode()->GetCharAttribs().GetDefFont() = maEditDoc.GetDefFont();
            if (maStatus.UseCharAttribs())
                aPaM.GetNode()->CreateDefFont();
        }

        if ( bNewContent && GetStatus().DoOnlineSpelling() && pC->GetWrongList() )
        {
            aPaM.GetNode()->SetWrongList( pC->GetWrongList()->Clone() );
        }

        // Wrap when followed by other ...
        if ( n < ( nContents-1) )
        {
            if ( bNewContent )
                aPaM = ImpFastInsertParagraph( nPara+1 );
            else
                aPaM = ImpInsertParaBreak( aPaM, false );
        }
    }

    aSel.Max() = aPaM;
    DBG_ASSERT( !aSel.DbgIsBuggy( maEditDoc ), "InsertBibTextObject: Selection broken!(1)" );
    return aSel;
}

void ImpEditEngine::GetAllMisspellRanges( std::vector<editeng::MisspellRanges>& rRanges ) const
{
    std::vector<editeng::MisspellRanges> aRanges;
    const EditDoc& rDoc = GetEditDoc();
    for (sal_Int32 i = 0, n = rDoc.Count(); i < n; ++i)
    {
        const ContentNode* pNode = rDoc.GetObject(i);
        const WrongList* pWrongList = pNode->GetWrongList();
        if (!pWrongList)
            continue;

        aRanges.emplace_back(i, std::vector(pWrongList->GetRanges()));
    }

    aRanges.swap(rRanges);
}

void ImpEditEngine::SetAllMisspellRanges( const std::vector<editeng::MisspellRanges>&&nbsp;rRanges )
{
    EditDoc& rDoc = GetEditDoc();
    for (auto const& rParaRanges : rRanges)
    {
        ContentNode* pNode = rDoc.GetObject(rParaRanges.mnParagraph);
        if (!pNode)
            continue;

        pNode->CreateWrongList();
        WrongList* pWrongList = pNode->GetWrongList();
        pWrongList->SetRanges(std::vector(rParaRanges.maRanges));
    }
}

editeng::LanguageSpan ImpEditEngine::GetLanguage( sal_Int32 nPara, sal_Int32 nPos )
{
    ContentNode* pNode = maEditDoc.GetObject( nPara );
    DBG_ASSERT( pNode, "GetLanguage - nPara is invalid!" );
    return pNode ? GetLanguage( EditPaM( pNode, nPos ) ) : editeng::LanguageSpan{};
}

editeng::LanguageSpan ImpEditEngine::GetLanguage( const EditPaM& rPaM, sal_Int32* pEndPos ) const
{
    short nScriptTypeI18N = GetI18NScriptType( rPaM, pEndPos ); // pEndPos will be valid now, pointing to ScriptChange or NodeLen
    SvtScriptType nScriptType = SvtLanguageOptions::FromI18NToSvtScriptType(nScriptTypeI18N);
    sal_uInt16 nLangId = GetScriptItemId( EE_CHAR_LANGUAGE, nScriptType );
    const SvxLanguageItem* pLangItem = &static_cast<const SvxLanguageItem&>(rPaM.GetNode()->GetContentAttribs().GetItem( nLangId ));
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=94 H=89 G=91

¤ Dauer der Verarbeitung: 0.23 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge