/* -*- 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 <sal/config.h>
 
#include <o3tl/any.hxx>
#include <xmloff/unointerfacetouniqueidentifiermapper.hxx>
#include <rtl/ustrbuf.hxx>
#include <sal/types.h>
#include <sal/log.hxx>
#include <osl/diagnose.h>
#include <com/sun/star/frame/XModel.hpp>
#include <com/sun/star/lang/XServiceInfo.hpp>
#include <com/sun/star/container/XEnumerationAccess.hpp>
#include <com/sun/star/container/XEnumeration.hpp>
#include <com/sun/star/container/XIndexReplace.hpp>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/beans/XMultiPropertySet.hpp>
#include <com/sun/star/beans/XPropertyState.hpp>
#include <com/sun/star/beans/UnknownPropertyException.hpp>
#include <com/sun/star/graphic/XGraphic.hpp>
#include <com/sun/star/text/XTextSectionsSupplier.hpp>
#include <com/sun/star/text/XTextTablesSupplier.hpp>
#include <com/sun/star/text/XNumberingRulesSupplier.hpp>
#include <com/sun/star/text/XChapterNumberingSupplier.hpp>
#include <com/sun/star/text/XTextDocument.hpp>
#include <com/sun/star/text/XTextTable.hpp>
#include <com/sun/star/text/XText.hpp>
#include <com/sun/star/text/XTextContent.hpp>
#include <com/sun/star/text/XTextRange.hpp>
#include <com/sun/star/text/XTextField.hpp>
#include <com/sun/star/container/XNamed.hpp>
#include <com/sun/star/container/XContentEnumerationAccess.hpp>
#include <com/sun/star/text/XTextFrame.hpp>
#include <com/sun/star/container/XNameAccess.hpp>
#include <com/sun/star/text/SizeType.hpp>
#include <com/sun/star/text/HoriOrientation.hpp>
#include <com/sun/star/text/VertOrientation.hpp>
#include <com/sun/star/text/TextContentAnchorType.hpp>
#include <com/sun/star/text/XTextFramesSupplier.hpp>
#include <com/sun/star/text/XTextGraphicObjectsSupplier.hpp>
#include <com/sun/star/text/XTextEmbeddedObjectsSupplier.hpp>
#include <com/sun/star/drawing/XDrawPageSupplier.hpp>
#include <com/sun/star/document/XEventsSupplier.hpp>
#include <com/sun/star/document/XRedlinesSupplier.hpp>
#include <com/sun/star/text/XFormField.hpp>
#include <com/sun/star/text/XTextSection.hpp>
#include <com/sun/star/drawing/XShape.hpp>
#include <com/sun/star/style/XAutoStylesSupplier.hpp>
#include <com/sun/star/style/XAutoStyleFamily.hpp>
#include <com/sun/star/text/XTextFieldsSupplier.hpp>
#include <com/sun/star/drawing/XControlShape.hpp>
 
#include <sax/tools/converter.hxx>
 
#include <xmloff/xmlnamespace.hxx>
#include <xmloff/xmlaustp.hxx>
#include <xmloff/families.hxx>
#include "txtexppr.hxx"
#include <xmloff/xmluconv.hxx>
#include "XMLAnchorTypePropHdl.hxx"
#include <xexptran.hxx>
#include <xmloff/ProgressBarHelper.hxx>
#include <xmloff/namespacemap.hxx>
#include <xmloff/xmlexp.hxx>
#include <txtflde.hxx>
#include <xmloff/txtprmap.hxx>
#include <XMLImageMapExport.hxx>
#include "XMLTextNumRuleInfo.hxx"
#include <xmloff/XMLTextListAutoStylePool.hxx>
#include <xmloff/txtparae.hxx>
#include "XMLSectionExport.hxx"
#include "XMLIndexMarkExport.hxx"
#include <xmloff/XMLEventExport.hxx>
#include "XMLRedlineExport.hxx"
#include <MultiPropertySetHelper.hxx>
#include <xmloff/formlayerexport.hxx>
#include "XMLTextCharStyleNamesElementExport.hxx"
#include <xmloff/odffields.hxx>
#include <xmloff/maptype.hxx>
#include <basegfx/polygon/b2dpolypolygon.hxx>
#include <basegfx/polygon/b2dpolypolygontools.hxx>
#include <basegfx/polygon/b2dpolygontools.hxx>
#include <com/sun/star/embed/ElementModes.hpp>
#include <com/sun/star/embed/XTransactedObject.hpp>
#include <com/sun/star/document/XStorageBasedDocument.hpp>
#include <txtlists.hxx>
#include <com/sun/star/rdf/XMetadatable.hpp>
#include <list>
#include <unordered_map>
#include <memory>
#include <vector>
#include <algorithm>
#include <iterator>
#include <officecfg/Office/Common.hxx>
#include <o3tl/safeint.hxx>
#include <comphelper/scopeguard.hxx>
#include <comphelper/sequenceashashmap.hxx>
 
using namespace ::com::sun::star;
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::lang;
using namespace ::com::sun::star::beans;
using namespace ::com::sun::star::container;
using namespace ::com::sun::star::text;
using namespace ::com::sun::star::style;
using namespace ::com::sun::star::drawing;
using namespace ::com::sun::star::document;
using namespace ::com::sun::star::graphic;
using namespace ::xmloff;
using namespace ::xmloff::token;
 
// Implement Title/Description Elements UI (#i73249#)
constexpr OUString gsTitle(u"Title"_ustr);
constexpr OUString gsDescription(u"Description"_ustr);
constexpr OUStringLiteral gsAnchorPageNo(u"AnchorPageNo");
constexpr OUStringLiteral gsAnchorType(u"AnchorType");
constexpr OUString gsBookmark(u"Bookmark"_ustr);
constexpr OUString gsChainNextName(u"ChainNextName"_ustr);
constexpr OUString gsContourPolyPolygon(u"ContourPolyPolygon"_ustr);
constexpr OUStringLiteral gsDocumentIndexMark(u"DocumentIndexMark");
constexpr OUStringLiteral gsFrame(u"Frame");
constexpr OUStringLiteral gsGraphicFilter(u"GraphicFilter");
constexpr OUStringLiteral gsGraphicRotation(u"GraphicRotation");
constexpr OUString gsHeight(u"Height"_ustr);
constexpr OUStringLiteral gsHoriOrient(u"HoriOrient");
constexpr OUStringLiteral gsHoriOrientPosition(u"HoriOrientPosition");
constexpr OUString gsHyperLinkName(u"HyperLinkName"_ustr);
constexpr OUString gsHyperLinkTarget(u"HyperLinkTarget"_ustr);
constexpr OUString gsHyperLinkURL(u"HyperLinkURL"_ustr);
constexpr OUString gsIsAutomaticContour(u"IsAutomaticContour"_ustr);
constexpr OUString gsIsCollapsed(u"IsCollapsed"_ustr);
constexpr OUString gsIsPixelContour(u"IsPixelContour"_ustr);
constexpr OUString gsIsStart(u"IsStart"_ustr);
constexpr OUString gsIsSyncHeightToWidth(u"IsSyncHeightToWidth"_ustr);
constexpr OUString gsIsSyncWidthToHeight(u"IsSyncWidthToHeight"_ustr);
constexpr OUString gsNumberingRules(u"NumberingRules"_ustr);
constexpr OUString gsParaConditionalStyleName(u"ParaConditionalStyleName"_ustr);
constexpr OUStringLiteral gsParagraphService(u"com.sun.star.text.Paragraph");
constexpr OUStringLiteral gsRedline(u"Redline");
constexpr OUString gsReferenceMark(u"ReferenceMark"_ustr);
constexpr OUString gsRelativeHeight(u"RelativeHeight"_ustr);
constexpr OUString gsRelativeWidth(u"RelativeWidth"_ustr);
constexpr OUStringLiteral gsRuby(u"Ruby");
constexpr OUStringLiteral gsRubyCharStyleName(u"RubyCharStyleName");
constexpr OUStringLiteral gsRubyText(u"RubyText");
constexpr OUString gsServerMap(u"ServerMap"_ustr);
constexpr OUString gsShapeService(u"com.sun.star.drawing.Shape"_ustr);
constexpr OUString gsSizeType(u"SizeType"_ustr);
constexpr OUStringLiteral gsSoftPageBreak( u"SoftPageBreak"  );
constexpr OUStringLiteral gsTableService(u"com.sun.star.text.TextTable");
constexpr OUStringLiteral gsText(u"Text");
constexpr OUString gsTextContentService(u"com.sun.star.text.TextContent"_ustr);
constexpr OUStringLiteral gsTextEmbeddedService(u"com.sun.star.text.TextEmbeddedObject");
constexpr OUString gsTextField(u"TextField"_ustr);
constexpr OUStringLiteral gsTextFieldService(u"com.sun.star.text.TextField");
constexpr OUStringLiteral gsTextFrameService(u"com.sun.star.text.TextFrame");
constexpr OUStringLiteral gsTextGraphicService(u"com.sun.star.text.TextGraphicObject");
constexpr OUString gsTextPortionType(u"TextPortionType"_ustr);
constexpr OUString gsUnvisitedCharStyleName(u"UnvisitedCharStyleName"_ustr);
constexpr OUStringLiteral gsVertOrient(u"VertOrient");
constexpr OUStringLiteral gsVertOrientPosition(u"VertOrientPosition");
constexpr OUString gsVisitedCharStyleName(u"VisitedCharStyleName"_ustr);
constexpr OUString gsWidth(u"Width"_ustr);
constexpr OUString gsWidthType( u"WidthType"_ustr  );
constexpr OUStringLiteral gsTextFieldStart( u"TextFieldStart"  );
constexpr OUStringLiteral gsTextFieldSep(u"TextFieldSeparator");
constexpr OUStringLiteral gsTextFieldEnd( u"TextFieldEnd"  );
constexpr OUStringLiteral gsTextFieldStartEnd( u"TextFieldStartEnd"  );
 
namespace
{
    class TextContentSet
    {
        public:
            typedef std::list<Reference<XTextContent>> contents_t;
            typedef std::back_insert_iterator<contents_t> inserter_t;
            typedef contents_t::const_iterator const_iterator_t;
 
            inserter_t getInserter()
                { return std::back_insert_iterator<contents_t>(m_vTextContents); };
            const_iterator_t getBegin() const
                { return m_vTextContents.begin(); };
            const_iterator_t getEnd() const
                { return m_vTextContents.end(); };
 
        private:
            contents_t m_vTextContents;
    };
 
    struct FrameRefHash
    {
        size_t operator()(const Reference<XTextFrame>& rFrame) const
            { return sal::static_int_cast<size_t>(reinterpret_cast<sal_uIntPtr>(rFrame.get())); }
    };
 
    bool lcl_TextContentsUnfiltered(const Reference<XTextContent>&)
        { return true; };
 
    bool lcl_ShapeFilter(const Reference<XTextContent>& xTxtContent)
    {
        Reference<XShape> xShape(xTxtContent, UNO_QUERY);
        if(!xShape.is())
            return false;
        Reference<XServiceInfo> xServiceInfo(xTxtContent, UNO_QUERY);
        return !xServiceInfo->supportsService(u"com.sun.star.text.TextFrame"_ustr) &&
               !xServiceInfo->supportsService(u"com.sun.star.text.TextGraphicObject"_ustr) &&
               !xServiceInfo->supportsService(u"com.sun.star.text.TextEmbeddedObject"_ustr);
    };
 
    class BoundFrames
    {
        public:
            typedef bool (*filter_t)(const Reference<XTextContent>&);
            BoundFrames(
                const Reference<XEnumerationAccess>& rEnumAccess,
                const filter_t& rFilter)
                : m_xEnumAccess(rEnumAccess)
            {
                Fill(rFilter);
            };
            BoundFrames()
                {};
            const TextContentSet& GetPageBoundContents() const
                { return m_vPageBounds; };
            const TextContentSet* GetFrameBoundContents(const Reference<XTextFrame>& rParentFrame) const
            {
                framebound_map_t::const_iterator it = m_vFrameBoundsOf.find(rParentFrame);
                if(it == m_vFrameBoundsOf.end())
                    return nullptr;
                return &(it->second);
            };
            Reference<XEnumeration> createEnumeration() const
            {
                if(!m_xEnumAccess.is())
                    return Reference<XEnumeration>();
                return m_xEnumAccess->createEnumeration();
            };
 
        private:
            typedef std::unordered_map<
                Reference<XTextFrame>,
                TextContentSet,
                FrameRefHash> framebound_map_t;
            TextContentSet m_vPageBounds;
            framebound_map_t m_vFrameBoundsOf;
            const Reference<XEnumerationAccess> m_xEnumAccess;
            void Fill(const filter_t& rFilter);
    };
 
    class FieldParamExporter
    {
        public:
            FieldParamExporter(SvXMLExport* const pExport, Reference<XNameContainer> const & xFieldParams)
                : m_pExport(pExport)
                , m_xFieldParams(xFieldParams)
                { };
            void Export();
 
        private:
            SvXMLExport* const m_pExport;
            const Reference<XNameContainer> m_xFieldParams;
 
            void ExportParameter(const OUString& sKey, const OUString& sValue);
    };
 
    struct HyperlinkData
    {
        OUString href, name, targetFrame, ustyleName, vstyleName;
        bool serverMap = false;
        css::uno::Reference<css::container::XNameReplace> events;
 
        HyperlinkData() = default;
        HyperlinkData(const css::uno::Reference<css::beans::XPropertySet>& rPropSet);
 
        bool operator==(const HyperlinkData&);
        bool operator!=(const HyperlinkData& rOther) { return !operator==(rOther); }
 
        bool addHyperlinkAttributes(SvXMLExport& rExport);
        void exportEvents(SvXMLExport& rExport);
    };
 
    HyperlinkData::HyperlinkData(const css::uno::Reference<css::beans::XPropertySet>& rPropSet)
    {
        const css::uno::Reference<css::beans::XPropertyState> xPropState(rPropSet, UNO_QUERY);
        const auto xPropSetInfo(rPropSet->getPropertySetInfo());
        if (xPropSetInfo->hasPropertyByName(gsHyperLinkURL)
            && (!xPropState.is()
                || PropertyState_DIRECT_VALUE == xPropState->getPropertyState(gsHyperLinkURL)))
        {
            rPropSet->getPropertyValue(gsHyperLinkURL) >>= href;
        }
 
        if (href.isEmpty())
            return;
 
        if (xPropSetInfo->hasPropertyByName(gsHyperLinkName)
            && (!xPropState.is()
                || PropertyState_DIRECT_VALUE == xPropState->getPropertyState(gsHyperLinkName)))
        {
            rPropSet->getPropertyValue(gsHyperLinkName) >>= name;
        }
 
        if (xPropSetInfo->hasPropertyByName(gsHyperLinkTarget)
            && (!xPropState.is()
                || PropertyState_DIRECT_VALUE == xPropState->getPropertyState(gsHyperLinkTarget)))
        {
            rPropSet->getPropertyValue(gsHyperLinkTarget) >>= targetFrame;
        }
 
        if (xPropSetInfo->hasPropertyByName(gsServerMap)
            && (!xPropState.is()
                || PropertyState_DIRECT_VALUE == xPropState->getPropertyState(gsServerMap)))
        {
            serverMap = *o3tl::doAccess<bool>(rPropSet->getPropertyValue(gsServerMap));
        }
 
        if (xPropSetInfo->hasPropertyByName(gsUnvisitedCharStyleName)
            && (!xPropState.is()
                || PropertyState_DIRECT_VALUE
                       == xPropState->getPropertyState(gsUnvisitedCharStyleName)))
        {
            rPropSet->getPropertyValue(gsUnvisitedCharStyleName) >>= ustyleName;
        }
 
        if (xPropSetInfo->hasPropertyByName(gsVisitedCharStyleName)
            && (!xPropState.is()
                || PropertyState_DIRECT_VALUE
                       == xPropState->getPropertyState(gsVisitedCharStyleName)))
        {
            rPropSet->getPropertyValue(gsVisitedCharStyleName) >>= vstyleName;
        }
 
        static constexpr OUString sHyperLinkEvents(u"HyperLinkEvents"_ustr);
        if (xPropSetInfo->hasPropertyByName(sHyperLinkEvents))
        {
            events.set(rPropSet->getPropertyValue(sHyperLinkEvents), uno::UNO_QUERY);
        }
    }
 
    bool HyperlinkData::operator==(const HyperlinkData& rOther)
    {
        if (href != rOther.href || name != rOther.name || targetFrame != rOther.targetFrame
            || ustyleName != rOther.ustyleName || vstyleName != rOther.vstyleName
            || serverMap != rOther.serverMap)
            return false;
 
        if (events == rOther.events)
            return true;
        if (!events || !rOther.events)
            return false;
 
        const css::uno::Sequence<OUString> aNames = events->getElementNames();
        if (aNames != rOther.events->getElementNames())
            return false;
        for (const auto& rName : aNames)
        {
            const css::uno::Any aAny = events->getByName(rName);
            const css::uno::Any aOtherAny = rOther.events->getByName(rName);
            if (aAny != aOtherAny)
                return false;
        }
        return true;
    }
 
    bool HyperlinkData::addHyperlinkAttributes(SvXMLExport& rExport)
    {
        if (href.isEmpty())
        {
            // hyperlink without a URL does not make sense
            OSL_ENSURE(false, "hyperlink without a URL --> no export to ODF");
            return false;
        }
 
        rExport.AddAttribute(XML_NAMESPACE_XLINK, XML_TYPE, XML_SIMPLE);
        rExport.AddAttribute(XML_NAMESPACE_XLINK, XML_HREF, rExport.GetRelativeReference(href));
 
        if (!name.isEmpty())
            rExport.AddAttribute(XML_NAMESPACE_OFFICE, XML_NAME, name);
 
        if (!targetFrame.isEmpty())
        {
            rExport.AddAttribute(XML_NAMESPACE_OFFICE, XML_TARGET_FRAME_NAME, targetFrame);
            enum XMLTokenEnum eTok = targetFrame == "_blank" ? XML_NEW : XML_REPLACE;
            rExport.AddAttribute(XML_NAMESPACE_XLINK, XML_SHOW, eTok);
        }
 
        if (serverMap)
            rExport.AddAttribute(XML_NAMESPACE_OFFICE, XML_SERVER_MAP, XML_TRUE);
 
        if (!ustyleName.isEmpty())
            rExport.AddAttribute(XML_NAMESPACE_TEXT, XML_STYLE_NAME,
                                 rExport.EncodeStyleName(ustyleName));
 
        if (!vstyleName.isEmpty())
            rExport.AddAttribute(XML_NAMESPACE_TEXT, XML_VISITED_STYLE_NAME,
                                 rExport.EncodeStyleName(vstyleName));
 
        return true;
    }
 
    void HyperlinkData::exportEvents(SvXMLExport& rExport)
    {
        // export events (if supported)
        if (events)
            rExport.GetEventExport().Export(events, false);
    }
}
 
namespace xmloff
{
    class BoundFrameSets
    {
        public:
            explicit BoundFrameSets(const Reference<XInterface>& rModel);
            const BoundFrames* GetTexts() const
                { return m_pTexts.get(); };
            const BoundFrames* GetGraphics() const
                { return m_pGraphics.get(); };
            const BoundFrames* GetEmbeddeds() const
                { return m_pEmbeddeds.get(); };
            const BoundFrames* GetShapes() const
                { return m_pShapes.get(); };
        private:
            std::unique_ptr<BoundFrames> m_pTexts;
            std::unique_ptr<BoundFrames> m_pGraphics;
            std::unique_ptr<BoundFrames> m_pEmbeddeds;
            std::unique_ptr<BoundFrames> m_pShapes;
    };
}
 
#ifdef DBG_UTIL
static bool txtparae_bContainsIllegalCharacters = false;
#endif
 
// The following map shows which property values are required:
 
// property                     auto style pass     export
 
// ParaStyleName                if style exists     always
// ParaConditionalStyleName     if style exists     always
// NumberingRules               if style exists     always
// TextSection                  always              always
// ParaChapterNumberingLevel    never               always
// NumberingIsNumber            never               always
 
// The conclusion is that for auto styles the first three properties
// should be queried using a multi property set if, and only if, an
// auto style needs to be exported. TextSection should be queried by
// an individual call to getPropertyvalue, because this seems to be
// less expensive than querying the first three properties if they aren't
// required.
 
// For the export pass all properties can be queried using a multi property
// set.
 
constexpr OUString aParagraphPropertyNamesAuto[] =
{
    u"NumberingRules"_ustr,
    u"ParaConditionalStyleName"_ustr,
    u"ParaStyleName"_ustr
};
 
namespace {
 
enum eParagraphPropertyNamesEnumAuto
{
    NUMBERING_RULES_AUTO = 0,
    PARA_CONDITIONAL_STYLE_NAME_AUTO = 1,
    PARA_STYLE_NAME_AUTO = 2
};
 
}
 
constexpr OUString aParagraphPropertyNames[] =
{
    u"NumberingIsNumber"_ustr,
    u"NumberingStyleName"_ustr,
    u"OutlineLevel"_ustr,
    u"ParaConditionalStyleName"_ustr,
    u"ParaStyleName"_ustr,
    u"TextSection"_ustr,
    u"OutlineContentVisible"_ustr
};
 
namespace {
 
enum eParagraphPropertyNamesEnum
{
    NUMBERING_IS_NUMBER = 0,
    PARA_NUMBERING_STYLENAME = 1,
    PARA_OUTLINE_LEVEL=2,
    PARA_CONDITIONAL_STYLE_NAME = 3,
    PARA_STYLE_NAME = 4,
    TEXT_SECTION = 5,
    PARA_OUTLINE_CONTENT_VISIBLE = 6
};
 
}
 
void BoundFrames::Fill(const filter_t& rFilter)
{
    if(!m_xEnumAccess.is())
        return;
    const Reference< XEnumeration > xEnum = m_xEnumAccess->createEnumeration();
    if(!xEnum.is())
        return;
    static constexpr OUStringLiteral our_sAnchorType(u"AnchorType");
    static constexpr OUStringLiteral our_sAnchorFrame(u"AnchorFrame");
    while(xEnum->hasMoreElements())
    {
        Reference<XPropertySet> xPropSet(xEnum->nextElement(), UNO_QUERY);
        Reference<XTextContent> xTextContent(xPropSet, UNO_QUERY);
        if(!xPropSet.is() || !xTextContent.is())
            continue;
        TextContentAnchorType eAnchor;
        xPropSet->getPropertyValue(our_sAnchorType) >>= eAnchor;
        if(TextContentAnchorType_AT_PAGE != eAnchor && TextContentAnchorType_AT_FRAME != eAnchor)
            continue;
        if(!rFilter(xTextContent))
            continue;
 
        TextContentSet::inserter_t pInserter = m_vPageBounds.getInserter();
        if(TextContentAnchorType_AT_FRAME == eAnchor)
        {
            Reference<XTextFrame> xAnchorTxtFrame(
                xPropSet->getPropertyValue(our_sAnchorFrame),
                uno::UNO_QUERY);
            pInserter = m_vFrameBoundsOf[xAnchorTxtFrame].getInserter();
        }
        *pInserter++ = xTextContent;
    }
}
 
BoundFrameSets::BoundFrameSets(const Reference<XInterface>& rModel)
    : m_pTexts(new BoundFrames())
    , m_pGraphics(new BoundFrames())
    , m_pEmbeddeds(new BoundFrames())
    , m_pShapes(new BoundFrames())
{
    const Reference<XTextFramesSupplier> xTFS(rModel, UNO_QUERY);
    const Reference<XTextGraphicObjectsSupplier> xGOS(rModel, UNO_QUERY);
    const Reference<XTextEmbeddedObjectsSupplier> xEOS(rModel, UNO_QUERY);
    const Reference<XDrawPageSupplier> xDPS(rModel, UNO_QUERY);
    if(xTFS.is())
        m_pTexts.reset(new BoundFrames(
            Reference<XEnumerationAccess>(xTFS->getTextFrames(), UNO_QUERY),
            &lcl_TextContentsUnfiltered));
    if(xGOS.is())
        m_pGraphics.reset(new BoundFrames(
            Reference<XEnumerationAccess>(xGOS->getGraphicObjects(), UNO_QUERY),
            &lcl_TextContentsUnfiltered));
    if(xEOS.is())
        m_pEmbeddeds.reset(new BoundFrames(
            Reference<XEnumerationAccess>(xEOS->getEmbeddedObjects(), UNO_QUERY),
            &lcl_TextContentsUnfiltered));
    if(xDPS.is())
        m_pShapes.reset(new BoundFrames(
            Reference<XEnumerationAccess>(xDPS->getDrawPage(), UNO_QUERY),
            &lcl_ShapeFilter));
};
 
void FieldParamExporter::Export()
{
    const Type aStringType = ::cppu::UnoType<OUString>::get();
    const Type aBoolType = cppu::UnoType<sal_Bool>::get();
    const Type aSeqType = cppu::UnoType<Sequence<OUString>>::get();
    const Type aIntType = ::cppu::UnoType<sal_Int32>::get();
    const Sequence<OUString> vParameters(m_xFieldParams->getElementNames());
    for(const auto & rParameter : vParameters)
    {
        const Any aValue = m_xFieldParams->getByName(rParameter);
        const Type& aValueType = aValue.getValueType();
        if(aValueType == aStringType)
        {
            OUString sValue;
            aValue >>= sValue;
            ExportParameter(rParameter,sValue);
 
            if ( rParameter == ODF_OLE_PARAM )
            {
                // Save the OLE object
                Reference< embed::XStorage > xTargetStg = m_pExport->GetTargetStorage();
                if (xTargetStg.is()) {
                    Reference< embed::XStorage > xDstStg = xTargetStg->openStorageElement(
                        u"OLELinks"_ustr, embed::ElementModes::WRITE );
 
                    if ( !xDstStg->hasByName( sValue ) ) {
                        Reference< XStorageBasedDocument > xStgDoc (
                            m_pExport->GetModel( ), UNO_QUERY );
                        Reference< embed::XStorage > xDocStg = xStgDoc->getDocumentStorage();
                        Reference< embed::XStorage > xOleStg = xDocStg->openStorageElement(
                            u"OLELinks"_ustr, embed::ElementModes::READ );
 
                        xOleStg->copyElementTo( sValue, xDstStg, sValue );
                        Reference< embed::XTransactedObject > xTransact( xDstStg, UNO_QUERY );
                        if ( xTransact.is( ) )
                            xTransact->commit( );
                    }
                } else {
                    SAL_WARN("xmloff", "no target storage");
                }
            }
        }
        else if(aValueType == aBoolType)
        {
            bool bValue = false;
            aValue >>= bValue;
            ExportParameter(rParameter, OUString::boolean(bValue) );
        }
        else if(aValueType == aSeqType)
        {
            Sequence<OUString> vValue;
            aValue >>= vValue;
            for (const OUString& i : vValue)
            {
                ExportParameter(rParameter, i);
            }
        }
        else if(aValueType == aIntType)
        {
            sal_Int32 nValue = 0;
            aValue >>= nValue;
            ExportParameter(rParameter, OUString::number(nValue));
        }
    }
}
 
void FieldParamExporter::ExportParameter(const OUString& sKey, const OUString& sValue)
{
    m_pExport->AddAttribute(XML_NAMESPACE_FIELD, XML_NAME, sKey);
    m_pExport->AddAttribute(XML_NAMESPACE_FIELD, XML_VALUE, sValue);
    m_pExport->StartElement(XML_NAMESPACE_FIELD, XML_PARAM, false);
    m_pExport->EndElement(XML_NAMESPACE_FIELD, XML_PARAM, false);
}
 
void XMLTextParagraphExport::Add( XmlStyleFamily nFamily,
                                  const Reference < XPropertySet > & rPropSet,
                                  const std::span<const XMLPropertyState> aAddStates,
                                  bool bDontSeek )
{
    rtl::Reference < SvXMLExportPropertyMapper > xPropMapper;
    switch( nFamily )
    {
    case XmlStyleFamily::TEXT_PARAGRAPH:
        xPropMapper = GetParaPropMapper();
        break;
    case XmlStyleFamily::TEXT_TEXT:
        xPropMapper = GetTextPropMapper();
        break;
    case XmlStyleFamily::TEXT_FRAME:
        xPropMapper = GetAutoFramePropMapper();
        break;
    case XmlStyleFamily::TEXT_SECTION:
        xPropMapper = GetSectionPropMapper();
        break;
    case XmlStyleFamily::TEXT_RUBY:
        xPropMapper = GetRubyPropMapper();
        break;
    default: break;
    }
    SAL_WARN_IF( !xPropMapper.is(), "xmloff", "There is the property mapper?" );
 
    std::vector< XMLPropertyState > aPropStates =
            xPropMapper->Filter(GetExport(), rPropSet);
 
    aPropStates.insert( aPropStates.end(), aAddStates.begin(), aAddStates.end() );
 
    if( aPropStates.empty() )
        return;
 
    Reference< XPropertySetInfo > xPropSetInfo(rPropSet->getPropertySetInfo());
    OUString sParent, sCondParent;
    switch( nFamily )
    {
    case XmlStyleFamily::TEXT_PARAGRAPH:
        if( xPropSetInfo->hasPropertyByName( gsParaStyleName ) )
        {
            rPropSet->getPropertyValue( gsParaStyleName ) >>= sParent;
        }
        if( xPropSetInfo->hasPropertyByName( gsParaConditionalStyleName ) )
        {
            rPropSet->getPropertyValue( gsParaConditionalStyleName ) >>= sCondParent;
        }
        if( xPropSetInfo->hasPropertyByName( gsNumberingRules ) )
        {
            Reference < XIndexReplace > xNumRule(rPropSet->getPropertyValue( gsNumberingRules ), uno::UNO_QUERY);
            if( xNumRule.is() && xNumRule->getCount() )
            {
                Reference < XNamed > xNamed( xNumRule, UNO_QUERY );
                OUString sName;
                if( xNamed.is() )
                    sName = xNamed->getName();
                bool bAdd = sName.isEmpty();
                if( !bAdd )
                {
                    Reference < XPropertySet > xNumPropSet( xNumRule,
                                                            UNO_QUERY );
                    if( xNumPropSet.is() &&
                        xNumPropSet->getPropertySetInfo()
                                   ->hasPropertyByName( u"IsAutomatic"_ustr ) )
                    {
                        bAdd = *o3tl::doAccess<bool>(xNumPropSet->getPropertyValue( u"IsAutomatic"_ustr ));
                        // Check on outline style (#i73361#)
                        if ( bAdd &&
                             xNumPropSet->getPropertySetInfo()
                                       ->hasPropertyByName( u"NumberingIsOutline"_ustr ) )
                        {
                            bAdd = !(*o3tl::doAccess<bool>(xNumPropSet->getPropertyValue( u"NumberingIsOutline"_ustr )));
                        }
                    }
                    else
                    {
                        bAdd = true;
                    }
                }
                if( bAdd )
                    maListAutoPool.Add( xNumRule );
            }
        }
        break;
    case XmlStyleFamily::TEXT_TEXT:
        {
            // Get parent and remove hyperlinks (they aren't of interest)
            rtl::Reference< XMLPropertySetMapper > xPM(xPropMapper->getPropertySetMapper());
            sal_uInt16 nIgnoreProps = 0;
            for( ::std::vector< XMLPropertyState >::iterator i(aPropStates.begin());
                  nIgnoreProps < 2 && i != aPropStates.end(); )
            {
                if( i->mnIndex == -1 )
                {
                    ++i;
                    continue;
                }
 
                switch( xPM->GetEntryContextId(i->mnIndex) )
                {
                case CTF_CHAR_STYLE_NAME:
                case CTF_HYPERLINK_URL:
                    i->mnIndex = -1;
                    nIgnoreProps++;
                    i = aPropStates.erase( i );
                    break;
                default:
                    ++i;
                    break;
                }
            }
        }
        break;
    case XmlStyleFamily::TEXT_FRAME:
        if( xPropSetInfo->hasPropertyByName( gsFrameStyleName ) )
        {
            rPropSet->getPropertyValue( gsFrameStyleName ) >>= sParent;
        }
        break;
    case XmlStyleFamily::TEXT_SECTION:
    case XmlStyleFamily::TEXT_RUBY:
        ; // section styles have no parents
        break;
    default: break;
    }
    if (aPropStates.size()) // could change after the previous check
    {
        GetAutoStylePool().Add( nFamily, sParent, std::vector(aPropStates), bDontSeek );
        if( !sCondParent.isEmpty() && sParent != sCondParent )
            GetAutoStylePool().Add( nFamily, sCondParent, std::move(aPropStates) );
    }
}
 
static bool lcl_validPropState( const XMLPropertyState& rState )
{
    return rState.mnIndex != -1;
}
 
void XMLTextParagraphExport::Add( XmlStyleFamily nFamily,
                                  MultiPropertySetHelper& rPropSetHelper,
                                  const Reference < XPropertySet > & rPropSet)
{
    rtl::Reference < SvXMLExportPropertyMapper > xPropMapper;
    switch( nFamily )
    {
    case XmlStyleFamily::TEXT_PARAGRAPH:
        xPropMapper = GetParaPropMapper();
        break;
    default: break;
    }
    SAL_WARN_IF( !xPropMapper.is(), "xmloff", "There is the property mapper?" );
 
    std::vector<XMLPropertyState> aPropStates(xPropMapper->Filter(GetExport(), rPropSet));
 
    if( rPropSetHelper.hasProperty( NUMBERING_RULES_AUTO ) )
    {
        Reference < XIndexReplace > xNumRule(rPropSetHelper.getValue( NUMBERING_RULES_AUTO,
            rPropSet, true ), uno::UNO_QUERY);
        if( xNumRule.is() && xNumRule->getCount() )
        {
            Reference < XNamed > xNamed( xNumRule, UNO_QUERY );
            OUString sName;
            if( xNamed.is() )
                sName = xNamed->getName();
            bool bAdd = sName.isEmpty();
            if( !bAdd )
            {
                Reference < XPropertySet > xNumPropSet( xNumRule,
                                                        UNO_QUERY );
                if( xNumPropSet.is() &&
                    xNumPropSet->getPropertySetInfo()
                               ->hasPropertyByName( u"IsAutomatic"_ustr ) )
                {
                    bAdd = *o3tl::doAccess<bool>(xNumPropSet->getPropertyValue( u"IsAutomatic"_ustr ));
                    // Check on outline style (#i73361#)
                    if ( bAdd &&
                         xNumPropSet->getPropertySetInfo()
                                   ->hasPropertyByName( u"NumberingIsOutline"_ustr ) )
                    {
                        bAdd = !(*o3tl::doAccess<bool>(xNumPropSet->getPropertyValue( u"NumberingIsOutline"_ustr )));
                    }
                }
                else
                {
                    bAdd = true;
                }
            }
            if( bAdd )
                maListAutoPool.Add( xNumRule );
        }
    }
 
    if( aPropStates.empty() )
        return;
 
    OUString sParent, sCondParent;
    switch( nFamily )
    {
    case XmlStyleFamily::TEXT_PARAGRAPH:
        if( rPropSetHelper.hasProperty( PARA_STYLE_NAME_AUTO ) )
        {
            rPropSetHelper.getValue( PARA_STYLE_NAME_AUTO, rPropSet,
                                            true ) >>= sParent;
        }
        if( rPropSetHelper.hasProperty( PARA_CONDITIONAL_STYLE_NAME_AUTO ) )
        {
            rPropSetHelper.getValue( PARA_CONDITIONAL_STYLE_NAME_AUTO,
                                             rPropSet, true ) >>= sCondParent;
        }
 
        break;
    default: break;
    }
 
    if( std::any_of( aPropStates.begin(), aPropStates.end(), lcl_validPropState ) )
    {
        GetAutoStylePool().Add( nFamily, sParent, std::vector(aPropStates) );
        if( !sCondParent.isEmpty() && sParent != sCondParent )
            GetAutoStylePool().Add( nFamily, sCondParent, std::move(aPropStates) );
    }
}
 
OUString XMLTextParagraphExport::Find(
        XmlStyleFamily nFamily,
        const Reference < XPropertySet > & rPropSet,
        const OUString& rParent,
        const std::span<const XMLPropertyState> aAddStates) const
{
    OUString sName( rParent );
    rtl::Reference < SvXMLExportPropertyMapper > xPropMapper;
    switch( nFamily )
    {
    case XmlStyleFamily::TEXT_PARAGRAPH:
        xPropMapper = GetParaPropMapper();
        break;
    case XmlStyleFamily::TEXT_FRAME:
        xPropMapper = GetAutoFramePropMapper();
        break;
    case XmlStyleFamily::TEXT_SECTION:
        xPropMapper = GetSectionPropMapper();
        break;
    case XmlStyleFamily::TEXT_RUBY:
        xPropMapper = GetRubyPropMapper();
        break;
    default: break;
    }
    SAL_WARN_IF( !xPropMapper.is(), "xmloff", "There is the property mapper?" );
    if( !xPropMapper.is() )
        return sName;
    std::vector<XMLPropertyState> aPropStates(xPropMapper->Filter(GetExport(), rPropSet));
    aPropStates.insert( aPropStates.end(), aAddStates.begin(), aAddStates.end() );
    if( std::any_of( aPropStates.begin(), aPropStates.end(), lcl_validPropState ) )
        sName = GetAutoStylePool().Find( nFamily, sName, aPropStates );
 
    return sName;
}
 
OUString XMLTextParagraphExport::FindTextStyle(
           const Reference < XPropertySet > & rPropSet,
        bool& rbHasCharStyle,
        bool& rbHasAutoStyle,
        const XMLPropertyState** ppAddStates ) const
{
    rtl::Reference < SvXMLExportPropertyMapper > xPropMapper(GetTextPropMapper());
    std::vector<XMLPropertyState> aPropStates(xPropMapper->Filter(GetExport(), rPropSet));
 
    // Get parent and remove hyperlinks (they aren't of interest)
    OUString sName;
    rbHasCharStyle = rbHasAutoStyle = false;
    sal_uInt16 nIgnoreProps = 0;
    rtl::Reference< XMLPropertySetMapper > xPM(xPropMapper->getPropertySetMapper());
    ::std::vector< XMLPropertyState >::iterator aFirstDel = aPropStates.end();
    ::std::vector< XMLPropertyState >::iterator aSecondDel = aPropStates.end();
 
    for( ::std::vector< XMLPropertyState >::iterator
            i = aPropStates.begin();
         nIgnoreProps < 2 && i != aPropStates.end();
         ++i )
    {
        if( i->mnIndex == -1 )
            continue;
 
        switch( xPM->GetEntryContextId(i->mnIndex) )
        {
        case CTF_CHAR_STYLE_NAME:
            i->maValue >>= sName;
            i->mnIndex = -1;
            rbHasCharStyle = !sName.isEmpty();
            if( nIgnoreProps )
                aSecondDel = i;
            else
                aFirstDel = i;
            nIgnoreProps++;
            break;
        case CTF_HYPERLINK_URL:
            i->mnIndex = -1;
            if( nIgnoreProps )
                aSecondDel = i;
            else
                aFirstDel = i;
            nIgnoreProps++;
            break;
        }
    }
    if( ppAddStates )
    {
        while( *ppAddStates )
        {
            aPropStates.push_back( **ppAddStates );
            ppAddStates++;
        }
    }
    if (aPropStates.size() - nIgnoreProps)
    {
        // erase the character style, otherwise the autostyle cannot be found!
        // erase the hyperlink, otherwise the autostyle cannot be found!
        if ( nIgnoreProps )
        {
            // If two elements of a vector have to be deleted,
            // we should delete the second one first.
            if( --nIgnoreProps )
                aPropStates.erase( aSecondDel );
            aPropStates.erase( aFirstDel );
        }
        sName = GetAutoStylePool().Find(
            XmlStyleFamily::TEXT_TEXT,
            OUString(), // AutoStyles should not have parents!
            aPropStates );
        rbHasAutoStyle = true;
    }
 
    return sName;
}
 
// adjustments to support lists independent from list style
void XMLTextParagraphExport::exportListChange(
        const XMLTextNumRuleInfo& rPrevInfo,
        const XMLTextNumRuleInfo& rNextInfo )
{
    // end a list
    if ( rPrevInfo.GetLevel() > 0 )
    {
        sal_uInt32 nListLevelsToBeClosed = 0; // unsigned larger type to safely multiply and compare
        if ( !rNextInfo.BelongsToSameList( rPrevInfo ) ||
             rNextInfo.GetLevel() <= 0 )
        {
            // close complete previous list
            nListLevelsToBeClosed = rPrevInfo.GetLevel();
        }
        else if ( rPrevInfo.GetLevel() > rNextInfo.GetLevel() )
        {
            // close corresponding sub lists
            nListLevelsToBeClosed = rPrevInfo.GetLevel() - rNextInfo.GetLevel();
        }
 
        if ( nListLevelsToBeClosed > 0 &&
             maListElements.size() >= 2 * nListLevelsToBeClosed )
        {
            do {
                for(size_t j = 0; j < 2; ++j)
                {
                    OUString aElem(maListElements.back());
                    maListElements.pop_back();
                    GetExport().EndElement(aElem, true);
                }
 
                // remove closed list from list stack
                mpTextListsHelper->PopListFromStack();
 
                --nListLevelsToBeClosed;
            } while ( nListLevelsToBeClosed > 0 );
        }
    }
 
    // start a new list
    if ( rNextInfo.GetLevel() > 0 )
    {
        bool bRootListToBeStarted = false;
        sal_Int16 nListLevelsToBeOpened = 0;
        if ( !rPrevInfo.BelongsToSameList( rNextInfo ) ||
             rPrevInfo.GetLevel() <= 0 )
        {
            // new root list
            bRootListToBeStarted = true;
            nListLevelsToBeOpened = rNextInfo.GetLevel();
        }
        else if ( rNextInfo.GetLevel() > rPrevInfo.GetLevel() )
        {
            // open corresponding sub lists
            nListLevelsToBeOpened = rNextInfo.GetLevel() - rPrevInfo.GetLevel();
        }
 
        if ( nListLevelsToBeOpened > 0 )
        {
            const OUString& sListStyleName( rNextInfo.GetNumRulesName() );
            // Currently only the text documents support <ListId>.
            // Thus, for other document types <sListId> is empty.
            const OUString& sListId( rNextInfo.GetListId() );
            bool bExportListStyle( true );
            bool bRestartNumberingAtContinuedList( false );
            sal_Int32 nRestartValueForContinuedList( -1 );
            bool bContinueingPreviousSubList = !bRootListToBeStarted &&
                                               rNextInfo.IsContinueingPreviousSubTree();
            do {
                GetExport().CheckAttrList();
 
                if ( bRootListToBeStarted )
                {
                    if ( !mpTextListsHelper->IsListProcessed( sListId ) )
                    {
                        if ( ExportListId() &&
                             !sListId.isEmpty() && !rNextInfo.IsListIdDefault() )
                        {
                            /* Property text:id at element <text:list> has to be
                               replaced by property xml:id (#i92221#)
                            */
                            GetExport().AddAttribute( XML_NAMESPACE_XML,
                                                      XML_ID,
                                                      sListId );
                        }
                        mpTextListsHelper->KeepListAsProcessed( sListId,
                                                                sListStyleName,
                                                                OUString() );
                    }
                    else
                    {
                        const OUString sNewListId(
                                        mpTextListsHelper->GenerateNewListId() );
                        if ( ExportListId() &&
                             !sListId.isEmpty() && !rNextInfo.IsListIdDefault() )
                        {
                            /* Property text:id at element <text:list> has to be
                               replaced by property xml:id (#i92221#)
                            */
                            GetExport().AddAttribute( XML_NAMESPACE_XML,
                                                      XML_ID,
                                                      sNewListId );
                        }
 
                        const OUString sContinueListId =
                            mpTextListsHelper->GetLastContinuingListId( sListId );
                        // store that list with list id <sNewListId> is last list,
                        // which has continued list with list id <sListId>
                        mpTextListsHelper->StoreLastContinuingList( sListId,
                                                                    sNewListId );
                        if ( sListStyleName ==
                                mpTextListsHelper->GetListStyleOfLastProcessedList() &&
                             // Inconsistent behavior regarding lists (#i92811#)
                             sContinueListId ==
                                mpTextListsHelper->GetLastProcessedListId() )
                        {
                            GetExport().AddAttribute( XML_NAMESPACE_TEXT,
                                                      XML_CONTINUE_NUMBERING,
                                                      XML_TRUE );
                        }
                        else
                        {
                            if ( ExportListId() &&
                                 !sListId.isEmpty() )
                            {
                                GetExport().AddAttribute( XML_NAMESPACE_TEXT,
                                                          XML_CONTINUE_LIST,
                                                          sContinueListId );
                            }
                        }
 
                        if ( rNextInfo.IsRestart() &&
                             ( nListLevelsToBeOpened != 1 ||
                               !rNextInfo.HasStartValue() ) )
                        {
                            bRestartNumberingAtContinuedList = true;
                            nRestartValueForContinuedList =
                                        rNextInfo.GetListLevelStartValue();
                        }
 
                        mpTextListsHelper->KeepListAsProcessed( sNewListId,
                                                                sListStyleName,
                                                                sContinueListId );
                    }
 
                    GetExport().AddAttribute( XML_NAMESPACE_TEXT, XML_STYLE_NAME,
                            GetExport().EncodeStyleName( sListStyleName ) );
                    bExportListStyle = false;
 
                    bRootListToBeStarted = false;
                }
                else if ( bExportListStyle &&
                          !mpTextListsHelper->EqualsToTopListStyleOnStack( sListStyleName ) )
                {
                    GetExport().AddAttribute( XML_NAMESPACE_TEXT, XML_STYLE_NAME,
                            GetExport().EncodeStyleName( sListStyleName ) );
                    bExportListStyle = false;
 
                }
                else
                {
                    // rhbz#746174: also export list restart for non root list
                    if (rNextInfo.IsRestart() && !rNextInfo.HasStartValue())
                    {
                        bRestartNumberingAtContinuedList = true;
                        nRestartValueForContinuedList =
                                        rNextInfo.GetListLevelStartValue();
                    }
                }
 
                if ( bContinueingPreviousSubList )
                {
                    GetExport().AddAttribute( XML_NAMESPACE_TEXT,
                                              XML_CONTINUE_NUMBERING, XML_TRUE );
                    bContinueingPreviousSubList = false;
                }
 
                enum XMLTokenEnum eLName = XML_LIST;
 
                OUString aElem(GetExport().GetNamespaceMap().GetQNameByKey(
                                            XML_NAMESPACE_TEXT,
                                            GetXMLToken(eLName) ) );
                GetExport().IgnorableWhitespace();
                GetExport().StartElement(aElem, false);
 
                maListElements.push_back(aElem);
 
                mpTextListsHelper->PushListOnStack( sListId,
                                                    sListStyleName );
 
                // <text:list-header> or <text:list-item>
                GetExport().CheckAttrList();
 
                /* Export start value at correct list item (#i97309#) */
                if ( nListLevelsToBeOpened == 1 )
                {
                    if ( rNextInfo.HasStartValue() )
                    {
                        OUString aTmp = OUString::number( static_cast<sal_Int32>(rNextInfo.GetStartValue()) );
                        GetExport().AddAttribute( XML_NAMESPACE_TEXT, XML_START_VALUE,
                                      aTmp );
                    }
                    else if (bRestartNumberingAtContinuedList)
                    {
                        GetExport().AddAttribute( XML_NAMESPACE_TEXT,
                                                  XML_START_VALUE,
                                                  OUString::number(nRestartValueForContinuedList) );
                        bRestartNumberingAtContinuedList = false;
                    }
                }
 
                eLName = ( rNextInfo.IsNumbered() || nListLevelsToBeOpened > 1 )
                         ? XML_LIST_ITEM
                         : XML_LIST_HEADER;
                aElem = GetExport().GetNamespaceMap().GetQNameByKey(
                                            XML_NAMESPACE_TEXT,
                                            GetXMLToken(eLName) );
                GetExport().IgnorableWhitespace();
                GetExport().StartElement(aElem, false);
                maListElements.push_back(aElem);
 
                // export of <text:number> element for last opened <text:list-item>, if requested
                if ( GetExport().exportTextNumberElement() &&
                     eLName == XML_LIST_ITEM && nListLevelsToBeOpened == 1 && // last iteration --> last opened <text:list-item>
                     !rNextInfo.ListLabelString().isEmpty() )
                {
                    const OUString aTextNumberElem =
                            GetExport().GetNamespaceMap().GetQNameByKey(
                                      XML_NAMESPACE_TEXT,
                                      GetXMLToken(XML_NUMBER) );
                    GetExport().IgnorableWhitespace();
                    GetExport().StartElement( aTextNumberElem, false );
                    GetExport().Characters( rNextInfo.ListLabelString() );
                    GetExport().EndElement( aTextNumberElem, true );
                }
                --nListLevelsToBeOpened;
            } while ( nListLevelsToBeOpened > 0 );
        }
    }
 
    bool bEndElement = false;
 
    if ( rNextInfo.GetLevel() > 0 &&
         rNextInfo.IsNumbered() &&
         rPrevInfo.BelongsToSameList( rNextInfo ) &&
         rPrevInfo.GetLevel() >= rNextInfo.GetLevel() )
    {
        assert(maListElements.size() >= 2 && "list elements missing");
        bEndElement = maListElements.size() >= 2;
    }
 
    if (!bEndElement)
        return;
 
    // close previous list-item
    GetExport().EndElement(maListElements.back(), true );
    maListElements.pop_back();
 
    // Only for sub lists (#i103745#)
    if ( rNextInfo.IsRestart() && !rNextInfo.HasStartValue() &&
         rNextInfo.GetLevel() != 1 )
    {
        // start new sub list respectively list on same list level
        GetExport().EndElement(maListElements.back(), true );
        GetExport().IgnorableWhitespace();
        GetExport().StartElement(maListElements.back(), false);
    }
 
    // open new list-item
    GetExport().CheckAttrList();
    if( rNextInfo.HasStartValue() )
    {
        OUString aTmp = OUString::number( static_cast<sal_Int32>(rNextInfo.GetStartValue()) );
        GetExport().AddAttribute( XML_NAMESPACE_TEXT, XML_START_VALUE, aTmp );
    }
    // Handle restart without start value on list level 1 (#i103745#)
    else if ( rNextInfo.IsRestart() && /*!rNextInfo.HasStartValue() &&*/
              rNextInfo.GetLevel() == 1 )
    {
        OUString aTmp = OUString::number( static_cast<sal_Int32>(rNextInfo.GetListLevelStartValue()) );
        GetExport().AddAttribute( XML_NAMESPACE_TEXT, XML_START_VALUE, aTmp );
    }
    if ( ( GetExport().getExportFlags() & SvXMLExportFlags::OASIS ) &&
        GetExport().getSaneDefaultVersion() >= SvtSaveOptions::ODFSVER_012)
    {
        const OUString& sListStyleName( rNextInfo.GetNumRulesName() );
        if ( !mpTextListsHelper->EqualsToTopListStyleOnStack( sListStyleName ) )
        {
            GetExport().AddAttribute( XML_NAMESPACE_TEXT,
                                      XML_STYLE_OVERRIDE,
                                      GetExport().EncodeStyleName( sListStyleName ) );
        }
    }
    OUString aElem( GetExport().GetNamespaceMap().GetQNameByKey(
                            XML_NAMESPACE_TEXT,
                            GetXMLToken(XML_LIST_ITEM) ) );
    GetExport().IgnorableWhitespace();
    GetExport().StartElement(aElem, false );
    maListElements.push_back(aElem);
 
    // export of <text:number> element for <text:list-item>, if requested
    if ( GetExport().exportTextNumberElement() &&
         !rNextInfo.ListLabelString().isEmpty() )
    {
        const OUString aTextNumberElem =
                GetExport().GetNamespaceMap().GetQNameByKey(
                          XML_NAMESPACE_TEXT,
                          GetXMLToken(XML_NUMBER) );
        GetExport().IgnorableWhitespace();
        GetExport().StartElement( aTextNumberElem, false );
        GetExport().Characters( rNextInfo.ListLabelString() );
        GetExport().EndElement( aTextNumberElem, true );
    }
 
}
 
struct XMLTextParagraphExport::Impl
{
    typedef ::std::map<Reference<XFormField>, sal_Int32> FieldMarkMap_t;
    FieldMarkMap_t m_FieldMarkMap;
 
    explicit Impl() {}
    sal_Int32 AddFieldMarkStart(Reference<XFormField> const& i_xFieldMark)
    {
        assert(m_FieldMarkMap.find(i_xFieldMark) == m_FieldMarkMap.end());
        sal_Int32 const ret(m_FieldMarkMap.size());
        m_FieldMarkMap.insert(::std::make_pair(i_xFieldMark, ret));
        return ret;
    }
    sal_Int32 GetFieldMarkIndex(Reference<XFormField> const& i_xFieldMark)
    {
        FieldMarkMap_t::const_iterator const it(
                m_FieldMarkMap.find(i_xFieldMark));
        // rely on SwXFieldmark::CreateXFieldmark returning the same instance
        // because the Reference in m_FieldMarkMap will keep it alive
        assert(it != m_FieldMarkMap.end());
        return it->second;
    }
};
 
struct XMLTextParagraphExport::DocumentListNodes
{
    struct NodeData
    {
        std::ptrdiff_t order;
        sal_Int32 index; // see SwNode::GetIndex and SwNodeOffset
        sal_uInt64 style_id; // actually a pointer to NumRule
        OUString list_id;
    };
    std::vector<NodeData> docListNodes;
    DocumentListNodes(const css::uno::Reference<css::frame::XModel>& xModel,
                      const std::vector<sal_Int32>& aDocumentNodeOrder)
    {
        // Sequence of nodes, each of them represented by three-element sequence,
        // corresponding to NodeData members
        css::uno::Sequence<css::uno::Sequence<css::uno::Any>> nodes;
        if (auto xPropSet = xModel.query<css::beans::XPropertySet>())
        {
            try
            {
                // See SwXTextDocument::getPropertyValue
                xPropSet->getPropertyValue(u"ODFExport_ListNodes"_ustr) >>= nodes;
            }
            catch (css::beans::UnknownPropertyException&)
            {
                // That's absolutely fine!
            }
        }
 
        docListNodes.reserve(nodes.getLength());
        for (const auto& node : nodes)
        {
            assert(node.getLength() == 3);
            sal_Int32 nodeIndex = node[0].get<sal_Int32>();
            auto nodeOrder = std::distance(
                aDocumentNodeOrder.begin(),
                std::find(aDocumentNodeOrder.begin(), aDocumentNodeOrder.end(), nodeIndex));
            docListNodes.push_back({ .order = nodeOrder,
                                     .index = nodeIndex,
                                     .style_id = node[1].get<sal_uInt64>(),
                                     .list_id = node[2].get<OUString>() });
        }
 
        std::sort(docListNodes.begin(), docListNodes.end(),
                  [](const NodeData& lhs, const NodeData& rhs) { return lhs.order < rhs.order; });
    }
    bool ShouldSkipListId(const Reference<XTextContent>& xTextContent) const
    {
        if (docListNodes.empty())
            return false;
 
        if (auto xPropSet = xTextContent.query<css::beans::XPropertySet>())
        {
            sal_Int32 index = 0;
            try
            {
                // See SwXParagraph::Impl::GetPropertyValues_Impl
                xPropSet->getPropertyValue(u"ODFExport_NodeIndex"_ustr) >>= index;
            }
            catch (css::beans::UnknownPropertyException&)
            {
                // That's absolutely fine!
                return false;
            }
 
            auto it = std::find_if(docListNodes.begin(), docListNodes.end(),
                                   [index](const NodeData& el) { return el.index == index; });
            if (it == docListNodes.end())
                return false;
 
            // We need to write the id, when there will be continuation of the list either with
            // a different list style, or after another list.
 
            for (auto next = it + 1; next != docListNodes.end(); ++next)
            {
                if (it->list_id != next->list_id)
                {
                    // List changed. We will have to refer to this id, only if there will
                    // appear a continuation of this list
                    return std::find_if(next + 1, docListNodes.end(),
                                        [list_id = it->list_id](const NodeData& data)
                                        { return data.list_id == list_id; })
                           == docListNodes.end();
                }
 
                if (it->style_id != next->style_id)
                {
                    // Same list, new style -> this "next" will refer to the id, no skipping
                    return false;
                }
                if (it->index + 1 != next->index)
                {
                    // we have a gap before the next node with the same list and style,
                    // with no other lists in between. There will be a continuation with a
                    // simple 'text:continue-numbering="true"'.
                    return true;
                }
                it = next; // walk through adjacent nodes of the same list
            }
            // all nodes were adjacent and of the same list and style -> no continuation, skip id
            return true;
        }
 
        return false;
    }
};
 
XMLTextParagraphExport::XMLTextParagraphExport(
        SvXMLExport& rExp,
        SvXMLAutoStylePoolP & rASP
        ) :
    XMLStyleExport( rExp, &rASP ),
    m_xImpl(new Impl),
    m_rAutoStylePool( rASP ),
    m_pBoundFrameSets(new BoundFrameSets(GetExport().GetModel())),
    maListAutoPool( GetExport() ),
    m_bProgress( false ),
    m_bBlock( false ),
    m_bOpenRuby( false ),
    mpTextListsHelper( nullptr ),
    mbCollected(false),
    m_aCharStyleNamesPropInfoCache( gsCharStyleNames )
{
    rtl::Reference < XMLPropertySetMapper > xPropMapper(new XMLTextPropertySetMapper( TextPropMap::PARA, true ));
    m_xParaPropMapper = new XMLTextExportPropertySetMapper( xPropMapper,
                                                             GetExport() );
 
    OUString sFamily( GetXMLToken(XML_PARAGRAPH) );
    OUString aPrefix(u'P');
    m_rAutoStylePool.AddFamily( XmlStyleFamily::TEXT_PARAGRAPH, sFamily,
                              m_xParaPropMapper, aPrefix );
 
    xPropMapper = new XMLTextPropertySetMapper( TextPropMap::TEXT, true );
    m_xTextPropMapper = new XMLTextExportPropertySetMapper( xPropMapper,
                                                             GetExport() );
    sFamily = GetXMLToken(XML_TEXT);
    aPrefix = "T";
    m_rAutoStylePool.AddFamily( XmlStyleFamily::TEXT_TEXT, sFamily,
                              m_xTextPropMapper, aPrefix );
 
    xPropMapper = new XMLTextPropertySetMapper( TextPropMap::AUTO_FRAME, true );
    m_xAutoFramePropMapper = new XMLTextExportPropertySetMapper( xPropMapper,
                                                                  GetExport() );
    sFamily = XML_STYLE_FAMILY_SD_GRAPHICS_NAME;
    aPrefix = "fr";
    m_rAutoStylePool.AddFamily( XmlStyleFamily::TEXT_FRAME, sFamily,
                              m_xAutoFramePropMapper, aPrefix );
 
    xPropMapper = new XMLTextPropertySetMapper( TextPropMap::SECTION, true );
    m_xSectionPropMapper = new XMLTextExportPropertySetMapper( xPropMapper,
                                                             GetExport() );
    sFamily = GetXMLToken( XML_SECTION );
    aPrefix = "Sect" ;
    m_rAutoStylePool.AddFamily( XmlStyleFamily::TEXT_SECTION, sFamily,
                              m_xSectionPropMapper, aPrefix );
 
    xPropMapper = new XMLTextPropertySetMapper( TextPropMap::RUBY, true );
    m_xRubyPropMapper = new SvXMLExportPropertyMapper( xPropMapper );
    sFamily = GetXMLToken( XML_RUBY );
    aPrefix = "Ru";
    m_rAutoStylePool.AddFamily( XmlStyleFamily::TEXT_RUBY, sFamily,
                              m_xRubyPropMapper, aPrefix );
 
    xPropMapper = new XMLTextPropertySetMapper( TextPropMap::FRAME, true );
    m_xFramePropMapper = new XMLTextExportPropertySetMapper( xPropMapper,
                                                              GetExport() );
 
    m_pSectionExport.reset( new XMLSectionExport( rExp, *this ) );
    m_pIndexMarkExport.reset( new XMLIndexMarkExport( rExp ) );
 
    if( ! IsBlockMode() &&
        Reference<XRedlinesSupplier>( GetExport().GetModel(), UNO_QUERY ).is())
        m_pRedlineExport.reset( new XMLRedlineExport( rExp ) );
 
    // The text field helper needs a pre-constructed XMLPropertyState
    // to export the combined characters field. We construct that
    // here, because we need the text property mapper to do it.
 
    // construct Any value, then find index
    sal_Int32 nIndex = m_xTextPropMapper->getPropertySetMapper()->FindEntryIndex(
                                "", XML_NAMESPACE_STYLE,
                                GetXMLToken(XML_TEXT_COMBINE));
    m_pFieldExport.reset( new XMLTextFieldExport( rExp, std::make_unique<XMLPropertyState>( nIndex, uno::Any(true) ) ) );
    PushNewTextListsHelper();
}
 
XMLTextParagraphExport::~XMLTextParagraphExport()
{
    m_pRedlineExport.reset();
    m_pIndexMarkExport.reset();
    m_pSectionExport.reset();
    m_pFieldExport.reset();
#ifdef DBG_UTIL
    txtparae_bContainsIllegalCharacters = false;
#endif
    PopTextListsHelper();
    SAL_WARN_IF( !maTextListsHelperStack.empty(), "xmloff",
                "misusage of text lists helper stack - it is not empty. Serious defect" );
}
 
SvXMLExportPropertyMapper *XMLTextParagraphExport::CreateShapeExtPropMapper(
        SvXMLExport& rExport )
{
    rtl::Reference < XMLPropertySetMapper > xPropMapper =
        new XMLTextPropertySetMapper( TextPropMap::SHAPE, true );
    return new XMLTextExportPropertySetMapper( xPropMapper, rExport );
}
 
SvXMLExportPropertyMapper *XMLTextParagraphExport::CreateCharExtPropMapper(
        SvXMLExport& rExport)
{
    XMLPropertySetMapper *pPropMapper =
        new XMLTextPropertySetMapper( TextPropMap::TEXT, true );
    return new XMLTextExportPropertySetMapper( pPropMapper, rExport );
}
 
SvXMLExportPropertyMapper *XMLTextParagraphExport::CreateParaExtPropMapper(
        SvXMLExport& rExport)
{
    XMLPropertySetMapper *pPropMapper =
        new XMLTextPropertySetMapper( TextPropMap::SHAPE_PARA, true );
    return new XMLTextExportPropertySetMapper( pPropMapper, rExport );
}
 
SvXMLExportPropertyMapper *XMLTextParagraphExport::CreateParaDefaultExtPropMapper(
        SvXMLExport& rExport)
{
    XMLPropertySetMapper *pPropMapper =
        new XMLTextPropertySetMapper( TextPropMap::TEXT_ADDITIONAL_DEFAULTS, true );
    return new XMLTextExportPropertySetMapper( pPropMapper, rExport );
}
 
void XMLTextParagraphExport::exportPageFrames( bool bIsProgress )
{
    const TextContentSet& rTexts = m_pBoundFrameSets->GetTexts()->GetPageBoundContents();
    const TextContentSet& rGraphics = m_pBoundFrameSets->GetGraphics()->GetPageBoundContents();
    const TextContentSet& rEmbeddeds = m_pBoundFrameSets->GetEmbeddeds()->GetPageBoundContents();
    const TextContentSet& rShapes = m_pBoundFrameSets->GetShapes()->GetPageBoundContents();
    for(TextContentSet::const_iterator_t it = rTexts.getBegin();
        it != rTexts.getEnd();
        ++it)
        exportTextFrame(*it, false/*bAutoStyles*/, bIsProgress, true);
    for(TextContentSet::const_iterator_t it = rGraphics.getBegin();
        it != rGraphics.getEnd();
        ++it)
        exportTextGraphic(*it, false/*bAutoStyles*/);
    for(TextContentSet::const_iterator_t it = rEmbeddeds.getBegin();
        it != rEmbeddeds.getEnd();
        ++it)
        exportTextEmbedded(*it, false/*bAutoStyles*/);
    for(TextContentSet::const_iterator_t it = rShapes.getBegin();
        it != rShapes.getEnd();
        ++it)
        exportShape(*it, false/*bAutoStyles*/);
}
 
void XMLTextParagraphExport::exportFrameFrames(
        bool bAutoStyles,
        bool bIsProgress,
        const Reference < XTextFrame >& rParentTxtFrame )
{
    const TextContentSet* const pTexts = m_pBoundFrameSets->GetTexts()->GetFrameBoundContents(rParentTxtFrame);
    if(pTexts)
        for(TextContentSet::const_iterator_t it = pTexts->getBegin();
            it != pTexts->getEnd();
            ++it)
            exportTextFrame(*it, bAutoStyles, bIsProgress, true);
    const TextContentSet* const pGraphics = m_pBoundFrameSets->GetGraphics()->GetFrameBoundContents(rParentTxtFrame);
    if(pGraphics)
        for(TextContentSet::const_iterator_t it = pGraphics->getBegin();
            it != pGraphics->getEnd();
            ++it)
            exportTextGraphic(*it, bAutoStyles);
    const TextContentSet* const pEmbeddeds = m_pBoundFrameSets->GetEmbeddeds()->GetFrameBoundContents(rParentTxtFrame);
    if(pEmbeddeds)
        for(TextContentSet::const_iterator_t it = pEmbeddeds->getBegin();
            it != pEmbeddeds->getEnd();
            ++it)
            exportTextEmbedded(*it, bAutoStyles);
    const TextContentSet* const pShapes = m_pBoundFrameSets->GetShapes()->GetFrameBoundContents(rParentTxtFrame);
    if(pShapes)
        for(TextContentSet::const_iterator_t it = pShapes->getBegin();
            it != pShapes->getEnd();
            ++it)
            exportShape(*it, bAutoStyles);
}
 
// bookmarks, reference marks (and TOC marks) are the same except for the
// element names. We use the same method for export and it an array with
// the proper element names
const enum XMLTokenEnum lcl_XmlReferenceElements[] = {
    XML_REFERENCE_MARK, XML_REFERENCE_MARK_START, XML_REFERENCE_MARK_END };
const enum XMLTokenEnum lcl_XmlBookmarkElements[] = {
    XML_BOOKMARK, XML_BOOKMARK_START, XML_BOOKMARK_END };
 
void XMLTextParagraphExport::collectTextAutoStylesAndNodeExportOrder(bool bIsProgress)
{
    GetExport().GetShapeExport(); // make sure the graphics styles family is added
 
    if (mbCollected)
        return;
 
    const bool bAutoStyles = true;
    const bool bExportContent = true;
 
    if (auto xTextDocument = GetExport().GetModel().query<XTextDocument>())
    {
        bInDocumentNodeOrderCollection = true;
        collectTextAutoStyles(xTextDocument->getText(), bIsProgress);
        bInDocumentNodeOrderCollection = false;
    }
 
    // Export text frames:
    Reference<XEnumeration> xTextFramesEnum = m_pBoundFrameSets->GetTexts()->createEnumeration();
    if(xTextFramesEnum.is())
        while(xTextFramesEnum->hasMoreElements())
        {
            Reference<XTextContent> xTxtCntnt(xTextFramesEnum->nextElement(), UNO_QUERY);
            if(xTxtCntnt.is())
                exportTextFrame(xTxtCntnt, bAutoStyles, bIsProgress, bExportContent);
        }
 
    // Export graphic objects:
    Reference<XEnumeration> xGraphicsEnum = m_pBoundFrameSets->GetGraphics()->createEnumeration();
    if(xGraphicsEnum.is())
        while(xGraphicsEnum->hasMoreElements())
        {
            Reference<XTextContent> xTxtCntnt(xGraphicsEnum->nextElement(), UNO_QUERY);
            if(xTxtCntnt.is())
                exportTextGraphic(xTxtCntnt, true);
        }
 
    // Export embedded objects:
    Reference<XEnumeration> xEmbeddedsEnum = m_pBoundFrameSets->GetEmbeddeds()->createEnumeration();
    if(xEmbeddedsEnum.is())
        while(xEmbeddedsEnum->hasMoreElements())
        {
            Reference<XTextContent> xTxtCntnt(xEmbeddedsEnum->nextElement(), UNO_QUERY);
            if(xTxtCntnt.is())
                exportTextEmbedded(xTxtCntnt, true);
        }
 
    // Export shapes:
    Reference<XEnumeration> xShapesEnum = m_pBoundFrameSets->GetShapes()->createEnumeration();
    if(xShapesEnum.is())
        while(xShapesEnum->hasMoreElements())
        {
            Reference<XTextContent> xTxtCntnt(xShapesEnum->nextElement(), UNO_QUERY);
            if(xTxtCntnt.is())
            {
                Reference<XServiceInfo> xServiceInfo(xTxtCntnt, UNO_QUERY);
                if( xServiceInfo->supportsService(gsShapeService))
                    exportShape(xTxtCntnt, true);
            }
        }
 
    if (GetExport().getExportFlags() & SvXMLExportFlags::CONTENT)
        exportTrackedChanges(true);
 
    mbCollected = true;
}
 
void XMLTextParagraphExport::exportText(
        const Reference < XText > & rText,
        bool bAutoStyles,
        bool bIsProgress,
        bool bExportParagraph,
        TextPNS eExtensionNS)
{
    if( bAutoStyles )
        GetExport().GetShapeExport(); // make sure the graphics styles family
                                      // is added
    Reference < XEnumerationAccess > xEA( rText, UNO_QUERY );
    if( ! xEA.is() )
        return;
 
    Reference < XEnumeration > xParaEnum(xEA->createEnumeration());
    Reference < XPropertySet > xPropertySet( rText, UNO_QUERY );
    Reference < XTextSection > xBaseSection;
 
    // #97718# footnotes don't supply paragraph enumerations in some cases
    // This is always a bug, but at least we don't want to crash.
    SAL_WARN_IF( !xParaEnum.is(), "xmloff", "We need a paragraph enumeration" );
    if( ! xParaEnum.is() )
        return;
 
    if (xPropertySet.is())
    {
        Reference < XPropertySetInfo > xInfo ( xPropertySet->getPropertySetInfo() );
 
        if( xInfo.is() )
        {
            if (xInfo->hasPropertyByName( gsTextSection ))
            {
                xPropertySet->getPropertyValue(gsTextSection) >>= xBaseSection ;
            }
        }
    }
 
    // #96530# Export redlines at start & end of XText before & after
    // exporting the text content enumeration
    if( !bAutoStyles && (m_pRedlineExport != nullptr) )
        m_pRedlineExport->ExportStartOrEndRedline( xPropertySet, true );
    exportTextContentEnumeration( xParaEnum, bAutoStyles, xBaseSection,
                                  bIsProgress, bExportParagraph, nullptr, eExtensionNS );
    if( !bAutoStyles && (m_pRedlineExport != nullptr) )
        m_pRedlineExport->ExportStartOrEndRedline( xPropertySet, false );
}
 
void XMLTextParagraphExport::exportText(
        const Reference < XText > & rText,
        const Reference < XTextSection > & rBaseSection,
        bool bAutoStyles,
        bool bIsProgress,
        bool bExportParagraph)
{
    if( bAutoStyles )
        GetExport().GetShapeExport(); // make sure the graphics styles family
                                      // is added
    Reference < XEnumerationAccess > xEA( rText, UNO_QUERY );
    Reference < XEnumeration > xParaEnum(xEA->createEnumeration());
 
    // #98165# don't continue without a paragraph enumeration
    if( ! xParaEnum.is() )
        return;
 
    // #96530# Export redlines at start & end of XText before & after
    // exporting the text content enumeration
    Reference<XPropertySet> xPropertySet;
    if( !bAutoStyles && (m_pRedlineExport != nullptr) )
    {
        xPropertySet.set(rText, uno::UNO_QUERY );
        m_pRedlineExport->ExportStartOrEndRedline( xPropertySet, true );
    }
    exportTextContentEnumeration( xParaEnum, bAutoStyles, rBaseSection,
                                  bIsProgress, bExportParagraph );
    if( !bAutoStyles && (m_pRedlineExport != nullptr) )
        m_pRedlineExport->ExportStartOrEndRedline( xPropertySet, false );
}
 
bool XMLTextParagraphExport::ExportListId() const
{
    return (GetExport().getExportFlags() & SvXMLExportFlags::OASIS)
           && GetExport().getSaneDefaultVersion() >= SvtSaveOptions::ODFSVER_012;
}
 
void XMLTextParagraphExport::RecordNodeIndex(const css::uno::Reference<css::text::XTextContent>& xTextContent)
{
    if (!bInDocumentNodeOrderCollection)
        return;
    if (auto xPropSet = xTextContent.query<css::beans::XPropertySet>())
    {
        try
        {
            sal_Int32 index = 0;
            // See SwXParagraph::Impl::GetPropertyValues_Impl
            xPropSet->getPropertyValue(u"ODFExport_NodeIndex"_ustr) >>= index;
            assert(std::find(maDocumentNodeOrder.begin(), maDocumentNodeOrder.end(), index)
                   == maDocumentNodeOrder.end());
            maDocumentNodeOrder.push_back(index);
        }
        catch (css::beans::UnknownPropertyException&)
        {
            // That's absolutely fine!
        }
    }
}
 
bool XMLTextParagraphExport::ShouldSkipListId(const Reference<XTextContent>& xTextContent)
{
    if (!mpDocumentListNodes)
    {
        if (ExportListId())
            mpDocumentListNodes.reset(new DocumentListNodes(GetExport().GetModel(), maDocumentNodeOrder));
        else
            mpDocumentListNodes.reset(new DocumentListNodes({}, {}));
    }
 
    return mpDocumentListNodes->ShouldSkipListId(xTextContent);
}
 
void XMLTextParagraphExport::exportTextContentEnumeration(
        const Reference < XEnumeration > & rContEnum,
        bool bAutoStyles,
        const Reference < XTextSection > & rBaseSection,
        bool bIsProgress,
        bool bExportParagraph,
        const Reference < XPropertySet > *pRangePropSet,
        TextPNS eExtensionNS )
{
    SAL_WARN_IF( !rContEnum.is(), "xmloff", "No enumeration to export!" );
    bool bHasMoreElements = rContEnum->hasMoreElements();
    if( !bHasMoreElements )
        return;
 
    XMLTextNumRuleInfo aPrevNumInfo;
    XMLTextNumRuleInfo aNextNumInfo;
 
    bool bHasContent = false;
    Reference<XTextSection> xCurrentTextSection(rBaseSection);
 
    MultiPropertySetHelper aPropSetHelper(
                               bAutoStyles ? std::span<const OUString>(aParagraphPropertyNamesAuto) :
                                          std::span<const OUString>(aParagraphPropertyNames) );
 
    bool bHoldElement = false;
    Reference < XTextContent > xTxtCntnt;
    while( bHoldElement || bHasMoreElements )
    {
        if (bHoldElement)
        {
            bHoldElement = false;
        }
        else
        {
            xTxtCntnt.set(rContEnum->nextElement(), uno::UNO_QUERY);
 
            aPropSetHelper.resetValues();
 
        }
 
        Reference<XServiceInfo> xServiceInfo( xTxtCntnt, UNO_QUERY );
        if( xServiceInfo->supportsService( gsParagraphService ) )
        {
            if( bAutoStyles )
            {
                RecordNodeIndex(xTxtCntnt);
                exportListAndSectionChange( xCurrentTextSection, xTxtCntnt,
                                            aPrevNumInfo, aNextNumInfo,
                                            bAutoStyles );
            }
            else
            {
                /* Pass list auto style pool to <XMLTextNumRuleInfo> instance
                   Pass info about request to export <text:number> element
                   to <XMLTextNumRuleInfo> instance (#i69627#)
                */
                aNextNumInfo.Set( xTxtCntnt,
                                  GetExport().writeOutlineStyleAsNormalListStyle(),
                                  GetListAutoStylePool(),
                                  GetExport().exportTextNumberElement(),
                                  ShouldSkipListId(xTxtCntnt) );
 
                exportListAndSectionChange( xCurrentTextSection, aPropSetHelper,
                                            TEXT_SECTION, xTxtCntnt,
                                            aPrevNumInfo, aNextNumInfo,
                                            bAutoStyles );
            }
 
            // if we found a mute section: skip all section content
            if (m_pSectionExport->IsMuteSection(xCurrentTextSection))
            {
                // Make sure headings are exported anyway.
                if( !bAutoStyles )
                    m_pSectionExport->ExportMasterDocHeadingDummies();
 
                while (rContEnum->hasMoreElements() &&
                       XMLSectionExport::IsInSection( xCurrentTextSection,
                                                    xTxtCntnt, true ))
                {
                    xTxtCntnt.set(rContEnum->nextElement(), uno::UNO_QUERY);
                    aPropSetHelper.resetValues();
                    aNextNumInfo.Reset();
                }
                // the first non-mute element still needs to be processed
                bHoldElement =
                    ! XMLSectionExport::IsInSection( xCurrentTextSection,
                                                   xTxtCntnt, false );
            }
            else
                exportParagraph( xTxtCntnt, bAutoStyles, bIsProgress,
                                 bExportParagraph, aPropSetHelper, eExtensionNS );
            bHasContent = true;
        }
        else if( xServiceInfo->supportsService( gsTableService ) )
        {
            if( !bAutoStyles )
            {
                aNextNumInfo.Reset();
            }
 
            exportListAndSectionChange( xCurrentTextSection, xTxtCntnt,
                                        aPrevNumInfo, aNextNumInfo,
                                        bAutoStyles );
 
            if (! m_pSectionExport->IsMuteSection(xCurrentTextSection))
            {
                // export start + end redlines (for wholly redlined tables)
                if ((! bAutoStyles) && (nullptr != m_pRedlineExport))
                    m_pRedlineExport->ExportStartOrEndRedline(xTxtCntnt, true);
 
                exportTable( xTxtCntnt, bAutoStyles, bIsProgress  );
 
                if ((! bAutoStyles) && (nullptr != m_pRedlineExport))
                    m_pRedlineExport->ExportStartOrEndRedline(xTxtCntnt, false);
            }
            else if( !bAutoStyles )
            {
                // Make sure headings are exported anyway.
                m_pSectionExport->ExportMasterDocHeadingDummies();
            }
 
            bHasContent = true;
        }
        else if( xServiceInfo->supportsService( gsTextFrameService ) )
        {
            exportTextFrame( xTxtCntnt, bAutoStyles, bIsProgress, true, pRangePropSet );
        }
        else if( xServiceInfo->supportsService( gsTextGraphicService ) )
        {
            exportTextGraphic( xTxtCntnt, bAutoStyles, pRangePropSet );
        }
        else if( xServiceInfo->supportsService( gsTextEmbeddedService ) )
        {
            exportTextEmbedded( xTxtCntnt, bAutoStyles, pRangePropSet );
        }
        else if( xServiceInfo->supportsService( gsShapeService ) )
        {
            exportShape( xTxtCntnt, bAutoStyles, pRangePropSet );
        }
        else
        {
            SAL_WARN_IF( xTxtCntnt.is(), "xmloff", "unknown text content" );
        }
 
        if( !bAutoStyles )
        {
            aPrevNumInfo = aNextNumInfo;
        }
 
        bHasMoreElements = rContEnum->hasMoreElements();
    }
 
    if( bHasContent && !bAutoStyles )
    {
        aNextNumInfo.Reset();
 
        // close open lists and sections; no new styles
        exportListAndSectionChange( xCurrentTextSection, rBaseSection,
                                    aPrevNumInfo, aNextNumInfo,
                                    bAutoStyles );
    }
}
 
void XMLTextParagraphExport::exportParagraph(
        const Reference < XTextContent > & rTextContent,
        bool bAutoStyles, bool bIsProgress, bool bExportParagraph,
        MultiPropertySetHelper& rPropSetHelper, TextPNS eExtensionNS)
{
    sal_Int16 nOutlineLevel = -1;
 
    if( bIsProgress )
    {
        ProgressBarHelper *pProgress = GetExport().GetProgressBarHelper();
        pProgress->SetValue( pProgress->GetValue()+1 );
    }
 
    // get property set or multi property set and initialize helper
    Reference<XMultiPropertySet> xMultiPropSet( rTextContent, UNO_QUERY );
    Reference<XPropertySet> xPropSet( rTextContent, UNO_QUERY );
 
    // check for supported properties
    if( !rPropSetHelper.checkedProperties() )
        rPropSetHelper.hasProperties( xPropSet->getPropertySetInfo() );
 
//  if( xMultiPropSet.is() )
//      rPropSetHelper.getValues( xMultiPropSet );
//  else
//      rPropSetHelper.getValues( xPropSet );
 
    if( bExportParagraph )
    {
        if( bAutoStyles )
        {
            Add( XmlStyleFamily::TEXT_PARAGRAPH, rPropSetHelper, xPropSet );
        }
        else
        {
            // xml:id for RDF metadata
            GetExport().AddAttributeXmlId(rTextContent);
            GetExport().AddAttributesRDFa(rTextContent);
 
            OUString sStyle;
            if( rPropSetHelper.hasProperty( PARA_STYLE_NAME ) )
            {
                if( xMultiPropSet.is() )
                    rPropSetHelper.getValue( PARA_STYLE_NAME,
                                                    xMultiPropSet ) >>= sStyle;
                else
                    rPropSetHelper.getValue( PARA_STYLE_NAME,
                                                    xPropSet ) >>= sStyle;
            }
 
            if( rTextContent.is() )
            {
                const OUString& rIdentifier = GetExport().getInterfaceToIdentifierMapper().getIdentifier( rTextContent );
                if( !rIdentifier.isEmpty() )
                {
                    // FIXME: this is just temporary until EditEngine
                    // paragraphs implement XMetadatable.
                    // then that must be used and not the mapper, because
                    // when both can be used we get two xml:id!
                    uno::Reference<rdf::XMetadatable> const xMeta(rTextContent,
                        uno::UNO_QUERY);
                    OSL_ENSURE(!xMeta.is(), "paragraph that implements "
                        "XMetadatable used in interfaceToIdentifierMapper?");
                    GetExport().AddAttributeIdLegacy(XML_NAMESPACE_TEXT,
                        rIdentifier);
                }
            }
 
            OUString sAutoStyle = Find( XmlStyleFamily::TEXT_PARAGRAPH, xPropSet, sStyle );
            if ( sAutoStyle.isEmpty() )
                sAutoStyle = sStyle;
            if( !sAutoStyle.isEmpty() )
                GetExport().AddAttribute( XML_NAMESPACE_TEXT, XML_STYLE_NAME,
                              GetExport().EncodeStyleName( sAutoStyle ) );
 
            if( rPropSetHelper.hasProperty( PARA_CONDITIONAL_STYLE_NAME ) )
            {
                OUString sCondStyle;
                if( xMultiPropSet.is() )
                    rPropSetHelper.getValue( PARA_CONDITIONAL_STYLE_NAME,
                                                     xMultiPropSet ) >>= sCondStyle;
                else
                    rPropSetHelper.getValue( PARA_CONDITIONAL_STYLE_NAME,
                                                     xPropSet ) >>= sCondStyle;
                if( sCondStyle != sStyle )
                {
                    sCondStyle = Find( XmlStyleFamily::TEXT_PARAGRAPH, xPropSet,
                                          sCondStyle );
                    if( !sCondStyle.isEmpty() )
                        GetExport().AddAttribute( XML_NAMESPACE_TEXT,
                                                  XML_COND_STYLE_NAME,
                              GetExport().EncodeStyleName( sCondStyle ) );
                }
            }
 
            if( rPropSetHelper.hasProperty( PARA_OUTLINE_LEVEL ) )
            {
                if( xMultiPropSet.is() )
                    rPropSetHelper.getValue( PARA_OUTLINE_LEVEL,
                                                     xMultiPropSet ) >>= nOutlineLevel;
                else
                    rPropSetHelper.getValue( PARA_OUTLINE_LEVEL,
                                                     xPropSet ) >>= nOutlineLevel;
 
                if( 0 < nOutlineLevel )
                {
                    GetExport().AddAttribute( XML_NAMESPACE_TEXT,
                                              XML_OUTLINE_LEVEL,
                                  OUString::number( sal_Int32( nOutlineLevel) ) );
 
                    if ( rPropSetHelper.hasProperty( PARA_OUTLINE_CONTENT_VISIBLE ) )
                    {
                        uno::Sequence<beans::PropertyValue> propList;
                        bool bIsOutlineContentVisible = true;
                        if( xMultiPropSet.is() )
                            rPropSetHelper.getValue(
                                       PARA_OUTLINE_CONTENT_VISIBLE, xMultiPropSet ) >>= propList;
                        else
                            rPropSetHelper.getValue(
                                       PARA_OUTLINE_CONTENT_VISIBLE, xPropSet ) >>= propList;
                        for (const auto& rProp : propList)
                        {
                            OUString propName = rProp.Name;
                            if (propName == "OutlineContentVisibleAttr")
                            {
                                rProp.Value >>= bIsOutlineContentVisible;
                                break;
                            }
                        }
                        if (!bIsOutlineContentVisible)
                        {
                            GetExport().AddAttribute( XML_NAMESPACE_LO_EXT,
                                              XML_OUTLINE_CONTENT_VISIBLE,
                                              XML_FALSE);
                        }
                    }
 
                    if( rPropSetHelper.hasProperty( NUMBERING_IS_NUMBER ) )
                    {
                        bool bIsNumber = false;
                        if( xMultiPropSet.is() )
                            rPropSetHelper.getValue(
                                       NUMBERING_IS_NUMBER, xMultiPropSet ) >>= bIsNumber;
                        else
                            rPropSetHelper.getValue(
                                       NUMBERING_IS_NUMBER, xPropSet ) >>= bIsNumber;
 
                        OUString sListStyleName;
                        if( xMultiPropSet.is() )
                            rPropSetHelper.getValue(
                                       PARA_NUMBERING_STYLENAME, xMultiPropSet ) >>= sListStyleName;
                        else
                            rPropSetHelper.getValue(
                                       PARA_NUMBERING_STYLENAME, xPropSet ) >>= sListStyleName;
 
                        bool bAssignedtoOutlineStyle = false;
                        {
                            Reference< XChapterNumberingSupplier > xCNSupplier( GetExport().GetModel(), UNO_QUERY );
 
                            if (xCNSupplier.is())
                            {
                                Reference< XIndexReplace > xNumRule ( xCNSupplier->getChapterNumberingRules() );
                                SAL_WARN_IF( !xNumRule.is(), "xmloff", "no chapter numbering rules" );
 
                                if (xNumRule.is())
                                {
                                    Reference< XPropertySet > xNumRulePropSet( xNumRule, UNO_QUERY );
                                    OUString sOutlineName;
                                    xNumRulePropSet->getPropertyValue(
                                        u"Name"_ustr ) >>= sOutlineName;
                                    bAssignedtoOutlineStyle = ( sListStyleName == sOutlineName );
                                }
                            }
                        }
 
                        if( ! bIsNumber && bAssignedtoOutlineStyle )
                            GetExport().AddAttribute( XML_NAMESPACE_TEXT,
                                                      XML_IS_LIST_HEADER,
                                                      XML_TRUE );
                    }
 
                    {
                        bool bIsRestartNumbering = false;
 
                        Reference< XPropertySetInfo >
                        xPropSetInfo(xMultiPropSet.is() ?
                                     xMultiPropSet->getPropertySetInfo():
                                     xPropSet->getPropertySetInfo());
 
                        if (xPropSetInfo->
                            hasPropertyByName(u"ParaIsNumberingRestart"_ustr))
                        {
                            xPropSet->getPropertyValue(u"ParaIsNumberingRestart"_ustr)
                                >>= bIsRestartNumbering;
                        }
 
                        if (bIsRestartNumbering)
                        {
                            GetExport().AddAttribute(XML_NAMESPACE_TEXT,
                                                     XML_RESTART_NUMBERING,
                                                     XML_TRUE);
 
                            if (xPropSetInfo->
                                hasPropertyByName(u"NumberingStartValue"_ustr))
                            {
                                sal_Int32 nStartValue = 0;
 
                                xPropSet->getPropertyValue(u"NumberingStartValue"_ustr)
                                    >>= nStartValue;
 
                                GetExport().
                                    AddAttribute(XML_NAMESPACE_TEXT,
                                                 XML_START_VALUE,
                                                 OUString::number(nStartValue));
                            }
                        }
                    }
                }
            }
        }
 
        if (GetExport().getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED)
        {
            try
            {
                // ParaMarkerAutoStyleSpan is a hidden property, just to pass the autostyle here
                // See SwXParagraph::Impl::GetPropertyValues_Impl
                css::uno::Any aVal = xPropSet->getPropertyValue(u"ParaMarkerAutoStyleSpan"_ustr);
                if (auto xFakeSpan = aVal.query<css::beans::XPropertySet>())
                {
                    if (bAutoStyles)
                    {
                        Add(XmlStyleFamily::TEXT_TEXT, xFakeSpan);
                    }
                    else
                    {
                        bool bIsUICharStyle, bHasAutoStyle;
                        OUString sStyle = FindTextStyle(xFakeSpan, bIsUICharStyle, bHasAutoStyle);
                        if (!sStyle.isEmpty())
                        {
                            GetExport().AddAttribute(XML_NAMESPACE_LO_EXT, XML_MARKER_STYLE_NAME,
                                                     sStyle);
                        }
                    }
                }
            }
            catch (const css::beans::UnknownPropertyException&)
            {
                // No problem
            }
        }
    }
 
    Reference < XEnumerationAccess > xEA( rTextContent, UNO_QUERY );
    Reference < XEnumeration > xTextEnum = xEA->createEnumeration();
 
    Reference < XEnumeration> xContentEnum;
    Reference < XContentEnumerationAccess > xCEA( rTextContent, UNO_QUERY );
    if( xCEA.is() )
        xContentEnum.set(xCEA->createContentEnumeration( gsTextContentService ));
    const bool bHasContentEnum = xContentEnum.is() &&
                                        xContentEnum->hasMoreElements();
 
    Reference < XTextSection > xSection;
    if( bHasContentEnum )
    {
        // For the auto styles, the multi property set helper is only used
        // if hard attributes are existing. Therefore, it seems to be a better
        // strategy to have the TextSection property separate, because otherwise
        // we always retrieve the style names even if they are not required.
        if( bAutoStyles )
        {
            if( xPropSet->getPropertySetInfo()->hasPropertyByName( gsTextSection ) )
            {
                xSection.set(xPropSet->getPropertyValue( gsTextSection ), uno::UNO_QUERY);
            }
        }
        else
        {
            if( rPropSetHelper.hasProperty( TEXT_SECTION ) )
            {
                xSection.set(rPropSetHelper.getValue( TEXT_SECTION ), uno::UNO_QUERY);
            }
        }
    }
 
    bool bPrevCharIsSpace(true); // true because whitespace at start is ignored
 
    {
        enum XMLTokenEnum eElem =
            0 < nOutlineLevel ? XML_H : XML_P;
        SvXMLElementExport aElem( GetExport(), !bAutoStyles, eExtensionNS == TextPNS::EXTENSION ? XML_NAMESPACE_LO_EXT : XML_NAMESPACE_TEXT, eElem,
                                  true, false );
        if( bHasContentEnum )
        {
            exportTextContentEnumeration(
                                    xContentEnum, bAutoStyles, xSection,
                                    bIsProgress );
        }
        exportTextRangeEnumeration(xTextEnum, bAutoStyles, bIsProgress, bPrevCharIsSpace);
    }
}
 
void XMLTextParagraphExport::exportTextRangeEnumeration(
        const Reference < XEnumeration > & rTextEnum,
        bool bAutoStyles, bool bIsProgress,
        bool & rPrevCharIsSpace)
{
    static const char sFieldMarkName[] = "__FieldMark_";
 
    /* This is  used for exporting to strict OpenDocument 1.2, in which case traditional
     * bookmarks are used instead of fieldmarks. */
    FieldmarkType openFieldMark = NONE;
 
    std::optional<SvXMLElementExport> oTextA;
    HyperlinkData aHyperlinkData;
 
    while( rTextEnum->hasMoreElements() )
    {
        Reference<XPropertySet> xPropSet(rTextEnum->nextElement(), UNO_QUERY);
        Reference < XTextRange > xTxtRange(xPropSet, uno::UNO_QUERY);
        Reference<XPropertySetInfo> xPropInfo(xPropSet->getPropertySetInfo());
 
        if (!bAutoStyles)
        {
            if (HyperlinkData aNewHyperlinkData(xPropSet); aNewHyperlinkData != aHyperlinkData)
            {
                aHyperlinkData = std::move(aNewHyperlinkData);
                oTextA.reset();
                if (aHyperlinkData.addHyperlinkAttributes(GetExport()))
                {
                    oTextA.emplace(GetExport(), true, XML_NAMESPACE_TEXT, XML_A, false, false);
                    aHyperlinkData.exportEvents(GetExport());
                }
            }
        }
 
        if (xPropInfo->hasPropertyByName(gsTextPortionType))
        {
            OUString sType;
            xPropSet->getPropertyValue(gsTextPortionType) >>= sType;
 
            if( sType == gsText)
            {
                exportTextRange( xTxtRange, bAutoStyles,
                                 rPrevCharIsSpace, openFieldMark);
            }
            else if( sType == gsTextField)
            {
                exportTextField(xTxtRange, bAutoStyles, bIsProgress, &rPrevCharIsSpace);
            }
            else if ( sType == "Annotation" )
            {
                exportTextField(xTxtRange, bAutoStyles, bIsProgress, &rPrevCharIsSpace);
            }
            else if ( sType == "AnnotationEnd" )
            {
                if (!bAutoStyles)
                {
                    Reference<XNamed> xBookmark(xPropSet->getPropertyValue(gsBookmark), UNO_QUERY);
                    const OUString aName = xBookmark->getName();
                    if (!aName.isEmpty())
                    {
                        GetExport().AddAttribute(XML_NAMESPACE_OFFICE, XML_NAME, aName);
                    }
                    SvXMLElementExport aElem( GetExport(), XML_NAMESPACE_OFFICE, XML_ANNOTATION_END, false, false );
                }
            }
            else if( sType == gsFrame )
            {
                Reference < XEnumeration> xContentEnum;
                Reference < XContentEnumerationAccess > xCEA( xTxtRange,
                                                              UNO_QUERY );
                if( xCEA.is() )
                    xContentEnum.set(xCEA->createContentEnumeration(
                                                    gsTextContentService ));
                // frames are never in sections
                Reference<XTextSection> xSection;
                if( xContentEnum.is() )
                    exportTextContentEnumeration( xContentEnum,
                                                    bAutoStyles,
                                                    xSection, bIsProgress, true,
                                                     &xPropSet  );
 
            }
            else if (sType == gsFootnote)
            {
                exportTextFootnote(xPropSet,
                                   xTxtRange->getString(),
                                   bAutoStyles, bIsProgress );
            }
            else if (sType == gsBookmark)
            {
                exportTextMark(xPropSet,
                               gsBookmark,
                               lcl_XmlBookmarkElements,
                               bAutoStyles);
            }
            else if (sType == gsReferenceMark)
            {
                exportTextMark(xPropSet,
                               gsReferenceMark,
                               lcl_XmlReferenceElements,
                               bAutoStyles);
            }
            else if (sType == gsDocumentIndexMark)
            {
                m_pIndexMarkExport->ExportIndexMark(xPropSet, bAutoStyles);
            }
            else if (sType == gsRedline)
            {
                if (nullptr != m_pRedlineExport)
                    m_pRedlineExport->ExportChange(xPropSet, bAutoStyles);
            }
            else if (sType == gsRuby)
            {
                exportRuby(xPropSet, bAutoStyles);
            }
            else if (sType == "InContentMetadata")
            {
                exportMeta(xPropSet, bAutoStyles, bIsProgress, rPrevCharIsSpace);
            }
            else if (sType == "ContentControl")
            {
                ExportContentControl(xPropSet, bAutoStyles, bIsProgress, rPrevCharIsSpace);
            }
            else if (sType == gsTextFieldStart)
            {
                Reference< css::text::XFormField > xFormField(xPropSet->getPropertyValue(gsBookmark), UNO_QUERY);
 
                /* As of now, textmarks are a proposed extension to the OpenDocument standard. */
                if (!bAutoStyles)
                {
                    if (GetExport().getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED)
                    {
                        Reference<XNamed> xBookmark(xPropSet->getPropertyValue(gsBookmark), UNO_QUERY);
                        if (xBookmark.is())
                        {
                            GetExport().AddAttribute(XML_NAMESPACE_TEXT, XML_NAME, xBookmark->getName());
                        }
 
                        if (xFormField.is())
                        {
                            GetExport().AddAttribute(XML_NAMESPACE_FIELD, XML_TYPE, xFormField->getFieldType());
                        }
 
                        GetExport().StartElement(XML_NAMESPACE_FIELD, XML_FIELDMARK_START, false);
                        if (xFormField.is())
                        {
                            FieldParamExporter(&GetExport(), xFormField->getParameters()).Export();
                        }
                        GetExport().EndElement(XML_NAMESPACE_FIELD, XML_FIELDMARK_START, false);
                    }
                    /* The OpenDocument standard does not include support for TextMarks for now, so use bookmarks instead. */
                    else
                    {
                        if (xFormField.is())
                        {
                            OUString sName;
                            Reference< css::container::XNameAccess > xParameters = xFormField->getParameters();
                            if (xParameters.is() && xParameters->hasByName(u"Name"_ustr))
                            {
                                const Any aValue = xParameters->getByName(u"Name"_ustr);
                                aValue >>= sName;
                            }
                            if (sName.isEmpty())
                            {   // name attribute is mandatory, so have to pull a
                                // rabbit out of the hat here
                                sName = sFieldMarkName + OUString::number(
                                        m_xImpl->AddFieldMarkStart(xFormField));
                            }
                            GetExport().AddAttribute(XML_NAMESPACE_TEXT, XML_NAME,
                                    sName);
                            SvXMLElementExport aElem( GetExport(), !bAutoStyles,
                                XML_NAMESPACE_TEXT, XML_BOOKMARK_START,
                                false, false );
                            const OUString sFieldType = xFormField->getFieldType();
                            if (sFieldType ==  ODF_FORMTEXT)
                            {
                                openFieldMark = TEXT;
                            }
                            else if (sFieldType == ODF_FORMCHECKBOX)
                            {
                                openFieldMark = CHECK;
                            }
                            else
                            {
                                openFieldMark = NONE;
                            }
                        }
                    }
                }
            }
            else if (sType == gsTextFieldSep)
            {
                if (!bAutoStyles)
                {
                    if (GetExport().getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED)
                    {
                        SvXMLElementExport aElem( GetExport(), !bAutoStyles,
                            XML_NAMESPACE_FIELD, XML_FIELDMARK_SEPARATOR,
                            false, false );
                    }
                }
            }
            else if (sType == gsTextFieldEnd)
            {
                if (!bAutoStyles)
                {
                    Reference< css::text::XFormField > xFormField(xPropSet->getPropertyValue(gsBookmark), UNO_QUERY);
 
                    if (GetExport().getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED)
                    {
                        SvXMLElementExport aElem( GetExport(), !bAutoStyles,
                            XML_NAMESPACE_FIELD, XML_FIELDMARK_END,
                            false, false );
                    }
                    else
                    {
                        if (xFormField.is())
                        {
                            OUString sName;
                            Reference< css::container::XNameAccess > xParameters = xFormField->getParameters();
                            if (xParameters.is() && xParameters->hasByName(u"Name"_ustr))
                            {
                                const Any aValue = xParameters->getByName(u"Name"_ustr);
                                aValue >>= sName;
                            }
                            if (sName.isEmpty())
                            {   // name attribute is mandatory, so have to pull a
                                // rabbit out of the hat here
                                sName = sFieldMarkName + OUString::number(
                                    m_xImpl->GetFieldMarkIndex(xFormField));
                            }
                            GetExport().AddAttribute(XML_NAMESPACE_TEXT, XML_NAME,
                                    sName);
                            SvXMLElementExport aElem( GetExport(), !bAutoStyles,
                                XML_NAMESPACE_TEXT, XML_BOOKMARK_END,
                                false, false );
                        }
                    }
                }
            }
            else if (sType == gsTextFieldStartEnd)
            {
                if (!bAutoStyles)
                {
                    if (GetExport().getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED)
                    {
                        Reference<XNamed> xBookmark(xPropSet->getPropertyValue(gsBookmark), UNO_QUERY);
                        if (xBookmark.is())
                        {
                            GetExport().AddAttribute(XML_NAMESPACE_TEXT, XML_NAME, xBookmark->getName());
                        }
                        Reference< css::text::XFormField > xFormField(xPropSet->getPropertyValue(gsBookmark), UNO_QUERY);
                        if (xFormField.is())
                        {
                            GetExport().AddAttribute(XML_NAMESPACE_FIELD, XML_TYPE, xFormField->getFieldType());
                        }
                        GetExport().StartElement(XML_NAMESPACE_FIELD, XML_FIELDMARK, false);
                        if (xFormField.is())
                        {
                            FieldParamExporter(&GetExport(), xFormField->getParameters()).Export();
                        }
                        GetExport().EndElement(XML_NAMESPACE_FIELD, XML_FIELDMARK, false);
                    }
                    else
                    {
                        Reference<XNamed> xBookmark(xPropSet->getPropertyValue(gsBookmark), UNO_QUERY);
                        if (xBookmark.is())
                        {
                            GetExport().AddAttribute(XML_NAMESPACE_TEXT, XML_NAME, xBookmark->getName());
                            SvXMLElementExport aElem( GetExport(), !bAutoStyles,
                                XML_NAMESPACE_TEXT, XML_BOOKMARK,
                                false, false );
                        }
                    }
                }
            }
            else if (sType == gsSoftPageBreak)
            {
                if (!bAutoStyles)
                    exportSoftPageBreak();
            }
            else if (sType == "LineBreak")
            {
                if (!bAutoStyles)
                    exportTextLineBreak(xPropSet);
            }
            else {
                OSL_FAIL("unknown text portion type");
            }
        }
        else
        {
            Reference<XServiceInfo> xServiceInfo( xTxtRange, UNO_QUERY );
            if( xServiceInfo->supportsService( gsTextFieldService ) )
            {
                exportTextField(xTxtRange, bAutoStyles, bIsProgress, &rPrevCharIsSpace);
            }
            else
            {
                // no TextPortionType property -> non-Writer app -> text
                exportTextRange(xTxtRange, bAutoStyles, rPrevCharIsSpace, openFieldMark);
            }
        }
    }
 
// now that there are nested enumerations for meta(-field), this may be valid!
//  SAL_WARN_IF( bOpenRuby, "xmloff", "Red Alert: Ruby still open!" );
}
 
void XMLTextParagraphExport::exportTable(
        const Reference < XTextContent > &,
        bool /*bAutoStyles*/, bool /*bIsProgress*/ )
{
}
 
void XMLTextParagraphExport::exportTextField(
        const Reference < XTextRange > & rTextRange,
        bool bAutoStyles, bool bIsProgress, bool *const pPrevCharIsSpace)
{
    Reference < XPropertySet > xPropSet( rTextRange, UNO_QUERY );
    // non-Writer apps need not support Property TextField, so test first
    if (!xPropSet->getPropertySetInfo()->hasPropertyByName( gsTextField ))
        return;
 
    Reference < XTextField > xTxtFld(xPropSet->getPropertyValue( gsTextField ), uno::UNO_QUERY);
    SAL_WARN_IF( !xTxtFld.is(), "xmloff", "text field missing" );
    if( xTxtFld.is() )
    {
        exportTextField(xTxtFld, bAutoStyles, bIsProgress, true, pPrevCharIsSpace);
    }
    else
    {
        // write only characters
        GetExport().Characters(rTextRange->getString());
    }
}
 
void XMLTextParagraphExport::exportTextField(
        const Reference < XTextField > & xTextField,
        const bool bAutoStyles, const bool bIsProgress,
        const bool bRecursive, bool *const pPrevCharIsSpace)
{
    if ( bAutoStyles )
    {
        m_pFieldExport->ExportFieldAutoStyle( xTextField, bIsProgress,
                bRecursive );
    }
    else
    {
        assert(pPrevCharIsSpace);
        m_pFieldExport->ExportField(xTextField, bIsProgress, *pPrevCharIsSpace);
    }
}
 
void XMLTextParagraphExport::exportSoftPageBreak()
{
    SvXMLElementExport aElem( GetExport(), XML_NAMESPACE_TEXT,
                              XML_SOFT_PAGE_BREAK, false,
                              false );
}
 
void XMLTextParagraphExport::exportTextLineBreak(
    const uno::Reference<beans::XPropertySet>& xPropSet)
{
    static const XMLTokenEnum aLineBreakClears[] = {
        XML_NONE,
        XML_LEFT,
        XML_RIGHT,
        XML_ALL,
    };
 
    uno::Reference<text::XTextContent> xLineBreak;
    xPropSet->getPropertyValue(u"LineBreak"_ustr) >>= xLineBreak;
    if (!xLineBreak.is())
    {
        return;
    }
 
    uno::Reference<beans::XPropertySet> xLineBreakProps(xLineBreak, uno::UNO_QUERY);
    if (!xLineBreakProps.is())
    {
        return;
    }
 
    sal_Int16 eClear{};
    xLineBreakProps->getPropertyValue(u"Clear"_ustr) >>= eClear;
    if (eClear >= 0 && o3tl::make_unsigned(eClear) < SAL_N_ELEMENTS(aLineBreakClears))
    {
        GetExport().AddAttribute(XML_NAMESPACE_LO_EXT, XML_CLEAR,
                                 GetXMLToken(aLineBreakClears[eClear]));
    }
    SvXMLElementExport aElem(GetExport(), XML_NAMESPACE_TEXT, XML_LINE_BREAK,
                             /*bIgnWSOutside=*/false, /*bIgnWSInside=*/false);
}
 
void XMLTextParagraphExport::exportTextMark(
    const Reference<XPropertySet> & rPropSet,
    const OUString& rProperty,
    const ::xmloff::token::XMLTokenEnum pElements[],
    bool bAutoStyles)
{
    // mib said: "Hau wech!"
 
    // (Originally, I'd export a span element in case the (book|reference)mark
    //  was formatted. This actually makes a difference in case some pervert
    //  sets a point reference mark in the document and, say, formats it bold.
    //  This basically meaningless formatting will now been thrown away
    //  (aka cleaned up), since mib said: ...                   dvo
 
    if (bAutoStyles)
        return;
 
    // name element
    Reference<XNamed> xName(rPropSet->getPropertyValue(rProperty), UNO_QUERY);
    GetExport().AddAttribute(XML_NAMESPACE_TEXT, XML_NAME,
                             xName->getName());
 
    // start, end, or point-reference?
    sal_Int8 nElement;
    if( *o3tl::doAccess<bool>(rPropSet->getPropertyValue(gsIsCollapsed)) )
    {
        nElement = 0;
    }
    else
    {
        nElement = *o3tl::doAccess<bool>(rPropSet->getPropertyValue(gsIsStart)) ? 1 : 2;
    }
 
    // bookmark, bookmark-start: xml:id and RDFa for RDF metadata
    if( nElement < 2 ) {
        GetExport().AddAttributeXmlId(xName);
        const uno::Reference<text::XTextContent> xTextContent(
                xName, uno::UNO_QUERY_THROW);
        GetExport().AddAttributesRDFa(xTextContent);
    }
 
    // bookmark-start: add attributes hidden and condition
    if (nElement == 1)
    {
        Reference<XPropertySet> bkmkProps(rPropSet->getPropertyValue(rProperty), UNO_QUERY);
        Reference<XPropertySetInfo> bkmkPropInfo = bkmkProps->getPropertySetInfo();
        OUString sHidden(u"BookmarkHidden"_ustr);
        if (bkmkPropInfo->hasPropertyByName(sHidden))
        {
            bool bHidden = false;
            bkmkProps->getPropertyValue(sHidden) >>= bHidden;
            if (bHidden)
            {
                GetExport().AddAttribute(XML_NAMESPACE_LO_EXT, u"hidden"_ustr, u"true"_ustr);
                OUString sCondition(u"BookmarkCondition"_ustr);
                if (bkmkPropInfo->hasPropertyByName(sCondition))
                {
                    OUString sBookmarkCondition;
                    bkmkProps->getPropertyValue(sCondition) >>= sBookmarkCondition;
                    GetExport().AddAttribute(XML_NAMESPACE_LO_EXT, u"condition"_ustr, sBookmarkCondition);
                }
            }
        }
    }
 
    // export element
    assert(pElements != nullptr);
    assert(0 <= nElement && nElement <= 2);
    SvXMLElementExport aElem(GetExport(),
                             XML_NAMESPACE_TEXT, pElements[nElement],
                             false, false);
    // else: no styles. (see above)
}
 
static bool lcl_txtpara_isBoundAsChar(
        const Reference < XPropertySet > & rPropSet,
        const Reference < XPropertySetInfo > & rPropSetInfo )
{
    bool bIsBoundAsChar = false;
    OUString sAnchorType( u"AnchorType"_ustr  );
    if( rPropSetInfo->hasPropertyByName( sAnchorType ) )
    {
        TextContentAnchorType eAnchor;
        rPropSet->getPropertyValue( sAnchorType ) >>= eAnchor;
        bIsBoundAsChar = TextContentAnchorType_AS_CHARACTER == eAnchor;
    }
 
    return bIsBoundAsChar;
}
 
XMLShapeExportFlags XMLTextParagraphExport::addTextFrameAttributes(
    const Reference < XPropertySet >& rPropSet,
    bool bShape,
    basegfx::B2DPoint* pCenter,
    OUString* pMinHeightValue,
    OUString* pMinWidthValue)
{
    XMLShapeExportFlags nShapeFeatures = SEF_DEFAULT;
 
    // draw:name (#97662#: not for shapes, since those names will be
    // treated in the shape export)
    if( !bShape )
    {
        Reference < XNamed > xNamed( rPropSet, UNO_QUERY );
        if( xNamed.is() )
        {
            OUString sName( xNamed->getName() );
            if( !sName.isEmpty() )
                GetExport().AddAttribute( XML_NAMESPACE_DRAW, XML_NAME,
                                          xNamed->getName() );
        }
    }
 
    OUStringBuffer sValue;
 
    // text:anchor-type
    TextContentAnchorType eAnchor = TextContentAnchorType_AT_PARAGRAPH;
    rPropSet->getPropertyValue( gsAnchorType ) >>= eAnchor;
    {
        XMLAnchorTypePropHdl aAnchorTypeHdl;
        OUString sTmp;
        aAnchorTypeHdl.exportXML( sTmp, uno::Any(eAnchor),
                                  GetExport().GetMM100UnitConverter() );
        GetExport().AddAttribute( XML_NAMESPACE_TEXT, XML_ANCHOR_TYPE, sTmp );
    }
 
    // text:anchor-page-number
    if( TextContentAnchorType_AT_PAGE == eAnchor )
    {
        sal_Int16 nPage = 0;
        rPropSet->getPropertyValue( gsAnchorPageNo ) >>= nPage;
        SAL_WARN_IF(nPage <= 0, "xmloff",
                "ERROR: writing invalid anchor-page-number 0");
        GetExport().AddAttribute( XML_NAMESPACE_TEXT, XML_ANCHOR_PAGE_NUMBER,
                                  OUString::number( nPage ) );
    }
    else
    {
        nShapeFeatures |= XMLShapeExportFlags::NO_WS;
    }
 
    // OD 2004-06-01 #i27691# - correction: no export of svg:x, if object
    // is anchored as-character.
    if ( !bShape &&
         eAnchor != TextContentAnchorType_AS_CHARACTER )
    {
        // svg:x
        sal_Int16 nHoriOrient =  HoriOrientation::NONE;
        rPropSet->getPropertyValue( gsHoriOrient ) >>= nHoriOrient;
        if( HoriOrientation::NONE == nHoriOrient )
        {
            sal_Int32 nPos = 0;
            rPropSet->getPropertyValue( gsHoriOrientPosition ) >>= nPos;
            GetExport().GetMM100UnitConverter().convertMeasureToXML(
                    sValue, nPos );
            GetExport().AddAttribute( XML_NAMESPACE_SVG, XML_X,
                                      sValue.makeStringAndClear() );
            if(nullptr != pCenter)
            {
                // add left edge to Center
                pCenter->setX(pCenter->getX() + nPos);
            }
        }
    }
    else if( TextContentAnchorType_AS_CHARACTER == eAnchor )
        nShapeFeatures = (nShapeFeatures & ~XMLShapeExportFlags::X);
 
    if( !bShape || TextContentAnchorType_AS_CHARACTER == eAnchor  )
    {
        // svg:y
        sal_Int16 nVertOrient =  VertOrientation::NONE;
        rPropSet->getPropertyValue( gsVertOrient ) >>= nVertOrient;
        if( VertOrientation::NONE == nVertOrient )
        {
            sal_Int32 nPos = 0;
            rPropSet->getPropertyValue( gsVertOrientPosition ) >>= nPos;
            GetExport().GetMM100UnitConverter().convertMeasureToXML(
                    sValue, nPos );
            GetExport().AddAttribute( XML_NAMESPACE_SVG, XML_Y,
                                      sValue.makeStringAndClear() );
            if(nullptr != pCenter)
            {
                // add top edge to Center
                pCenter->setY(pCenter->getY() + nPos);
            }
        }
        if( bShape )
            nShapeFeatures = (nShapeFeatures & ~XMLShapeExportFlags::Y);
    }
 
    Reference< XPropertySetInfo > xPropSetInfo(rPropSet->getPropertySetInfo());
 
    bool bSyncWidth = false;
    if (xPropSetInfo->hasPropertyByName(gsIsSyncWidthToHeight))
    {
        bSyncWidth = *o3tl::doAccess<bool>(rPropSet->getPropertyValue(gsIsSyncWidthToHeight));
    }
    sal_Int16 nRelWidth = 0;
    if (!bSyncWidth && xPropSetInfo->hasPropertyByName(gsRelativeWidth))
    {
        rPropSet->getPropertyValue(gsRelativeWidth) >>= nRelWidth;
    }
    bool bSyncHeight = false;
    if (xPropSetInfo->hasPropertyByName(gsIsSyncHeightToWidth))
    {
        bSyncHeight = *o3tl::doAccess<bool>(rPropSet->getPropertyValue(gsIsSyncHeightToWidth));
    }
    sal_Int16 nRelHeight = 0;
    if (!bSyncHeight && xPropSetInfo->hasPropertyByName(gsRelativeHeight))
    {
        rPropSet->getPropertyValue(gsRelativeHeight) >>= nRelHeight;
    }
    awt::Size aLayoutSize;
    if ((nRelWidth > 0 || nRelHeight > 0) && xPropSetInfo->hasPropertyByName(u"LayoutSize"_ustr))
    {
        rPropSet->getPropertyValue(u"LayoutSize"_ustr) >>= aLayoutSize;
    }
 
    bool bUseLayoutSize = true;
    if (bSyncWidth && bSyncHeight)
    {
        // This is broken, width depends on height and height depends on width. Don't use the
        // invalid layout size we got.
        bUseLayoutSize = false;
    }
    if (aLayoutSize.Width <= 0 || aLayoutSize.Height <= 0)
    {
        // This is broken, Writer frames have a minimal size, see MINFLY.
        bUseLayoutSize = false;
    }
 
    // svg:width
    sal_Int16 nWidthType = SizeType::FIX;
    if( xPropSetInfo->hasPropertyByName( gsWidthType ) )
    {
        rPropSet->getPropertyValue( gsWidthType ) >>= nWidthType;
    }
    if( xPropSetInfo->hasPropertyByName( gsWidth ) )
    {
        sal_Int32 nWidth =  0;
        // VAR size will be written as zero min-size
        if( SizeType::VARIABLE != nWidthType )
        {
            rPropSet->getPropertyValue( gsWidth ) >>= nWidth;
        }
        GetExport().GetMM100UnitConverter().convertMeasureToXML(sValue, nWidth);
        if( SizeType::FIX != nWidthType )
        {
            assert(pMinWidthValue);
            if (pMinWidthValue)
            {
                *pMinWidthValue = sValue.makeStringAndClear();
            }
        }
        else
        {
            if ((nRelWidth > 0 || bSyncWidth) && bUseLayoutSize)
            {
                // Relative width: write the layout size for the fallback width.
                sValue.setLength(0);
                GetExport().GetMM100UnitConverter().convertMeasureToXML(sValue, aLayoutSize.Width);
            }
 
            GetExport().AddAttribute( XML_NAMESPACE_SVG, XML_WIDTH,
                                      sValue.makeStringAndClear() );
            if(nullptr != pCenter)
            {
                // add half width to Center
                pCenter->setX(pCenter->getX() + (0.5 * nWidth));
            }
        }
    }
    if( xPropSetInfo->hasPropertyByName( gsIsSyncWidthToHeight ) )
    {
        if( bSyncWidth )
            GetExport().AddAttribute( XML_NAMESPACE_STYLE, XML_REL_WIDTH,
                                      XML_SCALE );
    }
    if( !bSyncWidth && xPropSetInfo->hasPropertyByName( gsRelativeWidth ) )
    {
        SAL_WARN_IF( nRelWidth < 0 || nRelWidth > 254, "xmloff",
                    "Got illegal relative width from API" );
        if( nRelWidth > 0 )
        {
            ::sax::Converter::convertPercent( sValue, nRelWidth );
            GetExport().AddAttribute( XML_NAMESPACE_STYLE, XML_REL_WIDTH,
                                      sValue.makeStringAndClear() );
        }
    }
 
    // svg:height, fo:min-height or style:rel-height
    sal_Int16 nSizeType = SizeType::FIX;
    if( xPropSetInfo->hasPropertyByName( gsSizeType ) )
    {
        rPropSet->getPropertyValue( gsSizeType ) >>= nSizeType;
    }
    if( xPropSetInfo->hasPropertyByName( gsHeight ) )
    {
        sal_Int32 nHeight =  0;
        if( SizeType::VARIABLE != nSizeType )
        {
            rPropSet->getPropertyValue( gsHeight ) >>= nHeight;
        }
        GetExport().GetMM100UnitConverter().convertMeasureToXML( sValue,
                                                            nHeight );
        if( SizeType::FIX != nSizeType && 0==nRelHeight && !bSyncHeight &&
             pMinHeightValue )
        {
            *pMinHeightValue = sValue.makeStringAndClear();
        }
        else
        {
            if ((nRelHeight > 0 || bSyncHeight) && bUseLayoutSize)
            {
                // Relative height: write the layout size for the fallback height.
                sValue.setLength(0);
                GetExport().GetMM100UnitConverter().convertMeasureToXML(sValue, aLayoutSize.Height);
            }
 
            GetExport().AddAttribute( XML_NAMESPACE_SVG, XML_HEIGHT,
                                      sValue.makeStringAndClear() );
            if(nullptr != pCenter)
            {
                // add half height to Center
                pCenter->setY(pCenter->getY() + (0.5 * nHeight));
            }
        }
    }
    if( bSyncHeight )
    {
        GetExport().AddAttribute( XML_NAMESPACE_STYLE, XML_REL_HEIGHT,
                SizeType::MIN == nSizeType ? XML_SCALE_MIN : XML_SCALE );
 
    }
    else if( nRelHeight > 0 )
    {
        ::sax::Converter::convertPercent( sValue, nRelHeight );
        if( SizeType::MIN == nSizeType )
        {
            assert(pMinHeightValue);
            if (pMinHeightValue)
            {
                *pMinHeightValue = sValue.makeStringAndClear();
            }
        }
        else
            GetExport().AddAttribute( XML_NAMESPACE_STYLE, XML_REL_HEIGHT,
                                      sValue.makeStringAndClear() );
    }
 
    OUString sZOrder( u"ZOrder"_ustr  );
    if( xPropSetInfo->hasPropertyByName( sZOrder ) )
    {
        sal_Int32 nZIndex = 0;
        rPropSet->getPropertyValue( sZOrder ) >>= nZIndex;
        if( -1 != nZIndex )
        {
            GetExport().AddAttribute( XML_NAMESPACE_DRAW, XML_ZINDEX,
                                      OUString::number( nZIndex ) );
        }
    }
 
    if (xPropSetInfo->hasPropertyByName(u"IsSplitAllowed"_ustr)
        && rPropSet->getPropertyValue(u"IsSplitAllowed"_ustr).get<bool>())
    {
        GetExport().AddAttribute(XML_NAMESPACE_LO_EXT, XML_MAY_BREAK_BETWEEN_PAGES, XML_TRUE);
    }
 
    return nShapeFeatures;
}
 
void XMLTextParagraphExport::exportAnyTextFrame(
        const Reference < XTextContent > & rTxtCntnt,
        FrameType eType,
        bool bAutoStyles,
        bool bIsProgress,
        bool bExportContent,
        const Reference < XPropertySet > *pRangePropSet)
{
    Reference < XPropertySet > xPropSet( rTxtCntnt, UNO_QUERY );
 
    if( bAutoStyles )
    {
        if( FrameType::Embedded == eType )
            _collectTextEmbeddedAutoStyles( xPropSet );
        // No text frame style for shapes (#i28745#)
        else if ( FrameType::Shape != eType )
            Add( XmlStyleFamily::TEXT_FRAME, xPropSet );
 
        if( pRangePropSet && lcl_txtpara_isBoundAsChar( xPropSet,
                                            xPropSet->getPropertySetInfo() ) )
            Add( XmlStyleFamily::TEXT_TEXT, *pRangePropSet );
 
        switch( eType )
        {
        case FrameType::Text:
            {
                // frame bound frames
                if ( bExportContent )
                {
                    Reference < XTextFrame > xTxtFrame( rTxtCntnt, UNO_QUERY );
                    bool bAlreadySeen = !maFrameRecurseGuard.insert(xTxtFrame).second;
                    if (bAlreadySeen)
                    {
                        SAL_WARN("xmloff", "loop in frame export, ditching");
                    }
                    else
                    {
                        comphelper::ScopeGuard const g([this, xTxtFrame]() {
                            maFrameRecurseGuard.erase(xTxtFrame);
                        });
                        Reference < XText > xTxt(xTxtFrame->getText());
                        exportFrameFrames( true, bIsProgress, xTxtFrame );
                        exportText( xTxt, bAutoStyles, bIsProgress, true );
                    }
                }
            }
            break;
        case FrameType::Shape:
            {
                Reference < XShape > xShape( rTxtCntnt, UNO_QUERY );
                bool bAlreadySeen = !maShapeRecurseGuard.insert(xShape).second;
                if (bAlreadySeen)
                {
                    SAL_WARN("xmloff", "loop in shape export, ditching");
                }
                else
                {
                    comphelper::ScopeGuard const g([this, xShape]() {
                        maShapeRecurseGuard.erase(xShape);
                    });
                    GetExport().GetShapeExport()->collectShapeAutoStyles( xShape );
                }
            }
            break;
        default:
            break;
        }
    }
    else
    {
        Reference< XPropertySetInfo > xPropSetInfo(xPropSet->getPropertySetInfo());
        {
            bool bAddCharStyles = pRangePropSet &&
                lcl_txtpara_isBoundAsChar( xPropSet, xPropSetInfo );
 
            bool bIsUICharStyle;
            bool bHasAutoStyle = false;
 
            OUString sStyle;
 
            if( bAddCharStyles )
                sStyle = FindTextStyle( *pRangePropSet, bIsUICharStyle, bHasAutoStyle );
            else
                bIsUICharStyle = false;
 
            bool bDoSomething = bIsUICharStyle
                && m_aCharStyleNamesPropInfoCache.hasProperty( *pRangePropSet );
            XMLTextCharStyleNamesElementExport aCharStylesExport(
                GetExport(), bDoSomething, bHasAutoStyle,
                bDoSomething ? *pRangePropSet : Reference<XPropertySet>(),
                gsCharStyleNames );
 
            if( !sStyle.isEmpty() )
                GetExport().AddAttribute( XML_NAMESPACE_TEXT, XML_STYLE_NAME,
                                  GetExport().EncodeStyleName( sStyle ) );
            {
                SvXMLElementExport aElem( GetExport(), !sStyle.isEmpty(),
                    XML_NAMESPACE_TEXT, XML_SPAN, false, false );
                {
                    SvXMLElementExport aElement( GetExport(),
                        FrameType::Shape != eType &&
                            HyperlinkData(xPropSet).addHyperlinkAttributes(GetExport()),
                        XML_NAMESPACE_DRAW, XML_A, false, false );
                    switch( eType )
                    {
                    case FrameType::Text:
                        _exportTextFrame( xPropSet, xPropSetInfo, bIsProgress );
                        break;
                    case FrameType::Graphic:
                        _exportTextGraphic( xPropSet, xPropSetInfo );
                        break;
                    case FrameType::Embedded:
                        _exportTextEmbedded( xPropSet, xPropSetInfo );
                        break;
                    case FrameType::Shape:
                        {
                            Reference < XShape > xShape( rTxtCntnt, UNO_QUERY );
                            XMLShapeExportFlags nFeatures =
                                addTextFrameAttributes( xPropSet, true );
                            GetExport().GetShapeExport()
                                ->exportShape( xShape, nFeatures );
                        }
                        break;
                    }
                }
            }
        }
    }
}
 
void XMLTextParagraphExport::_exportTextFrame(
        const Reference < XPropertySet > & rPropSet,
        const Reference < XPropertySetInfo > & rPropSetInfo,
        bool bIsProgress )
{
    Reference < XTextFrame > xTxtFrame( rPropSet, UNO_QUERY );
    Reference < XText > xTxt(xTxtFrame->getText());
 
    OUString sStyle;
    if( rPropSetInfo->hasPropertyByName( gsFrameStyleName ) )
    {
        rPropSet->getPropertyValue( gsFrameStyleName ) >>= sStyle;
    }
 
    OUString aMinHeightValue;
    OUString sMinWidthValue;
    OUString sAutoStyle = Find( XmlStyleFamily::TEXT_FRAME, rPropSet, sStyle );
    if ( sAutoStyle.isEmpty() )
        sAutoStyle = sStyle;
    if( !sAutoStyle.isEmpty() )
        GetExport().AddAttribute( XML_NAMESPACE_DRAW, XML_STYLE_NAME,
                              GetExport().EncodeStyleName( sAutoStyle ) );
    addTextFrameAttributes(rPropSet, false, nullptr, &aMinHeightValue, &sMinWidthValue);
 
    SvXMLElementExport aElem( GetExport(), XML_NAMESPACE_DRAW,
                              XML_FRAME, false, true );
 
    if( !aMinHeightValue.isEmpty() )
        GetExport().AddAttribute( XML_NAMESPACE_FO, XML_MIN_HEIGHT,
                                  aMinHeightValue );
 
    if (!sMinWidthValue.isEmpty())
    {
        GetExport().AddAttribute( XML_NAMESPACE_FO, XML_MIN_WIDTH,
                                  sMinWidthValue );
    }
 
    // draw:chain-next-name
    if( rPropSetInfo->hasPropertyByName( gsChainNextName ) )
    {
        OUString sNext;
        if( (rPropSet->getPropertyValue( gsChainNextName ) >>= sNext) && !sNext.isEmpty() )
            GetExport().AddAttribute( XML_NAMESPACE_DRAW,
                                      XML_CHAIN_NEXT_NAME,
                                      sNext );
    }
 
    {
        SvXMLElementExport aElement( GetExport(), XML_NAMESPACE_DRAW,
                                  XML_TEXT_BOX, true, true );
 
        // frames bound to frame
        exportFrameFrames( false, bIsProgress, xTxtFrame );
 
        exportText( xTxt, false, bIsProgress, true );
    }
 
    // script:events
    Reference<XEventsSupplier> xEventsSupp( xTxtFrame, UNO_QUERY );
    GetExport().GetEventExport().Export(xEventsSupp);
 
    // image map
    GetExport().GetImageMapExport().Export( rPropSet );
 
    // svg:title and svg:desc (#i73249#)
    exportTitleAndDescription( rPropSet, rPropSetInfo );
}
 
void XMLTextParagraphExport::exportContour(
    const Reference < XPropertySet > & rPropSet,
    const Reference < XPropertySetInfo > & rPropSetInfo )
{
    if( !rPropSetInfo->hasPropertyByName( gsContourPolyPolygon ) )
    {
        return;
    }
 
    PointSequenceSequence aSourcePolyPolygon;
    rPropSet->getPropertyValue( gsContourPolyPolygon ) >>= aSourcePolyPolygon;
    const basegfx::B2DPolyPolygon aPolyPolygon(
        basegfx::utils::UnoPointSequenceSequenceToB2DPolyPolygon(
            aSourcePolyPolygon));
    const sal_uInt32 nPolygonCount(aPolyPolygon.count());
 
    if(!nPolygonCount)
    {
        return;
    }
 
    const basegfx::B2DRange aPolyPolygonRange(aPolyPolygon.getB2DRange());
    bool bPixel(false);
 
    if( rPropSetInfo->hasPropertyByName( gsIsPixelContour ) )
    {
        bPixel = *o3tl::doAccess<bool>(rPropSet->getPropertyValue( gsIsPixelContour ));
    }
 
    // svg: width
    OUStringBuffer aStringBuffer( 10 );
 
    if(bPixel)
    {
        ::sax::Converter::convertMeasurePx(aStringBuffer, basegfx::fround(aPolyPolygonRange.getWidth()));
    }
    else
    {
        GetExport().GetMM100UnitConverter().convertMeasureToXML(aStringBuffer, basegfx::fround(aPolyPolygonRange.getWidth()));
    }
 
    GetExport().AddAttribute(XML_NAMESPACE_SVG, XML_WIDTH, aStringBuffer.makeStringAndClear());
 
    // svg: height
    if(bPixel)
    {
        ::sax::Converter::convertMeasurePx(aStringBuffer, basegfx::fround(aPolyPolygonRange.getHeight()));
    }
    else
    {
        GetExport().GetMM100UnitConverter().convertMeasureToXML(aStringBuffer, basegfx::fround(aPolyPolygonRange.getHeight()));
    }
 
    GetExport().AddAttribute(XML_NAMESPACE_SVG, XML_HEIGHT, aStringBuffer.makeStringAndClear());
 
    // svg:viewbox
    SdXMLImExViewBox aViewBox(0.0, 0.0, aPolyPolygonRange.getWidth(), aPolyPolygonRange.getHeight());
    GetExport().AddAttribute(XML_NAMESPACE_SVG, XML_VIEWBOX, aViewBox.GetExportString());
    enum XMLTokenEnum eElem = XML_TOKEN_INVALID;
 
    if(1 == nPolygonCount )
    {
        // simple polygon shape, can be written as svg:points sequence
        const OUString aPointString(
            basegfx::utils::exportToSvgPoints(
                aPolyPolygon.getB2DPolygon(0)));
 
        // write point array
        GetExport().AddAttribute(XML_NAMESPACE_DRAW, XML_POINTS, aPointString);
        eElem = XML_CONTOUR_POLYGON;
    }
    else
    {
        // polypolygon, needs to be written as a svg:path sequence
        const OUString aPolygonString(
            basegfx::utils::exportToSvgD(
                aPolyPolygon,
                true,           // bUseRelativeCoordinates
                false,          // bDetectQuadraticBeziers: not used in old, but maybe activated now
                true));         // bHandleRelativeNextPointCompatible
 
        // write point array
        GetExport().AddAttribute( XML_NAMESPACE_SVG, XML_D, aPolygonString);
        eElem = XML_CONTOUR_PATH;
    }
 
    if( rPropSetInfo->hasPropertyByName( gsIsAutomaticContour ) )
    {
        bool bTmp = *o3tl::doAccess<bool>(rPropSet->getPropertyValue(
                                            gsIsAutomaticContour ));
        GetExport().AddAttribute( XML_NAMESPACE_DRAW,
                      XML_RECREATE_ON_EDIT, bTmp ? XML_TRUE : XML_FALSE );
    }
 
    // write object now
    SvXMLElementExport aElem( GetExport(), XML_NAMESPACE_DRAW, eElem,
                              true, true );
}
 
void XMLTextParagraphExport::_exportTextGraphic(
        const Reference < XPropertySet > & rPropSet,
        const Reference < XPropertySetInfo > & rPropSetInfo )
{
    OUString sStyle;
    if( rPropSetInfo->hasPropertyByName( gsFrameStyleName ) )
    {
        rPropSet->getPropertyValue( gsFrameStyleName ) >>= sStyle;
    }
 
    OUString sAutoStyle = Find( XmlStyleFamily::TEXT_FRAME, rPropSet, sStyle );
    if ( sAutoStyle.isEmpty() )
        sAutoStyle = sStyle;
    if( !sAutoStyle.isEmpty() )
        GetExport().AddAttribute( XML_NAMESPACE_DRAW, XML_STYLE_NAME,
                                  GetExport().EncodeStyleName( sAutoStyle ) );
 
    // check if we need to use svg:transform
    sal_Int16 nRotation(0);
    rPropSet->getPropertyValue( gsGraphicRotation ) >>= nRotation;
    const bool bUseRotation(0 != nRotation);
    basegfx::B2DPoint aCenter(0.0, 0.0);
 
    // add TextFrame attributes like svg:x/y/width/height, also get back
    // object's center point if rotation is used and has to be exported
    addTextFrameAttributes(rPropSet, false, bUseRotation ? &aCenter : nullptr);
 
    // svg:transform
    if(bUseRotation)
    {
        // RotateFlyFrameFix: im/export full 'draw:transform' using existing tooling.
        // Currently only rotation is used, but combinations with 'draw:transform'
        // may be necessary in the future, so that svg:x/svg:y/svg:width/svg:height
        // may be extended/replaced with 'draw:transform' (see draw objects)
        SdXMLImExTransform2D aSdXMLImExTransform2D;
 
        // Convert from 10th degree integer to deg.
        // CAUTION: internal rotation is classically mathematically 'wrong' defined by ignoring that
        // we have a right-handed coordinate system, so need to correct this by mirroring
        // the rotation to get the correct transformation. See also case XML_TOK_TEXT_FRAME_TRANSFORM
        // in XMLTextFrameContext_Impl::XMLTextFrameContext_Impl and #i78696#
        // CAUTION-II: due to tdf#115782 it is better for current ODF to indeed write it with the wrong
        // orientation as in all other cases - ARGH! We will need to correct this in future ODF ASAP!
        const double fRotate(basegfx::deg2rad<10>(nRotation));
 
        // transform to rotation center which is the object's center
        aSdXMLImExTransform2D.AddTranslate(-aCenter);
 
        // add rotation itself
        // tdf#115529 but correct value modulo 2PI to have it positive and in the range of [0.0 .. 2PI[
        aSdXMLImExTransform2D.AddRotate(basegfx::normalizeToRange(fRotate, 2 * M_PI));
 
        // back-transform after rotation
        aSdXMLImExTransform2D.AddTranslate(aCenter);
 
        // Note: using GetTwipUnitConverter instead of GetMM100UnitConverter may be needed,
        // but is not generally available (as it should be, a 'current' UnitConverter should
        // be available at GetExport() - and maybe was once). May have to be addressed as soon
        // as translate transformations are used here.
        GetExport().AddAttribute(
            XML_NAMESPACE_DRAW,
            XML_TRANSFORM,
            aSdXMLImExTransform2D.GetExportString(GetExport().GetMM100UnitConverter()));
    }
 
    // original content
    SvXMLElementExport aElem(GetExport(), XML_NAMESPACE_DRAW, XML_FRAME, false, true);
 
    {
        // xlink:href
        uno::Reference<graphic::XGraphic> xGraphic;
        rPropSet->getPropertyValue(u"Graphic"_ustr) >>= xGraphic;
 
        OUString sInternalURL;
        OUString sOutMimeType;
 
        if (xGraphic.is())
        {
            sInternalURL = GetExport().AddEmbeddedXGraphic(xGraphic, sOutMimeType);
        }
 
        // If there still is no url, then graphic is empty
        if (!sInternalURL.isEmpty())
        {
            GetExport().AddAttribute(XML_NAMESPACE_XLINK, XML_HREF, sInternalURL);
            GetExport().AddAttribute(XML_NAMESPACE_XLINK, XML_TYPE, XML_SIMPLE);
            GetExport().AddAttribute(XML_NAMESPACE_XLINK, XML_SHOW, XML_EMBED);
            GetExport().AddAttribute(XML_NAMESPACE_XLINK, XML_ACTUATE, XML_ONLOAD);
        }
 
        // draw:filter-name
        OUString sGrfFilter;
        rPropSet->getPropertyValue( gsGraphicFilter ) >>= sGrfFilter;
        if( !sGrfFilter.isEmpty() )
            GetExport().AddAttribute( XML_NAMESPACE_DRAW, XML_FILTER_NAME,
                                      sGrfFilter );
 
        if (GetExport().getSaneDefaultVersion() > SvtSaveOptions::ODFSVER_012)
        {
            if (sOutMimeType.isEmpty())
            {
                GetExport().GetGraphicMimeTypeFromStream(xGraphic, sOutMimeType);
            }
            if (!sOutMimeType.isEmpty())
            {   // ODF 1.3 OFFICE-3943
                GetExport().AddAttribute(
                    SvtSaveOptions::ODFSVER_013 <= GetExport().getSaneDefaultVersion()
                        ? XML_NAMESPACE_DRAW
                        : XML_NAMESPACE_LO_EXT,
                    u"mime-type"_ustr, sOutMimeType);
            }
        }
 
 
        // optional office:binary-data
        if (xGraphic.is())
        {
            SvXMLElementExport aElement(GetExport(), XML_NAMESPACE_DRAW, XML_IMAGE, false, true );
            GetExport().AddEmbeddedXGraphicAsBase64(xGraphic);
        }
    }
 
    const bool bAddReplacementImages = officecfg::Office::Common::Save::Graphic::AddReplacementImages::get();
    if (bAddReplacementImages)
    {
        // replacement graphic for backwards compatibility, but
        // only for SVG and metafiles currently
        uno::Reference<graphic::XGraphic> xReplacementGraphic;
        rPropSet->getPropertyValue(u"ReplacementGraphic"_ustr) >>= xReplacementGraphic;
 
        OUString sInternalURL;
        OUString sOutMimeType;
 
        //Resolves: fdo#62461 put preferred image first above, followed by
        //fallback here
        if (xReplacementGraphic.is())
        {
            sInternalURL = GetExport().AddEmbeddedXGraphic(xReplacementGraphic, sOutMimeType);
        }
 
        // If there is no url, then graphic is empty
        if (!sInternalURL.isEmpty())
        {
            GetExport().AddAttribute(XML_NAMESPACE_XLINK, XML_HREF, sInternalURL);
            GetExport().AddAttribute(XML_NAMESPACE_XLINK, XML_TYPE, XML_SIMPLE);
            GetExport().AddAttribute(XML_NAMESPACE_XLINK, XML_SHOW, XML_EMBED);
            GetExport().AddAttribute(XML_NAMESPACE_XLINK, XML_ACTUATE, XML_ONLOAD);
        }
 
        if (GetExport().getSaneDefaultVersion() > SvtSaveOptions::ODFSVER_012)
        {
            if (sOutMimeType.isEmpty())
            {
                GetExport().GetGraphicMimeTypeFromStream(xReplacementGraphic, sOutMimeType);
            }
            if (!sOutMimeType.isEmpty())
            {   // ODF 1.3 OFFICE-3943
                GetExport().AddAttribute(
                    SvtSaveOptions::ODFSVER_013 <= GetExport().getSaneDefaultVersion()
                        ? XML_NAMESPACE_DRAW
                        : XML_NAMESPACE_LO_EXT,
                    u"mime-type"_ustr, sOutMimeType);
            }
        }
 
 
        // optional office:binary-data
        if (xReplacementGraphic.is())
        {
            SvXMLElementExport aElement(GetExport(), XML_NAMESPACE_DRAW, XML_IMAGE, true, true);
            GetExport().AddEmbeddedXGraphicAsBase64(xReplacementGraphic);
        }
    }
 
    // script:events
    Reference<XEventsSupplier> xEventsSupp( rPropSet, UNO_QUERY );
    GetExport().GetEventExport().Export(xEventsSupp);
 
    // image map
    GetExport().GetImageMapExport().Export( rPropSet );
 
    // svg:title and svg:desc (#i73249#)
    exportTitleAndDescription( rPropSet, rPropSetInfo );
 
    // draw:contour
    exportContour( rPropSet, rPropSetInfo );
}
 
void XMLTextParagraphExport::_collectTextEmbeddedAutoStyles(const Reference < XPropertySet > & )
{
    SAL_WARN( "xmloff", "no API implementation available" );
}
 
void XMLTextParagraphExport::_exportTextEmbedded(
        const Reference < XPropertySet > &,
        const Reference < XPropertySetInfo > & )
{
    SAL_WARN( "xmloff", "no API implementation available" );
}
 
void XMLTextParagraphExport::exportEvents( const Reference < XPropertySet > & rPropSet )
{
    // script:events
    Reference<XEventsSupplier> xEventsSupp( rPropSet, UNO_QUERY );
    GetExport().GetEventExport().Export(xEventsSupp);
 
    // image map
    if (rPropSet->getPropertySetInfo()->hasPropertyByName(u"ImageMap"_ustr))
        GetExport().GetImageMapExport().Export( rPropSet );
}
 
// Implement Title/Description Elements UI (#i73249#)
void XMLTextParagraphExport::exportTitleAndDescription(
        const Reference < XPropertySet > & rPropSet,
        const Reference < XPropertySetInfo > & rPropSetInfo )
{
    // svg:title
    if( rPropSetInfo->hasPropertyByName( gsTitle ) )
    {
        OUString sObjTitle;
        rPropSet->getPropertyValue( gsTitle ) >>= sObjTitle;
        if( !sObjTitle.isEmpty() )
        {
            SvXMLElementExport aElem( GetExport(), XML_NAMESPACE_SVG,
                                      XML_TITLE, true, false );
            GetExport().Characters( sObjTitle );
        }
    }
 
    // svg:description
    if( rPropSetInfo->hasPropertyByName( gsDescription ) )
    {
        OUString sObjDesc;
        rPropSet->getPropertyValue( gsDescription ) >>= sObjDesc;
        if( !sObjDesc.isEmpty() )
        {
            SvXMLElementExport aElem( GetExport(), XML_NAMESPACE_SVG,
                                      XML_DESC, true, false );
            GetExport().Characters( sObjDesc );
        }
    }
}
 
void XMLTextParagraphExport::exportTextRangeSpan(
    const css::uno::Reference< css::text::XTextRange > & rTextRange,
    Reference< XPropertySet > const & xPropSet,
    Reference < XPropertySetInfo > & xPropSetInfo,
    const bool bIsUICharStyle,
    const bool bHasAutoStyle,
    const OUString& sStyle,
    bool& rPrevCharIsSpace,
    FieldmarkType& openFieldMark )
{
    XMLTextCharStyleNamesElementExport aCharStylesExport(
            GetExport(),
            bIsUICharStyle && m_aCharStyleNamesPropInfoCache.hasProperty( xPropSet, xPropSetInfo ),
            bHasAutoStyle,
            xPropSet,
            gsCharStyleNames );
 
    if ( !sStyle.isEmpty() )
    {
        GetExport().AddAttribute( XML_NAMESPACE_TEXT, XML_STYLE_NAME, GetExport().EncodeStyleName( sStyle ) );
    }
    {
        SvXMLElementExport aElement( GetExport(), !sStyle.isEmpty(), XML_NAMESPACE_TEXT, XML_SPAN, false, false );
        const OUString aText( rTextRange->getString() );
        SvXMLElementExport aElem2( GetExport(), TEXT == openFieldMark,
            XML_NAMESPACE_TEXT, XML_TEXT_INPUT,
            false, false );
        exportCharacterData(aText, rPrevCharIsSpace);
        openFieldMark = NONE;
    }
}
 
void XMLTextParagraphExport::exportTextRange(
        const Reference< XTextRange > & rTextRange,
        bool bAutoStyles,
        bool& rPrevCharIsSpace,
        FieldmarkType& openFieldMark )
{
    Reference< XPropertySet > xPropSet( rTextRange, UNO_QUERY );
    if ( bAutoStyles )
    {
        Add( XmlStyleFamily::TEXT_TEXT, xPropSet );
    }
    else
    {
        bool bIsUICharStyle = false;
        bool bHasAutoStyle = false;
        const OUString sStyle(
            FindTextStyle( xPropSet, bIsUICharStyle, bHasAutoStyle ) );
 
        Reference < XPropertySetInfo > xPropSetInfo;
        exportTextRangeSpan( rTextRange, xPropSet, xPropSetInfo, bIsUICharStyle, bHasAutoStyle, sStyle, rPrevCharIsSpace, openFieldMark );
    }
}
 
void XMLTextParagraphExport::exportCharacterData(const OUString& rText,
                                           bool& rPrevCharIsSpace )
{
    sal_Int32 nExpStartPos = 0;
    sal_Int32 nEndPos = rText.getLength();
    sal_Int32 nSpaceChars = 0;
    for( sal_Int32 nPos = 0; nPos < nEndPos; nPos++ )
    {
        sal_Unicode cChar = rText[nPos];
        bool bExpCharAsText = true;
        bool bExpCharAsElement = false;
        bool bCurrCharIsSpace = false;
        switch( cChar )
        {
        case 0x0009:    // Tab
        case 0x000A:    // LF
            // These characters are exported as text.
            bExpCharAsElement = true;
            bExpCharAsText = false;
            break;
        case 0x000D:
            break;  // legal character
        case 0x0020:    // Blank
            if( rPrevCharIsSpace )
            {
                // If the previous character is a space character,
                // too, export a special space element.
                bExpCharAsText = false;
            }
            bCurrCharIsSpace = true;
            break;
        default:
            if( cChar < 0x0020 )
            {
#ifdef DBG_UTIL
                OSL_ENSURE( txtparae_bContainsIllegalCharacters ||
                            cChar >= 0x0020,
                            "illegal character in text content" );
                txtparae_bContainsIllegalCharacters = true;
#endif
                bExpCharAsText = false;
            }
            break;
        }
 
        // If the current character is not exported as text
           // the text that has not been exported by now has to be exported now.
        if( nPos > nExpStartPos && !bExpCharAsText )
        {
            SAL_WARN_IF( 0 != nSpaceChars, "xmloff", "pending spaces" );
            OUString sExp( rText.copy( nExpStartPos, nPos - nExpStartPos ) );
            GetExport().Characters( sExp );
            nExpStartPos = nPos;
        }
 
        // If there are spaces left that have not been exported and the
        // current character is not a space , the pending spaces have to be
        // exported now.
        if( nSpaceChars > 0 && !bCurrCharIsSpace )
        {
            SAL_WARN_IF( nExpStartPos != nPos, "xmloff", " pending characters" );
 
            if( nSpaceChars > 1 )
            {
                GetExport().AddAttribute( XML_NAMESPACE_TEXT, XML_C,
                              OUString::number(nSpaceChars) );
            }
 
            SvXMLElementExport aElem( GetExport(), XML_NAMESPACE_TEXT,
                                      XML_S, false, false );
 
            nSpaceChars = 0;
        }
 
        // If the current character has to be exported as a special
        // element, the element will be exported now.
        if( bExpCharAsElement )
        {
            switch( cChar )
            {
            case 0x0009:    // Tab
                {
                    SvXMLElementExport aElem( GetExport(), XML_NAMESPACE_TEXT,
                                              XML_TAB, false,
                                              false );
                }
                break;
            case 0x000A:    // LF
                {
                    SvXMLElementExport aElem( GetExport(), XML_NAMESPACE_TEXT,
                                              XML_LINE_BREAK, false,
                                              false );
                }
                break;
            }
        }
 
        // If the current character is a space, and the previous one
        // is a space, too, the number of pending spaces is incremented
        // only.
        if( bCurrCharIsSpace && rPrevCharIsSpace )
            nSpaceChars++;
        rPrevCharIsSpace = bCurrCharIsSpace;
 
        // If the current character is not exported as text, the start
        // position for text is the position behind the current position.
        if( !bExpCharAsText )
        {
            SAL_WARN_IF( nExpStartPos != nPos, "xmloff", "wrong export start pos" );
            nExpStartPos = nPos+1;
        }
    }
 
    if( nExpStartPos < nEndPos )
    {
        SAL_WARN_IF( 0 != nSpaceChars, "xmloff", " pending spaces " );
        OUString sExp( rText.copy( nExpStartPos, nEndPos - nExpStartPos ) );
        GetExport().Characters( sExp );
    }
 
    // If there are some spaces left, they have to be exported now.
    if( nSpaceChars > 0 )
    {
        if( nSpaceChars > 1 )
        {
            GetExport().AddAttribute( XML_NAMESPACE_TEXT, XML_C,
                          OUString::number(nSpaceChars) );
        }
 
        SvXMLElementExport aElem( GetExport(), XML_NAMESPACE_TEXT, XML_S,
                                  false, false );
    }
}
 
void XMLTextParagraphExport::exportTextDeclarations()
{
    m_pFieldExport->ExportFieldDeclarations();
 
    // get XPropertySet from the document and ask for AutoMarkFileURL.
    // If it exists, export the auto-mark-file element.
    Reference<XPropertySet> xPropertySet( GetExport().GetModel(), UNO_QUERY );
    if (!xPropertySet.is())
        return;
 
    OUString sUrl;
    OUString sIndexAutoMarkFileURL(
        u"IndexAutoMarkFileURL"_ustr);
    if (!xPropertySet->getPropertySetInfo()->hasPropertyByName(
        sIndexAutoMarkFileURL))
        return;
 
    xPropertySet->getPropertyValue(sIndexAutoMarkFileURL) >>= sUrl;
    if (!sUrl.isEmpty())
    {
        GetExport().AddAttribute( XML_NAMESPACE_XLINK, XML_HREF,
                                  GetExport().GetRelativeReference(sUrl) );
        SvXMLElementExport aAutoMarkElement(
            GetExport(), XML_NAMESPACE_TEXT,
            XML_ALPHABETICAL_INDEX_AUTO_MARK_FILE,
            true, true );
    }
}
 
void XMLTextParagraphExport::exportTextDeclarations(
    const Reference<XText> & rText )
{
    m_pFieldExport->ExportFieldDeclarations(rText);
}
 
void XMLTextParagraphExport::exportUsedDeclarations()
{
    m_pFieldExport->SetExportOnlyUsedFieldDeclarations( false/*bOnlyUsed*/ );
}
 
void XMLTextParagraphExport::exportTrackedChanges(bool bAutoStyles)
{
    if (nullptr != m_pRedlineExport)
        m_pRedlineExport->ExportChangesList( bAutoStyles );
}
 
void XMLTextParagraphExport::exportTrackedChanges(
    const Reference<XText> & rText,
    bool bAutoStyle)
{
    if (nullptr != m_pRedlineExport)
        m_pRedlineExport->ExportChangesList(rText, bAutoStyle);
}
 
void XMLTextParagraphExport::recordTrackedChangesForXText(
    const Reference<XText> & rText )
{
    if (nullptr != m_pRedlineExport)
        m_pRedlineExport->SetCurrentXText(rText);
}
 
void XMLTextParagraphExport::recordTrackedChangesNoXText()
{
    if (nullptr != m_pRedlineExport)
        m_pRedlineExport->SetCurrentXText();
}
 
void XMLTextParagraphExport::exportTableAutoStyles() {}
 
void XMLTextParagraphExport::exportTextAutoStyles()
{
    // tdf#135942: do not collect styles during their export: this may modify iterated containers
    mbCollected = true;
    exportTableAutoStyles();
 
    GetAutoStylePool().exportXML( XmlStyleFamily::TEXT_PARAGRAPH );
 
    GetAutoStylePool().exportXML( XmlStyleFamily::TEXT_TEXT );
 
    GetAutoStylePool().exportXML( XmlStyleFamily::TEXT_FRAME );
 
    GetAutoStylePool().exportXML( XmlStyleFamily::TEXT_SECTION );
 
    GetAutoStylePool().exportXML( XmlStyleFamily::TEXT_RUBY );
 
    maListAutoPool.exportXML();
}
 
void XMLTextParagraphExport::exportRuby(
    const Reference<XPropertySet> & rPropSet,
    bool bAutoStyles )
{
    // early out: a collapsed ruby makes no sense
    if (*o3tl::doAccess<bool>(rPropSet->getPropertyValue(gsIsCollapsed)))
        return;
 
    // start value ?
    bool bStart = *o3tl::doAccess<bool>(rPropSet->getPropertyValue(gsIsStart));
 
    if (bAutoStyles)
    {
        // ruby auto styles
        if (bStart)
            Add( XmlStyleFamily::TEXT_RUBY, rPropSet );
    }
    else
    {
        if (bStart)
        {
            // ruby start
 
            // we can only start a ruby if none is open
            assert(!m_bOpenRuby && "Can't open a ruby inside of ruby!");
            if( m_bOpenRuby )
                return;
 
            // save ruby text + ruby char style
            rPropSet->getPropertyValue(gsRubyText) >>= m_sOpenRubyText;
            rPropSet->getPropertyValue(gsRubyCharStyleName) >>= m_sOpenRubyCharStyle;
 
            // ruby style
            GetExport().CheckAttrList();
            OUString sStyleName(Find(XmlStyleFamily::TEXT_RUBY, rPropSet, u""_ustr));
            SAL_WARN_IF(sStyleName.isEmpty(), "xmloff", "Can't find ruby style!");
            GetExport().AddAttribute(XML_NAMESPACE_TEXT,
                                     XML_STYLE_NAME, sStyleName);
 
            // export <text:ruby> and <text:ruby-base> start elements
            GetExport().StartElement( XML_NAMESPACE_TEXT, XML_RUBY, false);
            GetExport().ClearAttrList();
            GetExport().StartElement( XML_NAMESPACE_TEXT, XML_RUBY_BASE,
                                      false );
            m_bOpenRuby = true;
        }
        else
        {
            // ruby end
 
            // check for an open ruby
            assert(m_bOpenRuby && "Can't close a ruby if none is open!");
            if( !m_bOpenRuby )
                return;
 
            // close <text:ruby-base>
            GetExport().EndElement(XML_NAMESPACE_TEXT, XML_RUBY_BASE,
                                   false);
 
            // write the ruby text (with char style)
            {
                if (!m_sOpenRubyCharStyle.isEmpty())
                    GetExport().AddAttribute(
                        XML_NAMESPACE_TEXT, XML_STYLE_NAME,
                        GetExport().EncodeStyleName( m_sOpenRubyCharStyle) );
 
                SvXMLElementExport aRubyElement(
                    GetExport(), XML_NAMESPACE_TEXT, XML_RUBY_TEXT,
                    false, false);
 
                GetExport().Characters(m_sOpenRubyText);
            }
 
            // and finally, close the ruby
            GetExport().EndElement(XML_NAMESPACE_TEXT, XML_RUBY, false);
            m_bOpenRuby = false;
        }
    }
}
 
void XMLTextParagraphExport::exportMeta(
    const Reference<XPropertySet> & i_xPortion,
    bool i_bAutoStyles, bool i_isProgress, bool & rPrevCharIsSpace)
{
    bool doExport(!i_bAutoStyles); // do not export element if autostyles
    // check version >= 1.2
    switch (GetExport().getSaneDefaultVersion()) {
        case SvtSaveOptions::ODFSVER_011: // fall through
        case SvtSaveOptions::ODFSVER_010: doExport = false; break;
        default: break;
    }
 
    const Reference< XTextContent > xTextContent(
            i_xPortion->getPropertyValue(u"InContentMetadata"_ustr), UNO_QUERY_THROW);
    const Reference< XEnumerationAccess > xEA( xTextContent, UNO_QUERY_THROW );
    const Reference< XEnumeration > xTextEnum( xEA->createEnumeration() );
 
    if (doExport)
    {
        const Reference<rdf::XMetadatable> xMeta(xTextContent, UNO_QUERY_THROW);
 
        // text:meta with neither xml:id nor RDFa is invalid
        xMeta->ensureMetadataReference();
 
        // xml:id and RDFa for RDF metadata
        GetExport().AddAttributeXmlId(xMeta);
        GetExport().AddAttributesRDFa(xTextContent);
    }
 
    SvXMLElementExport aElem( GetExport(), doExport,
        XML_NAMESPACE_TEXT, XML_META, false, false );
 
    // recurse to export content
    exportTextRangeEnumeration(xTextEnum, i_bAutoStyles, i_isProgress, rPrevCharIsSpace);
}
 
void XMLTextParagraphExport::ExportContentControl(
    const uno::Reference<beans::XPropertySet>& xPortion, bool bAutoStyles, bool isProgress,
    bool& rPrevCharIsSpace)
{
    // Do not export the element in the autostyle case.
    bool bExport = !bAutoStyles;
    if (!(GetExport().getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED))
    {
        bExport = false;
    }
 
    uno::Reference<text::XTextContent> xTextContent(xPortion->getPropertyValue(u"ContentControl"_ustr),
                                                    uno::UNO_QUERY_THROW);
    uno::Reference<container::XEnumerationAccess> xEA(xTextContent, uno::UNO_QUERY_THROW);
    uno::Reference<container::XEnumeration> xTextEnum = xEA->createEnumeration();
 
    uno::Reference<beans::XPropertySet> xPropertySet(xTextContent, uno::UNO_QUERY_THROW);
    if (bExport)
    {
        bool bShowingPlaceHolder = false;
        xPropertySet->getPropertyValue(u"ShowingPlaceHolder"_ustr) >>= bShowingPlaceHolder;
        if (bShowingPlaceHolder)
        {
            OUStringBuffer aBuffer;
            sax::Converter::convertBool(aBuffer, bShowingPlaceHolder);
            GetExport().AddAttribute(XML_NAMESPACE_LO_EXT, XML_SHOWING_PLACE_HOLDER,
                                     aBuffer.makeStringAndClear());
        }
 
        bool bCheckbox = false;
        xPropertySet->getPropertyValue(u"Checkbox"_ustr) >>= bCheckbox;
        if (bCheckbox)
        {
            OUStringBuffer aBuffer;
            sax::Converter::convertBool(aBuffer, bCheckbox);
            GetExport().AddAttribute(XML_NAMESPACE_LO_EXT, XML_CHECKBOX, aBuffer.makeStringAndClear());
        }
 
        bool bChecked = false;
        xPropertySet->getPropertyValue(u"Checked"_ustr) >>= bChecked;
        if (bChecked)
        {
            OUStringBuffer aBuffer;
            sax::Converter::convertBool(aBuffer, bChecked);
            GetExport().AddAttribute(XML_NAMESPACE_LO_EXT, XML_CHECKED, aBuffer.makeStringAndClear());
        }
 
        OUString aCheckedState;
        xPropertySet->getPropertyValue(u"CheckedState"_ustr) >>= aCheckedState;
        if (!aCheckedState.isEmpty())
        {
            GetExport().AddAttribute(XML_NAMESPACE_LO_EXT, XML_CHECKED_STATE, aCheckedState);
        }
 
        OUString aUncheckedState;
        xPropertySet->getPropertyValue(u"UncheckedState"_ustr) >>= aUncheckedState;
        if (!aUncheckedState.isEmpty())
        {
            GetExport().AddAttribute(XML_NAMESPACE_LO_EXT, XML_UNCHECKED_STATE, aUncheckedState);
        }
 
        bool bPicture = false;
        xPropertySet->getPropertyValue(u"Picture"_ustr) >>= bPicture;
        if (bPicture)
        {
            OUStringBuffer aBuffer;
            sax::Converter::convertBool(aBuffer, bPicture);
            GetExport().AddAttribute(XML_NAMESPACE_LO_EXT, XML_PICTURE,
                                     aBuffer.makeStringAndClear());
        }
 
        bool bDate = false;
        xPropertySet->getPropertyValue(u"Date"_ustr) >>= bDate;
        if (bDate)
        {
            OUStringBuffer aBuffer;
            sax::Converter::convertBool(aBuffer, bDate);
            GetExport().AddAttribute(XML_NAMESPACE_LO_EXT, XML_DATE, aBuffer.makeStringAndClear());
        }
 
        OUString aDateFormat;
        xPropertySet->getPropertyValue(u"DateFormat"_ustr) >>= aDateFormat;
        if (!aDateFormat.isEmpty())
        {
            GetExport().AddAttribute(XML_NAMESPACE_LO_EXT, XML_DATE_FORMAT, aDateFormat);
        }
 
        OUString aDateLanguage;
        xPropertySet->getPropertyValue(u"DateLanguage"_ustr) >>= aDateLanguage;
        if (!aDateLanguage.isEmpty())
        {
            GetExport().AddAttribute(XML_NAMESPACE_LO_EXT, XML_DATE_RFC_LANGUAGE_TAG, aDateLanguage);
        }
        OUString aCurrentDate;
        xPropertySet->getPropertyValue(u"CurrentDate"_ustr) >>= aCurrentDate;
        if (!aCurrentDate.isEmpty())
        {
            GetExport().AddAttribute(XML_NAMESPACE_LO_EXT, XML_CURRENT_DATE, aCurrentDate);
        }
 
        bool bPlainText = false;
        xPropertySet->getPropertyValue(u"PlainText"_ustr) >>= bPlainText;
        if (bPlainText)
        {
            OUStringBuffer aBuffer;
            sax::Converter::convertBool(aBuffer, bPlainText);
            GetExport().AddAttribute(XML_NAMESPACE_LO_EXT, XML_PLAIN_TEXT, aBuffer.makeStringAndClear());
        }
 
        bool bComboBox = false;
        xPropertySet->getPropertyValue(u"ComboBox"_ustr) >>= bComboBox;
        if (bComboBox)
        {
            OUStringBuffer aBuffer;
            sax::Converter::convertBool(aBuffer, bComboBox);
            GetExport().AddAttribute(XML_NAMESPACE_LO_EXT, XML_COMBOBOX, aBuffer.makeStringAndClear());
        }
 
        bool bDropDown = false;
        xPropertySet->getPropertyValue(u"DropDown"_ustr) >>= bDropDown;
        if (bDropDown)
        {
            OUStringBuffer aBuffer;
            sax::Converter::convertBool(aBuffer, bDropDown);
            GetExport().AddAttribute(XML_NAMESPACE_LO_EXT, XML_DROPDOWN, aBuffer.makeStringAndClear());
        }
 
        OUString aAlias;
        xPropertySet->getPropertyValue(u"Alias"_ustr) >>= aAlias;
        if (!aAlias.isEmpty())
        {
            GetExport().AddAttribute(XML_NAMESPACE_LO_EXT, XML_ALIAS, aAlias);
        }
 
        OUString aTag;
        xPropertySet->getPropertyValue(u"Tag"_ustr) >>= aTag;
        if (!aTag.isEmpty())
        {
            GetExport().AddAttribute(XML_NAMESPACE_LO_EXT, XML_TAG, aTag);
        }
 
        sal_Int32 nId = 0;
        xPropertySet->getPropertyValue(u"Id"_ustr) >>= nId;
        if (nId)
        {
            GetExport().AddAttribute(XML_NAMESPACE_LO_EXT, XML_ID, OUString::number(nId));
        }
 
        sal_uInt32 nTabIndex = 0;
        if ((xPropertySet->getPropertyValue(u"TabIndex"_ustr) >>= nTabIndex) && nTabIndex)
        {
            GetExport().AddAttribute(XML_NAMESPACE_LO_EXT, XML_TAB_INDEX,
                                     OUString::number(nTabIndex));
        }
 
        OUString aLock;
        xPropertySet->getPropertyValue(u"Lock"_ustr) >>= aLock;
        if (!aLock.isEmpty())
        {
            GetExport().AddAttribute(XML_NAMESPACE_LO_EXT, XML_LOCK, aLock);
        }
    }
 
    SvXMLElementExport aElem(GetExport(), bExport, XML_NAMESPACE_LO_EXT, XML_CONTENT_CONTROL, false,
                             false);
 
    if (bExport)
    {
        // Export list items of dropdowns.
        uno::Sequence<beans::PropertyValues> aListItems;
        xPropertySet->getPropertyValue(u"ListItems"_ustr) >>= aListItems;
        for (const auto& rListItem : aListItems)
        {
            comphelper::SequenceAsHashMap aMap(rListItem);
            auto it = aMap.find(u"DisplayText"_ustr);
            OUString aValue;
            if (it != aMap.end() && (it->second >>= aValue) && !aValue.isEmpty())
            {
                GetExport().AddAttribute(XML_NAMESPACE_LO_EXT, XML_DISPLAY_TEXT, aValue);
            }
 
            it = aMap.find(u"Value"_ustr);
            if (it != aMap.end() && (it->second >>= aValue))
            {
                GetExport().AddAttribute(XML_NAMESPACE_LO_EXT, XML_VALUE, aValue);
            }
 
            SvXMLElementExport aItem(GetExport(), bExport, XML_NAMESPACE_LO_EXT, XML_LIST_ITEM, false,
                    false);
        }
    }
 
    // Recurse to export content.
    exportTextRangeEnumeration(xTextEnum, bAutoStyles, isProgress, rPrevCharIsSpace);
}
 
void XMLTextParagraphExport::PreventExportOfControlsInMuteSections(
    const Reference<XIndexAccess> & rShapes,
    const rtl::Reference<xmloff::OFormLayerXMLExport>& xFormExport   )
{
    // check parameters ad pre-conditions
    if( ( ! rShapes.is() ) || ( ! xFormExport.is() ) )
    {
        // if we don't have shapes or a form export, there's nothing to do
        return;
    }
    SAL_WARN_IF( m_pSectionExport == nullptr, "xmloff", "We need the section export." );
 
    Reference<XEnumeration> xShapesEnum = m_pBoundFrameSets->GetShapes()->createEnumeration();
    if(!xShapesEnum.is())
        return;
    while( xShapesEnum->hasMoreElements() )
    {
        // now we need to check
        // 1) if this is a control shape, and
        // 2) if it's in a mute section
        // if both answers are 'yes', notify the form layer export
 
        // we join accessing the shape and testing for control
        Reference<XControlShape> xControlShape(xShapesEnum->nextElement(), UNO_QUERY);
        if( xControlShape.is() )
        {
            //            Reference<XPropertySet> xPropSet( xControlShape, UNO_QUERY );
            //            Reference<XTextContent> xTextContent;
            //            xPropSet->getPropertyValue("TextRange") >>= xTextContent;
 
            Reference<XTextContent> xTextContent( xControlShape, UNO_QUERY );
            if( xTextContent.is() )
            {
                if( m_pSectionExport->IsMuteSection( xTextContent, false ) )
                {
                    // Ah, we've found a shape that
                    // 1) is a control shape
                    // 2) is anchored in a mute section
                    // so: don't export it!
                    xFormExport->excludeFromExport(
                        xControlShape->getControl() );
                }
                // else: not in mute section -> should be exported -> nothing
                // to do
            }
            // else: no anchor -> ignore
        }
        // else: no control shape -> nothing to do
    }
}
 
void XMLTextParagraphExport::PushNewTextListsHelper()
{
    maTextListsHelperStack.emplace_back( new XMLTextListsHelper() );
    mpTextListsHelper = maTextListsHelperStack.back().get();
}
 
void XMLTextParagraphExport::PopTextListsHelper()
{
    mpTextListsHelper = nullptr;
    maTextListsHelperStack.pop_back();
    if ( !maTextListsHelperStack.empty() )
    {
        mpTextListsHelper = maTextListsHelperStack.back().get();
    }
}
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

V530 The return value of function 'Add' is required to be utilized.

V530 The return value of function 'Add' is required to be utilized.

V530 The return value of function 'Add' is required to be utilized.

V530 The return value of function 'Add' is required to be utilized.

V530 The return value of function 'Add' is required to be utilized.

V547 Expression '0 < nOutlineLevel' is always false.

V547 Expression '0 < nOutlineLevel' is always false.

V547 Expression 'TextContentAnchorType_AT_PAGE == eAnchor' is always false.

V547 Expression 'HoriOrientation::NONE == nHoriOrient' is always true.

V547 Expression is always false.

V547 Expression 'VertOrientation::NONE == nVertOrient' is always true.

V547 Expression 'SizeType::VARIABLE != nWidthType' is always true.

V547 Expression 'SizeType::FIX != nWidthType' is always false.

V547 Expression 'nRelWidth > 0' is always false.

V547 Expression 'SizeType::VARIABLE != nSizeType' is always true.

V547 Expression 'SizeType::MIN == nSizeType' is always false.

V547 Expression 'nRelHeight > 0' is always false.

V547 Expression '- 1 != nZIndex' is always true.

V547 Expression '0 != nRotation' is always false.

V614 Uninitialized variable 'eAnchor' used.

V614 Uninitialized variable 'eAnchor' used.

V547 Expression 'bHidden' is always false.

V547 Expression 'bShape' is always false.

V547 Expression 'bShowingPlaceHolder' is always false.

V547 Expression 'bCheckbox' is always false.

V547 Expression 'bChecked' is always false.

V547 Expression 'bPicture' is always false.

V547 Expression 'bDate' is always false.

V547 Expression 'bPlainText' is always false.

V547 Expression 'bComboBox' is always false.

V547 Expression 'bDropDown' is always false.

V547 Expression 'nId' is always false.

V560 A part of conditional expression is always true.

V560 A part of conditional expression is always false.

V560 A part of conditional expression is always false: nRelHeight > 0.

V560 A part of conditional expression is always false: nRelWidth > 0.

V560 A part of conditional expression is always false: nRelWidth > 0.

V560 A part of conditional expression is always false: nRelWidth < 0.

V560 A part of conditional expression is always false: nRelWidth > 254.

V560 A part of conditional expression is always false: SizeType::FIX != nSizeType.

V560 A part of conditional expression is always true: 0 == nRelHeight.

V560 A part of conditional expression is always false: nRelHeight > 0.

V788 The variable 'index', captured in a lambda expression, has a constant value.

V1023 A pointer without owner is added to the 'maTextListsHelperStack' container by the 'emplace_back' method. A memory leak will occur in case of an exception.