include/oox/token/relationship.hxx           |    1 
 oox/source/token/relationship.inc            |    3 +-
 sc/inc/dbdata.hxx                            |   12 +++++++++
 sc/inc/document.hxx                          |   10 +++++++
 sc/source/core/tool/dbdata.cxx               |    5 +++
 sc/source/filter/excel/excdoc.cxx            |   27 +++++++++++++++++++++
 sc/source/filter/excel/xedbdata.cxx          |   23 +++++++++++++++++-
 sc/source/filter/inc/tablecolumnsbuffer.hxx  |    5 +++
 sc/source/filter/oox/tablecolumnsbuffer.cxx  |   14 +++++++++++
 sc/source/filter/oox/tablecolumnscontext.cxx |    9 +++++--
 sc/source/filter/oox/workbookfragment.cxx    |   34 +++++++++++++++++++++++++++
 11 files changed, 139 insertions(+), 4 deletions(-)

New commits:
commit f84f8f2ea4f7eea090075b85339599b9a3f367c4
Author:     Bayram Çiçek <bayram.ci...@collabora.com>
AuthorDate: Mon Jul 28 01:12:11 2025 +0300
Commit:     Caolán McNamara <caolan.mcnam...@collabora.com>
CommitDate: Tue Aug 5 22:11:51 2025 +0200

    tdf#167689: Calc: Support xmlMaps.xml
    
    - add Relationship::XMLMAPS in relationship.inc
    
    - save xl/xmlMaps.xml reference into [Content_Types].xml
      - <Override PartName="/xl/xmlMaps.xml" ContentType="application/xml"/>
    
    - save xl/xmlMaps.xml relationship into xl/_rels/workbook.xml.rels
      - <Relationship Id="." Type=".../relationships/xmlMaps" 
Target="/xl/xmlMaps.xml"/>
    
    - import & export xl/xmlMaps.xml
    
    - import & export <xmlColumnPr> of <tableColumn> (in xl/tables/table[n].xml)
    
    Signed-off-by: Bayram Çiçek <bayram.ci...@collabora.com>
    Change-Id: I9666fc921afb7332305bc1d9ce3f2a092e376002
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/188451
    Reviewed-by: Caolán McNamara <caolan.mcnam...@collabora.com>
    Tested-by: Jenkins

diff --git a/include/oox/token/relationship.hxx 
b/include/oox/token/relationship.hxx
index cc97340583f1..56640076f854 100644
--- a/include/oox/token/relationship.hxx
+++ b/include/oox/token/relationship.hxx
@@ -70,6 +70,7 @@ enum class Relationship
     VMLDRAWING,
     WORDVBADATA,
     WORKSHEET,
+    XMLMAPS,
     NUM_ENTRIES // last, unused
 };
 
diff --git a/oox/source/token/relationship.inc 
b/oox/source/token/relationship.inc
index bf09ee518009..dee443a5fd6c 100644
--- a/oox/source/token/relationship.inc
+++ b/oox/source/token/relationship.inc
@@ -49,4 +49,5 @@
 {Relationship::AUDIO, 
u"http://schemas.openxmlformats.org/officeDocument/2006/relationships/audio"},
 {Relationship::VMLDRAWING, 
u"http://schemas.openxmlformats.org/officeDocument/2006/relationships/vmlDrawing"},
 {Relationship::WORDVBADATA, 
u"http://schemas.microsoft.com/office/2006/relationships/wordVbaData"},
-{Relationship::WORKSHEET, 
u"http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet"}
+{Relationship::WORKSHEET, 
u"http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet"},
+{Relationship::XMLMAPS, 
u"http://schemas.openxmlformats.org/officeDocument/2006/relationships/xmlMaps"}
diff --git a/sc/inc/dbdata.hxx b/sc/inc/dbdata.hxx
index 5a587c982b19..497c7a741a3c 100644
--- a/sc/inc/dbdata.hxx
+++ b/sc/inc/dbdata.hxx
@@ -48,6 +48,15 @@ struct TableColumnAttributes
     std::optional<OUString> maTotalsFunction = std::nullopt;
 };
 
+// xmlColumnPr attributes
+struct XmlColumnPrAttributes
+{
+    sal_uInt32          mnMapId;
+    OUString            msXpath;
+    OUString            msXmlDataType;
+    bool                mbDenormalized;
+};
+
 /** Container base class to provide selected access for ScDBData. */
 class ScDBDataContainerBase
 {
@@ -99,6 +108,7 @@ private:
 
     ::std::vector< OUString > maTableColumnNames;   ///< names of table columns
     ::std::vector< TableColumnAttributes > maTableColumnAttributes; ///< 
attributes of table columns
+    ::std::vector< XmlColumnPrAttributes > maXmlColumnPrAttributes; ///< 
attributes of <xmlColumnPr>
     bool            mbTableColumnNamesDirty;
     SCSIZE          nFilteredRowCount;
 
@@ -155,6 +165,8 @@ public:
     SC_DLLPUBLIC const ::std::vector< OUString >& GetTableColumnNames() const 
{ return maTableColumnNames; }
     SC_DLLPUBLIC void SetTableColumnAttributes( ::std::vector< 
TableColumnAttributes >&& rAttributes );
     SC_DLLPUBLIC const ::std::vector< TableColumnAttributes >& 
GetTableColumnAttributes() const { return maTableColumnAttributes; }
+    SC_DLLPUBLIC void SetXmlColumnPrAttributes( const XmlColumnPrAttributes& 
rAttributes );
+    SC_DLLPUBLIC const ::std::vector< XmlColumnPrAttributes >& 
GetXmlColumnPrAttributes() const { return maXmlColumnPrAttributes; }
     bool        AreTableColumnNamesDirty() const { return 
mbTableColumnNamesDirty; }
 
     /** Refresh/update the column names with the header row's cell contents. */
diff --git a/sc/inc/document.hxx b/sc/inc/document.hxx
index 08b27296514f..565a0683496f 100644
--- a/sc/inc/document.hxx
+++ b/sc/inc/document.hxx
@@ -613,6 +613,8 @@ private:
     bool mbConnectionXml = false;
     bool mbCustomXml = false;
     OUString aCustomXmlFragmentPath;
+    bool mbXmlMapsXml = false;
+    std::string sXmlMapsContent;
 
 public:
     bool                     IsCellInChangeTrack(const ScAddress &cell,Color 
*pColCellBorder);
@@ -629,6 +631,14 @@ public:
     const OUString & getCustomXmlItems() { return aCustomXmlFragmentPath; }
     bool hasCustomXml() { return mbCustomXml; }
 
+    void setHasXmlMaps(bool bUse, const std::string& sContent)
+    {
+        mbXmlMapsXml = bUse;
+        sXmlMapsContent = sContent;
+    }
+    const std::string& getXmlMapsItem() { return sXmlMapsContent; }
+    bool hasXmlMaps() { return mbXmlMapsXml; }
+
     bool IsEmbedFonts() const { return mbEmbedFonts; }
     bool IsEmbedUsedFontsOnly() const { return mbEmbedUsedFontsOnly; }
     bool IsEmbedFontScriptLatin() const { return mbEmbedFontScriptLatin; }
diff --git a/sc/source/core/tool/dbdata.cxx b/sc/source/core/tool/dbdata.cxx
index 333a4a12ddb0..576ad0d79c51 100644
--- a/sc/source/core/tool/dbdata.cxx
+++ b/sc/source/core/tool/dbdata.cxx
@@ -702,6 +702,11 @@ void ScDBData::SetTableColumnAttributes( ::std::vector< 
TableColumnAttributes >&
     maTableColumnAttributes = std::move(rAttributes);
 }
 
+void ScDBData::SetXmlColumnPrAttributes( const XmlColumnPrAttributes& 
rAttributes )
+{
+    maXmlColumnPrAttributes.push_back(rAttributes);
+}
+
 void ScDBData::AdjustTableColumnAttributes( UpdateRefMode eUpdateRefMode, 
SCCOL nDx, SCCOL nCol1,
         SCCOL nOldCol1, SCCOL nOldCol2, SCCOL nNewCol1, SCCOL nNewCol2 )
 {
diff --git a/sc/source/filter/excel/excdoc.cxx 
b/sc/source/filter/excel/excdoc.cxx
index d11d68d18987..05b0f08814d3 100644
--- a/sc/source/filter/excel/excdoc.cxx
+++ b/sc/source/filter/excel/excdoc.cxx
@@ -1274,6 +1274,33 @@ void ExcDocument::WriteXml( XclExpXmlStream& rStrm )
                           oox::getRelationship(Relationship::CUSTOMXML), 
sCustomXmlPath);
     }
 
+    if (rDoc.hasXmlMaps())
+    {
+        // save xl/xmlMaps.xml relationship into xl/_rels/workbook.xml.rels
+        // save xl/xmlMaps.xml reference into [Content_Types].xml and open 
stream to xl/xmlMaps.xml
+        sax_fastparser::FSHelperPtr aXmlMapsXml = rStrm.CreateOutputStream(
+            "xl/xmlMaps.xml", u"/xl/xmlMaps.xml", 
rStrm.GetCurrentStream()->getOutputStream(),
+            "application/xml", oox::getRelationship(Relationship::XMLMAPS));
+
+        // start exporting xl/xmlMaps.xml
+        /* this adds <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+        XML declaration automatically at the top of the file. */
+        rStrm.PushStream(aXmlMapsXml);
+
+        // get xmlMaps.xml content as string
+        std::string sXmlMapsContent = rDoc.getXmlMapsItem();
+
+        /* we should remove <?xml version="1.0" encoding="UTF-8"?>
+        XML declaration from the string as rStrm.PushStream() already adds it. 
*/
+        sal_uInt32 nXmlDeclarationLocation = sXmlMapsContent.find(">");
+        sXmlMapsContent = sXmlMapsContent.substr(nXmlDeclarationLocation + 1);
+
+        // write contents into xl/xmlMaps.xml
+        rStrm.GetCurrentStream()->write(sXmlMapsContent);
+
+        rStrm.PopStream();
+    }
+
     // write if it has been read|imported or explicitly changed
     // or if ref syntax isn't what would be native for our file format
     // i.e. ExcelA1 in this case
diff --git a/sc/source/filter/excel/xedbdata.cxx 
b/sc/source/filter/excel/xedbdata.cxx
index 5bea87e72b96..ea5123700feb 100644
--- a/sc/source/filter/excel/xedbdata.cxx
+++ b/sc/source/filter/excel/xedbdata.cxx
@@ -13,6 +13,7 @@
 #include <document.hxx>
 #include <oox/export/utils.hxx>
 #include <oox/token/namespaces.hxx>
+#include <sax/fastattribs.hxx>
 
 using namespace oox;
 
@@ -222,6 +223,7 @@ void XclExpTables::SaveTableXml( XclExpXmlStream& rStrm, 
const Entry& rEntry )
 
     const std::vector< OUString >& rColNames = rData.GetTableColumnNames();
     const std::vector< TableColumnAttributes >& rColAttributes = 
rData.GetTableColumnAttributes();
+    const std::vector< XmlColumnPrAttributes >& rXmlColPrAttributes = 
rData.GetXmlColumnPrAttributes();
     if (!rColNames.empty())
     {
         pTableStrm->startElement(XML_tableColumns,
@@ -235,7 +237,7 @@ void XclExpTables::SaveTableXml( XclExpXmlStream& rStrm, 
const Entry& rEntry )
 
             // OOXTODO: write <totalsRowFormula> once we support it.
 
-            pTableStrm->singleElement( XML_tableColumn,
+            pTableStrm->startElement( XML_tableColumn,
                     XML_id, OString::number(i+1),
                     XML_name, rColNames[i].toUtf8(),
                     XML_totalsRowFunction, (i < rColAttributes.size() ? 
rColAttributes[i].maTotalsFunction : std::nullopt)
@@ -249,6 +251,25 @@ void XclExpTables::SaveTableXml( XclExpXmlStream& rStrm, 
const Entry& rEntry )
                     // OOXTODO: XML_totalsRowLabel, ...,
                     // OOXTODO: XML_uniqueName, ...
             );
+
+            if (!rXmlColPrAttributes.empty() && (i < 
rXmlColPrAttributes.size()))
+            {
+                // export <xmlColumnPr>
+                rtl::Reference<sax_fastparser::FastAttributeList> 
pXmlColumnPrAttrList
+                    = sax_fastparser::FastSerializerHelper::createAttrList();
+
+                pXmlColumnPrAttrList->add(XML_mapId,
+                                          
OUString::number(rXmlColPrAttributes[i].mnMapId));
+                pXmlColumnPrAttrList->add(XML_xpath, 
rXmlColPrAttributes[i].msXpath);
+                pXmlColumnPrAttrList->add(XML_xmlDataType, 
rXmlColPrAttributes[i].msXmlDataType);
+                pXmlColumnPrAttrList->add(XML_denormalized,
+                                          
ToPsz10(rXmlColPrAttributes[i].mbDenormalized));
+
+                pTableStrm->singleElement(XML_xmlColumnPr, 
pXmlColumnPrAttrList);
+            }
+
+            // put </tableColumn>
+            pTableStrm->endElement(XML_tableColumn);
         }
 
         pTableStrm->endElement( XML_tableColumns);
diff --git a/sc/source/filter/inc/tablecolumnsbuffer.hxx 
b/sc/source/filter/inc/tablecolumnsbuffer.hxx
index efcfd979b219..83b6684c4bb9 100644
--- a/sc/source/filter/inc/tablecolumnsbuffer.hxx
+++ b/sc/source/filter/inc/tablecolumnsbuffer.hxx
@@ -43,12 +43,17 @@ public:
     const OUString&     getName() const;
     /** Gets the attributes of this column. */
     const TableColumnAttributes& getColumnAttributes() const;
+    /** Imports XML column properties for the xmlColumnPr element. */
+    void                importXmlColumnPr(const AttributeList& rAttribs);
+    /** Gets the XML column properties. <xmlColumnPr> */
+    const XmlColumnPrAttributes& getXmlColumnPrAttributes() const;
 
 private:
     OUString            maName;
     sal_Int32           mnId;
     sal_Int32           mnDataDxfId;
     TableColumnAttributes maColumnAttributes;
+    XmlColumnPrAttributes maXmlColumnPrAttributes;
 };
 
 class TableColumns : public WorkbookHelper
diff --git a/sc/source/filter/oox/tablecolumnsbuffer.cxx 
b/sc/source/filter/oox/tablecolumnsbuffer.cxx
index 4baa7f1bde8a..b7a4564b1aa3 100644
--- a/sc/source/filter/oox/tablecolumnsbuffer.cxx
+++ b/sc/source/filter/oox/tablecolumnsbuffer.cxx
@@ -57,6 +57,19 @@ const TableColumnAttributes& 
TableColumn::getColumnAttributes() const
     return maColumnAttributes;
 }
 
+void TableColumn::importXmlColumnPr(const AttributeList& rAttribs)
+{
+    maXmlColumnPrAttributes.mnMapId = rAttribs.getInteger(XML_mapId, 0);
+    maXmlColumnPrAttributes.msXpath = rAttribs.getXString(XML_xpath, 
OUString());
+    maXmlColumnPrAttributes.msXmlDataType = 
rAttribs.getXString(XML_xmlDataType, OUString());
+    maXmlColumnPrAttributes.mbDenormalized = 
rAttribs.getBool(XML_denormalized, false);
+}
+
+const XmlColumnPrAttributes& TableColumn::getXmlColumnPrAttributes() const
+{
+    return maXmlColumnPrAttributes;
+}
+
 TableColumns::TableColumns( const WorkbookHelper& rHelper ) :
     WorkbookHelper( rHelper ),
     mnCount(0)
@@ -95,6 +108,7 @@ bool TableColumns::finalizeImport( ScDBData* pDBData )
         {
             aNames[i] = rxTableColumn->getName();
             aAttributesVector[i] = rxTableColumn->getColumnAttributes();
+            pDBData->SetXmlColumnPrAttributes( 
rxTableColumn->getXmlColumnPrAttributes() );
             ++i;
         }
         pDBData->SetTableColumnNames( std::move(aNames) );
diff --git a/sc/source/filter/oox/tablecolumnscontext.cxx 
b/sc/source/filter/oox/tablecolumnscontext.cxx
index 270f544bd503..1129bcd22f7d 100644
--- a/sc/source/filter/oox/tablecolumnscontext.cxx
+++ b/sc/source/filter/oox/tablecolumnscontext.cxx
@@ -32,9 +32,14 @@ TableColumnContext::TableColumnContext( 
WorksheetContextBase& rParent, TableColu
 {
 }
 
-ContextHandlerRef TableColumnContext::onCreateContext( sal_Int32 /*nElement*/, 
const AttributeList& /*rAttribs*/ )
+ContextHandlerRef TableColumnContext::onCreateContext( sal_Int32 nElement, 
const AttributeList& rAttribs )
 {
-    /* no known child elements */
+    switch (nElement)
+    {
+        case XLS_TOKEN(xmlColumnPr):
+            mrTableColumn.importXmlColumnPr( rAttribs );
+            break;
+    }
     return nullptr;
 }
 
diff --git a/sc/source/filter/oox/workbookfragment.cxx 
b/sc/source/filter/oox/workbookfragment.cxx
index 91025369aad2..9b3ed3b7e5ed 100644
--- a/sc/source/filter/oox/workbookfragment.cxx
+++ b/sc/source/filter/oox/workbookfragment.cxx
@@ -78,6 +78,7 @@
 #include <officecfg/Office/Calc.hxx>
 
 #include <xestream.hxx>
+#include <ucbhelper/content.hxx>
 
 namespace oox::xls {
 
@@ -399,6 +400,39 @@ void WorkbookFragment::finalizeImport()
     if (!aCustomXmlFragmentPath.isEmpty())
         getScDocument().setHasCustomXml(true, aCustomXmlFragmentPath);
 
+    // read the xmlMaps substream (from xl/_rels/workbook.xml.rels)
+    OUString aXmlMapsFragmentPath = getFragmentPathFromFirstTypeFromOfficeDoc( 
u"xmlMaps" );
+    if (!aXmlMapsFragmentPath.isEmpty())
+    {
+        // read xmlMaps.xml
+        std::string sXmlMapsContent;
+        size_t nBufferSize = 4096;
+
+        ucbhelper::Content aContent(aXmlMapsFragmentPath,
+                                    Reference<css::ucb::XCommandEnvironment>(),
+                                    comphelper::getProcessComponentContext());
+
+        Reference<XInputStream> xXmlMapsInputStream(
+            getBaseFilter().openInputStream(aXmlMapsFragmentPath), 
UNO_SET_THROW);
+
+        std::ostringstream aStrmBuf;
+        Sequence<sal_Int8> aBytes;
+        size_t nBytesRead = 0;
+
+        do
+        {
+            nBytesRead = xXmlMapsInputStream->readBytes(aBytes, nBufferSize);
+            const sal_Int8* p = aBytes.getConstArray();
+            aStrmBuf << std::string(p, p + nBytesRead);
+        } while (nBytesRead == nBufferSize);
+
+        // put raw content of xmlMaps.xml into sXmlMapsContent
+        sXmlMapsContent = aStrmBuf.str();
+
+        if (!sXmlMapsContent.empty())
+            getScDocument().setHasXmlMaps(true, sXmlMapsContent);
+    }
+
     xGlobalSegment->setPosition( 1.0 );
 
     /*  Create fragments for all sheets, before importing them. Needed to do

Reply via email to