sw/source/filter/ww8/docxattributeoutput.cxx |  755 +++++++++++----------------
 sw/source/filter/ww8/docxattributeoutput.hxx |   73 +-
 2 files changed, 352 insertions(+), 476 deletions(-)

New commits:
commit 5f3af56b2c0ef6c628a7cfe5ce6e86f8e1765f5f
Author:     Vasily Melenchuk <vasily.melenc...@cib.de>
AuthorDate: Tue Nov 16 18:12:39 2021 +0300
Commit:     Vasily Melenchuk <vasily.melenc...@cib.de>
CommitDate: Mon Nov 29 11:42:51 2021 +0100

    sw: refactoring for docx sdt blocks output
    
    Previous approach with almost duplicate storage and processing
    for paragraph-level and run-level sdt blocks was very bulky.
    Especially this became visible once supported std elements list
    start to grow.
    
    Paragraph-level sdt block and run-level sdt block data are almost
    identical, so they can be kept in one standard class
    (SdtBlockHelper) and use the same methods for collecting data
    from grabbags and writing to output without passing huge amount
    of parameters.
    
    Change-Id: I4e1183eddf20317e954cb8aca72e97a4fc45ac68
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/125372
    Tested-by: Jenkins
    Reviewed-by: Michael Stahl <michael.st...@allotropia.de>
    Reviewed-by: Vasily Melenchuk <vasily.melenc...@cib.de>

diff --git a/sw/source/filter/ww8/docxattributeoutput.cxx 
b/sw/source/filter/ww8/docxattributeoutput.cxx
index 4ddb54a517f5..6f531bef6cd6 100644
--- a/sw/source/filter/ww8/docxattributeoutput.cxx
+++ b/sw/source/filter/ww8/docxattributeoutput.cxx
@@ -318,6 +318,28 @@ static bool lcl_isOnelinerSdt(std::u16string_view rName)
     return rName == u"Title" || rName == u"Subtitle" || rName == u"Company";
 }
 
+static void AddToAttrList(rtl::Reference<sax_fastparser::FastAttributeList>& 
pAttrList, sal_Int32 nAttrs, ...)
+{
+    if (!pAttrList.is())
+        pAttrList = FastSerializerHelper::createAttrList();
+
+    va_list args;
+    va_start(args, nAttrs);
+    for (sal_Int32 i = 0; i < nAttrs; i++)
+    {
+        sal_Int32 nName = va_arg(args, sal_Int32);
+        const char* pValue = va_arg(args, const char*);
+        if (pValue)
+            pAttrList->add(nName, pValue);
+    }
+    va_end(args);
+}
+
+static void AddToAttrList(rtl::Reference<sax_fastparser::FastAttributeList>& 
pAttrList, sal_Int32 nAttrName, const char* sAttrValue)
+{
+    AddToAttrList(pAttrList, 1, nAttrName, sAttrValue);
+}
+
 // write a floating table directly to docx without the surrounding frame
 void DocxAttributeOutput::WriteFloatingTable(ww8::Frame const* pParentFrame)
 {
@@ -461,7 +483,7 @@ sal_Int32 
DocxAttributeOutput::StartParagraph(ww8::WW8TableNodeInfo::Pointer_t p
     // would normally arrive, it would be too late (would be after the
     // paragraph start has been written).
     bool bEndParaSdt = false;
-    if (m_bStartedParaSdt)
+    if (m_aParagraphSdt.m_bStartedSdt)
     {
         SwTextNode* pTextNode = m_rExport.m_pCurPam->GetNode().GetTextNode();
         if (pTextNode && pTextNode->GetpSwAttrSet())
@@ -471,17 +493,16 @@ sal_Int32 
DocxAttributeOutput::StartParagraph(ww8::WW8TableNodeInfo::Pointer_t p
             {
                 const SfxGrabBagItem& rParaGrabBag = static_cast<const 
SfxGrabBagItem&>(*pItem);
                 const std::map<OUString, css::uno::Any>& rMap = 
rParaGrabBag.GetGrabBag();
-                bEndParaSdt = m_bStartedParaSdt && 
rMap.find("ParaSdtEndBefore") != rMap.end();
+                bEndParaSdt = m_aParagraphSdt.m_bStartedSdt && 
rMap.find("ParaSdtEndBefore") != rMap.end();
             }
         }
     }
     // TODO also avoid multiline paragraphs in those SDT types for shape text
-    bool bOneliner = m_bStartedParaSdt && 
!m_rExport.SdrExporter().IsDMLAndVMLDrawingOpen() && 
lcl_isOnelinerSdt(m_aStartedParagraphSdtPrAlias);
-    if (bEndParaSdt || (m_bStartedParaSdt && m_bHadSectPr) || bOneliner)
+    bool bOneliner = m_aParagraphSdt.m_bStartedSdt && 
!m_rExport.SdrExporter().IsDMLAndVMLDrawingOpen() && 
lcl_isOnelinerSdt(m_aStartedParagraphSdtPrAlias);
+    if (bEndParaSdt || (m_aParagraphSdt.m_bStartedSdt && m_bHadSectPr) || 
bOneliner)
     {
         // This is the common case: "close sdt before the current paragraph" 
was requested by the next paragraph.
-        EndSdtBlock();
-        m_bStartedParaSdt = false;
+        m_aParagraphSdt.EndSdtBlock(m_pSerializer);
         m_aStartedParagraphSdtPrAlias.clear();
     }
     m_bHadSectPr = false;
@@ -583,24 +604,261 @@ static OString convertToOOXMLHoriOrientRel(sal_Int16 
nOrientRel)
     }
 }
 
-static void lcl_deleteAndResetTheLists(
-    rtl::Reference<sax_fastparser::FastAttributeList>& pSdtPrTokenChildren,
-    rtl::Reference<sax_fastparser::FastAttributeList>& pSdtPrDataBindingAttrs,
-    rtl::Reference<sax_fastparser::FastAttributeList>& pSdtPrTextAttrs,
-    OUString& rSdtPrAlias, OUString& rSdtPrPlaceholderDocPart, OUString& 
rColor)
+void SdtBlockHelper::DeleteAndResetTheLists()
 {
-    if( pSdtPrTokenChildren.is() )
-        pSdtPrTokenChildren.clear();
-    if( pSdtPrDataBindingAttrs.is() )
-        pSdtPrDataBindingAttrs.clear();
-    if (pSdtPrTextAttrs.is())
-        pSdtPrTextAttrs.clear();
-    if (!rSdtPrAlias.isEmpty())
-        rSdtPrAlias.clear();
-    if (!rSdtPrPlaceholderDocPart.isEmpty())
-        rSdtPrPlaceholderDocPart.clear();
-    if (!rColor.isEmpty())
-        rColor.clear();
+    if (m_pTokenChildren.is() )
+        m_pTokenChildren.clear();
+    if (m_pDataBindingAttrs.is() )
+        m_pDataBindingAttrs.clear();
+    if (m_pTextAttrs.is())
+        m_pTextAttrs.clear();
+    if (!m_aAlias.isEmpty())
+        m_aAlias.clear();
+    if (!m_aPlaceHolderDocPart.isEmpty())
+        m_aPlaceHolderDocPart.clear();
+    if (!m_aColor.isEmpty())
+        m_aColor.clear();
+    m_bHasId = false;
+}
+
+void SdtBlockHelper::WriteSdtBlock(::sax_fastparser::FSHelperPtr& pSerializer, 
bool bRunTextIsOn, bool bParagraphHasDrawing)
+{
+    if (m_nSdtPrToken <= 0 && !m_pDataBindingAttrs.is() && !m_bHasId)
+        return;
+
+    // sdt start mark
+    pSerializer->mark(Tag_WriteSdtBlock);
+
+    pSerializer->startElementNS(XML_w, XML_sdt);
+
+    // output sdt properties
+    pSerializer->startElementNS(XML_w, XML_sdtPr);
+
+    if (m_nSdtPrToken > 0 && m_pTokenChildren.is())
+    {
+        if (!m_pTokenAttributes.is())
+            pSerializer->startElement(m_nSdtPrToken);
+        else
+        {
+            rtl::Reference<FastAttributeList> xAttrList = 
std::move(m_pTokenAttributes);
+            pSerializer->startElement(m_nSdtPrToken, xAttrList);
+        }
+
+        if (m_nSdtPrToken == FSNS(XML_w, XML_date) || m_nSdtPrToken == 
FSNS(XML_w, XML_docPartObj) || m_nSdtPrToken == FSNS(XML_w, XML_docPartList) || 
m_nSdtPrToken == FSNS(XML_w14, XML_checkbox)) {
+            const uno::Sequence<xml::FastAttribute> aChildren = 
m_pTokenChildren->getFastAttributes();
+            for (const auto& rChild : aChildren)
+                pSerializer->singleElement(rChild.Token, FSNS(XML_w, XML_val), 
rChild.Value);
+        }
+
+        pSerializer->endElement(m_nSdtPrToken);
+    }
+    else if ((m_nSdtPrToken > 0) && m_nSdtPrToken != FSNS(XML_w, XML_id) && 
!(bRunTextIsOn && bParagraphHasDrawing))
+    {
+        if (!m_pTokenAttributes.is())
+            pSerializer->singleElement(m_nSdtPrToken);
+        else
+        {
+            rtl::Reference<FastAttributeList> xAttrList = 
std::move(m_pTokenAttributes);
+            pSerializer->singleElement(m_nSdtPrToken, xAttrList);
+        }
+    }
+
+    if (m_nSdtPrToken == FSNS(XML_w, XML_id) || m_bHasId)
+        //Word won't open a document with an empty id tag, we fill it with a 
random number
+        pSerializer->singleElementNS(XML_w, XML_id, FSNS(XML_w, XML_val),
+            OString::number(comphelper::rng::uniform_int_distribution(0, 
std::numeric_limits<int>::max())));
+
+    if (m_pDataBindingAttrs.is() && !bParagraphHasDrawing)
+    {
+        rtl::Reference<FastAttributeList> xAttrList = 
std::move(m_pDataBindingAttrs);
+        pSerializer->singleElementNS(XML_w, XML_dataBinding, xAttrList);
+    }
+
+    if (m_pTextAttrs.is())
+    {
+        rtl::Reference<FastAttributeList> xAttrList = std::move(m_pTextAttrs);
+        pSerializer->singleElementNS(XML_w, XML_text, xAttrList);
+    }
+
+    if (!m_aPlaceHolderDocPart.isEmpty())
+    {
+        pSerializer->startElementNS(XML_w, XML_placeholder);
+        pSerializer->singleElementNS(XML_w, XML_docPart, FSNS(XML_w, XML_val), 
m_aPlaceHolderDocPart);
+        pSerializer->endElementNS(XML_w, XML_placeholder);
+    }
+    if (!m_aColor.isEmpty())
+    {
+        pSerializer->singleElementNS(XML_w15, XML_color, FSNS(XML_w, XML_val), 
m_aColor);
+    }
+
+    if (!m_aAlias.isEmpty())
+        pSerializer->singleElementNS(XML_w, XML_alias, FSNS(XML_w, XML_val), 
m_aAlias);
+
+    pSerializer->endElementNS(XML_w, XML_sdtPr);
+
+    // sdt contents start tag
+    pSerializer->startElementNS(XML_w, XML_sdtContent);
+
+    // prepend the tags since the sdt start mark before the paragraph
+    pSerializer->mergeTopMarks(Tag_WriteSdtBlock, 
sax_fastparser::MergeMarks::PREPEND);
+
+    // write the ending tags after the paragraph
+    m_bStartedSdt = true;
+
+    // clear sdt status
+    m_nSdtPrToken = 0;
+    m_pTokenChildren.clear();
+    m_pDataBindingAttrs.clear();
+    m_pTextAttrs.clear();
+    m_aAlias.clear();
+    m_bHasId = false;
+}
+
+void SdtBlockHelper::EndSdtBlock(::sax_fastparser::FSHelperPtr& pSerializer)
+{
+    pSerializer->endElementNS(XML_w, XML_sdtContent);
+    pSerializer->endElementNS(XML_w, XML_sdt);
+    m_bStartedSdt = false;
+}
+
+void SdtBlockHelper::GetSdtParamsFromGrabBag(const 
uno::Sequence<beans::PropertyValue>& aGrabBagSdt)
+{
+    for (const beans::PropertyValue& aPropertyValue : aGrabBagSdt)
+    {
+        if (aPropertyValue.Name == "ooxml:CT_SdtPr_checkbox")
+        {
+            m_nSdtPrToken = FSNS(XML_w14, XML_checkbox);
+            uno::Sequence<beans::PropertyValue> aGrabBag;
+            aPropertyValue.Value >>= aGrabBag;
+            for (const auto& rProp : std::as_const(aGrabBag))
+            {
+                OUString sValue = rProp.Value.get<OUString>();
+                if (rProp.Name == "ooxml:CT_SdtCheckbox_checked")
+                    AddToAttrList(m_pTokenChildren,
+                        FSNS(XML_w14, XML_checked),
+                        OUStringToOString(sValue, 
RTL_TEXTENCODING_UTF8).getStr());
+                else if (rProp.Name == "ooxml:CT_SdtCheckbox_checkedState")
+                    AddToAttrList(m_pTokenChildren,
+                        FSNS(XML_w14, XML_checkedState),
+                        OUStringToOString(sValue, 
RTL_TEXTENCODING_UTF8).getStr());
+                else if (rProp.Name == "ooxml:CT_SdtCheckbox_uncheckedState")
+                    AddToAttrList(m_pTokenChildren,
+                        FSNS(XML_w14, XML_uncheckedState),
+                        OUStringToOString(sValue, 
RTL_TEXTENCODING_UTF8).getStr());
+            }
+        }
+        else if (aPropertyValue.Name == "ooxml:CT_SdtPr_dataBinding" && 
!m_pDataBindingAttrs.is())
+        {
+            uno::Sequence<beans::PropertyValue> aGrabBag;
+            aPropertyValue.Value >>= aGrabBag;
+            for (const auto& rProp : std::as_const(aGrabBag))
+            {
+                OUString sValue = rProp.Value.get<OUString>();
+                if (rProp.Name == "ooxml:CT_DataBinding_prefixMappings")
+                    AddToAttrList( m_pDataBindingAttrs,
+                                    FSNS( XML_w, XML_prefixMappings ),
+                                    OUStringToOString( sValue, 
RTL_TEXTENCODING_UTF8 ).getStr() );
+                else if (rProp.Name == "ooxml:CT_DataBinding_xpath")
+                    AddToAttrList( m_pDataBindingAttrs,
+                                    FSNS( XML_w, XML_xpath ),
+                                    OUStringToOString( sValue, 
RTL_TEXTENCODING_UTF8 ).getStr() );
+                else if (rProp.Name == "ooxml:CT_DataBinding_storeItemID")
+                    AddToAttrList( m_pDataBindingAttrs,
+                                    FSNS( XML_w, XML_storeItemID ),
+                                    OUStringToOString( sValue, 
RTL_TEXTENCODING_UTF8 ).getStr() );
+            }
+        }
+        else if (aPropertyValue.Name == "ooxml:CT_SdtPr_text")
+        {
+            uno::Sequence<beans::PropertyValue> aGrabBag;
+            aPropertyValue.Value >>= aGrabBag;
+            if (aGrabBag.hasElements())
+            {
+                for (const auto& rProp : std::as_const(aGrabBag))
+                {
+                    OUString sValue = rProp.Value.get<OUString>();
+                    if (rProp.Name == "ooxml:CT_SdtText_multiLine")
+                        AddToAttrList(m_pTextAttrs,
+                            FSNS(XML_w, XML_multiLine),
+                            OUStringToOString(sValue, 
RTL_TEXTENCODING_UTF8).getStr());
+                }
+            }
+            else
+            {
+                // We still have w:text, but no attrs
+                m_nSdtPrToken = FSNS(XML_w, XML_text);
+            }
+        }
+        else if (aPropertyValue.Name == "ooxml:CT_SdtPlaceholder_docPart")
+        {
+            uno::Sequence<beans::PropertyValue> aGrabBag;
+            aPropertyValue.Value >>= aGrabBag;
+            for (const auto& rProp : std::as_const(aGrabBag))
+            {
+                OUString sValue = rProp.Value.get<OUString>();
+                if (rProp.Name == "ooxml:CT_SdtPlaceholder_docPart_val")
+                    m_aPlaceHolderDocPart = sValue;
+            }
+        }
+        else if (aPropertyValue.Name == "ooxml:CT_SdtPr_color")
+        {
+            uno::Sequence<beans::PropertyValue> aGrabBag;
+            aPropertyValue.Value >>= aGrabBag;
+            for (const auto& rProp : std::as_const(aGrabBag))
+            {
+                OUString sValue = rProp.Value.get<OUString>();
+                if (rProp.Name == "ooxml:CT_SdtColor_val")
+                    m_aColor = sValue;
+            }
+        }
+        else if (aPropertyValue.Name == "ooxml:CT_SdtPr_alias" && 
m_aAlias.isEmpty())
+        {
+            if (!(aPropertyValue.Value >>= m_aAlias))
+                SAL_WARN("sw.ww8", "DocxAttributeOutput::GrabBag: unexpected 
sdt alias value");
+        }
+        else if (aPropertyValue.Name == "ooxml:CT_SdtPr_id")
+            m_bHasId = true;
+        else if (aPropertyValue.Name == "ooxml:CT_SdtPr_citation")
+            m_nSdtPrToken = FSNS(XML_w, XML_citation);
+        else if (aPropertyValue.Name == "ooxml:CT_SdtPr_docPartObj" ||
+            aPropertyValue.Name == "ooxml:CT_SdtPr_docPartList")
+        {
+            if (aPropertyValue.Name == "ooxml:CT_SdtPr_docPartObj")
+                m_nSdtPrToken = FSNS(XML_w, XML_docPartObj);
+            else if (aPropertyValue.Name == "ooxml:CT_SdtPr_docPartList")
+                m_nSdtPrToken = FSNS(XML_w, XML_docPartList);
+
+            uno::Sequence<beans::PropertyValue> aGrabBag;
+            aPropertyValue.Value >>= aGrabBag;
+            for (const auto& rProp : std::as_const(aGrabBag))
+            {
+                OUString sValue = rProp.Value.get<OUString>();
+                if (rProp.Name == "ooxml:CT_SdtDocPart_docPartGallery")
+                    AddToAttrList(m_pTokenChildren,
+                        FSNS(XML_w, XML_docPartGallery),
+                        OUStringToOString(sValue, 
RTL_TEXTENCODING_UTF8).getStr());
+                else if (rProp.Name == "ooxml:CT_SdtDocPart_docPartCategory")
+                    AddToAttrList(m_pTokenChildren,
+                        FSNS(XML_w, XML_docPartCategory),
+                        OUStringToOString(sValue, 
RTL_TEXTENCODING_UTF8).getStr());
+                else if (rProp.Name == "ooxml:CT_SdtDocPart_docPartUnique")
+                {
+                    if (sValue.isEmpty())
+                        sValue = "true";
+                    AddToAttrList(m_pTokenChildren, FSNS(XML_w, 
XML_docPartUnique),
+                        OUStringToOString(sValue, 
RTL_TEXTENCODING_UTF8).getStr());
+                }
+            }
+        }
+        else if (aPropertyValue.Name == "ooxml:CT_SdtPr_equation")
+            m_nSdtPrToken = FSNS(XML_w, XML_equation);
+        else if (aPropertyValue.Name == "ooxml:CT_SdtPr_picture")
+            m_nSdtPrToken = FSNS(XML_w, XML_picture);
+        else if (aPropertyValue.Name == "ooxml:CT_SdtPr_group")
+            m_nSdtPrToken = FSNS(XML_w, XML_group);
+        else
+            SAL_WARN("sw.ww8", "GetSdtParamsFromGrabBag unhandled SdtPr grab 
bag property " << aPropertyValue.Name);
+    }
 }
 
 void DocxAttributeOutput::PopulateFrameProperties(const SwFrameFormat* 
pFrameFormat, const Size& rSize)
@@ -681,7 +939,7 @@ void DocxAttributeOutput::EndParagraph( 
ww8::WW8TableNodeInfoInner::Pointer_t pT
     ++m_nTextFrameLevel;
     if( m_nTextFrameLevel == 1 && 
!m_rExport.SdrExporter().IsDMLAndVMLDrawingOpen() )
     {
-        comphelper::FlagRestorationGuard 
aStartedParaSdtGuard(m_bStartedParaSdt, false);
+        comphelper::FlagRestorationGuard 
aStartedParaSdtGuard(m_aParagraphSdt.m_bStartedSdt, false);
 
         assert(!m_pPostponedCustomShape);
         m_pPostponedCustomShape.reset(new std::vector<PostponedDrawing>);
@@ -696,11 +954,10 @@ void DocxAttributeOutput::EndParagraph( 
ww8::WW8TableNodeInfoInner::Pointer_t pT
 
             if (!TextBoxIsFramePr(rFrameFormat) || m_bWritingHeaderFooter)
             {
-                if (m_bStartedCharSdt)
+                if (m_aRunSdt.m_bStartedSdt)
                 {
                     // Run-level SDT still open? Close it before 
AlternateContent.
-                    EndSdtBlock();
-                    m_bStartedCharSdt = false;
+                    m_aRunSdt.EndSdtBlock(m_pSerializer);
                 }
                 m_pSerializer->startElementNS(XML_w, XML_r);
                 m_pSerializer->startElementNS(XML_mc, XML_AlternateContent);
@@ -790,11 +1047,10 @@ void DocxAttributeOutput::EndParagraph( 
ww8::WW8TableNodeInfoInner::Pointer_t pT
         m_nHyperLinkCount = 0;
     }
 
-    if (m_bStartedCharSdt)
+    if (m_aRunSdt.m_bStartedSdt)
     {
         // Run-level SDT still open? Close it now.
-        EndSdtBlock();
-        m_bStartedCharSdt = false;
+        m_aRunSdt.EndSdtBlock(m_pSerializer);
     }
 
     if (m_bPageBreakAfter)
@@ -806,21 +1062,25 @@ void DocxAttributeOutput::EndParagraph( 
ww8::WW8TableNodeInfoInner::Pointer_t pT
 
     m_pSerializer->endElementNS( XML_w, XML_p );
     // on export sdt blocks are never nested ATM
-    if( !m_bAnchorLinkedToNode && !m_bStartedParaSdt )
-        WriteSdtBlock(m_nParagraphSdtPrToken, m_pParagraphSdtPrTokenChildren,
-                      m_pParagraphSdtPrTokenAttributes, 
m_pParagraphSdtPrDataBindingAttrs,
-                      m_pParagraphSdtPrTextAttrs, m_aParagraphSdtPrAlias,
-                      m_aParagraphSdtPrPlaceHolderDocPart, 
m_aParagraphSdtPrColor, /*bPara=*/true);
+    if (!m_bAnchorLinkedToNode && !m_aParagraphSdt.m_bStartedSdt)
+    {
+        m_aParagraphSdt.WriteSdtBlock(m_pSerializer, m_bRunTextIsOn, 
m_rExport.SdrExporter().IsParagraphHasDrawing());
+
+        if (m_aParagraphSdt.m_bStartedSdt)
+        {
+            if (m_tableReference->m_bTableCellOpen)
+                m_tableReference->m_bTableCellParaSdtOpen = true;
+            if (m_rExport.SdrExporter().IsDMLAndVMLDrawingOpen())
+                m_rExport.SdrExporter().setParagraphSdtOpen(true);
+        }
+    }
     else
     {
         //These should be written out to the actual Node and not to the anchor.
         //Clear them as they will be repopulated when the node is processed.
-        m_nParagraphSdtPrToken = 0;
-        m_bParagraphSdtHasId = false;
-        lcl_deleteAndResetTheLists(m_pParagraphSdtPrTokenChildren,
-                                   m_pParagraphSdtPrDataBindingAttrs, 
m_pParagraphSdtPrTextAttrs,
-                                   m_aParagraphSdtPrAlias, 
m_aParagraphSdtPrPlaceHolderDocPart,
-                                   m_aParagraphSdtPrColor);
+        m_aParagraphSdt.m_nSdtPrToken = 0;
+        m_aParagraphSdt.m_bHasId = false;
+        m_aParagraphSdt.DeleteAndResetTheLists();
     }
 
     m_pSerializer->mark(Tag_StartParagraph_2);
@@ -853,123 +1113,6 @@ void DocxAttributeOutput::EndParagraph( 
ww8::WW8TableNodeInfoInner::Pointer_t pT
     m_aBookmarksOfParagraphEnd.clear();
 }
 
-void DocxAttributeOutput::WriteSdtBlock( sal_Int32& nSdtPrToken,
-                                         
rtl::Reference<sax_fastparser::FastAttributeList>& pSdtPrTokenChildren,
-                                         
rtl::Reference<sax_fastparser::FastAttributeList>& pSdtPrTokenAttributes,
-                                         
rtl::Reference<sax_fastparser::FastAttributeList>& pSdtPrDataBindingAttrs,
-                                         
rtl::Reference<sax_fastparser::FastAttributeList>& pSdtPrTextAttrs,
-                                         OUString& rSdtPrAlias,
-                                         OUString& rSdtPrPlaceholderDocPart,
-                                         OUString& rColor,
-                                         bool bPara )
-{
-    if( nSdtPrToken <= 0 && !pSdtPrDataBindingAttrs.is() && 
!m_bParagraphSdtHasId)
-        return;
-
-    // sdt start mark
-    m_pSerializer->mark(Tag_WriteSdtBlock);
-
-    m_pSerializer->startElementNS(XML_w, XML_sdt);
-
-    // output sdt properties
-    m_pSerializer->startElementNS(XML_w, XML_sdtPr);
-
-    if( nSdtPrToken > 0 && pSdtPrTokenChildren.is() )
-    {
-        if (!pSdtPrTokenAttributes.is())
-            m_pSerializer->startElement(nSdtPrToken);
-        else
-        {
-            rtl::Reference<FastAttributeList> xAttrList = 
std::move(pSdtPrTokenAttributes);
-            m_pSerializer->startElement(nSdtPrToken, xAttrList);
-        }
-
-        if (nSdtPrToken ==  FSNS( XML_w, XML_date ) || nSdtPrToken ==  FSNS( 
XML_w, XML_docPartObj ) || nSdtPrToken ==  FSNS( XML_w, XML_docPartList ) || 
nSdtPrToken ==  FSNS( XML_w14, XML_checkbox )) {
-            const uno::Sequence<xml::FastAttribute> aChildren = 
pSdtPrTokenChildren->getFastAttributes();
-            for( const auto& rChild : aChildren )
-                m_pSerializer->singleElement(rChild.Token, FSNS(XML_w, 
XML_val), rChild.Value);
-        }
-
-        m_pSerializer->endElement( nSdtPrToken );
-    }
-    else if( (nSdtPrToken > 0) && nSdtPrToken != FSNS( XML_w, XML_id ) && 
!(m_bRunTextIsOn && m_rExport.SdrExporter().IsParagraphHasDrawing()))
-    {
-        if (!pSdtPrTokenAttributes.is())
-            m_pSerializer->singleElement(nSdtPrToken);
-        else
-        {
-            rtl::Reference<FastAttributeList> xAttrList = 
std::move(pSdtPrTokenAttributes);
-            m_pSerializer->singleElement(nSdtPrToken, xAttrList);
-        }
-    }
-
-    if( nSdtPrToken == FSNS( XML_w, XML_id ) || ( bPara && 
m_bParagraphSdtHasId ) )
-        //Word won't open a document with an empty id tag, we fill it with a 
random number
-        m_pSerializer->singleElementNS(XML_w, XML_id, FSNS(XML_w, XML_val),
-                                      
OString::number(comphelper::rng::uniform_int_distribution(0, 
std::numeric_limits<int>::max())));
-
-    if( pSdtPrDataBindingAttrs.is() && 
!m_rExport.SdrExporter().IsParagraphHasDrawing())
-    {
-        rtl::Reference<FastAttributeList> xAttrList = std::move( 
pSdtPrDataBindingAttrs );
-        m_pSerializer->singleElementNS(XML_w, XML_dataBinding, xAttrList);
-    }
-
-    if (pSdtPrTextAttrs.is())
-    {
-        rtl::Reference<FastAttributeList> xAttrList = 
std::move(pSdtPrTextAttrs);
-        m_pSerializer->singleElementNS(XML_w, XML_text, xAttrList);
-    }
-
-    if (!rSdtPrPlaceholderDocPart.isEmpty())
-    {
-        m_pSerializer->startElementNS(XML_w, XML_placeholder);
-        m_pSerializer->singleElementNS(XML_w, XML_docPart, FSNS(XML_w, 
XML_val), rSdtPrPlaceholderDocPart);
-        m_pSerializer->endElementNS(XML_w, XML_placeholder);
-    }
-    if (!rColor.isEmpty())
-    {
-        m_pSerializer->singleElementNS(XML_w15, XML_color, FSNS(XML_w, 
XML_val), rColor);
-    }
-
-    if (!rSdtPrAlias.isEmpty())
-        m_pSerializer->singleElementNS(XML_w, XML_alias, FSNS(XML_w, XML_val), 
rSdtPrAlias);
-
-    m_pSerializer->endElementNS( XML_w, XML_sdtPr );
-
-    // sdt contents start tag
-    m_pSerializer->startElementNS(XML_w, XML_sdtContent);
-
-    // prepend the tags since the sdt start mark before the paragraph
-    m_pSerializer->mergeTopMarks(Tag_WriteSdtBlock, 
sax_fastparser::MergeMarks::PREPEND);
-
-    // write the ending tags after the paragraph
-    if (bPara)
-    {
-        m_bStartedParaSdt = true;
-        if (m_tableReference->m_bTableCellOpen)
-            m_tableReference->m_bTableCellParaSdtOpen = true;
-        if (m_rExport.SdrExporter().IsDMLAndVMLDrawingOpen())
-            m_rExport.SdrExporter().setParagraphSdtOpen(true);
-    }
-    else
-        // Support multiple runs inside a run-level SDT: don't close the SDT 
block yet.
-        m_bStartedCharSdt = true;
-
-    // clear sdt status
-    nSdtPrToken = 0;
-    pSdtPrTokenChildren.clear();
-    pSdtPrDataBindingAttrs.clear();
-    pSdtPrTextAttrs.clear();
-    rSdtPrAlias.clear();
-    m_bParagraphSdtHasId = false;
-}
-
-void DocxAttributeOutput::EndSdtBlock()
-{
-    m_pSerializer->endElementNS( XML_w, XML_sdtContent );
-    m_pSerializer->endElementNS( XML_w, XML_sdt );
-}
-
 #define MAX_CELL_IN_WORD 62
 
 void 
DocxAttributeOutput::SyncNodelessCells(ww8::WW8TableNodeInfoInner::Pointer_t 
const & pInner, sal_Int32 nCell, sal_uInt32 nRow)
@@ -1464,12 +1607,11 @@ void DocxAttributeOutput::EndRun(const SwTextNode* 
pNode, sal_Int32 nPos, bool /
 
         // if another sdt starts in this run, then wait
         // as closing the sdt now, might cause nesting of sdts
-        if (m_nRunSdtPrToken > 0)
+        if (m_aRunSdt.m_nSdtPrToken > 0)
             bCloseEarlierSDT = true;
         else
-            EndSdtBlock();
+            m_aRunSdt.EndSdtBlock(m_pSerializer);
         m_bEndCharSdt = false;
-        m_bStartedCharSdt = false;
     }
 
     if ( m_closeHyperlinkInPreviousRun )
@@ -1636,27 +1778,22 @@ void DocxAttributeOutput::EndRun(const SwTextNode* 
pNode, sal_Int32 nPos, bool /
 
     // enclose in a sdt block, if necessary: if one is already started, then 
don't do it for now
     // (so on export sdt blocks are never nested ATM)
-    if ( !m_bAnchorLinkedToNode && !m_bStartedCharSdt )
+    if ( !m_bAnchorLinkedToNode && !m_aRunSdt.m_bStartedSdt)
     {
-        rtl::Reference<sax_fastparser::FastAttributeList> 
pRunSdtPrTokenAttributes;
-        WriteSdtBlock(m_nRunSdtPrToken, m_pRunSdtPrTokenChildren, 
pRunSdtPrTokenAttributes,
-                      m_pRunSdtPrDataBindingAttrs, m_pRunSdtPrTextAttrs, 
m_aRunSdtPrAlias,
-                      m_aRunSdtPrPlaceHolderDocPart, m_aRunSdtPrColor, 
/*bPara=*/false);
+        m_aRunSdt.WriteSdtBlock(m_pSerializer, m_bRunTextIsOn, 
m_rExport.SdrExporter().IsParagraphHasDrawing());
     }
     else
     {
         //These should be written out to the actual Node and not to the anchor.
         //Clear them as they will be repopulated when the node is processed.
-        m_nRunSdtPrToken = 0;
-        lcl_deleteAndResetTheLists(m_pRunSdtPrTokenChildren, 
m_pRunSdtPrDataBindingAttrs,
-                                   m_pRunSdtPrTextAttrs, m_aRunSdtPrAlias,
-                                   m_aRunSdtPrPlaceHolderDocPart, 
m_aRunSdtPrColor);
+        m_aRunSdt.m_nSdtPrToken = 0;
+        m_aRunSdt.DeleteAndResetTheLists();
     }
 
     if (bCloseEarlierSDT)
     {
         m_pSerializer->mark(Tag_EndRun_2);
-        EndSdtBlock();
+        m_aRunSdt.EndSdtBlock(m_pSerializer);
         m_pSerializer->mergeTopMarks(Tag_EndRun_2, 
sax_fastparser::MergeMarks::PREPEND);
     }
 
@@ -2865,7 +3002,7 @@ void DocxAttributeOutput::GetSdtEndBefore(const 
SdrObject* pSdrObj)
 
     auto pProp = std::find_if(std::cbegin(aGrabBag), std::cend(aGrabBag),
         [this](const beans::PropertyValue& rProp) {
-            return "SdtEndBefore" == rProp.Name && m_bStartedCharSdt && 
!m_bEndCharSdt; });
+            return "SdtEndBefore" == rProp.Name && m_aRunSdt.m_bStartedSdt && 
!m_bEndCharSdt; });
     if (pProp != std::cend(aGrabBag))
         pProp->Value >>= m_bEndCharSdt;
 }
@@ -6426,8 +6563,8 @@ void 
DocxAttributeOutput::pushToTableExportContext(DocxTableExportContext& rCont
     rContext.m_nTableDepth = m_tableReference->m_nTableDepth;
     m_tableReference->m_nTableDepth = 0;
 
-    rContext.m_bStartedParaSdt = m_bStartedParaSdt;
-    m_bStartedParaSdt = false;
+    rContext.m_bStartedParaSdt = m_aParagraphSdt.m_bStartedSdt;
+    m_aParagraphSdt.m_bStartedSdt = false;
 }
 
 void DocxAttributeOutput::popFromTableExportContext(DocxTableExportContext 
const & rContext)
@@ -6435,7 +6572,7 @@ void 
DocxAttributeOutput::popFromTableExportContext(DocxTableExportContext const
     m_rExport.m_pTableInfo = rContext.m_pTableInfo;
     m_tableReference->m_bTableCellOpen = rContext.m_bTableCellOpen;
     m_tableReference->m_nTableDepth = rContext.m_nTableDepth;
-    m_bStartedParaSdt = rContext.m_bStartedParaSdt;
+    m_aParagraphSdt.m_bStartedSdt = rContext.m_bStartedParaSdt;
 }
 
 void DocxAttributeOutput::WriteTextBox(uno::Reference<drawing::XShape> xShape)
@@ -6828,11 +6965,10 @@ void DocxAttributeOutput::SectionBreak( sal_uInt8 nC, 
bool bBreakAfter, const WW
 
 void DocxAttributeOutput::EndParaSdtBlock()
 {
-    if (m_bStartedParaSdt)
+    if (m_aParagraphSdt.m_bStartedSdt)
     {
         // Paragraph-level SDT still open? Close it now.
-        EndSdtBlock();
-        m_bStartedParaSdt = false;
+        m_aParagraphSdt.EndSdtBlock(m_pSerializer);
     }
 }
 
@@ -9814,143 +9950,8 @@ void DocxAttributeOutput::ParaGrabBag(const 
SfxGrabBagItem& rItem)
         {
             const uno::Sequence<beans::PropertyValue> aGrabBagSdt =
                     rGrabBagElement.second.get< 
uno::Sequence<beans::PropertyValue> >();
-            for (const beans::PropertyValue& aPropertyValue : aGrabBagSdt)
-            {
-                if (aPropertyValue.Name == "ooxml:CT_SdtPr_docPartObj" ||
-                        aPropertyValue.Name == "ooxml:CT_SdtPr_docPartList")
-                {
-                    if (aPropertyValue.Name == "ooxml:CT_SdtPr_docPartObj")
-                        m_nParagraphSdtPrToken = FSNS( XML_w, XML_docPartObj );
-                    else if (aPropertyValue.Name == 
"ooxml:CT_SdtPr_docPartList")
-                        m_nParagraphSdtPrToken = FSNS( XML_w, XML_docPartList 
);
-
-                    uno::Sequence<beans::PropertyValue> aGrabBag;
-                    aPropertyValue.Value >>= aGrabBag;
-                    for (const auto& rProp : std::as_const(aGrabBag))
-                    {
-                        OUString sValue = rProp.Value.get<OUString>();
-                        if (rProp.Name == "ooxml:CT_SdtDocPart_docPartGallery")
-                            AddToAttrList( m_pParagraphSdtPrTokenChildren,
-                                           FSNS( XML_w, XML_docPartGallery ),
-                                           OUStringToOString( sValue, 
RTL_TEXTENCODING_UTF8 ).getStr() );
-                        else if (rProp.Name == 
"ooxml:CT_SdtDocPart_docPartCategory")
-                            AddToAttrList( m_pParagraphSdtPrTokenChildren,
-                                           FSNS( XML_w, XML_docPartCategory ),
-                                           OUStringToOString( sValue, 
RTL_TEXTENCODING_UTF8 ).getStr() );
-                        else if (rProp.Name == 
"ooxml:CT_SdtDocPart_docPartUnique")
-                        {
-                            if (sValue.isEmpty())
-                                sValue = "true";
-                            AddToAttrList( m_pParagraphSdtPrTokenChildren, 
FSNS( XML_w, XML_docPartUnique ),
-                            OUStringToOString( sValue, RTL_TEXTENCODING_UTF8 
).getStr() );
-                        }
-                    }
-                }
-                else if (aPropertyValue.Name == "ooxml:CT_SdtPr_equation")
-                    m_nParagraphSdtPrToken = FSNS( XML_w, XML_equation );
-                else if (aPropertyValue.Name == "ooxml:CT_SdtPr_picture")
-                    m_nParagraphSdtPrToken = FSNS( XML_w, XML_picture );
-                else if (aPropertyValue.Name == "ooxml:CT_SdtPr_citation")
-                    m_nParagraphSdtPrToken = FSNS( XML_w, XML_citation );
-                else if (aPropertyValue.Name == "ooxml:CT_SdtPr_group")
-                    m_nParagraphSdtPrToken = FSNS( XML_w, XML_group );
-                else if (aPropertyValue.Name == "ooxml:CT_SdtPr_text")
-                {
-                    uno::Sequence<beans::PropertyValue> aGrabBag;
-                    aPropertyValue.Value >>= aGrabBag;
-                    if (aGrabBag.hasElements())
-                    {
-                        for (const auto& rProp : std::as_const(aGrabBag))
-                        {
-                            OUString sValue = rProp.Value.get<OUString>();
-                            if (rProp.Name == "ooxml:CT_SdtText_multiLine")
-                                AddToAttrList(m_pParagraphSdtPrTextAttrs,
-                                    FSNS(XML_w, XML_multiLine),
-                                    OUStringToOString(sValue, 
RTL_TEXTENCODING_UTF8).getStr());
-                        }
-                    }
-                    else
-                    {
-                        // We still have w:text, but no attrs
-                        m_nParagraphSdtPrToken = FSNS(XML_w, XML_text);
-                    }
-                }
-                else if (aPropertyValue.Name == "ooxml:CT_SdtPr_dataBinding" 
&& !m_pParagraphSdtPrDataBindingAttrs.is())
-                {
-                    uno::Sequence<beans::PropertyValue> aGrabBag;
-                    aPropertyValue.Value >>= aGrabBag;
-                    for (const auto& rProp : std::as_const(aGrabBag))
-                    {
-                        OUString sValue = rProp.Value.get<OUString>();
-                        if (rProp.Name == 
"ooxml:CT_DataBinding_prefixMappings")
-                            AddToAttrList( m_pParagraphSdtPrDataBindingAttrs,
-                                           FSNS( XML_w, XML_prefixMappings ),
-                                           OUStringToOString( sValue, 
RTL_TEXTENCODING_UTF8 ).getStr() );
-                        else if (rProp.Name == "ooxml:CT_DataBinding_xpath")
-                            AddToAttrList( m_pParagraphSdtPrDataBindingAttrs,
-                                           FSNS( XML_w, XML_xpath ),
-                                           OUStringToOString( sValue, 
RTL_TEXTENCODING_UTF8 ).getStr() );
-                        else if (rProp.Name == 
"ooxml:CT_DataBinding_storeItemID")
-                            AddToAttrList( m_pParagraphSdtPrDataBindingAttrs,
-                                           FSNS( XML_w, XML_storeItemID ),
-                                           OUStringToOString( sValue, 
RTL_TEXTENCODING_UTF8 ).getStr() );
-                    }
-                }
-                else if (aPropertyValue.Name == 
"ooxml:CT_SdtPlaceholder_docPart")
-                {
-                    uno::Sequence<beans::PropertyValue> aGrabBag;
-                    aPropertyValue.Value >>= aGrabBag;
-                    for (const auto& rProp : std::as_const(aGrabBag))
-                    {
-                        OUString sValue = rProp.Value.get<OUString>();
-                        if (rProp.Name == 
"ooxml:CT_SdtPlaceholder_docPart_val")
-                            m_aParagraphSdtPrPlaceHolderDocPart = sValue;
-                    }
-                }
-                else if (aPropertyValue.Name == "ooxml:CT_SdtPr_color")
-                {
-                    uno::Sequence<beans::PropertyValue> aGrabBag;
-                    aPropertyValue.Value >>= aGrabBag;
-                    for (const auto& rProp : std::as_const(aGrabBag))
-                    {
-                        OUString sValue = rProp.Value.get<OUString>();
-                        if (rProp.Name == "ooxml:CT_SdtColor_val")
-                            m_aParagraphSdtPrColor = sValue;
-                    }
-                }
-                else if (aPropertyValue.Name == "ooxml:CT_SdtPr_alias" && 
m_aParagraphSdtPrAlias.isEmpty())
-                {
-                    if (!(aPropertyValue.Value >>= m_aParagraphSdtPrAlias))
-                        SAL_WARN("sw.ww8", "DocxAttributeOutput::ParaGrabBag: 
unexpected sdt alias value");
-                    m_aStartedParagraphSdtPrAlias = m_aParagraphSdtPrAlias;
-                }
-                else if (aPropertyValue.Name == "ooxml:CT_SdtPr_checkbox")
-                {
-                    m_nParagraphSdtPrToken = FSNS( XML_w14, XML_checkbox );
-                    uno::Sequence<beans::PropertyValue> aGrabBag;
-                    aPropertyValue.Value >>= aGrabBag;
-                    for (const auto& rProp : std::as_const(aGrabBag))
-                    {
-                        OUString sValue = rProp.Value.get<OUString>();
-                        if (rProp.Name == "ooxml:CT_SdtCheckbox_checked")
-                            AddToAttrList( m_pParagraphSdtPrTokenChildren,
-                                           FSNS( XML_w14, XML_checked ),
-                                           OUStringToOString( sValue, 
RTL_TEXTENCODING_UTF8 ).getStr() );
-                        else if (rProp.Name == 
"ooxml:CT_SdtCheckbox_checkedState")
-                            AddToAttrList( m_pParagraphSdtPrTokenChildren,
-                                           FSNS( XML_w14, XML_checkedState ),
-                                           OUStringToOString( sValue, 
RTL_TEXTENCODING_UTF8 ).getStr() );
-                        else if (rProp.Name == 
"ooxml:CT_SdtCheckbox_uncheckedState")
-                            AddToAttrList( m_pParagraphSdtPrTokenChildren,
-                                           FSNS( XML_w14, XML_uncheckedState ),
-                                           OUStringToOString( sValue, 
RTL_TEXTENCODING_UTF8 ).getStr() );
-                    }
-                }
-                else if (aPropertyValue.Name == "ooxml:CT_SdtPr_id")
-                    m_bParagraphSdtHasId = true;
-                else
-                    SAL_WARN("sw.ww8", "DocxAttributeOutput::ParaGrabBag: 
unhandled SdtPr grab bag property " << aPropertyValue.Name);
-            }
+            m_aParagraphSdt.GetSdtParamsFromGrabBag(aGrabBagSdt);
+            m_aStartedParagraphSdtPrAlias = m_aParagraphSdt.m_aAlias;
         }
         else if (rGrabBagElement.first == "ParaCnfStyle")
         {
@@ -10081,107 +10082,14 @@ void DocxAttributeOutput::CharGrabBag( const 
SfxGrabBagItem& rItem )
         }
         else if (rGrabBagElement.first == "SdtEndBefore")
         {
-            if (m_bStartedCharSdt)
+            if (m_aRunSdt.m_bStartedSdt)
                 m_bEndCharSdt = true;
         }
         else if (rGrabBagElement.first == "SdtPr" && FLY_NOT_PROCESSED != 
m_nStateOfFlyFrame )
         {
             const uno::Sequence<beans::PropertyValue> aGrabBagSdt =
                     rGrabBagElement.second.get< 
uno::Sequence<beans::PropertyValue> >();
-            for (const beans::PropertyValue& aPropertyValue : aGrabBagSdt)
-            {
-                if (aPropertyValue.Name == "ooxml:CT_SdtPr_checkbox")
-                {
-                    m_nRunSdtPrToken = FSNS( XML_w14, XML_checkbox );
-                    uno::Sequence<beans::PropertyValue> aGrabBag;
-                    aPropertyValue.Value >>= aGrabBag;
-                    for (const auto& rProp : std::as_const(aGrabBag))
-                    {
-                        OUString sValue = rProp.Value.get<OUString>();
-                        if (rProp.Name == "ooxml:CT_SdtCheckbox_checked")
-                            AddToAttrList( m_pRunSdtPrTokenChildren,
-                                           FSNS( XML_w14, XML_checked ),
-                                           OUStringToOString( sValue, 
RTL_TEXTENCODING_UTF8 ).getStr() );
-                        else if (rProp.Name == 
"ooxml:CT_SdtCheckbox_checkedState")
-                            AddToAttrList( m_pRunSdtPrTokenChildren,
-                                           FSNS( XML_w14, XML_checkedState ),
-                                           OUStringToOString( sValue, 
RTL_TEXTENCODING_UTF8 ).getStr() );
-                        else if (rProp.Name == 
"ooxml:CT_SdtCheckbox_uncheckedState")
-                            AddToAttrList( m_pRunSdtPrTokenChildren,
-                                           FSNS( XML_w14, XML_uncheckedState ),
-                                           OUStringToOString( sValue, 
RTL_TEXTENCODING_UTF8 ).getStr() );
-                    }
-                }
-                else if (aPropertyValue.Name == "ooxml:CT_SdtPr_text")
-                {
-                    uno::Sequence<beans::PropertyValue> aGrabBag;
-                    aPropertyValue.Value >>= aGrabBag;
-                    for (const auto& rProp : std::as_const(aGrabBag))
-                    {
-                        OUString sValue = rProp.Value.get<OUString>();
-                        if (rProp.Name == "ooxml:CT_SdtText_multiLine")
-                            AddToAttrList(m_pRunSdtPrTextAttrs,
-                                FSNS(XML_w, XML_multiLine),
-                                OUStringToOString(sValue, 
RTL_TEXTENCODING_UTF8).getStr());
-                    }
-                }
-                else if (aPropertyValue.Name == "ooxml:CT_SdtPr_dataBinding" 
&& !m_pRunSdtPrDataBindingAttrs.is())
-                {
-                    uno::Sequence<beans::PropertyValue> aGrabBag;
-                    aPropertyValue.Value >>= aGrabBag;
-                    for (const auto& rProp : std::as_const(aGrabBag))
-                    {
-                        OUString sValue = rProp.Value.get<OUString>();
-                        if (rProp.Name == 
"ooxml:CT_DataBinding_prefixMappings")
-                            AddToAttrList( m_pRunSdtPrDataBindingAttrs,
-                                           FSNS( XML_w, XML_prefixMappings ),
-                                           OUStringToOString( sValue, 
RTL_TEXTENCODING_UTF8 ).getStr() );
-                        else if (rProp.Name == "ooxml:CT_DataBinding_xpath")
-                            AddToAttrList( m_pRunSdtPrDataBindingAttrs,
-                                           FSNS( XML_w, XML_xpath ),
-                                           OUStringToOString( sValue, 
RTL_TEXTENCODING_UTF8 ).getStr() );
-                        else if (rProp.Name == 
"ooxml:CT_DataBinding_storeItemID")
-                            AddToAttrList( m_pRunSdtPrDataBindingAttrs,
-                                           FSNS( XML_w, XML_storeItemID ),
-                                           OUStringToOString( sValue, 
RTL_TEXTENCODING_UTF8 ).getStr() );
-                    }
-                }
-                else if (aPropertyValue.Name == 
"ooxml:CT_SdtPlaceholder_docPart")
-                {
-                    uno::Sequence<beans::PropertyValue> aGrabBag;
-                    aPropertyValue.Value >>= aGrabBag;
-                    for (const auto& rProp : std::as_const(aGrabBag))
-                    {
-                        OUString sValue = rProp.Value.get<OUString>();
-                        if (rProp.Name == 
"ooxml:CT_SdtPlaceholder_docPart_val")
-                            m_aRunSdtPrPlaceHolderDocPart = sValue;
-                    }
-                }
-                else if (aPropertyValue.Name == "ooxml:CT_SdtPr_color")
-                {
-                    uno::Sequence<beans::PropertyValue> aGrabBag;
-                    aPropertyValue.Value >>= aGrabBag;
-                    for (const auto& rProp : std::as_const(aGrabBag))
-                    {
-                        OUString sValue = rProp.Value.get<OUString>();
-                        if (rProp.Name == "ooxml:CT_SdtColor_val")
-                            m_aRunSdtPrColor = sValue;
-                    }
-                }
-                else if (aPropertyValue.Name == "ooxml:CT_SdtPr_alias" && 
m_aRunSdtPrAlias.isEmpty())
-                {
-                    if (!(aPropertyValue.Value >>= m_aRunSdtPrAlias))
-                        SAL_WARN("sw.ww8", "DocxAttributeOutput::CharGrabBag: 
unexpected sdt alias value");
-                }
-                //do not overwrite the parent node.
-                else if (aPropertyValue.Name == "ooxml:CT_SdtPr_text" && 
!m_pRunSdtPrTokenChildren.is())
-                    m_nRunSdtPrToken = FSNS( XML_w, XML_text );
-                else if (aPropertyValue.Name == "ooxml:CT_SdtPr_id" && 
m_nRunSdtPrToken == 0)
-                    // only write id token as a marker if no other exist
-                    m_nRunSdtPrToken = FSNS( XML_w, XML_id );
-                else if (aPropertyValue.Name == "ooxml:CT_SdtPr_citation")
-                    m_nRunSdtPrToken = FSNS( XML_w, XML_citation );
-            }
+            m_aRunSdt.GetSdtParamsFromGrabBag(aGrabBagSdt);
         }
         else
             SAL_INFO("sw.ww8", "DocxAttributeOutput::CharGrabBag: unhandled 
grab bag property " << rGrabBagElement.first);
@@ -10194,8 +10102,6 @@ DocxAttributeOutput::DocxAttributeOutput( DocxExport 
&rExport, const FSHelperPtr
       m_pSerializer( pSerializer ),
       m_rDrawingML( *pDrawingML ),
       m_bEndCharSdt(false),
-      m_bStartedCharSdt(false),
-      m_bStartedParaSdt(false),
       m_endPageRef( false ),
       m_pFootnotesList( new ::docx::FootnotesList() ),
       m_pEndnotesList( new ::docx::FootnotesList() ),
@@ -10239,10 +10145,7 @@ DocxAttributeOutput::DocxAttributeOutput( DocxExport 
&rExport, const FSHelperPtr
       m_bParaAfterAutoSpacing(false),
       m_nParaBeforeSpacing(0),
       m_nParaAfterSpacing(0)
-    , m_nParagraphSdtPrToken(0)
-    , m_nRunSdtPrToken(0)
     , m_nStateOfFlyFrame( FLY_NOT_PROCESSED )
-    , m_bParagraphSdtHasId(false)
 {
     // Push initial items to the RelId cache. In case the document contains no
     // special streams (headers, footers, etc.) then these items are used
@@ -10305,26 +10208,4 @@ void DocxAttributeOutput::BulletDefinition(int nId, 
const Graphic& rGraphic, Siz
     m_pSerializer->endElementNS(XML_w, XML_numPicBullet);
 }
 
-void DocxAttributeOutput::AddToAttrList( 
rtl::Reference<sax_fastparser::FastAttributeList>& pAttrList, sal_Int32 
nAttrName, const char* sAttrValue )
-{
-    AddToAttrList( pAttrList, 1, nAttrName, sAttrValue );
-}
-
-void DocxAttributeOutput::AddToAttrList( 
rtl::Reference<sax_fastparser::FastAttributeList>& pAttrList, sal_Int32 nAttrs, 
... )
-{
-    if( !pAttrList.is() )
-        pAttrList = FastSerializerHelper::createAttrList();
-
-    va_list args;
-    va_start( args, nAttrs );
-    for( sal_Int32 i = 0; i<nAttrs; i++)
-    {
-        sal_Int32 nName = va_arg( args, sal_Int32 );
-        const char* pValue = va_arg( args, const char* );
-        if( pValue )
-            pAttrList->add( nName, pValue );
-    }
-    va_end( args );
-}
-
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/ww8/docxattributeoutput.hxx 
b/sw/source/filter/ww8/docxattributeoutput.hxx
index 967e42e54e77..595b30e7329b 100644
--- a/sw/source/filter/ww8/docxattributeoutput.hxx
+++ b/sw/source/filter/ww8/docxattributeoutput.hxx
@@ -121,6 +121,36 @@ struct TableReference
     }
 };
 
+class SdtBlockHelper
+{
+public:
+    SdtBlockHelper()
+        : m_bHasId(false)
+        , m_bStartedSdt(false)
+        , m_nSdtPrToken(0)
+    {}
+
+    bool m_bHasId;
+    bool m_bStartedSdt;
+    rtl::Reference<sax_fastparser::FastAttributeList> m_pTokenChildren;
+    rtl::Reference<sax_fastparser::FastAttributeList> m_pTokenAttributes;
+    rtl::Reference<sax_fastparser::FastAttributeList> m_pTextAttrs;
+    rtl::Reference<sax_fastparser::FastAttributeList> m_pDataBindingAttrs;
+    OUString m_aColor;
+    OUString m_aPlaceHolderDocPart;
+    OUString m_aAlias;
+    sal_Int32 m_nSdtPrToken;
+
+    void DeleteAndResetTheLists();
+
+    void WriteSdtBlock(::sax_fastparser::FSHelperPtr& pSerializer, bool 
bRunTextIsOn, bool bParagraphHasDrawing);
+
+    /// Closes a currently open SDT block.
+    void EndSdtBlock(::sax_fastparser::FSHelperPtr& pSerializer);
+
+    void GetSdtParamsFromGrabBag(const uno::Sequence<beans::PropertyValue>& 
aGrabBagSdt);
+};
+
 /// The class that has handlers for various resource types when exporting as 
DOCX.
 class DocxAttributeOutput : public AttributeOutputBase, public 
oox::vml::VMLTextExport, public oox::drawingml::DMLTextExport
 {
@@ -720,18 +750,6 @@ private:
     void WritePostponedCustomShape();
     void WriteFlyFrame(const ww8::Frame& rFrame);
 
-    void WriteSdtBlock(sal_Int32& nSdtPrToken,
-                       rtl::Reference<sax_fastparser::FastAttributeList>& 
pSdtPrTokenChildren,
-                       rtl::Reference<sax_fastparser::FastAttributeList>& 
pSdtPrTokenAttributes,
-                       rtl::Reference<sax_fastparser::FastAttributeList>& 
pSdtPrDataBindingAttrs,
-                       rtl::Reference<sax_fastparser::FastAttributeList>& 
pSdtPrTextAttrs,
-                       OUString& rSdtPrAlias,
-                       OUString& rSdtPrPlaceholderDocPart,
-                       OUString& rColor,
-                       bool bPara);
-    /// Closes a currently open SDT block.
-    void EndSdtBlock();
-
     void WriteFormDateStart(const OUString& sFullDate, const OUString& 
sDateFormat, const OUString& sLang);
     void WriteSdtDropDownStart(std::u16string_view rName, OUString const& 
rSelected, uno::Sequence<OUString> const& rListItems);
     void WriteSdtDropDownEnd(OUString const& rSelected, 
uno::Sequence<OUString> const& rListItems);
@@ -744,9 +762,6 @@ private:
     void EndField_Impl( const SwTextNode* pNode, sal_Int32 nPos, FieldInfos& 
rInfos );
     void DoWriteFieldRunProperties( const SwTextNode* pNode, sal_Int32 nPos, 
bool bWriteCombChars = false );
 
-    static void AddToAttrList( 
rtl::Reference<sax_fastparser::FastAttributeList>& pAttrList, sal_Int32 
nAttrName, const char* sAttrValue );
-    static void AddToAttrList( 
rtl::Reference<sax_fastparser::FastAttributeList>& pAttrList, sal_Int32 nArgs, 
... );
-
     rtl::Reference<sax_fastparser::FastAttributeList> m_pFontsAttrList;
     rtl::Reference<sax_fastparser::FastAttributeList> 
m_pEastAsianLayoutAttrList;
     rtl::Reference<sax_fastparser::FastAttributeList> m_pCharLangAttrList;
@@ -755,10 +770,6 @@ private:
     rtl::Reference<sax_fastparser::FastAttributeList> m_pHyperlinkAttrList;
     /// If the current SDT around runs should be ended before the current run.
     bool m_bEndCharSdt;
-    /// If an SDT around runs is currently open.
-    bool m_bStartedCharSdt;
-    /// If an SDT around paragraphs is currently open.
-    bool m_bStartedParaSdt;
     /// Attributes of the run color
     rtl::Reference<sax_fastparser::FastAttributeList> m_pColorAttrList;
     sal_uInt8 m_nCharTransparence = 0;
@@ -993,30 +1004,14 @@ private:
     /// RelId <-> BitmapChecksum cache, similar to m_aRelIdCache, but used for 
non-Writer graphics, handled in oox.
     std::stack< std::map<BitmapChecksum, std::pair<OUString, OUString>> > 
m_aSdrRelIdCache;
 
-    /// members to control the existence of grabbagged SDT properties in the 
paragraph
-    sal_Int32 m_nParagraphSdtPrToken;
-    rtl::Reference<sax_fastparser::FastAttributeList> 
m_pParagraphSdtPrTokenChildren;
-    rtl::Reference<sax_fastparser::FastAttributeList> 
m_pParagraphSdtPrTokenAttributes;
-    rtl::Reference<sax_fastparser::FastAttributeList> 
m_pParagraphSdtPrDataBindingAttrs;
-    rtl::Reference<sax_fastparser::FastAttributeList> 
m_pParagraphSdtPrTextAttrs;
-    /// members to control the existence of grabbagged SDT properties in the 
text run
-    sal_Int32 m_nRunSdtPrToken;
+    SdtBlockHelper m_aParagraphSdt;
+    SdtBlockHelper m_aRunSdt;
+
     /// State of the Fly at current position
     FlyProcessingState m_nStateOfFlyFrame;
-    rtl::Reference<sax_fastparser::FastAttributeList> m_pRunSdtPrTokenChildren;
-    rtl::Reference<sax_fastparser::FastAttributeList> 
m_pRunSdtPrDataBindingAttrs;
-    rtl::Reference<sax_fastparser::FastAttributeList> m_pRunSdtPrTextAttrs;
-    /// Value of the <w:alias> paragraph SDT element.
-    OUString m_aParagraphSdtPrAlias;
-    OUString m_aParagraphSdtPrPlaceHolderDocPart;
-    OUString m_aParagraphSdtPrColor;
+
     /// Same as m_aParagraphSdtPrAlias, but its content is available till the 
SDT is closed.
     OUString m_aStartedParagraphSdtPrAlias;
-    OUString m_aRunSdtPrAlias;
-    OUString m_aRunSdtPrPlaceHolderDocPart;
-    OUString m_aRunSdtPrColor;
-    /// Currently paragraph SDT has a <w:id> child element.
-    bool m_bParagraphSdtHasId;
 
     std::vector<std::map<SvxBoxItemLine, css::table::BorderLine2>> 
m_aTableStyleConfs;
 

Reply via email to