include/oox/token/relationship.hxx                          |    1 
 oox/source/token/relationship.inc                           |    3 -
 sc/inc/dbdata.hxx                                           |   26 ++++++++++
 sc/inc/document.hxx                                         |   10 ++++
 sc/qa/unit/data/xlsx/tdf167689_xmlMaps_and_xmlColumnPr.xlsx |binary
 sc/qa/unit/subsequent_export_test4.cxx                      |   22 ++++++++
 sc/source/filter/excel/excdoc.cxx                           |   30 ++++++++++++
 sc/source/filter/excel/xedbdata.cxx                         |   23 ++++++++-
 sc/source/filter/inc/tablecolumnsbuffer.hxx                 |    7 ++
 sc/source/filter/oox/tablecolumnsbuffer.cxx                 |   28 +++++++++++
 sc/source/filter/oox/tablecolumnscontext.cxx                |    9 ++-
 sc/source/filter/oox/workbookfragment.cxx                   |   30 ++++++++++++
 12 files changed, 185 insertions(+), 4 deletions(-)

New commits:
commit 49df6ea88eb27a13188b0a9a84c24c29d49611ad
Author:     Bayram Çiçek <bayram.ci...@collabora.com>
AuthorDate: Fri Sep 19 09:25:23 2025 +0200
Commit:     Bayram Çiçek <bayram.ci...@collabora.com>
CommitDate: Fri Sep 19 17:01:34 2025 +0200

    Revert "Revert "tdf#167689: Calc: Support xmlMaps.xml" and..."
    
    This reverts commit d7b747fea4323ff126d1eadc6cd70e9570c852a3.
    
    Reason for revert: regressions are fixed, re-reverting.
    
    Change-Id: I7f06a14e572103de9bcf514f87f912f125398294
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/191176
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com>
    Reviewed-by: Bayram Çiçek <bayram.ci...@collabora.com>

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 be6d193f7d54..5723ff4d7743 100644
--- a/sc/inc/dbdata.hxx
+++ b/sc/inc/dbdata.hxx
@@ -48,6 +48,26 @@ struct TableColumnAttributes
     std::optional<OUString> maTotalsFunction = std::nullopt;
 };
 
+// xmlColumnPr attributes
+struct XmlColumnPrModel
+{
+    sal_uInt32          mnMapId;
+    OUString            msXpath;
+    OUString            msXmlDataType;
+    bool                mbDenormalized;
+
+    explicit            XmlColumnPrModel();
+};
+
+struct TableColumnModel
+{
+    typedef std::unique_ptr<XmlColumnPrModel> XmlColumnPrModelPtr;
+    XmlColumnPrModelPtr mxXmlColumnPr; // Special settings for XML Column 
Properties.
+    XmlColumnPrModel&   createXmlColumnPr();
+
+    explicit            TableColumnModel();
+};
+
 /** Container base class to provide selected access for ScDBData. */
 class ScDBDataContainerBase
 {
@@ -99,6 +119,7 @@ private:
 
     ::std::vector< OUString > maTableColumnNames;   ///< names of table columns
     ::std::vector< TableColumnAttributes > maTableColumnAttributes; ///< 
attributes of table columns
+    ::std::vector< TableColumnModel > maTableColumnModel;
     bool            mbTableColumnNamesDirty;
     SCSIZE          nFilteredRowCount;
 
@@ -155,6 +176,11 @@ 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 SetTableColumnModel( TableColumnModel& rModel )
+    {
+        maTableColumnModel.push_back(std::move(rModel));
+    }
+    SC_DLLPUBLIC const ::std::vector< TableColumnModel >& 
GetTableColumnModel() const { return maTableColumnModel; }
     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 fd6d1eeeea61..840419e498af 100644
--- a/sc/inc/document.hxx
+++ b/sc/inc/document.hxx
@@ -629,6 +629,8 @@ private:
     bool mbConnectionXml = false;
     bool mbCustomXml = false;
     OUString aCustomXmlFragmentPath;
+    bool mbXmlMapsXml = false;
+    std::string sXmlMapsContent;
 
 public:
     bool                     IsCellInChangeTrack(const ScAddress &cell,Color 
*pColCellBorder);
@@ -645,6 +647,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/qa/unit/data/xlsx/tdf167689_xmlMaps_and_xmlColumnPr.xlsx 
b/sc/qa/unit/data/xlsx/tdf167689_xmlMaps_and_xmlColumnPr.xlsx
new file mode 100644
index 000000000000..afa63e3236e4
Binary files /dev/null and 
b/sc/qa/unit/data/xlsx/tdf167689_xmlMaps_and_xmlColumnPr.xlsx differ
diff --git a/sc/qa/unit/subsequent_export_test4.cxx 
b/sc/qa/unit/subsequent_export_test4.cxx
index 2bd47c95359a..b039b5b5c92a 100644
--- a/sc/qa/unit/subsequent_export_test4.cxx
+++ b/sc/qa/unit/subsequent_export_test4.cxx
@@ -1731,6 +1731,28 @@ CPPUNIT_TEST_FIXTURE(ScExportTest4, 
testTotalsRowFunction)
     }
 }
 
+CPPUNIT_TEST_FIXTURE(ScExportTest4, testTdf167689_xmlMaps_and_xmlColumnPr)
+{
+    createScDoc("xlsx/tdf167689_xmlMaps_and_xmlColumnPr.xlsx");
+    save(u"Calc Office Open XML"_ustr);
+
+    // xl/xmlMaps.xml
+    xmlDocUniquePtr pDocXml = parseExport(u"xl/xmlMaps.xml"_ustr);
+    CPPUNIT_ASSERT(pDocXml);
+
+    assertXPath(pDocXml,
+                
"/x:MapInfo/Schema/xsd:schema/xsd:element/xsd:complexType/xsd:sequence/xsd:element/"
+                
"xsd:complexType/xsd:sequence/xsd:element[3]/xsd:complexType/xsd:sequence/"
+                "xsd:element[1]",
+                "name", u"Code");
+
+    // test <xmlColumnPr> of xl/tables/table1.xml
+    xmlDocUniquePtr pDocXmlTables = parseExport(u"xl/tables/table1.xml"_ustr);
+    CPPUNIT_ASSERT(pDocXmlTables);
+    assertXPath(pDocXmlTables, 
"/x:table/x:tableColumns/x:tableColumn[1]/x:xmlColumnPr", "xpath",
+                u"/DataList/TransactionTypeList/TransactionType/Code");
+}
+
 CPPUNIT_TEST_FIXTURE(ScExportTest4, testAutofilterHiddenButton)
 {
     createScDoc("xlsx/hiddenButton.xlsx");
diff --git a/sc/source/filter/excel/excdoc.cxx 
b/sc/source/filter/excel/excdoc.cxx
index 1615cebd3ca3..73f6d01eafd8 100644
--- a/sc/source/filter/excel/excdoc.cxx
+++ b/sc/source/filter/excel/excdoc.cxx
@@ -1275,6 +1275,36 @@ 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"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. 
*/
+        const std::size_t nXmlDeclarationLocation = sXmlMapsContent.find("?>");
+
+        // but there may not be an XML declaration, we should also check that.
+        if (nXmlDeclarationLocation != std::string::npos)
+            sXmlMapsContent = sXmlMapsContent.substr(nXmlDeclarationLocation + 
2);
+
+        // 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..128e145ac51d 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< TableColumnModel >& rTableColumnModel = 
rData.GetTableColumnModel();
     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 (i < rTableColumnModel.size() && 
rTableColumnModel[i].mxXmlColumnPr)
+            {
+                // export <xmlColumnPr>
+                rtl::Reference<sax_fastparser::FastAttributeList> 
pXmlColumnPrAttrList
+                    = sax_fastparser::FastSerializerHelper::createAttrList();
+
+                XmlColumnPrModel* rXmlColRef = 
rTableColumnModel[i].mxXmlColumnPr.get();
+
+                pXmlColumnPrAttrList->add(XML_mapId, 
OUString::number(rXmlColRef->mnMapId));
+                pXmlColumnPrAttrList->add(XML_xpath, rXmlColRef->msXpath);
+                pXmlColumnPrAttrList->add(XML_xmlDataType, 
rXmlColRef->msXmlDataType);
+                pXmlColumnPrAttrList->add(XML_denormalized, 
ToPsz10(rXmlColRef->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 f54c2eb5533e..d18d9dcab957 100644
--- a/sc/source/filter/inc/tablecolumnsbuffer.hxx
+++ b/sc/source/filter/inc/tablecolumnsbuffer.hxx
@@ -22,6 +22,7 @@
 #include <oox/helper/refvector.hxx>
 #include <dbdata.hxx>
 #include "workbookhelper.hxx"
+#include <osl/diagnose.h>
 
 namespace oox { class AttributeList; }
 namespace oox { class SequenceInputStream; }
@@ -44,12 +45,18 @@ 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);
+    /** Returns access to the table column model data. */
+    TableColumnModel& getModel() { return maModel; }
 
 private:
     OUString            maName;
     sal_Int32           mnId;
     sal_Int32           mnDataDxfId;
     TableColumnAttributes maColumnAttributes;
+
+    TableColumnModel    maModel;
 };
 
 class TableColumns : public WorkbookHelper
diff --git a/sc/source/filter/oox/tablecolumnsbuffer.cxx 
b/sc/source/filter/oox/tablecolumnsbuffer.cxx
index 4baa7f1bde8a..55112f7be7d2 100644
--- a/sc/source/filter/oox/tablecolumnsbuffer.cxx
+++ b/sc/source/filter/oox/tablecolumnsbuffer.cxx
@@ -23,6 +23,23 @@
 #include <oox/helper/attributelist.hxx>
 #include <oox/token/tokens.hxx>
 
+XmlColumnPrModel::XmlColumnPrModel() :
+    mnMapId( 1 ),
+    msXpath( OUString() ),
+    msXmlDataType( OUString() ),
+    mbDenormalized( false )
+{
+}
+
+TableColumnModel::TableColumnModel() {}
+
+XmlColumnPrModel& TableColumnModel::createXmlColumnPr()
+{
+    OSL_ENSURE( !mxXmlColumnPr, "TableColumnModel::createXmlColumnPr - 
multiple call" );
+    mxXmlColumnPr.reset( new XmlColumnPrModel );
+    return *mxXmlColumnPr;
+}
+
 namespace oox::xls {
 
 TableColumn::TableColumn( const WorkbookHelper& rHelper ) :
@@ -57,6 +74,16 @@ const TableColumnAttributes& 
TableColumn::getColumnAttributes() const
     return maColumnAttributes;
 }
 
+void TableColumn::importXmlColumnPr(const AttributeList& rAttribs)
+{
+    XmlColumnPrModel& rXmlColumnPr = maModel.createXmlColumnPr();
+
+    rXmlColumnPr.mnMapId = rAttribs.getInteger(XML_mapId, 0);
+    rXmlColumnPr.msXpath = rAttribs.getXString(XML_xpath, OUString());
+    rXmlColumnPr.msXmlDataType = rAttribs.getXString(XML_xmlDataType, 
OUString());
+    rXmlColumnPr.mbDenormalized = rAttribs.getBool(XML_denormalized, false);
+}
+
 TableColumns::TableColumns( const WorkbookHelper& rHelper ) :
     WorkbookHelper( rHelper ),
     mnCount(0)
@@ -95,6 +122,7 @@ bool TableColumns::finalizeImport( ScDBData* pDBData )
         {
             aNames[i] = rxTableColumn->getName();
             aAttributesVector[i] = rxTableColumn->getColumnAttributes();
+            pDBData->SetTableColumnModel( rxTableColumn->getModel() );
             ++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 805c175f6456..c55e42f34a15 100644
--- a/sc/source/filter/oox/workbookfragment.cxx
+++ b/sc/source/filter/oox/workbookfragment.cxx
@@ -77,6 +77,7 @@
 #include <officecfg/Office/Calc.hxx>
 
 #include <xestream.hxx>
+#include <ucbhelper/content.hxx>
 
 namespace oox::xls {
 
@@ -391,6 +392,35 @@ 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;
+
+        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