filter/qa/unit/data/dashedline.fodg                                       |   
28 +
 filter/qa/unit/svg.cxx                                                    |   
14 
 filter/source/svg/svgwriter.cxx                                           |   
12 
 include/test/xmltesttools.hxx                                             |    
5 
 include/vcl/dndlistenercontainer.hxx                                      |    
3 
 include/vcl/seleng.hxx                                                    |    
2 
 oox/source/export/shapes.cxx                                              |    
3 
 oox/source/export/vmlexport.cxx                                           |    
2 
 oox/source/ole/vbaexport.cxx                                              |    
8 
 oox/source/vml/vmlshapecontext.cxx                                        |    
7 
 sc/source/ui/miscdlgs/linkarea.cxx                                        |    
4 
 sw/qa/core/layout/flycnt.cxx                                              |   
10 
 sw/qa/extras/layout/data/tdf170381-split-float-table-in-float-table.docx  
|binary
 sw/qa/extras/layout/data/tdf170381-split-float-table-in-normal-table.docx 
|binary
 sw/qa/extras/layout/layout4.cxx                                           |    
7 
 sw/qa/extras/layout/layout6.cxx                                           |  
271 ++++++++++
 sw/qa/extras/ooxmlexport/ooxmlexport11.cxx                                |    
6 
 sw/qa/extras/ooxmlexport/ooxmlexport12.cxx                                |   
15 
 sw/qa/extras/ooxmlexport/ooxmlexport13.cxx                                |   
18 
 sw/qa/extras/ooxmlexport/ooxmlexport16.cxx                                |    
2 
 sw/qa/extras/ooxmlexport/ooxmlexport17.cxx                                |   
14 
 sw/source/core/inc/flyfrm.hxx                                             |    
2 
 sw/source/core/inc/frame.hxx                                              |    
2 
 sw/source/core/inc/tabfrm.hxx                                             |    
2 
 sw/source/core/layout/findfrm.cxx                                         |   
18 
 sw/source/core/layout/fly.cxx                                             |   
57 +-
 sw/source/core/layout/tabfrm.cxx                                          |   
12 
 sw/source/core/objectpositioning/tocntntanchoredobjectposition.cxx        |    
9 
 sw/source/core/text/itrform2.cxx                                          |   
13 
 sw/source/core/text/txtfly.cxx                                            |    
5 
 sw/source/filter/ww8/docxattributeoutput.cxx                              |   
21 
 sw/source/filter/ww8/docxtableexport.cxx                                  |    
7 
 sw/source/writerfilter/dmapper/DomainMapper.cxx                           |    
2 
 test/source/xmltesttools.cxx                                              |   
28 +
 vcl/source/window/dndlistenercontainer.cxx                                |    
6 
 vcl/source/window/seleng.cxx                                              |   
24 
 36 files changed, 568 insertions(+), 71 deletions(-)

New commits:
commit 12ccaa2c207a0500789d75fb2899f4ea1c76aafa
Author:     Mike Kaganski <[email protected]>
AuthorDate: Tue Jan 20 12:11:45 2026 +0500
Commit:     Andras Timar <[email protected]>
CommitDate: Wed Jan 21 16:45:23 2026 +0100

    tdf#170396: implement SVG export support for dashed lines
    
    Commit b71d9a6d15cfb8a50afdea5ac064f40d84c561f8 (do not apply line dashing
    in drawinglayer (tdf#136957), 2021-04-29) made sure that metafiles pass
    dashing information in LineInfo of MetaPolyLineAction, instead of altering
    the polyline itself. Since SVG export ignored dashing information in
    MetaPolyLineAction, the exported lines were solid since that patch.
    
    This patch introduces respective export support, emitting stroke-dasharray
    attribute. As far as I see, it has exactly the same semantics, and renders
    correctly in chrome and firefox. It also imports OK into Draw.
    
    Ref: 
https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Attribute/stroke-dasharray
    
    Change-Id: Ifb634a4f3608ee5c2c76727ef4416b80f8f90709
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/197618
    Tested-by: Jenkins
    Reviewed-by: Mike Kaganski <[email protected]>
    (cherry picked from commit c103d337bd41eaa44b65accabe978fe542e41d06)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/197639
    Reviewed-by: Xisco Fauli <[email protected]>

diff --git a/filter/qa/unit/data/dashedline.fodg 
b/filter/qa/unit/data/dashedline.fodg
new file mode 100644
index 000000000000..73ca01124945
--- /dev/null
+++ b/filter/qa/unit/data/dashedline.fodg
@@ -0,0 +1,28 @@
+<?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:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" 
xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" 
office:version="1.4" 
office:mimetype="application/vnd.oasis.opendocument.graphics">
+ <office:styles>
+  <draw:stroke-dash draw:name="DoubleDashDotDot" draw:style="rect" 
draw:dots1="1" draw:dots1-length="800%" draw:dots2="2" draw:dots2-length="90%" 
draw:distance="300%"/>
+  <style:style style:name="standard" style:family="graphic">
+   <style:graphic-properties draw:stroke="solid" svg:stroke-width="1mm" 
svg:stroke-color="#008000" draw:fill="none" draw:shadow="hidden"/>
+  </style:style>
+ </office:styles>
+ <office:automatic-styles>
+  <style:page-layout style:name="PM0">
+   <style:page-layout-properties fo:margin-top="1cm" fo:margin-bottom="1cm" 
fo:margin-left="1cm" fo:margin-right="1cm" fo:page-width="14cm" 
fo:page-height="4cm"/>
+  </style:page-layout>
+  <style:style style:name="gr1" style:family="graphic" 
style:parent-style-name="standard">
+   <style:graphic-properties draw:stroke="dash" 
draw:stroke-dash="DoubleDashDotDot" draw:textarea-vertical-align="middle"/>
+  </style:style>
+ </office:automatic-styles>
+ <office:master-styles>
+  <style:master-page style:name="Default" style:page-layout-name="PM0"/>
+ </office:master-styles>
+ <office:body>
+  <office:drawing>
+   <draw:page draw:name="page1" draw:master-page-name="Default">
+    <draw:line draw:style-name="gr1" svg:x1="2cm" svg:y1="2cm" svg:x2="12cm" 
svg:y2="2cm"/>
+   </draw:page>
+  </office:drawing>
+ </office:body>
+</office:document>
\ No newline at end of file
diff --git a/filter/qa/unit/svg.cxx b/filter/qa/unit/svg.cxx
index 0faa7a59922e..17637d10de3c 100644
--- a/filter/qa/unit/svg.cxx
+++ b/filter/qa/unit/svg.cxx
@@ -397,6 +397,20 @@ CPPUNIT_TEST_FIXTURE(SvgFilterTest, testTdf166789)
     CPPUNIT_ASSERT_DOUBLES_EQUAL(7000, length.toInt32(), 70); // allow 1% for 
rounding errors
 }
 
+CPPUNIT_TEST_FIXTURE(SvgFilterTest, testDashedLine)
+{
+    // A dashed line
+    loadFromFile(u"dashedline.fodg");
+
+    save(TestFilter::SVG_DRAW);
+
+    xmlDocUniquePtr pXmlDoc = parseExportedFile();
+    CPPUNIT_ASSERT(pXmlDoc);
+
+    // The result must include 'stroke-dasharray' attribute
+    assertXPath(pXmlDoc, "/svg:svg/svg:g//svg:path", "stroke-dasharray", 
u"800,300,90,300,90,300");
+}
+
 CPPUNIT_PLUGIN_IMPLEMENT();
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/filter/source/svg/svgwriter.cxx b/filter/source/svg/svgwriter.cxx
index 65e1a38e842f..f299b959bbe8 100644
--- a/filter/source/svg/svgwriter.cxx
+++ b/filter/source/svg/svgwriter.cxx
@@ -2141,6 +2141,18 @@ void SVGActionWriter::ImplAddLineAttr( const LineInfo 
&rAttrs )
         }
     }
 
+    if (rAttrs.GetStyle() == LineStyle::Dash)
+    {
+        OUStringBuffer aDashArrayStr;
+        for (double x : rAttrs.GetDotDashArray())
+        {
+            if (!aDashArrayStr.isEmpty())
+                aDashArrayStr.append(",");
+            aDashArrayStr.append(x);
+        }
+        if (!aDashArrayStr.isEmpty())
+            mrExport.AddAttribute(u"stroke-dasharray"_ustr, 
aDashArrayStr.makeStringAndClear());
+    }
 }
 
 
commit a7c351391a244a733bb41052ceee3bd66799b000
Author:     Noel Grandin <[email protected]>
AuthorDate: Tue Jan 20 07:39:59 2026 +0100
Commit:     Andras Timar <[email protected]>
CommitDate: Wed Jan 21 16:45:23 2026 +0100

    Revert "mso-test: invalid xdr:twoCellAnchor"
    
    This reverts commit 6b9794ea1c6c24d7c48a1b43a9302c51ea607215,
    because
    With this change, now these files are corrupt after a RT
    -forum-mso-en3-23097.docx
    -forum-mso-en4-364124.docx
    -forum-mso-en4-428907.docx
    -tdf104181-3.docx
    -tdf104181-6.docx
    
    Change-Id: Ib3010fbf0868897fbe8d1f325f3ccdd33475cf3e
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/197616
    Tested-by: Jenkins
    Reviewed-by: Noel Grandin <[email protected]>
    (cherry picked from commit 2a7ee38519d9b1c3db6efe83ccff50a4af0593d2)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/197640
    Reviewed-by: Xisco Fauli <[email protected]>

diff --git a/oox/source/export/shapes.cxx b/oox/source/export/shapes.cxx
index 3ddf1c3fbd7f..4adb4f2a187c 100644
--- a/oox/source/export/shapes.cxx
+++ b/oox/source/export/shapes.cxx
@@ -1422,7 +1422,10 @@ void ShapeExport::WriteGraphicObjectShapePart( const 
Reference< XShape >& xShape
                         && (xShapeProps->getPropertyValue(u"MediaURL"_ustr) 
>>= sMediaURL);
 
     if (!xGraphic.is() && !bHasMediaURL)
+    {
         SAL_INFO("oox.shape", "no graphic or media URL found");
+        return;
+    }
 
     FSHelperPtr pFS = GetFS();
     XmlFilterBase* pFB = GetFB();
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport16.cxx 
b/sw/qa/extras/ooxmlexport/ooxmlexport16.cxx
index 4de1842e917c..9210da630603 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport16.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport16.cxx
@@ -592,7 +592,7 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf141173_missingFrames)
     saveAndReload(TestFilter::DOCX);
     // Without the fix in place, almost all of the text and textboxes were 
missing.
     // Without the fix, there were only 2 shapes (mostly unseen).
-    CPPUNIT_ASSERT_EQUAL(14, getShapes());
+    CPPUNIT_ASSERT_EQUAL(13, getShapes());
 }
 
 DECLARE_OOXMLEXPORT_TEST(testTdf142404_tabSpacing, "tdf142404_tabSpacing.docx")
commit 09b044758b1810a4065cfa9aa0094266cef7333e
Author:     Noel Grandin <[email protected]>
AuthorDate: Thu Jan 15 15:06:13 2026 +0200
Commit:     Andras Timar <[email protected]>
CommitDate: Wed Jan 21 16:45:23 2026 +0100

    officeotron: moveFromRangeStart must have a w:date attribute
    
    so just give it our null date
    
    Change-Id: I22bdd9ffd53dbe7d4608a759dfc9dd93c658f44a
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/197426
    Reviewed-by: Michael Stahl <[email protected]>
    Tested-by: Jenkins
    (cherry picked from commit 232b4ff3d530080efe28b8212c44952a625f8bc9)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/197561
    Reviewed-by: Xisco Fauli <[email protected]>

diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport12.cxx 
b/sw/qa/extras/ooxmlexport/ooxmlexport12.cxx
index 9d6490d1e118..b724869b42bf 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport12.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport12.cxx
@@ -1209,9 +1209,6 @@ DECLARE_OOXMLEXPORT_TEST(testTdf118521_marginsLR, 
"tdf118521_marginsLR.docx")
 
 DECLARE_OOXMLEXPORT_TEST(testTdf104797, "tdf104797.docx")
 {
-    // FIXME: validation error in OOXML export: Errors: 2
-    skipValidation();
-
     // check moveFrom and moveTo
     CPPUNIT_ASSERT_EQUAL(u"Will this sentence be duplicated?"_ustr, 
getParagraph(1)->getString());
     CPPUNIT_ASSERT_EQUAL(u""_ustr, getRun(getParagraph(1), 1)->getString());
@@ -1250,9 +1247,6 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf145720)
     // check moveFromRangeStart/End and moveToRangeStart/End (to keep tracked 
text moving)
     createSwDoc("tdf104797.docx");
 
-    // FIXME: validation error in OOXML export: Errors: 2
-    skipValidation();
-
     save(TestFilter::DOCX);
     xmlDocUniquePtr pXmlDoc = parseExport(u"word/document.xml"_ustr);
     // These were 0 (missing move*FromRange* elements)
@@ -1269,10 +1263,11 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf145720)
     // mandatory authors and dates
     assertXPath(pXmlDoc, "/w:document/w:body/w:p[1]/w:moveFromRangeStart", 
"author", u"Tekijä");
     assertXPath(pXmlDoc, "/w:document/w:body/w:p[2]/w:moveToRangeStart", 
"author", u"Tekijä");
-    // no date (anonymized change)
-    // This failed, date was exported as w:date="0-00-00T00:00:00Z", and later 
"1970-01-01T00:00:00Z"
-    assertXPathNoAttribute(pXmlDoc, 
"/w:document/w:body/w:p[1]/w:moveFromRangeStart", "date");
-    assertXPathNoAttribute(pXmlDoc, 
"/w:document/w:body/w:p[2]/w:moveToRangeStart", "date");
+    // anonymized date
+    assertXPath(pXmlDoc, "/w:document/w:body/w:p[1]/w:moveFromRangeStart", 
"date",
+                u"1970-01-01T00:00:00Z");
+    assertXPath(pXmlDoc, "/w:document/w:body/w:p[2]/w:moveToRangeStart", 
"date",
+                u"1970-01-01T00:00:00Z");
 }
 
 CPPUNIT_TEST_FIXTURE(Test, testTdf150166)
diff --git a/sw/source/filter/ww8/docxattributeoutput.cxx 
b/sw/source/filter/ww8/docxattributeoutput.cxx
index f0c0270d1ff0..fb29e28eea6e 100644
--- a/sw/source/filter/ww8/docxattributeoutput.cxx
+++ b/sw/source/filter/ww8/docxattributeoutput.cxx
@@ -2265,6 +2265,9 @@ void 
DocxAttributeOutput::DoWriteMoveRangeTagStart(std::u16string_view bookmarkN
                     : OUStringToOString(rAuthor, RTL_TEXTENCODING_UTF8));
     if (!bNoDate)
         pAttributeList->add(FSNS(XML_w, XML_date ), DateTimeToOString( 
aDateTime ));
+    else
+        // w:data is a required attribute, so just use a placeholder date
+        pAttributeList->add(FSNS(XML_w, XML_date ), "1970-01-01T00:00:00Z");
     pAttributeList->add(FSNS(XML_w, XML_name), bookmarkName);
     m_pSerializer->singleElementNS( XML_w, bFrom ? XML_moveFromRangeStart : 
XML_moveToRangeStart, pAttributeList );
 
commit a5611c65bf2419241b65f122725403d2e9f399c0
Author:     Noel Grandin <[email protected]>
AuthorDate: Mon Jan 19 16:56:32 2026 +0200
Commit:     Andras Timar <[email protected]>
CommitDate: Wed Jan 21 16:45:23 2026 +0100

    officeotron: w:shd must have val attribute
    
    Change-Id: Ia4ec654a6ba6c341a89994ac7a5f6e575ae1defa
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/197591
    Tested-by: Jenkins
    Reviewed-by: Noel Grandin <[email protected]>
    (cherry picked from commit 6ebb696bebb04e2296a6dfcba769ec4bd3f8ed88)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/197636
    Reviewed-by: Xisco Fauli <[email protected]>

diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport11.cxx 
b/sw/qa/extras/ooxmlexport/ooxmlexport11.cxx
index 18abf65f0303..08a1a1443914 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport11.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport11.cxx
@@ -36,9 +36,6 @@ public:
 
 DECLARE_OOXMLEXPORT_TEST(testTdf57589_hashColor, "tdf57589_hashColor.docx")
 {
-    // FIXME: validation error in OOXML export: Errors: 51
-    skipValidation();
-
     CPPUNIT_ASSERT_EQUAL(drawing::FillStyle_SOLID, 
getProperty<drawing::FillStyle>(getParagraph(1), u"FillStyle"_ustr));
     CPPUNIT_ASSERT_EQUAL(COL_LIGHTMAGENTA, getProperty<Color>(getParagraph(1), 
u"ParaBackColor"_ustr));
     CPPUNIT_ASSERT_EQUAL(drawing::FillStyle_NONE, 
getProperty<drawing::FillStyle>(getParagraph(2), u"FillStyle"_ustr));
diff --git a/sw/source/filter/ww8/docxtableexport.cxx 
b/sw/source/filter/ww8/docxtableexport.cxx
index 6be12ab131ef..ab76c65ea2d3 100644
--- a/sw/source/filter/ww8/docxtableexport.cxx
+++ b/sw/source/filter/ww8/docxtableexport.cxx
@@ -571,6 +571,7 @@ void DocxAttributeOutput::TableBackgrounds(
     }
     else
     {
+        bool bAddedValAttr = false;
         rtl::Reference<sax_fastparser::FastAttributeList> pAttrList;
 
         for (const auto & [ name, val ] : rGrabBag)
@@ -595,8 +596,14 @@ void DocxAttributeOutput::TableBackgrounds(
             else if (name == "color")
                 AddToAttrList(pAttrList, FSNS(XML_w, XML_color), 
val.get<OUString>());
             else if (name == "val")
+            {
                 AddToAttrList(pAttrList, FSNS(XML_w, XML_val), 
val.get<OUString>());
+                bAddedValAttr = true;
+            }
         }
+        // w:val attribute is required
+        if (!bAddedValAttr)
+            AddToAttrList(pAttrList, FSNS(XML_w, XML_val), "clear");
         m_pSerializer->singleElementNS(XML_w, XML_shd, pAttrList);
     }
 }
commit fb1bdaaf2f8314c516757086ba08ba68fa83e6f1
Author:     Mike Kaganski <[email protected]>
AuthorDate: Sun Jan 18 20:41:01 2026 +0500
Commit:     Andras Timar <[email protected]>
CommitDate: Wed Jan 21 16:45:23 2026 +0100

    tdf#170381: try to avoid handling of split-but-not-yet-moved floating tables
    
    When a floating table is being split, it creates a follow table. Later, 
still
    in Split, the follow gets reformatted, creating a follow fly and its anchor
    (which itself is a follow of a text frame). But at this point, the anchor is
    still next to its precede, and all of these haven't yet moved to the next
    page (see SwFrame::GetNextFlyLeaf); the follow fly is still registered in 
the
    precede's page. The actual move will happen only when the follow anchor will
    move to the next page as part of higher-level layout.
    
    In this intermediate state, after split but before move, the floating table
    and its fly should get specal handling. It must not be taken into account
    when calculating object intersections; it must not try to move forward 
itself
    (as part of its own re-layout).
    
    On the other hand, it can't be excluded from any handling. It must calculate
    its size and position (even though its position will eventually be changed) 
-
    without that, things fall apart badly.
    
    When working on it, it turned out, that SwTextFormatter::FormatLine had a
    problem when calculating its real height. The old code used to set the 
height
    of the last-on-page line to the full available space, plus one; and there 
was
    an exception for `HasNonLastSplitFlyDrawObj` case, where "plus one" was not
    used. But that wasn't enough; in case of a floating table in floating table,
    using spacing, the calculation created lines that were too high, causing
    oscillation. I attempted to find a better algorithm to calculate the height,
    e.g. using Grow with bTxt set to true; but all things I tried failed. Thus I
    disabled the call to SetRealHeight in case of HasNonLastSplitFlyDrawObj. It
    feels unsafe, and may require more work, if it turns out problematic.
    
    Another problem was found in 
SwToContentAnchoredObjectPosition::CalcPosition.
    It has a code that calls SwTabFrame::SetDoesObjsFit( false ), but this may
    force moving of objects without final size. The condition was improved.
    
    In GetFlyAnchorBottom, there is a code that decides if legacy behavior must
    be used, where the fly can overlap the bottom margin. It checked if anchor
    is in body. But it didn't consider the case when the anchor itself was in a
    fly (and in that case, thought that the anchor isn't in body); that made the
    floating-table-in-floating-table case calculate anchor bottom incorrectly
    (as in legacy mode), causing move forward and layout loop. Fixed here.
    
    One existing test (testSplitFlyInTextSection in 
sw/qa/core/layout/flycnt.cxx)
    started to hang with this change. That seems to not be a real regression per
    se: turns out, that floattable-in-text-section.docx already started to hang
    in master when opened in GUI; and the change only aligned that between the
    GUI and the unit test. I couldn't find a fix for that; the test was 
disabled.
    
    Change-Id: I738ba4def4a9ddae447b833acc46df1d20de93e4
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/197524
    Tested-by: Jenkins
    Reviewed-by: Mike Kaganski <[email protected]>
    (cherry picked from commit fb203eb57a1ba0accf1894672d35ddf9430d8b05)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/197548
    Reviewed-by: Xisco Fauli <[email protected]>

diff --git a/sw/qa/core/layout/flycnt.cxx b/sw/qa/core/layout/flycnt.cxx
index 48d816ba435b..e390f0a7e3c9 100644
--- a/sw/qa/core/layout/flycnt.cxx
+++ b/sw/qa/core/layout/flycnt.cxx
@@ -741,6 +741,15 @@ CPPUNIT_TEST_FIXTURE(Test, testSplitFlyThenTable)
     save(TestFilter::PDF_WRITER);
 }
 
+/* FIXME: hangs indefinitely; prior to disabling, it passed the test, but hung 
interactively; and
+   even before that, it produced a wrong layout anyway.
+   The problem is somehow related to section. The looping sequence is:
+   1. The table tries to split on page 1, with space less than minimal row 
height.
+   2. It splits successfully between rows 1 and 2. The follow moves to page 2.
+   3. The original table still doesn't fit page 1, and moves to page 2 (for 
some reason, into the
+      same follow fly that holds its follow).
+   4. It detects that its next is its follow, and joins it.
+   5. It moves pack to page 1. But all the generated follow flys get collected 
on page 3 (!).
 CPPUNIT_TEST_FIXTURE(Test, testSplitFlyInTextSection)
 {
     // The document contains a DOCX cont sect break, which is mapped to a 
TextSection.
@@ -748,6 +757,7 @@ CPPUNIT_TEST_FIXTURE(Test, testSplitFlyInTextSection)
     // section frame, which is broken.
     createSwDoc("floattable-in-text-section.docx");
 }
+*/
 
 CPPUNIT_TEST_FIXTURE(Test, testSplitFlyTableRowKeep)
 {
diff --git 
a/sw/qa/extras/layout/data/tdf170381-split-float-table-in-float-table.docx 
b/sw/qa/extras/layout/data/tdf170381-split-float-table-in-float-table.docx
new file mode 100644
index 000000000000..fc5b3a72ef50
Binary files /dev/null and 
b/sw/qa/extras/layout/data/tdf170381-split-float-table-in-float-table.docx 
differ
diff --git 
a/sw/qa/extras/layout/data/tdf170381-split-float-table-in-normal-table.docx 
b/sw/qa/extras/layout/data/tdf170381-split-float-table-in-normal-table.docx
new file mode 100644
index 000000000000..979a7618de7d
Binary files /dev/null and 
b/sw/qa/extras/layout/data/tdf170381-split-float-table-in-normal-table.docx 
differ
diff --git a/sw/qa/extras/layout/layout6.cxx b/sw/qa/extras/layout/layout6.cxx
index 59571bcdf03f..e69f42a197ab 100644
--- a/sw/qa/extras/layout/layout6.cxx
+++ b/sw/qa/extras/layout/layout6.cxx
@@ -1771,6 +1771,277 @@ CPPUNIT_TEST_FIXTURE(SwLayoutWriter6, testTdf155306)
                 "expand", u"2");
 }
 
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter6, 
testTdf170381_split_float_table_in_normal_table)
+{
+    // Given a document with a normal table containing a floating table which 
is split across
+    // pages:
+    createSwDoc("tdf170381-split-float-table-in-normal-table.docx");
+
+    // 1. It must not hang.
+    // 2. Check some correct layout aspects:
+
+    xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+    CPPUNIT_ASSERT(pXmlDoc);
+
+    // Exactly two pages:
+    assertXPath(pXmlDoc, "//page", 2);
+
+    // Exactly one object anchored at each page:
+    assertXPath(pXmlDoc, "//page[1]/sorted_objs/fly", 1);
+    assertXPath(pXmlDoc, "//page[2]/sorted_objs/fly", 1);
+
+    // Get the ids of the two flys (for both pages):
+    OString f1 = getXPath(pXmlDoc, "//page[1]/sorted_objs/fly", 
"ptr").toUtf8();
+    OString f2 = getXPath(pXmlDoc, "//page[2]/sorted_objs/fly", 
"ptr").toUtf8();
+    CPPUNIT_ASSERT(f1 != f2);
+    assertXPath(pXmlDoc, "//anchored/fly", 2);
+    OString aP1FlyTab = "//anchored/fly[@ptr='" + f1 + "']/tab";
+    OString aP2FlyTab = "//anchored/fly[@ptr='" + f2 + "']/tab";
+
+    // Exactly one normal (master / follow) table on each page:
+    assertXPath(pXmlDoc, "//page[1]/body/tab", 1);
+    assertXPath(pXmlDoc, "//page[2]/body/tab", 1);
+    assertXPathNoAttribute(pXmlDoc, "//page[1]/body/tab", "precede");
+    assertXPath(pXmlDoc, "//page[1]/body/tab", "follow",
+                getXPath(pXmlDoc, "//page[2]/body/tab", "id"));
+    assertXPathNoAttribute(pXmlDoc, "//page[2]/body/tab", "follow");
+    assertXPath(pXmlDoc, "//page[2]/body/tab", "precede",
+                getXPath(pXmlDoc, "//page[1]/body/tab", "id"));
+
+    // Exactly two rows in the first page's normal table:
+    assertXPath(pXmlDoc, "//page[1]/body/tab/row", 2);
+
+    // Check the text of the first (repeating) row's cell text:
+    assertXPath(pXmlDoc, "//page[1]/body/tab/row[1]/cell", 1);
+    assertXPath(pXmlDoc, 
"//page[1]/body/tab/row[1]/cell/txt[1]//SwLineLayout", "portion",
+                u"elit ipsum lorem dolor");
+    assertXPath(pXmlDoc, 
"//page[1]/body/tab/row[1]/cell/txt[2]//SwLineLayout", "portion",
+                u"amet elit amet sit adipiscing adipiscing consectetur 
consectetur elit dolor");
+    assertXPath(pXmlDoc, 
"//page[1]/body/tab/row[1]/cell/txt[3]//SwLineLayout", "portion", u"");
+    assertXPath(pXmlDoc, 
"//page[1]/body/tab/row[1]/cell/txt[4]//SwLineLayout", "portion", u"");
+
+    // The second row's cell has a single master paragraph with two anchored 
flys:
+    assertXPath(pXmlDoc, "//page[1]/body/tab/row[2]/cell", 1);
+    assertXPath(pXmlDoc, "//page[1]/body/tab/row[2]/cell/txt", 1);
+    OUString followId = getXPath(pXmlDoc, 
"//page[1]/body/tab/row[2]/cell/txt", "follow");
+    CPPUNIT_ASSERT_GREATER(sal_Int32(0), followId.toInt32());
+    assertXPath(pXmlDoc, "//page[1]/body/tab/row[2]/cell/txt/anchored/fly", 2);
+
+    // Exactly four rows in the second page's normal table:
+    assertXPath(pXmlDoc, "//page[2]/body/tab/row", 4);
+
+    // Check the text of the first (repeating) row's cell text:
+    assertXPath(pXmlDoc, "//page[2]/body/tab/row[1]/cell", 1);
+    assertXPath(pXmlDoc, 
"//page[2]/body/tab/row[1]/cell/txt[1]//SwLineLayout", "portion",
+                u"elit ipsum lorem dolor");
+    assertXPath(pXmlDoc, 
"//page[2]/body/tab/row[1]/cell/txt[2]//SwLineLayout", "portion",
+                u"amet elit amet sit adipiscing adipiscing consectetur 
consectetur elit dolor");
+    assertXPath(pXmlDoc, 
"//page[2]/body/tab/row[1]/cell/txt[3]//SwLineLayout", "portion", u"");
+    assertXPath(pXmlDoc, 
"//page[2]/body/tab/row[1]/cell/txt[4]//SwLineLayout", "portion", u"");
+
+    // The second row's cell has a single follow paragraph:
+    assertXPath(pXmlDoc, "//page[2]/body/tab/row[2]/cell", 1);
+    assertXPath(pXmlDoc, "//page[1]/body/tab/row[2]/cell/txt", 1);
+    assertXPath(pXmlDoc, "//page[2]/body/tab/row[2]/cell/txt", "id", followId);
+    assertXPath(pXmlDoc, "//page[2]/body/tab/row[2]/cell/txt", "precede",
+                getXPath(pXmlDoc, "//page[1]/body/tab/row[2]/cell/txt", "id"));
+
+    // Test floating tables' content (the line split must be correct).
+
+    auto assertCellLines
+        = [&](int page, int row, std::initializer_list<std::u16string_view> 
lines) {
+              OString base = (page == 1 ? aP1FlyTab : aP2FlyTab) + "/row[" + 
OString::number(row)
+                             + "]/cell/txt/SwParaPortion/SwLineLayout[";
+              int i = 1;
+              for (const auto& line : lines)
+                  assertXPath(pXmlDoc, base + OString::number(i++) + "]", 
"portion", line);
+          };
+
+    // Page 1's floating table:
+    // NB: the *intended correct layout* is, when the first page's floating 
table has 5 rows!
+    // Currently asserting 6 rows on page 1, but row 6 must move to page 2, 
when fixed properly.
+
+    std::initializer_list<std::u16string_view> page1cells[] = {
+        { u"amet sit consectetur ", u"elit" },
+        { u"" },
+        { u"dolor dolor dolor ", u"ipsum" },
+        { u"amet ipsum amet dolor ", u"elit sit" },
+        { u"ipsum consectetur ", u"consectetur amet ", u"adipiscing ipsum" },
+        // NB: this must move to the follow!
+        { u"" },
+    };
+
+    assertXPath(pXmlDoc, aP1FlyTab + "/row", std::size(page1cells));
+    for (size_t r = 0; r < std::size(page1cells); ++r)
+        assertCellLines(1, r + 1, page1cells[r]);
+
+    // Page 2's floating table:
+
+    std::initializer_list<std::u16string_view> page2cells[] = {
+        { u"adipiscing ipsum elit ", u"lorem" },
+        { u"lorem adipiscing sit sit ", u"lorem lorem" },
+        { u"ipsum lorem ", u"consectetur amet amet ", u"ipsum" },
+        { u"" },
+        { u"sit consectetur ", u"adipiscing sit" },
+        { u"elit consectetur lorem ", u"consectetur ", u"consectetur lorem sit 
",
+          u"sit dolor elit adipiscing ", u"consectetur sit" },
+        { u"consectetur dolor ", u"dolor sit elit lorem ", u"consectetur dolor 
", u"lorem ipsum" },
+    };
+
+    assertXPath(pXmlDoc, aP2FlyTab + "/row", std::size(page2cells));
+    for (size_t r = 0; r < std::size(page2cells); ++r)
+        assertCellLines(2, r + 1, page2cells[r]);
+}
+
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter6, 
testTdf170381_split_float_table_in_float_table)
+{
+    // Given a document with a floating table containing another floating 
table which is split
+    // across pages:
+    createSwDoc("tdf170381-split-float-table-in-float-table.docx");
+
+    // 1. It must not hang.
+    // 2. Check some correct layout aspects:
+
+    xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+    CPPUNIT_ASSERT(pXmlDoc);
+
+    // Exactly two pages:
+    assertXPath(pXmlDoc, "//page", 2);
+
+    // Exactly two objects anchored at each page:
+    assertXPath(pXmlDoc, "//page[1]/sorted_objs/fly", 2);
+    assertXPath(pXmlDoc, "//page[2]/sorted_objs/fly", 2);
+
+    // Exactly one (master/follow) paragraph on each page:
+    assertXPath(pXmlDoc, "//page[1]/body/txt", 1);
+    assertXPath(pXmlDoc, "//page[2]/body/txt", 1);
+    assertXPath(pXmlDoc, "//page[1]/body/txt", "follow",
+                getXPath(pXmlDoc, "//page[2]/body/txt", "id"));
+    assertXPath(pXmlDoc, "//page[2]/body/txt", "precede",
+                getXPath(pXmlDoc, "//page[1]/body/txt", "id"));
+
+    // Page 1's paragraph has two anchored flys:
+    assertXPath(pXmlDoc, "//page[1]/body/txt/anchored/fly", 2);
+
+    // Get the ids of the two outer flys.
+    // Page 1:
+    OString f1 = getXPath(pXmlDoc, "//page[1]/sorted_objs/fly[1]", 
"ptr").toUtf8();
+    OString f2 = getXPath(pXmlDoc, "//page[1]/sorted_objs/fly[2]", 
"ptr").toUtf8();
+    CPPUNIT_ASSERT(f1 != f2);
+    OString filter1 = "@ptr='" + f1 + "' or @ptr='" + f2 + "'";
+    OUString id = getXPath(pXmlDoc, "//page[1]/body/txt/anchored/fly[" + 
filter1 + "]", "id");
+    OString aP1OuterFlyTab = "//anchored/fly[@id='" + id.toUtf8() + "']/tab";
+
+    // Page 2:
+    f1 = getXPath(pXmlDoc, "//page[2]/sorted_objs/fly[1]", "ptr").toUtf8();
+    f2 = getXPath(pXmlDoc, "//page[2]/sorted_objs/fly[2]", "ptr").toUtf8();
+    CPPUNIT_ASSERT(f1 != f2);
+    OString filter2 = "@ptr='" + f1 + "' or @ptr='" + f2 + "'";
+    id = getXPath(pXmlDoc, "//page[1]/body/txt/anchored/fly[" + filter2 + "]", 
"id");
+    OString aP2OuterFlyTab = "//anchored/fly[@id='" + id.toUtf8() + "']/tab";
+
+    // Exactly one row in both top-level floating tables:
+    assertXPath(pXmlDoc, aP1OuterFlyTab + "/row", 1);
+    assertXPath(pXmlDoc, aP2OuterFlyTab + "/row", 1);
+
+    // Exactly one cell in both top-level floating tables:
+    assertXPath(pXmlDoc, aP1OuterFlyTab + "/row/cell", 1);
+    assertXPath(pXmlDoc, aP2OuterFlyTab + "/row/cell", 1);
+
+    // First page's top-level floating table's cell has three paragraphs:
+    assertXPath(pXmlDoc, aP1OuterFlyTab + "/row/cell/txt", 3);
+    // Check text in the first two paragraphs:
+    assertXPath(pXmlDoc, aP1OuterFlyTab + "/row/cell/txt[1]//SwLineLayout", 
"portion",
+                u"Table1 A1 dolor elit");
+    assertXPath(pXmlDoc, aP1OuterFlyTab + "/row/cell/txt[2]//SwLineLayout", 
"portion",
+                u"adipiscing dolor adipiscing amet ipsum elit sit elit lorem 
elit adipiscing "
+                "dolor ipsum");
+    // The third paragraph has two attached inner floating tables:
+    assertXPath(pXmlDoc, aP1OuterFlyTab + "/row/cell/txt[3]/anchored/fly", 2);
+
+    // Get the ids of the two inner flys.
+    // Page 1:
+    id = getXPath(pXmlDoc, aP1OuterFlyTab + "/row/cell/txt[3]/anchored/fly[" + 
filter1 + "]", "id");
+    OString aP1InnerFlyTab = "//anchored/fly[@id='" + id.toUtf8() + "']/tab";
+
+    // Page 2:
+    id = getXPath(pXmlDoc, aP1OuterFlyTab + "/row/cell/txt[3]/anchored/fly[" + 
filter2 + "]", "id");
+    OString aP2InnerFlyTab = "//anchored/fly[@id='" + id.toUtf8() + "']/tab";
+
+    // Check the layout of the inner tables (splitting of lines and rows).
+
+    auto assertCellLines
+        = [&](int page, int row, std::initializer_list<std::u16string_view> 
lines) {
+              OString base = (page == 1 ? aP1InnerFlyTab : aP2InnerFlyTab) + 
"/row["
+                             + OString::number(row) + 
"]/cell[1]/txt/SwParaPortion/SwLineLayout[";
+              int i = 1;
+              for (const auto& line : lines)
+                  assertXPath(pXmlDoc, base + OString::number(i++) + "]", 
"portion", line);
+          };
+
+    // Page 1's inner table:
+    // NB: the *intended correct layout* is, when the first page's inner 
floating table is split
+    // after row 21! Currently part of row 22 is on page 1, but must move to 
page 2, when fixed.
+
+    std::initializer_list<std::u16string_view> page1cells[] = {
+        { u"Table2 A1 sit amet ", u"ipsum consectetur ", u"ipsum amet ", 
u"adipiscing amet elit ",
+          u"dolor consectetur" },
+        { u"Table2 A2 ", u"consectetur ", u"adipiscing adipiscing ", 
u"consectetur dolor sit ",
+          u"amet lorem" },
+        { u"Table2 A3 dolor elit ", u"amet ipsum ", u"adipiscing ipsum ", 
u"dolor lorem" },
+        { u"Table2 A4" },
+        { u"Table2 A5 amet dolor ", u"elit consectetur lorem ", u"dolor sit 
amet" },
+        { u"Table2 A6 sit dolor ", u"elit consectetur elit sit ", u"dolor 
adipiscing" },
+        { u"Table2 A7" },
+        { u"Table2 A8 ", u"consectetur ipsum ", u"dolor adipiscing ", u"ipsum 
dolor dolor ",
+          u"sit elit consectetur ", u"adipiscing" },
+        { u"Table2 A9 adipiscing ", u"amet dolor amet ", u"lorem elit sit 
amet" },
+        { u"Table2 A10 amet ", u"lorem elit elit elit ", u"adipiscing elit 
sit" },
+        { u"Table2 A11" },
+        { u"Table2 A12 sit ", u"adipiscing adipiscing ", u"consectetur sit 
ipsum ",
+          u"consectetur ipsum" },
+        { u"Table2 A13 amet ", u"dolor consectetur ", u"amet dolor ipsum sit 
", u"sit" },
+        { u"Table2 A14" },
+        { u"Table2 A15 dolor ", u"dolor elit dolor ", u"dolor ipsum ", 
u"consectetur amet ",
+          u"elit sit" },
+        { u"Table2 A16 ipsum ", u"lorem adipiscing sit ", u"sit dolor lorem 
elit" },
+        { u"Table2 A17 sit dolor ", u"adipiscing ", u"consectetur elit ", 
u"ipsum lorem sit" },
+        { u"Table2 A18" },
+        { u"Table2 A19 sit ", u"adipiscing ", u"consectetur ", u"adipiscing 
lorem ",
+          u"ipsum amet elit" },
+        { u"Table2 A20 ipsum ", u"amet consectetur elit ", u"amet amet sit sit 
", u"adipiscing" },
+        { u"Table2 A21" },
+        // NB: this must merge to the first row of the follow!
+        { u"Table2 A22 elit ", u"ipsum elit elit sit elit " },
+    };
+
+    assertXPath(pXmlDoc, aP1InnerFlyTab + "/row", std::size(page1cells));
+    for (size_t r = 0; r < std::size(page1cells); ++r)
+        assertCellLines(1, r + 1, page1cells[r]);
+
+    // Page 2's inner table:
+
+    std::initializer_list<std::u16string_view> page2cells[] = {
+        { u"sit consectetur ", u"amet sit" },
+        { u"Table2 A23 ", u"consectetur amet ", u"lorem consectetur elit ", 
u"dolor sit elit" },
+        { u"Table2 A24 ipsum ", u"amet ipsum amet ", u"consectetur lorem ", 
u"amet sit" },
+        { u"Table2 A25" },
+        { u"Table2 A26 ", u"consectetur dolor ", u"consectetur ", u"adipiscing 
dolor dolor ",
+          u"lorem adipiscing" },
+        { u"Table2 A27 dolor sit ", u"elit dolor ipsum lorem ", u"dolor elit" 
},
+        { u"Table2 A28" },
+        { u"Table2 A29 ipsum ", u"ipsum amet ipsum" },
+        { u"Table2 A30 amet ", u"ipsum lorem ", u"consectetur ipsum ", u"ipsum 
lorem ipsum ",
+          u"ipsum sit consectetur ", u"consectetur" },
+        { u"Table2 A31 ", u"consectetur elit sit ", u"ipsum adipiscing ", 
u"ipsum ipsum ",
+          u"consectetur" },
+    };
+
+    assertXPath(pXmlDoc, aP2InnerFlyTab + "/row", std::size(page2cells));
+    for (size_t r = 0; r < std::size(page2cells); ++r)
+        assertCellLines(2, r + 1, page2cells[r]);
+}
+
 } // end of anonymous namespace
 
 CPPUNIT_PLUGIN_IMPLEMENT();
diff --git a/sw/source/core/inc/flyfrm.hxx b/sw/source/core/inc/flyfrm.hxx
index 30509dd0eb50..9dc03bc323af 100644
--- a/sw/source/core/inc/flyfrm.hxx
+++ b/sw/source/core/inc/flyfrm.hxx
@@ -308,6 +308,8 @@ public:
 
     SW_DLLPUBLIC SwFlyAtContentFrame* DynCastFlyAtContentFrame();
 
+    bool IsSplitButNotYetMovedFollow() const;
+
 private:
     void UpdateUnfloatButton(SwWrtShell* pWrtSh, bool bShow) const;
     void PaintDecorators() const;
diff --git a/sw/source/core/inc/frame.hxx b/sw/source/core/inc/frame.hxx
index 143ad395ee90..8c286f69a5e0 100644
--- a/sw/source/core/inc/frame.hxx
+++ b/sw/source/core/inc/frame.hxx
@@ -976,6 +976,8 @@ public:
 
     /// Determines if the upper margin of this frame should be ignored.
     bool IsCollapseUpper() const;
+
+    bool IsInSplitButNotYetMovedFollow() const;
 };
 
 inline void SwFrame::InvalidateInfFlags()
diff --git a/sw/source/core/inc/tabfrm.hxx b/sw/source/core/inc/tabfrm.hxx
index 07449b23da01..a7fdeefedafb 100644
--- a/sw/source/core/inc/tabfrm.hxx
+++ b/sw/source/core/inc/tabfrm.hxx
@@ -236,6 +236,8 @@ public:
 
     sal_uInt16 GetBottomLineSize() const;
 
+    bool IsSplitButNotYetMovedFloatingFollow() const;
+
     void dumpAsXml(xmlTextWriterPtr writer = nullptr) const override;
 };
 
diff --git a/sw/source/core/layout/findfrm.cxx 
b/sw/source/core/layout/findfrm.cxx
index f7a3bbd36d9d..0092b74542a4 100644
--- a/sw/source/core/layout/findfrm.cxx
+++ b/sw/source/core/layout/findfrm.cxx
@@ -1502,6 +1502,20 @@ static bool lcl_IsInSectionDirectly( const SwFrame *pUp )
     return false;
 }
 
+bool SwFrame::IsInSplitButNotYetMovedFollow() const
+{
+    const SwFrame* pFrame = this;
+    while (pFrame && pFrame->IsInTab() && pFrame->IsInFly())
+    {
+        const SwTabFrame* pTabFrame = pFrame->FindTabFrame();
+        assert(pTabFrame);
+        if (pTabFrame->IsSplitButNotYetMovedFloatingFollow())
+            return true;
+        pFrame = pTabFrame->GetUpper();
+    }
+    return false;
+}
+
 /** determine, if frame is moveable in given environment
 
     OD 08.08.2003 #110978#
@@ -1512,6 +1526,10 @@ static bool lcl_IsInSectionDirectly( const SwFrame *pUp )
 */
 bool SwFrame::IsMoveable( const SwLayoutFrame* _pLayoutFrame ) const
 {
+    if (IsTabFrame())
+        if (static_cast<const 
SwTabFrame*>(this)->IsSplitButNotYetMovedFloatingFollow())
+           return false;
+
     bool bRetVal = false;
 
     if ( !_pLayoutFrame )
diff --git a/sw/source/core/layout/fly.cxx b/sw/source/core/layout/fly.cxx
index 380df804bac8..289d081f9445 100644
--- a/sw/source/core/layout/fly.cxx
+++ b/sw/source/core/layout/fly.cxx
@@ -84,6 +84,31 @@ using namespace ::com::sun::star;
 
 namespace
 {
+// True if the anchor is (directly or indirectly) in the document body.
+bool isAnchorInDocBody(const SwFrame& rAnchor)
+{
+    for (auto p = &rAnchor; p; p = p->FindFlyFrame()->GetAnchorFrame())
+    {
+        if (p->IsInDocBody())
+            return true;
+        if (!p->IsInFly())
+            return false;
+    }
+    return false;
+}
+
+// True means Word <= 2010 behavior
+bool isLegacyBehavior(const SwFlyFrame& rFly, const SwFrame& rAnchor)
+{
+    const auto* pFrameFormat = rFly.GetFrameFormat();
+    if 
(!pFrameFormat->getIDocumentSettingAccess().get(DocumentSettingId::TAB_OVER_MARGIN))
+        return false;
+    // Allow overlap with bottom margin / footer only in case we're relative 
to the page frame.
+    bool bVertPageFrame
+        = pFrameFormat->GetVertOrient().GetRelationOrient() == 
text::RelOrientation::PAGE_FRAME;
+    return bVertPageFrame || !isAnchorInDocBody(rAnchor);
+}
+
 /// Gets the bottom position which is a deadline for a split fly.
 SwTwips GetFlyAnchorBottom(SwFlyFrame& rFly, const SwFrame& rAnchor)
 {
@@ -101,13 +126,7 @@ SwTwips GetFlyAnchorBottom(SwFlyFrame& rFly, const 
SwFrame& rAnchor)
         return 0;
     }
 
-    const auto* pFrameFormat = rFly.GetFrameFormat();
-    const IDocumentSettingAccess& rIDSA = 
pFrameFormat->getIDocumentSettingAccess();
-    // Allow overlap with bottom margin / footer only in case we're relative 
to the page frame.
-    bool bVertPageFrame = pFrameFormat->GetVertOrient().GetRelationOrient() == 
text::RelOrientation::PAGE_FRAME;
-    bool bInBody = rAnchor.IsInDocBody();
-    bool bLegacy = rIDSA.get(DocumentSettingId::TAB_OVER_MARGIN) && 
(bVertPageFrame || !bInBody);
-    if (bLegacy)
+    if (isLegacyBehavior(rFly, rAnchor))
     {
         // Word <= 2010 style: the fly can overlap with the bottom margin / 
footer area in case the
         // fly height fits the body height and the fly bottom fits the page.
@@ -2288,6 +2307,30 @@ SwFlyAtContentFrame* 
SwFlyFrame::DynCastFlyAtContentFrame()
     return IsFlyAtContentFrame() ? static_cast<SwFlyAtContentFrame*>(this) : 
nullptr;
 }
 
+bool SwFlyFrame::IsSplitButNotYetMovedFollow() const
+{
+    if (IsFlySplitAllowed())
+    {
+        auto& rFlyAtContentFrame = 
static_cast<SwFlyAtContentFrame&>(const_cast<SwFlyFrame&>(*this));
+        if (rFlyAtContentFrame.IsFollow())
+        {
+            auto pThisAnchor = rFlyAtContentFrame.FindAnchorCharFrame();
+            if (!pThisAnchor)
+                return true; // no anchor frame has been created yet
+            auto pPrecedeAnchor = 
rFlyAtContentFrame.GetPrecede()->FindAnchorCharFrame();
+            assert(pPrecedeAnchor);
+            if (pThisAnchor->GetUpper() == pPrecedeAnchor->GetUpper())
+            {
+                // This is a just-split follow fly, and it is waiting to be 
moved to the next page
+                // together with its anchor. See SwFrame::GetNextFlyLeaf and 
its "nesting" case
+                // handling.
+                return true;
+            }
+        }
+    }
+    return false;
+}
+
 SwTwips SwFlyFrame::Grow_(SwTwips nDist, SwResizeLimitReason& reason, bool 
bTst)
 {
     if (!Lower())
diff --git a/sw/source/core/layout/tabfrm.cxx b/sw/source/core/layout/tabfrm.cxx
index eb7d49360c8f..a5bbd2b3710b 100644
--- a/sw/source/core/layout/tabfrm.cxx
+++ b/sw/source/core/layout/tabfrm.cxx
@@ -1515,6 +1515,18 @@ bool SwTabFrame::Split(const SwTwips nCutPos, bool 
bTryToSplit,
     return bRet;
 }
 
+bool SwTabFrame::IsSplitButNotYetMovedFloatingFollow() const
+{
+    if (IsFollow() && GetUpper() && GetUpper()->IsFlyFrame())
+    {
+        // Test if this is a just-split follow nested floating table, and it 
is waiting to be moved
+        // to the next page together with its anchor. We get here while 
formatting all the outer
+        // cells' anchored objects, before the outer table splits eventually.
+        return static_cast<const 
SwFlyFrame*>(GetUpper())->IsSplitButNotYetMovedFollow();
+    }
+    return false;
+}
+
 namespace
 {
     bool CanDeleteFollow(const SwTabFrame *pFoll)
diff --git a/sw/source/core/objectpositioning/tocntntanchoredobjectposition.cxx 
b/sw/source/core/objectpositioning/tocntntanchoredobjectposition.cxx
index 57b200c81d58..71c66cf1cfca 100644
--- a/sw/source/core/objectpositioning/tocntntanchoredobjectposition.cxx
+++ b/sw/source/core/objectpositioning/tocntntanchoredobjectposition.cxx
@@ -964,7 +964,8 @@ void SwToContentAnchoredObjectPosition::CalcPosition()
                     nDist = aRectFnSet.BottomDist( 
GetAnchoredObj().GetObjRect(),
                         aRectFnSet.GetPrtBottom(*pUpperOfOrientFrame) );
                     if ( nDist < 0 &&
-                         pOrientFrame == &rAnchorTextFrame && 
!pOrientFrame->GetIndPrev() )
+                         pOrientFrame == &rAnchorTextFrame && 
!pOrientFrame->GetIndPrev() &&
+                         pUpperOfOrientFrame->isFrameAreaDefinitionValid() )
                     {
                         const_cast<SwTabFrame*>(pOrientFrame->FindTabFrame())
                                                         ->SetDoesObjsFit( 
false );
@@ -1224,6 +1225,9 @@ void SwToContentAnchoredObjectPosition::CalcOverlap(const 
SwTextFrame* pAnchorFr
     SwFlyFrame* pFlyFrame = GetAnchoredObj().DynCastFlyFrame();
     if (pFlyFrame && pFlyFrame->IsFlySplitAllowed())
     {
+        if (pFlyFrame->IsSplitButNotYetMovedFollow())
+            return; // Don't check overlaps until the follow is moved.
+
         // At least for split flys we need to consider objects on the same 
page, but anchored in
         // different text frames.
         bSplitFly = true;
@@ -1270,6 +1274,9 @@ void SwToContentAnchoredObjectPosition::CalcOverlap(const 
SwTextFrame* pAnchorFr
                 continue;
             }
 
+            if (pAnchoredObjFly->IsSplitButNotYetMovedFollow())
+                continue; // Don't check overlaps with not-yet-moved objects.
+
             if 
(pAnchoredObjFly->getRootFrame()->IsInFlyDelList(pAnchoredObjFly))
             {
                 // A fly overlapping with a to-be-deleted fly is fine.
diff --git a/sw/source/core/text/itrform2.cxx b/sw/source/core/text/itrform2.cxx
index 551c52ad4564..47fbfc4b4335 100644
--- a/sw/source/core/text/itrform2.cxx
+++ b/sw/source/core/text/itrform2.cxx
@@ -2104,15 +2104,10 @@ TextFrameIndex 
SwTextFormatter::FormatLine(TextFrameIndex const nStartPos)
         if( GetInfo().IsStop() )
         {
             m_pCurr->SetLen(TextFrameIndex(0));
-            m_pCurr->Height( GetFrameRstHeight() + 1, false );
-            m_pCurr->SetRealHeight( GetFrameRstHeight() + 1 );
-
-            // Don't oversize the line in case of split flys, so we don't try 
to move the anchor
-            // of a precede fly forward, next to its follow.
-            if (m_pFrame->HasNonLastSplitFlyDrawObj())
-            {
-                m_pCurr->SetRealHeight(GetFrameRstHeight());
-            }
+            auto nFrameRstHeight = GetFrameRstHeight();
+            m_pCurr->Height(nFrameRstHeight + 1, false);
+            if (!m_pFrame->HasNonLastSplitFlyDrawObj())
+                m_pCurr->SetRealHeight(nFrameRstHeight + 1);
 
             m_pCurr->Width(0);
             m_pCurr->Truncate();
diff --git a/sw/source/core/text/txtfly.cxx b/sw/source/core/text/txtfly.cxx
index b9e7229c5c76..45df77b30b25 100644
--- a/sw/source/core/text/txtfly.cxx
+++ b/sw/source/core/text/txtfly.cxx
@@ -900,7 +900,7 @@ SwAnchoredObjList& SwTextFly::InitAnchoredObjList()
     // #i68520#
     mpAnchoredObjList.reset(new SwAnchoredObjList);
 
-    if( nCount && bWrapAllowed )
+    if (nCount && bWrapAllowed && 
!m_pCurrFrame->IsInSplitButNotYetMovedFollow())
     {
         SwRect const aRect(GetFrameArea());
         // Make ourselves a little smaller than we are,
@@ -929,7 +929,8 @@ SwAnchoredObjList& SwTextFly::InitAnchoredObjList()
                  !pAnchoredObj->ConsiderForTextWrap() ||
                  ( mbIgnoreObjsInHeaderFooter && !bFooterHeader &&
                    pAnchoredObj->GetAnchorFrame()->FindFooterOrHeader() ) ||
-                 ( bAllowCompatWrap && 
!pAnchoredObj->GetFrameFormat()->GetFollowTextFlow().GetValue() )
+                 ( bAllowCompatWrap && 
!pAnchoredObj->GetFrameFormat()->GetFollowTextFlow().GetValue() ) ||
+                 ( pAnchoredObj->DynCastFlyFrame()  && 
pAnchoredObj->DynCastFlyFrame()->IsSplitButNotYetMovedFollow() )
                )
             {
                 continue;
commit 9ac800377c4e44532b936bd61f5c51213894a460
Author:     Noel Grandin <[email protected]>
AuthorDate: Wed Jan 14 15:28:06 2026 +0200
Commit:     Andras Timar <[email protected]>
CommitDate: Wed Jan 21 16:45:23 2026 +0100

    officeotron: ordering of w:startOverride wrong
    
    we end up with
    
        <w:lvlOverride w:ilvl="0">
            <w:lvl w:ilvl="0">
                ....
            </w:lvl>
            <w:startOverride w:val="1"/>
        </w:lvlOverride>
    
    but startOverride needs to come before w:lvl
    
    Change-Id: Ide251945b312d48101e277970f1a53fc52c8b5a7
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/197290
    Reviewed-by: Michael Stahl <[email protected]>
    Tested-by: Jenkins
    Signed-off-by: Xisco Fauli <[email protected]>
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/197430

diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport17.cxx 
b/sw/qa/extras/ooxmlexport/ooxmlexport17.cxx
index bddf3715c7bd..3a42e7e3890d 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport17.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport17.cxx
@@ -755,9 +755,6 @@ DECLARE_OOXMLEXPORT_TEST(testTdf142407, "tdf142407.docx")
 
 DECLARE_OOXMLEXPORT_TEST(testWPGBodyPr, "WPGbodyPr.docx")
 {
-    // FIXME: validation error in OOXML export: Errors: 3
-    skipValidation();
-
     // There are a WPG shape and a picture
     CPPUNIT_ASSERT_EQUAL(2, getShapes());
 
@@ -803,9 +800,6 @@ DECLARE_OOXMLEXPORT_TEST(testWPGBodyPr, "WPGbodyPr.docx")
 
 DECLARE_OOXMLEXPORT_TEST(testTdf146851_1, "tdf146851_1.docx")
 {
-    // FIXME: validation error in OOXML export: Errors: 1
-    skipValidation();
-
     uno::Reference<beans::XPropertySet> xPara;
 
     xPara.set(getParagraph(1, u"qwerty"_ustr), uno::UNO_QUERY);
@@ -990,9 +984,6 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf146955)
 {
     createSwDoc("tdf146955.odt");
 
-    // FIXME: validation error in OOXML export: Errors: 9
-    skipValidation();
-
     saveAndReload(TestFilter::DOCX);
     // import of a (broken?) DOCX export with dozens of frames raised a SAX 
exception,
     // when the code tried to access to a non-existent footnote
diff --git a/sw/source/filter/ww8/docxattributeoutput.cxx 
b/sw/source/filter/ww8/docxattributeoutput.cxx
index 1e780c0a1658..f0c0270d1ff0 100644
--- a/sw/source/filter/ww8/docxattributeoutput.cxx
+++ b/sw/source/filter/ww8/docxattributeoutput.cxx
@@ -7798,16 +7798,16 @@ void DocxAttributeOutput::OverrideNumberingDefinition(
 
             m_pSerializer->startElementNS(XML_w, XML_lvlOverride, FSNS(XML_w, 
XML_ilvl), OString::number(nLevel));
 
-            if (bListsAreDifferent)
-            {
-                GetExport().NumberingLevel(rRule, nLevel);
-            }
             if (levelOverride != rLevelOverrides.end())
             {
                 // list numbering restart override
                 m_pSerializer->singleElementNS(XML_w, XML_startOverride,
                     FSNS(XML_w, XML_val), 
OString::number(levelOverride->second));
             }
+            if (bListsAreDifferent)
+            {
+                GetExport().NumberingLevel(rRule, nLevel);
+            }
 
             m_pSerializer->endElementNS(XML_w, XML_lvlOverride);
         }
commit 920131875277129dfc52c459116d7b965c870163
Author:     Noel Grandin <[email protected]>
AuthorDate: Thu Jan 15 14:36:41 2026 +0200
Commit:     Andras Timar <[email protected]>
CommitDate: Wed Jan 21 16:45:23 2026 +0100

    officeotron: w:r inside w:pPr is invalid
    
    we end with the following in word/header1.xml:
    
    <w:hdr>
        <w:p>
            <w:pPr>
                <w:pStyle w:val="Header"/>
                <w:r>
                    <w:br w:type="page"/>
                </w:r>
                <w:rPr></w:rPr>
            </w:pPr>
    
    restrict the fix to the case of writing header data,
    just in case I missed some other case somewhere.
    
    Change-Id: I451480a075c55cafd016279d229dbf0698c01265
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/197425
    Tested-by: Jenkins
    Reviewed-by: Michael Stahl <[email protected]>
    (cherry picked from commit 821e808610a9710d4875bff65604f34aacf4a29c)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/197590
    Reviewed-by: Xisco Fauli <[email protected]>

diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport13.cxx 
b/sw/qa/extras/ooxmlexport/ooxmlexport13.cxx
index 4ada85283bde..851ba1e29f45 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport13.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport13.cxx
@@ -111,9 +111,6 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf121374_sectionHF)
 {
     createSwDoc("tdf121374_sectionHF.odt");
 
-    // FIXME: validation error in OOXML export: Errors: 1
-    skipValidation();
-
     saveAndReload(TestFilter::DOCX);
     uno::Reference<beans::XPropertySet> 
xPageStyle(getStyles(u"PageStyles"_ustr)->getByName(u"Standard"_ustr), 
uno::UNO_QUERY);
     uno::Reference<text::XTextRange> xFooterText = getProperty< 
uno::Reference<text::XTextRange> >(xPageStyle, u"FooterText"_ustr);
diff --git a/sw/source/filter/ww8/docxattributeoutput.cxx 
b/sw/source/filter/ww8/docxattributeoutput.cxx
index 271cdc9b381b..1e780c0a1658 100644
--- a/sw/source/filter/ww8/docxattributeoutput.cxx
+++ b/sw/source/filter/ww8/docxattributeoutput.cxx
@@ -7094,6 +7094,8 @@ void DocxAttributeOutput::PageBreakBefore( bool bBreak )
 
 void DocxAttributeOutput::SectionBreak( sal_uInt8 nC, bool bBreakAfter, const 
WW8_SepInfo* pSectionInfo, bool bExtraPageBreak)
 {
+    if (m_bWritingHeaderFooter && m_bOpenedParaPr)
+        return; // do not put a run inside <w:hdr>..<w:p>..<w:pPr>
     switch ( nC )
     {
         case msword::ColumnBreak:
commit 76c4125b159f5461a96ae2ce665cbd8e86b062a5
Author:     Noel Grandin <[email protected]>
AuthorDate: Thu Jan 15 16:03:07 2026 +0200
Commit:     Andras Timar <[email protected]>
CommitDate: Wed Jan 21 16:45:23 2026 +0100

    officeotron: attribute signinginstructions should not have a namespace 
prefix
    
    we end up with
    
      <o:signatureline ... o:signinginstructions="Check the machines!"
    
    where the "o:" prefix on signinginstructions is incorrect.
    
    Just to be safe, when loading, try both the old and the new style.
    
    However, this does mean that creating such data on a new version of LO,
    and then sending it to someone with an old version of LO, this data will be 
lost.
    
    Change-Id: I3ec366a2de1ba4945aae1ff67656eda0868333e1
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/197427
    Tested-by: Jenkins
    Reviewed-by: Michael Stahl <[email protected]>
    (cherry picked from commit bf5cc0ae565fb3a11c4cb6b081f99f6daea26dfb)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/197562
    Reviewed-by: Xisco Fauli <[email protected]>

diff --git a/oox/source/export/vmlexport.cxx b/oox/source/export/vmlexport.cxx
index 0613426e6fe5..40fb4624fa5c 100644
--- a/oox/source/export/vmlexport.cxx
+++ b/oox/source/export/vmlexport.cxx
@@ -711,7 +711,7 @@ void VMLExport::Commit( EscherPropertyContainer& rProps, 
const tools::Rectangle&
                         {
                             
pAttrListSignatureLine->add(XML_signinginstructionsset, "t");
                             pAttrListSignatureLine->add(
-                                FSNS(XML_o, XML_signinginstructions),
+                                XML_signinginstructions,
                                 
pSdrGrafObj->getSignatureLineSigningInstructions());
                         }
                         pAttrListSignatureLine->add(
diff --git a/oox/source/vml/vmlshapecontext.cxx 
b/oox/source/vml/vmlshapecontext.cxx
index 5839fa511116..01e2054bb078 100644
--- a/oox/source/vml/vmlshapecontext.cxx
+++ b/oox/source/vml/vmlshapecontext.cxx
@@ -649,7 +649,12 @@ ContextHandlerRef ShapeContext::onCreateContext( sal_Int32 
nElement, const Attri
             mrShapeModel.maSignatureLineSuggestedSignerEmail
                 = rAttribs.getStringDefaulted(O_TOKEN(suggestedsigneremail));
             mrShapeModel.maSignatureLineSigningInstructions
-                = rAttribs.getStringDefaulted(O_TOKEN(signinginstructions));
+                = rAttribs.getStringDefaulted(XML_signinginstructions);
+            // we used to save this with an "o:" prefix, which is incorrect, 
so to support older
+            // data, try the older way if the correct way is empty.
+            if (mrShapeModel.maSignatureLineSigningInstructions.isEmpty())
+                mrShapeModel.maSignatureLineSigningInstructions
+                    = 
rAttribs.getStringDefaulted(O_TOKEN(signinginstructions));
             mrShapeModel.mbSignatureLineShowSignDate = 
ConversionHelper::decodeBool(
                 rAttribs.getString(XML_showsigndate, u"t"_ustr)); // default 
is true
             mrShapeModel.mbSignatureLineCanAddComment = 
ConversionHelper::decodeBool(
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport11.cxx 
b/sw/qa/extras/ooxmlexport/ooxmlexport11.cxx
index 869fdb02eca1..18abf65f0303 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport11.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport11.cxx
@@ -389,9 +389,6 @@ CPPUNIT_TEST_FIXTURE(Test, 
testTdf44832_testSectionWithDifferentHeader)
 
 DECLARE_OOXMLEXPORT_TEST(testSignatureLineShape, 
"signature-line-all-props-set.docx")
 {
-    // FIXME: validation error in OOXML export: Errors: 1
-    skipValidation();
-
     uno::Reference<drawing::XShape> xSignatureLineShape = getShape(1);
     uno::Reference<beans::XPropertySet> xPropSet(xSignatureLineShape, 
uno::UNO_QUERY);
 
commit a5c9179d5cd47b829fb708177a75739a80ecfc86
Author:     Xisco Fauli <[email protected]>
AuthorDate: Fri Jan 16 10:43:40 2026 +0100
Commit:     Andras Timar <[email protected]>
CommitDate: Wed Jan 21 16:45:23 2026 +0100

    sw_ooxmlexport13: Add test for b2dd08c6af51
    
    Change-Id: I0cae335f075b785de907c4aa8ec47c9ca086cd12
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/197436
    Tested-by: Jenkins
    Reviewed-by: Xisco Fauli <[email protected]>
    (cherry picked from commit eaca892394d5206e8a7062068b03df57353ac99f)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/197589
    Tested-by: Aron Budea <[email protected]>
    Reviewed-by: Aron Budea <[email protected]>

diff --git a/include/test/xmltesttools.hxx b/include/test/xmltesttools.hxx
index 94f4cf18a816..76140972c85b 100644
--- a/include/test/xmltesttools.hxx
+++ b/include/test/xmltesttools.hxx
@@ -73,6 +73,11 @@ protected:
      * Useful for checking relative order of elements.
      */
     int           getXPathPosition(const xmlDocUniquePtr& pXmlDoc, const char* 
pXPath, const char* pChildName);
+    /**
+     * Get the position of the attribute named rName of the parent node 
specified by pXPath.
+     * Useful for checking relative order of elements.
+     */
+    int           getXPathAttributePosition(const xmlDocUniquePtr& pXmlDoc, 
const char* pXPath, const char* pAttributeName);
     /**
      * Get the number of the nodes returned by the pXPath.
      */
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport13.cxx 
b/sw/qa/extras/ooxmlexport/ooxmlexport13.cxx
index 75e6ae776e66..4ada85283bde 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport13.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport13.cxx
@@ -756,6 +756,21 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf169802_hidden_shape)
     // Layout mustn't contain fly portion, without the fix it would contain 
several
     int nFlyNodes = countXPathNodes(pDump, "//*[contains(@type, 
'PortionType::Fly')]");
     CPPUNIT_ASSERT_EQUAL_MESSAGE("No fly portion nodes must exist in the 
layout", 0, nFlyNodes);
+
+    save(TestFilter::DOCX);
+
+    xmlDocUniquePtr pXmlDoc = parseExport(u"word/document.xml"_ustr);
+    const char* const 
sPath("/w:document/w:body/w:p[1]/w:r[1]/mc:AlternateContent/mc:Choice/w:drawing/wp:anchor");
+    CPPUNIT_ASSERT_EQUAL(0, getXPathAttributePosition(pXmlDoc, sPath, 
"distT"));
+    CPPUNIT_ASSERT_EQUAL(1, getXPathAttributePosition(pXmlDoc, sPath, 
"distB"));
+    CPPUNIT_ASSERT_EQUAL(2, getXPathAttributePosition(pXmlDoc, sPath, 
"distL"));
+    CPPUNIT_ASSERT_EQUAL(3, getXPathAttributePosition(pXmlDoc, sPath, 
"distR"));
+    CPPUNIT_ASSERT_EQUAL(4, getXPathAttributePosition(pXmlDoc, sPath, 
"simplePos"));
+    CPPUNIT_ASSERT_EQUAL(5, getXPathAttributePosition(pXmlDoc, sPath, 
"relativeHeight"));
+    CPPUNIT_ASSERT_EQUAL(6, getXPathAttributePosition(pXmlDoc, sPath, 
"behindDoc"));
+    CPPUNIT_ASSERT_EQUAL(7, getXPathAttributePosition(pXmlDoc, sPath, 
"locked"));
+    CPPUNIT_ASSERT_EQUAL(8, getXPathAttributePosition(pXmlDoc, sPath, 
"layoutInCell"));
+    CPPUNIT_ASSERT_EQUAL(9, getXPathAttributePosition(pXmlDoc, sPath, 
"allowOverlap"));
 }
 
 DECLARE_OOXMLEXPORT_TEST(testTdf124594, "tdf124594.docx")
diff --git a/test/source/xmltesttools.cxx b/test/source/xmltesttools.cxx
index aac5425cff55..70bd853bc785 100644
--- a/test/source/xmltesttools.cxx
+++ b/test/source/xmltesttools.cxx
@@ -329,6 +329,34 @@ int XmlTestTools::getXPathPosition(const xmlDocUniquePtr& 
pXmlDoc, const char* p
     return nRet;
 }
 
+int XmlTestTools::getXPathAttributePosition(const xmlDocUniquePtr& pXmlDoc, 
const char* pXPath, const char* pAttributeName)
+{
+    xmlXPathObjectPtr pXmlObj = getXPathNode(pXmlDoc, pXPath);
+    xmlNodeSetPtr pXmlNodes = pXmlObj->nodesetval;
+    CPPUNIT_ASSERT(pXmlNodes);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE(OString(OString::Concat("In <") + 
pXmlDoc->name + ">, XPath '" + pXPath + "' number of nodes is 
incorrect").getStr(),
+                                 1,
+                                 xmlXPathNodeSetGetLength(pXmlNodes));
+    xmlNodePtr pXmlNode = pXmlNodes->nodeTab[0];
+    int nRet = 0;
+    bool bFound = false;
+    for (xmlAttrPtr pAttribute = pXmlNode->properties; pAttribute; pAttribute 
= pAttribute->next)
+    {
+        if (oconvert(pAttribute->name) == pAttributeName)
+        {
+            bFound = true;
+            break;
+        }
+        ++nRet;
+    }
+    xmlXPathFreeObject(pXmlObj);
+    CPPUNIT_ASSERT_MESSAGE(OString(OString::Concat("In <") + pXmlDoc->name + 
">, XPath '" + pXPath
+                                   + "' attribute '" + pAttributeName + "' not 
found")
+                               .getStr(),
+        bFound);
+    return nRet;
+}
+
 void XmlTestTools::assertXPathNodeName(const xmlDocUniquePtr& pXmlDoc, const 
char* pXPath,
                                        std::string_view rExpectedName)
 {
commit da3545181012c3546ba49d70a5ba8fabdeb641d2
Author:     [email protected] <[email protected]>
AuthorDate: Thu Jan 15 16:58:32 2026 -0500
Commit:     Andras Timar <[email protected]>
CommitDate: Wed Jan 21 16:45:23 2026 +0100

    tdf#170322 docx export: close blockSDT when frame is done
    
    If a framePr frame contains (only) a blockSDT,
    then it was NOT closing the SDT element that it had started,
    and the popFromTableExportContext guard
    was resetting m_aParagraphSdt.m_bStartedSdt to false,
    and thus it was NEVER getting closed, creating invalid XML.
    
    make CppunitTest_sw_layoutwriter4 CPPUNIT_TEST_NAME=testTdf159259
    
    Change-Id: I882686474346accc56b95322e39c0a2c4756d21f
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/197410
    Reviewed-by: Justin Luth <[email protected]>
    Tested-by: Jenkins
    Signed-off-by: Xisco Fauli <[email protected]>
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/197556

diff --git a/sw/qa/extras/layout/layout4.cxx b/sw/qa/extras/layout/layout4.cxx
index 37a32c3ab533..ebe72e405b53 100644
--- a/sw/qa/extras/layout/layout4.cxx
+++ b/sw/qa/extras/layout/layout4.cxx
@@ -1122,11 +1122,11 @@ CPPUNIT_TEST_FIXTURE(SwLayoutWriter4, testTdf159259)
     CPPUNIT_ASSERT_EQUAL(paraHeight, flyHeight);
 
     // tdf#170322: MS Word considers the document corrupt if a plainText 
control contains a field
-    // save(TestFilter::DOCX);
-    // xmlDocUniquePtr pXmlDocument = parseExport(u"word/document.xml"_ustr);
-    // assertXPath(pXmlDocument, "//w:sdt/w:sdtPr", 1);
-    // // the sdtPr must be a richText control, not plainText
-    // assertXPath(pXmlDocument, "//w:sdt/w:sdtPr/w:text", 0);
+    save(TestFilter::DOCX);
+    xmlDocUniquePtr pXmlDocument = parseExport(u"word/document.xml"_ustr);
+    assertXPath(pXmlDocument, "//w:sdt/w:sdtPr", 1);
+    // the sdtPr must be a richText control, not plainText
+    assertXPath(pXmlDocument, "//w:sdt/w:sdtPr/w:text", 0);
 }
 
 CPPUNIT_TEST_FIXTURE(SwLayoutWriter4, testLargeTopParaMarginAfterHiddenSection)
diff --git a/sw/source/filter/ww8/docxattributeoutput.cxx 
b/sw/source/filter/ww8/docxattributeoutput.cxx
index de9da3af3c80..271cdc9b381b 100644
--- a/sw/source/filter/ww8/docxattributeoutput.cxx
+++ b/sw/source/filter/ww8/docxattributeoutput.cxx
@@ -1292,7 +1292,10 @@ void DocxAttributeOutput::EndParagraph( const 
ww8::WW8TableNodeInfoInner::Pointe
     {
         DocxTableExportContext aTableExportContext(*this);
         m_aFramePr.SetFrame(pFrame.get(), !m_xTableWrt ? -1 : 
m_tableReference.m_nTableDepth);
+        const bool bOldStartedSdt = m_aParagraphSdt.m_bStartedSdt;
         m_rExport.SdrExporter().writeOnlyTextOfFrame(pFrame.get());
+        if (!bOldStartedSdt && m_aParagraphSdt.m_bStartedSdt)
+            m_aParagraphSdt.EndSdtBlock(m_pSerializer);
         m_aFramePr.SetFrame(nullptr);
     }
 
commit e8bc4c66cd89461d70b1733f6e6526c322bf3ed6
Author:     Samuel Mehrbrodt <[email protected]>
AuthorDate: Mon Jan 12 14:52:42 2026 +0100
Commit:     Andras Timar <[email protected]>
CommitDate: Wed Jan 21 16:45:22 2026 +0100

    tdf#170340: Fix drag&drop with multiselection from extensions
    
    When registering drag gesture listeners via an extension,
    the selection handling code did not recognize that as "drag mode" being set.
    Dragging multiple elements did not work consequently.
    
    Change this and consider registered drag gesture listeners also as
    "drag mode" being set.
    
    Change-Id: I37de30ad3c385001f08988d462f6e7dfb3f0183c
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/197117
    Reviewed-by: Stephan Bergmann <[email protected]>
    Tested-by: Jenkins
    (cherry picked from commit 2173a79c691f793a6a6f55353d406e6c71c41e09)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/197219
    Reviewed-by: Thorsten Behrens <[email protected]>

diff --git a/include/vcl/dndlistenercontainer.hxx 
b/include/vcl/dndlistenercontainer.hxx
index a359796d1894..790c9b28aefc 100644
--- a/include/vcl/dndlistenercontainer.hxx
+++ b/include/vcl/dndlistenercontainer.hxx
@@ -79,6 +79,9 @@ public:
     virtual void SAL_CALL removeDragGestureListener( const 
css::uno::Reference< css::datatransfer::dnd::XDragGestureListener >& dgl ) 
override;
     virtual void SAL_CALL resetRecognizer(  ) override;
 
+    // Helper method to check if there are any drag gesture listeners 
registered
+    bool hasDragGestureListeners() const;
+
        /*
      * XDropTargetDragContext
      */
diff --git a/include/vcl/seleng.hxx b/include/vcl/seleng.hxx
index a0f069e04c1e..5f5498a6caec 100644
--- a/include/vcl/seleng.hxx
+++ b/include/vcl/seleng.hxx
@@ -91,6 +91,8 @@ private:
 
     inline bool         ShouldDeselect( bool bModifierKey1 ) const;
                                 // determines to deselect or not when Ctrl-key 
is pressed on CursorPosChanging
+    bool                IsDragEnabled() const;
+                                // checks if dragging is enabled via flag or 
drag gesture listeners
 public:
 
                         SelectionEngine( vcl::Window* pWindow,
diff --git a/vcl/source/window/dndlistenercontainer.cxx 
b/vcl/source/window/dndlistenercontainer.cxx
index f3f5b6cda0e5..1970bfcb524f 100644
--- a/vcl/source/window/dndlistenercontainer.cxx
+++ b/vcl/source/window/dndlistenercontainer.cxx
@@ -50,6 +50,12 @@ void SAL_CALL DNDListenerContainer::resetRecognizer(  )
 {
 }
 
+bool DNDListenerContainer::hasDragGestureListeners() const
+{
+    std::unique_lock g(m_aMutex);
+    return maDragGestureListeners.getLength(g) > 0;
+}
+
 void SAL_CALL DNDListenerContainer::addDropTargetListener( const Reference< 
XDropTargetListener >& dtl )
 {
     std::unique_lock g(m_aMutex);
diff --git a/vcl/source/window/seleng.cxx b/vcl/source/window/seleng.cxx
index a22ecaa7c5dd..05a5859bdbda 100644
--- a/vcl/source/window/seleng.cxx
+++ b/vcl/source/window/seleng.cxx
@@ -20,6 +20,7 @@
 #include <vcl/commandevent.hxx>
 #include <vcl/window.hxx>
 #include <vcl/seleng.hxx>
+#include <vcl/dndlistenercontainer.hxx>
 #include <comphelper/lok.hxx>
 #include <sal/log.hxx>
 
@@ -32,6 +33,22 @@ inline bool SelectionEngine::ShouldDeselect( bool 
bModifierKey1 ) const
     return eSelMode != SelectionMode::Multiple || !bModifierKey1;
 }
 
+bool SelectionEngine::IsDragEnabled() const
+{
+    // Check if drag is enabled via flag
+    if (nFlags & SelectionEngineFlags::DRG_ENAB)
+        return true;
+
+    // Extensions might have registered drag gesture listeners
+    // while the drag flag is not set - in this case we also
+    // want to allow drag operations. Otherwise D&D from
+    // extensions would not work properly (esp. with multiple selection).
+    if (!pWin)
+        return false;
+    rtl::Reference<DNDListenerContainer> rDropTarget = pWin->GetDropTarget();
+    return rDropTarget.is() && rDropTarget->hasDragGestureListeners();
+}
+
 // TODO: throw out FunctionSet::SelectAtPoint
 
 SelectionEngine::SelectionEngine( vcl::Window* pWindow, FunctionSet* pFuncSet 
) :
@@ -153,8 +170,9 @@ bool SelectionEngine::SelMouseButtonDown( const MouseEvent& 
rMEvt )
         case 0:     // KEY_NO_KEY
         {
             bool bSelAtPoint = pFunctionSet->IsSelectionAtPoint( aPos );
+            bool bDragEnabled = IsDragEnabled();
             nFlags &= ~SelectionEngineFlags::IN_ADD;
-            if ( (nFlags & SelectionEngineFlags::DRG_ENAB) && bSelAtPoint )
+            if ( bDragEnabled && bSelAtPoint )
             {
                 nFlags |= SelectionEngineFlags::WAIT_UPEVT;
                 nFlags &= ~SelectionEngineFlags::IN_SEL;
@@ -171,7 +189,7 @@ bool SelectionEngine::SelMouseButtonDown( const MouseEvent& 
rMEvt )
             }
             pFunctionSet->SetCursorAtPoint( aPos );
             // special case Single-Selection, to enable simple Select+Drag
-            if (eSelMode == SelectionMode::Single && (nFlags & 
SelectionEngineFlags::DRG_ENAB))
+            if (eSelMode == SelectionMode::Single && bDragEnabled)
                 nFlags |= SelectionEngineFlags::WAIT_UPEVT;
             return true;
         }
@@ -375,7 +393,7 @@ bool SelectionEngine::Command( const CommandEvent& rCEvt )
         return false;
 
     nFlags |= SelectionEngineFlags::CMDEVT;
-    if ( nFlags & SelectionEngineFlags::DRG_ENAB )
+    if ( IsDragEnabled() )
     {
         SAL_WARN_IF( !rCEvt.IsMouseEvent(), "vcl", "STARTDRAG: Not a 
MouseEvent" );
         if ( pFunctionSet->IsSelectionAtPoint( rCEvt.GetMousePosPixel() ) )
commit 8a9afc3fa143c70207ac4fed2182112638c4b98c
Author:     Justin Luth <[email protected]>
AuthorDate: Wed Jan 14 15:16:10 2026 -0500
Commit:     Andras Timar <[email protected]>
CommitDate: Wed Jan 21 16:45:22 2026 +0100

    tdf#170322 writerfilter: import fields into richText, not plainText
    
    MS Word complains that documents are corrupt
    if a <w:text/> w:sdt (plainText content control)
    contains a field.
    
    This failing was brought to our attention by 24.2.2
    commit 13a11632014ccc27199667c6a1e313f8ff616d6d
        "tdf#159259: make sure to set FieldStartRange in sdt helper"
    
    One 'field' that is NOT adversely affected is a hyperlink,
    but I don't see how to identify that, since
      m_pImpl->GetTopFieldContext()->GetFieldId()
    was optional-has_no_value at this point
    as seen with ooxmlexport3's glossaryWithEmail.docx.
    
    But I can't really imagine a big problem
    turning a plainText into a richText by accident
    for the cases where it is not strictly necessary.
    In fact, I expect that ALL unknown types
    ought to be richText instead of plainText,
    since by definition not specifying anything is a richText control.
    
    make CppunitTest_sw_layoutwriter4 CPPUNIT_TEST_NAME=testTdf159259
    except that this was not writing an ending </w:sdt>...
    
    Change-Id: Ib393f8eaf024d332be3f3603f72ef644fb5fe8c7
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/197300
    Tested-by: Jenkins
    Reviewed-by: Justin Luth <[email protected]>
    Signed-off-by: Xisco Fauli <[email protected]>
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/197555

diff --git a/sw/qa/extras/layout/layout4.cxx b/sw/qa/extras/layout/layout4.cxx
index b1e3897acd12..37a32c3ab533 100644
--- a/sw/qa/extras/layout/layout4.cxx
+++ b/sw/qa/extras/layout/layout4.cxx
@@ -1120,6 +1120,13 @@ CPPUNIT_TEST_FIXTURE(SwLayoutWriter4, testTdf159259)
 
     CPPUNIT_ASSERT_EQUAL(paraRight, flyRight); // The fly is right-aligned
     CPPUNIT_ASSERT_EQUAL(paraHeight, flyHeight);
+
+    // tdf#170322: MS Word considers the document corrupt if a plainText 
control contains a field
+    // save(TestFilter::DOCX);
+    // xmlDocUniquePtr pXmlDocument = parseExport(u"word/document.xml"_ustr);
+    // assertXPath(pXmlDocument, "//w:sdt/w:sdtPr", 1);
+    // // the sdtPr must be a richText control, not plainText
+    // assertXPath(pXmlDocument, "//w:sdt/w:sdtPr/w:text", 0);
 }
 
 CPPUNIT_TEST_FIXTURE(SwLayoutWriter4, testLargeTopParaMarginAfterHiddenSection)
diff --git a/sw/source/writerfilter/dmapper/DomainMapper.cxx 
b/sw/source/writerfilter/dmapper/DomainMapper.cxx
index c9598ebad25f..98ed01303c07 100644
--- a/sw/source/writerfilter/dmapper/DomainMapper.cxx
+++ b/sw/source/writerfilter/dmapper/DomainMapper.cxx
@@ -4884,6 +4884,8 @@ void DomainMapper::lcl_utext(const sal_Unicode *const 
data_, size_t len)
                         m_pImpl->m_pSdtHelper->createPlainTextControl();
                     else if (!m_pImpl->m_pSdtHelper->isFieldStartRangeSet())
                         
m_pImpl->m_pSdtHelper->setFieldStartRange(GetCurrentTextRange()->getEnd());
+                    // MS Word says plainText control containing a field is a 
corrupt file
+                    
m_pImpl->m_pSdtHelper->setControlType(SdtControlType::richText);
                 }
                 m_pImpl->AppendFieldCommand(sText);
             }
commit 0bfc1ebdffb364cb570903cc091a9ad7fca10475
Author:     Noel Grandin <[email protected]>
AuthorDate: Wed Jan 14 19:02:11 2026 +0200
Commit:     Andras Timar <[email protected]>
CommitDate: Wed Jan 21 16:45:22 2026 +0100

    officeotron: tabIndex=-1 is invalid
    
    since the value is constrained by the spec to be unsigned.
    
    Change-Id: I9b40b6fc9634cbe5c0e848ba15c9ce7918b2f067
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/197291
    Tested-by: Jenkins
    Reviewed-by: Michael Stahl <[email protected]>
    Signed-off-by: Xisco Fauli <[email protected]>
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/197431

diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport17.cxx 
b/sw/qa/extras/ooxmlexport/ooxmlexport17.cxx
index 28ed1b321bad..bddf3715c7bd 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport17.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport17.cxx
@@ -427,14 +427,10 @@ CPPUNIT_TEST_FIXTURE(Test, testDateContentControlExport)
     xContentControlProps->setPropertyValue(u"Alias"_ustr, 
uno::Any(u"myalias"_ustr));
     xContentControlProps->setPropertyValue(u"Tag"_ustr, 
uno::Any(u"mytag"_ustr));
     xContentControlProps->setPropertyValue(u"Id"_ustr, 
uno::Any(static_cast<sal_Int32>(123)));
-    xContentControlProps->setPropertyValue(u"TabIndex"_ustr, 
uno::Any(sal_uInt32(4294967295))); // -1
     xContentControlProps->setPropertyValue(u"Lock"_ustr, 
uno::Any(u"sdtLocked"_ustr));
 
     xText->insertTextContent(xCursor, xContentControl, /*bAbsorb=*/true);
 
-    // FIXME: validation error in OOXML export: Errors: 2
-    skipValidation();
-
     // When exporting to DOCX:
     save(TestFilter::DOCX);
 
@@ -457,7 +453,6 @@ CPPUNIT_TEST_FIXTURE(Test, testDateContentControlExport)
     assertXPath(pXmlDoc, "//w:sdt/w:sdtPr/w:alias", "val", u"myalias");
     assertXPath(pXmlDoc, "//w:sdt/w:sdtPr/w:tag", "val", u"mytag");
     assertXPath(pXmlDoc, "//w:sdt/w:sdtPr/w:id", "val", u"123");
-    assertXPath(pXmlDoc, "//w:sdt/w:sdtPr/w:tabIndex", "val", u"-1");
     assertXPath(pXmlDoc, "//w:sdt/w:sdtPr/w:lock", "val", u"sdtLocked");
 }
 
diff --git a/sw/source/filter/ww8/docxattributeoutput.cxx 
b/sw/source/filter/ww8/docxattributeoutput.cxx
index 3668c9b1aef5..de9da3af3c80 100644
--- a/sw/source/filter/ww8/docxattributeoutput.cxx
+++ b/sw/source/filter/ww8/docxattributeoutput.cxx
@@ -2720,8 +2720,9 @@ void DocxAttributeOutput::WriteContentControlStart()
     {
         // write the unsigned value as if it were signed since that is all we 
can import
         const sal_Int32 nTabIndex = 
static_cast<sal_Int32>(m_pContentControl->GetTabIndex());
-        m_pSerializer->singleElementNS(XML_w, XML_tabIndex, FSNS(XML_w, 
XML_val),
-                                       OString::number(nTabIndex));
+        if (nTabIndex != -1)
+            m_pSerializer->singleElementNS(XML_w, XML_tabIndex, FSNS(XML_w, 
XML_val),
+                                           OString::number(nTabIndex));
     }
 
     if (m_pContentControl->GetPicture())
commit c35f28cbbe31401112ce5635f2483126cb963123
Author:     Caolán McNamara <[email protected]>
AuthorDate: Fri Jan 16 11:39:28 2026 +0000
Commit:     Andras Timar <[email protected]>
CommitDate: Wed Jan 21 16:45:22 2026 +0100

    reuse comphelper::rng::uniform_uint_distribution
    
    see also:
    
    commit 6d9228d6b14d968fa92df3ca018a555f8652e579
    Date:   Fri May 3 14:17:27 2024 +0100
    
        lok: reseed comphelper's random number generator on fork.
    
        Also avoid std::random_device it doesn't work in a COOL
        kit process.
    
    Change-Id: I0ea3607a689632bf75861d367db70ac6c7f40a90
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/197443
    Tested-by: Jenkins
    Reviewed-by: Caolán McNamara <[email protected]>
    (cherry picked from commit 0e439c7289a3a0679929a9baf7ee41c8f31e6d96)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/197471

diff --git a/oox/source/ole/vbaexport.cxx b/oox/source/ole/vbaexport.cxx
index 049c8fc1e9a9..7131c8755d13 100644
--- a/oox/source/ole/vbaexport.cxx
+++ b/oox/source/ole/vbaexport.cxx
@@ -10,7 +10,6 @@
 #include <sal/config.h>
 
 #include <cassert>
-#include <random>
 #include <string_view>
 
 #include <oox/ole/vbaexport.hxx>
@@ -30,6 +29,7 @@
 
 #include <sot/storage.hxx>
 
+#include <comphelper/random.hxx>
 #include <comphelper/xmltools.hxx>
 #include <utility>
 #include <rtl/tencinfo.h>
@@ -376,14 +376,10 @@ VBAEncryption::VBAEncryption(const sal_uInt8* pData, 
const sal_uInt16 length,
     ,mnEncryptedByte2(0)
     ,mnProjKey(nProjKey)
     ,mnIgnoredLength(0)
-    ,mnSeed(0x00)
+    ,mnSeed(comphelper::rng::uniform_uint_distribution(0, 255))
     ,mnVersionEnc(0)
     ,meTextEncoding(eTextEncoding)
 {
-    std::random_device rd;
-    std::mt19937 gen(rd());
-    std::uniform_int_distribution<> dis(0, 255);
-    mnSeed = dis(gen);
 }
 
 void VBAEncryption::writeSeed()
commit bfc56d2e40705287a497dcbd7273932ebdf01be4
Author:     Ilmari Lauhakangas <[email protected]>
AuthorDate: Sat Jan 17 19:06:34 2026 +0200
Commit:     Andras Timar <[email protected]>
CommitDate: Wed Jan 21 16:45:22 2026 +0100

    tdf#168132 Make sure ranges do not show as disabled in Calc's
    
    External Data dialog
    
    Change-Id: I5cc64cea920f778e33f0a3e28e082839ceb8314a
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/197509
    Reviewed-by: Noel Grandin <[email protected]>
    Tested-by: Ilmari Lauhakangas <[email protected]>
    Reviewed-by: Ilmari Lauhakangas <[email protected]>
    Tested-by: Jenkins
    (cherry picked from commit c6028a9ce60f25027b2901e9cc03b3a8f8883810)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/197520

diff --git a/sc/source/ui/miscdlgs/linkarea.cxx 
b/sc/source/ui/miscdlgs/linkarea.cxx
index ca559e626aff..c02610c4fc30 100644
--- a/sc/source/ui/miscdlgs/linkarea.cxx
+++ b/sc/source/ui/miscdlgs/linkarea.cxx
@@ -266,8 +266,10 @@ void ScLinkedAreaDlg::UpdateSourceRanges()
 
     m_xLbRanges->thaw();
 
-    if (m_xLbRanges->n_children() >= 1)
+    if (m_xLbRanges->n_children() >= 1) {
         m_xLbRanges->select(0);
+        m_xLbRanges->set_sensitive(true);
+    }
     else
     {
         m_xLbRanges->append_text(ScResId(STR_NO_NAMED_RANGES_AVAILABLE));

Reply via email to