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


Quelle  uwriter.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/.
 */


#include <sal/config.h>
#include <test/bootstrapfixture.hxx>

#include <com/sun/star/i18n/WordType.hpp>

#include <comphelper/processfactory.hxx>
#include <i18nutil/transliteration.hxx>
#include <editeng/adjustitem.hxx>
#include <editeng/boxitem.hxx>
#include <editeng/brushitem.hxx>
#include <editeng/colritem.hxx>
#include <editeng/contouritem.hxx>
#include <editeng/crossedoutitem.hxx>
#include <editeng/fhgtitem.hxx>
#include <editeng/fontitem.hxx>
#include <editeng/justifyitem.hxx>
#include <editeng/lineitem.hxx>
#include <editeng/postitem.hxx>
#include <editeng/shdditem.hxx>
#include <editeng/udlnitem.hxx>
#include <editeng/wghtitem.hxx>
#include <svl/intitem.hxx>
#include <svx/algitem.hxx>
#include <svx/rotmodit.hxx>
#include <tools/urlobj.hxx>
#include <unotools/tempfile.hxx>
#include <unotools/transliterationwrapper.hxx>

#include <editeng/langitem.hxx>
#include <editeng/charhiddenitem.hxx>

#include <sfx2/docfilt.hxx>
#include <sfx2/docfile.hxx>

#include <xmloff/odffields.hxx>

#include <breakit.hxx>
#include <doc.hxx>
#include <IDocumentRedlineAccess.hxx>
#include <IDocumentFieldsAccess.hxx>
#include <IDocumentStatistics.hxx>
#include <cellfml.hxx>
#include <docsh.hxx>
#include <docstat.hxx>
#include <docufld.hxx>
#include <fmtanchr.hxx>
#include <ndtxt.hxx>
#include <shellres.hxx>
#include <swscanner.hxx>
#include <swdll.hxx>
#include <swtypes.hxx>
#include <fmtftn.hxx>
#include <fmtrfmrk.hxx>
#include <fmtinfmt.hxx>
#include <fchrfmt.hxx>
#include <fmtfld.hxx>
#include <redline.hxx>
#include <docary.hxx>
#include <modeltoviewhelper.hxx>
#include <IMark.hxx>
#include <ring.hxx>
#include <calbck.hxx>
#include <pagedesc.hxx>
#include <calc.hxx>
#include <scriptinfo.hxx>
#include <rubylist.hxx>
#include <txatbase.hxx>

#include <tblafmt.hxx>
#include <unotbl.hxx>
#include <IDocumentMarkAccess.hxx>
#include <itabenum.hxx>

typedef rtl::Reference<SwDocShell> SwDocShellRef;

using namespace ::com::sun::star;

/* Implementation of Swdoc-Test class */

class SwDocTest : public test::BootstrapFixture
{
public:
    SwDocTest()
        : m_pDoc(nullptr)
    {
    }

    virtual void setUp() override;
    virtual void tearDown() override;

    void testTableAutoFormats();
    void testPageDescName();
    void testFileNameFields();
    void testDocStat();
    void testModelToViewHelperPassthrough();
    void testModelToViewHelperExpandFieldsExpandFootnote();
    void testModelToViewHelperExpandFieldsExpandFootnoteReplaceMode();
    void testModelToViewHelperExpandFields();
    void testModelToViewHelperExpandFieldsReplaceMode();
    void testModelToViewHelperExpandFieldsHideInvisible();
    void testModelToViewHelperExpandFieldsHideRedlined();
    void testModelToViewHelperExpandFieldsHideInvisibleExpandFootnote();
    void testModelToViewHelperExpandFieldsHideInvisibleExpandFootnoteReplaceMode();
    void testModelToViewHelperExpandFieldsHideHideRedlinedExpandFootnote();
    void testModelToViewHelperExpandFieldsHideHideRedlinedExpandFootnoteReplaceMode();
    void testModelToViewHelperHideInvisibleHideRedlined();
    void testModelToViewHelperExpandFieldsHideInvisibleHideRedlinedExpandFootnote();
    void testModelToViewHelperExpandFieldsHideInvisibleHideRedlinedExpandFootnoteReplaceMode();
    void testModelToViewHelperExpandFieldsExpandFootnote2();
    void testModelToViewHelperExpandFieldsExpandFootnoteReplaceMode2();
    void testSwScanner();
    void testUserPerceivedCharCount();
    void testMergePortionsDeleteNotSorted();
    void testGraphicAnchorDeletion();
    void testTransliterate();
    void testMarkMove();
    void testFormulas();
    void testIntrusiveRing();
    void testClientModify();
    void testBroadcastingModify();
    void testWriterMultiListener();
    void test64kPageDescs();
    void testTdf92308();
    void testTableCellComparison();
    void testFillRubyList();
    void testSetRubyList();

    CPPUNIT_TEST_SUITE(SwDocTest);

    CPPUNIT_TEST(testTransliterate);
    CPPUNIT_TEST(testTableAutoFormats);
    CPPUNIT_TEST(testPageDescName);
    CPPUNIT_TEST(testFileNameFields);
    CPPUNIT_TEST(testDocStat);
    CPPUNIT_TEST(testModelToViewHelperPassthrough);
    CPPUNIT_TEST(testModelToViewHelperExpandFieldsExpandFootnote);
    CPPUNIT_TEST(testModelToViewHelperExpandFieldsExpandFootnoteReplaceMode);
    CPPUNIT_TEST(testModelToViewHelperExpandFields);
    CPPUNIT_TEST(testModelToViewHelperExpandFieldsReplaceMode);
    CPPUNIT_TEST(testModelToViewHelperExpandFieldsHideInvisible);
    CPPUNIT_TEST(testModelToViewHelperExpandFieldsHideRedlined);
    CPPUNIT_TEST(testModelToViewHelperExpandFieldsHideInvisibleExpandFootnote);
    CPPUNIT_TEST(testModelToViewHelperExpandFieldsHideInvisibleExpandFootnoteReplaceMode);
    CPPUNIT_TEST(testModelToViewHelperExpandFieldsHideHideRedlinedExpandFootnote);
    CPPUNIT_TEST(testModelToViewHelperExpandFieldsHideHideRedlinedExpandFootnoteReplaceMode);
    CPPUNIT_TEST(testModelToViewHelperHideInvisibleHideRedlined);
    CPPUNIT_TEST(testModelToViewHelperExpandFieldsHideInvisibleHideRedlinedExpandFootnote);
    CPPUNIT_TEST(testModelToViewHelperExpandFieldsHideInvisibleHideRedlinedExpandFootnoteReplaceMode);
    CPPUNIT_TEST(testModelToViewHelperExpandFieldsExpandFootnote2);
    CPPUNIT_TEST(testModelToViewHelperExpandFieldsExpandFootnoteReplaceMode2);
    CPPUNIT_TEST(testSwScanner);
    CPPUNIT_TEST(testUserPerceivedCharCount);
    CPPUNIT_TEST(testMergePortionsDeleteNotSorted);
    CPPUNIT_TEST(testGraphicAnchorDeletion);
    CPPUNIT_TEST(testMarkMove);
    CPPUNIT_TEST(testFormulas);
    CPPUNIT_TEST(testIntrusiveRing);
    CPPUNIT_TEST(testClientModify);
    CPPUNIT_TEST(testBroadcastingModify);
    CPPUNIT_TEST(testWriterMultiListener);
    CPPUNIT_TEST(test64kPageDescs);
    CPPUNIT_TEST(testTdf92308);
    CPPUNIT_TEST(testTableCellComparison);
    CPPUNIT_TEST(testFillRubyList);
    CPPUNIT_TEST(testSetRubyList);
    CPPUNIT_TEST_SUITE_END();

private:
    SwDoc *m_pDoc;
    SwDocShellRef m_xDocShRef;
};

void SwDocTest::testPageDescName()
{
    ShellResource aShellResources;

    std::vector<OUString> aResults;

    //These names must be unique for each different combination, otherwise
    //duplicate page description names may exist, which will causes lookup
    //by name to be incorrect, and so the corresponding export to .odt
    aResults.push_back(aShellResources.GetPageDescName(1, ShellResource::NORMAL_PAGE));
    aResults.push_back(aShellResources.GetPageDescName(1, ShellResource::FIRST_PAGE));
    aResults.push_back(aShellResources.GetPageDescName(1, ShellResource::FOLLOW_PAGE));

    std::sort(aResults.begin(), aResults.end());
    aResults.erase(std::unique(aResults.begin(), aResults.end()), aResults.end());

    CPPUNIT_ASSERT_EQUAL_MESSAGE("GetPageDescName results must be unique"static_cast<size_t>(3), aResults.size());
}

//See https://bugs.libreoffice.org/show_bug.cgi?id=32463
void SwDocTest::testFileNameFields()
{
    //Here's a file name with some chars in it that will be %% encoded, when expanding
    //SwFileNameFields we want to restore the original readable filename
    utl::TempFileNamed aTempFile(u"demo [name]");
    aTempFile.EnableKillingFile();

    INetURLObject aTempFileURL(aTempFile.GetURL());
    OUString sFileURL = aTempFileURL.GetMainURL(INetURLObject::DecodeMechanism::NONE);
    SfxMedium aDstMed(sFileURL, StreamMode::STD_READWRITE);

    auto pFilter = std::make_shared<SfxFilter>(
        "Text",
        OUString(), SfxFilterFlags::NONE, SotClipboardFormatId::NONE, OUString(), OUString(),
        "TEXT", OUString() );
    aDstMed.SetFilter(pFilter);

    m_xDocShRef->DoSaveAs(aDstMed);
    m_xDocShRef->DoSaveCompleted(&aDstMed);

    const INetURLObject &rUrlObj = m_xDocShRef->GetMedium()->GetURLObject();

    SwFileNameFieldType aNameField(*m_pDoc);

    {
        OUString sResult(aNameField.Expand(FF_NAME));
        OUString sExpected(rUrlObj.getName(INetURLObject::LAST_SEGMENT,
            true,INetURLObject::DecodeMechanism::WithCharset));
        CPPUNIT_ASSERT_EQUAL_MESSAGE("Expected Readable FileName", sExpected, sResult);
    }

    {
        OUString sResult(aNameField.Expand(FF_PATHNAME));
        OUString sExpected(rUrlObj.GetFull());
        CPPUNIT_ASSERT_EQUAL_MESSAGE("Expected Readable FileName", sExpected, sResult);
    }

    {
        OUString sResult(aNameField.Expand(FF_PATH));
        INetURLObject aTemp(rUrlObj);
        aTemp.removeSegment();
        OUString sExpected(aTemp.PathToFileName());
        CPPUNIT_ASSERT_EQUAL_MESSAGE("Expected Readable FileName", sExpected, sResult);
    }

    {
        OUString sResult(aNameField.Expand(FF_NAME_NOEXT));
        OUString sExpected(rUrlObj.getName(INetURLObject::LAST_SEGMENT,
            true,INetURLObject::DecodeMechanism::WithCharset));
        //Chop off .tmp
        sExpected = sExpected.copy(0, sExpected.getLength() - 4);
        CPPUNIT_ASSERT_EQUAL_MESSAGE("Expected Readable FileName", sExpected, sResult);
    }

    m_xDocShRef->DoInitNew();
}

//See http://lists.freedesktop.org/archives/libreoffice/2011-August/016666.html
//Remove unnecessary parameter to IDocumentStatistics::UpdateDocStat for
//motivation
void SwDocTest::testDocStat()
{
    CPPUNIT_ASSERT_EQUAL_MESSAGE("Expected initial 0 count"static_cast<sal_uLong>(0), m_pDoc->getIDocumentStatistics().GetDocStat().nChar);

    SwNodeIndex aIdx(m_pDoc->GetNodes().GetEndOfContent(), -1);
    SwPaM aPaM(aIdx);

    OUString sText(u"Hello World"_ustr);
    m_pDoc->getIDocumentContentOperations().InsertString(aPaM, sText);

    CPPUNIT_ASSERT_EQUAL_MESSAGE("Should still be non-updated 0 count"static_cast<sal_uLong>(0), m_pDoc->getIDocumentStatistics().GetDocStat().nChar);

    SwDocStat aDocStat = m_pDoc->getIDocumentStatistics().GetUpdatedDocStat( falsetrue );
    sal_uLong nLen = static_cast<sal_uLong>(sText.getLength());

    CPPUNIT_ASSERT_EQUAL_MESSAGE("Should now have updated count", nLen, aDocStat.nChar);

    CPPUNIT_ASSERT_EQUAL_MESSAGE("And cache is updated too", nLen, m_pDoc->getIDocumentStatistics().GetDocStat().nChar);
}

//For UI character counts we should follow UAX#29 and display the user
//perceived characters, not the number of codepoints, nor the number of code
//units http://unicode.org/reports/tr29/
void SwDocTest::testUserPerceivedCharCount()
{
    SwBreakIt *pBreakIter = SwBreakIt::Get();

    //Grapheme example, two different unicode code-points perceived by the user as a single
    //glyph
    static constexpr OUStringLiteral sALEF_QAMATS = u"\u05D0\u05B8";
    sal_Int32 nGraphemeCount = pBreakIter->getGraphemeCount(sALEF_QAMATS);
    CPPUNIT_ASSERT_EQUAL_MESSAGE("Grapheme Count should be 1"static_cast<sal_Int32>(1), nGraphemeCount);

    //Surrogate pair example, one single unicode code-point (U+1D11E)
    //represented as two code units in UTF-16
    static constexpr OUStringLiteral sGCLEF = u"\U0001D11E";
    sal_Int32 nCount = pBreakIter->getGraphemeCount(sGCLEF);
    CPPUNIT_ASSERT_EQUAL_MESSAGE("Surrogate Pair should be counted as single character"static_cast<sal_Int32>(1), nCount);
}

static SwTextNode* getModelToViewTestDocument(SwDoc *pDoc)
{
    SwNodeIndex aIdx(pDoc->GetNodes().GetEndOfContent(), -1);
    SwPaM aPaM(aIdx);

    SwFormatFootnote aFootnote;
    aFootnote.SetNumStr(u"foo"_ustr);

    pDoc->getIDocumentContentOperations().AppendTextNode(*aPaM.GetPoint());
    pDoc->getIDocumentContentOperations().InsertString(aPaM, u"AAAAA BBBBB "_ustr);
    SwTextNode* pTextNode = aPaM.GetPointNode().GetTextNode();
    sal_Int32 nPos = aPaM.GetPoint()->GetContentIndex();
    pTextNode->InsertItem(aFootnote, nPos, nPos);
    pDoc->getIDocumentContentOperations().InsertString(aPaM, u" CCCCC "_ustr);
    nPos = aPaM.GetPoint()->GetContentIndex();
    pTextNode->InsertItem(aFootnote, nPos, nPos);
    pDoc->getIDocumentContentOperations().InsertString(aPaM, u" DDDDD"_ustr);
    CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>((4*5) + 5 + 2), pTextNode->GetText().getLength());

    //set start of selection to first B
    aPaM.GetPoint()->nContent.Assign(aPaM.GetPointContentNode(), 6);
    aPaM.SetMark();
    //set end of selection to last C
    aPaM.GetPoint()->nContent.Assign(aPaM.GetPointContentNode(), 14);
    //set character attribute hidden on range
    SvxCharHiddenItem aHidden(true, RES_CHRATR_HIDDEN);
    pDoc->getIDocumentContentOperations().InsertPoolItem(aPaM, aHidden );
    aPaM.DeleteMark();

    //turn on red-lining and show changes
    pDoc->getIDocumentRedlineAccess().SetRedlineFlags(RedlineFlags::On | RedlineFlags::ShowDelete|RedlineFlags::ShowInsert);
    CPPUNIT_ASSERT_MESSAGE("redlining should be on", pDoc->getIDocumentRedlineAccess().IsRedlineOn());
    CPPUNIT_ASSERT_MESSAGE("redlines should be visible", IDocumentRedlineAccess::IsShowChanges(pDoc->getIDocumentRedlineAccess().GetRedlineFlags()));

    //set start of selection to last A
    aPaM.GetPoint()->nContent.Assign(aPaM.GetPointContentNode(), 4);
    aPaM.SetMark();
    //set end of selection to second last B
    aPaM.GetPoint()->nContent.Assign(aPaM.GetPointContentNode(), 9);
    pDoc->getIDocumentContentOperations().DeleteAndJoin(aPaM);    //redline-aware deletion api
    aPaM.DeleteMark();

    return pTextNode;
}

static SwTextNode* getModelToViewTestDocument2(SwDoc *pDoc)
{
    getModelToViewTestDocument(pDoc);

    SwNodeIndex aIdx(pDoc->GetNodes().GetEndOfContent(), -1);
    SwPaM aPaM(aIdx);

    pDoc->getIDocumentContentOperations().AppendTextNode(*aPaM.GetPoint());
    pDoc->getIDocumentContentOperations().InsertString(aPaM, u"AAAAA"_ustr);
    IDocumentMarkAccess* pMarksAccess = pDoc->getIDocumentMarkAccess();
    sw::mark::Fieldmark *pFieldmark =
            pMarksAccess->makeNoTextFieldBookmark(aPaM, SwMarkName(u"test"_ustr), ODF_FORMDROPDOWN);
    CPPUNIT_ASSERT(pFieldmark);
    uno::Sequence< OUString > vListEntries { u"BBBBB"_ustr };
    (*pFieldmark->GetParameters())[ODF_FORMDROPDOWN_LISTENTRY] <<= vListEntries;
    (*pFieldmark->GetParameters())[ODF_FORMDROPDOWN_RESULT] <<= sal_Int32(0);
    pDoc->getIDocumentContentOperations().InsertString(aPaM, u"CCCCC"_ustr);
    SwTextNode* pTextNode = aPaM.GetPointNode().GetTextNode();
    CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(11),
            pTextNode->GetText().getLength());

    return pTextNode;
}

void SwDocTest::testModelToViewHelperPassthrough()
{
    SwTextNode* pTextNode = getModelToViewTestDocument(m_pDoc);

    ModelToViewHelper aModelToViewHelper(*pTextNode, nullptr, ExpandMode::PassThrough);
    OUString sViewText = aModelToViewHelper.getViewText();
    OUString sModelText = pTextNode->GetText();
    CPPUNIT_ASSERT_EQUAL(sModelText, sViewText);
}

void SwDocTest::testModelToViewHelperExpandFieldsExpandFootnote()
{
    SwTextNode* pTextNode = getModelToViewTestDocument(m_pDoc);

    ModelToViewHelper aModelToViewHelper(*pTextNode, nullptr,
            ExpandMode::ExpandFields | ExpandMode::ExpandFootnote);
    OUString sViewText = aModelToViewHelper.getViewText();
    CPPUNIT_ASSERT_EQUAL(
        u"AAAAA BBBBB foo CCCCC foo DDDDD"_ustr, sViewText);
}

void SwDocTest::testModelToViewHelperExpandFieldsExpandFootnoteReplaceMode()
{
    SwTextNode* pTextNode = getModelToViewTestDocument(m_pDoc);

    ModelToViewHelper aModelToViewHelper(*pTextNode, nullptr,
            ExpandMode::ExpandFields | ExpandMode::ExpandFootnote | ExpandMode::ReplaceMode);
    OUString sViewText = aModelToViewHelper.getViewText();
    CPPUNIT_ASSERT_EQUAL(
        OUString("AAAAA BBBBB " + OUStringChar(CHAR_ZWSP) + " CCCCC " + OUStringChar(CHAR_ZWSP) + " DDDDD"),
        sViewText);
    CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2),
        aModelToViewHelper.getFootnotePositions().size());
    CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(12),
        aModelToViewHelper.getFootnotePositions()[0]);
    CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(20),
        aModelToViewHelper.getFootnotePositions()[1]);
    CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(0),
        aModelToViewHelper.getFieldPositions().size());
}

void SwDocTest::testModelToViewHelperExpandFields()
{
    SwTextNode* pTextNode = getModelToViewTestDocument(m_pDoc);

    ModelToViewHelper aModelToViewHelper(*pTextNode, nullptr, ExpandMode::ExpandFields);
    OUString sViewText = aModelToViewHelper.getViewText();
    CPPUNIT_ASSERT_EQUAL(
        u"AAAAA BBBBB CCCCC DDDDD"_ustr, sViewText);
}

void SwDocTest::testModelToViewHelperExpandFieldsReplaceMode()
{
    SwTextNode* pTextNode = getModelToViewTestDocument(m_pDoc);

    ModelToViewHelper aModelToViewHelper(*pTextNode, nullptr,
        ExpandMode::ExpandFields | ExpandMode::ReplaceMode);
    OUString sViewText = aModelToViewHelper.getViewText();
    CPPUNIT_ASSERT_EQUAL(u"AAAAA BBBBB CCCCC DDDDD"_ustr,
        sViewText);
    CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(0),
        aModelToViewHelper.getFootnotePositions().size());
    CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(0),
        aModelToViewHelper.getFieldPositions().size());
}

void SwDocTest::testModelToViewHelperExpandFieldsHideInvisible()
{
    SwTextNode* pTextNode = getModelToViewTestDocument(m_pDoc);

    ModelToViewHelper aModelToViewHelper(*pTextNode, nullptr, ExpandMode::HideInvisible);
    OUString sViewText = aModelToViewHelper.getViewText();
    CPPUNIT_ASSERT_EQUAL(
        OUString("AAAAA CCCCC " + OUStringChar(CH_TXTATR_BREAKWORD) + " DDDDD"),
        sViewText);
}

void SwDocTest::testModelToViewHelperExpandFieldsHideRedlined()
{
    SwTextNode* pTextNode = getModelToViewTestDocument(m_pDoc);

    ModelToViewHelper aModelToViewHelper(*pTextNode, nullptr, ExpandMode::HideDeletions);
    OUString sViewText = aModelToViewHelper.getViewText();
    CPPUNIT_ASSERT_EQUAL(
        OUString("AAAABB " + OUStringChar(CH_TXTATR_BREAKWORD) + " CCCCC " + OUStringChar(CH_TXTATR_BREAKWORD) + " DDDDD"),
        sViewText);
}

void SwDocTest::testModelToViewHelperExpandFieldsHideInvisibleExpandFootnote()
{
    SwTextNode* pTextNode = getModelToViewTestDocument(m_pDoc);

    ModelToViewHelper aModelToViewHelper(*pTextNode, nullptr,
        ExpandMode::ExpandFields | ExpandMode::HideInvisible | ExpandMode::ExpandFootnote);
    OUString sViewText = aModelToViewHelper.getViewText();
    CPPUNIT_ASSERT_EQUAL(u"AAAAA CCCCC foo DDDDD"_ustr, sViewText);
}

void SwDocTest::testModelToViewHelperExpandFieldsHideInvisibleExpandFootnoteReplaceMode()
{
    SwTextNode* pTextNode = getModelToViewTestDocument(m_pDoc);

    ModelToViewHelper aModelToViewHelper(*pTextNode, nullptr,
        ExpandMode::ExpandFields | ExpandMode::HideInvisible | ExpandMode::ExpandFootnote | ExpandMode::ReplaceMode);
    OUString sViewText = aModelToViewHelper.getViewText();
    CPPUNIT_ASSERT_EQUAL(
        OUString("AAAAA CCCCC " + OUStringChar(CHAR_ZWSP) + " DDDDD"),
        sViewText);
    CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1),
        aModelToViewHelper.getFootnotePositions().size());
    CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(12),
        aModelToViewHelper.getFootnotePositions()[0]);
    CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(0),
        aModelToViewHelper.getFieldPositions().size());
}

void SwDocTest::testModelToViewHelperExpandFieldsHideHideRedlinedExpandFootnote()
{
    SwTextNode* pTextNode = getModelToViewTestDocument(m_pDoc);

    ModelToViewHelper aModelToViewHelper(*pTextNode, nullptr,
        ExpandMode::ExpandFields | ExpandMode::HideDeletions | ExpandMode::ExpandFootnote);
    OUString sViewText = aModelToViewHelper.getViewText();
    CPPUNIT_ASSERT_EQUAL(
        u"AAAABB foo CCCCC foo DDDDD"_ustr, sViewText);
}

void SwDocTest::testModelToViewHelperExpandFieldsHideHideRedlinedExpandFootnoteReplaceMode()
{
    SwTextNode* pTextNode = getModelToViewTestDocument(m_pDoc);

    ModelToViewHelper aModelToViewHelper(*pTextNode, nullptr,
        ExpandMode::ExpandFields | ExpandMode::HideDeletions | ExpandMode::ExpandFootnote | ExpandMode::ReplaceMode);
    OUString sViewText = aModelToViewHelper.getViewText();
    CPPUNIT_ASSERT_EQUAL(
       OUString("AAAABB " + OUStringChar(CHAR_ZWSP) + " CCCCC " + OUStringChar(CHAR_ZWSP) + " DDDDD"),
       sViewText);
    CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2),
        aModelToViewHelper.getFootnotePositions().size());
    CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(7),
        aModelToViewHelper.getFootnotePositions()[0]);
    CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(15),
        aModelToViewHelper.getFootnotePositions()[1]);
    CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(0),
        aModelToViewHelper.getFieldPositions().size());
}

void SwDocTest::testModelToViewHelperHideInvisibleHideRedlined()
{
    SwTextNode* pTextNode = getModelToViewTestDocument(m_pDoc);

    ModelToViewHelper aModelToViewHelper(*pTextNode, nullptr,
        ExpandMode::HideInvisible | ExpandMode::HideDeletions);
    OUString sViewText = aModelToViewHelper.getViewText();
    OUString aBuffer = "AAAACCCCC " +
        OUStringChar(CH_TXTATR_BREAKWORD) +
        " DDDDD";
    CPPUNIT_ASSERT_EQUAL(aBuffer, sViewText);
}

void SwDocTest::testModelToViewHelperExpandFieldsHideInvisibleHideRedlinedExpandFootnote()
{
    SwTextNode* pTextNode = getModelToViewTestDocument(m_pDoc);

    ModelToViewHelper aModelToViewHelper(*pTextNode, nullptr,
        ExpandMode::ExpandFields | ExpandMode::HideInvisible | ExpandMode::HideDeletions | ExpandMode::ExpandFootnote);
    OUString sViewText = aModelToViewHelper.getViewText();
    CPPUNIT_ASSERT_EQUAL(u"AAAACCCCC foo DDDDD"_ustr, sViewText);
}

void SwDocTest::testModelToViewHelperExpandFieldsHideInvisibleHideRedlinedExpandFootnoteReplaceMode()
{
    SwTextNode* pTextNode = getModelToViewTestDocument(m_pDoc);

    ModelToViewHelper aModelToViewHelper(*pTextNode, nullptr,
        ExpandMode::ExpandFields | ExpandMode::HideInvisible | ExpandMode::HideDeletions | ExpandMode::ExpandFootnote | ExpandMode::ReplaceMode);
    OUString sViewText = aModelToViewHelper.getViewText();
    CPPUNIT_ASSERT_EQUAL(sViewText,
        OUString("AAAACCCCC " + OUStringChar(CHAR_ZWSP) + " DDDDD"));
    CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1),
        aModelToViewHelper.getFootnotePositions().size());
    CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(10),
        aModelToViewHelper.getFootnotePositions()[0]);
    CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(0),
        aModelToViewHelper.getFieldPositions().size());
}

void SwDocTest::testModelToViewHelperExpandFieldsExpandFootnote2()
{
    SwTextNode* pTextNode = getModelToViewTestDocument2(m_pDoc);

    ModelToViewHelper aModelToViewHelper(*pTextNode, nullptr,
        ExpandMode::ExpandFields | ExpandMode::ExpandFootnote);
    OUString sViewText = aModelToViewHelper.getViewText();
    CPPUNIT_ASSERT_EQUAL(u"AAAAABBBBBCCCCC"_ustr, sViewText);
}

void SwDocTest::testModelToViewHelperExpandFieldsExpandFootnoteReplaceMode2()
{
    SwTextNode* pTextNode = getModelToViewTestDocument2(m_pDoc);

    ModelToViewHelper aModelToViewHelper(*pTextNode, nullptr,
        ExpandMode::ExpandFields | ExpandMode::ExpandFootnote | ExpandMode::ReplaceMode);
    OUString sViewText = aModelToViewHelper.getViewText();
    CPPUNIT_ASSERT_EQUAL(
        OUString("AAAAA" + OUStringChar(CHAR_ZWSP) + "CCCCC"),
        sViewText);
    CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(0),
        aModelToViewHelper.getFootnotePositions().size());
    CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1),
        aModelToViewHelper.getFieldPositions().size());
    CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(5),
        aModelToViewHelper.getFieldPositions()[0]);
}

void SwDocTest::testSwScanner()
{
    SwNodeIndex aIdx(m_pDoc->GetNodes().GetEndOfContent(), -1);
    SwPaM aPaM(aIdx);

    SwTextNode* pTextNode = aPaM.GetPointNode().GetTextNode();

    CPPUNIT_ASSERT_MESSAGE("Has Text Node", pTextNode);

    //See https://bugs.libreoffice.org/show_bug.cgi?id=40449
    //See https://bugs.libreoffice.org/show_bug.cgi?id=39365
    //Use a temporary OUString as the arg, as that's the trouble behind
    //fdo#40449 and fdo#39365
    {
        SwScanner aScanner(*pTextNode,
            u"Hello World"_ustr,
            nullptr, ModelToViewHelper(), i18n::WordType::DICTIONARY_WORD, 0,
            RTL_CONSTASCII_LENGTH("Hello World"));

        bool bFirstOk = aScanner.NextWord();
        CPPUNIT_ASSERT_MESSAGE("First Token", bFirstOk);
        const OUString &rHello = aScanner.GetWord();
        CPPUNIT_ASSERT_EQUAL(u"Hello"_ustr, rHello);

        bool bSecondOk = aScanner.NextWord();
        CPPUNIT_ASSERT_MESSAGE("Second Token", bSecondOk);
        const OUString &rWorld = aScanner.GetWord();
        CPPUNIT_ASSERT_EQUAL(u"World"_ustr, rWorld);
    }

    //See https://www.libreoffice.org/bugzilla/show_bug.cgi?id=45271
    {
        static constexpr OUString IDEOGRAPHICFULLSTOP_D = u"\u3002D"_ustr;

        m_pDoc->getIDocumentContentOperations().InsertString(aPaM, IDEOGRAPHICFULLSTOP_D);

        SvxLanguageItem aCJKLangItem( LANGUAGE_CHINESE_SIMPLIFIED, RES_CHRATR_CJK_LANGUAGE );
        SvxLanguageItem aWestLangItem( LANGUAGE_ENGLISH_US, RES_CHRATR_LANGUAGE );
        m_pDoc->getIDocumentContentOperations().InsertPoolItem(aPaM, aCJKLangItem );
        m_pDoc->getIDocumentContentOperations().InsertPoolItem(aPaM, aWestLangItem );

        SwDocStat aDocStat;
        pTextNode = aPaM.GetPointNode().GetTextNode();
        pTextNode->CountWords(aDocStat, 0, IDEOGRAPHICFULLSTOP_D.getLength());

        CPPUNIT_ASSERT_EQUAL(static_cast<sal_uLong>(2), aDocStat.nChar);
        CPPUNIT_ASSERT_EQUAL(static_cast<sal_uLong>(2), aDocStat.nCharExcludingSpaces);
    }
    {
        static constexpr OUString test =
            u"\u3053\u306E\u65E5\u672C\u8A9E\u306F\u6B63\u3057"
            "\u304F\u6570\u3048\u3089\u308C\u308B\u3067\u3057"
            "\u3087\u3046\u304B\u3002And "
            "let's th"
            "row some"
            " English"
            " in to m"
            "ake it i"
            "nteresti"
            "ng. \u305D\u3057\u3066"
            "\u3001\u307E\u305F\u65E5\u672C\u8A9E\u3000\u3000"
            "\u3067\u3082\u4ECA\u56DE\u306F\u7A7A\u767D\u3092"
            "\u3000\u3000\u5165\u308C\u307E\u3057\u305F\u3002"
            " So how"
            " does th"
            "is do? "_ustr;
        m_pDoc->getIDocumentContentOperations().AppendTextNode(*aPaM.GetPoint());
        m_pDoc->getIDocumentContentOperations().InsertString(aPaM, test);

        SvxLanguageItem aCJKLangItem( LANGUAGE_JAPANESE, RES_CHRATR_CJK_LANGUAGE );
        SvxLanguageItem aWestLangItem( LANGUAGE_ENGLISH_US, RES_CHRATR_LANGUAGE );
        m_pDoc->getIDocumentContentOperations().InsertPoolItem(aPaM, aCJKLangItem );
        m_pDoc->getIDocumentContentOperations().InsertPoolItem(aPaM, aWestLangItem );

        SwDocStat aDocStat;
        pTextNode = aPaM.GetPointNode().GetTextNode();
        pTextNode->CountWords(aDocStat, 0, test.getLength());
        CPPUNIT_ASSERT_EQUAL_MESSAGE("words"static_cast<sal_uLong>(58), aDocStat.nWord);
        CPPUNIT_ASSERT_EQUAL_MESSAGE("Asian characters and Korean words"static_cast<sal_uLong>(43), aDocStat.nAsianWord);
        CPPUNIT_ASSERT_EQUAL_MESSAGE("non-whitespace chars"static_cast<sal_uLong>(105), aDocStat.nCharExcludingSpaces);
        CPPUNIT_ASSERT_EQUAL_MESSAGE("characters"static_cast<sal_uLong>(128), aDocStat.nChar);
    }

    //See https://bz.apache.org/ooo/show_bug.cgi?id=89042
    //See https://bugs.libreoffice.org/show_bug.cgi?id=53399
    {
        SwDocStat aDocStat;

        static constexpr OUString aShouldBeThree =
            u"Should "
            "\u2018be thr"
            "ee\u2019"_ustr;

        m_pDoc->getIDocumentContentOperations().AppendTextNode(*aPaM.GetPoint());
        m_pDoc->getIDocumentContentOperations().InsertString(aPaM, aShouldBeThree);
        pTextNode = aPaM.GetPointNode().GetTextNode();
        pTextNode->CountWords(aDocStat, 0, aShouldBeThree.getLength());
        CPPUNIT_ASSERT_EQUAL(static_cast<sal_uLong>(3), aDocStat.nWord);

        static constexpr OUString aShouldBeFive =
            u"french "
            // <<   nbsp
            "\u00AB\u00A0savoi"
            // nnbsp
            "r\u202fcalcu"
            //   idspace >>
            "ler\u3000\u00BB"_ustr;

        m_pDoc->getIDocumentContentOperations().AppendTextNode(*aPaM.GetPoint());
        m_pDoc->getIDocumentContentOperations().InsertString(aPaM, aShouldBeFive);
        pTextNode = aPaM.GetPointNode().GetTextNode();
        aDocStat.Reset();
        pTextNode->CountWords(aDocStat, 0, aShouldBeFive.getLength());
        CPPUNIT_ASSERT_EQUAL(static_cast<sal_uLong>(5), aDocStat.nWord);
    }

    //See https://bugs.libreoffice.org/show_bug.cgi?id=49629
    {
        SwDocStat aDocStat;

        m_pDoc->getIDocumentContentOperations().AppendTextNode(*aPaM.GetPoint());
        m_pDoc->getIDocumentContentOperations().InsertString(aPaM, u"Apple"_ustr);
        pTextNode = aPaM.GetPointNode().GetTextNode();
        sal_Int32 nPos = aPaM.GetPoint()->GetContentIndex();
        SwFormatFootnote aFootnote;
        aFootnote.SetNumStr(u"banana"_ustr);
        SwTextAttr* pTA = pTextNode->InsertItem(aFootnote, nPos, nPos);
        CPPUNIT_ASSERT(pTA);
        CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(6), pTextNode->Len()); //Apple + 0x02
        pTextNode->CountWords(aDocStat, 0, pTextNode->Len());
        CPPUNIT_ASSERT_EQUAL(static_cast<sal_uLong>(1), aDocStat.nWord);
        CPPUNIT_ASSERT_EQUAL_MESSAGE("footnote should be expanded"static_cast<sal_uLong>(11), aDocStat.nChar);

        const sal_Int32 nNextPos = aPaM.GetPoint()->GetContentIndex();
        CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(nPos+1), nNextPos);
        SwFormatRefMark aRef(SwMarkName(u"refmark"_ustr));
        pTA = pTextNode->InsertItem(aRef, nNextPos, nNextPos);
        CPPUNIT_ASSERT(pTA);

        aDocStat.Reset();
        pTextNode->SetWordCountDirty(true);
        pTextNode->CountWords(aDocStat, 0, pTextNode->Len());
        CPPUNIT_ASSERT_EQUAL(static_cast<sal_uLong>(1), aDocStat.nWord);
        CPPUNIT_ASSERT_EQUAL_MESSAGE("refmark anchor should not be counted"static_cast<sal_uLong>(11), aDocStat.nChar);

        m_pDoc->getIDocumentContentOperations().AppendTextNode(*aPaM.GetPoint());
        m_pDoc->getIDocumentContentOperations().InsertString(aPaM, u"Apple"_ustr);

        DateTime aDate(DateTime::SYSTEM);
        SwPostItField aPostIt(
            static_cast<SwPostItFieldType*>(m_pDoc->getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::Postit)), u"An Author"_ustr,
            u"Some Text"_ustr, u"Initials"_ustr, SwMarkName(u"Name"_ustr), aDate );
        m_pDoc->getIDocumentContentOperations().InsertPoolItem(aPaM, SwFormatField(aPostIt));

        m_pDoc->getIDocumentContentOperations().InsertString(aPaM, u"Apple"_ustr);
        pTextNode = aPaM.GetPointNode().GetTextNode();
        aDocStat.Reset();
        pTextNode->CountWords(aDocStat, 0, pTextNode->Len());
        CPPUNIT_ASSERT_EQUAL(static_cast<sal_uLong>(1), aDocStat.nWord);
        CPPUNIT_ASSERT_EQUAL_MESSAGE("postit anchor should effectively not exist"static_cast<sal_uLong>(10), aDocStat.nChar);
        CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(11), pTextNode->Len());

        aDocStat.Reset();
    }

    //See https://bugs.libreoffice.org/show_bug.cgi?id=46757
    {
        SwDocStat aDocStat;

        static constexpr OUString aString = u"Lorem ipsum"_ustr;
        m_pDoc->getIDocumentContentOperations().AppendTextNode(*aPaM.GetPoint());
        m_pDoc->getIDocumentContentOperations().InsertString(aPaM, aString);
        pTextNode = aPaM.GetPointNode().GetTextNode();
        pTextNode->CountWords(aDocStat, 0, pTextNode->Len());
        CPPUNIT_ASSERT_EQUAL(static_cast<sal_uLong>(2), aDocStat.nWord);

        //turn on red-lining and show changes
        m_pDoc->getIDocumentRedlineAccess().SetRedlineFlags(RedlineFlags::On | RedlineFlags::ShowDelete|RedlineFlags::ShowInsert);
        CPPUNIT_ASSERT_MESSAGE("redlining should be on", m_pDoc->getIDocumentRedlineAccess().IsRedlineOn());
        CPPUNIT_ASSERT_MESSAGE("redlines should be visible", IDocumentRedlineAccess::IsShowChanges(m_pDoc->getIDocumentRedlineAccess().GetRedlineFlags()));

        //delete everything except the first word
        aPaM.SetMark(); //set start of selection to current pos
        aPaM.GetPoint()->nContent.Assign(aPaM.GetPointContentNode(), 5);   //set end of selection to fifth char of current node
        m_pDoc->getIDocumentContentOperations().DeleteAndJoin(aPaM);    //redline-aware deletion api
        //"real underlying text should be the same"
        CPPUNIT_ASSERT_EQUAL(aString, pTextNode->GetText());

        aDocStat.Reset();
        pTextNode->SetWordCountDirty(true);
        pTextNode->CountWords(aDocStat, 0, pTextNode->Len()); //but word-counting the text should only count the non-deleted text
        CPPUNIT_ASSERT_EQUAL(static_cast<sal_uLong>(1), aDocStat.nWord);

        pTextNode->SetWordCountDirty(true);

        //keep red-lining on but hide changes
        m_pDoc->getIDocumentRedlineAccess().SetRedlineFlags(RedlineFlags::On);
        CPPUNIT_ASSERT_MESSAGE("redlining should be still on", m_pDoc->getIDocumentRedlineAccess().IsRedlineOn());
        CPPUNIT_ASSERT_MESSAGE("redlines should be invisible", !IDocumentRedlineAccess::IsShowChanges(m_pDoc->getIDocumentRedlineAccess().GetRedlineFlags()));

        aDocStat.Reset();
        pTextNode->CountWords(aDocStat, 0, pTextNode->Len()); //but word-counting the text should only count the non-deleted text
        CPPUNIT_ASSERT_EQUAL(static_cast<sal_uLong>(1), aDocStat.nWord);

        OUString sLorem = pTextNode->GetText();
        CPPUNIT_ASSERT_EQUAL(u"Lorem"_ustr, sLorem);

        const SwRedlineTable& rTable = m_pDoc->getIDocumentRedlineAccess().GetRedlineTable();

        SwNodes& rNds = m_pDoc->GetNodes();
        CPPUNIT_ASSERT_EQUAL(static_cast<SwRedlineTable::size_type>(1), rTable.size());

        const SwNodeIndex* pNodeIdx = rTable[0]->GetContentIdx();
        CPPUNIT_ASSERT(pNodeIdx);

        pTextNode = rNds[ pNodeIdx->GetIndex() + 1 ]->GetTextNode();        //first deleted txtnode
        CPPUNIT_ASSERT(pTextNode);

        OUString sIpsum = pTextNode->GetText();
        CPPUNIT_ASSERT_EQUAL(u" ipsum"_ustr, sIpsum);

        aDocStat.Reset();
        pTextNode->CountWords(aDocStat, 0, pTextNode->Len()); //word-counting the text should only count the non-deleted text, and this whole chunk should be ignored
        CPPUNIT_ASSERT_EQUAL(static_cast<sal_uLong>(0), aDocStat.nWord);
        CPPUNIT_ASSERT_EQUAL(static_cast<sal_uLong>(0), aDocStat.nChar);

        // https://bugs.libreoffice.org/show_bug.cgi?id=68347 we do want to count
        // redline *added* text though
        m_pDoc->getIDocumentRedlineAccess().SetRedlineFlags(RedlineFlags::On | RedlineFlags::ShowDelete|RedlineFlags::ShowInsert);
        aPaM.DeleteMark();
        aPaM.GetPoint()->nContent.Assign(aPaM.GetPointContentNode(), 0);
        m_pDoc->getIDocumentContentOperations().InsertString(aPaM, u"redline-new-text "_ustr);
        aDocStat.Reset();
        pTextNode = aPaM.GetPointNode().GetTextNode();
        pTextNode->SetWordCountDirty(true);
        pTextNode->CountWords(aDocStat, 0, pTextNode->Len());
        CPPUNIT_ASSERT_EQUAL(static_cast<sal_uLong>(2), aDocStat.nWord);
        //redline-new-text Lorem ipsum
        //+++++++++++++++++     ------
        //select start of original text and part of deleted text
        aDocStat.Reset();
        pTextNode->CountWords(aDocStat, 17, 25);
        CPPUNIT_ASSERT_EQUAL(static_cast<sal_uLong>(5), aDocStat.nChar);
    }

    //See https://bugs.libreoffice.org/show_bug.cgi?id=38983
    {
        SwDocStat aDocStat;

        OUString sTemplate(u"ThisXis a test."_ustr);

        m_pDoc->getIDocumentContentOperations().AppendTextNode(*aPaM.GetPoint());
        m_pDoc->getIDocumentContentOperations().InsertString(aPaM, sTemplate.replace('X'' '));
        pTextNode = aPaM.GetPointNode().GetTextNode();
        pTextNode->CountWords(aDocStat, 0, pTextNode->Len());
        CPPUNIT_ASSERT_EQUAL(sal_uLong(4), aDocStat.nWord);
        CPPUNIT_ASSERT_EQUAL(sal_uLong(12), aDocStat.nCharExcludingSpaces);
        CPPUNIT_ASSERT_EQUAL(sal_uLong(15), aDocStat.nChar);
        aDocStat.Reset();

        m_pDoc->getIDocumentContentOperations().AppendTextNode(*aPaM.GetPoint());
        m_pDoc->getIDocumentContentOperations().InsertString(aPaM, sTemplate.replaceAll("X"" = "));
        pTextNode = aPaM.GetPointNode().GetTextNode();
        pTextNode->CountWords(aDocStat, 0, pTextNode->Len());
        CPPUNIT_ASSERT_EQUAL(sal_uLong(5), aDocStat.nWord);
        CPPUNIT_ASSERT_EQUAL(sal_uLong(13), aDocStat.nCharExcludingSpaces);
        CPPUNIT_ASSERT_EQUAL(sal_uLong(17), aDocStat.nChar);
        aDocStat.Reset();

        m_pDoc->getIDocumentContentOperations().AppendTextNode(*aPaM.GetPoint());
        m_pDoc->getIDocumentContentOperations().InsertString(aPaM, sTemplate.replaceAll("X"" _ "));
        pTextNode = aPaM.GetPointNode().GetTextNode();
        pTextNode->CountWords(aDocStat, 0, pTextNode->Len());
        CPPUNIT_ASSERT_EQUAL(sal_uLong(5), aDocStat.nWord);
        CPPUNIT_ASSERT_EQUAL(sal_uLong(13), aDocStat.nCharExcludingSpaces);
        CPPUNIT_ASSERT_EQUAL(sal_uLong(17), aDocStat.nChar);
        aDocStat.Reset();

        m_pDoc->getIDocumentContentOperations().AppendTextNode(*aPaM.GetPoint());
        m_pDoc->getIDocumentContentOperations().InsertString(aPaM, sTemplate.replaceAll("X"" -- "));
        pTextNode = aPaM.GetPointNode().GetTextNode();
        pTextNode->CountWords(aDocStat, 0, pTextNode->Len());
        CPPUNIT_ASSERT_EQUAL(sal_uLong(5), aDocStat.nWord);
        CPPUNIT_ASSERT_EQUAL(sal_uLong(14), aDocStat.nCharExcludingSpaces);
        CPPUNIT_ASSERT_EQUAL(sal_uLong(18), aDocStat.nChar);
        aDocStat.Reset();

        m_pDoc->getIDocumentContentOperations().AppendTextNode(*aPaM.GetPoint());
        m_pDoc->getIDocumentContentOperations().InsertString(aPaM, sTemplate.replace('X''_'));
        pTextNode = aPaM.GetPointNode().GetTextNode();
        pTextNode->CountWords(aDocStat, 0, pTextNode->Len());
        CPPUNIT_ASSERT_EQUAL(sal_uLong(3), aDocStat.nWord);
        CPPUNIT_ASSERT_EQUAL(sal_uLong(13), aDocStat.nCharExcludingSpaces);
        CPPUNIT_ASSERT_EQUAL(sal_uLong(15), aDocStat.nChar);
        aDocStat.Reset();

        m_pDoc->getIDocumentContentOperations().AppendTextNode(*aPaM.GetPoint());
        m_pDoc->getIDocumentContentOperations().InsertString(aPaM, sTemplate.replace('X''-'));
        pTextNode = aPaM.GetPointNode().GetTextNode();
        pTextNode->CountWords(aDocStat, 0, pTextNode->Len());
        CPPUNIT_ASSERT_EQUAL(sal_uLong(3), aDocStat.nWord);
        CPPUNIT_ASSERT_EQUAL(sal_uLong(13), aDocStat.nCharExcludingSpaces);
        CPPUNIT_ASSERT_EQUAL(sal_uLong(15), aDocStat.nChar);
        aDocStat.Reset();

        m_pDoc->getIDocumentContentOperations().AppendTextNode(*aPaM.GetPoint());
        m_pDoc->getIDocumentContentOperations().InsertString(aPaM, sTemplate.replace('X', 0x2012));
        pTextNode = aPaM.GetPointNode().GetTextNode();
        pTextNode->CountWords(aDocStat, 0, pTextNode->Len());
        CPPUNIT_ASSERT_EQUAL(sal_uLong(3), aDocStat.nWord);
        CPPUNIT_ASSERT_EQUAL(sal_uLong(13), aDocStat.nCharExcludingSpaces);
        CPPUNIT_ASSERT_EQUAL(sal_uLong(15), aDocStat.nChar);
        aDocStat.Reset();

        m_pDoc->getIDocumentContentOperations().AppendTextNode(*aPaM.GetPoint());
        m_pDoc->getIDocumentContentOperations().InsertString(aPaM, sTemplate.replace('X', 0x2015));
        pTextNode = aPaM.GetPointNode().GetTextNode();
        pTextNode->CountWords(aDocStat, 0, pTextNode->Len());
        CPPUNIT_ASSERT_EQUAL(sal_uLong(3), aDocStat.nWord);
        CPPUNIT_ASSERT_EQUAL(sal_uLong(13), aDocStat.nCharExcludingSpaces);
        CPPUNIT_ASSERT_EQUAL(sal_uLong(15), aDocStat.nChar);
        aDocStat.Reset();

        //But default configuration should, msword-alike treat emdash
        //and endash as word separators for word-counting
        m_pDoc->getIDocumentContentOperations().AppendTextNode(*aPaM.GetPoint());
        m_pDoc->getIDocumentContentOperations().InsertString(aPaM, sTemplate.replace('X', 0x2013));
        pTextNode = aPaM.GetPointNode().GetTextNode();
        pTextNode->CountWords(aDocStat, 0, pTextNode->Len());
        CPPUNIT_ASSERT_EQUAL(sal_uLong(4), aDocStat.nWord);
        CPPUNIT_ASSERT_EQUAL(sal_uLong(13), aDocStat.nCharExcludingSpaces);
        CPPUNIT_ASSERT_EQUAL(sal_uLong(15), aDocStat.nChar);
        aDocStat.Reset();

        m_pDoc->getIDocumentContentOperations().AppendTextNode(*aPaM.GetPoint());
        m_pDoc->getIDocumentContentOperations().InsertString(aPaM, sTemplate.replace('X', 0x2014));
        pTextNode = aPaM.GetPointNode().GetTextNode();
        pTextNode->CountWords(aDocStat, 0, pTextNode->Len());
        CPPUNIT_ASSERT_EQUAL(sal_uLong(4), aDocStat.nWord);
        CPPUNIT_ASSERT_EQUAL(sal_uLong(13), aDocStat.nCharExcludingSpaces);
        CPPUNIT_ASSERT_EQUAL(sal_uLong(15), aDocStat.nChar);
        aDocStat.Reset();

        static constexpr OUStringLiteral sChunk = u" \u2013 ";
        m_pDoc->getIDocumentContentOperations().AppendTextNode(*aPaM.GetPoint());
        m_pDoc->getIDocumentContentOperations().InsertString(aPaM, sTemplate.replaceAll("X", sChunk));
        pTextNode = aPaM.GetPointNode().GetTextNode();
        pTextNode->CountWords(aDocStat, 0, pTextNode->Len());
        CPPUNIT_ASSERT_EQUAL(sal_uLong(4), aDocStat.nWord);
        CPPUNIT_ASSERT_EQUAL(sal_uLong(13), aDocStat.nCharExcludingSpaces);
        CPPUNIT_ASSERT_EQUAL(sal_uLong(17), aDocStat.nChar);
        aDocStat.Reset();
    }

    // tdf#150621 Korean words should be counted individually, rather than by syllable.
    //
    // Per i#80815, the intention for the word count feature is to emulate the behavior of MS Word.
    {
        auto fnAssertWords = [&](const OUString& aStr, sal_uLong nWords, sal_uLong nAsianWords)
        {
            m_pDoc->getIDocumentContentOperations().AppendTextNode(*aPaM.GetPoint());

            SvxLanguageItem aCJKLangItem(LANGUAGE_KOREAN, RES_CHRATR_CJK_LANGUAGE);
            SvxLanguageItem aWestLangItem(LANGUAGE_ENGLISH_US, RES_CHRATR_LANGUAGE);
            m_pDoc->getIDocumentContentOperations().InsertPoolItem(aPaM, aCJKLangItem);
            m_pDoc->getIDocumentContentOperations().InsertPoolItem(aPaM, aWestLangItem);

            m_pDoc->getIDocumentContentOperations().InsertString(aPaM, aStr);

            SwDocStat aDocStat;
            pTextNode = aPaM.GetPointNode().GetTextNode();
            pTextNode->CountWords(aDocStat, 0, aStr.getLength());
            CPPUNIT_ASSERT_EQUAL_MESSAGE("words", nWords, aDocStat.nWord);
            CPPUNIT_ASSERT_EQUAL_MESSAGE("Asian characters and Korean words", nAsianWords,
                                         aDocStat.nAsianWord);
        };

        // Basic case: Korean words are counted as space-delimited. In particular, grammatical
        // particles are treated as part of the previous word.
        fnAssertWords(u"저는 영화를 봤어요"_ustr, 3, 3);

        // Mixed script: Korean is mostly written in hangul, but hanja are still used in certain
        // situations (e.g. abbreviations in newspaper articles). For Chinese and Japanese, such
        // ideographs would be counted individually as words. In Korean, however, they are treated
        // no differently than hangul characters.
        fnAssertWords(u"尹탄핵"_ustr, 1, 1);
        fnAssertWords(u"尹 탄핵"_ustr, 2, 2);

        // These mixed-script results are anomalous, but reflect the behavior of MSW.
        fnAssertWords(u"불렀다...與"_ustr, 1, 1);
        fnAssertWords(u"불렀다 ...與"_ustr, 2, 1);
        fnAssertWords(u"불렀다 ... 與"_ustr, 3, 2);
    }
}

void SwDocTest::testMergePortionsDeleteNotSorted()
{
    SwNodeIndex aIdx(m_pDoc->GetNodes().GetEndOfContent(), -1);
    SwPaM aPaM(aIdx);
    m_pDoc->getIDocumentContentOperations().InsertString(aPaM, u" AABBCC"_ustr);

    SwCharFormat *const pCharFormat(m_pDoc->MakeCharFormat(UIName(u"foo"_ustr), nullptr));
    SwFormatCharFormat const charFormat(pCharFormat);

    SwFormatINetFormat const inetFormat(u"http://example.com"_ustr, u""_ustr);

    IDocumentContentOperations & rIDCO(m_pDoc->getIDocumentContentOperations());
    aPaM.SetMark();
    aPaM.GetPoint()->nContent = 2;
    aPaM.GetMark()->nContent = 4;
    rIDCO.InsertPoolItem(aPaM, charFormat);
    aPaM.GetPoint()->nContent = 2;
    aPaM.GetMark()->nContent = 5;
    rIDCO.InsertPoolItem(aPaM, inetFormat);
    aPaM.GetPoint()->nContent = 6;
    aPaM.GetMark()->nContent = 8;
    rIDCO.InsertPoolItem(aPaM, charFormat);
    aPaM.GetPoint()->nContent = 4;
    aPaM.GetMark()->nContent = 6;
    // this triggered an STL assert in SwpHints::MergePortions()
    rIDCO.InsertPoolItem(aPaM, charFormat);
}

//See https://bugs.libreoffice.org/show_bug.cgi?id=40599
void SwDocTest::testGraphicAnchorDeletion()
{
    CPPUNIT_ASSERT_EQUAL_MESSAGE("Expected initial 0 count"static_cast<sal_uLong>(0), m_pDoc->getIDocumentStatistics().GetDocStat().nChar);

    SwNodeIndex aIdx(m_pDoc->GetNodes().GetEndOfContent(), -1);
    SwPaM aPaM(aIdx);

    m_pDoc->getIDocumentContentOperations().InsertString(aPaM, u"Paragraph 1"_ustr);
    m_pDoc->getIDocumentContentOperations().AppendTextNode(*aPaM.GetPoint());

    m_pDoc->getIDocumentContentOperations().InsertString(aPaM, u"graphic anchor>><_ustr);
    SwNodeIndex nPara2(aPaM.GetPoint()->GetNode());
    m_pDoc->getIDocumentContentOperations().AppendTextNode(*aPaM.GetPoint());

    m_pDoc->getIDocumentContentOperations().InsertString(aPaM, u"Paragraph 3"_ustr);

    aPaM.GetPoint()->Assign(nPara2);
    aPaM.GetPoint()->SetContent(RTL_CONSTASCII_LENGTH("graphic anchor>>"));

    //Insert a graphic at X of >>X<< in paragraph 2
    SfxItemSet aFlySet(m_pDoc->GetAttrPool(), svl::Items<RES_FRMATR_BEGIN, RES_FRMATR_END-1>);
    SwFormatAnchor aAnchor(RndStdIds::FLY_AS_CHAR);
    aAnchor.SetAnchor(aPaM.GetPoint());
    aFlySet.Put(aAnchor);
    SwFlyFrameFormat *pFrame = m_pDoc->getIDocumentContentOperations().InsertGraphic(aPaM, OUString(), OUString(), nullptr, &aFlySet, nullptr, nullptr);
    CPPUNIT_ASSERT_MESSAGE("Expected frame", pFrame != nullptr);

    CPPUNIT_ASSERT_EQUAL_MESSAGE("Should be 1 graphic"static_cast<size_t>(1), m_pDoc->GetFlyCount(FLYCNTTYPE_GRF));

    //Delete >X<
    aPaM.GetPoint()->Assign(nPara2);
    aPaM.GetPoint()->SetContent(
        RTL_CONSTASCII_LENGTH("graphic anchor>><")+1);
    aPaM.SetMark();
    aPaM.GetPoint()->Assign(nPara2);
    aPaM.GetPoint()->SetContent(RTL_CONSTASCII_LENGTH("graphic anchor>"));
    m_pDoc->getIDocumentContentOperations().DeleteRange(aPaM);

#ifdef DEBUG_AS_HTML
    {
        SvFileStream aPasteDebug(OUString("cppunitDEBUG.html"), StreamMode::WRITE|StreamMode::TRUNC);
        WriterRef xWrt;
        GetHTMLWriter( String(), String(), xWrt );
        SwWriter aDbgWrt( aPasteDebug, *m_pDoc );
        aDbgWrt.Write( xWrt );
    }
#endif

    CPPUNIT_ASSERT_EQUAL_MESSAGE("Should be 0 graphics"static_cast<size_t>(0), m_pDoc->GetFlyCount(FLYCNTTYPE_GRF));

    //Now, if instead we swap RndStdIds::FLY_AS_CHAR (inline graphic) to RndStdIds::FLY_AT_CHAR (anchored to character)
    //and repeat the above, graphic is *not* deleted, i.e. it belongs to the paragraph, not the
    //range to which its anchored, which is annoying.
}

void SwDocTest::testTableAutoFormats()
{
    SwGlobals::ensure();

    //create new AutoFormatTable
    SwTableAutoFormatTable aTableAFT;

    //check the style size - default is expected
    CPPUNIT_ASSERT_EQUAL( size_t(1),  aTableAFT.size() );

    //create new style
    SwTableAutoFormat aTableAF( TableStyleName(u"TestItemStyle"_ustr) );

    //create new AutoFormat
    SwBoxAutoFormat aBoxAF;
    SwAutoFormatProps& rBoxFP = aBoxAF.GetProps();

    //SetFont
    SvxFontItem aFont( RES_CHRATR_FONT );
    aFont.SetFamily( FontFamily::FAMILY_DECORATIVE );
    aFont.SetPitch( FontPitch::PITCH_VARIABLE );
    aFont.SetCharSet( RTL_TEXTENCODING_MS_1251 );
    rBoxFP.SetFont( aFont );
    //SetHeight
    SvxFontHeightItem aHeight( 280, 120, RES_CHRATR_FONTSIZE );
    rBoxFP.SetHeight( aHeight );
    //SetWeight
    SvxWeightItem aWeight( FontWeight::WEIGHT_BOLD, RES_CHRATR_WEIGHT );
    rBoxFP.SetWeight( aWeight );
    //SetPosture
    SvxPostureItem aPosture( FontItalic::ITALIC_NORMAL, RES_CHRATR_POSTURE );
    rBoxFP.SetPosture( aPosture );
    //SetCJKFont
    SvxFontItem aCJKFont( RES_CHRATR_FONT );
    aCJKFont.SetFamily( FontFamily::FAMILY_MODERN );
    aCJKFont.SetPitch( FontPitch::PITCH_FIXED );
    aCJKFont.SetCharSet( RTL_TEXTENCODING_MS_1251 );
    rBoxFP.SetCJKFont( aCJKFont );
    //SetCJKHeight
    SvxFontHeightItem aCJKHeight( 230, 110, RES_CHRATR_FONTSIZE );
    rBoxFP.SetCJKHeight( aCJKHeight );
    //SetCJKWeight
    SvxWeightItem aCJKWeight( FontWeight::WEIGHT_SEMIBOLD, RES_CHRATR_WEIGHT );
    rBoxFP.SetCJKWeight( aCJKWeight );
    //SetCJKPosture
    SvxPostureItem aCJKPosture( FontItalic::ITALIC_OBLIQUE, RES_CHRATR_POSTURE );
    rBoxFP.SetCJKPosture( aCJKPosture );
    //SetCTLFont
    SvxFontItem aCTLFont( RES_CHRATR_FONT );
    aCTLFont.SetFamily( FontFamily::FAMILY_ROMAN );
    aCTLFont.SetPitch( FontPitch::PITCH_FIXED );
    aCTLFont.SetCharSet( RTL_TEXTENCODING_MS_1251 );
    rBoxFP.SetCTLFont( aCTLFont );
    //SetCTLHeight
    SvxFontHeightItem aCTLHeight( 215, 105, RES_CHRATR_FONTSIZE );
    rBoxFP.SetCTLHeight( aCTLHeight );
    //SetCTLWeight
    SvxWeightItem aCTLWeight( FontWeight::WEIGHT_ULTRABOLD, RES_CHRATR_WEIGHT );
    rBoxFP.SetCTLWeight( aCTLWeight );
    //SetCTLPosture
    SvxPostureItem aCTLPosture( FontItalic::ITALIC_OBLIQUE, RES_CHRATR_POSTURE );
    rBoxFP.SetCTLPosture( aCTLPosture );
    //SetUnderline
    SvxUnderlineItem aUnderline( FontLineStyle::LINESTYLE_DOTTED, RES_CHRATR_UNDERLINE );
    rBoxFP.SetUnderline( aUnderline );
    //SetOverline
    SvxOverlineItem aOverline( FontLineStyle::LINESTYLE_DASH, RES_CHRATR_OVERLINE );
    rBoxFP.SetOverline( aOverline );
    //SetCrossedOut
    SvxCrossedOutItem aCrossedOut( FontStrikeout::STRIKEOUT_BOLD, RES_CHRATR_CROSSEDOUT );
    rBoxFP.SetCrossedOut( aCrossedOut );
    //SetContour
    SvxContourItem aContour( true, RES_CHRATR_CONTOUR );
    rBoxFP.SetContour( aContour );
    //SetShadowed
    SvxShadowedItem aShadowed( false, RES_CHRATR_SHADOWED );
    rBoxFP.SetShadowed( aShadowed );
    //SetColor
    SvxColorItem aColor( Color(0xFF23FF), RES_CHRATR_COLOR );
    rBoxFP.SetColor( aColor );
    //SetAdjust
    SvxAdjustItem aAdjust( SvxAdjust::Center, RES_PARATR_ADJUST );
    rBoxFP.SetAdjust( aAdjust );
    //SetTextOrientation
    SvxFrameDirectionItem aTOrientation( SvxFrameDirection::Vertical_RL_TB, RES_FRAMEDIR );
    rBoxFP.SetTextOrientation( aTOrientation );
    //SetVerticalAlignment
    SwFormatVertOrient aVAlignment( 3, css::text::VertOrientation::CENTER, css::text::RelOrientation::PAGE_LEFT );
    rBoxFP.SetVerticalAlignment( aVAlignment );
    //SetBox
    SvxBoxItem aBox( RES_BOX );
    aBox.SetAllDistances( 5 );
    rBoxFP.SetBox( aBox );
    //SetBackground
    SvxBrushItem aBackground( Color(0xFF11FF), RES_BACKGROUND );
    rBoxFP.SetBackground( aBackground );
    //Set m_aTLBR
    SvxLineItem aTLBRLine(0); aTLBRLine.ScaleMetrics( 11,12 );
    rBoxFP.SetTLBR(aTLBRLine);
    //Set m_aBLTR
    SvxLineItem aBLTRLine(0); aBLTRLine.ScaleMetrics( 13,14 );
    rBoxFP.SetBLTR(aBLTRLine);
    //Set m_aHorJustify
    SvxHorJustifyItem aHJustify( SvxCellHorJustify::Center, 0 );
    rBoxFP.SetHorJustify(aHJustify);
    //Set m_aVerJustify
    SvxVerJustifyItem aVJustify( SvxCellVerJustify::Center , 0 );
    rBoxFP.SetVerJustify(aVJustify);
    //Set m_aStacked
    SfxBoolItem aStacked(0, true);
    rBoxFP.SetStacked(aStacked);
    //Set m_aMargin
    SvxMarginItem aSvxMarginItem(sal_Int16(4), sal_Int16(2), sal_Int16(3), sal_Int16(3), TypedWhichId<SvxMarginItem>(0));
    rBoxFP.SetMargin(aSvxMarginItem);
    //Set m_aLinebreak
    SfxBoolItem aLBreak(0, true);
    rBoxFP.SetLinebreak(aLBreak);
    //Set m_aRotateAngle
    SfxInt32Item aRAngle(sal_Int32(5));
    rBoxFP.SetRotateAngle(aRAngle);
    //Set m_aRotateMode
    SvxRotateModeItem aSvxRotateModeItem(SVX_ROTATE_MODE_CENTER, TypedWhichId<SvxRotateModeItem>(0));
    rBoxFP.SetRotateMode(aSvxRotateModeItem);
    //Set m_sNumFormatString
    OUString aNFString = u"UnitTestFormat"_ustr;
    rBoxFP.SetNumFormatString(aNFString);
    //Set m_eSysLanguage
    LanguageType aSLang( LANGUAGE_ENGLISH_INDIA );
    rBoxFP.SetSysLanguage(aSLang);
    //Set m_eNumFormatLanguage
    LanguageType aNFLang( LANGUAGE_GERMAN );
    rBoxFP.SetNumFormatLanguage(aNFLang);
    //Set m_aKeepWithNextPara
    SvxFormatKeepItem aKWNPara( true, 0 );
    aTableAF.SetKeepWithNextPara(aKWNPara);
    //Set m_aRepeatHeading
    sal_uInt16 aRHeading = 3;
    aTableAF.m_aRepeatHeading = aRHeading;
    //Set m_bLayoutSplit
    bool aLSplit = false;
    aTableAF.m_bLayoutSplit = aLSplit;
    //Set m_bRowSplit
    bool aRSplit = false;
    aTableAF.m_bRowSplit = aRSplit;
    //Set m_bCollapsingBorders
    bool aCBorders = false;
    aTableAF.m_bCollapsingBorders = aCBorders;
    //Set m_aShadow
    SvxShadowItem aShadow( 0, nullptr, 103, SvxShadowLocation::BottomLeft );
    aTableAF.SetShadow(aShadow);
    //Set bInclFont
    bool aIFont = false;
    aTableAF.m_bInclFont = aIFont;
    //Set bInclJustify
    bool aIJustify = false;
    aTableAF.m_bInclJustify = aIJustify;
    //Set bInclFrame
    bool aIFrame = false;
    aTableAF.m_bInclFrame = aIFrame;
    //Set bInclBackground
    bool aIBackground = false;
    aTableAF.m_bInclBackground = aIBackground;
    //Set bInclValueFormat
    bool aIVFormat = false;
    aTableAF.m_bInclValueFormat = aIVFormat;

    //set the box format to AutoFormat
    aTableAF.SetBoxFormat( aBoxAF, sal_uInt8(0) );
    //add AutoFormat to AutoFormatTable
    aTableAFT.AddAutoFormat( aTableAF );

    //check the style size
    CPPUNIT_ASSERT_EQUAL( size_t(2),  aTableAFT.size() );

    //save the bInclFontstyles
    aTableAFT.Save();

    //check the style size after save
    CPPUNIT_ASSERT_EQUAL( size_t(2),  aTableAFT.size() );

    //create new AutoFormatTable
    SwTableAutoFormatTable aLoadTAFT;

    //check the style size after load
    CPPUNIT_ASSERT_EQUAL( size_t(2),  aLoadTAFT.size() );

    //assert the values
    SwTableAutoFormat* pLoadAF = aLoadTAFT.FindAutoFormat( TableStyleName(u"TestItemStyle"_ustr) );
    CPPUNIT_ASSERT( pLoadAF );
    const SwAutoFormatProps& rLoadFP = pLoadAF->GetBoxFormat(0).GetProps();
    //GetFont
    CPPUNIT_ASSERT( bool( rLoadFP.GetFont() == aFont ) );
    //GetHeight
    CPPUNIT_ASSERT( bool( rLoadFP.GetHeight() == aHeight ) );
    //GetWeight
    CPPUNIT_ASSERT( bool( rLoadFP.GetWeight() == aWeight ) );
    //GetPosture
    CPPUNIT_ASSERT( bool( rLoadFP.GetPosture() == aPosture ) );
    //GetCJKFont
    CPPUNIT_ASSERT( bool( rLoadFP.GetCJKFont() == aCJKFont ) );
    //GetCJKHeight
    CPPUNIT_ASSERT( bool( rLoadFP.GetCJKHeight() == aCJKHeight ) );
    //GetCJKWeight
    CPPUNIT_ASSERT( bool( rLoadFP.GetCJKWeight() == aCJKWeight ) );
    //GetCJKPosture
    CPPUNIT_ASSERT( bool( rLoadFP.GetCJKPosture() == aCJKPosture ) );
    //GetCTLFont
    CPPUNIT_ASSERT( bool( rLoadFP.GetCTLFont() == aCTLFont ) );
    //GetCTLHeight
    CPPUNIT_ASSERT( bool( rLoadFP.GetCTLHeight() == aCTLHeight ) );
    //GetCTLWeight
    CPPUNIT_ASSERT( bool( rLoadFP.GetCTLWeight() == aCTLWeight ) );
    //GetCTLPosture
    CPPUNIT_ASSERT( bool( rLoadFP.GetCTLPosture() == aCTLPosture ) );
    //GetUnderline
    CPPUNIT_ASSERT( bool( rLoadFP.GetUnderline() == aUnderline ) );
    //GetOverline
    CPPUNIT_ASSERT( bool( rLoadFP.GetOverline() == aOverline ) );
    //GetCrossedOut
    CPPUNIT_ASSERT( bool( rLoadFP.GetCrossedOut() == aCrossedOut ) );
    //GetContour
    CPPUNIT_ASSERT( bool( rLoadFP.GetContour() == aContour ) );
    //GetShadowed
    CPPUNIT_ASSERT( bool( rLoadFP.GetShadowed() == aShadowed ) );
    //GetColor
    CPPUNIT_ASSERT( bool( rLoadFP.GetColor() == aColor) );
    //GetAdjust
    CPPUNIT_ASSERT( bool( rLoadFP.GetAdjust() == aAdjust ) );
    //GetTextOrientation
    CPPUNIT_ASSERT( bool( rLoadFP.GetTextOrientation() == aTOrientation ) );
    //GetVerticalAlignment
    CPPUNIT_ASSERT (bool( rLoadFP.GetVerticalAlignment() == aVAlignment ) );
    //GetBox
    CPPUNIT_ASSERT( bool( rLoadFP.GetBox() == aBox ) );
    //GetBackground
    CPPUNIT_ASSERT( bool( rLoadFP.GetBackground() == aBackground ) );
    //Get m_aTLBR
    CPPUNIT_ASSERT( bool( rLoadFP.GetTLBR() == aTLBRLine ) );
    //Get m_aBLTR
    CPPUNIT_ASSERT( bool( rLoadFP.GetBLTR() == aBLTRLine ) );
    //Get m_aHorJustify
    CPPUNIT_ASSERT( bool( rLoadFP.GetHorJustify() == aHJustify ) );
    //Get m_aVerJustify
    CPPUNIT_ASSERT( bool( rLoadFP.GetVerJustify() == aVJustify ) );
    //Get m_aStacked
    CPPUNIT_ASSERT( bool( rLoadFP.GetStacked() == aStacked ) );
    //Get m_aMargin
    CPPUNIT_ASSERT( bool( rLoadFP.GetMargin() == aSvxMarginItem ) );
    //Get m_aLinebreak
    CPPUNIT_ASSERT( bool( rLoadFP.GetLinebreak() == aLBreak ) );
    //Get m_aRotateAngle
    CPPUNIT_ASSERT( bool( rLoadFP.GetRotateAngle() == aRAngle ) );
    //Get m_aRotateMode
    //SvxRotateModeItem aRMode = rBoxFP.m_aRotateMode;GetRotateMode
    CPPUNIT_ASSERT( bool( rLoadFP.GetRotateMode() == aSvxRotateModeItem ) );
    //Get m_sNumFormatString
    CPPUNIT_ASSERT( bool( rLoadFP.GetNumFormatString() == aNFString ) );
    //Get m_eSysLanguage
    CPPUNIT_ASSERT( bool( rLoadFP.GetSysLanguage() == aSLang ) );
    //Get m_eNumFormatLanguage
    CPPUNIT_ASSERT( bool( rLoadFP.GetNumFormatLanguage() == aNFLang ) );
    //Get m_aKeepWithNextPara
    CPPUNIT_ASSERT( bool( pLoadAF->GetKeepWithNextPara() == aKWNPara ) );
    //Get m_aRepeatHeading
    CPPUNIT_ASSERT( bool( pLoadAF->m_aRepeatHeading == aRHeading ) );
    //Get m_bLayoutSplit
    CPPUNIT_ASSERT( bool( pLoadAF->m_bLayoutSplit == aLSplit ) );
    //Get m_bRowSplit
    CPPUNIT_ASSERT( bool( pLoadAF->m_bRowSplit == aRSplit ) );
    //Get m_bCollapsingBorders
    CPPUNIT_ASSERT( bool( pLoadAF->m_bCollapsingBorders == aCBorders ) );
    //Get m_aShadow
    CPPUNIT_ASSERT( bool( pLoadAF->GetShadow() == aShadow ) );
    //Get bInclFont
    CPPUNIT_ASSERT( bool( pLoadAF->m_bInclFont == aIFont ) );
    //Get bInclJustify
    CPPUNIT_ASSERT( bool( pLoadAF->m_bInclJustify == aIJustify ) );
    //Get bInclFrame
    CPPUNIT_ASSERT( bool( pLoadAF->m_bInclFrame == aIFrame ) );
    //Get bInclBackground
    CPPUNIT_ASSERT( bool( pLoadAF->m_bInclBackground == aIBackground ) );
    //Get bInclValueFormat
    CPPUNIT_ASSERT( bool( pLoadAF->m_bInclValueFormat == aIVFormat ) );
}

static OUString
translitTest(SwDoc & rDoc, const SwPaM & rPaM, TransliterationFlags const nType)
{
    utl::TransliterationWrapper aTrans(
            ::comphelper::getProcessComponentContext(), nType);
    rDoc.getIDocumentContentOperations().TransliterateText(rPaM, aTrans);
    return rPaM.GetText();
}

void SwDocTest::testTransliterate()
{
    // just some simple test to see if it's totally broken
    SwNodeIndex aIdx(m_pDoc->GetNodes().GetEndOfContent(), -1);
    SwPaM aPaM(aIdx);
    m_pDoc->getIDocumentContentOperations().InsertString(aPaM, u"foobar"_ustr);
    aPaM.SetMark();
    aPaM.GetPoint()->nContent = 0;
    CPPUNIT_ASSERT_EQUAL(u"foobar"_ustr, aPaM.GetText());

    CPPUNIT_ASSERT_EQUAL(u"FOOBAR"_ustr,
            translitTest(*m_pDoc, aPaM,
                TransliterationFlags::LOWERCASE_UPPERCASE));
    CPPUNIT_ASSERT_EQUAL(u"Foobar"_ustr,
            translitTest(*m_pDoc, aPaM,
                TransliterationFlags::TITLE_CASE));
    CPPUNIT_ASSERT_EQUAL(u"fOOBAR"_ustr,
            translitTest(*m_pDoc, aPaM,
                TransliterationFlags::TOGGLE_CASE));
    CPPUNIT_ASSERT_EQUAL(u"foobar"_ustr,
            translitTest(*m_pDoc, aPaM,
                TransliterationFlags::UPPERCASE_LOWERCASE));
    CPPUNIT_ASSERT_EQUAL(u"Foobar"_ustr,
            translitTest(*m_pDoc, aPaM,
                TransliterationFlags::SENTENCE_CASE));
    CPPUNIT_ASSERT_EQUAL(u"Foobar"_ustr,
            translitTest(*m_pDoc, aPaM,
                TransliterationFlags::HIRAGANA_KATAKANA));

    m_pDoc->getIDocumentContentOperations().AppendTextNode(*aPaM.GetPoint());
    m_pDoc->getIDocumentContentOperations().InsertString(aPaM, u"one (two) three"_ustr);
    aPaM.SetMark();
    aPaM.GetMark()->nContent = 0;
    CPPUNIT_ASSERT_EQUAL(u"One (Two) Three"_ustr,
            translitTest(*m_pDoc, aPaM,
                TransliterationFlags::TITLE_CASE));
}

namespace
{
    class SwTableFormulaTest : public SwTableFormula
    {
        SwTableNode *m_pNode;
    public:
        SwTableFormulaTest(const OUString &rStr, SwTableNode *pNode)
            : SwTableFormula(rStr)
            , m_pNode(pNode)
        {
            m_eNmType = INTRNL_NAME;
        }
        virtual const SwNode* GetNodeOfFormula() const override
        {
            return m_pNode;
        }
    };
}

//tdf#66353 Expression is faulty
void SwDocTest::testFormulas()
{
    SwNodeIndex aIdx(m_pDoc->GetNodes().GetEndOfContent(), -1);
    SwPosition aPos(aIdx);

    const SwTable *pTable = m_pDoc->InsertTable(
        SwInsertTableOptions(SwInsertTableFlags::HeadlineNoBorder, 0), aPos, 1, 3, 0);
    SwTableNode* pTableNode = pTable->GetTableNode();
    SwTableFormulaTest aFormula(u"<\x12-1,0>+"_ustr, pTableNode);

    aFormula.PtrToBoxNm(pTable);

    CPPUNIT_ASSERT_EQUAL(u"+"_ustr, aFormula.GetFormula());

    // tdf#61228: Evaluating non-defined function should return an error
    SwCalc aCalc(*m_pDoc);
    SwSbxValue val = aCalc.Calculate(u"foobar()"_ustr);
    CPPUNIT_ASSERT(aCalc.IsCalcError());
    CPPUNIT_ASSERT(val.IsVoidValue());
    CPPUNIT_ASSERT(val.IsDouble());
    CPPUNIT_ASSERT_EQUAL(DBL_MAX, val.GetDouble());
    // Evaluating non-defined variable should return 0 without an error
    val = aCalc.Calculate(u"foobar"_ustr);
    CPPUNIT_ASSERT(!aCalc.IsCalcError());
    CPPUNIT_ASSERT(val.IsVoidValue());
    CPPUNIT_ASSERT(val.IsLong());
    CPPUNIT_ASSERT_EQUAL(sal_Int32(0), val.GetLong());
}

void SwDocTest::testMarkMove()
{
    IDocumentMarkAccess* pMarksAccess = m_pDoc->getIDocumentMarkAccess();

    {
        SwNodeIndex aIdx(m_pDoc->GetNodes().GetEndOfContent(), -1);
        SwPaM aPaM(aIdx);
        m_pDoc->getIDocumentContentOperations().InsertString(aPaM, u"Paragraph 1"_ustr);
        aPaM.SetMark();
        aPaM.GetMark()->nContent -= aPaM.GetMark()->GetContentIndex();
        pMarksAccess->makeMark(aPaM, SwMarkName(u"Para1"_ustr),
            IDocumentMarkAccess::MarkType::BOOKMARK, sw::mark::InsertMode::New);

        m_pDoc->getIDocumentContentOperations().AppendTextNode(*aPaM.GetPoint());
        m_pDoc->getIDocumentContentOperations().InsertString(aPaM, u"Paragraph 2"_ustr);
        aPaM.SetMark();
        aPaM.GetMark()->nContent -= aPaM.GetMark()->GetContentIndex();
        pMarksAccess->makeMark(aPaM, SwMarkName(u"Para2"_ustr),
            IDocumentMarkAccess::MarkType::BOOKMARK, sw::mark::InsertMode::New);

        m_pDoc->getIDocumentContentOperations().AppendTextNode(*aPaM.GetPoint());
        m_pDoc->getIDocumentContentOperations().InsertString(aPaM, u"Paragraph 3"_ustr);
        aPaM.SetMark();
        aPaM.GetMark()->nContent -= aPaM.GetMark()->GetContentIndex();
        pMarksAccess->makeMark(aPaM, SwMarkName(u"Para3"_ustr),
            IDocumentMarkAccess::MarkType::BOOKMARK, sw::mark::InsertMode::New);
    }

    // join paragraph 2 and 3 and check
    {
        SwNodeIndex aIdx(m_pDoc->GetNodes().GetEndOfContent(), -2);
        SwTextNode& rParaNode2 = dynamic_cast<SwTextNode&>(aIdx.GetNode());
        rParaNode2.JoinNext();
    }
    ::sw::mark::MarkBase* pBM1 = *pMarksAccess->findMark(SwMarkName(u"Para1"_ustr));
    ::sw::mark::MarkBase* pBM2 = *pMarksAccess->findMark(SwMarkName(u"Para2"_ustr));
    ::sw::mark::MarkBase* pBM3 = *pMarksAccess->findMark(SwMarkName(u"Para3"_ustr));

    CPPUNIT_ASSERT_EQUAL(sal_Int32(0) , pBM1->GetMarkStart().GetContentIndex());
    CPPUNIT_ASSERT_EQUAL(sal_Int32(11), pBM1->GetMarkEnd().GetContentIndex());
    CPPUNIT_ASSERT_EQUAL(
        pBM1->GetMarkStart().GetNodeIndex(),
        pBM1->GetMarkEnd().GetNodeIndex());

    CPPUNIT_ASSERT_EQUAL(sal_Int32(0) , pBM2->GetMarkStart().GetContentIndex());
    CPPUNIT_ASSERT_EQUAL(sal_Int32(11), pBM2->GetMarkEnd().GetContentIndex());
    CPPUNIT_ASSERT_EQUAL(
        pBM2->GetMarkStart().GetNodeIndex(),
        pBM2->GetMarkEnd().GetNodeIndex());

    CPPUNIT_ASSERT_EQUAL(sal_Int32(11), pBM3->GetMarkStart().GetContentIndex());
    CPPUNIT_ASSERT_EQUAL(sal_Int32(22), pBM3->GetMarkEnd().GetContentIndex());
    CPPUNIT_ASSERT_EQUAL(
        pBM3->GetMarkStart().GetNodeIndex(),
        pBM3->GetMarkEnd().GetNodeIndex());

    CPPUNIT_ASSERT_EQUAL(
        pBM1->GetMarkStart().GetNodeIndex()+1,
        pBM2->GetMarkStart().GetNodeIndex());
    CPPUNIT_ASSERT_EQUAL(
        pBM2->GetMarkStart().GetNodeIndex(),
        pBM3->GetMarkStart().GetNodeIndex());

    // cut some text
    {
        SwNodeIndex aIdx(m_pDoc->GetNodes().GetEndOfContent(), -1);
        SwPaM aPaM(aIdx, aIdx, SwNodeOffset(-1));
--> --------------------

--> maximum size reached

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

Messung V0.5
C=88 H=100 G=94

¤ 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
 




SSL  | sichere Verbindung  | Haftungsausschluß  | Kontakt  | Seitenstruktur  | © 2026 JDD |