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