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