accessibility/source/standard/vclxaccessibletoolbox.cxx      |   11 
 configure.ac                                                 |   14 
 distro-configs/LibreOfficeLinux.conf                         |    1 
 filter/qa/unit/svg.cxx                                       |    3 
 include/svx/sdrmasterpagedescriptor.hxx                      |    3 
 include/svx/sdrpageuser.hxx                                  |    3 
 include/svx/svdpage.hxx                                      |    1 
 sal/osl/unx/file.cxx                                         |    6 
 sc/inc/SolverSettings.hxx                                    |  117 ++-
 sc/qa/uitest/data/tdf129701.ods                              |binary
 sc/qa/uitest/pasteSpecial/tdf129701-PasteUnformated.py       |   70 +
 sc/qa/unit/data/ods/tdf158735.ods                            |binary
 sc/qa/unit/ucalc_solver.cxx                                  |   29 
 sc/source/core/data/SolverSettings.cxx                       |  220 +++++
 sc/source/ui/dbgui/PivotLayoutTreeListBase.cxx               |   12 
 sc/source/ui/docshell/impex.cxx                              |   19 
 sd/qa/unit/export-tests-ooxml4.cxx                           |   45 +
 sd/source/filter/eppt/pptx-epptooxml.cxx                     |   38 -
 sw/qa/extras/htmlexport/data/char_border_and_font_color.fodt |   14 
 sw/qa/extras/htmlexport/htmlexport.cxx                       |   21 
 sw/qa/uitest/writer_tests7/tdf150443.py                      |    4 
 sw/source/core/edit/edsect.cxx                               |    3 
 sw/source/filter/html/css1atr.cxx                            |    6 
 sw/source/filter/html/htmlatr.cxx                            |  409 ++++-------
 sw/source/uibase/uiview/view2.cxx                            |   13 
 vcl/source/window/accessibility.cxx                          |    2 
 vcl/source/window/paint.cxx                                  |    2 
 vcl/unx/gtk3/salnativewidgets-gtk.cxx                        |   20 
 28 files changed, 802 insertions(+), 284 deletions(-)

New commits:
commit 0f599907ae96699006dbc05643a4e19592b64b52
Author:     Mike Kaganski <[email protected]>
AuthorDate: Sat Mar 9 08:03:08 2024 +0100
Commit:     Andras Timar <[email protected]>
CommitDate: Tue Mar 12 15:28:19 2024 +0100

    Enable CVE tests on Windows by default
    
    Since commit c16969b9bc73fdd77e763299d6aea7b614e203e2
    (tdf#84553 Detect and warn of Windows Antivirus., 2020-02-08),
    it is checked that antivirus is disabled in $SRC_ROOT and $BUILDDIR.
    
    This reverts commit a6b2c618cb02168bba950652367f494a1021cf53
    (disable cve tests by default on windows, 2014-10-01).
    
    Change-Id: I3816e97cfb4559f7647167ed291b75468b03dc4d
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/164612
    Tested-by: Jenkins
    Reviewed-by: Mike Kaganski <[email protected]>
    (cherry picked from commit 4c65ab909598766e5859a5f0ce4bf55b23c9551b)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/164621
    Reviewed-by: Xisco Fauli <[email protected]>

diff --git a/configure.ac b/configure.ac
index a16f1204325d..7ba76360416d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -11102,20 +11102,6 @@ dnl 
===================================================================
 dnl Check for disabling cve_tests
 dnl ===================================================================
 AC_MSG_CHECKING([whether to execute CVE tests])
-# If not explicitly enabled or disabled, default
-if test -z "$enable_cve_tests"; then
-    case "$OS" in
-    WNT)
-        # Default cves off for Windows with its wild and wonderful
-        # variety of AV software kicking in and panicking
-        enable_cve_tests=no
-        ;;
-    *)
-        # otherwise yes
-        enable_cve_tests=yes
-        ;;
-    esac
-fi
 if test "$enable_cve_tests" = "no"; then
     AC_MSG_RESULT([no])
     DISABLE_CVE_TESTS=TRUE
diff --git a/filter/qa/unit/svg.cxx b/filter/qa/unit/svg.cxx
index bbf956045e5d..308fe75f8a86 100644
--- a/filter/qa/unit/svg.cxx
+++ b/filter/qa/unit/svg.cxx
@@ -46,7 +46,8 @@ void SvgFilterTest::registerNamespaces(xmlXPathContextPtr& 
pXmlXpathCtx)
 
 CPPUNIT_TEST_FIXTURE(SvgFilterTest, testPreserveJpg)
 {
-#if !defined(MACOSX)
+// On Windows, SVGFilter::filterWriterOrCalc can't get current frame to obtain 
selection
+#if !defined(MACOSX) && !defined(_WIN32)
     // Load a document with a jpeg image in it.
     loadFromFile(u"preserve-jpg.odt");
 
commit 8865e57b2d4e81a4f5196456d67a406b85070492
Author:     Julien Nabet <[email protected]>
AuthorDate: Fri Mar 8 17:35:00 2024 +0100
Commit:     Andras Timar <[email protected]>
CommitDate: Tue Mar 12 15:28:19 2024 +0100

    tdf#160095: fix crash when using ALT+RETURN twice
    
    Change-Id: Idc4c84039115c8d88418246a17e281c26fb49b70
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/164592
    Tested-by: Jenkins
    Reviewed-by: Julien Nabet <[email protected]>
    (cherry picked from commit 82e6236cd6ba6f45aa913a3be606e6b00f81fe07)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/164614
    Reviewed-by: Xisco Fauli <[email protected]>

diff --git a/sw/source/core/edit/edsect.cxx b/sw/source/core/edit/edsect.cxx
index daaa7296e81e..070a37a90d2f 100644
--- a/sw/source/core/edit/edsect.cxx
+++ b/sw/source/core/edit/edsect.cxx
@@ -316,6 +316,9 @@ static const SwNode* lcl_SpecialInsertNode(const 
SwPosition* pCurrentPos)
         // find the table/section which is close
         if( pTableNode == nullptr )
         {
+            if( pSectionNode == nullptr )
+                return nullptr;
+
             pInnermostNode = pSectionNode;
             pSection = &static_cast<const 
SwSectionNode*>(pSectionNode)->GetSection();
         }
commit 89879bfd0e46a9e98b0008e13425aa463bc34b28
Author:     Sarper Akdemir <[email protected]>
AuthorDate: Wed Mar 6 13:48:14 2024 +0300
Commit:     Andras Timar <[email protected]>
CommitDate: Tue Mar 12 15:28:19 2024 +0100

    tdf#159931: pptx export: export each used slide layout for a master
    
    attempts to fix the slideLayout reference related regression
    from Idb6b88ebe87a83818d8eb27a1fa087652a002c0c.
    
    To correctly export the all used slideLayout instances for a
    given master, iterate through sdr::PageUsers of that master
    and figure out all used layouts.
    
    Change-Id: I0f58befac1ba4d5ec01aeedbb5f611c83683dcf8
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/164468
    Tested-by: Jenkins
    Reviewed-by: Balazs Varga <[email protected]>
    Reviewed-by: Sarper Akdemir <[email protected]>
    (cherry picked from commit a35831becee3781daf8628c48944660d31d84d8b)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/164495

diff --git a/include/svx/sdrmasterpagedescriptor.hxx 
b/include/svx/sdrmasterpagedescriptor.hxx
index d7eac22a1b5c..9be1e663bcff 100644
--- a/include/svx/sdrmasterpagedescriptor.hxx
+++ b/include/svx/sdrmasterpagedescriptor.hxx
@@ -22,6 +22,7 @@
 
 #include <svx/sdrpageuser.hxx>
 #include <svx/svdsob.hxx>
+#include <svx/svxdllapi.h>
 #include <memory>
 
 class SdrPageProperties;
@@ -30,7 +31,7 @@ namespace sdr::contact { class ViewContact; }
 
 namespace sdr
 {
-    class MasterPageDescriptor final : public sdr::PageUser
+    class SVXCORE_DLLPUBLIC MasterPageDescriptor final : public sdr::PageUser
     {
     private:
         SdrPage&                                        maOwnerPage;
diff --git a/include/svx/sdrpageuser.hxx b/include/svx/sdrpageuser.hxx
index 8c31a2936c7e..5557b2729777 100644
--- a/include/svx/sdrpageuser.hxx
+++ b/include/svx/sdrpageuser.hxx
@@ -21,6 +21,7 @@
 #define INCLUDED_SVX_SDRPAGEUSER_HXX
 
 #include <vector>
+#include <svx/svxdllapi.h>
 
 class SdrPage;
 
@@ -30,7 +31,7 @@ class SdrPage;
 
 namespace sdr
 {
-    class PageUser
+    class SVXCORE_DLLPUBLIC PageUser
     {
     public:
         // this method is called from the destructor of the referenced page.
diff --git a/include/svx/svdpage.hxx b/include/svx/svdpage.hxx
index c70a2f1f4a2d..cb1e7f9f0b20 100644
--- a/include/svx/svdpage.hxx
+++ b/include/svx/svdpage.hxx
@@ -405,6 +405,7 @@ private:
 public:
     void AddPageUser(sdr::PageUser& rNewUser);
     void RemovePageUser(sdr::PageUser& rOldUser);
+    const sdr::PageUserVector& GetPageUsers() const { return maPageUsers; };
 
     // SdrModel access on SdrPage level
     SdrModel& getSdrModelFromSdrPage() const { return mrSdrModelFromSdrPage; }
diff --git a/sd/qa/unit/export-tests-ooxml4.cxx 
b/sd/qa/unit/export-tests-ooxml4.cxx
index c4c061c50d2d..06cc2805d64d 100644
--- a/sd/qa/unit/export-tests-ooxml4.cxx
+++ b/sd/qa/unit/export-tests-ooxml4.cxx
@@ -1095,6 +1095,51 @@ CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest4, 
testTdf157740_slideMasters)
     assertXPath(pXmlDocContent, 
"/p:sldMaster/p:sldLayoutIdLst/p:sldLayoutId"_ostr, 1);
 }
 
+CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest4, testTdf159931_slideLayouts)
+{
+    createSdImpressDoc("odp/repeatBitmapMode.odp");
+    save("Impress Office Open XML");
+
+    xmlDocUniquePtr pXmlDocRels1 = 
parseExport("ppt/slides/_rels/slide1.xml.rels");
+    xmlDocUniquePtr pXmlDocRels2 = 
parseExport("ppt/slides/_rels/slide2.xml.rels");
+
+    assertXPath(
+        pXmlDocRels1,
+        
"(/rels:Relationships/rels:Relationship[@Type='http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout'])"_ostr);
+
+    // the relative target e.g. "../slideLayouts/slideLayout2.xml"
+    OUString sRelativeLayoutPath1 = getXPathContent(
+        pXmlDocRels1,
+        
"(/rels:Relationships/rels:Relationship[@Type='http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout'])/@Target"_ostr);
+
+    assertXPath(
+        pXmlDocRels2,
+        
"(/rels:Relationships/rels:Relationship[@Type='http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout'])"_ostr);
+
+    // the relative target e.g. "../slideLayouts/slideLayout1.xml"
+    OUString sRelativeLayoutPath2 = getXPathContent(
+        pXmlDocRels2,
+        
"(/rels:Relationships/rels:Relationship[@Type='http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout'])/@Target"_ostr);
+
+    uno::Reference<packages::zip::XZipFileAccess2> xNameAccess
+        = 
packages::zip::ZipFileAccess::createWithURL(comphelper::getComponentContext(m_xSFactory),
+                                                      maTempFile.GetURL());
+
+    // Check that the referenced slideLayout files exist
+    // Without the accompanying fix in place, this test would have failed with:
+    // equality assertion failed
+    // - Expected: 1
+    // - Actual  : 0
+    // i.e. the referenced slideLayout file was missing on export.
+    OUString sSlideLayoutName1 = sRelativeLayoutPath1.getToken(2, '/');
+    OUString sSlideLayoutName2 = sRelativeLayoutPath2.getToken(2, '/');
+
+    CPPUNIT_ASSERT_EQUAL(true,
+                         bool(xNameAccess->hasByName("ppt/slideLayouts/" + 
sSlideLayoutName1)));
+    CPPUNIT_ASSERT_EQUAL(true,
+                         bool(xNameAccess->hasByName("ppt/slideLayouts/" + 
sSlideLayoutName2)));
+}
+
 CPPUNIT_PLUGIN_IMPLEMENT();
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sd/source/filter/eppt/pptx-epptooxml.cxx 
b/sd/source/filter/eppt/pptx-epptooxml.cxx
index 2148653dcd20..96f135ecf585 100644
--- a/sd/source/filter/eppt/pptx-epptooxml.cxx
+++ b/sd/source/filter/eppt/pptx-epptooxml.cxx
@@ -63,6 +63,7 @@
 #include "../ppt/pptanimations.hxx"
 
 #include <i18nlangtag/languagetag.hxx>
+#include <svx/sdrmasterpagedescriptor.hxx>
 #include <svx/svdpage.hxx>
 #include <svx/unoapi.hxx>
 #include <svx/svdogrp.hxx>
@@ -1515,23 +1516,48 @@ void PowerPointExport::ImplWriteSlideMaster(sal_uInt32 
nPageNum, Reference< XPro
     // use master's id type as they have same range, mso does that as well
     pFS->startElementNS(XML_p, XML_sldLayoutIdLst);
 
-    sal_Int32 nLayout = 0;
-    OUString aSlideName;
-    css::uno::Reference< css::beans::XPropertySet >xPagePropSet;
+    auto getLayoutsUsedForMaster = [](SdrPage* pMaster) -> 
std::unordered_set<sal_Int32>
+    {
+        if (!pMaster)
+            return {};
+
+        std::unordered_set<sal_Int32> aUsedLayouts{};
+        for (const auto* pPageUser : pMaster->GetPageUsers())
+        {
+            const auto* pMasterPageDescriptor
+                = dynamic_cast<const sdr::MasterPageDescriptor*>(pPageUser);
+
+            if (!pMasterPageDescriptor)
+                continue;
+
+            AutoLayout eLayout
+                = 
static_cast<SdPage&>(pMasterPageDescriptor->GetOwnerPage()).GetAutoLayout();
+            aUsedLayouts.insert(eLayout);
+        }
+        return aUsedLayouts;
+    };
+
+    std::unordered_set<sal_Int32> aLayouts = 
getLayoutsUsedForMaster(pMasterPage);
+
+    css::uno::Reference< css::beans::XPropertySet > xPagePropSet;
     xPagePropSet.set(mXDrawPage, UNO_QUERY);
     if (xPagePropSet.is())
     {
         uno::Any aAny;
         if (GetPropertyValue(aAny, xPagePropSet, "SlideLayout"))
-            aAny >>= nLayout;
+            aLayouts.insert(aAny.get<sal_Int32>());
     }
 
+    OUString aSlideName;
     Reference< XNamed > xNamed(mXDrawPage, UNO_QUERY);
     if (xNamed.is())
         aSlideName = xNamed->getName();
 
-    ImplWritePPTXLayout(nLayout, nPageNum, aSlideName);
-    AddLayoutIdAndRelation(pFS, GetLayoutFileId(nLayout, nPageNum));
+    for (auto nLayout : aLayouts)
+    {
+        ImplWritePPTXLayout(nLayout, nPageNum, aSlideName);
+        AddLayoutIdAndRelation(pFS, GetLayoutFileId(nLayout, nPageNum));
+    }
 
     pFS->endElementNS(XML_p, XML_sldLayoutIdLst);
 
commit 57ba760482f166f025756eb81036a60d1a3e81e2
Author:     Stephan Bergmann <[email protected]>
AuthorDate: Fri Mar 8 08:38:44 2024 +0100
Commit:     Andras Timar <[email protected]>
CommitDate: Tue Mar 12 15:28:19 2024 +0100

    Blind fix for Linux 32-bit builds
    
    ...which, according to
    <https://lists.freedesktop.org/archives/libreoffice/2024-March/091666.html> 
"32
    bit build failure (smb, narrowing)", started to fail with
    
    > /<<PKGBUILDDIR>>/sal/osl/unx/file.cxx: In function ‘void 
osl_file_adjustLockFlags(const rtl::OString&, int*, sal_uInt32*)’:
    > /<<PKGBUILDDIR>>/sal/osl/unx/file.cxx:71:26: error: narrowing conversion 
of ‘4283649346’ from ‘unsigned int’ to ‘int’ [-Wnarrowing]
    >     71 | #define CIFS_SUPER_MAGIC 0xFF534D42
    >        |                          ^~~~~~~~~~
    > /<<PKGBUILDDIR>>/sal/osl/unx/file.cxx:795:14: note: in expansion of macro 
‘CIFS_SUPER_MAGIC’
    >    795 |         case CIFS_SUPER_MAGIC:
    >        |              ^~~~~~~~~~~~~~~~
    
    etc.  My Fedora 39 "Linux man-pages 6.05" statfs(2) man page explains about 
the
    struct statfs f_type field of __fsword_t type:  "The __fsword_t type used 
for
    various fields in the statfs structure definition is a glibc internal type, 
not
    intended for public use.  This leaves the programmer in a bit of a conundrum
    when trying to copy or compare these fields to local variables in a program.
    Using unsigned int for such variables suffices on most systems."  But the
    underlying __FSWORD_T_TYPE looks like it is actually defined as a signed 
type in
    /usr/include/bits/typesizes.h.
    
    Change-Id: Ida3ae84031c4e48b0d6e69d76b66b4e4facfa1ae
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/164561
    Tested-by: René Engelhard <[email protected]>
    Reviewed-by: René Engelhard <[email protected]>
    Tested-by: Jenkins
    Reviewed-by: Stephan Bergmann <[email protected]>
    (cherry picked from commit 0f5dfaebd61b9cabbe9762865563c2296ebb0112)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/164504

diff --git a/sal/osl/unx/file.cxx b/sal/osl/unx/file.cxx
index ada4f29578d9..912d61a50614 100644
--- a/sal/osl/unx/file.cxx
+++ b/sal/osl/unx/file.cxx
@@ -68,9 +68,9 @@
 #ifdef LINUX
 #include <sys/vfs.h>
 // As documented by the kernel
-#define SMB_SUPER_MAGIC  0x517B
-#define CIFS_SUPER_MAGIC 0xFF534D42
-#define SMB2_SUPER_MAGIC 0xFE534D42
+#define SMB_SUPER_MAGIC  static_cast<__fsword_t>(0x517B)
+#define CIFS_SUPER_MAGIC static_cast<__fsword_t>(0xFF534D42)
+#define SMB2_SUPER_MAGIC static_cast<__fsword_t>(0xFE534D42)
 #endif
 
 namespace {
commit a1c94168c820788e5fad72ae7c6af2355d395fa8
Author:     Michael Stahl <[email protected]>
AuthorDate: Tue Mar 5 19:21:21 2024 +0100
Commit:     Andras Timar <[email protected]>
CommitDate: Tue Mar 12 15:28:19 2024 +0100

    vcl,accessibility: try to fix a crash while disposing SvxFontNameBox_Impl
    
    This happened in a 6.3 based branch, no idea how to reproduce it.
    Clearly the SvxFontNameBox_Impl is being disposed and in its base class
    Window::dispose() when a call to SvxFontNameBox_Impl::CreateAccessible()
    happens, which seems very suspicious; try to prevent that.
    
    mergedlo.dll!ImplListBox::InsertEntry(long nPos, const rtl::OUString & 
rStr) Zeile 2225
            unter d:\lo    mergedlo.dll!ComboBox::InsertEntry(const 
rtl::OUString & rStr, long nPos) Zeile 886
            unter d:\lo    mergedlo.dll!FontNameBox::Fill(const FontList * 
pList) Zeile 447
            unter d:\lo    [Inlineframe] 
mergedlo.dll!SvxFontNameBox_Impl::Fill(const FontList * pList) Zeile 236
            unter d:\lo    mergedlo.dll!lcl_GetDocFontList(const FontList * * 
ppFontList, SvxFontNameBox_Impl * pBox) Zeile 1290
            unter d:\lo    mergedlo.dll!SvxFontNameBox_Impl::FillList() Zeile 
1331
            unter d:\lo    mergedlo.dll!SvxFontNameBox_Impl::CreateAccessible() 
Zeile 3739
            unter d:\lo    mergedlo.dll!vcl::Window::GetAccessible(bool 
bCreate) Zeile 129
            unter d:\lo    
acclo.dll!VCLXAccessibleToolBox::getAccessibleChild(long i) Zeile 733
            unter d:\lo    
acclo.dll!VCLXAccessibleToolBox::GetItemWindowAccessible(const VclWindowEvent & 
rVclWindowEvent) Zeile 784
            unter d:\lo    
acclo.dll!VCLXAccessibleToolBox::GetChildAccessible(const VclWindowEvent & 
rVclWindowEvent) Zeile 795
            unter d:\lo    
mergedlo.dll!VCLXAccessibleComponent::ProcessWindowChildEvent(const 
VclWindowEvent & rVclWindowEvent) Zeile 165
            unter d:\lo    
acclo.dll!VCLXAccessibleToolBox::ProcessWindowChildEvent(const VclWindowEvent & 
rVclWindowEvent) Zeile 657
            unter d:\lo    
mergedlo.dll!VCLXAccessibleComponent::WindowChildEventListener(VclWindowEvent & 
rEvent) Zeile 129
            unter d:\lo    
mergedlo.dll!VCLXAccessibleComponent::LinkStubWindowChildEventListener(void * 
instance, VclWindowEvent & data) Zeile 118
            unter d:\lo    [Inlineframe] mergedlo.dll!Link<VclWindowEvent 
&,void>::Call(VclWindowEvent &) Zeile 84
            unter d:\lo    
mergedlo.dll!vcl::Window::CallEventListeners(VclEventId nEvent, void * pData) 
Zeile 280
            unter d:\lo    mergedlo.dll!vcl::Window::ImplResetReallyVisible() 
Zeile 735
            unter d:\lo    mergedlo.dll!vcl::Window::ImplResetReallyVisible() 
Zeile 747
            unter d:\lo    mergedlo.dll!vcl::Window::Show(bool bVisible, 
ShowFlags nFlags) Zeile 2198
            unter d:\lo    mergedlo.dll!vcl::Window::Show(bool bVisible, 
ShowFlags nFlags) Zeile 2176
            unter d:\lo    [Inlineframe] mergedlo.dll!vcl::Window::Hide() Zeile 
925
            unter d:\lo    mergedlo.dll!vcl::Window::dispose() Zeile 402
            unter d:\lo    mergedlo.dll!Edit::dispose() Zeile 258
            unter d:\lo    mergedlo.dll!ComboBox::dispose() Zeile 132
            unter d:\lo    mergedlo.dll!SvxFontNameBox_Impl::dispose() Zeile 
1322
            unter d:\lo    [Inlineframe] 
mergedlo.dll!VclPtr<SvxFontNameBox_Impl>::disposeAndClear() Zeile 206
            unter d:\lo    mergedlo.dll!SvxFontNameToolBoxControl::dispose() 
Zeile 3309
            unter d:\lo    
mergedlo.dll!framework::ToolBarManager::RemoveControllers() Zeile 651
            unter d:\lo    mergedlo.dll!framework::ToolBarManager::dispose() 
Zeile 468
            unter d:\lo    mergedlo.dll!framework::ToolBarWrapper::dispose() 
Zeile 105
            unter d:\lo    
mergedlo.dll!framework::ToolbarLayoutManager::destroyToolbars() Zeile 666
            unter d:\lo    
mergedlo.dll!framework::ToolbarLayoutManager::reset() Zeile 364
            unter d:\lo    
mergedlo.dll!framework::LayoutManager::implts_reset(bool bAttached) Zeile 458
            unter d:\lo    
mergedlo.dll!framework::LayoutManager::frameAction(const 
com::sun::star::frame::FrameActionEvent & aEvent) Zeile 2757
            unter d:\lo    mergedlo.dll!`anonymous 
namespace'::XFrameImpl::implts_sendFrameActionEvent(const 
com::sun::star::frame::FrameAction & aAction) Zeile 2952
            unter d:\lo    mergedlo.dll!`anonymous 
namespace'::XFrameImpl::setComponent(const 
com::sun::star::uno::Reference<com::sun::star::awt::XWindow> & 
xComponentWindow, const 
com::sun::star::uno::Reference<com::sun::star::frame::XController> & 
xController) Zeile 1470
            unter d:\lo    mergedlo.dll!`anonymous 
namespace'::XFrameImpl::close(unsigned char bDeliverOwnership) Zeile 1701
            unter d:\lo    
mergedlo.dll!framework::pattern::frame::closeIt(const 
com::sun::star::uno::Reference<com::sun::star::uno::XInterface> & xResource) 
Zeile 62
            unter d:\lo    [Inlineframe] 
mergedlo.dll!framework::CloseDispatcher::implts_closeFrame() Zeile 492
            unter d:\lo    
    Change-Id: Ie05cd05158df58021d0fb4a19e9e38cd35af2426
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/164451
    Tested-by: Jenkins
    Reviewed-by: Michael Weghorn <[email protected]>
    Reviewed-by: Michael Stahl <[email protected]>
    (cherry picked from commit 825dde03999a55d02e4d5bc88a4d5beacb65e67f)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/164503
    Reviewed-by: Noel Grandin <[email protected]>

diff --git a/accessibility/source/standard/vclxaccessibletoolbox.cxx 
b/accessibility/source/standard/vclxaccessibletoolbox.cxx
index acb1d2547133..c9f4546c1251 100644
--- a/accessibility/source/standard/vclxaccessibletoolbox.cxx
+++ b/accessibility/source/standard/vclxaccessibletoolbox.cxx
@@ -630,9 +630,14 @@ Reference< XAccessible > SAL_CALL 
VCLXAccessibleToolBox::getAccessibleChild( sal
         if ( pItemWindow )
         {
             Reference< XAccessible> xParent = xChild;
-            rtl::Reference<OToolBoxWindowItem> xChild2( new 
OToolBoxWindowItem(0,::comphelper::getProcessComponentContext(),pItemWindow->GetAccessible(),xParent)
 );
-            pItemWindow->SetAccessible(xChild2);
-            xChild->SetChild( xChild2 );
+            auto const xInnerAcc(pItemWindow->GetAccessible());
+            if (xInnerAcc) // else child is being disposed - avoid crashing
+            {
+                rtl::Reference<OToolBoxWindowItem> xChild2(new 
OToolBoxWindowItem(0,
+                    ::comphelper::getProcessComponentContext(), xInnerAcc, 
xParent));
+                pItemWindow->SetAccessible(xChild2);
+                xChild->SetChild( xChild2 );
+            }
         }
         if ( nHighlightItemId > ToolBoxItemId(0) && nItemId == 
nHighlightItemId )
             xChild->SetFocus( true );
diff --git a/vcl/source/window/accessibility.cxx 
b/vcl/source/window/accessibility.cxx
index 3c6103ac31d9..295a5c3878fc 100644
--- a/vcl/source/window/accessibility.cxx
+++ b/vcl/source/window/accessibility.cxx
@@ -69,7 +69,7 @@ css::uno::Reference< css::accessibility::XAccessible > 
Window::GetAccessible( bo
     */
     if ( !mpWindowImpl )
         return css::uno::Reference< css::accessibility::XAccessible >();
-    if ( !mpWindowImpl->mxAccessible.is() && bCreate )
+    if (!mpWindowImpl->mxAccessible.is() && !mpWindowImpl->mbInDispose && 
bCreate)
         mpWindowImpl->mxAccessible = CreateAccessible();
 
     return mpWindowImpl->mxAccessible;
commit 108577b75d12091a6edba19fbe1f90479d4564e2
Author:     Patrick Luby <[email protected]>
AuthorDate: Thu Mar 7 17:22:35 2024 -0500
Commit:     Andras Timar <[email protected]>
CommitDate: Tue Mar 12 15:28:19 2024 +0100

    tdf#159996 use transparent alpha mask for output device
    
    Since commit 81994cb2b8b32453a92bcb011830fcb884f22ff3, fixed text
    needs to be drawn to an output device with a transparent alpha
    mask.
    
    Change-Id: I2036e8c6c9133b1caceb24aeca40f04524c16b23
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/164556
    Tested-by: Jenkins
    Reviewed-by: Patrick Luby <[email protected]>
    (cherry picked from commit 73c14fcf97a1dcc9c95c98f9223449957b0b4963)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/164499
    Reviewed-by: Noel Grandin <[email protected]>

diff --git a/vcl/source/window/paint.cxx b/vcl/source/window/paint.cxx
index 7cb1c969a983..5996b818d4bb 100644
--- a/vcl/source/window/paint.cxx
+++ b/vcl/source/window/paint.cxx
@@ -1528,7 +1528,7 @@ void Window::ImplPaintToDevice( OutputDevice* 
i_pTargetOutDev, const Point& i_rP
     VclPtrInstance<VirtualDevice> pMaskedDevice(*i_pTargetOutDev,
                                                 DeviceFormat::WITH_ALPHA);
 
-    pMaskedDevice->SetOutputSizePixel( GetOutputSizePixel() );
+    pMaskedDevice->SetOutputSizePixel( GetOutputSizePixel(), true, true );
     pMaskedDevice->EnableRTL( IsRTLEnabled() );
     aMtf.WindStart();
     aMtf.Play(*pMaskedDevice);
commit 76df54e4b392757ab06ee5dde44ef39ae15c1b56
Author:     Julien Nabet <[email protected]>
AuthorDate: Fri Mar 1 12:14:52 2024 +0100
Commit:     Andras Timar <[email protected]>
CommitDate: Tue Mar 12 15:28:19 2024 +0100

    tdf#159955: fix custom page number
    
    Change-Id: I1a56a4ba266dfb3c21e3b77263ecaa44b4e6dad5
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/164210
    Tested-by: Jenkins
    Reviewed-by: Julien Nabet <[email protected]>
    (cherry picked from commit d440dcfcaf688544a7a6d8bc478048159e5200f0)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/164197
    Reviewed-by: Xisco Fauli <[email protected]>

diff --git a/sw/source/uibase/uiview/view2.cxx 
b/sw/source/uibase/uiview/view2.cxx
index a9658fe92ab7..039dbdf9665e 100644
--- a/sw/source/uibase/uiview/view2.cxx
+++ b/sw/source/uibase/uiview/view2.cxx
@@ -288,14 +288,21 @@ OUString SwView::GetPageStr(sal_uInt16 nPhyNum, 
sal_uInt16 nVirtNum, const OUStr
                     ? SwResId(STR_PAGE_COUNT_PRINTED)
                     : (extra.isEmpty() ? SwResId(STR_PAGE_COUNT) : 
SwResId(STR_PAGE_COUNT_CUSTOM)));
     aStr = aStr.replaceFirst("%1", OUString::number(nPhyNum));
-    aStr = aStr.replaceFirst("%2", OUString::number(nPageCount));
     if (nPageCount != nPrintedPageCount)
     {
+        aStr = aStr.replaceFirst("%2", OUString::number(nPageCount));
         aStr = aStr.replaceFirst("%3", OUString::number(nPrintedPhyNum));
         aStr = aStr.replaceFirst("%4", OUString::number(nPrintedPageCount));
     }
-    else
-        aStr = aStr.replaceFirst("%3", extra);
+    else {
+        if (extra.isEmpty())
+            aStr = aStr.replaceFirst("%2", OUString::number(nPageCount));
+        else
+        {
+            aStr = aStr.replaceFirst("%2", extra);
+            aStr = aStr.replaceFirst("%3", OUString::number(nPageCount));
+        }
+    }
 
     return aStr;
 }
commit 3b59c1ceec8828ba450bb7434809689d92a83e2f
Author:     Caolán McNamara <[email protected]>
AuthorDate: Wed Mar 6 11:38:29 2024 +0000
Commit:     Andras Timar <[email protected]>
CommitDate: Tue Mar 12 15:28:19 2024 +0100

    Breeze scrollbars clipped, allocated space not wide/tall enough
    
    https: //github.com/flathub/org.libreoffice.LibreOffice/issues/247
    Change-Id: If04d4dd8b9608fc7c3a10bf36bad287cf183c2d2
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/164389
    Tested-by: Jenkins
    Reviewed-by: Christian Lohmaier <[email protected]>

diff --git a/vcl/unx/gtk3/salnativewidgets-gtk.cxx 
b/vcl/unx/gtk3/salnativewidgets-gtk.cxx
index bfdeed423846..391baed22291 100644
--- a/vcl/unx/gtk3/salnativewidgets-gtk.cxx
+++ b/vcl/unx/gtk3/salnativewidgets-gtk.cxx
@@ -355,7 +355,6 @@ static GtkWidget* gCacheWindow;
 static GtkWidget* gDumbContainer;
 #if GTK_CHECK_VERSION(4, 0, 0)
 static GtkWidget* gVScrollbar;
-static GtkWidget* gHScrollbar;
 static GtkWidget* gTextView;
 #else
 static GtkWidget* gComboBox;
@@ -363,6 +362,7 @@ static GtkWidget* gListBox;
 static GtkWidget* gSpinBox;
 static GtkWidget* gTreeViewWidget;
 #endif
+static GtkWidget* gHScrollbar;
 static GtkWidget* gEntryBox;
 
 namespace
@@ -2625,10 +2625,11 @@ bool GtkSalGraphics::updateSettings(AllSettings& 
rSettings)
     // set scrollbar settings
     gint min_slider_length = 21;
 
+    GtkRequisition natural_horz_scroll_size;
+    gtk_widget_get_preferred_size(gHScrollbar, nullptr, 
&natural_horz_scroll_size);
+
 #if GTK_CHECK_VERSION(4, 0, 0)
-    GtkRequisition natural_size;
-    gtk_widget_get_preferred_size(gHScrollbar, nullptr, &natural_size);
-    aStyleSet.SetScrollBarSize(natural_size.height);
+    aStyleSet.SetScrollBarSize(natural_horz_scroll_size.height);
 #else
     // Grab some button style attributes
     Size aSize;
@@ -2646,6 +2647,10 @@ bool GtkSalGraphics::updateSettings(AllSettings& 
rSettings)
     if (has_forward || has_backward || has_forward2 || has_backward2)
         QuerySize(mpHScrollbarButtonStyle, aSize);
 
+    // Recent breeze (Mar 2024) has 17 vs 10, while Adwaita still reports 14 
vs 14.
+    if (natural_horz_scroll_size.height > aSize.Height())
+        aSize.setHeight(natural_horz_scroll_size.height);
+
     aStyleSet.SetScrollBarSize(aSize.Height());
 
     gtk_style_context_get(mpVScrollbarSliderStyle, 
gtk_style_context_get_state(mpVScrollbarSliderStyle),
@@ -2932,15 +2937,16 @@ GtkSalGraphics::GtkSalGraphics( GtkSalFrame *pFrame, 
GtkWidget *pWindow )
     mpToolButtonStyle = gtk_widget_get_style_context(GTK_WIDGET(pButton));
 #endif
 
+    gHScrollbar = gtk_scrollbar_new(GTK_ORIENTATION_HORIZONTAL, nullptr);
+    gtk_fixed_put(GTK_FIXED(gDumbContainer), gHScrollbar, 0, 0);
+    gtk_widget_show(gHScrollbar);
+
 #if GTK_CHECK_VERSION(4, 0, 0)
     gVScrollbar = gtk_scrollbar_new(GTK_ORIENTATION_VERTICAL, nullptr);
     gtk_fixed_put(GTK_FIXED(gDumbContainer), gVScrollbar, 0, 0);
     gtk_widget_show(gVScrollbar);
     mpVScrollbarStyle = gtk_widget_get_style_context(gVScrollbar);
 
-    gHScrollbar = gtk_scrollbar_new(GTK_ORIENTATION_HORIZONTAL, nullptr);
-    gtk_fixed_put(GTK_FIXED(gDumbContainer), gHScrollbar, 0, 0);
-    gtk_widget_show(gHScrollbar);
     mpHScrollbarStyle = gtk_widget_get_style_context(gHScrollbar);
 
     gTextView = gtk_text_view_new();
commit b16b7d0fdeab0b4191c6c75c9e886d1ccfeaf09d
Author:     Tomaž Vajngerl <[email protected]>
AuthorDate: Thu Feb 29 23:51:26 2024 +0900
Commit:     Andras Timar <[email protected]>
CommitDate: Tue Mar 12 15:28:19 2024 +0100

    tdf#83720 Pivot Table: Data field should always at last place
    
    In the pivot table dialog we should always put the "Data" field to
    the last place or the cell formats won't be shown correct in the
    pivot table.
    
    Change-Id: If4befe4fff1e6f04d9b709615a1955e3b5f4b4cc
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/164161
    Tested-by: Jenkins
    Reviewed-by: Tomaž Vajngerl <[email protected]>
    (cherry picked from commit 78065e3798439dd790d1be5ac5c219477285c888)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/164126
    Reviewed-by: Christian Lohmaier <[email protected]>

diff --git a/sc/source/ui/dbgui/PivotLayoutTreeListBase.cxx 
b/sc/source/ui/dbgui/PivotLayoutTreeListBase.cxx
index 45af29a4f1a4..672de9559c4e 100644
--- a/sc/source/ui/dbgui/PivotLayoutTreeListBase.cxx
+++ b/sc/source/ui/dbgui/PivotLayoutTreeListBase.cxx
@@ -67,6 +67,9 @@ void 
ScPivotLayoutTreeListBase::PushEntriesToPivotFieldVector(ScPivotFieldVector
     std::unique_ptr<weld::TreeIter> xEachEntry(mxControl->make_iterator());
     if (!mxControl->get_iter_first(*xEachEntry))
         return;
+
+    std::optional<ScPivotField> oDataField;
+
     do
     {
         ScItemValue* pItemValue = 
weld::fromId<ScItemValue*>(mxControl->get_id(*xEachEntry));
@@ -78,8 +81,15 @@ void 
ScPivotLayoutTreeListBase::PushEntriesToPivotFieldVector(ScPivotFieldVector
         aField.nFuncMask     = rFunctionData.mnFuncMask;
         aField.mnDupCount    = rFunctionData.mnDupCount;
         aField.maFieldRef    = rFunctionData.maFieldRef;
-        rVector.push_back(aField);
+
+        if (aField.nCol == PIVOT_DATA_FIELD)
+            oDataField = aField;
+        else
+            rVector.push_back(aField);
     } while (mxControl->iter_next(*xEachEntry));
+
+    if (oDataField)
+        rVector.push_back(*oDataField);
 }
 
 void ScPivotLayoutTreeListBase::InsertEntryForSourceTarget(weld::TreeView& 
/*pSource*/, int /*nTarget*/)
commit ed5bb7356ded2af5ec75f99c96a8da94b2eded82
Author:     Rafael Lima <[email protected]>
AuthorDate: Mon Mar 4 19:01:40 2024 +0100
Commit:     Andras Timar <[email protected]>
CommitDate: Tue Mar 12 15:28:19 2024 +0100

    tdf#158735 Save solver settings for DEPS and SCO as well
    
    When bug tdf#38948 was originally fixed, the solvers DEPS and SCO were not 
considered. This caused a regression, because setting engine options for these 
solvers made them never be saved, even in its own sheet.
    
    This patch fixes that by incorporating the engine options for DEPS and SCO.
    
    Change-Id: I93af712f91da2f7b1ac57ed74f6c2c2d7d411bba
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/164376
    Tested-by: Jenkins
    Reviewed-by: Tomaž Vajngerl <[email protected]>
    (cherry picked from commit 04d884cc99eb66679fb254129b54488bd40e5abf)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/164385
    Reviewed-by: Xisco Fauli <[email protected]>

diff --git a/sc/inc/SolverSettings.hxx b/sc/inc/SolverSettings.hxx
index ec1ef994a7b8..985e8d30f796 100644
--- a/sc/inc/SolverSettings.hxx
+++ b/sc/inc/SolverSettings.hxx
@@ -39,11 +39,34 @@ enum SolverParameter
     SP_LO_ENGINE, // Engine name used in LO
     SP_MS_ENGINE, // Engine ID used in MSO
     SP_INTEGER, // Assume all variables are integer (0: no, 1: yes)
+    // LpSolve, CoinMP and SwarmSolver
     SP_NON_NEGATIVE, // Assume non negativity (1: yes, 2: no)
     SP_EPSILON_LEVEL, // Epsilon level
     SP_LIMIT_BBDEPTH, // Branch and bound depth
     SP_TIMEOUT, // Time limit to return a solution
-    SP_ALGORITHM // Algorithm used by the SwarmSolver (1, 2 or 3)
+    SP_ALGORITHM, // Algorithm used by the SwarmSolver (1, 2 or 3)
+    // Engine options common for DEPS and SCO
+    SP_SWARM_SIZE, // Size of Swarm
+    SP_LEARNING_CYCLES, // Learning Cycles
+    SP_GUESS_VARIABLE_RANGE, // Variable Bounds Guessing
+    SP_VARIABLE_RANGE_THRESHOLD, // Variable Bounds Threshold (when guessing)
+    SP_ACR_COMPARATOR, // Use ACR Comparator (instead of BCH)
+    SP_RND_STARTING_POINT, // Use Random starting point
+    SP_STRONGER_PRNG, // Use a stronger random generator (slower)
+    SP_STAGNATION_LIMIT, // Stagnation Limit
+    SP_STAGNATION_TOLERANCE, // Stagnation Tolerance
+    SP_ENHANCED_STATUS, // Show enhanced solver status
+    // DEPS Options
+    SP_AGENT_SWITCH_RATE, // Agent Switch Rate (DE Probability)
+    SP_SCALING_MIN, // DE: Min Scaling Factor (0-1.2)
+    SP_SCALING_MAX, // DE: Max Scaling Factor (0-1.2)
+    SP_CROSSOVER_PROB, // DE: Crossover Probability (0-1)
+    SP_COGNITIVE_CONST, // Cognitive Constant
+    SP_SOCIAL_CONST, // Social Constant
+    SP_CONSTRICTION_COEFF, // PS: Constriction Coefficient
+    SP_MUTATION_PROB, // Mutation Probability (0-0.005)
+    // SCO Options
+    SP_LIBRARY_SIZE, // Size of library
 };
 
 // Starts at 1 to maintain MS compatibility
@@ -123,6 +146,28 @@ private:
     OUString m_sLimitBBDepth;
     OUString m_sTimeout;
     OUString m_sAlgorithm;
+    // DEPS and SCO
+    OUString m_sSwarmSize;
+    OUString m_sLearningCycles;
+    OUString m_sGuessVariableRange;
+    OUString m_sVariableRangeThreshold;
+    OUString m_sUseACRComparator;
+    OUString m_sUseRandomStartingPoint;
+    OUString m_sUseStrongerPRNG;
+    OUString m_sStagnationLimit;
+    OUString m_sTolerance;
+    OUString m_sEnhancedSolverStatus;
+    // DEPS only
+    OUString m_sAgentSwitchRate;
+    OUString m_sScalingFactorMin;
+    OUString m_sScalingFactorMax;
+    OUString m_sCrossoverProbability;
+    OUString m_sCognitiveConstant;
+    OUString m_sSocialConstant;
+    OUString m_sConstrictionCoeff;
+    OUString m_sMutationProbability;
+    OUString m_sLibrarySize;
+
     css::uno::Sequence<css::beans::PropertyValue> m_aEngineOptions;
 
     std::vector<ModelConstraint> m_aConstraints;
@@ -131,7 +176,9 @@ private:
 
     // Used to create or read a single solver parameter based on its named 
range
     bool ReadParamValue(SolverParameter eParam, OUString& rValue, bool 
bRemoveQuotes = false);
+    bool ReadDoubleParamValue(SolverParameter eParam, OUString& rValue);
     void WriteParamValue(SolverParameter eParam, OUString sValue, bool bQuoted 
= false);
+    void WriteDoubleParamValue(SolverParameter eParam, std::u16string_view 
sValue);
 
     // Creates or reads all constraints stored in named ranges
     void ReadConstraints();
@@ -149,19 +196,46 @@ private:
 
     // Maps solver parameters to named ranges
     std::map<SolverParameter, OUString> m_mNamedRanges
-        = { { SP_OBJ_CELL, "solver_opt" },      { SP_OBJ_TYPE, "solver_typ" },
-            { SP_OBJ_VAL, "solver_val" },       { SP_VAR_CELLS, "solver_adj" },
-            { SP_CONSTR_COUNT, "solver_num" },  { SP_LO_ENGINE, 
"solver_lo_eng" },
-            { SP_MS_ENGINE, "solver_eng" },     { SP_INTEGER, "solver_int" },
-            { SP_NON_NEGATIVE, "solver_neg" },  { SP_EPSILON_LEVEL, 
"solver_eps" },
-            { SP_LIMIT_BBDEPTH, "solver_bbd" }, { SP_TIMEOUT, "solver_tim" },
-            { SP_ALGORITHM, "solver_alg" } };
+        = { { SP_OBJ_CELL, "solver_opt" },
+            { SP_OBJ_TYPE, "solver_typ" },
+            { SP_OBJ_VAL, "solver_val" },
+            { SP_VAR_CELLS, "solver_adj" },
+            { SP_CONSTR_COUNT, "solver_num" },
+            { SP_LO_ENGINE, "solver_lo_eng" },
+            { SP_MS_ENGINE, "solver_eng" },
+            { SP_INTEGER, "solver_int" },
+            { SP_NON_NEGATIVE, "solver_neg" },
+            { SP_EPSILON_LEVEL, "solver_eps" },
+            { SP_LIMIT_BBDEPTH, "solver_bbd" },
+            { SP_TIMEOUT, "solver_tim" },
+            { SP_ALGORITHM, "solver_alg" },
+            { SP_SWARM_SIZE, "solver_ssz" },
+            { SP_LEARNING_CYCLES, "solver_lcy" },
+            { SP_GUESS_VARIABLE_RANGE, "solver_gvr" },
+            { SP_VARIABLE_RANGE_THRESHOLD, "solver_vrt" },
+            { SP_ACR_COMPARATOR, "solver_acr" },
+            { SP_RND_STARTING_POINT, "solver_rsp" },
+            { SP_STRONGER_PRNG, "solver_prng" },
+            { SP_STAGNATION_LIMIT, "solver_slim" },
+            { SP_STAGNATION_TOLERANCE, "solver_stol" },
+            { SP_ENHANCED_STATUS, "solver_enst" },
+            { SP_AGENT_SWITCH_RATE, "solver_asr" },
+            { SP_SCALING_MIN, "solver_smin" },
+            { SP_SCALING_MAX, "solver_smax" },
+            { SP_CROSSOVER_PROB, "solver_crpb" },
+            { SP_COGNITIVE_CONST, "solver_cog" },
+            { SP_SOCIAL_CONST, "solver_soc" },
+            { SP_CONSTRICTION_COEFF, "solver_ccoeff" },
+            { SP_MUTATION_PROB, "solver_mtpb" },
+            { SP_LIBRARY_SIZE, "solver_lbsz" } };
 
     // Maps LO solver implementation names to MS engine codes
     std::map<OUString, OUString> SolverNamesToExcelEngines = {
         { "com.sun.star.comp.Calc.CoinMPSolver", "2" }, // Simplex LP
         { "com.sun.star.comp.Calc.LpsolveSolver", "2" }, // Simplex LP
-        { "com.sun.star.comp.Calc.SwarmSolver", "1" } // GRG Nonlinear
+        { "com.sun.star.comp.Calc.SwarmSolver", "1" }, // GRG Nonlinear
+        { "com.sun.star.comp.Calc.NLPSolver.DEPSSolverImpl", "3" }, // DEPS
+        { "com.sun.star.comp.Calc.NLPSolver.SCOSolverImpl", "3" } // SCO
     };
 
     // Maps MS solver engine codes to LO solver implementation names
@@ -180,7 +254,30 @@ private:
             { "EpsilonLevel", { SP_EPSILON_LEVEL, "solver_eps", "int" } },
             { "LimitBBDepth", { SP_LIMIT_BBDEPTH, "solver_bbd", "bool" } },
             { "Timeout", { SP_TIMEOUT, "solver_tim", "int" } },
-            { "Algorithm", { SP_ALGORITHM, "solver_alg", "int" } } };
+            { "Algorithm", { SP_ALGORITHM, "solver_alg", "int" } },
+            // SCO and DEPS
+            { "AssumeNonNegative", { SP_NON_NEGATIVE, "solver_neg", "bool" } },
+            { "SwarmSize", { SP_SWARM_SIZE, "solver_ssz", "int" } },
+            { "LearningCycles", { SP_LEARNING_CYCLES, "solver_lcy", "int" } },
+            { "GuessVariableRange", { SP_GUESS_VARIABLE_RANGE, "solver_gvr", 
"bool" } },
+            { "VariableRangeThreshold", { SP_VARIABLE_RANGE_THRESHOLD, 
"solver_vrt", "double" } },
+            { "UseACRComparator", { SP_ACR_COMPARATOR, "solver_acr", "bool" } 
},
+            { "UseRandomStartingPoint", { SP_RND_STARTING_POINT, "solver_rsp", 
"bool" } },
+            { "UseStrongerPRNG", { SP_STRONGER_PRNG, "solver_prng", "bool" } },
+            { "StagnationLimit", { SP_STAGNATION_LIMIT, "solver_slim", "int" } 
},
+            { "Tolerance", { SP_STAGNATION_TOLERANCE, "solver_stol", "double" 
} },
+            { "EnhancedSolverStatus", { SP_ENHANCED_STATUS, "solver_enst", 
"bool" } },
+            // DEPS only
+            { "AgentSwitchRate", { SP_AGENT_SWITCH_RATE, "solver_asr", 
"double" } },
+            { "DEFactorMin", { SP_SCALING_MIN, "solver_smin", "double" } },
+            { "DEFactorMax", { SP_SCALING_MAX, "solver_smax", "double" } },
+            { "DECR", { SP_CROSSOVER_PROB, "solver_crpb", "double" } },
+            { "PSC1", { SP_COGNITIVE_CONST, "solver_cog", "double" } },
+            { "PSC2", { SP_SOCIAL_CONST, "solver_soc", "double" } },
+            { "PSWeight", { SP_CONSTRICTION_COEFF, "solver_ccoeff", "double" } 
},
+            { "PSCL", { SP_MUTATION_PROB, "solver_mtpb", "double" } },
+            // SCO only
+            { "LibrarySize", { SP_LIBRARY_SIZE, "solver_lbsz", "int" } } };
 
     // Stores the roots used for named ranges of constraint parts
     // Items here must be in the same order as in ConstraintPart enum
diff --git a/sc/qa/unit/data/ods/tdf158735.ods 
b/sc/qa/unit/data/ods/tdf158735.ods
new file mode 100644
index 000000000000..6003f29bf38a
Binary files /dev/null and b/sc/qa/unit/data/ods/tdf158735.ods differ
diff --git a/sc/qa/unit/ucalc_solver.cxx b/sc/qa/unit/ucalc_solver.cxx
index 47770ec0c0e5..7834597e9c07 100644
--- a/sc/qa/unit/ucalc_solver.cxx
+++ b/sc/qa/unit/ucalc_solver.cxx
@@ -163,4 +163,33 @@ CPPUNIT_TEST_FIXTURE(SolverTest, tdf156815)
     CPPUNIT_ASSERT_EQUAL(OUString("$NewName.$B$2"), aConstraints[0].aRightStr);
 }
 
+// Tests if settings for the DEPS and SCO solvers are kept in the file
+CPPUNIT_TEST_FIXTURE(SolverTest, tdf158735)
+{
+    createScDoc("ods/tdf158735.ods");
+    ScDocument* pDoc = getScDoc();
+
+    // Test the non-default values of the DEPS model
+    ScTable* pTable = pDoc->FetchTable(0);
+    std::shared_ptr<sc::SolverSettings> pSettings = 
pTable->GetSolverSettings();
+    CPPUNIT_ASSERT(pSettings);
+    
CPPUNIT_ASSERT_EQUAL(OUString("com.sun.star.comp.Calc.NLPSolver.DEPSSolverImpl"),
+                         pSettings->GetParameter(SP_LO_ENGINE));
+    CPPUNIT_ASSERT_EQUAL(OUString("0.45"), 
pSettings->GetParameter(SP_AGENT_SWITCH_RATE));
+    CPPUNIT_ASSERT_EQUAL(OUString("0.85"), 
pSettings->GetParameter(SP_CROSSOVER_PROB));
+    CPPUNIT_ASSERT_EQUAL(OUString("1500"), 
pSettings->GetParameter(SP_LEARNING_CYCLES));
+    CPPUNIT_ASSERT_EQUAL(OUString("0"), 
pSettings->GetParameter(SP_ENHANCED_STATUS));
+
+    // Test the non-default values of the SCO model
+    pTable = pDoc->FetchTable(1);
+    pSettings = pTable->GetSolverSettings();
+    CPPUNIT_ASSERT(pSettings);
+    
CPPUNIT_ASSERT_EQUAL(OUString("com.sun.star.comp.Calc.NLPSolver.SCOSolverImpl"),
+                         pSettings->GetParameter(SP_LO_ENGINE));
+    CPPUNIT_ASSERT_EQUAL(OUString("180"), 
pSettings->GetParameter(SP_LIBRARY_SIZE));
+    CPPUNIT_ASSERT_EQUAL(OUString("0.00055"), 
pSettings->GetParameter(SP_STAGNATION_TOLERANCE));
+    CPPUNIT_ASSERT_EQUAL(OUString("1"), 
pSettings->GetParameter(SP_RND_STARTING_POINT));
+    CPPUNIT_ASSERT_EQUAL(OUString("80"), 
pSettings->GetParameter(SP_STAGNATION_LIMIT));
+}
+
 CPPUNIT_PLUGIN_IMPLEMENT();
diff --git a/sc/source/core/data/SolverSettings.cxx 
b/sc/source/core/data/SolverSettings.cxx
index 60eb747f55f5..64badb8c2790 100644
--- a/sc/source/core/data/SolverSettings.cxx
+++ b/sc/source/core/data/SolverSettings.cxx
@@ -11,6 +11,7 @@
 #include <global.hxx>
 #include <table.hxx>
 #include <docsh.hxx>
+#include <rtl/math.hxx>
 #include <solverutil.hxx>
 #include <unotools/charclass.hxx>
 #include <SolverSettings.hxx>
@@ -73,6 +74,28 @@ void SolverSettings::Initialize()
     ReadParamValue(SP_LIMIT_BBDEPTH, m_sLimitBBDepth);
     ReadParamValue(SP_TIMEOUT, m_sTimeout);
     ReadParamValue(SP_ALGORITHM, m_sAlgorithm);
+    // Engine options common for DEPS and SCO
+    ReadParamValue(SP_SWARM_SIZE, m_sSwarmSize);
+    ReadParamValue(SP_LEARNING_CYCLES, m_sLearningCycles);
+    ReadParamValue(SP_GUESS_VARIABLE_RANGE, m_sGuessVariableRange);
+    ReadDoubleParamValue(SP_VARIABLE_RANGE_THRESHOLD, 
m_sVariableRangeThreshold);
+    ReadParamValue(SP_ACR_COMPARATOR, m_sUseACRComparator);
+    ReadParamValue(SP_RND_STARTING_POINT, m_sUseRandomStartingPoint);
+    ReadParamValue(SP_STRONGER_PRNG, m_sUseStrongerPRNG);
+    ReadParamValue(SP_STAGNATION_LIMIT, m_sStagnationLimit);
+    ReadDoubleParamValue(SP_STAGNATION_TOLERANCE, m_sTolerance);
+    ReadParamValue(SP_ENHANCED_STATUS, m_sEnhancedSolverStatus);
+    // DEPS Options
+    ReadDoubleParamValue(SP_AGENT_SWITCH_RATE, m_sAgentSwitchRate);
+    ReadDoubleParamValue(SP_SCALING_MIN, m_sScalingFactorMin);
+    ReadDoubleParamValue(SP_SCALING_MAX, m_sScalingFactorMax);
+    ReadDoubleParamValue(SP_CROSSOVER_PROB, m_sCrossoverProbability);
+    ReadDoubleParamValue(SP_COGNITIVE_CONST, m_sCognitiveConstant);
+    ReadDoubleParamValue(SP_SOCIAL_CONST, m_sSocialConstant);
+    ReadDoubleParamValue(SP_CONSTRICTION_COEFF, m_sConstrictionCoeff);
+    ReadDoubleParamValue(SP_MUTATION_PROB, m_sMutationProbability);
+    // SCO Options
+    ReadParamValue(SP_LIBRARY_SIZE, m_sLibrarySize);
 }
 
 // Returns the current value of the parameter in the object as a string
@@ -119,6 +142,63 @@ OUString SolverSettings::GetParameter(SolverParameter 
eParam)
         case SP_ALGORITHM:
             return m_sAlgorithm;
             break;
+        case SP_SWARM_SIZE:
+            return m_sSwarmSize;
+            break;
+        case SP_LEARNING_CYCLES:
+            return m_sLearningCycles;
+            break;
+        case SP_GUESS_VARIABLE_RANGE:
+            return m_sGuessVariableRange;
+            break;
+        case SP_VARIABLE_RANGE_THRESHOLD:
+            return m_sVariableRangeThreshold;
+            break;
+        case SP_ACR_COMPARATOR:
+            return m_sUseACRComparator;
+            break;
+        case SP_RND_STARTING_POINT:
+            return m_sUseRandomStartingPoint;
+            break;
+        case SP_STRONGER_PRNG:
+            return m_sUseStrongerPRNG;
+            break;
+        case SP_STAGNATION_LIMIT:
+            return m_sStagnationLimit;
+            break;
+        case SP_STAGNATION_TOLERANCE:
+            return m_sTolerance;
+            break;
+        case SP_ENHANCED_STATUS:
+            return m_sEnhancedSolverStatus;
+            break;
+        case SP_AGENT_SWITCH_RATE:
+            return m_sAgentSwitchRate;
+            break;
+        case SP_SCALING_MIN:
+            return m_sScalingFactorMin;
+            break;
+        case SP_SCALING_MAX:
+            return m_sScalingFactorMax;
+            break;
+        case SP_CROSSOVER_PROB:
+            return m_sCrossoverProbability;
+            break;
+        case SP_COGNITIVE_CONST:
+            return m_sCognitiveConstant;
+            break;
+        case SP_SOCIAL_CONST:
+            return m_sSocialConstant;
+            break;
+        case SP_CONSTRICTION_COEFF:
+            return m_sConstrictionCoeff;
+            break;
+        case SP_MUTATION_PROB:
+            return m_sMutationProbability;
+            break;
+        case SP_LIBRARY_SIZE:
+            return m_sLibrarySize;
+            break;
         default:
             return "";
     }
@@ -188,6 +268,75 @@ void SolverSettings::SetParameter(SolverParameter eParam, 
OUString sValue)
                 m_sAlgorithm = sValue;
         }
         break;
+        case SP_SWARM_SIZE:
+            m_sSwarmSize = sValue;
+            break;
+        case SP_LEARNING_CYCLES:
+            m_sLearningCycles = sValue;
+            break;
+        case SP_GUESS_VARIABLE_RANGE:
+            m_sGuessVariableRange = sValue;
+            break;
+        case SP_VARIABLE_RANGE_THRESHOLD:
+            m_sVariableRangeThreshold = sValue;
+            break;
+        case SP_ACR_COMPARATOR:
+        {
+            if (sValue == "0" || sValue == "1")
+                m_sUseACRComparator = sValue;
+        }
+        break;
+        case SP_RND_STARTING_POINT:
+        {
+            if (sValue == "0" || sValue == "1")
+                m_sUseRandomStartingPoint = sValue;
+        }
+        break;
+        case SP_STRONGER_PRNG:
+        {
+            if (sValue == "0" || sValue == "1")
+                m_sUseStrongerPRNG = sValue;
+        }
+        break;
+        case SP_STAGNATION_LIMIT:
+            m_sStagnationLimit = sValue;
+            break;
+        case SP_STAGNATION_TOLERANCE:
+            m_sTolerance = sValue;
+            break;
+        case SP_ENHANCED_STATUS:
+        {
+            if (sValue == "0" || sValue == "1")
+                m_sEnhancedSolverStatus = sValue;
+        }
+        break;
+        case SP_AGENT_SWITCH_RATE:
+            m_sAgentSwitchRate = sValue;
+            break;
+        case SP_SCALING_MIN:
+            m_sScalingFactorMin = sValue;
+            break;
+        case SP_SCALING_MAX:
+            m_sScalingFactorMax = sValue;
+            break;
+        case SP_CROSSOVER_PROB:
+            m_sCrossoverProbability = sValue;
+            break;
+        case SP_COGNITIVE_CONST:
+            m_sCognitiveConstant = sValue;
+            break;
+        case SP_SOCIAL_CONST:
+            m_sSocialConstant = sValue;
+            break;
+        case SP_CONSTRICTION_COEFF:
+            m_sConstrictionCoeff = sValue;
+            break;
+        case SP_MUTATION_PROB:
+            m_sMutationProbability = sValue;
+            break;
+        case SP_LIBRARY_SIZE:
+            m_sLibrarySize = sValue;
+            break;
         default:
             break;
     }
@@ -321,12 +470,35 @@ void SolverSettings::SaveSolverSettings()
     sal_Int32 nConstrCount = m_aConstraints.size();
     WriteParamValue(SP_CONSTR_COUNT, OUString::number(nConstrCount));
 
+    // Solver engine options
     WriteParamValue(SP_INTEGER, m_sInteger);
     WriteParamValue(SP_NON_NEGATIVE, m_sNonNegative);
     WriteParamValue(SP_EPSILON_LEVEL, m_sEpsilonLevel);
     WriteParamValue(SP_LIMIT_BBDEPTH, m_sLimitBBDepth);
     WriteParamValue(SP_TIMEOUT, m_sTimeout);
     WriteParamValue(SP_ALGORITHM, m_sAlgorithm);
+    // Engine options common for DEPS and SCO
+    WriteParamValue(SP_SWARM_SIZE, m_sSwarmSize);
+    WriteParamValue(SP_LEARNING_CYCLES, m_sLearningCycles);
+    WriteParamValue(SP_GUESS_VARIABLE_RANGE, m_sGuessVariableRange);
+    WriteDoubleParamValue(SP_VARIABLE_RANGE_THRESHOLD, 
m_sVariableRangeThreshold);
+    WriteParamValue(SP_ACR_COMPARATOR, m_sUseACRComparator);
+    WriteParamValue(SP_RND_STARTING_POINT, m_sUseRandomStartingPoint);
+    WriteParamValue(SP_STRONGER_PRNG, m_sUseStrongerPRNG);
+    WriteParamValue(SP_STAGNATION_LIMIT, m_sStagnationLimit);
+    WriteDoubleParamValue(SP_STAGNATION_TOLERANCE, m_sTolerance);
+    WriteParamValue(SP_ENHANCED_STATUS, m_sEnhancedSolverStatus);
+    // DEPS Options
+    WriteDoubleParamValue(SP_AGENT_SWITCH_RATE, m_sAgentSwitchRate);
+    WriteDoubleParamValue(SP_SCALING_MIN, m_sScalingFactorMin);
+    WriteDoubleParamValue(SP_SCALING_MAX, m_sScalingFactorMax);
+    WriteDoubleParamValue(SP_CROSSOVER_PROB, m_sCrossoverProbability);
+    WriteDoubleParamValue(SP_COGNITIVE_CONST, m_sCognitiveConstant);
+    WriteDoubleParamValue(SP_SOCIAL_CONST, m_sSocialConstant);
+    WriteDoubleParamValue(SP_CONSTRICTION_COEFF, m_sConstrictionCoeff);
+    WriteDoubleParamValue(SP_MUTATION_PROB, m_sMutationProbability);
+    // SCO Options
+    WriteParamValue(SP_LIBRARY_SIZE, m_sLibrarySize);
 
     if (m_pDocShell)
         m_pDocShell->SetDocumentModified();
@@ -354,6 +526,26 @@ bool SolverSettings::ReadParamValue(SolverParameter 
eParam, OUString& rValue, bo
     return false;
 }
 
+// Reads a parameter value of type 'double' from the named range and into 
rValue
+bool SolverSettings::ReadDoubleParamValue(SolverParameter eParam, OUString& 
rValue)
+{
+    const auto iter = m_mNamedRanges.find(eParam);
+    assert(iter != m_mNamedRanges.end());
+    OUString sRange = iter->second;
+    ScRangeData* pRangeData
+        = 
m_pRangeName->findByUpperName(ScGlobal::getCharClass().uppercase(sRange));
+    if (pRangeData)
+    {
+        OUString sLocalizedValue = pRangeData->GetSymbol();
+        double fValue = rtl::math::stringToDouble(sLocalizedValue,
+                                                  
ScGlobal::getLocaleData().getNumDecimalSep()[0],
+                                                  
ScGlobal::getLocaleData().getNumThousandSep()[0]);
+        rValue = OUString::number(fValue);
+        return true;
+    }
+    return false;
+}
+
 /* Writes a parameter value to the file as a named range.
  * Argument bQuoted indicates whether the value should be enclosed with quotes 
or not (used
  * for string expressions that must be enclosed with quotes)
@@ -375,6 +567,22 @@ void SolverSettings::WriteParamValue(SolverParameter 
eParam, OUString sValue, bo
     m_pRangeName->insert(pNewEntry);
 }
 
+// Writes a parameter value of type 'double' to the file as a named range
+// The argument 'sValue' uses dot as decimal separator and needs to be 
localized before
+// being written to the file
+void SolverSettings::WriteDoubleParamValue(SolverParameter eParam, 
std::u16string_view sValue)
+{
+    const auto iter = m_mNamedRanges.find(eParam);
+    assert(iter != m_mNamedRanges.end());
+    OUString sRange = iter->second;
+    double fValue = rtl::math::stringToDouble(sValue, '.', ',');
+    OUString sLocalizedValue = rtl::math::doubleToUString(
+        fValue, rtl_math_StringFormat_Automatic, rtl_math_DecimalPlaces_Max,
+        ScGlobal::getLocaleData().getNumDecimalSep()[0], true);
+    ScRangeData* pNewEntry = new ScRangeData(m_rDoc, sRange, sLocalizedValue);
+    m_pRangeName->insert(pNewEntry);
+}
+
 void 
SolverSettings::GetEngineOptions(css::uno::Sequence<css::beans::PropertyValue>& 
aOptions)
 {
     sal_Int32 nOptionsSize = aOptions.getLength();
@@ -398,6 +606,12 @@ void 
SolverSettings::GetEngineOptions(css::uno::Sequence<css::beans::PropertyVal
                 pParamValues[i] = css::beans::PropertyValue(sLOParamName, -1, 
nValue,
                                                             
css::beans::PropertyState_DIRECT_VALUE);
             }
+            if (sParamType == "double")
+            {
+                css::uno::Any fValue(sParamValue.toDouble());
+                pParamValues[i] = css::beans::PropertyValue(sLOParamName, -1, 
fValue,
+                                                            
css::beans::PropertyState_DIRECT_VALUE);
+            }
             if (sParamType == "bool")
             {
                 // The parameter NonNegative is a special case for MS 
compatibility
@@ -438,6 +652,12 @@ void 
SolverSettings::SetEngineOptions(css::uno::Sequence<css::beans::PropertyVal
                 aProp.Value >>= nValue;
                 SetParameter(eParamId, OUString::number(nValue));
             }
+            if (sParamType == "double")
+            {
+                double fValue = 0;
+                aProp.Value >>= fValue;
+                SetParameter(eParamId, OUString::number(fValue));
+            }
             if (sParamType == "bool")
             {
                 bool bValue = false;
commit 73f8cdb207eef3b53ce0c207f22daffb2318e041
Author:     Michael Stahl <[email protected]>
AuthorDate: Mon Mar 4 12:47:15 2024 +0100
Commit:     Andras Timar <[email protected]>
CommitDate: Tue Mar 12 15:28:18 2024 +0100

    sw: UITest_writer_tests8 test_tdf150443 more tolerant
    
    This fails on Fedora 39, also in the libreoffice-24-2 branch, with:
        self.assertEqual(get_state_as_dict(xWriterEdit)["CurrentPage"], "4")
    AssertionError: '5' != '4'
    
    Presumably a font substitution changing metrics problem, as "NexusSansOT"
    is not found.
    
    It looks like the exact page doesn't matter for the test.
    
    Change-Id: I62eadb8321352ea47a0f8f83ab91fd50ff01f7e4
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/164353
    Tested-by: Jenkins
    Reviewed-by: Michael Stahl <[email protected]>
    (cherry picked from commit 0b21e2a404c114529376dc50764dc0286dafc745)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/164364
    Reviewed-by: Xisco Fauli <[email protected]>

diff --git a/sw/qa/uitest/writer_tests7/tdf150443.py 
b/sw/qa/uitest/writer_tests7/tdf150443.py
index fb39bd8a0375..91937551a4b3 100644
--- a/sw/qa/uitest/writer_tests7/tdf150443.py
+++ b/sw/qa/uitest/writer_tests7/tdf150443.py
@@ -26,7 +26,9 @@ class tdf150443(UITestCase):
                 xsearch = xDialog.getChild("search")
                 xsearch.executeAction("CLICK", tuple())  #first search
                 xToolkit.processEventsToIdle()
-                
self.assertEqual(get_state_as_dict(xWriterEdit)["CurrentPage"], "4")
+                page = get_state_as_dict(xWriterEdit)["CurrentPage"]
+                # page may depend on font subsitution, just check it moved
+                self.assertTrue(page == "4" or page == "5")
 
             # reject the tracked table row in Manage Changes dialog window
             with 
self.ui_test.execute_modeless_dialog_through_command(".uno:AcceptTrackedChanges",
 close_button="close") as xTrackDlg:
commit 32e3ca5697c7cebee91539b2711d975fcc0d4738
Author:     Mike Kaganski <[email protected]>
AuthorDate: Mon Mar 4 12:20:13 2024 +0600
Commit:     Andras Timar <[email protected]>
CommitDate: Tue Mar 12 15:28:18 2024 +0100

    tdf160017: make sure to emit the closing tags in correct order
    
    This reimplements how the starts and ends of attributes are stored in
    HTMLEndPosLst. Instead of a plain list, now it is a sorted map, with
    positions as keys, and a vector of HTMLStartEndPos* as values.
    
    In commit b94b1fe936ddc4a9b86fbeb9c9c6ab0fca52f0bc (CharBrd 9.1: HTML
    filters, 2013-09-08), the character borders attributes started to be
    set in a special order, in front of the position's other attributes,
    to allow merging them. But that created a problem of knowing in which
    order to close respective tags.
    
    The change here sorts the closing tags for the current node only when
    writing them. At this point, it is possible to consider the opening
    positions correctly.
    
    Change-Id: I466ffa1c0eb28874ded003035e0cf772e31585b3
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/164325
    Tested-by: Jenkins
    Reviewed-by: Mike Kaganski <[email protected]>
    Signed-off-by: Xisco Fauli <[email protected]>
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/164357

diff --git a/sw/qa/extras/htmlexport/data/char_border_and_font_color.fodt 
b/sw/qa/extras/htmlexport/data/char_border_and_font_color.fodt
new file mode 100644
index 000000000000..bda2ec63133c
--- /dev/null
+++ b/sw/qa/extras/htmlexport/data/char_border_and_font_color.fodt
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<office:document 
xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" 
xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" 
xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" 
xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" 
xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0"
 office:version="1.3" office:mimetype="application/vnd.oasis.opendocument.text">
+ <office:automatic-styles>
+  <style:style style:name="P1" style:family="paragraph">
+   <style:text-properties fo:color="#000000" loext:border="none"/>
+  </style:style>
+ </office:automatic-styles>
+ <office:body>
+  <office:text>
+   <text:p text:style-name="P1">foo</text:p>
+  </office:text>
+ </office:body>
+</office:document>
\ No newline at end of file
diff --git a/sw/qa/extras/htmlexport/htmlexport.cxx 
b/sw/qa/extras/htmlexport/htmlexport.cxx
index 1bd883be111f..de2e9da4c678 100644
--- a/sw/qa/extras/htmlexport/htmlexport.cxx
+++ b/sw/qa/extras/htmlexport/htmlexport.cxx
@@ -3032,6 +3032,27 @@ CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, 
testReqIF_NoBrClearForImageWrap)
         0);
 }
 
+CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testReqIF_Tdf160017_spanClosingOrder)
+{
+    // Given a document with a paragraph having explicit font color and 
character border properties:
+    createSwDoc("char_border_and_font_color.fodt");
+    // When exporting to reqif:
+    ExportToReqif();
+    // Without the fix, this would fail, because there was an extra closing 
</reqif-xhtml:span>
+    WrapReqifFromTempFile();
+}
+
+CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testHTML_Tdf160017_spanClosingOrder)
+{
+    // Given a document with a paragraph having explicit font color and 
character border properties:
+    createSwDoc("char_border_and_font_color.fodt");
+    // When exporting to HTML:
+    ExportToHTML();
+    // Parse it as XML (strict!)
+    // Without the fix, this would fail, because span and font elements closed 
in wrong order
+    CPPUNIT_ASSERT(parseXml(maTempFile));
+}
+
 CPPUNIT_PLUGIN_IMPLEMENT();
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/html/css1atr.cxx 
b/sw/source/filter/html/css1atr.cxx
index 30ba8d3c0a75..2587edfaf200 100644
--- a/sw/source/filter/html/css1atr.cxx
+++ b/sw/source/filter/html/css1atr.cxx
@@ -3301,14 +3301,16 @@ SwHTMLWriter& OutCSS1_SvxBox( SwHTMLWriter& rWrt, const 
SfxPoolItem& rHt )
 
     if( rHt.Which() == RES_CHRATR_BOX )
     {
+        constexpr std::string_view inline_block("inline-block");
         if( rWrt.m_bTagOn )
         {
             // Inline-block to make the line height changing correspond to the 
character border
-            rWrt.OutCSS1_PropertyAscii(sCSS1_P_display, "inline-block");
+            rWrt.OutCSS1_PropertyAscii(sCSS1_P_display, inline_block);
         }
         else
         {
-            HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), 
Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_span), false );
+            if (!IgnorePropertyForReqIF(rWrt.mbReqIF, sCSS1_P_display, 
inline_block))
+                HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), 
Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_span), false );
             return rWrt;
         }
     }
diff --git a/sw/source/filter/html/htmlatr.cxx 
b/sw/source/filter/html/htmlatr.cxx
index 9f67d1ee0304..c880082018f1 100644
--- a/sw/source/filter/html/htmlatr.cxx
+++ b/sw/source/filter/html/htmlatr.cxx
@@ -1058,7 +1058,7 @@ public:
 
     HTMLStartEndPos( const SfxPoolItem& rItem, sal_Int32 nStt, sal_Int32 nE );
 
-    const SfxPoolItem* GetItem() const { return m_pItem.get(); }
+    const SfxPoolItem& GetItem() const { return *m_pItem; }
 
     void SetStart(sal_Int32 nStt) { m_nStart = nStt; }
     sal_Int32 GetStart() const { return m_nStart; }
@@ -1075,7 +1075,7 @@ HTMLStartEndPos::HTMLStartEndPos(const SfxPoolItem& 
rItem, sal_Int32 nStt, sal_I
     , m_pItem(rItem.Clone())
 {}
 
-typedef std::vector<HTMLStartEndPos *> HTMLStartEndPositions;
+typedef std::map<sal_Int32, std::vector<HTMLStartEndPos*>> 
HTMLStartEndPositions;
 
 namespace {
 
@@ -1091,8 +1091,8 @@ enum HTMLOnOffState { HTML_NOT_SUPPORTED,   // 
unsupported Attribute
 
 class HTMLEndPosLst
 {
-    HTMLStartEndPositions m_aStartLst; // list, sorted for start positions
-    HTMLStartEndPositions m_aEndLst; // list, sorted for end positions
+    HTMLStartEndPositions m_aStartLst; // list, each position's elements 
sorted by appearance order
+    HTMLStartEndPositions m_aEndLst; // list, no sort of elements in position
     std::deque<sal_Int32> m_aScriptChgLst; // positions where script changes
         // 0 is not contained in this list,
         // but the text length
@@ -1110,8 +1110,7 @@ class HTMLEndPosLst
 
     // Insert/remove a SttEndPos in/from the Start and End lists.
     // The end position is known.
-    void InsertItem_( HTMLStartEndPos *pPos, HTMLStartEndPositions::size_type 
nEndPos );
-    void RemoveItem_( HTMLStartEndPositions::size_type nEndPos );
+    void InsertItem_(HTMLStartEndPos* pPos);
 
     // determine the 'type' of the attribute
     HTMLOnOffState GetHTMLItemState( const SfxPoolItem& rItem );
@@ -1125,8 +1124,7 @@ class HTMLEndPosLst
                                           sal_Int32 nEndPos );
 
     // adapt the end of a split item
-    void FixSplittedItem( HTMLStartEndPos *pPos, sal_Int32 nNewEnd,
-                            HTMLStartEndPositions::size_type nStartPos );
+    void FixSplittedItem(HTMLStartEndPos* pPos, sal_Int32 nNewEnd);
 
     // insert an attribute in the lists and, if necessary, split it
     void InsertItem( const SfxPoolItem& rItem, sal_Int32 nStart,
@@ -1144,6 +1142,8 @@ class HTMLEndPosLst
     const SwHTMLFormatInfo *GetFormatInfo( const SwFormat& rFormat,
                                      SwHTMLFormatInfos& rFormatInfos );
 
+    void OutEndAttrs(SwHTMLWriter& rWrt, std::vector<HTMLStartEndPos*>& 
posItems);
+
 public:
 
     HTMLEndPosLst( SwDoc *pDoc, SwDoc* pTemplate, std::optional<Color> 
xDfltColor,
@@ -1169,36 +1169,46 @@ public:
     bool IsHTMLMode(sal_uLong nMode) const { return (m_nHTMLMode & nMode) != 
0; }
 };
 
-}
-
-void HTMLEndPosLst::InsertItem_( HTMLStartEndPos *pPos, 
HTMLStartEndPositions::size_type nEndPos )
+struct SortEnds
 {
-    // Insert the attribute in the Start list behind all attributes that
-    // were started before, or at the same position.
-    sal_Int32 nStart = pPos->GetStart();
-    HTMLStartEndPositions::size_type i {0};
+    HTMLStartEndPositions& m_startList;
+    SortEnds(HTMLStartEndPositions& startList) : m_startList(startList) {}
+    bool operator()(const HTMLStartEndPos* p1, const HTMLStartEndPos* p2)
+    {
+        // if p1 start after p2, then it ends before
+        if (p1->GetStart() > p2->GetStart())
+            return true;
+        if (p1->GetStart() < p2->GetStart())
+            return false;
+        for (const auto p : m_startList[p1->GetStart()])
+        {
+            if (p == p1)
+                return false;
+            if (p == p2)
+                return true;
+        }
+        assert(!"Neither p1 nor p2 found in their start list");
+        return false;
+    }
+};
 
-    while (i < m_aStartLst.size() && m_aStartLst[i]->GetStart() <= nStart)
-        ++i;
-    m_aStartLst.insert(m_aStartLst.begin() + i, pPos);
+#ifndef NDEBUG
+bool IsEmpty(const HTMLStartEndPositions& l)
+{
+    return std::find_if(l.begin(), l.end(), [](auto& i) { return 
!i.second.empty(); }) == l.end();
+}
+#endif
 
-    // the position in the End list was supplied
-    m_aEndLst.insert(m_aEndLst.begin() + nEndPos, pPos);
 }
 
-void HTMLEndPosLst::RemoveItem_( HTMLStartEndPositions::size_type nEndPos )
+void HTMLEndPosLst::InsertItem_(HTMLStartEndPos* pPos)
 {
-    HTMLStartEndPos* pPos = m_aEndLst[nEndPos];
+    // Character border attribute must be the first which is written out 
because of border merge.
+    auto& posItems1 = m_aStartLst[pPos->GetStart()];
+    auto it = pPos->GetItem().Which() == RES_CHRATR_BOX ? posItems1.begin() : 
posItems1.end();
+    posItems1.insert(it, pPos);
 
-    // now, we are looking for it in the Start list
-    HTMLStartEndPositions::iterator it = std::find(m_aStartLst.begin(), 
m_aStartLst.end(), pPos);
-    OSL_ENSURE(it != m_aStartLst.end(), "Item not found in Start List!");
-    if (it != m_aStartLst.end())
-        m_aStartLst.erase(it);
-
-    m_aEndLst.erase(m_aEndLst.begin() + nEndPos);
-
-    delete pPos;
+    m_aEndLst[pPos->GetEnd()].push_back(pPos);
 }
 
 HTMLOnOffState HTMLEndPosLst::GetHTMLItemState( const SfxPoolItem& rItem )
@@ -1352,23 +1362,25 @@ HTMLOnOffState HTMLEndPosLst::GetHTMLItemState( const 
SfxPoolItem& rItem )
 
 bool HTMLEndPosLst::ExistsOnTagItem( sal_uInt16 nWhich, sal_Int32 nPos )
 {
-    for (auto pTest : m_aStartLst)
+    for (const auto& [startPos, items] : m_aStartLst)
     {
-        if( pTest->GetStart() > nPos )
+        if (startPos > nPos)
         {
             // this attribute, and all attributes that follow, start later
             break;
         }
-        else if( pTest->GetEnd() > nPos )
+
+        for (const auto* pTest : items)
         {
-            // the attribute starts before, or at, the current position and
-            // ends after it
-            const SfxPoolItem *pItem = pTest->GetItem();
-            if( pItem->Which() == nWhich &&
-                HTML_ON_VALUE == GetHTMLItemState(*pItem) )
+            if (pTest->GetEnd() > nPos)
             {
-                // an OnTag attribute was found
-                return true;
+                // the attribute starts before, or at, the current position 
and ends after it
+                const SfxPoolItem& rItem = pTest->GetItem();
+                if (rItem.Which() == nWhich && HTML_ON_VALUE == 
GetHTMLItemState(rItem))
+                {
+                    // an OnTag attribute was found
+                    return true;
+                }
             }
         }
     }
@@ -1386,24 +1398,17 @@ bool HTMLEndPosLst::ExistsOffTagItem( sal_uInt16 
nWhich, sal_Int32 nStartPos,
         return false;
     }
 
-    for (auto pTest : m_aStartLst)
+    for (const auto* pTest : m_aStartLst[nStartPos])
     {
-        if( pTest->GetStart() > nStartPos )
-        {
-            // this attribute, and all attributes that follow, start later
-            break;
-        }
-        else if( pTest->GetStart()==nStartPos &&
-                 pTest->GetEnd()==nEndPos )
+        if (pTest->GetEnd() == nEndPos)
         {
-            // the attribute starts before or at the current position and
-            // ends after it
-            const SfxPoolItem *pItem = pTest->GetItem();
-            sal_uInt16 nTstWhich = pItem->Which();
+            // the attribute starts before or at the current position and ends 
after it
+            const SfxPoolItem& rItem = pTest->GetItem();
+            sal_uInt16 nTstWhich = rItem.Which();
             if( (nTstWhich == RES_CHRATR_CROSSEDOUT ||
                  nTstWhich == RES_CHRATR_UNDERLINE ||
                  nTstWhich == RES_CHRATR_BLINK) &&
-                HTML_OFF_VALUE == GetHTMLItemState(*pItem) )
+                HTML_OFF_VALUE == GetHTMLItemState(rItem) )
             {
                 // an OffTag attribute was found that is exported the same
                 // way as the current item
@@ -1415,55 +1420,51 @@ bool HTMLEndPosLst::ExistsOffTagItem( sal_uInt16 
nWhich, sal_Int32 nStartPos,
     return false;
 }
 
-void HTMLEndPosLst::FixSplittedItem( HTMLStartEndPos *pPos, sal_Int32 nNewEnd,
-                                        HTMLStartEndPositions::size_type 
nStartPos )
+void HTMLEndPosLst::FixSplittedItem(HTMLStartEndPos* pPos, sal_Int32 nNewEnd)
 {
+    // remove the item from the End list
+    std::erase(m_aEndLst[pPos->GetEnd()], pPos);
     // fix the end position accordingly
     pPos->SetEnd( nNewEnd );
-
-    // remove the item from the End list
-    HTMLStartEndPositions::iterator it = std::find(m_aEndLst.begin(), 
m_aEndLst.end(), pPos);
-    OSL_ENSURE(it != m_aEndLst.end(), "Item not found in End List!");
-    if (it != m_aEndLst.end())
-        m_aEndLst.erase(it);
-
-    // from now on, it is closed as the last one at the corresponding position
-    HTMLStartEndPositions::size_type nEndPos {0};
-    while (nEndPos < m_aEndLst.size() && m_aEndLst[nEndPos]->GetEnd() <= 
nNewEnd)
-        ++nEndPos;
-    m_aEndLst.insert(m_aEndLst.begin() + nEndPos, pPos);
+    // from now on, it is closed at the corresponding position
+    m_aEndLst[nNewEnd].push_back(pPos);
 
     // now, adjust the attributes that got started afterwards
-    for (HTMLStartEndPositions::size_type i = nStartPos + 1; i < 
m_aStartLst.size(); ++i)
+    const sal_Int32 nPos = pPos->GetStart();
+    for (const auto& [startPos, items] : m_aStartLst)
     {
-        HTMLStartEndPos* pTest = m_aStartLst[i];
-        sal_Int32 nTestEnd = pTest->GetEnd();
-        if( pTest->GetStart() >= nNewEnd )
-        {
-            // the Test attribute and all the following ones start, after the
-            // split attribute ends
+        if (startPos < nPos)
+            continue;
+
+        if (startPos >= nNewEnd)
             break;
+
+        auto it = items.begin();
+        if (startPos == nPos)
+        {
+            it = std::find(items.begin(), items.end(), pPos);
+            if (it != items.end())
+                ++it;
         }
-        else if( nTestEnd > nNewEnd )
+        for (; it != items.end(); ++it)
         {
+            HTMLStartEndPos* pTest = *it;
+            const sal_Int32 nTestEnd = pTest->GetEnd();
+            if (nTestEnd <= nNewEnd)
+                continue;
+
             // the Test attribute starts before the split attribute
             // ends, and ends afterwards, i.e., it must be split, as well
 
+            // remove the attribute from the End list
+            std::erase(m_aEndLst[pTest->GetEnd()], pTest);
             // set the new end
             pTest->SetEnd( nNewEnd );
-
-            // remove the attribute from the End list
-            it = std::find(m_aEndLst.begin(), m_aEndLst.end(), pTest);
-            OSL_ENSURE(it != m_aEndLst.end(), "Item not found in End List!");
-            if (it != m_aEndLst.end())
-                m_aEndLst.erase(it);
-
-            // it now ends as the first attribute in the respective position.
-            // We already know this position in the End list.
-            m_aEndLst.insert(m_aEndLst.begin() + nEndPos, pTest);
+            // it now ends in the respective position.
+            m_aEndLst[nNewEnd].push_back(pTest);
 
             // insert the 'rest' of the attribute
-            InsertItem( *pTest->GetItem(), nNewEnd, nTestEnd );
+            InsertItem( pTest->GetItem(), nNewEnd, nTestEnd );
         }
     }
 }
@@ -1471,36 +1472,38 @@ void HTMLEndPosLst::FixSplittedItem( HTMLStartEndPos 
*pPos, sal_Int32 nNewEnd,
 void HTMLEndPosLst::InsertItem( const SfxPoolItem& rItem, sal_Int32 nStart,
                                                           sal_Int32 nEnd )
 {
-    HTMLStartEndPositions::size_type i;
-    for (i = 0; i < m_aEndLst.size(); i++)
+    assert(nStart < nEnd);
+
+    for (auto& [endPos, items] : m_aEndLst)
     {
-        HTMLStartEndPos* pTest = m_aEndLst[i];
-        sal_Int32 nTestEnd = pTest->GetEnd();
-        if( nTestEnd <= nStart )
+        if (endPos <= nStart)
         {
             // the Test attribute ends, before the new one starts
             continue;
         }
-        else if( nTestEnd < nEnd )
+        if (endPos >= nEnd)
+        {
+            // the Test attribute (and all that follow) ends, before the new
+            // one ends
+            break;
+        }
+
+        std::sort(items.begin(), items.end(), SortEnds(m_aStartLst));
+
+        for (HTMLStartEndPos* pTest : items)
         {
             if( pTest->GetStart() < nStart )
             {
                 // the Test attribute ends, before the new one ends. Thus, the
                 // new attribute must be split.
-                InsertItem_( new HTMLStartEndPos( rItem, nStart, nTestEnd ), i 
);
-                nStart = nTestEnd;
+                InsertItem_(new HTMLStartEndPos(rItem, nStart, endPos));
+                nStart = endPos;
             }
         }
-        else
-        {
-            // the Test attribute (and all that follow) ends, before the new
-            // one ends
-            break;
-        }
     }
 
     // one attribute must still be inserted
-    InsertItem_( new HTMLStartEndPos( rItem, nStart, nEnd ), i );
+    InsertItem_(new HTMLStartEndPos(rItem, nStart, nEnd));
 }
 
 void HTMLEndPosLst::SplitItem( const SfxPoolItem& rItem, sal_Int32 nStart,
@@ -1511,59 +1514,47 @@ void HTMLEndPosLst::SplitItem( const SfxPoolItem& 
rItem, sal_Int32 nStart,
     // first, we must search for the old items by using the start list and
     // determine the new item range
 
-    for (HTMLStartEndPositions::size_type i = 0; i < m_aStartLst.size(); ++i)
+    for (auto& [nTestStart, items] : m_aStartLst)
     {
-        HTMLStartEndPos* pTest = m_aStartLst[i];
-        sal_Int32 nTestStart = pTest->GetStart();
-        sal_Int32 nTestEnd = pTest->GetEnd();
-
         if( nTestStart >= nEnd )
         {
             // this attribute, and all that follow, start later
             break;
         }
-        else if( nTestEnd > nStart )
+
+        for (auto it = items.begin(); it != items.end();)
         {
+            HTMLStartEndPos* pTest = *it;
+            sal_Int32 nTestEnd = pTest->GetEnd();
+            if (nTestEnd <= nStart)
+                continue;
+
             // the Test attribute ends in the range that must be deleted
-            const SfxPoolItem *pItem = pTest->GetItem();
+            const SfxPoolItem& rTestItem = pTest->GetItem();
 
             // only the corresponding OnTag attributes have to be considered
-            if( pItem->Which() == nWhich &&
-                HTML_ON_VALUE == GetHTMLItemState( *pItem ) )
+            if (rTestItem.Which() == nWhich && HTML_ON_VALUE == 
GetHTMLItemState(rTestItem))
             {
-                bool bDelete = true;
+                // if necessary, insert the second part of the split
+                // attribute
+                if (nTestEnd > nEnd)
+                    InsertItem(pTest->GetItem(), nEnd, nTestEnd);
 
-                if( nTestStart < nStart )
-                {
-                    // the start of the new attribute corresponds to the new
-                    // end of the attribute
-                    FixSplittedItem( pTest, nStart, i );
-                    bDelete = false;
-                }
-                else
+                if (nTestStart >= nStart)
                 {
                     // the Test item only starts after the new end of the
                     // attribute. Therefore, it can be completely erased.
-                    m_aStartLst.erase(m_aStartLst.begin() + i);
-                    i--;
-
-                    HTMLStartEndPositions::iterator it
-                        = std::find(m_aEndLst.begin(), m_aEndLst.end(), pTest);
-                    OSL_ENSURE(it != m_aEndLst.end(), "Item not found in End 
List!");
-                    if (it != m_aEndLst.end())
-                        m_aEndLst.erase(it);
-                }
-
-                // if necessary, insert the second part of the split
-                // attribute
-                if( nTestEnd > nEnd )
-                {
-                    InsertItem( *pTest->GetItem(), nEnd, nTestEnd );
+                    it = items.erase(it);
+                    std::erase(m_aEndLst[pTest->GetEnd()], pTest);
+                    delete pTest;
+                    continue;
                 }
 
-                if( bDelete )
-                    delete pTest;
+                // the start of the new attribute corresponds to the new
+                // end of the attribute
+                FixSplittedItem(pTest, nStart);
             }
+            ++it;
         }
     }
 }
@@ -1611,8 +1602,8 @@ HTMLEndPosLst::HTMLEndPosLst(SwDoc* pD, SwDoc* pTempl, 
std::optional<Color> xDfl
 
 HTMLEndPosLst::~HTMLEndPosLst()
 {
-    OSL_ENSURE(m_aStartLst.empty(), "Start List not empty in destructor");
-    OSL_ENSURE(m_aEndLst.empty(), "End List not empty in destructor");
+    assert(IsEmpty(m_aStartLst) && "Start List not empty in destructor");
+    assert(IsEmpty(m_aEndLst) && "End List not empty in destructor");
 }
 
 void HTMLEndPosLst::InsertNoScript( const SfxPoolItem& rItem,
@@ -1900,53 +1891,25 @@ void HTMLEndPosLst::OutStartAttrs( SwHTMLWriter& rWrt, 
sal_Int32 nPos )
 {
     rWrt.m_bTagOn = true;
 
-    // Character border attribute must be the first which is written out
-    // because of border merge.
-    HTMLStartEndPositions::size_type nCharBoxIndex = 0;
-    while (nCharBoxIndex < m_aStartLst.size()
-           && m_aStartLst[nCharBoxIndex]->GetItem()->Which() != RES_CHRATR_BOX)
-    {
-        ++nCharBoxIndex;
-    }
-
+    auto it = m_aStartLst.find(nPos);
+    if (it == m_aStartLst.end())
+        return;
     // the attributes of the start list are sorted in ascending order
-    for (HTMLStartEndPositions::size_type i = 0; i < m_aStartLst.size(); ++i)
+    for (HTMLStartEndPos* pPos : it->second)
     {
-        HTMLStartEndPos *pPos = nullptr;
-        if (nCharBoxIndex < m_aStartLst.size())
-        {
-            if( i == 0 )
-                pPos = m_aStartLst[nCharBoxIndex];
-            else if( i == nCharBoxIndex )
-                pPos = m_aStartLst[0];
-            else
-                pPos = m_aStartLst[i];
-        }
-        else
-            pPos = m_aStartLst[i];
-
-        sal_Int32 nStart = pPos->GetStart();
-        if( nStart > nPos )
-        {
-            // this attribute, and all that follow, will be opened later on
-            break;
-        }
-        else if( nStart == nPos )
+        // output the attribute
+        sal_uInt16 nCSS1Script = rWrt.m_nCSS1Script;
+        sal_uInt16 nWhich = pPos->GetItem().Which();
+        if( RES_TXTATR_CHARFMT == nWhich ||
+            RES_TXTATR_INETFMT == nWhich ||
+             RES_PARATR_DROP == nWhich )
         {
-            // output the attribute
-            sal_uInt16 nCSS1Script = rWrt.m_nCSS1Script;
-            sal_uInt16 nWhich = pPos->GetItem()->Which();
-            if( RES_TXTATR_CHARFMT == nWhich ||
-                RES_TXTATR_INETFMT == nWhich ||
-                 RES_PARATR_DROP == nWhich )
-            {
-                rWrt.m_nCSS1Script = GetScriptAtPos( nPos, nCSS1Script );
-            }
-            HTMLOutFuncs::FlushToAscii( rWrt.Strm() ); // was one time only - 
do we still need it?
-            Out( aHTMLAttrFnTab, *pPos->GetItem(), rWrt );
-            rWrt.maStartedAttributes[pPos->GetItem()->Which()]++;
-            rWrt.m_nCSS1Script = nCSS1Script;
+            rWrt.m_nCSS1Script = GetScriptAtPos( nPos, nCSS1Script );
         }
+        HTMLOutFuncs::FlushToAscii( rWrt.Strm() ); // was one time only - do 
we still need it?
+        Out( aHTMLAttrFnTab, pPos->GetItem(), rWrt );
+        rWrt.maStartedAttributes[pPos->GetItem().Which()]++;
+        rWrt.m_nCSS1Script = nCSS1Script;
     }
 }
 
@@ -1954,59 +1917,55 @@ void HTMLEndPosLst::OutEndAttrs( SwHTMLWriter& rWrt, 
sal_Int32 nPos )
 {
     rWrt.m_bTagOn = false;
 
-    // the attributes in the End list are sorted in ascending order
-    HTMLStartEndPositions::size_type i {0};
-    while (i < m_aEndLst.size())
+    if (nPos == SAL_MAX_INT32)
+    {
+        for (auto& element : m_aEndLst)
+            OutEndAttrs(rWrt, element.second);
+    }
+    else
     {
-        HTMLStartEndPos* pPos = m_aEndLst[i];
-        sal_Int32 nEnd = pPos->GetEnd();
+        auto it = m_aEndLst.find(nPos);
+        if (it != m_aEndLst.end())
+            OutEndAttrs(rWrt, it->second);
+    }
+}
 
-        if( SAL_MAX_INT32 == nPos || nEnd == nPos )
+void HTMLEndPosLst::OutEndAttrs(SwHTMLWriter& rWrt, 
std::vector<HTMLStartEndPos*>& posItems)
+{
+    std::sort(posItems.begin(), posItems.end(), SortEnds(m_aStartLst));
+    for (auto it = posItems.begin(); it != posItems.end(); it = 
posItems.erase(it))
+    {
+        HTMLStartEndPos* pPos = *it;
+        HTMLOutFuncs::FlushToAscii( rWrt.Strm() ); // was one time only - do 
we still need it?
+        // Skip closing span if next character span has the same border 
(border merge)
+        bool bSkipOut = false;
+        if( pPos->GetItem().Which() == RES_CHRATR_BOX )
         {
-            HTMLOutFuncs::FlushToAscii( rWrt.Strm() ); // was one time only - 
do we still need it?
-            // Skip closing span if next character span has the same border 
(border merge)
-            bool bSkipOut = false;
-            if( pPos->GetItem()->Which() == RES_CHRATR_BOX )
+            auto& startPosItems = m_aStartLst[pPos->GetEnd()];
+            for (auto it2 = startPosItems.begin(); it2 != startPosItems.end(); 
++it2)
             {
-                HTMLStartEndPositions::iterator it
-                    = std::find(m_aStartLst.begin(), m_aStartLst.end(), pPos);
-                OSL_ENSURE(it != m_aStartLst.end(), "Item not found in Start 
List!");
-                if (it != m_aStartLst.end())
-                    ++it;
-                while (it != m_aStartLst.end())
+                HTMLStartEndPos* pEndPos = *it2;
+                if( pEndPos->GetItem().Which() == RES_CHRATR_BOX &&
+                    static_cast<const SvxBoxItem&>(pEndPos->GetItem()) ==
+                    static_cast<const SvxBoxItem&>(pPos->GetItem()) )
                 {
-                    HTMLStartEndPos *pEndPos = *it;
-                    if( pEndPos->GetItem()->Which() == RES_CHRATR_BOX &&
-                        *static_cast<const SvxBoxItem*>(pEndPos->GetItem()) ==
-                        *static_cast<const SvxBoxItem*>(pPos->GetItem()) )
-                    {
-                        pEndPos->SetStart(pPos->GetStart());
-                        bSkipOut = true;
-                        break;
-                    }
-                    ++it;
+                    startPosItems.erase(it2);
+                    pEndPos->SetStart(pPos->GetStart());
+                    auto& oldStartPosItems = m_aStartLst[pEndPos->GetStart()];
+                    oldStartPosItems.insert(oldStartPosItems.begin(), pEndPos);
+                    bSkipOut = true;
+                    break;
                 }
             }
-            if( !bSkipOut )
-            {
-                Out( aHTMLAttrFnTab, *pPos->GetItem(), rWrt );
-                rWrt.maStartedAttributes[pPos->GetItem()->Which()]--;
-            }
-            RemoveItem_( i );
-        }
-        else if( nEnd > nPos )
-        {
-            // this attribute, and all that follow, are closed later on
-            break;
         }
-        else
+        if( !bSkipOut )
         {
-            // The attribute is closed before the current position. This
-            // is not allowed, but we can handle it anyway.
-            OSL_ENSURE( nEnd >= nPos,
-                    "The attribute should've been closed a long time ago" );
-            i++;
+            Out( aHTMLAttrFnTab, pPos->GetItem(), rWrt );
+            rWrt.maStartedAttributes[pPos->GetItem().Which()]--;
         }
+
+        std::erase(m_aStartLst[pPos->GetStart()], pPos);
+        delete pPos;
     }
 }
 
commit 6018ba7cddd94be1ba932387dce9cf3a032001e3
Author:     Laurent Balland <[email protected]>
AuthorDate: Thu Dec 7 19:38:54 2023 +0100
Commit:     Andras Timar <[email protected]>
CommitDate: Tue Mar 12 15:28:18 2024 +0100

    tdf#129701 Skip empty cell: treat last column
    
    When pasting data as text, with "Skip empty cells" not checked, empty
    cells in the last column of the source do not empty cells in
    destination, because there is nothing anymore to paste and while loop
    stops.
    This commit adds a flag to check if the last cell of each line is empty,
    and treat it if "Skip empty cells" is not checked.
    
    Add UITest to Copy data with empty cells;
    Paste Unformatted text, without "Skip empty cells";
    Check data with empty cells in last column.
    
    Change-Id: Idec823fb1c27b803f49c6d13d6c757ef0f41e437
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/160951
    Tested-by: Jenkins
    Reviewed-by: Laurent Balland <[email protected]>
    (cherry picked from commit e66ffda7c7f1e78dbca082ff7549ded121dc7356)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/163536
    Reviewed-by: Christian Lohmaier <[email protected]>

diff --git a/sc/qa/uitest/data/tdf129701.ods b/sc/qa/uitest/data/tdf129701.ods
new file mode 100644
index 000000000000..94407fcd566e
Binary files /dev/null and b/sc/qa/uitest/data/tdf129701.ods differ
diff --git a/sc/qa/uitest/pasteSpecial/tdf129701-PasteUnformated.py 
b/sc/qa/uitest/pasteSpecial/tdf129701-PasteUnformated.py
new file mode 100644
index 000000000000..596cd62f8bc1
--- /dev/null
+++ b/sc/qa/uitest/pasteSpecial/tdf129701-PasteUnformated.py
@@ -0,0 +1,70 @@
+# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+from uitest.framework import UITestCase
+from libreoffice.uno.propertyvalue import mkPropertyValues
+from uitest.uihelper.common import get_url_for_data_file
+from uitest.uihelper.common import get_state_as_dict
+from libreoffice.calc.document import get_cell_by_position
+
+class tdf129701(UITestCase):
+
+    def test_tdf129701(self):
+
+        with self.ui_test.load_file(get_url_for_data_file("tdf129701.ods")):
+            xCalcDoc = self.xUITest.getTopFocusWindow()
+            gridwin = xCalcDoc.getChild("grid_window")
+            gridwin.executeAction("SELECT", mkPropertyValues({"RANGE": 
"D21:F25"}))
+            self.xUITest.executeCommand(".uno:Copy")
+
+            gridwin.executeAction("SELECT", mkPropertyValues({"CELL": "J4"}))
+            with 
self.ui_test.execute_dialog_through_command(".uno:PasteUnformatted", 
close_button="ok") as xDialog:
+
+                xSkipEmtyCells   = xDialog.getChild("skipemptycells")
+                xSeparatedBy     = xDialog.getChild("toseparatedby")
+                xTab             = xDialog.getChild("tab")
+                xMergeDelimiters = xDialog.getChild("mergedelimiters")
+
+                xSeparatedBy.executeAction("CLICK", tuple())
+                if get_state_as_dict(xTab)['Selected'] == 'false':
+                    xTab.executeAction("CLICK", tuple())
+                if get_state_as_dict(xMergeDelimiters)['Selected'] == 'true':
+                    xMergeDelimiters.executeAction("CLICK", tuple())
+                if get_state_as_dict(xSkipEmtyCells)['Selected'] == 'true':
+                    xSkipEmtyCells.executeAction("CLICK", tuple())
+                # Check wether Skip empty cells is unselected
+                self.assertEqual('false', 
get_state_as_dict(xSkipEmtyCells)['Selected'])
+
+            document = self.ui_test.get_component()
+            # Without the fix in place, this test would have failed with
+            # non empty cells in column 11
+
+            self.assertEqual( "x1", get_cell_by_position(document, 0, 9, 
3).getString())
+            self.assertEqual( ""  , get_cell_by_position(document, 0,10, 
3).getString())
+            self.assertEqual( ""  , get_cell_by_position(document, 0,11, 
3).getString())
+            self.assertEqual("A16", get_cell_by_position(document, 0,12, 
3).getString())
+            self.assertEqual( ""  , get_cell_by_position(document, 0, 9, 
4).getString())
+            self.assertEqual( "x2", get_cell_by_position(document, 0,10, 
4).getString())
+            self.assertEqual( ""  , get_cell_by_position(document, 0,11, 
4).getString())
+            self.assertEqual("A17", get_cell_by_position(document, 0,12, 
4).getString())
+            self.assertEqual( ""  , get_cell_by_position(document, 0, 9, 
5).getString())
+            self.assertEqual( ""  , get_cell_by_position(document, 0,10, 
5).getString())
+            self.assertEqual( "x3", get_cell_by_position(document, 0,11, 
5).getString())
+            self.assertEqual("A18", get_cell_by_position(document, 0,12, 
5).getString())
+            self.assertEqual( ""  , get_cell_by_position(document, 0, 9, 
6).getString())
+            self.assertEqual( "x4", get_cell_by_position(document, 0,10, 
6).getString())
+            self.assertEqual( ""  , get_cell_by_position(document, 0,11, 
6).getString())
+            self.assertEqual("A19", get_cell_by_position(document, 0,12, 
6).getString())
+            self.assertEqual( "x5", get_cell_by_position(document, 0, 9, 
7).getString())
+            self.assertEqual( "x6", get_cell_by_position(document, 0,10, 
7).getString())
+            self.assertEqual( "x7", get_cell_by_position(document, 0,11, 
7).getString())
+            self.assertEqual("A20", get_cell_by_position(document, 0,12, 
7).getString())
+
+            self.ui_test.close_doc()
+
+# vim: set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/sc/source/ui/docshell/impex.cxx b/sc/source/ui/docshell/impex.cxx
index 4a585657de1a..8e6315db9419 100644
--- a/sc/source/ui/docshell/impex.cxx
+++ b/sc/source/ui/docshell/impex.cxx
@@ -1662,6 +1662,7 @@ bool ScImportExport::ExtText2Doc( SvStream& rStrm )
     ScDocumentImport aDocImport(rDoc);
     do
     {
+        SCCOL nLastCol = nEndCol; // tdf#129701 preserve value of nEndCol
         for( ;; )
         {
             aLine = ReadCsvLine(rStrm, !bFixed, aSeps, cStr, cDetectSep);
@@ -1745,11 +1746,14 @@ bool ScImportExport::ExtText2Doc( SvStream& rStrm )
                 SCCOL nSourceCol = 0;
                 sal_uInt16 nInfoStart = 0;
                 const sal_Unicode* p = aLine.getStr();
+                // tdf#129701 if there is only one column, and user wants to 
treat empty cells,
+                // we need to detect *p = null
+                bool bIsLastColEmpty = !(*p) && !bSkipEmptyCells && 
!bDetermineRange;
                 // Yes, the check is nCol<=rDoc.MaxCol()+1, +1 because it is 
only an
                 // overflow if there is really data following to be put behind
                 // the last column, which doesn't happen if info is
                 // SC_COL_SKIP.
-                while (*p && nCol <= rDoc.MaxCol()+1)
+                while ( (*p || bIsLastColEmpty) && nCol <= rDoc.MaxCol()+1)
                 {
                     bool bIsQuoted = false;
                     p = ScImportExport::ScanNextFieldFromString( p, aCell,
@@ -1780,9 +1784,18 @@ bool ScImportExport::ExtText2Doc( SvStream& rStrm )
                                 aTransliteration, aCalendar,
                                 pEnglishTransliteration.get(), 
pEnglishCalendar.get());
                         }
-                        ++nCol;
-                    }
+                        if (bIsLastColEmpty)
+                        {
+                            bIsLastColEmpty = false; // toggle to stop
+                        }
+                        else
+                        {
+                            ++nCol;
+                            // tdf#129701 detect if there is a last empty 
column when we need it
+                            bIsLastColEmpty = !(*p) && !bSkipEmptyCells && 
!bDetermineRange && nCol == nLastCol;
+                        }
 
+                    }
                     ++nSourceCol;
                 }
             }
commit c69758a45db34649e338067c24416cbc715cee8f
Author:     Michael Stahl <[email protected]>
AuthorDate: Thu Feb 15 13:06:32 2024 +0100
Commit:     Andras Timar <[email protected]>
CommitDate: Tue Mar 12 15:28:18 2024 +0100

    tdf#159502 remove --without-system-libxml from LibreOfficeLinux.conf
    
    configure.ac explicitly excludes libxml from --without-system-libs but
    this distro-config "helpfully" adds it explicitly, when the system
    already contains a usable libxml/libxslt anyway.
    
    Change-Id: I79d65d9ed9185277ffde8624efc152b290a6ffb3
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/163438
    Tested-by: Jenkins
    Reviewed-by: Michael Stahl <[email protected]>
    (cherry picked from commit 6f6364e572a0570fb097fec098314bffadc5b474)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/163409
    Reviewed-by: Christian Lohmaier <[email protected]>

diff --git a/distro-configs/LibreOfficeLinux.conf 
b/distro-configs/LibreOfficeLinux.conf
index db759a8cb4e5..fba02d362d31 100644
--- a/distro-configs/LibreOfficeLinux.conf
+++ b/distro-configs/LibreOfficeLinux.conf
@@ -4,7 +4,6 @@
 --without-system-poppler
 --without-system-openssl
 --without-system-libpng
---without-system-libxml
 --without-system-jpeg
 --without-system-jars
 --without-system-postgresql

Reply via email to