[Libreoffice-commits] core.git: sw/qa sw/source writerfilter/source

Jacobo Aragunde Pérez jaragunde at igalia.com
Mon Mar 17 06:10:12 PDT 2014


 sw/qa/extras/ooxmlexport/ooxmlexport.cxx     |   16 ++---
 sw/qa/extras/ooxmlexport/ooxmlsdrexport.cxx  |    2 
 sw/source/filter/ww8/docxattributeoutput.cxx |   73 +++++++++++++++++++++++++++
 sw/source/filter/ww8/docxattributeoutput.hxx |    6 ++
 writerfilter/source/dmapper/DomainMapper.cxx |   29 ++++++++++
 writerfilter/source/dmapper/PropertyIds.cxx  |    1 
 writerfilter/source/dmapper/PropertyIds.hxx  |    1 
 writerfilter/source/dmapper/SdtHelper.cxx    |   35 ++++++++++++
 writerfilter/source/dmapper/SdtHelper.hxx    |    8 ++
 writerfilter/source/ooxml/model.xml          |    5 +
 10 files changed, 167 insertions(+), 9 deletions(-)

New commits:
commit 55211e612d2cbed03dd81c039d07ea4e936c2804
Author: Jacobo Aragunde Pérez <jaragunde at igalia.com>
Date:   Mon Mar 17 13:44:36 2014 +0100

    ooxml: preserve w:sdt blocks containing w:docPartObj properties
    
    The <w:docPartObj> property inside <w:sdt> blocks determines a block
    inside the document that can be dinamically updated and enables Word
    2010 to show a hover button to update it. It is used for TOCs,
    bibliography, etc. LibreOffice ignored these blocks and removed them
    from the document on export.
    
    In this patch, we make the importer save the <w:docPartObj> tag and
    its contents in the paragraph interop grab bag.
    
    On export we read the paragraph grab bag and restore the sdt block
    back to the document; we don't know if the paragraph must be enclosed
    in a sdt block when we start it, so we used the parser marks to be
    able to prepend the start of the block before the paragraph opening
    tag.
    
    The grab bag on import is managed by the SdtHelper class. Added a set
    of methods for that purpose.
    
    The ooxml model file was modified to assign token ids to the children
    of w:docPartObj.
    
    Fixed several unit tests that didn't expect the <w:sdt> tag to be
    exported. Also modified testBibliography inside ooxmlexport test
    suite to add checks for this patch.
    
    TODO: in the imported documents the sdtContent block may contain
    several paragraphs but the exporter code as it is can only wrap one
    paragraph. As a result, if the sdt block contained several paragraphs
    the second and next paragraphs will be outside the block in the
    exported document.
    
    Change-Id: I5333fc5ad91a3c50198a4f7647424a2101268c12

diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport.cxx
index e31bef9..0bc7d60 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport.cxx
@@ -2165,9 +2165,7 @@ DECLARE_OOXMLEXPORT_TEST(testPreserveZfield,"preserve_Z_field_TOC.docx")
     if (!pXmlDoc)
         return;
 
-    // FIXME "p[2]" will have to be "p[1]", once the TOC import code is fixed
-    // not to insert an empty paragraph before TOC.
-    assertXPathContent(pXmlDoc, "/w:document/w:body/w:p[2]/w:r[2]/w:instrText", " TOC \\z \\f \\o \"1-3\" \\h");
+    assertXPathContent(pXmlDoc, "/w:document/w:body/w:sdt/w:sdtContent/w:p[1]/w:r[2]/w:instrText", " TOC \\z \\f \\o \"1-3\" \\h");
 }
 
 DECLARE_OOXMLEXPORT_TEST(testPreserveWfieldTOC, "PreserveWfieldTOC.docx")
@@ -2176,7 +2174,7 @@ DECLARE_OOXMLEXPORT_TEST(testPreserveWfieldTOC, "PreserveWfieldTOC.docx")
     if (!pXmlDoc)
         return;
 
-    assertXPathContent(pXmlDoc, "/w:document/w:body/w:p[2]/w:r[2]/w:instrText", " TOC \\z \\w \\f \\o \"1-3\" \\h");
+    assertXPathContent(pXmlDoc, "/w:document/w:body/w:p/w:r[2]/w:instrText", " TOC \\z \\w \\f \\o \"1-3\" \\h");
 }
 
 DECLARE_OOXMLEXPORT_TEST(testFieldFlagB,"TOC_field_b.docx")
@@ -2197,7 +2195,7 @@ DECLARE_OOXMLEXPORT_TEST(testPreserveXfieldTOC, "PreserveXfieldTOC.docx")
     if (!pXmlDoc)
         return;
 
-    assertXPathContent(pXmlDoc, "/w:document/w:body/w:p[2]/w:r[2]/w:instrText", " TOC \\x \\f \\o \"1-3\" \\h");
+    assertXPathContent(pXmlDoc, "/w:document/w:body/w:p/w:r[2]/w:instrText", " TOC \\x \\f \\o \"1-3\" \\h");
 }
 
 DECLARE_OOXMLEXPORT_TEST(testTrackChangesParagraphProperties, "testTrackChangesParagraphProperties.docx")
@@ -2260,7 +2258,7 @@ DECLARE_OOXMLEXPORT_TEST(testTOCFlag_u,"testTOCFlag_u.docx")
 
     // FIXME "p[2]" will have to be "p[1]", once the TOC import code is fixed
     // not to insert an empty paragraph before TOC.
-    assertXPathContent(pXmlDoc, "/w:document/w:body/w:p[2]/w:r[2]/w:instrText", " TOC \\z \\o \"1-9\" \\u \\h");
+    assertXPathContent(pXmlDoc, "/w:document/w:body/w:p/w:r[2]/w:instrText", " TOC \\z \\o \"1-9\" \\u \\h");
 }
 
 DECLARE_OOXMLEXPORT_TEST(testTestTitlePage, "testTitlePage.docx")
@@ -2610,7 +2608,7 @@ DECLARE_OOXMLEXPORT_TEST(testPageref, "testPageref.docx")
     if (!pXmlDoc)
         return;
 
-    assertXPathContent(pXmlDoc, "/w:document/w:body/w:p[2]/w:hyperlink/w:r[3]/w:instrText", "PAGEREF _Toc355095261 \\h");
+    assertXPathContent(pXmlDoc, "/w:document/w:body/w:p[1]/w:hyperlink/w:r[3]/w:instrText", "PAGEREF _Toc355095261 \\h");
 }
 
 DECLARE_OOXMLEXPORT_TEST(testAlphabeticalIndex_AutoColumn,"alphabeticalIndex_AutoColumn.docx")
@@ -2637,7 +2635,9 @@ DECLARE_OOXMLEXPORT_TEST(testBibliography,"FDO75133.docx")
     if (!pXmlDoc)
         return;
 
-    assertXPathContent(pXmlDoc, "/w:document/w:body/w:p[3]/w:r[2]/w:instrText", " BIBLIOGRAPHY ");
+    assertXPathContent(pXmlDoc, "/w:document/w:body/w:p/w:r[2]/w:instrText", " BIBLIOGRAPHY ");
+    assertXPath(pXmlDoc, "/w:document/w:body/w:sdt/w:sdtPr/w:docPartObj/w:docPartGallery", "val", "Bibliographies");
+    assertXPath(pXmlDoc, "/w:document/w:body/w:sdt/w:sdtPr/w:docPartObj/w:docPartUnique", 1);
 }
 
 DECLARE_OOXMLEXPORT_TEST(testOleObject, "test_ole_object.docx")
diff --git a/sw/qa/extras/ooxmlexport/ooxmlsdrexport.cxx b/sw/qa/extras/ooxmlexport/ooxmlsdrexport.cxx
index d46f583..361b523 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlsdrexport.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlsdrexport.cxx
@@ -750,7 +750,7 @@ DECLARE_OOXMLEXPORT_TEST(testFdo69616, "fdo69616.docx")
     if (!pXmlDoc)
         return;
     // VML
-    CPPUNIT_ASSERT(getXPath(pXmlDoc, "/w:document/w:body/w:p[1]/w:r[1]/mc:AlternateContent/mc:Fallback/w:pict/v:group", "coordorigin").match("696,725"));
+    CPPUNIT_ASSERT(getXPath(pXmlDoc, "/w:document/w:body/w:sdt/w:sdtContent/w:p[1]/w:r[1]/mc:AlternateContent/mc:Fallback/w:pict/v:group", "coordorigin").match("696,725"));
 }
 
 DECLARE_OOXMLEXPORT_TEST(testAlignForShape,"Shape.docx")
diff --git a/sw/source/filter/ww8/docxattributeoutput.cxx b/sw/source/filter/ww8/docxattributeoutput.cxx
index 968fd55..ac4f211 100644
--- a/sw/source/filter/ww8/docxattributeoutput.cxx
+++ b/sw/source/filter/ww8/docxattributeoutput.cxx
@@ -270,6 +270,10 @@ void DocxAttributeOutput::StartParagraph( ww8::WW8TableNodeInfo::Pointer_t pText
         }
     }
 
+    // this mark is used to be able to enclose the paragraph inside a sdr tag.
+    // We will only know if we have to do that later.
+    m_pSerializer->mark();
+
     m_pSerializer->startElementNS( XML_w, XML_p, FSEND );
 
     // postpone the output of the run (we get it before the paragraph
@@ -357,6 +361,9 @@ void DocxAttributeOutput::EndParagraph( ww8::WW8TableNodeInfoInner::Pointer_t pT
 
     m_pSerializer->endElementNS( XML_w, XML_p );
 
+    WriteParagraphSdt();
+    m_pSerializer->mergeTopMarks();
+
     // Check for end of cell, rows, tables here
     FinishTableRowCell( pTextNodeInfoInner );
 
@@ -364,6 +371,45 @@ void DocxAttributeOutput::EndParagraph( ww8::WW8TableNodeInfoInner::Pointer_t pT
 
 }
 
+void DocxAttributeOutput::WriteParagraphSdt()
+{
+    if( m_nSdtPrToken > 0 )
+    {
+        // sdt start mark
+        m_pSerializer->mark();
+
+        m_pSerializer->startElementNS( XML_w, XML_sdt, FSEND );
+
+        // output sdt properties
+        m_pSerializer->startElementNS( XML_w, XML_sdtPr, FSEND );
+        m_pSerializer->startElement( m_nSdtPrToken, FSEND );
+
+        uno::Sequence<xml::FastAttribute> aChildren = m_pSdtPrTokenChildren->getFastAttributes();
+        for( sal_Int32 i=0; i < aChildren.getLength(); ++i )
+            m_pSerializer->singleElement( aChildren[i].Token,
+                                          FSNS(XML_w, XML_val),
+                                          rtl::OUStringToOString( aChildren[i].Value, RTL_TEXTENCODING_UTF8 ).getStr(),
+                                          FSEND );
+
+        m_pSerializer->endElement( m_nSdtPrToken );
+        m_pSerializer->endElementNS( XML_w, XML_sdtPr );
+
+        // sdt contents start tag
+        m_pSerializer->startElementNS( XML_w, XML_sdtContent, FSEND );
+
+        // prepend the tags since the sdt start mark before the paragraph
+        m_pSerializer->mergeTopMarks( sax_fastparser::MERGE_MARKS_PREPEND );
+
+        // write the ending tags after the paragraph
+        m_pSerializer->endElementNS( XML_w, XML_sdtContent );
+        m_pSerializer->endElementNS( XML_w, XML_sdt );
+
+        // clear sdt status
+        m_nSdtPrToken = 0;
+        delete m_pSdtPrTokenChildren; m_pSdtPrTokenChildren = NULL;
+    }
+}
+
 void DocxAttributeOutput::FinishTableRowCell( ww8::WW8TableNodeInfoInner::Pointer_t pInner, bool bForceEmptyParagraph )
 {
     if ( pInner.get() )
@@ -6811,6 +6857,30 @@ void DocxAttributeOutput::ParaGrabBag(const SfxGrabBagItem& rItem)
                     FSNS(XML_w, XML_themeFill), OUStringToOString(sThemeFill, RTL_TEXTENCODING_UTF8).getStr(),
                     FSNS(XML_w, XML_fill), OUStringToOString(sOriginalFill, RTL_TEXTENCODING_UTF8).getStr());
         }
+        else if (i->first == "ParaSdtPr")
+        {
+            beans::PropertyValue aPropertyValue = i->second.get<beans::PropertyValue>();
+            if (aPropertyValue.Name == "ooxml:CT_SdtPr_docPartObj")
+            {
+                m_nSdtPrToken = FSNS( XML_w, XML_docPartObj );
+                uno::Sequence<beans::PropertyValue> aGrabBag;
+                aPropertyValue.Value >>= aGrabBag;
+                for (sal_Int32 j=0; j < aGrabBag.getLength(); ++j)
+                {
+                    OUString sValue = aGrabBag[j].Value.get<OUString>();
+                    if (aGrabBag[j].Name == "ooxml:CT_SdtDocPart_docPartGallery")
+                        AddToAttrList( m_pSdtPrTokenChildren,
+                                       FSNS( XML_w, XML_docPartGallery ),
+                                       rtl::OUStringToOString( sValue, RTL_TEXTENCODING_UTF8 ).getStr() );
+                    else if (aGrabBag[j].Name == "ooxml:CT_SdtDocPart_docPartCategory")
+                        AddToAttrList( m_pSdtPrTokenChildren,
+                                       FSNS( XML_w, XML_docPartCategory ),
+                                       rtl::OUStringToOString( sValue, RTL_TEXTENCODING_UTF8 ).getStr() );
+                    else if (aGrabBag[j].Name == "ooxml:CT_SdtDocPart_docPartUnique")
+                        AddToAttrList( m_pSdtPrTokenChildren, FSNS( XML_w, XML_docPartUnique ), "" );
+                }
+            }
+        }
         else
             SAL_INFO("sw.ww8", "DocxAttributeOutput::ParaGrabBag: unhandled grab bag property " << i->first );
     }
@@ -6987,6 +7057,8 @@ DocxAttributeOutput::DocxAttributeOutput( DocxExport &rExport, FSHelperPtr pSeri
       m_nParaBeforeSpacing(0),
       m_nParaAfterSpacing(0),
       m_setFootnote(false)
+    , m_nSdtPrToken(0)
+    , m_pSdtPrTokenChildren(NULL)
 {
 }
 
@@ -7005,6 +7077,7 @@ DocxAttributeOutput::~DocxAttributeOutput()
     delete m_pEndnotesList, m_pEndnotesList = NULL;
 
     delete m_pTableWrt, m_pTableWrt = NULL;
+    delete m_pSdtPrTokenChildren; m_pSdtPrTokenChildren = NULL;
 }
 
 DocxExport& DocxAttributeOutput::GetExport()
diff --git a/sw/source/filter/ww8/docxattributeoutput.hxx b/sw/source/filter/ww8/docxattributeoutput.hxx
index 042fea6..b5a58d8 100644
--- a/sw/source/filter/ww8/docxattributeoutput.hxx
+++ b/sw/source/filter/ww8/docxattributeoutput.hxx
@@ -682,6 +682,8 @@ private:
     void WritePostponedVMLDrawing();
     void WritePostponedDMLDrawing();
 
+    void WriteParagraphSdt();
+
     void StartField_Impl( FieldInfos& rInfos, bool bWriteRun = false );
     void DoWriteCmd( const OUString& rCmd );
     void CmdField_Impl( FieldInfos& rInfos );
@@ -854,6 +856,10 @@ private:
     /// RelId <-> Graphic* cache, so that in case of alternate content, the same graphic only gets written once.
     std::map<const Graphic*, OString> m_aRelIdCache;
 
+    /// members to control the existence of grabbagged SDT properties in the paragraph
+    sal_Int32 m_nSdtPrToken;
+    ::sax_fastparser::FastAttributeList *m_pSdtPrTokenChildren;
+
 public:
     DocxAttributeOutput( DocxExport &rExport, ::sax_fastparser::FSHelperPtr pSerializer, oox::drawingml::DrawingML* pDrawingML );
 
diff --git a/writerfilter/source/dmapper/DomainMapper.cxx b/writerfilter/source/dmapper/DomainMapper.cxx
index 43821d9..7c25991 100644
--- a/writerfilter/source/dmapper/DomainMapper.cxx
+++ b/writerfilter/source/dmapper/DomainMapper.cxx
@@ -18,6 +18,7 @@
  */
 #include "PageBordersHandler.hxx"
 
+#include <resourcemodel/QNameToString.hxx>
 #include <resourcemodel/ResourceModelHelper.hxx>
 #include <SdtHelper.hxx>
 #include <TDefTableHandler.hxx>
@@ -2228,6 +2229,27 @@ void DomainMapper::sprmWithProps( Sprm& rSprm, PropertyMapPtr rContext )
         m_pImpl->m_pSdtHelper->getLocale().append(sStringValue);
     }
     break;
+    case NS_ooxml::LN_CT_SdtPr_docPartObj:
+    {
+        // this is an unsupported SDT property, create a grab bag for it
+        OUString sName = OUString::createFromAscii((*QNameToString::Instance())(nSprmId).c_str());
+        m_pImpl->m_pSdtHelper->enableInteropGrabBag(sName);
+
+        // process subitems
+        writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
+        if (pProperties.get() != NULL)
+            pProperties->resolve(*this);
+    }
+    break;
+    case NS_ooxml::LN_CT_SdtDocPart_docPartGallery:
+    case NS_ooxml::LN_CT_SdtDocPart_docPartCategory:
+    case NS_ooxml::LN_CT_SdtDocPart_docPartUnique:
+    {
+        // this is a child of an unsupported SDT property, store in the grab bag
+        OUString sName = OUString::createFromAscii((*QNameToString::Instance())(nSprmId).c_str());
+        m_pImpl->m_pSdtHelper->appendToInteropGrabBag(sName, uno::Any(sStringValue));
+    }
+    break;
     case NS_ooxml::LN_EG_SectPrContents_pgNumType:
     {
         writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
@@ -2614,6 +2636,13 @@ void DomainMapper::lcl_utext(const sal_uInt8 * data_, size_t len)
         m_pImpl->m_pSdtHelper->createDateControl(sText);
         return;
     }
+    else if (m_pImpl->m_pSdtHelper->isInteropGrabBagEnabled())
+    {
+        // there are unsupported SDT properties in the document
+        // save them in the paragraph interop grab bag
+        uno::Any aPropValue = uno::makeAny(m_pImpl->m_pSdtHelper->getInteropGrabBagAndClear());
+        m_pImpl->GetTopContextOfType(CONTEXT_PARAGRAPH)->Insert(PROP_PARA_SDTPR, aPropValue, true, PARA_GRAB_BAG);
+    }
     else if (len == 1 && sText[0] == 0x03)
     {
         // This is the uFtnEdnSep, remember that the document has a separator.
diff --git a/writerfilter/source/dmapper/PropertyIds.cxx b/writerfilter/source/dmapper/PropertyIds.cxx
index f78c10b..a26cdf9 100644
--- a/writerfilter/source/dmapper/PropertyIds.cxx
+++ b/writerfilter/source/dmapper/PropertyIds.cxx
@@ -387,6 +387,7 @@ OUString PropertyNameSupplier::GetName( PropertyIds eId ) const
             case PROP_CHAR_NUMSPACING_TEXT_EFFECT    :   sName = "CharNumSpacingTextEffect"; break;
             case PROP_CHAR_STYLISTICSETS_TEXT_EFFECT :   sName = "CharStylisticSetsTextEffect"; break;
             case PROP_CHAR_CNTXTALTS_TEXT_EFFECT     :   sName = "CharCntxtAltsTextEffect"; break;
+            case PROP_PARA_SDTPR                     :   sName = "ParaSdtPr"; break;
         }
         ::std::pair<PropertyNameMap_t::iterator,bool> aInsertIt =
                 m_pImpl->aNameMap.insert( PropertyNameMap_t::value_type( eId, sName ));
diff --git a/writerfilter/source/dmapper/PropertyIds.hxx b/writerfilter/source/dmapper/PropertyIds.hxx
index fbbf596..6370a1d 100644
--- a/writerfilter/source/dmapper/PropertyIds.hxx
+++ b/writerfilter/source/dmapper/PropertyIds.hxx
@@ -359,6 +359,7 @@ enum PropertyIds
         ,PROP_CHAR_NUMSPACING_TEXT_EFFECT
         ,PROP_CHAR_STYLISTICSETS_TEXT_EFFECT
         ,PROP_CHAR_CNTXTALTS_TEXT_EFFECT
+        ,PROP_PARA_SDTPR
     };
 struct PropertyNameSupplier_Impl;
 class PropertyNameSupplier
diff --git a/writerfilter/source/dmapper/SdtHelper.cxx b/writerfilter/source/dmapper/SdtHelper.cxx
index df9a26e..af30b74 100644
--- a/writerfilter/source/dmapper/SdtHelper.cxx
+++ b/writerfilter/source/dmapper/SdtHelper.cxx
@@ -188,6 +188,41 @@ bool SdtHelper::hasElements()
     return m_bHasElements;
 }
 
+void SdtHelper::appendToInteropGrabBag(const OUString& rName, const css::uno::Any& rValue)
+{
+    if(isInteropGrabBagEnabled())
+    {
+        sal_Int32 nLength = m_aGrabBag.getLength();
+        m_aGrabBag.realloc(nLength + 1);
+        m_aGrabBag[nLength].Name = rName;
+        m_aGrabBag[nLength].Value = rValue;
+    }
+}
+
+beans::PropertyValue SdtHelper::getInteropGrabBagAndClear()
+{
+    beans::PropertyValue aProp;
+    if(isInteropGrabBagEnabled())
+    {
+        aProp.Name = m_sGrabBagName;
+        aProp.Value = uno::Any(m_aGrabBag);
+
+        m_aGrabBag.realloc(0);
+        m_sGrabBagName = "";
+    }
+    return aProp;
+}
+
+void SdtHelper::enableInteropGrabBag(const OUString& rName)
+{
+    m_sGrabBagName = rName;
+}
+
+bool SdtHelper::isInteropGrabBagEnabled()
+{
+    return !m_sGrabBagName.isEmpty();
+}
+
 } // namespace dmapper
 } // namespace writerfilter
 
diff --git a/writerfilter/source/dmapper/SdtHelper.hxx b/writerfilter/source/dmapper/SdtHelper.hxx
index 7d9f9cb..95f6559 100644
--- a/writerfilter/source/dmapper/SdtHelper.hxx
+++ b/writerfilter/source/dmapper/SdtHelper.hxx
@@ -57,6 +57,9 @@ class SdtHelper
     OUStringBuffer m_sDateFormat;
     /// Locale string as it comes from the ooxml document.
     OUStringBuffer m_sLocale;
+    /// Grab bag to store unsupported SDTs, aiming to save them back on export.
+    com::sun::star::uno::Sequence<com::sun::star::beans::PropertyValue> m_aGrabBag;
+    OUString m_sGrabBagName;
 
     bool m_bHasElements;
 
@@ -80,6 +83,11 @@ public:
     void createDropDownControl();
     /// Create date control from w:sdt's w:date.
     void createDateControl(OUString& rContentText);
+
+    void appendToInteropGrabBag(const OUString& rName, const css::uno::Any& rValue);
+    com::sun::star::beans::PropertyValue getInteropGrabBagAndClear();
+    void enableInteropGrabBag(const OUString& rName);
+    bool isInteropGrabBagEnabled();
 };
 
 } // namespace dmapper
diff --git a/writerfilter/source/ooxml/model.xml b/writerfilter/source/ooxml/model.xml
index ec54c2b..417a0de 100644
--- a/writerfilter/source/ooxml/model.xml
+++ b/writerfilter/source/ooxml/model.xml
@@ -24571,6 +24571,11 @@
       <element name="dateFormat" tokenid="ooxml:CT_SdtDate_dateFormat"/>
       <element name="lid" tokenid="ooxml:CT_SdtDate_lid"/>
     </resource>
+    <resource name="CT_SdtDocPart" resource="Properties" tag="field">
+      <element name="docPartGallery" tokenid="ooxml:CT_SdtDocPart_docPartGallery"/>
+      <element name="docPartCategory" tokenid="ooxml:CT_SdtDocPart_docPartCategory"/>
+      <element name="docPartUnique" tokenid="ooxml:CT_SdtDocPart_docPartUnique"/>
+    </resource>
     <resource name="CT_SdtListItem" resource="Properties" tag="field">
       <attribute name="displayText" tokenid="ooxml:CT_SdtListItem_displayText"/>
       <attribute name="value" tokenid="ooxml:CT_SdtListItem_value"/>


More information about the Libreoffice-commits mailing list