sc/qa/unit/data/xls/pictureOrder.xls   |binary
 sc/qa/unit/subsequent_export_test3.cxx |   18 ++++++++++++++++++
 sc/source/filter/excel/excdoc.cxx      |    7 ++-----
 sc/source/filter/excel/xeescher.cxx    |    4 +++-
 sc/source/filter/inc/xcl97rec.hxx      |    4 +++-
 sc/source/filter/inc/xeescher.hxx      |    2 +-
 sc/source/filter/xcl97/xcl97rec.cxx    |   11 +++++++++++
 7 files changed, 38 insertions(+), 8 deletions(-)

New commits:
commit 749e49c00afbaf711c1c68c28d208e1335fb6701
Author:     Ujjawal Kumar <[email protected]>
AuthorDate: Fri Feb 27 14:54:05 2026 +0530
Commit:     Miklos Vajna <[email protected]>
CommitDate: Fri Mar 6 09:48:53 2026 +0100

    ooxml: Save <picture> before control objs as listed in the ECMA specs
    
    Bug document: fdo69453-1.xls
    
    The above document contains <picture> tag on sheet 20 and 21 and is written
    after the <mc:AlternateContent> which is an incorrect order according to the
    ECMA specs and should be written before control objects.
    
    Change-Id: I14bb128281c75d47ee063dd516588a58a0f37dce
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/200905
    Tested-by: Jenkins CollaboraOffice <[email protected]>
    Reviewed-by: Miklos Vajna <[email protected]>

diff --git a/sc/qa/unit/data/xls/pictureOrder.xls 
b/sc/qa/unit/data/xls/pictureOrder.xls
new file mode 100644
index 000000000000..07e8a9d4508f
Binary files /dev/null and b/sc/qa/unit/data/xls/pictureOrder.xls differ
diff --git a/sc/qa/unit/subsequent_export_test3.cxx 
b/sc/qa/unit/subsequent_export_test3.cxx
index 721c8c5edc34..40a32fae80f2 100644
--- a/sc/qa/unit/subsequent_export_test3.cxx
+++ b/sc/qa/unit/subsequent_export_test3.cxx
@@ -2093,6 +2093,24 @@ CPPUNIT_TEST_FIXTURE(ScExportTest3, testTdf170292)
                        u"OFFSET(_cat1,0,2,,)");
 }
 
+CPPUNIT_TEST_FIXTURE(ScExportTest3, testPictureTagOrder)
+{
+    createScDoc("xls/pictureOrder.xls");
+    save(TestFilter::XLSX);
+
+    xmlDocUniquePtr pSheet = parseExport(u"xl/worksheets/sheet3.xml"_ustr);
+    CPPUNIT_ASSERT(pSheet);
+    assertXPath(pSheet, "/x:worksheet/x:picture", 1);
+    assertXPath(pSheet, "/x:worksheet/mc:AlternateContent", 1);
+    assertXPath(pSheet, 
"/x:worksheet/x:picture/following-sibling::mc:AlternateContent", 1);
+
+    pSheet = parseExport(u"xl/worksheets/sheet4.xml"_ustr);
+    CPPUNIT_ASSERT(pSheet);
+    assertXPath(pSheet, "/x:worksheet/x:picture", 1);
+    assertXPath(pSheet, "/x:worksheet/mc:AlternateContent", 1);
+    assertXPath(pSheet, 
"/x:worksheet/x:picture/following-sibling::mc:AlternateContent", 1);
+}
+
 CPPUNIT_PLUGIN_IMPLEMENT();
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/excdoc.cxx 
b/sc/source/filter/excel/excdoc.cxx
index 86e525ad690f..fcede55fe909 100644
--- a/sc/source/filter/excel/excdoc.cxx
+++ b/sc/source/filter/excel/excdoc.cxx
@@ -638,12 +638,9 @@ void ExcTable::FillAsTableXml()
 
     aRecList.AppendRecord( xPageSett );
 
+    std::unique_ptr<XclExpImgData> pImgData(xPageSett->getGraphicExport());
     // all MSODRAWING and OBJ stuff of this sheet goes here
-    aRecList.AppendRecord( GetObjectManager().ProcessDrawing( GetSdrPage( 
mnScTab ) ) );
-
-    XclExpImgData* pImgData = xPageSett->getGraphicExport();
-    if (pImgData)
-        aRecList.AppendRecord(pImgData);
+    aRecList.AppendRecord( GetObjectManager().ProcessDrawing( GetSdrPage( 
mnScTab ), std::move(pImgData) ) );
 
     // <tableParts> after <drawing> and before <extLst>
     aRecList.AppendRecord( GetTablesManager().GetTablesBySheet( mnScTab));
diff --git a/sc/source/filter/excel/xeescher.cxx 
b/sc/source/filter/excel/xeescher.cxx
index 005ae21ef86c..ca1eb5a56463 100644
--- a/sc/source/filter/excel/xeescher.cxx
+++ b/sc/source/filter/excel/xeescher.cxx
@@ -2037,7 +2037,7 @@ void XclExpObjectManager::StartSheet()
     mxObjList = new XclExpObjList( GetRoot(), *mxEscherEx );
 }
 
-rtl::Reference< XclExpRecordBase > XclExpObjectManager::ProcessDrawing( const 
SdrPage* pSdrPage )
+rtl::Reference< XclExpRecordBase > XclExpObjectManager::ProcessDrawing( const 
SdrPage* pSdrPage, std::unique_ptr<XclExpImgData> pImgData )
 {
     if( pSdrPage )
         mxEscherEx->AddSdrPage( *pSdrPage, GetOutput() != EXC_OUTPUT_BINARY );
@@ -2045,6 +2045,8 @@ rtl::Reference< XclExpRecordBase > 
XclExpObjectManager::ProcessDrawing( const Sd
     OSL_ENSURE( mxEscherEx->GetGroupLevel() <= 1, 
"XclExpObjectManager::ProcessDrawing - still groups open?" );
     while( mxEscherEx->GetGroupLevel() )
         mxEscherEx->LeaveGroup();
+    if (pImgData)
+        mxObjList->SetImageData(std::move(pImgData));
     mxObjList->EndSheet();
     return mxObjList;
 }
diff --git a/sc/source/filter/inc/xcl97rec.hxx 
b/sc/source/filter/inc/xcl97rec.hxx
index 27a7b4261af5..58e23be2c193 100644
--- a/sc/source/filter/inc/xcl97rec.hxx
+++ b/sc/source/filter/inc/xcl97rec.hxx
@@ -27,6 +27,7 @@
 #include <svx/svdobj.hxx>
 #include <oox/export/drawingml.hxx>
 
+class XclExpImgData;
 class XclObj;
 class XclExpMsoDrawing;
 class SdrCaptionObj;
@@ -75,7 +76,7 @@ public:
     iterator end () { return maObjs.end(); }
 
     XclExpMsoDrawing* GetMsodrawingPerSheet() { return 
pMsodrawingPerSheet.get(); }
-
+    void SetImageData(std::unique_ptr<XclExpImgData> pImgData);
                                 /// close groups and DgContainer opened in ctor
     void                EndSheet();
 
@@ -88,6 +89,7 @@ private:
     XclEscherEx&        mrEscherEx;
     std::unique_ptr<XclExpMsoDrawing> pMsodrawingPerSheet;
     std::unique_ptr<XclExpMsoDrawing> pSolverContainer;
+    std::unique_ptr<XclExpImgData> m_pImgData;
 
     std::vector<std::unique_ptr<XclObj>> maObjs;
 };
diff --git a/sc/source/filter/inc/xeescher.hxx 
b/sc/source/filter/inc/xeescher.hxx
index bca4b8801ef2..7aba695f90ec 100644
--- a/sc/source/filter/inc/xeescher.hxx
+++ b/sc/source/filter/inc/xeescher.hxx
@@ -423,7 +423,7 @@ public:
 
     /** Processes a drawing page and returns the record block containing all
         related records (MSODRAWING, OBJ, TXO, charts, etc.). */
-    rtl::Reference< XclExpRecordBase > ProcessDrawing( const SdrPage* pSdrPage 
);
+    rtl::Reference< XclExpRecordBase > ProcessDrawing( const SdrPage* 
pSdrPage, std::unique_ptr<XclExpImgData> pImgData = nullptr);
     /** Processes a collection of UNO shapes and returns the record block
         containing all related records (MSODRAWING, OBJ, TXO, charts, etc.). */
     rtl::Reference< XclExpRecordBase > ProcessDrawing( const 
css::uno::Reference< css::drawing::XShapes >& rxShapes );
diff --git a/sc/source/filter/xcl97/xcl97rec.cxx 
b/sc/source/filter/xcl97/xcl97rec.cxx
index fbd00455271b..fd165830f7f9 100644
--- a/sc/source/filter/xcl97/xcl97rec.cxx
+++ b/sc/source/filter/xcl97/xcl97rec.cxx
@@ -132,6 +132,11 @@ std::unique_ptr<XclObj> XclExpObjList::pop_back ()
     return ret;
 }
 
+void XclExpObjList::SetImageData(std::unique_ptr<XclExpImgData> pImgData)
+{
+    m_pImgData = std::move(pImgData);
+}
+
 void XclExpObjList::EndSheet()
 {
     // Is there still something in the stream? -> The solver container
@@ -398,10 +403,16 @@ void XclExpObjList::SaveXml( XclExpXmlStream& rStrm )
         pSolverContainer->SaveXml( rStrm );
 
     if( maObjs.empty())
+    {
+        if (m_pImgData)
+            m_pImgData->SaveXml(rStrm);
         return;
+    }
 
     SaveDrawingMLObjects( *this, rStrm );
     SaveVmlObjects( *this, rStrm );
+    if (m_pImgData)
+        m_pImgData->SaveXml(rStrm);
     SaveFormControlObjects( *this, rStrm );
 }
 

Reply via email to