core.git: Branch 'libreoffice-26-2' - sw/qa sw/source
sw/qa/extras/htmlimport/data/tdf171772-inserted-text.html |2
sw/qa/extras/htmlimport/data/tdf171772-strikeout-variants.html |6 +
sw/qa/extras/htmlimport/htmlimport.cxx | 32
+-
sw/source/filter/html/swhtml.cxx |9 ++
4 files changed, 46 insertions(+), 3 deletions(-)
New commits:
commit 25b0f0562fd928373857274b341004e47a75e30b
Author: Andreas Heinisch
AuthorDate: Thu Apr 16 10:48:00 2026 +0200
Commit: Adolfo Jayme Barrientos
CommitDate: Fri Apr 17 02:16:45 2026 +0200
tdf#171772 - Properly close ins/del tag to prevent persistent formatting
Change-Id: I99a8dbed6075e05a70852d1165df4f6b30e56e39
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/203720
Tested-by: Jenkins
Reviewed-by: Andreas Heinisch
(cherry picked from commit d8b8dd009b6ec46f8745ac0fe22b8b3c4bc96377)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/203759
Reviewed-by: Adolfo Jayme Barrientos
diff --git a/sw/qa/extras/htmlimport/data/tdf171772-inserted-text.html
b/sw/qa/extras/htmlimport/data/tdf171772-inserted-text.html
new file mode 100644
index ..805a3c03dd2d
--- /dev/null
+++ b/sw/qa/extras/htmlimport/data/tdf171772-inserted-text.html
@@ -0,0 +1,2 @@
+ins
+auto
\ No newline at end of file
diff --git a/sw/qa/extras/htmlimport/data/tdf171772-strikeout-variants.html
b/sw/qa/extras/htmlimport/data/tdf171772-strikeout-variants.html
new file mode 100644
index ..3185b28746bc
--- /dev/null
+++ b/sw/qa/extras/htmlimport/data/tdf171772-strikeout-variants.html
@@ -0,0 +1,6 @@
+s
+auto
+strike
+auto
+del
+auto
\ No newline at end of file
diff --git a/sw/qa/extras/htmlimport/htmlimport.cxx
b/sw/qa/extras/htmlimport/htmlimport.cxx
index 842c5408d82c..ced37d9da9a4 100644
--- a/sw/qa/extras/htmlimport/htmlimport.cxx
+++ b/sw/qa/extras/htmlimport/htmlimport.cxx
@@ -408,11 +408,29 @@ CPPUNIT_TEST_FIXTURE(HtmlImportTest,
testTdf79298StrikeoutVariants)
getProperty(getRun(getParagraph(3), 1),
u"CharStrikeout"_ustr));
}
+CPPUNIT_TEST_FIXTURE(HtmlImportTest, testTdf171772StrikeoutVariants)
+{
+createSwWebDoc("tdf171772-strikeout-variants.html");
+
+// Without the accompanying fix in place, this tests would have failed
with:
+// - Expected: 0 (FontStrikeout::NONE)
+// - Actual : 1 (FontStrikeout::SINGLE)
+CPPUNIT_ASSERT_EQUAL_MESSAGE(
+"Strikeout for not properly closed",
sal_Int16(awt::FontStrikeout::NONE),
+getProperty(getRun(getParagraph(2), 1),
u"CharStrikeout"_ustr));
+CPPUNIT_ASSERT_EQUAL_MESSAGE(
+"Strikeout for not properly closed",
sal_Int16(awt::FontStrikeout::NONE),
+getProperty(getRun(getParagraph(4), 1),
u"CharStrikeout"_ustr));
+CPPUNIT_ASSERT_EQUAL_MESSAGE(
+"Strikeout for not properly closed",
sal_Int16(awt::FontStrikeout::NONE),
+getProperty(getRun(getParagraph(6), 1),
u"CharStrikeout"_ustr));
+}
+
CPPUNIT_TEST_FIXTURE(HtmlImportTest, testTdf132770InsertedText)
{
createSwWebDoc("tdf132770-inserted-text.html");
-// Without the accompanying fix in place, this tests would have failed
with:
+// Without the accompanying fix in place, this test would have failed with:
// - Expected: 1 (FontLineStyle::LINESTYLE_SINGLE)
// - Actual : 0 (FontLineStyle::NONE)
CPPUNIT_ASSERT_EQUAL_MESSAGE(
@@ -420,6 +438,18 @@ CPPUNIT_TEST_FIXTURE(HtmlImportTest,
testTdf132770InsertedText)
getProperty(getRun(getParagraph(1), 1),
u"CharUnderline"_ustr));
}
+CPPUNIT_TEST_FIXTURE(HtmlImportTest, testTdf171772InsertedText)
+{
+createSwWebDoc("tdf171772-inserted-text.html");
+
+// Without the accompanying fix in place, this test would have failed with:
+// - Expected: 0 (FontLineStyle::NONE)
+// - Actual : 1 (FontLineStyle::LINESTYLE_SINGLE)
+CPPUNIT_ASSERT_EQUAL_MESSAGE(
+"Underline for not properly closed",
sal_Int16(awt::FontUnderline::NONE),
+getProperty(getRun(getParagraph(2), 1),
u"CharUnderline"_ustr));
+}
+
CPPUNIT_TEST_FIXTURE(HtmlImportTest, testTdf142781)
{
// FIXME: the DPI check should be removed when either (1) the test is
fixed to work with
diff --git a/sw/source/filter/html/swhtml.cxx b/sw/source/filter/html/swhtml.cxx
index 9b3c37a3b808..47a6fb93eb40 100644
--- a/sw/source/filter/html/swhtml.cxx
+++ b/sw/source/filter/html/swhtml.cxx
@@ -1945,6 +1945,13 @@ void SwHTMLParser::NextToken( HtmlTokenId nToken )
EndTag( nToken );
break;
+// tdf#171772 - properly close inserted tag to prevent persistent underline
+case HtmlTokenId::INSERTEDTEXT_OFF:
+EndTag(HtmlTokenId::UNDERLINE_OFF);
+break;
+
+// tdf#171772 - properly close deleted tag to prevent persistent
strikethrough
+case HtmlTokenId::DELETEDTEXT_OFF:
case HtmlTokenId::STRIKETHROUGH_OFF:
EndTag( HtmlTokenId::STRIKE_OFF );
break;
@@ -2004,8 +2011,6 @@ void SwHTMLParser::NextToke
core.git: Branch 'libreoffice-26-2' - sw/qa sw/source
sw/qa/extras/ooxmlexport/ooxmlexport3.cxx |3 ++-
sw/source/filter/ww8/docxtableexport.cxx |2 +-
2 files changed, 3 insertions(+), 2 deletions(-)
New commits:
commit 8f5f726a55bb86acfab226cf32eac02e3f29c8c6
Author: Aron Budea
AuthorDate: Mon Mar 23 12:59:56 2026 +1030
Commit: Christian Lohmaier
CommitDate: Wed Apr 8 12:56:16 2026 +0200
sw: don't export true value for cantSplit element to OOXML
Not necessary, Word doesn't export it, and OOXML
validator complains about the test doc if val attribute
is present with true value:
{"Description":"The attribute
'http://schemas.openxmlformats.org/wordprocessingml/2006/main:val'
has invalid value 'true'. The Enumeration constraint failed.",
"Path":{"Namespaces":{},"XPath":
"/w:document[1]/w:body[1]/w:tbl[2]/w:tr[1]/w:trPr[1]/w:cantSplit[1]",
"PartUri":"/word/document.xml"},
"Id":"Sch_AttributeValueDataTypeDetailed","ErrorType":"Schema"}
Change-Id: I964320ea2ef6b48795224ac14545a2eaf4a3b5bc
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/202396
Tested-by: Jenkins
Reviewed-by: Aron Budea
(cherry picked from commit 9827413548210941eea1805a0ff653b07ccd164a)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/202680
Reviewed-by: Christian Lohmaier
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport3.cxx
b/sw/qa/extras/ooxmlexport/ooxmlexport3.cxx
index 852e7900f1dd..6e64eb87ebfc 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport3.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport3.cxx
@@ -992,7 +992,8 @@ CPPUNIT_TEST_FIXTURE(Test, testcantSplit)
// writing during export was causing problem
that all the cell data used to come on same page
xmlDocUniquePtr pXmlDoc = parseExport(u"word/document.xml"_ustr);
assertXPath(pXmlDoc,
"/w:document/w:body/w:tbl[1]/w:tr/w:trPr/w:cantSplit",0);
-assertXPath(pXmlDoc,
"/w:document/w:body/w:tbl[2]/w:tr/w:trPr/w:cantSplit","val",u"true");
+// this is how true is represented, otherwise OOXML validator complains:
+assertXPathNoAttribute(pXmlDoc,
"/w:document/w:body/w:tbl[2]/w:tr/w:trPr/w:cantSplit", "val");
}
CPPUNIT_TEST_FIXTURE(Test, testDontSplitTable)
diff --git a/sw/source/filter/ww8/docxtableexport.cxx
b/sw/source/filter/ww8/docxtableexport.cxx
index ab76c65ea2d3..757333d741f2 100644
--- a/sw/source/filter/ww8/docxtableexport.cxx
+++ b/sw/source/filter/ww8/docxtableexport.cxx
@@ -806,7 +806,7 @@ void DocxAttributeOutput::TableCanSplit(
// if rSplittable is true then no need to write
// as default row prop is allow row to break across page.
if (!rSplittable.GetValue())
-m_pSerializer->singleElementNS(XML_w, XML_cantSplit, FSNS(XML_w,
XML_val), "true");
+m_pSerializer->singleElementNS(XML_w, XML_cantSplit);
}
void DocxAttributeOutput::TableBidi(
core.git: Branch 'libreoffice-26-2' - sw/qa sw/source
sw/qa/extras/ww8export/data/clearing-break-with-fly.docx |binary
sw/qa/extras/ww8export/ww8export3.cxx| 13 +
sw/source/filter/ww8/ww8par.hxx |9 +
sw/source/filter/ww8/ww8par2.cxx | 10 +-
sw/source/filter/ww8/ww8par6.cxx |6 +-
5 files changed, 24 insertions(+), 14 deletions(-)
New commits:
commit bc4f4aff4ed0196fc2b23d61e319a2fa361837f7
Author: Caolán McNamara
AuthorDate: Fri Mar 13 20:55:50 2026 +
Commit: Christian Lohmaier
CommitDate: Tue Apr 7 14:36:18 2026 +0200
crashtesting: SwContentIndexReg::~SwContentIndexReg on reimport of docx->doc
seen on converting tdf105261-1.docx to doc and importing.
Looks to me that the ww8par6.cxx case is a copy of the ww8par2.cxx
case and we could use the bodges added for
ofz#38011 save and restore m_pLastAnchorPos via UnoCursor
ofz#9858 Bad-cast
in ww8par6.cxx too, but lets try replacing the character rather
than deleting and inserting a new one, and keep the flyframes
anchored to that position and see if can sidestep those issues.
Change-Id: Ib5da08558d054c5cae5935e525245a717fc9075c
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/201678
Reviewed-by: Caolán McNamara
Tested-by: Jenkins
(cherry picked from commit 9c7c9a59a753fd7765b53c4277e413367d6985f3)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/201809
Reviewed-by: Christian Lohmaier
diff --git a/sw/qa/extras/ww8export/data/clearing-break-with-fly.docx
b/sw/qa/extras/ww8export/data/clearing-break-with-fly.docx
new file mode 100644
index ..165ca7cffc46
Binary files /dev/null and
b/sw/qa/extras/ww8export/data/clearing-break-with-fly.docx differ
diff --git a/sw/qa/extras/ww8export/ww8export3.cxx
b/sw/qa/extras/ww8export/ww8export3.cxx
index 8dbd63fb7dac..269cc7cb2f50 100644
--- a/sw/qa/extras/ww8export/ww8export3.cxx
+++ b/sw/qa/extras/ww8export/ww8export3.cxx
@@ -1147,6 +1147,19 @@ CPPUNIT_TEST_FIXTURE(Test, testClearingBreak)
verify();
}
+CPPUNIT_TEST_FIXTURE(Test, testClearingBreakWithFly)
+{
+// Given a .docx with clearing breaks and fly frames anchored at the same
+// character positions, export to .doc and reimport:
+// Without the fix, the .doc import would crash because Read_LineBreakClear
+// used DeleteRange to replace the linebreak char, which triggered
+// DelFlyInRange and destroyed fly frames whose content nodes still had
+// content indices registered.
+createSwDoc("clearing-break-with-fly.docx");
+saveAndReload(TestFilter::DOC);
+CPPUNIT_ASSERT_EQUAL(3, getShapes());
+}
+
CPPUNIT_TEST_FIXTURE(Test, testTdf142840)
{
createSwDoc("tdf142840.odt");
diff --git a/sw/source/filter/ww8/ww8par.hxx b/sw/source/filter/ww8/ww8par.hxx
index caf93b45060a..3509ce0904be 100644
--- a/sw/source/filter/ww8/ww8par.hxx
+++ b/sw/source/filter/ww8/ww8par.hxx
@@ -118,6 +118,15 @@ namespace com::sun::star{
namespace lang{class XMultiServiceFactory;}
}
+// Replace a character with a text attribute item. Use EraseText rather than
+// DeleteRange to avoid DelFlyInRange deleting fly frames that happen to be
+// anchored at this character position.
+inline SwTextAttr* ReplaceCharWithItem(SwTextNode& rText, sal_Int32 nPos,
SfxPoolItem& rItem)
+{
+rText.EraseText(SwPosition(rText, nPos), 1);
+return rText.InsertItem(rItem, nPos, nPos);
+}
+
// defines only for the WW8-variable of the INI file
#define WW8FL_NO_STYLES 2
#define WW8FL_NO_GRAF 0x80
diff --git a/sw/source/filter/ww8/ww8par2.cxx b/sw/source/filter/ww8/ww8par2.cxx
index 46c493b84425..b633781f2b5b 100644
--- a/sw/source/filter/ww8/ww8par2.cxx
+++ b/sw/source/filter/ww8/ww8par2.cxx
@@ -222,16 +222,8 @@ sal_uInt16 SwWW8ImplReader::End_Footnote()
if (pText && nPos)
{
sChar += OUStringChar(pText->GetText()[--nPos]);
-m_pPaM->SetMark();
-m_pPaM->GetMark()->AdjustContent(-1);
-std::shared_ptr xLastAnchorCursor(m_oLastAnchorPos ?
m_rDoc.CreateUnoCursor(*m_oLastAnchorPos) : nullptr);
-m_oLastAnchorPos.reset();
-m_rDoc.getIDocumentContentOperations().DeleteRange( *m_pPaM );
-m_pPaM->DeleteMark();
-if (xLastAnchorCursor)
-m_oLastAnchorPos.emplace(*xLastAnchorCursor->GetPoint());
SwFormatFootnote aFootnote(rDesc.meType == MAN_EDN);
-pFN = static_cast(pText->InsertItem(aFootnote, nPos,
nPos));
+pFN = static_cast(ReplaceCharWithItem(*pText, nPos,
aFootnote));
}
OSL_ENSURE(pFN, "Problems creating the footnote text");
if (pFN)
diff --git a/sw/source/filter/ww8/ww8par6.cxx b/sw/source/filter/ww8/ww8par6.cxx
index 92ec2141ac79..09b6de463654 100644
--- a/sw/source/filter/ww8/ww8par6.cxx
+++ b/sw/source/filter/ww8/ww8par6.cxx
@@ -4736,13 +4736,9 @@ void SwWW8ImplReader::Read_LineBreakClear(sal_uInt16
/*nId*/, const sal
core.git: Branch 'libreoffice-26-2' - sw/qa sw/source
sw/qa/writerfilter/ooxml/data/floattable-multi-nested.docx |binary
sw/qa/writerfilter/ooxml/ooxml.cxx | 20 +
sw/source/writerfilter/ooxml/OOXMLParserState.cxx | 45 -
sw/source/writerfilter/ooxml/OOXMLParserState.hxx |6 +
sw/source/writerfilter/ooxml/OOXMLPropertySet.cxx |6 +
sw/source/writerfilter/ooxml/OOXMLPropertySet.hxx |2
6 files changed, 78 insertions(+), 1 deletion(-)
New commits:
commit 52a7c1d296ee819357db11a308166cb9a51aa8b8
Author: Miklos Vajna
AuthorDate: Wed Mar 18 16:29:18 2026 +0100
Commit: Christian Lohmaier
CommitDate: Tue Apr 7 14:34:05 2026 +0200
tdf#171387 sw floattable, DOCX import: avoid hang with 3 nested floating
tables
Regression from commit 5dc4ab7e3a071133b08db1c234eac6e6c253516e
(tdf#157119 sw floattable: fix moving master of split fly to next page,
2023-09-22), Writer layout went into a loop on opening the bugdoc.
There are multiple problems here at a Writer layout level, if the actual
feature of having 3 nested floating tables would have to work across
pages: first there is some oscillation around borders (+- 10 twips for
the calculated fly position, which looks like the border width of the
table), and then later a lot of new fly frames are created, even if
SwFrame::GetNextFlyLeaf() has code to reuse existing follows. These need
addressing if the actual feature of multi-nesting floating tables are
wanted.
However, the primary problem is that that the document doesn't open and
in practice the inner table needs no wrapping, it's nominally a floating
table probably by accident. So fix this at a DOCX tokenizer level: if a
floating table has multiple parents, then the inner table itself should
be inline, till Writer layout lacks the feature to work with this model.
A further alternative would be to fix this in
sw/source/writerfilter/dmapper/, but our import processes inner tables
first, so by the time we need to decide if the inner table should be
floating or not, dmapper doesn't have the info if the parent tables will
be floating or not, so that is not the right place to implement the DOCX
import change.
Change-Id: I32ac8d1c7c9b372bed32b56eb22f41c2d0f8925d
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/202149
Reviewed-by: Miklos Vajna
Tested-by: Jenkins
(cherry picked from commit f7faa162c274df921fdb431ddab7c74d26f9c1cf)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/202450
Reviewed-by: Christian Lohmaier
diff --git a/sw/qa/writerfilter/ooxml/data/floattable-multi-nested.docx
b/sw/qa/writerfilter/ooxml/data/floattable-multi-nested.docx
new file mode 100644
index ..1d49bc4714b7
Binary files /dev/null and
b/sw/qa/writerfilter/ooxml/data/floattable-multi-nested.docx differ
diff --git a/sw/qa/writerfilter/ooxml/ooxml.cxx
b/sw/qa/writerfilter/ooxml/ooxml.cxx
index 4a6940620c91..90a3fb85b774 100644
--- a/sw/qa/writerfilter/ooxml/ooxml.cxx
+++ b/sw/qa/writerfilter/ooxml/ooxml.cxx
@@ -171,6 +171,26 @@ CPPUNIT_TEST_FIXTURE(Test, testTableWafterRowHeight)
// 1 point for the border).
CPPUNIT_ASSERT_EQUAL(static_cast(240), nRow2Height);
}
+
+CPPUNIT_TEST_FIXTURE(Test, testFloattableMultiNested)
+{
+// Given a document with 3 nested floating tables (outer, middle, inner):
+// When laying out that document:
+createSwDoc("floattable-multi-nested.docx");
+
+// Then no layout loop should happen:
+// Without the accompanying fix in place, this test would have failed with
a layout loop.
+xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+// Page 1 has the 3 fly masters, the innermost as inline is OK:
+int nPage1Flys = countXPathNodes(pXmlDoc, "//page[1]/sorted_objs/fly");
+CPPUNIT_ASSERT_GREATEREQUAL(2, nPage1Flys);
+// Page 2 has the 3 fly follows, the innermost as inline is OK:
+int nPage2Flys = countXPathNodes(pXmlDoc, "//page[2]/sorted_objs/fly");
+CPPUNIT_ASSERT_GREATEREQUAL(2, nPage2Flys);
+// 6 table frames: 3 masters and 3 follows, all listed on the page of the
master's anchor:
+int nTables = countXPathNodes(pXmlDoc, "//page[1]//tab");
+CPPUNIT_ASSERT_EQUAL(6, nTables);
+}
}
CPPUNIT_PLUGIN_IMPLEMENT();
diff --git a/sw/source/writerfilter/ooxml/OOXMLParserState.cxx
b/sw/source/writerfilter/ooxml/OOXMLParserState.cxx
index 9a145bfdd038..41909c726f16 100644
--- a/sw/source/writerfilter/ooxml/OOXMLParserState.cxx
+++ b/sw/source/writerfilter/ooxml/OOXMLParserState.cxx
@@ -19,6 +19,7 @@
#include "OOXMLParserState.hxx"
#include "Handler.hxx"
+#include
#include
@@ -188,7 +189,20 @@ void OOXMLParserState::resolveTableProperties(Stream &
rStream)
if (rTableProps)
{
-rStream.props(rTableProps.get());
+bool bMultiNesting = m_nFloatingTableLevels >= 3;
+if (bMultiNesting && !m_aIsFl
core.git: Branch 'libreoffice-26-2' - sw/qa sw/source
sw/qa/extras/ooxmlexport/ooxmlexport25.cxx |2
sw/source/writerfilter/dmapper/DomainMapperTableHandler.cxx | 38 ++--
2 files changed, 21 insertions(+), 19 deletions(-)
New commits:
commit 50547484c58622b3d2c9b998ad9c0b6e33437282
Author: Justin Luth
AuthorDate: Thu Mar 19 10:45:58 2026 -0400
Commit: Adolfo Jayme Barrientos
CommitDate: Sun Apr 5 10:41:23 2026 +0200
tdf#167843 writerfilter: ignore style tblHeader if not using style
Thanks to Timur, who did the research for this
and basically handed the fix to me on a silver platter.
This fixes a regression from LO 6.4
commit f9aac900ada3d507526d5b51fc7a10ab4cae
tdf#81100 DOCX import: repeat header according to table style
Even though the firstRow table style defines firstRow as a header,
if the table is not using the firstRow style,
then don't apply the style's header setting.
make CppunitTest_sw_ooxmlexport25 \
CPPUNIT_TEST_NAME=testTdf167843_tblLook_firstRow_tblHeader
Change-Id: Ie000fd376d588ef0bd7a7e18eb92e9cbd851e448
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/202190
Reviewed-by: Justin Luth
Tested-by: Jenkins
(cherry picked from commit 41f9430dbd5617ddc0439cce0bfcc1fc68bca781)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/203213
Reviewed-by: Adolfo Jayme Barrientos
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport25.cxx
b/sw/qa/extras/ooxmlexport/ooxmlexport25.cxx
index 1704b47683aa..1f0c3e78cb11 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport25.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport25.cxx
@@ -109,7 +109,7 @@
DECLARE_OOXMLEXPORT_TEST(testTdf167843_tblLook_firstRow_tblHeader,
CPPUNIT_ASSERT_EQUAL(COL_AUTO, getProperty(xCell,
u"BackColor"_ustr));
// since firstRow style is not applied, do not inherit the style's
tblHeader
-// CPPUNIT_ASSERT_EQUAL(sal_Int32(0), getProperty(xTextTable,
u"HeaderRowCount"_ustr));
+CPPUNIT_ASSERT_EQUAL(sal_Int32(0), getProperty(xTextTable,
u"HeaderRowCount"_ustr));
// w:firstColumn not provided, so it is on by default (regardless of
w:val, although it agrees)
xCell.set(xTextTable->getCellByName(u"A1"_ustr), uno::UNO_QUERY);
diff --git a/sw/source/writerfilter/dmapper/DomainMapperTableHandler.cxx
b/sw/source/writerfilter/dmapper/DomainMapperTableHandler.cxx
index 9d0df3dbf5f1..b9b9baf52ece 100644
--- a/sw/source/writerfilter/dmapper/DomainMapperTableHandler.cxx
+++ b/sw/source/writerfilter/dmapper/DomainMapperTableHandler.cxx
@@ -369,6 +369,24 @@ TableStyleSheetEntry *
DomainMapperTableHandler::endTableGetTableStyle(TableInfo
aGrabBag[u"TablePosition"_ustr] = uno::Any();
}
+// This one is simply preserving all the table look attributes for a
round-trip.
+const std::optional oTableLook
+= m_aTableProperties->getProperty(META_PROP_TABLE_LOOK);
+if (oTableLook)
+{
+aGrabBag[u"TableStyleLook"_ustr] = oTableLook->second;
+m_aTableProperties->Erase(oTableLook->first);
+}
+
+// The "val" attribute's numeric value: a bit flag indicating applied
style elements.
+const std::optional aTblLook
+= m_aTableProperties->getProperty(PROP_TBL_LOOK);
+if(aTblLook)
+{
+aTblLook->second >>= rInfo.nTblLook;
+m_aTableProperties->Erase( aTblLook->first );
+}
+
std::optional aTableStyleVal =
m_aTableProperties->getProperty(META_PROP_TABLE_STYLE_NAME);
if(aTableStyleVal)
{
@@ -425,9 +443,9 @@ TableStyleSheetEntry *
DomainMapperTableHandler::endTableGetTableStyle(TableInfo
m_aTableProperties->dumpXml();
TagLogger::getInstance().endElement();
#endif
-if (pTableStyle)
+if (pTableStyle && (rInfo.nTblLook & 0x020)) // first row
style used
{
-// apply tblHeader setting of the table style
+// apply (without overwriting) tblHeader setting of the
table style
PropertyMapPtr pHeaderStyleProps =
pTableStyle->GetProperties(CNF_FIRST_ROW);
if ( pHeaderStyleProps->getProperty(PROP_HEADER_ROW_COUNT)
)
m_aTableProperties->Insert(PROP_HEADER_ROW_COUNT,
uno::Any( sal_Int32(1)), false);
@@ -435,22 +453,6 @@ TableStyleSheetEntry *
DomainMapperTableHandler::endTableGetTableStyle(TableInfo
}
}
-// This is the one preserving just all the table look attributes.
-std::optional oTableLook =
m_aTableProperties->getProperty(META_PROP_TABLE_LOOK);
-if (oTableLook)
-{
-aGrabBag[u"TableStyleLook"_ustr] = oTableLook->second;
-m_aTableProperties->Erase(oTableLook->first);
-}
-
-// This is just the "val" attribute's numeric value.
-const std::optional aTblLook =
core.git: Branch 'libreoffice-26-2' - sw/qa sw/source
sw/qa/extras/ooxmlexport/data/tdf167843_tblLook_firstRow_tblHeader.docx |binary
sw/qa/extras/ooxmlexport/ooxmlexport25.cxx | 33
+
sw/source/writerfilter/dmapper/DomainMapperTableManager.cxx | 64
+++---
sw/source/writerfilter/dmapper/DomainMapperTableManager.hxx | 14
+-
4 files changed, 95 insertions(+), 16 deletions(-)
New commits:
commit 974c1e10258171469afa0ac5f9369e4b4e879d0f
Author: Justin Luth
AuthorDate: Thu Mar 19 08:34:37 2026 -0400
Commit: Adolfo Jayme Barrientos
CommitDate: Sun Apr 5 10:40:07 2026 +0200
tdf#167843 writerfilter: prefer explict tblLook elements over w:val bits
In DOCX 2007, w:tblLook only specified a (legacy) w:val bitflag,
but in DOCX 2010, overriding individual elements were added
(and apparently in Strict mode the w:val is not specified at all).
Up till now, we had been ignoring the individual elements
and only using w:val.
If the w:val was not provided (as in Strict mode),
or changed to not match the individual elements,
then LO would apply the wrong table style elements.
Although tblLook has all kinds of Errata documentation,
nothing indicated how to handle this mixture of values.
Testing proved that if a single explicit element was provided,
then w:val was ignored, and that all unspecified elements are 'true'.
If tblLook is not provided at all, then it defaults to w:val 0x4A0.
make CppunitTest_sw_ooxmlexport25 \
CPPUNIT_TEST_NAME=testTdf167843_tblLook_firstRow_tblHeader
Change-Id: I47f8a24fa46868d393d95b68af08294f14970f00
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/202182
Reviewed-by: Justin Luth
Tested-by: Jenkins
(cherry picked from commit f53e97d238fb3baba551c98d02329cc595ba1750)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/203212
Reviewed-by: Adolfo Jayme Barrientos
diff --git
a/sw/qa/extras/ooxmlexport/data/tdf167843_tblLook_firstRow_tblHeader.docx
b/sw/qa/extras/ooxmlexport/data/tdf167843_tblLook_firstRow_tblHeader.docx
new file mode 100644
index ..9fbdf4c3059f
Binary files /dev/null and
b/sw/qa/extras/ooxmlexport/data/tdf167843_tblLook_firstRow_tblHeader.docx differ
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport25.cxx
b/sw/qa/extras/ooxmlexport/ooxmlexport25.cxx
index 2b43959614f5..1704b47683aa 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport25.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport25.cxx
@@ -91,6 +91,39 @@ DECLARE_OOXMLEXPORT_TEST(testTdf171299_tableInField,
"tdf171299_tableInField.doc
CPPUNIT_ASSERT_EQUAL(OUString("ID"), xCell->getString());
}
+DECLARE_OOXMLEXPORT_TEST(testTdf167843_tblLook_firstRow_tblHeader,
+ "tdf167843_tblLook_firstRow_tblHeader.docx")
+{
+// Given a table with a colorful table style defined
+// but a hand-mangled tblLook where the legacy w:val does not match the
explicit elements.
+//
+
+uno::Reference xTablesSupplier(mxComponent,
uno::UNO_QUERY);
+uno::Reference
xTables(xTablesSupplier->getTextTables(),
+uno::UNO_QUERY);
+uno::Reference xTextTable(xTables->getByIndex(0),
uno::UNO_QUERY);
+
+// w:firstRow=0, so firstRow style should not be applied - i.e. no
formatting.
+// w:val disagrees, but it is ignored since a w:firstRow has been
explicitly provided
+uno::Reference
xCell(xTextTable->getCellByName(u"B1"_ustr), uno::UNO_QUERY);
+CPPUNIT_ASSERT_EQUAL(COL_AUTO, getProperty(xCell,
u"BackColor"_ustr));
+
+// since firstRow style is not applied, do not inherit the style's
tblHeader
+// CPPUNIT_ASSERT_EQUAL(sal_Int32(0), getProperty(xTextTable,
u"HeaderRowCount"_ustr));
+
+// w:firstColumn not provided, so it is on by default (regardless of
w:val, although it agrees)
+xCell.set(xTextTable->getCellByName(u"A1"_ustr), uno::UNO_QUERY);
+CPPUNIT_ASSERT_EQUAL(Color(0xd9d9d9), getProperty(xCell,
u"BackColor"_ustr)); // gray 1
+
+// w:lastColumn not provided, so it is on by default (regardless of w:val,
which disagrees)
+xCell.set(xTextTable->getCellByName(u"G1"_ustr), uno::UNO_QUERY);
+CPPUNIT_ASSERT_EQUAL(Color(0xbfbfbf), getProperty(xCell,
u"BackColor"_ustr)); // gray 2
+
+// w:lastRow not provided, so it is on by default (regardless of w:val,
which disagrees)
+xCell.set(xTextTable->getCellByName(u"A8"_ustr), uno::UNO_QUERY);
+CPPUNIT_ASSERT_EQUAL(Color(0xc1e4f5), getProperty(xCell,
u"BackColor"_ustr)); // blue
+}
+
DECLARE_OOXMLEXPORT_TEST(testTdf166141_linkedStyles,
"tdf166141_linkedStyles.docx")
{
// Given a document with settings.xml containing both linkStyles and
attachedTemplate
diff --git a/sw/source/writerfilter/dmapper/DomainMapperTableManager.cxx
b/sw/source/writerfilter/dmapper/DomainMapperTableManager.cxx
index 95aebf8f730e..bc2595a0a7c4 100644
--- a/sw/source/writerfilter/dmapper/Do
core.git: Branch 'libreoffice-26-2' - sw/qa sw/source
sw/qa/extras/rtfexport/data/tdf134614_toc_indent.rtf | 17 ++
sw/qa/extras/rtfexport/rtfexport8.cxx| 50 +++
sw/source/filter/ww8/rtfattributeoutput.cxx | 26 +
sw/source/filter/ww8/rtfattributeoutput.hxx |5 +
4 files changed, 97 insertions(+), 1 deletion(-)
New commits:
commit e9efa3b88ea33fe557b673895be5f80bd03647d9
Author: Mike Kaganski
AuthorDate: Sun Mar 22 20:17:27 2026 +0500
Commit: Adolfo Jayme Barrientos
CommitDate: Fri Apr 3 10:21:01 2026 +0200
tdf#134614: defer closing TOC field after the last contained \par
When EndTOX() closed the TOC field during the last paragraph's run, the }}}
went into m_aRunText and ended up before \par, putting the \par outside the
field result. Word then ignored paragraph properties for that last
paragraph.
This change defers outputting the closing braces until after \par is
written.
Change-Id: I6d7ab3b516e725938fc2151f8ec4f4b834f33068
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/202379
Tested-by: Jenkins
Reviewed-by: Mike Kaganski
(cherry picked from commit e39a2d201ec1cc136e4ef4f4c5b539ab46a55b87)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/202497
Reviewed-by: Adolfo Jayme Barrientos
diff --git a/sw/qa/extras/rtfexport/data/tdf134614_toc_indent.rtf
b/sw/qa/extras/rtfexport/data/tdf134614_toc_indent.rtf
new file mode 100644
index ..a26cd4360ec9
--- /dev/null
+++ b/sw/qa/extras/rtfexport/data/tdf134614_toc_indent.rtf
@@ -0,0 +1,17 @@
+{ tf1nsi\deff0
+{onttbl{0swisscharset0 Liberation Sans;}}
+{\stylesheet
+{\s00s20\sb0\sa0 Normal;}
+{\s10s24\sb0\sa0 TOC 1;}
+{\s20s24\li360\sb0\sa0 TOC 3;}
+{\s3\outlinelevel00s32\sb240\sa60 Heading 1;}
+{\s4\outlinelevel20s26\sb240\sa60 Heading 3;}
+}
+\pard\s1 qr ldotx9270\plain0s24{ield{\*ldinst TOC \o "1-9"
\h}{ldrslt {ield{\*ldinst HYPERLINK \l "_Toc1"}{ldrslt First Heading ab
1}}\par
+\pard\s2 qr ldotx9270\li360 {ield{\*ldinst HYPERLINK \l
"_Toc2"}{ldrslt Sub Heading ab 1}}\par}}
+\par
+\pard\s3\sb240\sa60{\*kmkstart _Toc1}{\*kmkend _Toc1}First Heading\par
+\pard\s00 Some body text under first heading.\par
+\pard\s4\sb240\sa60{\*kmkstart _Toc2}{\*kmkend _Toc2}Sub Heading\par
+\pard\s00 Some body text under sub heading.\par
+}
diff --git a/sw/qa/extras/rtfexport/rtfexport8.cxx
b/sw/qa/extras/rtfexport/rtfexport8.cxx
index 136b614ab125..bed821cd788f 100644
--- a/sw/qa/extras/rtfexport/rtfexport8.cxx
+++ b/sw/qa/extras/rtfexport/rtfexport8.cxx
@@ -1324,6 +1324,56 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf168533)
getShape(2).queryThrow()->getString());
}
+CPPUNIT_TEST_FIXTURE(Test, testTdf134614_TOC_indent)
+{
+// Given an RTF document with a TOC field containing indented entries:
+createSwDoc("tdf134614_toc_indent.rtf");
+save(TestFilter::RTF);
+
+SvStream* pStream = maTempFile.GetStream(StreamMode::READ);
+CPPUNIT_ASSERT(pStream);
+OString aRtfContent(read_uInt8s_ToOString(*pStream, pStream->TellEnd()));
+
+// Check that a position is inside a given RTF group scope. Returns true
if nPos is reached
+// before the scope closes.
+auto isInsideScope = [&aRtfContent](sal_Int32 nScopeStart, sal_Int32 nPos)
{
+int nDepth = 1; // the opening { is before nScopeStart
+for (sal_Int32 i = nScopeStart; i < aRtfContent.getLength(); ++i)
+{
+if (i == nPos)
+return true;
+
+if (char ch = aRtfContent[i]; ch == '{')
+++nDepth;
+else if (ch == '}' && --nDepth == 0)
+return false;
+}
+return false;
+};
+
+// Find the TOC field result and the last TOC entry
+sal_Int32 nFldrslt = aRtfContent.indexOf("\fldrslt");
+CPPUNIT_ASSERT_GREATER(sal_Int32(0), nFldrslt);
+sal_Int32 nLastEntry = aRtfContent.indexOf("Sub Heading", nFldrslt);
+CPPUNIT_ASSERT_GREATER(sal_Int32(0), nLastEntry);
+sal_Int32 nParAfterLastEntry = aRtfContent.indexOf("\par", nLastEntry);
+CPPUNIT_ASSERT_GREATER(sal_Int32(0), nParAfterLastEntry);
+
+// \par for the last TOC entry must be inside the {ldrslt ...} scope.
Without the fix, the
+// field-close braces were appended during the last paragraph's run,
before \par, placing \par
+// outside the field result. Word then ignored paragraph properties (like
\li indent) for that
+// last entry.
+CPPUNIT_ASSERT(isInsideScope(nFldrslt, nParAfterLastEntry));
+
+// The TOC entries may contain inner HYPERLINK fields. Their field-close
must NOT be deferred -
+// only the outermost TOC field close should be. Verify that the inner
HYPERLINK field around
+// "Sub Heading" is fully closed before \par.
+sal_Int32 nInnerFldrslt = aRtfContent.lastIndexOf("\fldrslt", nLastEntry);
+CPPUNIT_ASSERT_GREATER(nFldrslt, nInnerFldrslt); // must be a differen
core.git: Branch 'libreoffice-26-2' - sw/qa sw/source
sw/qa/extras/ooxmlexport/data/tdf164694__import.docx |binary
sw/qa/extras/ooxmlexport/ooxmlexport17.cxx | 29 +
sw/source/writerfilter/dmapper/DomainMapper_Impl.cxx |2
sw/source/writerfilter/dmapper/SdtHelper.cxx | 33 +++
sw/source/writerfilter/dmapper/SdtHelper.hxx |2
5 files changed, 58 insertions(+), 8 deletions(-)
New commits:
commit ff75436c42ae93cbb3164a6d2392db3de96f15b1
Author: Aron Budea
AuthorDate: Wed Mar 18 00:21:05 2026 +1030
Commit: Adolfo Jayme Barrientos
CommitDate: Fri Apr 3 09:20:59 2026 +0200
tdf#164694 content control: import data-bound 4-digit year as proper date
Otherwise it's treated as number, and eg. year 2011
becomes 2011 days from start of epoch: 1905.
Could be seen in date picker and when exported to DOCX.
Clashing values:
- word/headerX.xml:
- customXml/item1.xml: 2011
On data binding, custom XML element takes priority.
Change-Id: I222f20bdc69fee3571859d408ee74de95d5c092f
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/201921
Tested-by: Jenkins
Reviewed-by: Aron Budea
(cherry picked from commit 41c400c577207dbdf73640b6e6eed2f685971342)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/202681
Reviewed-by: Adolfo Jayme Barrientos
diff --git a/sw/qa/extras/ooxmlexport/data/tdf164694__import.docx
b/sw/qa/extras/ooxmlexport/data/tdf164694__import.docx
new file mode 100644
index ..26b050211898
Binary files /dev/null and
b/sw/qa/extras/ooxmlexport/data/tdf164694__import.docx differ
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport17.cxx
b/sw/qa/extras/ooxmlexport/ooxmlexport17.cxx
index 9d11d309a074..3984518e9bf4 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport17.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport17.cxx
@@ -461,6 +461,35 @@ CPPUNIT_TEST_FIXTURE(Test, testDateContentControlExport)
assertXPath(pXmlDoc, "//w:sdt/w:sdtPr/w:lock", "val", u"sdtLocked");
}
+CPPUNIT_TEST_FIXTURE(Test, testTdf164694__import)
+{
+createSwDoc("tdf164694__import.docx");
+saveAndReload(TestFilter::DOCX);
+
+xmlDocUniquePtr pXmlHeader = parseExport(u"word/header2.xml"_ustr);
+CPPUNIT_ASSERT(pXmlHeader);
+// Without the fix in place this would be 1905-07-03T00:00:00Z (= 2011
days from start of epoch)
+CPPUNIT_ASSERT_EQUAL(
+u"2011-01-01T00:00:00Z"_ustr,
+getXPath(pXmlHeader,
"/w:hdr/w:tbl/w:tr/w:tc[2]/w:p/w:sdt/w:sdtPr/w:date", "fullDate"));
+// This would still be OK without the fix
+CPPUNIT_ASSERT_EQUAL(
+u"2011"_ustr,
+getXPathContent(pXmlHeader,
"/w:hdr/w:tbl/w:tr/w:tc[2]/w:p/w:sdt/w:sdtContent/w:r[2]/w:t"));
+saveAndReload(TestFilter::DOCX);
+
+pXmlHeader = parseExport(u"word/header2.xml"_ustr);
+CPPUNIT_ASSERT(pXmlHeader);
+// Same as previous
+CPPUNIT_ASSERT_EQUAL(
+u"2011-01-01T00:00:00Z"_ustr,
+getXPath(pXmlHeader,
"/w:hdr/w:tbl/w:tr/w:tc[2]/w:p/w:sdt/w:sdtPr/w:date", "fullDate"));
+// Without the fix in place this would be 1905
+CPPUNIT_ASSERT_EQUAL(
+u"2011"_ustr,
+getXPathContent(pXmlHeader,
"/w:hdr/w:tbl/w:tr/w:tc[2]/w:p/w:sdt/w:sdtContent/w:r[2]/w:t"));
+}
+
CPPUNIT_TEST_FIXTURE(Test, testNegativePageBorder)
{
// Given a document with a negative border distance:
diff --git a/sw/source/writerfilter/dmapper/DomainMapper_Impl.cxx
b/sw/source/writerfilter/dmapper/DomainMapper_Impl.cxx
index 8d59fc382a79..6f6ffbb7ae20 100644
--- a/sw/source/writerfilter/dmapper/DomainMapper_Impl.cxx
+++ b/sw/source/writerfilter/dmapper/DomainMapper_Impl.cxx
@@ -1257,7 +1257,7 @@ void DomainMapper_Impl::PopSdt()
OUString aCurrentDate = m_pSdtHelper->getDate().makeStringAndClear();
if (oData.has_value())
{
-aCurrentDate = *oData;
+aCurrentDate = SdtHelper::AdjustDateString(*oData, aDateFormat);
bDateFromDataBinding = true;
}
xContentControl->setPropertyValue(u"CurrentDate"_ustr,
diff --git a/sw/source/writerfilter/dmapper/SdtHelper.cxx
b/sw/source/writerfilter/dmapper/SdtHelper.cxx
index cd841ecc9e60..e103508c8d01 100644
--- a/sw/source/writerfilter/dmapper/SdtHelper.cxx
+++ b/sw/source/writerfilter/dmapper/SdtHelper.cxx
@@ -12,6 +12,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -38,6 +39,8 @@
#include
#include
+#include
+
namespace writerfilter::dmapper
{
using namespace ::com::sun::star;
@@ -275,6 +278,26 @@ std::optional
SdtHelper::getValueFromDataBinding()
return {};
}
+OUString SdtHelper::AdjustDateString(std::u16string_view sDate,
std::u16string_view sDateFormat)
+{
+OUString sResult(sDate);
+
+sal_Int32 nTimeSep = sResult.indexOf("T");
+// Trim time part of datetime
+if (nTimeSep != -1)
+{
+sResult = sResult.copy(0, nTimeSep);
+}
+// Special case: only year in
core.git: Branch 'libreoffice-26-2' - sw/qa sw/source
sw/qa/extras/ooxmlexport/data/tdf145542_image_size_rotated_and_stretched.fodt
| 22 +
sw/qa/extras/ooxmlexport/ooxmlexport26.cxx
| 16
sw/source/filter/ww8/docxattributeoutput.cxx
| 39 ++
3 files changed, 65 insertions(+), 12 deletions(-)
New commits:
commit b69d66620f26caf74d209bd3a38d9b60ce1550bd
Author: Mike Kaganski
AuthorDate: Mon Mar 23 11:35:50 2026 +0500
Commit: Adolfo Jayme Barrientos
CommitDate: Wed Mar 25 11:52:33 2026 +0100
tdf#145542: DOCX export: also handle rotated+stretched case
Commit Ic9da5b2f8b47818a47e03cac620440108e0ca595 handled the non-rotated
case
by using LayoutSize instead of the Size property. But for images that are
both
rotated AND scaled (e.g., relative width + rotation), Size still gave the
unstretched unrotated dimensions — wrong for both wp:extent and
a:xfrm/a:ext.
Derive the scale factor from the bounding box (LayoutSize), the unscaled
size
(Size property), and the rotation angle. Get the stretched-but-unrotated
size
scaling Size by it. This handles all three cases.
There is some difference how rotated+scaled objects are layed out by Writer
and Word. This change improves the export to DOCX, but not perfectly.
Change-Id: I013659d324b9f3e9f5f2e9ee737544ea5610bd17
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/202408
Tested-by: Jenkins
Reviewed-by: Mike Kaganski
Signed-off-by: Xisco Fauli
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/202621
Reviewed-by: Adolfo Jayme Barrientos
diff --git
a/sw/qa/extras/ooxmlexport/data/tdf145542_image_size_rotated_and_stretched.fodt
b/sw/qa/extras/ooxmlexport/data/tdf145542_image_size_rotated_and_stretched.fodt
new file mode 100644
index ..981e752ff7a3
--- /dev/null
+++
b/sw/qa/extras/ooxmlexport/data/tdf145542_image_size_rotated_and_stretched.fodt
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
iVBORw0KGgoNSUhEUgAAAMYAAABjAQMAAAD6hO1DBlBMVEUAAAD/AAAb/40iAXRSTlMAQObYZgAAAK9JREFUSMed1rGJA0AMBECX9AU8uPUtaQsQyIkDBx4wUjrBcZxO2sdn/T1U/5SnJZRShrKWUEoZylpCKWUoawmllKGsJZRShrKWUEoZylpCKWUoawmllKGsJZRSBvI+aL9WKKUMZS2hlDKUtYRSylB6OOdwn1JCObxP2SGhHPqtlFAO/6eUUA7zoJRQDvOtlFAO87qUUA77p5RQDvu0lFAO+aCUUA55p5RQfstvznwvOCFYloT8wJsASUVORK5CYII
+
+
+The image above has svg:width=3cm but style:rel-width=100%, so it
should display at full page width. It is also rotated 30 degrees. The DOCX
export should write the stretched-but-unrotated size.
+
+
+
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport26.cxx
b/sw/qa/extras/ooxmlexport/ooxmlexport26.cxx
index 0f3dddc141f2..c7cb964a0ad9 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport26.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport26.cxx
@@ -929,6 +929,22 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf145542_imageSize)
CPPUNIT_ASSERT_GREATER(sal_Int64(600), nCx); // 170 mm = 612 EMU
}
+CPPUNIT_TEST_FIXTURE(Test, testTdf145542_imageSizeRotatedAndStretched)
+{
+// Given an ODT with an image that is both rotated (30 degrees) and
stretched
+// (svg:width="3cm" but style:rel-width="100%"). The DOCX export must
write the
+// stretched-but-unrotated size, not the unstretched 3cm.
+createSwDoc("tdf145542_image_size_rotated_and_stretched.fodt");
+save(TestFilter::DOCX);
+
+xmlDocUniquePtr pXmlDoc = parseExport(u"word/document.xml"_ustr);
+// The stretched-but-unrotated width for a 3:2 rectangle scaled to fill
17cm text area
+// and rotated 30 degrees is ~14.2cm (≈ 510 EMU). Without the fix,
this was ~108
+// (the unstretched 3cm).
+const sal_Int64 nCx = getXPath(pXmlDoc, "//wp:inline/wp:extent",
"cx").toInt64();
+CPPUNIT_ASSERT_GREATER(sal_Int64(500), nCx);
+}
+
CPPUNIT_PLUGIN_IMPLEMENT();
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/ww8/docxattributeoutput.cxx
b/sw/source/filter/ww8/docxattributeoutput.cxx
index edd6b8215fd3..7fa05ed63f0e 100644
--- a/sw/source/filter/ww8/docxattributeoutput.cxx
+++ b/sw/source/filter/ww8/docxattributeoutput.cxx
@@ -102,6 +102,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -5529,19 +5530,33 @@ void DocxAttributeOutput::FlyFrameGraphic( const
SwGrfNode* pGrfNode, const Size
assert(xShapePropSet);
}
+// aSize is used for both wp:extent and a:xfrm/a:ext (unrotated content
size).
+// tdf#138953: for rotated images, aSize must be the unrotated size, not
LayoutSize (rSize)
+// which is the rotation bounding box. tdf#145542: for non-rotated images,
Size property may
+// differ from LayoutSize when scaled (e.g., relative width or older ODT),
so use rSize.
+// For the rotated+scaled case, derive the stretched-but-unrotated size
from the bounding
+// box (rSize), the unscaled size (Size property), and the rotation a
core.git: Branch 'libreoffice-26-2' - sw/qa sw/source
sw/qa/extras/ooxmlexport/data/tdf145542_image_size.fodt | 21
sw/qa/extras/ooxmlexport/ooxmlexport26.cxx | 14 ++
sw/source/filter/ww8/docxattributeoutput.cxx| 12 ++---
3 files changed, 44 insertions(+), 3 deletions(-)
New commits:
commit 5ddd8324b3b95fd0c569ad14d455300c52ccfb07
Author: Mike Kaganski
AuthorDate: Mon Mar 23 01:06:12 2026 +0500
Commit: Xisco Fauli
CommitDate: Tue Mar 24 16:12:44 2026 +0100
tdf#145542: DOCX export: use layout size for non-rotated images
The fix for tdf#138953 changed the DOCX image export to always use the
UNO Size property (unrotated dimensions) instead of the frame LayoutSize.
This is correct for rotated unstretched images, where LayoutSize includes
the rotation bounding box. But for non-rotated images where Size differs
from LayoutSize (e.g., frames with style:rel-width="100%" imported from
HTML or saved by older LO versions), it used the unstretched size, giving
wrong image size.
Only use the Size property when the image is actually rotated.
Note: if an image is both rotated AND scaled, Size still gives the wrong
(unstretched unrotated) dimensions. The correct fix for that case would
require deriving the scale factor from Size, LayoutSize, and the rotation
angle.
Change-Id: Ic9da5b2f8b47818a47e03cac620440108e0ca595
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/202386
Reviewed-by: Mike Kaganski
Tested-by: Jenkins
(cherry picked from commit b0939ce1e1defcd4e12b49019d9fa0c7c1de0213)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/202446
Reviewed-by: Xisco Fauli
diff --git a/sw/qa/extras/ooxmlexport/data/tdf145542_image_size.fodt
b/sw/qa/extras/ooxmlexport/data/tdf145542_image_size.fodt
new file mode 100644
index ..31b1ed8efbc6
--- /dev/null
+++ b/sw/qa/extras/ooxmlexport/data/tdf145542_image_size.fodt
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
iVBORw0KGgoNSUhEUgAAAMYAAABjAQMAAAD6hO1DBlBMVEUAAAD/AAAb/40iAXRSTlMAQObYZgAAAK9JREFUSMed1rGJA0AMBECX9AU8uPUtaQsQyIkDBx4wUjrBcZxO2sdn/T1U/5SnJZRShrKWUEoZylpCKWUoawmllKGsJZRShrKWUEoZylpCKWUoawmllKGsJZRSBvI+aL9WKKUMZS2hlDKUtYRSylB6OOdwn1JCObxP2SGhHPqtlFAO/6eUUA7zoJRQDvOtlFAO87qUUA77p5RQDvu0lFAO+aCUUA55p5RQfstvznwvOCFYloT8wJsASUVORK5CYII
+
+
+
+
+
\ No newline at end of file
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport26.cxx
b/sw/qa/extras/ooxmlexport/ooxmlexport26.cxx
index 055a49636e68..0f3dddc141f2 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport26.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport26.cxx
@@ -915,6 +915,20 @@
DECLARE_OOXMLEXPORT_TEST(testTdf143384_tableInFoot_negativeMargins,
CPPUNIT_ASSERT_EQUAL(1, getPages());
}
+CPPUNIT_TEST_FIXTURE(Test, testTdf145542_imageSize)
+{
+// Given an ODT with an image in a frame: svg:width="2cm" but
style:rel-width="100%",
+// so the displayed size is full page width, not 2cm.
+createSwDoc("tdf145542_image_size.fodt");
+save(TestFilter::DOCX);
+
+xmlDocUniquePtr pXmlDoc = parseExport(u"word/document.xml"_ustr);
+// tdf#145542: the exported wp:extent must reflect the rendered size (full
page width 17cm),
+// not the unstretched svg:width="2cm" (= 72 EMU). Without the fix,
this was 72.
+const sal_Int64 nCx = getXPath(pXmlDoc, "//wp:inline/wp:extent",
"cx").toInt64();
+CPPUNIT_ASSERT_GREATER(sal_Int64(600), nCx); // 170 mm = 612 EMU
+}
+
CPPUNIT_PLUGIN_IMPLEMENT();
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/ww8/docxattributeoutput.cxx
b/sw/source/filter/ww8/docxattributeoutput.cxx
index 4c0ac61e07b2..edd6b8215fd3 100644
--- a/sw/source/filter/ww8/docxattributeoutput.cxx
+++ b/sw/source/filter/ww8/docxattributeoutput.cxx
@@ -5530,9 +5530,15 @@ void DocxAttributeOutput::FlyFrameGraphic( const
SwGrfNode* pGrfNode, const Size
}
Size aSize = rSize;
-// We need the original (cropped, but unrotated) size of object. So prefer
the object data,
-// and only use passed frame size as fallback.
-if (xShapePropSet)
+// tdf#138953: for rotated images, we need the unrotated size from the
object's Size property,
+// because rSize (layout size) includes the rotation bounding box.
tdf#145542: for non-rotated
+// images, size may differ from layout size when the image was scaled
(e.g., HTML import with
+// relative width, or older ODT files). In that case, rSize (layout size)
is the correct size.
+// TODO/FIXME: if an image is both rotated AND scaled, Size gives the
unstretched unrotated
+// dimensions, which is still wrong. The correct value would be the
stretched-but-unrotated
+// size, e.g. by deriving the scale factor from Size, LayoutSize, and the
rotation angle.
+if (xShapePropSet && pGrfNode
+&& pGrfNode->GetSwAttrSet().Get(RES_GRFATR_ROTATION).GetValue() !=
core.git: Branch 'libreoffice-26-2' - sw/qa sw/source
sw/qa/core/text/itrpaint.cxx | 20
sw/source/core/text/itrpaint.cxx |2 +-
2 files changed, 21 insertions(+), 1 deletion(-)
New commits:
commit 88331a6e8f7c25c0a987c9316737ec046478bca1
Author: Miklos Vajna
AuthorDate: Fri Mar 20 11:31:04 2026 +0100
Commit: Adolfo Jayme Barrientos
CommitDate: Tue Mar 24 10:28:50 2026 +0100
tdf#171191 sw redline render mode: fix crash on IME input
Create a new Writer document, enable track changes, type "a", "1", "a"
with IME (pinyin) input, crash.
What seems to happen is that we have a transient redlie that's not yet
inserted into the redline table but already has its index assigned while
IME composition is in progress.
Fix this by simply handling this as if there would be no redline: once
the composition is done, we'll re-render anyway and that index will be
inside the bounds of the redline array anyway.
Regression from commit a24dd7b1d742ccd59768db8b6b6a522588952108
(cool#13574 sw redline render mode: avoid coloring, set lightness,
2026-01-15).
Change-Id: I08c667d4da40d20cd3a54ffbcb53f5acd2912722
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/202248
Reviewed-by: Miklos Vajna
Tested-by: Jenkins
(cherry picked from commit 2c8e2b71d7c6630c14229f194682a7a1b198d3e1)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/202499
Reviewed-by: Adolfo Jayme Barrientos
diff --git a/sw/qa/core/text/itrpaint.cxx b/sw/qa/core/text/itrpaint.cxx
index 710f359ac0fc..012f653a7de9 100644
--- a/sw/qa/core/text/itrpaint.cxx
+++ b/sw/qa/core/text/itrpaint.cxx
@@ -16,8 +16,11 @@
#include
#include
#include
+#include
#include
+#include
+#include
#include
#include
#include
@@ -374,6 +377,23 @@ CPPUNIT_TEST_FIXTURE(Test,
testInlineImageRedlineRenderModeOmitInsertDelete)
CPPUNIT_ASSERT(!aPolygons.empty());
CPPUNIT_ASSERT(RectangleContainsPolygons(aImages[2].m_aRectangle,
aPolygons));
}
+
+CPPUNIT_TEST_FIXTURE(Test, testRedlineRenderModeIME)
+{
+// Given an empty document with track changes enabled:
+createSwDoc();
+dispatchCommand(mxComponent, u".uno:TrackChanges"_ustr, {});
+
+// When typing via IME:
+// Then we should not crash on rendering:
+SwDocShell* pDocShell = getSwDocShell();
+SwEditWin& rEditWin = pDocShell->GetView()->GetEditWin();
+rEditWin.PostExtTextInputEvent(VclEventId::ExtTextInput, u"a"_ustr);
+rEditWin.PostExtTextInputEvent(VclEventId::EndExtTextInput, u""_ustr);
+// Without the accompanying fix in place, this test would have crashed.
+rEditWin.PostExtTextInputEvent(VclEventId::ExtTextInput, u"a"_ustr);
+rEditWin.PostExtTextInputEvent(VclEventId::EndExtTextInput, u""_ustr);
+}
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/itrpaint.cxx b/sw/source/core/text/itrpaint.cxx
index 33af3776a82c..e26079d76c51 100644
--- a/sw/source/core/text/itrpaint.cxx
+++ b/sw/source/core/text/itrpaint.cxx
@@ -149,7 +149,7 @@ SwTextPaintOmitter::SwTextPaintOmitter(SwTextPainter&
rPainter, const SwRedlineT
}
SwRedlineTable::size_type nRedline = rPainter.GetRedln()->GetAct();
-if (nRedline == SwRedlineTable::npos)
+if (nRedline == SwRedlineTable::npos || nRedline >= rRedlineTable.size())
{
return;
}
core.git: Branch 'libreoffice-26-2' - sw/qa sw/source
sw/qa/extras/ooxmlexport/data/tdf171383_refFieldInCell.docx |binary
sw/qa/extras/ooxmlexport/ooxmlexport25.cxx | 20
sw/source/writerfilter/dmapper/DomainMapper_Impl.cxx|4 ++
3 files changed, 24 insertions(+)
New commits:
commit c1f2bd5c839eb5c594def2beeec76fafce32e4c9
Author: Justin Luth
AuthorDate: Wed Mar 18 06:22:57 2026 -0400
Commit: Adolfo Jayme Barrientos
CommitDate: Thu Mar 19 22:37:20 2026 +0100
tdf#171383 writerfilter: provide cell start range with delayed fields
This fixes an issue exposed in 25.8.0 by
commit aa689d596c44c1c51ab9345df5d063768936fe74
tdf#166940 sw: defer line break in ref field
The ending content of the last cell
was being pulled in the following cell
if that cell started with a delayed-finish field.
make CppunitTest_sw_ooxmlexport25 \
CPPUNIT_TEST_NAME=testTdf171383_refFieldInCell
Change-Id: I8e0b1a314fad02748a1b12e92e561858de519bac
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/201990
Reviewed-by: Justin Luth
Tested-by: Jenkins
(cherry picked from commit 40bc407071e1a7037877743fbaed1133309a3628)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/202158
Reviewed-by: Adolfo Jayme Barrientos
diff --git a/sw/qa/extras/ooxmlexport/data/tdf171383_refFieldInCell.docx
b/sw/qa/extras/ooxmlexport/data/tdf171383_refFieldInCell.docx
new file mode 100644
index ..ffd45cc65686
Binary files /dev/null and
b/sw/qa/extras/ooxmlexport/data/tdf171383_refFieldInCell.docx differ
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport25.cxx
b/sw/qa/extras/ooxmlexport/ooxmlexport25.cxx
index 32f4ccc2fef1..2b43959614f5 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport25.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport25.cxx
@@ -54,6 +54,26 @@ DECLARE_OOXMLEXPORT_TEST(testTdf166544_noTopMargin_fields,
"tdf166544_noTopMargi
CPPUNIT_ASSERT_EQUAL(sal_Int32(269), nHeight);
}
+DECLARE_OOXMLEXPORT_TEST(testTdf171383_refFieldInCell,
"tdf171383_refFieldInCell.docx")
+{
+// given a document where the table contains a multi-paragraph reference
field
+
+uno::Reference xTablesSupplier(mxComponent,
uno::UNO_QUERY);
+uno::Reference
xTables(xTablesSupplier->getTextTables(),
+uno::UNO_QUERY);
+uno::Reference xTextTable(xTables->getByIndex(0),
uno::UNO_QUERY);
+uno::Reference
xCell(xTextTable->getCellByName(u"A1"_ustr), uno::UNO_QUERY);
+CPPUNIT_ASSERT_EQUAL(OUString("Xyz"), xCell->getString());
+uno::Reference xPara(getParagraphOfText(1,
xCell->getText()),
+ uno::UNO_QUERY);
+CPPUNIT_ASSERT_EQUAL(OUString("1"), getProperty(xPara,
u"ListLabelString"_ustr));
+
+xCell.set(xTextTable->getCellByName(u"B1"_ustr), uno::UNO_QUERY);
+CPPUNIT_ASSERT(xCell->getString().startsWith("Issue 1"));
+xPara.set(getParagraphOfText(1, xCell->getText()), uno::UNO_QUERY);
+CPPUNIT_ASSERT_EQUAL(OUString(""), getProperty(xPara,
u"ListLabelString"_ustr));
+}
+
DECLARE_OOXMLEXPORT_TEST(testTdf171299_tableInField,
"tdf171299_tableInField.docx")
{
// given a document where the field contains a table
diff --git a/sw/source/writerfilter/dmapper/DomainMapper_Impl.cxx
b/sw/source/writerfilter/dmapper/DomainMapper_Impl.cxx
index 0e2c419dc510..8d59fc382a79 100644
--- a/sw/source/writerfilter/dmapper/DomainMapper_Impl.cxx
+++ b/sw/source/writerfilter/dmapper/DomainMapper_Impl.cxx
@@ -2251,6 +2251,10 @@ void DomainMapper_Impl::finishParagraph( const
PropertyMapPtr& pPropertyMap, con
{
if (pFieldContext->GetFieldId() == FIELD_IF ||
pFieldContext->GetFieldId() == FIELD_REF)
{
+// in case a cell needs to start: make sure it knows the
current text position
+if (hasTableManager())
+
getTableManager().handle(m_rDMapper.GetCurrentTextRange()->getEnd());
+
// Conditional text fields can't contain newlines, finish the
paragraph later.
FieldParagraph aFinish{pPropertyMap, bRemove};
pFieldContext->GetParagraphsToFinish().push_back(aFinish);
core.git: Branch 'libreoffice-26-2' - sw/qa sw/source
sw/qa/extras/layout/data/tdf171161.fodt | 14
sw/qa/extras/layout/layout3.cxx | 35
sw/source/core/text/inftxt.cxx | 17 ---
3 files changed, 63 insertions(+), 3 deletions(-)
New commits:
commit 2bf50f714307fba9e8a83e9853ff2ccc192eb335
Author: László Németh
AuthorDate: Thu Mar 12 11:51:05 2026 +0100
Commit: Xisco Fauli
CommitDate: Fri Mar 13 12:38:38 2026 +0100
tdf#171161 tdf#168251 sw glyph scaling: fix scale width regression
Do not disable scale width character setting if
line-level automatic glyph scaling is not applied
in the paragraph.
Regression since commit 45ec7bd76196dcc60b4c2db2f6f00623ecbaf5a4
"tdf#168251 cui offapi xmloff sw glyph scaling: extend UNO/UX/ODF".
Change-Id: I3d47be27349f0be4c4c65f1da5662a3daf6c7063
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/201528
Reviewed-by: László Németh
Tested-by: Jenkins
(cherry picked from commit 7ed8a8ee39951f74e364091fafff20269359ba15)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/201562
Reviewed-by: Xisco Fauli
diff --git a/sw/qa/extras/layout/data/tdf171161.fodt
b/sw/qa/extras/layout/data/tdf171161.fodt
new file mode 100644
index ..6cd03b5802fc
--- /dev/null
+++ b/sw/qa/extras/layout/data/tdf171161.fodt
@@ -0,0 +1,14 @@
+
+
+http://openoffice.org/2004/office";
xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0"
xmlns:drawooo="http://openoffice.org/2010/draw";
xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0"
xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0"
xmlns:of="urn:oasis:names:tc:opendocument:xmlns:of:1.2"
xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0"
office:mimetype="application/vnd.oasis.opendocument.text">
+
+
+
+
+
+
+
+ Aa
+
+
+
diff --git a/sw/qa/extras/layout/layout3.cxx b/sw/qa/extras/layout/layout3.cxx
index d6f43dd6ff6b..d10736e6ecb1 100644
--- a/sw/qa/extras/layout/layout3.cxx
+++ b/sw/qa/extras/layout/layout3.cxx
@@ -847,6 +847,41 @@ CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf168251)
}
}
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf171161)
+{
+createSwDoc("tdf171161.fodt");
+// Ensure that all text portions are calculated before testing.
+SwDocShell* pShell = getSwDocShell();
+
+// Dump the rendering of the first page as an XML file.
+std::shared_ptr xMetaFile = pShell->GetPreviewMetaFile();
+MetafileXmlDump dumper;
+
+xmlDocUniquePtr pXmlDoc = dumpAndParse(dumper, *xMetaFile);
+CPPUNIT_ASSERT(pXmlDoc);
+
+// Find the first text array action
+for (size_t nAction = 0; nAction < xMetaFile->GetActionSize(); nAction++)
+{
+auto pAction = xMetaFile->GetAction(nAction);
+if (pAction->GetType() == MetaActionType::TEXTARRAY)
+{
+auto pTextArrayAction = static_cast(pAction);
+auto pDXArray = pTextArrayAction->GetDXArray();
+
+// There should be 2 characters in the first portion on the first
line
+CPPUNIT_ASSERT_EQUAL(size_t(2), pDXArray.size());
+
+// Assert we are using the expected position for the
+// second character of the first word with narrow scale width.
+// This was 17490, now 8745, according to the 50% letter scaling
+CPPUNIT_ASSERT_LESS(sal_Int32(8800), sal_Int32(pDXArray[1]));
+
+break;
+}
+}
+}
+
CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf169168_scaling)
{
createSwDoc("tdf169168_scaling.fodt");
diff --git a/sw/source/core/text/inftxt.cxx b/sw/source/core/text/inftxt.cxx
index da133a9c3dec..e7260836279d 100644
--- a/sw/source/core/text/inftxt.cxx
+++ b/sw/source/core/text/inftxt.cxx
@@ -29,6 +29,8 @@
#include
#include
#include
+#include
+#include
#include
#include
#include
@@ -810,9 +812,18 @@ void SwTextPaintInfo::DrawText_( const OUString &rText,
const SwLinePortion &rPo
if ( GetLetterSpacing() != 0 )
aDrawInf.SetLetterSpacing( GetLetterSpacing() );
-// set custom glyph scaling (hyphenation hasn't been supported yet)
-// Note: set 100 percent, too (to reset the setting of the
previous line)
-aDrawInf.SetScaleWidth( GetScaleWidth() );
+const SwAttrSet& rAttrSet =
GetTextFrame()->GetTextNodeForParaProps()->GetSwAttrSet();
+const SvxAdjustItem* pAdjust = rAttrSet.GetItem(RES_PARATR_ADJUST);
+// microtypograpy: line-level automatic scaling between the custom
range
+if ( pAdjust->GetPropScaleWidthMinimum() != 100 ||
+pAdjust->GetPropScaleWidthMaximum() != 100 )
+{
+// Note: set 100 percent, too (to reset the automatic scaling
of the previous line)
+aDrawInf.SetScaleWidth( GetScaleWidth() );
+}
+else
+ // tdf#171161 otherwise use
core.git: Branch 'libreoffice-26-2' - sw/qa sw/source
sw/qa/extras/layout/data/min2.fodt | 320 + sw/qa/extras/layout/layout3.cxx| 19 ++ sw/source/core/layout/tabfrm.cxx |4 3 files changed, 343 insertions(+) New commits: commit aa09fc511da5f16af6ebe5b56ef407394ee3902f Author: Michael Stahl AuthorDate: Wed Feb 25 09:33:48 2026 +0100 Commit: Adolfo Jayme Barrientos CommitDate: Wed Mar 4 17:35:00 2026 +0100 sw: layout: invalidate first row when table upper margin changes The problem is that when the user inserts a paragraph break in the yellow section on the first page, the table below this section disappears. Previously, one line of text of the table was on the first page, but it must move to the next page now, and then what happens is that inside SwTabFrame::Format() the CalcFlyOffsets() finds an overlap with the text frame that was already on the 2nd page; at this time the table print area height is 9711, and this causes the table frame to have the Y position of the print area set to 10383. Now the first row is positioned based on that, but then another iteration of SwTabFrame::Format() sees its height as 7672 and thus the table now fits before the text frame, the table frame upper margin is reset to 0. But the problem is, the first row's position is not invalidated, so all the table content remains positioned outside the table frame. Reportedly this started to happen since commit 8e3afdb5989d571410350f1d43fcf26492a4eaff and apparently the problem doesn't happen when there isn't a fly anchored in the footer but there's no obvious relation to the missing table invalidation. Change-Id: I6552ef3340b47abcfa5eb3a70aa1dc27733da253 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/200294 Reviewed-by: Miklos Vajna Tested-by: Jenkins CollaboraOffice (cherry picked from commit 66723c13181e0e56a6a9e55862ece318c9d45f23) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/200340 Tested-by: Jenkins Reviewed-by: Michael Stahl (cherry picked from commit ce6536debfd0ad4530c7121f9e48b9278d204f6b) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/200450 Reviewed-by: Adolfo Jayme Barrientos diff --git a/sw/qa/extras/layout/data/min2.fodt b/sw/qa/extras/layout/data/min2.fodt new file mode 100644 index ..4b50cd7570d6 --- /dev/null +++ b/sw/qa/extras/layout/data/min2.fodt @@ -0,0 +1,320 @@ + +http://www.w3.org/TR/css3-text/"; xmlns:grddl="http://www.w3.org/2003/g/data-view#"; xmlns:xhtml="http://www.w3.org/1999/xhtml"; xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; xmlns:xsd="http://www.w3.org/2001/XMLSchema"; xmlns:xforms="http://www.w3.org/2002/xforms"; xmlns:dom="http://www.w3.org/2001/xml-events"; xmlns:script="urn:oasis:names:tc:opendocument:xmlns:script:1.0" xmlns:form="urn:oasis:names:tc:opendocument:xmlns:form:1.0" xmlns:math="http://www.w3.org/1998/Math/MathML"; xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:ooo="http://openoffice.org/2004/office"; xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:config="urn:oasis:names:tc:opendocument:xmlns:config:1.0" xmlns:ooow="http://openoffice.org/2004/writer"; xmlns:xlink="http://www.w3.org/1999/xlink"; xmlns:drawooo="http://openoffice.org/2010/draw"; xmlns:oooc="http://openoffice.org/2004/calc"; xmlns:dc="http://purl.org/dc/elements/1.1/"; xmlns:c alcext="urn:org:documentfoundation:names:experimental:calc:xmlns:calcext: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:of="urn:oasis:names:tc:opendocument:xmlns:of:1.2" xmlns:tableooo="http://openoffice.org/2009/table"; xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" xmlns:dr3d="urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0" xmlns:rpt="http://openoffice.org/2005/report"; xmlns:formx="urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:form:1.0" xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" xmlns:chart="urn:oasis:names:tc:opendocument:xmlns:chart:1.0" xmlns:officeooo="http://openoffice.org/2009/office"; xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:field="urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0" xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns: meta:1.0" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" office:version="1.4" office:mimetype="application/vnd.oasis.opendocument.text"> + Collabora_Office/25.04.9.1$Linux_X86_64 LibreOffice_project/2853d108adde0c3ee82e729c99d284a2a9fdd6262009-11-03T15:47:08.98000de-DE170P1DT32M10S2026-02-10T10:45:40.24200 + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
core.git: Branch 'libreoffice-26-2' - sw/qa sw/source
sw/qa/extras/layout/data/tdf16.fodt | 13 +
sw/qa/extras/layout/layout6.cxx | 26 ++
sw/source/core/layout/calcmove.cxx |3 ++-
3 files changed, 41 insertions(+), 1 deletion(-)
New commits:
commit 3fee62758506d1667df37402ad8aab8cdc01032f
Author: Mike Kaganski
AuthorDate: Mon Feb 16 16:45:49 2026 +0500
Commit: Xisco Fauli
CommitDate: Fri Feb 20 10:28:57 2026 +0100
tdf#16: Also hide footnotes when text frame is hidden
This is similar to what MakePrtArea does in that case: call HideHidden.
Change-Id: I0625c00c5ec168698feed9d34079a1e84a8fa1b9
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/199477
Tested-by: Jenkins
Reviewed-by: Mike Kaganski
Signed-off-by: Xisco Fauli
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/199499
diff --git a/sw/qa/extras/layout/data/tdf16.fodt
b/sw/qa/extras/layout/data/tdf16.fodt
new file mode 100644
index ..c554309a6e5b
--- /dev/null
+++ b/sw/qa/extras/layout/data/tdf16.fodt
@@ -0,0 +1,13 @@
+
+
+http://openoffice.org/2004/writer"; office:version="1.4"
office:mimetype="application/vnd.oasis.opendocument.text">
+
+
+
+
+This is a hidden section.1
+ This is a footnote in the hidden
section. Here are some random words in
it.
+
+
+
+
\ No newline at end of file
diff --git a/sw/qa/extras/layout/layout6.cxx b/sw/qa/extras/layout/layout6.cxx
index f4c8cb590ffc..75abbc4fc7d5 100644
--- a/sw/qa/extras/layout/layout6.cxx
+++ b/sw/qa/extras/layout/layout6.cxx
@@ -2204,6 +2204,32 @@ CPPUNIT_TEST_FIXTURE(SwLayoutWriter6, testTdf170811)
parseLayoutDump();
}
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter6, testTdf16)
+{
+// Open a document with a section with a paragraph with a footnote
+createSwDoc("tdf16.fodt");
+
+// Initially, the hide condition evaluates to false, so footnote is
visible:
+{
+xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+assertXPath(pXmlDoc, "//ftn", 1);
+}
+
+// Set hide condition to "1"
+auto xTextSectionsSupplier =
mxComponent.queryThrow();
+auto xSections = xTextSectionsSupplier->getTextSections();
+CPPUNIT_ASSERT(xSections);
+auto xSection =
xSections->getByName(u"Section1"_ustr).queryThrow();
+xSection->setPropertyValue(u"Condition"_ustr, css::uno::Any(u"1"_ustr));
+Scheduler::ProcessEventsToIdle();
+
+// The footnote must get hidden - without the fix, the text failed,
because there was a ftn
+{
+xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+assertXPath(pXmlDoc, "//ftn", 0);
+}
+}
+
} // end of anonymous namespace
CPPUNIT_PLUGIN_IMPLEMENT();
diff --git a/sw/source/core/layout/calcmove.cxx
b/sw/source/core/layout/calcmove.cxx
index 1fdc0c047e5e..e7d8d4949b79 100644
--- a/sw/source/core/layout/calcmove.cxx
+++ b/sw/source/core/layout/calcmove.cxx
@@ -1326,11 +1326,12 @@ void SwContentFrame::MakeAll(vcl::RenderContext*
/*pRenderContext*/)
bool const isHiddenNow(static_cast(this)->IsHiddenNowImpl());
if (isHiddenNow)
{
+// Similar to respective code in MakePrtArea
while (HasFollow())
{
static_cast(*this).JoinFrame();
}
-HideAndShowObjects();
+static_cast(this)->HideHidden(); // also hides footnotes
}
std::optional oDeleteGuard(std::in_place, this);
core.git: Branch 'libreoffice-26-2' - sw/qa sw/source
sw/qa/extras/ooxmlexport/ooxmlexport18.cxx | 22 +-
sw/source/filter/ww8/docxattributeoutput.cxx |6 --
2 files changed, 25 insertions(+), 3 deletions(-)
New commits:
commit 4e394b423acade3313072731f8863e8407a5465e
Author: Justin Luth
AuthorDate: Fri Feb 13 09:34:04 2026 -0500
Commit: Xisco Fauli
CommitDate: Fri Feb 20 08:42:52 2026 +0100
tdf#147724 docx export: don't export storeItemID if empty
This patch is for the benefit of MS Word,
which reports a document as corrupt if w:storeItemID=""
It is kindof interesting, because it is not invalid
if storeItemID is not provided.
But if it is provided, then it must not be empty.
make CppunitTest_sw_ooxmlexport18 CPPUNIT_TEST_NAME=testTdf147724
Change-Id: Iaa1de4b98e3470a3ab28fe48a46252060e70a257
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/199354
Tested-by: Jenkins
Reviewed-by: Justin Luth
(cherry picked from commit 3eb2871aefd01f2d0ce51848b7a6760b6adc9cb8)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/199391
Reviewed-by: Xisco Fauli
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport18.cxx
b/sw/qa/extras/ooxmlexport/ooxmlexport18.cxx
index 47c3a69bdf1b..39294987712c 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport18.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport18.cxx
@@ -608,8 +608,10 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf151912)
assertXPath(pXmlDoc, "//w:sdt//w:sdtPr/w:id", "val", u"1802566103");
}
-DECLARE_OOXMLEXPORT_TEST(testTdf147724, "tdf147724.docx")
+CPPUNIT_TEST_FIXTURE(Test, testTdf147724)
{
+skipValidation(); // ERROR Attribute 'storeItemID' must appear on element
'w:dataBinding'.
+createSwDoc("tdf147724.docx");
xmlDocUniquePtr pLayout = parseLayoutDump();
// Ensure we load field value from external XML correctly (it was
"HERUNTERLADEN")
@@ -619,6 +621,24 @@ DECLARE_OOXMLEXPORT_TEST(testTdf147724, "tdf147724.docx")
// There 2 variants possible, both are acceptable
OUString sFieldResult = getXPathContent(pLayout,
"/root/page[1]/body/txt[2]");
CPPUNIT_ASSERT(sFieldResult == "Placeholder -> *HERUNTERLADEN*" ||
sFieldResult == "Placeholder -> *ABC*");
+
+saveAndReload(TestFilter::DOCX);
+pLayout = parseLayoutDump();
+
+// Ensure we load field value from external XML correctly (it was
"HERUNTERLADEN")
+assertXPathContent(pLayout, "/root/page[1]/body/txt[1]", u"Placeholder ->
*ABC*");
+
+// This SDT has no storage id, it is not an error, but content can be
taken from any suitable XML
+// There 2 variants possible, both are acceptable
+sFieldResult = getXPathContent(pLayout, "/root/page[1]/body/txt[2]");
+CPPUNIT_ASSERT(sFieldResult == "Placeholder -> *HERUNTERLADEN*" ||
sFieldResult == "Placeholder -> *ABC*");
+
+xmlDocUniquePtr pXmlDoc = parseExport(u"word/document.xml"_ustr);
+assertXPath(pXmlDoc, "//w:p/w:sdt/w:sdtPr/w:dataBinding", 2); // two
w:sdt's with w:dataBinding
+assertXPath(pXmlDoc, "//w:p[1]/w:sdt/w:sdtPr/w:dataBinding", "storeItemID",
+u"{4E8A9591-F074-446B-902F-511FF79C122F}");
+// empty storeItemID's must not be specified: MS Word considers those
document corrupt
+assertXPathNoAttribute(pXmlDoc, "//w:p[2]/w:sdt/w:sdtPr/w:dataBinding",
"storeItemID");
}
DECLARE_OOXMLEXPORT_TEST(testTdf130782, "chart.docx")
diff --git a/sw/source/filter/ww8/docxattributeoutput.cxx
b/sw/source/filter/ww8/docxattributeoutput.cxx
index 6f9a64543a4e..4c892649697f 100644
--- a/sw/source/filter/ww8/docxattributeoutput.cxx
+++ b/sw/source/filter/ww8/docxattributeoutput.cxx
@@ -2723,12 +2723,14 @@ void DocxAttributeOutput::WriteContentControlStart()
m_pSerializer->singleElementNS(XML_w, XML_showingPlcHdr);
}
-if (!m_pContentControl->GetDataBindingPrefixMappings().isEmpty() ||
!m_pContentControl->GetDataBindingXpath().isEmpty() ||
!m_pContentControl->GetDataBindingStoreItemID().isEmpty())
+const OUString sID = m_pContentControl->GetDataBindingStoreItemID();
+if (!sID.isEmpty() ||
!m_pContentControl->GetDataBindingPrefixMappings().isEmpty()
+|| !m_pContentControl->GetDataBindingXpath().isEmpty())
{
m_pSerializer->singleElementNS( XML_w, XML_dataBinding,
FSNS(XML_w, XML_prefixMappings),
m_pContentControl->GetDataBindingPrefixMappings(),
FSNS(XML_w, XML_xpath), m_pContentControl->GetDataBindingXpath(),
-FSNS(XML_w, XML_storeItemID),
m_pContentControl->GetDataBindingStoreItemID());
+FSNS(XML_w, XML_storeItemID), sax_fastparser::UseIf(sID,
!sID.isEmpty()));
}
if (m_pContentControl->GetTabIndex())
core.git: Branch 'libreoffice-26-2' - sw/qa sw/source
sw/qa/extras/layout/data/tdf170811.fodt | 30 ++
sw/qa/extras/layout/layout6.cxx |8
sw/source/core/text/porexp.cxx |9 +
3 files changed, 47 insertions(+)
New commits:
commit 66879873c96243c7c4b2532ce39ad576a232c110
Author: Mike Kaganski
AuthorDate: Mon Feb 16 14:06:37 2026 +0500
Commit: Xisco Fauli
CommitDate: Thu Feb 19 10:06:30 2026 +0100
tdf#170811: SwBlankPortion as part of field expansion must use SwTextSlot
Since commit e3c068cb190e6cf04a49f9984f984d5ef72f9d9a (tdf#167946:
reimplement the fix for tdf#120677, 2026-02-07), SwBlankPortion stopped
using SwExpandPortion::Format, and therefore, SwTextSlot (that replaced
the text in SwTextFormatInfo). That was the intended change, because
replacing the text destroyed the context for formatting, and that was
the root of the problem.
However, SwBlankPortion may appear as part of a field expansion. Then
its length is zero (see SwTextFormatter::NewPortion, which sets length
to zero in that case). To avoid wrong formatting (where zero length
automatically means in SwTextGuess::Guess, that line break goes here),
which would change portion lingth to 1 (see SwTextPortion::BreakCut),
which would then try to access missing string indexes ... - to avoid
all that, restore use of SwTextSlot optionally for the "length is 0"
case.
A follow-up needs to improve the use of SwTextSlot here, to use the
actual, full expansion of the field text - which would allow to format
the NBSP in context of the surrounding text, and to fix tdf#56085.
Change-Id: I86497ed3099eec585f4454e1db19e070ce1b17d6
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/199458
Tested-by: Jenkins
Reviewed-by: Mike Kaganski
Signed-off-by: Xisco Fauli
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/199498
diff --git a/sw/qa/extras/layout/data/tdf170811.fodt
b/sw/qa/extras/layout/data/tdf170811.fodt
new file mode 100644
index ..bf34144cf5f2
--- /dev/null
+++ b/sw/qa/extras/layout/data/tdf170811.fodt
@@ -0,0 +1,30 @@
+
+
+
+ word nobreakspace word space
word nobreakspace word
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/sw/qa/extras/layout/layout6.cxx b/sw/qa/extras/layout/layout6.cxx
index bebc6deb7563..f4c8cb590ffc 100644
--- a/sw/qa/extras/layout/layout6.cxx
+++ b/sw/qa/extras/layout/layout6.cxx
@@ -2196,6 +2196,14 @@ CPPUNIT_TEST_FIXTURE(SwLayoutWriter6, testTdf169607)
assertXPath(pXmlDoc, "//page", 3);
}
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter6, testTdf170811)
+{
+// This used to fail assertion
+createSwDoc("tdf170811.fodt");
+// This must succeed
+parseLayoutDump();
+}
+
} // end of anonymous namespace
CPPUNIT_PLUGIN_IMPLEMENT();
diff --git a/sw/source/core/text/porexp.cxx b/sw/source/core/text/porexp.cxx
index a9bb3add21b0..eb6dd2719fe6 100644
--- a/sw/source/core/text/porexp.cxx
+++ b/sw/source/core/text/porexp.cxx
@@ -195,6 +195,15 @@ void SwBlankPortion::FormatEOL( SwTextFormatInfo &rInf )
*/
bool SwBlankPortion::Format( SwTextFormatInfo &rInf )
{
+std::optional oTextSlot;
+if (!GetLen())
+{
+// This is a hook char of a field portion. Use expansion.
+// FIXME: tdf#56085 the slot must use parent field portion text, to
correctly format
+// in the context of surrounding text
+oTextSlot.emplace(&rInf, this, true, false);
+}
+
const bool bFull = rInf.IsUnderflow() || SwTextPortion::Format(rInf);
if( bFull && MayUnderflow( rInf, rInf.GetIdx(), rInf.IsUnderflow() ) )
{
core.git: Branch 'libreoffice-26-2' - sw/qa sw/source
sw/qa/core/edit/data/compare-new.odt |binary
sw/qa/core/edit/data/compare-old.odt |binary
sw/qa/core/edit/edit.cxx | 80 +++
sw/source/core/edit/editsh.cxx | 54 +++
4 files changed, 133 insertions(+), 1 deletion(-)
New commits:
commit 31d381aee9cc0a44965321bb9ed442f7487dc2e8
Author: Miklos Vajna
AuthorDate: Fri Feb 13 09:07:51 2026 +0100
Commit: Xisco Fauli
CommitDate: Mon Feb 16 18:27:53 2026 +0100
cool#13988 sw redline render mode: expose old/new author/date when comparing
Document compare works best when you open the new document, then perform
document compare taking the old document as a parameter. This way
insertions and deletions are correct (instead of being the other way
around.)
This is not intuitive, so help a LOK client provide hints to the user by
exposing the last modified date and the last modifying user when
comparing.
Unfortunately this info is just available when comparing, so we can't
send it again on document load, since the "this document" model doesn't
have both the old and new metadata, we only compare content.
Change-Id: I24c1968ea359536efbe26d7a673374ad42c2ff83
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/199464
Reviewed-by: Miklos Vajna
Tested-by: Jenkins
(cherry picked from commit c71d2bdb298639573fa5a93cdd5f211dabcf61c7)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/199483
Reviewed-by: Xisco Fauli
diff --git a/sw/qa/core/edit/data/compare-new.odt
b/sw/qa/core/edit/data/compare-new.odt
new file mode 100644
index ..50b6ae83f46f
Binary files /dev/null and b/sw/qa/core/edit/data/compare-new.odt differ
diff --git a/sw/qa/core/edit/data/compare-old.odt
b/sw/qa/core/edit/data/compare-old.odt
new file mode 100644
index ..e3200824433c
Binary files /dev/null and b/sw/qa/core/edit/data/compare-old.odt differ
diff --git a/sw/qa/core/edit/edit.cxx b/sw/qa/core/edit/edit.cxx
index 270490688315..21f187ebee59 100644
--- a/sw/qa/core/edit/edit.cxx
+++ b/sw/qa/core/edit/edit.cxx
@@ -9,7 +9,13 @@
#include
+#include
+
#include
+#include
+#include
+#include
+#include
#include
#include
@@ -32,6 +38,33 @@ public:
{
}
};
+
+/// LOK view callback for test purposes.
+struct ViewCallback
+{
+std::vector m_aStateChanges;
+
+static void callback(int nType, const char* pPayload, void* pData);
+void callbackImpl(int nType, const char* pPayload);
+};
+
+void ViewCallback::callback(int nType, const char* pPayload, void* pData)
+{
+static_cast(pData)->callbackImpl(nType, pPayload);
+}
+
+void ViewCallback::callbackImpl(int nType, const char* pPayload)
+{
+switch (nType)
+{
+case LOK_CALLBACK_STATE_CHANGED:
+{
+OUString aPayload = OUString::fromUtf8(pPayload);
+m_aStateChanges.push_back(aPayload);
+}
+break;
+}
+}
}
CPPUNIT_TEST_FIXTURE(Test, testRedlineHidden)
@@ -451,6 +484,53 @@ CPPUNIT_TEST_FIXTURE(Test, testRedlineReinstateSelf)
CPPUNIT_ASSERT_EQUAL(static_cast(1), rRedlines.size());
}
+CPPUNIT_TEST_FIXTURE(Test, testDocumentCompareCallback)
+{
+// Set up LOK:
+comphelper::LibreOfficeKit::setActive(true);
+
+// Given a new document:
+createSwDoc("compare-new.odt");
+SwDocShell* pDocShell = getSwDocShell();
+SwWrtShell* pWrtShell = pDocShell->GetWrtShell();
+ViewCallback aCallback;
+TestLokCallbackWrapper aCallbackWrapper(&ViewCallback::callback,
&aCallback);
+
pWrtShell->GetSfxViewShell()->setLibreOfficeKitViewCallback(&aCallbackWrapper);
+
aCallbackWrapper.setLOKViewId(SfxLokHelper::getView(*pWrtShell->GetSfxViewShell()));
+
+// When comparing with an old document:
+OUString aOther = createFileURL(u"compare-old.odt");
+uno::Sequence aArgs = {
+comphelper::makePropertyValue("URL", aOther),
+comphelper::makePropertyValue("NoAcceptDialog", true),
+};
+dispatchCommand(mxComponent, ".uno:CompareDocuments", aArgs);
+
+// Then make sure a JSON callback with the expected content is emitted:
+auto it = std::find_if(aCallback.m_aStateChanges.begin(),
aCallback.m_aStateChanges.end(),
+ [](const OUString& i) -> bool { return
i.startsWith("{"); });
+CPPUNIT_ASSERT(it != aCallback.m_aStateChanges.end());
+std::stringstream aStream((std::string(it->toUtf8(;
+boost::property_tree::ptree aTree;
+boost::property_tree::read_json(aStream, aTree);
+CPPUNIT_ASSERT_EQUAL(std::string("CompareDocumentsProperties"),
+ aTree.get("commandName"));
+CPPUNIT_ASSERT_EQUAL(std::string("Alice"),
+
aTree.get("state.metadata.otherDocument.modifiedBy"));
+CPPUNIT_ASSERT_EQUAL(std::string("2010-02-12T13:51:17.860434211"),
+
aTree.get("state.metadata.otherDocument.modificationDate"));
+
core.git: Branch 'libreoffice-26-2' - sw/qa sw/source
sw/qa/core/text/data/redline-bullet.docx |binary
sw/qa/core/text/porfld.cxx | 35 +++
sw/source/core/layout/paintfrm.cxx |9 ++-
sw/source/core/text/porfld.cxx |4 ---
sw/source/core/text/porfld.hxx |2 +
sw/source/core/text/porfly.cxx |4 ---
sw/source/core/text/txttab.cxx | 11 +++--
7 files changed, 51 insertions(+), 14 deletions(-)
New commits:
commit 7f4377025e6c86cabca7f908d3f2e41885e4b553
Author: Miklos Vajna
AuthorDate: Fri Feb 6 14:27:09 2026 +0100
Commit: Adolfo Jayme Barrientos
CommitDate: Fri Feb 13 17:27:39 2026 +0100
cool#13988 sw redline render mode: handle tab portions
Open the bugdoc, switch to non-standard redline render mode (e.g.
dispatch .uno:RedlineRenderMode), the tab portion in the second line
should not be crossed out, but it is.
What happens is similar to number portions, but here the tab portion
doesn't have its own font, it conditionally inherits the font of the
previous number portion.
Fix this by improving SwTabPortion::Paint() to use the new
GetRedlineRenderModeFont() of the number portion when we're in
non-standard redline render mode.
Also simplify some existing checks for getting the redline render mode,
now that I found that SwTextSizeInfo::GetOpt() gives you a
SwViewOption&.
Change-Id: I1bd5aca7705de6641630439f6ff83a73a7fba8b2
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/198970
Reviewed-by: Miklos Vajna
Tested-by: Jenkins
(cherry picked from commit edef9fffa00bddb8f03ef00e6b693c967a15b28f)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/199071
Reviewed-by: Adolfo Jayme Barrientos
diff --git a/sw/qa/core/text/data/redline-bullet.docx
b/sw/qa/core/text/data/redline-bullet.docx
new file mode 100644
index ..0830adca71a5
Binary files /dev/null and b/sw/qa/core/text/data/redline-bullet.docx differ
diff --git a/sw/qa/core/text/porfld.cxx b/sw/qa/core/text/porfld.cxx
index 6ab3d3f3c550..67b36a596292 100644
--- a/sw/qa/core/text/porfld.cxx
+++ b/sw/qa/core/text/porfld.cxx
@@ -66,6 +66,41 @@ CPPUNIT_TEST_FIXTURE(Test,
testNumberPortionRedlineRenderMode)
// i.e. there was an unexpected underline.
CPPUNIT_ASSERT_EQUAL(u"0"_ustr, aUnderline);
}
+
+CPPUNIT_TEST_FIXTURE(Test, testTabPortionRedlineRenderMode)
+{
+// Given a document with redlines, the tab number portion is deleted:
+createSwDoc("redline-bullet.docx");
+SwDocShell* pDocShell = getSwDocShell();
+
+// When redline render mode is standard:
+std::shared_ptr xMetaFile = pDocShell->GetPreviewMetaFile();
+
+// Then make sure we paint a strikeout:
+MetafileXmlDump aDumper;
+xmlDocUniquePtr pXmlDoc = dumpAndParse(aDumper, *xMetaFile);
+assertXPath(pXmlDoc, "//stretchtext", 1);
+OUString aStrikeout
+= getXPath(pXmlDoc, "(//stretchtext)[1]/preceding-sibling::font[1]",
"strikeout");
+CPPUNIT_ASSERT_EQUAL(u"1"_ustr, aStrikeout);
+
+// And given "omit inserts" redline render mode:
+SwWrtShell* pWrtShell = pDocShell->GetWrtShell();
+SwViewOption aOpt(*pWrtShell->GetViewOptions());
+aOpt.SetRedlineRenderMode(SwRedlineRenderMode::OmitInserts);
+pWrtShell->ApplyViewOptions(aOpt);
+
+// When rendering:
+xMetaFile = pDocShell->GetPreviewMetaFile();
+
+// Then make sure we don't paint a strikeout:
+pXmlDoc = dumpAndParse(aDumper, *xMetaFile);
+// Without the accompanying fix in place, this test would have failed with:
+// - Expected: 0
+// - Actual : 1
+// i.e. the stretched text was painted, which used a strikeout font.
+assertXPath(pXmlDoc, "//stretchtext", 0);
+}
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/paintfrm.cxx
b/sw/source/core/layout/paintfrm.cxx
index 3af59db24d89..aa3452d7d006 100644
--- a/sw/source/core/layout/paintfrm.cxx
+++ b/sw/source/core/layout/paintfrm.cxx
@@ -4274,6 +4274,9 @@ void SwFlyFrame::PaintSwFrame(vcl::RenderContext&
rRenderContext, SwRect const&
}
}
+const SwViewOption* pViewOptions = pShell ? pShell->GetViewOptions() :
nullptr;
+SwRedlineRenderMode eRedlineRenderMode
+= pViewOptions ? pViewOptions->GetRedlineRenderMode() :
SwRedlineRenderMode::Standard;
{
bool bContour = GetFormat()->GetSurround().IsContour();
tools::PolyPolygon aPoly;
@@ -4320,9 +4323,6 @@ void SwFlyFrame::PaintSwFrame(vcl::RenderContext&
rRenderContext, SwRect const&
}
}
// paint of margin needed.
-const SwViewOption* pViewOptions = pShell ? pShell->GetViewOptions() :
nullptr;
-SwRedlineRenderMode eRedlineRenderMode = pViewOptions ?
pViewOptions->GetRedlineRenderMode()
-: SwRedlineRenderMode::Standard;
const bool bPaintMarginOnly( !bPaintCompleteBack &&
core.git: Branch 'libreoffice-26-2' - sw/qa sw/source
sw/qa/extras/ooxmlexport/ooxmlfieldexport.cxx |6 ++
sw/source/filter/ww8/wrtw8nds.cxx | 16 +---
2 files changed, 19 insertions(+), 3 deletions(-)
New commits:
commit ab7e8ef92630cce4438ceb418c6e6796e7c672a0
Author: Justin Luth
AuthorDate: Tue Feb 10 12:13:59 2026 -0500
Commit: Xisco Fauli
CommitDate: Thu Feb 12 16:53:30 2026 +0100
tdf#170686 docx export: end SDT before writing final flies
A plainText-y content control is reported as corrupt
by Microsoft Word if it contains a floating frame.
When a runSdt was at the end of the paragraph,
the export was dumping any flies at the end
into the Sdt run.
Instead, the Sdt should first be closed,
and then the remaining flies
should be placed into their own separate run.
Possibly, this should happen for fields as well?
Or even for any paragraph, like we did in bug tdf#170516?
It probably also belongs in the other 'if nNextAttr == nEnd'
but I couldn't find any example unit tests to confirm.
make CppunitTest_sw_ooxmlfieldexport \
CPPUNIT_TEST_NAME=testContentControlShape
Change-Id: Ic1c160ed9c9985d4fcc308a271ee168fe00ea82b
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/199092
Tested-by: Jenkins
Reviewed-by: Justin Luth
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/199284
Reviewed-by: Xisco Fauli
diff --git a/sw/qa/extras/ooxmlexport/ooxmlfieldexport.cxx
b/sw/qa/extras/ooxmlexport/ooxmlfieldexport.cxx
index 4b65fe4b8fc8..f8d806e8a041 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlfieldexport.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlfieldexport.cxx
@@ -1096,6 +1096,12 @@ CPPUNIT_TEST_FIXTURE(Test, testContentControlShape)
// Then make sure that completes without an assertion failure, which would
mean not-well-formed
// output was produced, since the was conditional but the
was unconditional:
save(TestFilter::DOCX);
+
+// tdf#170686: floating shapes are not allowed inside plainText date
controls
+xmlDocUniquePtr pXmlDoc = parseExport(u"word/document.xml"_ustr);
+assertXPath(pXmlDoc, "//w:sdt", 1); // only one sdt
+assertXPath(pXmlDoc, "//mc:AlternateContent", 1); // only one drawing
+assertXPath(pXmlDoc, "//w:body/w:p/w:r/mc:AlternateContent", 1); // and it
is not inside the sdt
}
CPPUNIT_TEST_FIXTURE(Test, testTdf104823)
diff --git a/sw/source/filter/ww8/wrtw8nds.cxx
b/sw/source/filter/ww8/wrtw8nds.cxx
index 2f7c6882d746..ccbaf678958c 100644
--- a/sw/source/filter/ww8/wrtw8nds.cxx
+++ b/sw/source/filter/ww8/wrtw8nds.cxx
@@ -2510,9 +2510,9 @@ void MSWordExportBase::OutputTextNode( SwTextNode& rNode )
OUString aSavedSnippet ;
// Don't redline content-controls--Word doesn't do them.
-SwTextAttr* pAttr = rNode.GetTextAttrAt(nCurrentPos,
RES_TXTATR_CONTENTCONTROL,
-
sw::GetTextAttrMode::Default);
-if (pAttr && pAttr->GetStart() == nCurrentPos)
+const SwTextAttr* pSdt = rNode.GetTextAttrAt(nCurrentPos,
RES_TXTATR_CONTENTCONTROL,
+
sw::GetTextAttrMode::Default);
+if (pSdt && pSdt->GetStart() == nCurrentPos)
{
pRedlineData = nullptr;
}
@@ -2816,6 +2816,16 @@ void MSWordExportBase::OutputTextNode( SwTextNode& rNode
)
else
{
// insert final graphic anchors if any before CR
+if (pSdt && *pSdt->GetEnd() == nEnd &&
aAttrIter.HasFlysAt(nEnd))
+{
+// Close the content control run before exporting
final flies,
+// otherwise the flies will be moved into the Sdt
run,
+// which (for a non-richText Sdt) will be
considered corrupt in MS Word.
+AttrOutput().EndRun(&rNode, nCurrentPos, nLen,
/*bLastRun=*/false);
+nLen = 0;
+nCurrentPos = nEnd;
+AttrOutput().StartRun(pRedlineData, nCurrentPos,
bSingleEmptyRun);
+}
nStateOfFlyFrame = aAttrIter.OutFlys( nEnd );
// insert final bookmarks if any before CR and after
flys
AppendBookmarks( rNode, nEnd, 1 );
core.git: Branch 'libreoffice-26-2' - sw/qa sw/source
sw/qa/extras/ooxmlexport/data/tdf168988_grabbagDatePicker.docx |binary
sw/qa/extras/ooxmlexport/ooxmlexport25.cxx | 14 +++
sw/source/filter/ww8/docxattributeoutput.cxx | 18
++
3 files changed, 18 insertions(+), 14 deletions(-)
New commits:
commit 05a2bdfc892f903cfa316f23562bccd179125f4d
Author: Justin Luth
AuthorDate: Wed Feb 11 16:25:49 2026 -0500
Commit: Xisco Fauli
CommitDate: Thu Feb 12 16:52:56 2026 +0100
tdf#168988 docx export: ensure valid ISO8601 FormDate fullDate
Without this fix, datePickers with invalid content
were dumping the content into the fullDate element on export,
which caused MS Word to report the file as corrupt.
datePickers inside tables are imported
as fields by LO, not as a date content control.
(I believe this can be changed by an advanced config setting.)
So, if invalid content was loaded into ODF_FORMDATE_CURRENTDATE
(which is fine - it SHOULD be displayed)
it was then being round-tripped as both content
AND as the fullDate element.
Just round-trip non-date strings as content.
make CppunitTest_sw_ooxmlexport25 \
CPPUNIT_TEST_NAME=testTdf168988_grabbagDatePicker
Change-Id: I1275de94f1da5f0bb9a319c52ea8a4c67e9564ae
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/199218
Tested-by: Jenkins
Reviewed-by: Justin Luth
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/199278
Reviewed-by: Xisco Fauli
diff --git a/sw/qa/extras/ooxmlexport/data/tdf168988_grabbagDatePicker.docx
b/sw/qa/extras/ooxmlexport/data/tdf168988_grabbagDatePicker.docx
new file mode 100644
index ..ac8c29eb943a
Binary files /dev/null and
b/sw/qa/extras/ooxmlexport/data/tdf168988_grabbagDatePicker.docx differ
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport25.cxx
b/sw/qa/extras/ooxmlexport/ooxmlexport25.cxx
index 4649e916b556..5b855c4db3a3 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport25.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport25.cxx
@@ -181,6 +181,20 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf165359_SdtWithDrawing)
assertXPath(pXmlDoc, "//w:sdtPr/w:text", 0);
}
+CPPUNIT_TEST_FIXTURE(Test, testTdf168988_grabbagDatePicker)
+{
+createSwDoc("tdf168988_grabbagDatePicker.docx");
+
+save(TestFilter::DOCX);
+xmlDocUniquePtr pXmlDoc = parseExport(u"word/document.xml"_ustr);
+assertXPath(pXmlDoc, "//w:sdt", 1); // only one sdt
+assertXPath(pXmlDoc, "//w:sdt/w:sdtPr/w:date", 1); // it is a date content
control
+// there is no valid date, so fullDate must not be provided (or MS Word
says 'corrupt file')
+assertXPathNoAttribute(pXmlDoc, "//w:sdt/w:sdtPr/w:date", "fullDate");
+assertXPathContent(pXmlDoc, "//w:sdt/w:sdtContent/w:r/w:t",
+ u" 2012./2013.");
+}
+
CPPUNIT_TEST_FIXTURE(Test, testTdf170389_manyTabstops)
{
createSwDoc("tdf170389_manyTabstops.odt");
diff --git a/sw/source/filter/ww8/docxattributeoutput.cxx
b/sw/source/filter/ww8/docxattributeoutput.cxx
index 79dc73574236..3992dfdc1a75 100644
--- a/sw/source/filter/ww8/docxattributeoutput.cxx
+++ b/sw/source/filter/ww8/docxattributeoutput.cxx
@@ -2950,20 +2950,10 @@ void DocxAttributeOutput::StartField_Impl( const
SwTextNode* pNode, sal_Int32 nP
FieldMarkParamsHelper params(rFieldmark);
OUString sFullDate;
-OUString sCurrentDate;
-params.extractParam( ODF_FORMDATE_CURRENTDATE, sCurrentDate );
-if(!sCurrentDate.isEmpty())
-{
-sFullDate = sCurrentDate + "T00:00:00Z";
-}
-else
-{
-std::pair aResult = rFieldmark.GetCurrentDate();
-if(aResult.first)
-{
-sFullDate =
rFieldmark.GetDateInStandardDateFormat(aResult.second) + "T00:00:00Z";
-}
-}
+std::pair aResult = rFieldmark.GetCurrentDate();
+// fullDate must be a valid ISO8601 date string or MS Word reports the
file as corrupt
+if (aResult.first) // bFoundValidDate is true
+sFullDate = rFieldmark.GetDateInStandardDateFormat(aResult.second)
+ "T00:00:00Z";
OUString sDateFormat;
params.extractParam( ODF_FORMDATE_DATEFORMAT, sDateFormat );
core.git: Branch 'libreoffice-26-2' - sw/qa sw/source
sw/qa/extras/tiledrendering/tiledrendering.cxx | 56 +
sw/source/core/doc/notxtfrm.cxx|2
2 files changed, 57 insertions(+), 1 deletion(-)
New commits:
commit 1797ea2d05b5ed462305737af4a55cd1d94fe4ad
Author: Caolán McNamara
AuthorDate: Tue Jan 13 21:22:12 2026 +
Commit: Adolfo Jayme Barrientos
CommitDate: Thu Feb 12 14:11:00 2026 +0100
Resolves: tdf#168710 don't use dark/light bg color when printing chart
Change-Id: I0291a86744a5aa248a4174aa9e40aa646ddd0350
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/198908
Tested-by: Jenkins
Reviewed-by: Adolfo Jayme Barrientos
diff --git a/sw/qa/extras/tiledrendering/tiledrendering.cxx
b/sw/qa/extras/tiledrendering/tiledrendering.cxx
index 66f433c21c44..2240014de0db 100644
--- a/sw/qa/extras/tiledrendering/tiledrendering.cxx
+++ b/sw/qa/extras/tiledrendering/tiledrendering.cxx
@@ -31,6 +31,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -3884,6 +3885,61 @@ CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest,
testSwitchingChartToDarkMode)
CPPUNIT_ASSERT(nBlackPixels > nWhitePixels);
}
+// Toggle chart into dark mode and print as pdf. The automatic text
+// color should render into pdf as blank and not as the white of
+// the on-screen representation
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testPrintDarkModeChart)
+{
+addDarkLightThemes(COL_BLACK, COL_WHITE);
+SwXTextDocument* pXTextDocument = createDoc("large-chart-labels.odt");
+CPPUNIT_ASSERT(pXTextDocument);
+
+SwView* pView = getSwDocShell()->GetView();
+uno::Reference xFrame =
pView->GetViewFrame().GetFrame().GetFrameInterface();
+uno::Sequence aPropertyValues =
comphelper::InitPropertySequence(
+{
+{ "NewTheme", uno::Any(u"Dark"_ustr) },
+}
+);
+comphelper::dispatchCommand(u".uno:ChangeTheme"_ustr, xFrame,
aPropertyValues);
+CPPUNIT_ASSERT_EQUAL("S;Dark"_ostr, pXTextDocument->getViewRenderState());
+
+uno::Sequence args{
+comphelper::makePropertyValue(u"SynchronMode"_ustr, true),
+comphelper::makePropertyValue(u"URL"_ustr, maTempFile.GetURL())
+};
+dispatchCommand(mxComponent, u".uno:ExportDirectToPDF"_ustr, args);
+
+std::shared_ptr pPDFium = vcl::pdf::PDFiumLibrary::get();
+
+if (pPDFium)
+{
+SvFileStream aPDFFile(maTempFile.GetURL(), StreamMode::READ);
+SvMemoryStream aMemory;
+aMemory.WriteStream(aPDFFile);
+
+std::unique_ptr pPdfDocument
+= pPDFium->openDocument(aMemory.GetData(), aMemory.GetSize(),
OString());
+CPPUNIT_ASSERT(pPdfDocument);
+CPPUNIT_ASSERT_EQUAL(1, pPdfDocument->getPageCount());
+
+auto pPage = pPdfDocument->openPage(0);
+CPPUNIT_ASSERT(pPage);
+
+int nPageObjectCount = pPage->getObjectCount();
+
+auto pTextPage = pPage->getTextPage();
+
+for (int i = 0; i < nPageObjectCount; ++i)
+{
+std::unique_ptr pPageObject =
pPage->getObject(i);
+// The text should all be black.
+if (pPageObject->getType() == vcl::pdf::PDFPageObjectType::Text)
+CPPUNIT_ASSERT_EQUAL(COL_BLACK, pPageObject->getFillColor());
+}
+}
+}
+
CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testTdf159626_yellowPatternFill)
{
SwXTextDocument* pXTextDocument =
createDoc("tdf159626_yellowPatternFill.docx");
diff --git a/sw/source/core/doc/notxtfrm.cxx b/sw/source/core/doc/notxtfrm.cxx
index b0a2f572a4eb..d5d6c4346053 100644
--- a/sw/source/core/doc/notxtfrm.cxx
+++ b/sw/source/core/doc/notxtfrm.cxx
@@ -1399,7 +1399,7 @@ void SwNoTextFrame::ImplPaintPictureBitmap(
vcl::RenderContext* pOut,
SdrModel& rModel = pPage->getSdrModelFromSdrPage();
SdrOutliner& rOutl = rModel.GetDrawOutliner();
aOldBackColor = rOutl.GetBackgroundColor();
-rOutl.SetBackgroundColor(pPage->GetPageBackgroundColor());
+
rOutl.SetBackgroundColor(pPage->GetPageBackgroundColor(nullptr, !bPrn));
}
bDone = paintUsingPrimitivesHelper(
core.git: Branch 'libreoffice-26-2' - sw/qa sw/source
sw/qa/extras/ooxmlexport/ooxmlexport20.cxx |2 +-
sw/source/filter/ww8/wrtw8nds.cxx | 12
2 files changed, 9 insertions(+), 5 deletions(-)
New commits:
commit ea67230a570b064f23428acbe18c321c6b0bac41
Author: Justin Luth
AuthorDate: Tue Feb 3 15:46:17 2026 -0500
Commit: Xisco Fauli
CommitDate: Tue Feb 10 14:45:53 2026 +0100
tdf#170588 docx export: stop duplicating bookmark in empty w:r
The comment says
// Append bookmarks in this range after flys,
// exclusive of final position of this range
but it did nothing to exclude a final position of zero.
When nNextAttr == nEnd, then AppendBookmark is run
It is guarantined to run once at the end,
and thus it is safe to eliminate it here.
Probably worth noting that normally by definition
nCurrentPos is exclusive of final position.
The only time nCurrentPos == nEnd is when nEnd == 0
(since it loops while nCurrentPos < nEnd),
so it seemed the clearest to say '0 != nEnd'
instead of 'nCurrentPos == nEnd'.
Change-Id: I5da3bc2a17c98faa7a73604e8a640ed456f25dd5
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/198628
Reviewed-by: Justin Luth
Tested-by: Jenkins
(cherry picked from commit fb273cf50f9fca0d0d561a6011299502f55f464e)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/198998
Reviewed-by: Xisco Fauli
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport20.cxx
b/sw/qa/extras/ooxmlexport/ooxmlexport20.cxx
index c39ae316a796..ff797a81db86 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport20.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport20.cxx
@@ -315,7 +315,7 @@ CPPUNIT_TEST_FIXTURE(Test, testFdo77129)
// tdf#170588: stop duplicating bookmarkStarts
// Counts of bookmark Starts and Ends really ought to be identical...
assertXPath(pXmlDoc, "//w:bookmarkEnd", 4);
-assertXPath(pXmlDoc, "//w:bookmarkStart", 5);
+assertXPath(pXmlDoc, "//w:bookmarkStart", 4);
}
// Test the same testdoc used for testFdo77129.
diff --git a/sw/source/filter/ww8/wrtw8nds.cxx
b/sw/source/filter/ww8/wrtw8nds.cxx
index f795be4b6b0c..2f7c6882d746 100644
--- a/sw/source/filter/ww8/wrtw8nds.cxx
+++ b/sw/source/filter/ww8/wrtw8nds.cxx
@@ -2563,10 +2563,14 @@ void MSWordExportBase::OutputTextNode( SwTextNode&
rNode )
AttrOutput().SetAnchorIsLinkedToNode( bPostponeWritingText &&
(FLY_POSTPONED != nStateOfFlyFrame) );
// Append bookmarks in this range after flys, exclusive of final
// position of this range
-AppendBookmarks( rNode, nCurrentPos, nNextAttr - nCurrentPos,
pRedlineData );
-// Sadly only possible for main or glossary document parts:
ECMA-376 Part 1 sect. 11.3.2
-if ( m_nTextTyp == TXT_MAINTEXT )
-AppendAnnotationMarks(aAttrIter, nCurrentPos, nNextAttr -
nCurrentPos);
+if (0 != nEnd) // start == final position is written when
nNextAttr == nEnd
+{
+AppendBookmarks(rNode, nCurrentPos, nNextAttr - nCurrentPos,
pRedlineData);
+// Sadly, comments are only possible
+// for main or glossary document parts: ECMA-376 Part 1 sect.
11.3.2
+if (m_nTextTyp == TXT_MAINTEXT)
+AppendAnnotationMarks(aAttrIter, nCurrentPos, nNextAttr -
nCurrentPos);
+}
// At the moment smarttags are only written for paragraphs, at the
// beginning of the paragraph.
core.git: Branch 'libreoffice-26-2' - sw/qa sw/source
sw/qa/extras/ooxmlexport/ooxmlexport20.cxx |5 + sw/source/filter/ww8/docxexport.cxx|4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) New commits: commit 61d5bb3c66192d95e27e4d843d09996814bce2c0 Author: Justin Luth AuthorDate: Tue Feb 3 13:05:08 2026 -0500 Commit: Xisco Fauli CommitDate: Tue Feb 10 14:45:30 2026 +0100 tdf#170588 docx export: stop duplicating bookmark starts Bookmarks can span multiple nodes. Since we weren't checking the node we were often adding the bookmark start multiple times, once for the start node and again for the end node. This is guaranteed to happen for any bookmark starting at position zero, and possible for the other positions. An extra end bookmark doesn't matter, because it isn't written if it doesn't find the name in the starts. A similar fix was needed for comments in bug 170457. make CppunitTest_sw_ooxmlexport20 CPPUNIT_TEST_NAME=testFdo77129 Change-Id: I595595a2b48aabf861179ade1b3097fc31e2a12e Reviewed-on: https://gerrit.libreoffice.org/c/core/+/198620 Reviewed-by: Justin Luth Tested-by: Jenkins (cherry picked from commit fe67e1899b3d3322b4e6721349af51a0c0416383) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/198997 Reviewed-by: Xisco Fauli diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport20.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport20.cxx index f77f9aa65a4e..c39ae316a796 100644 --- a/sw/qa/extras/ooxmlexport/ooxmlexport20.cxx +++ b/sw/qa/extras/ooxmlexport/ooxmlexport20.cxx @@ -311,6 +311,11 @@ CPPUNIT_TEST_FIXTURE(Test, testFdo77129) // Data was lost from this paragraph. assertXPathContent(pXmlDoc, "/w:document/w:body/w:p[4]/w:r[1]/w:t", u"Abstract"); + +// tdf#170588: stop duplicating bookmarkStarts +// Counts of bookmark Starts and Ends really ought to be identical... +assertXPath(pXmlDoc, "//w:bookmarkEnd", 4); +assertXPath(pXmlDoc, "//w:bookmarkStart", 5); } // Test the same testdoc used for testFdo77129. diff --git a/sw/source/filter/ww8/docxexport.cxx b/sw/source/filter/ww8/docxexport.cxx index 63a084c6a44a..d06f58e228f3 100644 --- a/sw/source/filter/ww8/docxexport.cxx +++ b/sw/source/filter/ww8/docxexport.cxx @@ -177,10 +177,10 @@ void DocxExport::AppendBookmarks( const SwTextNode& rNode, sal_Int32 nCurrentPos const sal_Int32 nStart = pMark->GetMarkStart().GetContentIndex(); const sal_Int32 nEnd = pMark->GetMarkEnd().GetContentIndex(); -if ( nStart == nCurrentPos ) +if (nStart == nCurrentPos && rNode == pMark->GetMarkStart().GetNode()) aStarts.push_back( pMark->GetName().toString() ); -if ( nEnd == nCurrentPos ) +if (nEnd == nCurrentPos && rNode == pMark->GetMarkEnd().GetNode()) aEnds.push_back( pMark->GetName().toString() ); } }
core.git: Branch 'libreoffice-26-2' - sw/qa sw/source
sw/qa/extras/layout/data/tdf169607-big-letters.fodt | 25
sw/qa/extras/layout/layout6.cxx | 13 ++
sw/source/core/text/frmform.cxx | 13 ++
3 files changed, 51 insertions(+)
New commits:
commit a8bd2b794b308acfd8822b9f88326f61963fc1bf
Author: Mike Kaganski
AuthorDate: Sun Feb 8 17:08:52 2026 +0500
Commit: Xisco Fauli
CommitDate: Tue Feb 10 14:42:31 2026 +0100
tdf#169607: Do split when learned that it wasn't an empty master split
Commit ae9e8f3f6d10b0be2fe5b9b238a531b17e0d67da prevented splitting
master frame at the beginning of page, because there shouldn't be an
empty frame there. But the actual number of characters that goes to
the master frame will only be known after FindBreak, at which point
it may turn out that the suppression was wrong.
This change checks this situation, and splits anyway. It is similar
to what happens below, where SplitFrame is also called.
Change-Id: If32fe2817a91724d704a096111a963f63deee593
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/198902
Tested-by: Jenkins
Reviewed-by: Mike Kaganski
Signed-off-by: Xisco Fauli
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/198955
diff --git a/sw/qa/extras/layout/data/tdf169607-big-letters.fodt
b/sw/qa/extras/layout/data/tdf169607-big-letters.fodt
new file mode 100644
index ..1875781f248d
--- /dev/null
+++ b/sw/qa/extras/layout/data/tdf169607-big-letters.fodt
@@ -0,0 +1,25 @@
+
+
+http://openoffice.org/2004/office";
xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0"
xmlns:config="urn:oasis:names:tc:opendocument:xmlns:config: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.4" office:mimetype="application/vnd.oasis.opendocument.text">
+
+
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ FOOBAR
+
+
+
\ No newline at end of file
diff --git a/sw/qa/extras/layout/layout6.cxx b/sw/qa/extras/layout/layout6.cxx
index 1217dc28ff87..bebc6deb7563 100644
--- a/sw/qa/extras/layout/layout6.cxx
+++ b/sw/qa/extras/layout/layout6.cxx
@@ -2183,6 +2183,19 @@ CPPUNIT_TEST_FIXTURE(SwLayoutWriter6, testTdf167946)
assertXPath(pXmlDoc, "//SwLineLayout[2]/child::*[81]", "portion", u",");
}
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter6, testTdf169607)
+{
+// Given a document with a sequence of huge letters, each of which don't
fit to page vertically:
+createSwDoc("tdf169607-big-letters.fodt");
+
+xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+CPPUNIT_ASSERT(pXmlDoc);
+
+// There must be three pages, because the sequence must split two
characters per page. Before
+// the fix, there was only one page:
+assertXPath(pXmlDoc, "//page", 3);
+}
+
} // end of anonymous namespace
CPPUNIT_PLUGIN_IMPLEMENT();
diff --git a/sw/source/core/text/frmform.cxx b/sw/source/core/text/frmform.cxx
index dc4e06c89292..7afd83430384 100644
--- a/sw/source/core/text/frmform.cxx
+++ b/sw/source/core/text/frmform.cxx
@@ -1233,6 +1233,7 @@ void SwTextFrame::FormatAdjust( SwTextFormatter &rLine,
GetDrawObjs() && GetDrawObjs()->size() == 1 &&
(*GetDrawObjs())[0]->GetFrameFormat()->GetAnchor().GetAnchorId()
== RndStdIds::FLY_AS_CHAR;
+bool bCreateNewSuppressedByEmptySplit = false;
if (bLoneAsCharAnchoredObj)
{
// Still try split text frame if we have columns.
@@ -1263,7 +1264,10 @@ void SwTextFrame::FormatAdjust( SwTextFormatter &rLine,
// frame if we have columns.
if (pBodyFrame && !FindColFrame() &&
isFirstVisibleFrameInPageBody(this)
&& !hasFly(this) && !hasAtPageFly(pBodyFrame))
+{
createNew = false;
+bCreateNewSuppressedByEmptySplit = true;
+}
}
}
@@ -1317,6 +1321,15 @@ void SwTextFrame::FormatAdjust( SwTextFormatter &rLine,
if( !bDelta )
GetFollow()->ManipOfst( nEnd );
}
+else
+{
+if (bCreateNewSuppressedByEmptySplit && nEnd != nOld)
+{
+// Not empty split: nEnd moved
+SplitFrame(nEnd);
+dontJoin = true;
+}
+}
}
else
{ // If we pass over lines, we must not call Join in Follows, instead we
even
core.git: Branch 'libreoffice-26-2' - sw/qa sw/source
sw/qa/extras/layout/data/nbsp-moved-to-new-line.fodt | 28 +
sw/qa/extras/layout/layout6.cxx | 39 +++
sw/source/core/text/guess.cxx|3 -
sw/source/core/text/porexp.cxx |4 -
sw/source/core/text/porexp.hxx |2
5 files changed, 72 insertions(+), 4 deletions(-)
New commits:
commit f28d05f3beb8f57f182150afca06a2395eb55a57
Author: Mike Kaganski
AuthorDate: Sat Feb 7 14:57:33 2026 +0500
Commit: Xisco Fauli
CommitDate: Tue Feb 10 14:42:11 2026 +0100
tdf#167946: reimplement the fix for tdf#120677
Commit 4bb28ad217ea9d6511b6921dcd3d28328edcb4d6 (tdf#120677: restore
treatment of blanks in SwTextGuess::Guess, 2018-11-12) made NBSPs
behave like ordinary spaces for line breaking purposes. But that is
wrong; NBSPs are explicitly defined in UAX #14 to behave differently.
It turns out, that the problem in tdf#120677 was caused by specifics
of handling of SwBlankPortion, which was defined as descendant of
SwExpandPortion (since initial import). SwExpandPortion::Format uses
special logic to change the passed SwTextFormatInfo using SwTextSlot,
to allow formatting text that is different from what was initially
passed to SwTextFormatInfo. This logic isn't needed for blanks, and
happens to confuse the normal line-breaking algorithm to return zero
for blank portions with NBSP. SwExpandPortion::Paint method, also
previously used by SwBlankPortion, doesn't seem to include any logic
specifically useful for blanks as well, and respective methods of
SwTextPortion can be used instead.
This change reverts the functional change of the previous fix for
tdf#120677 (the unit test is kept), and makes SwBlankPortion inherit
from SwTextPortion, which fixes both tdf#120677 and tdf#167946.
Change-Id: I9109069093a7f58a997a93723f81137d1a6bd7f8
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/198882
Reviewed-by: Mike Kaganski
Tested-by: Jenkins
Signed-off-by: Xisco Fauli
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/198954
diff --git a/sw/qa/extras/layout/data/nbsp-moved-to-new-line.fodt
b/sw/qa/extras/layout/data/nbsp-moved-to-new-line.fodt
new file mode 100644
index ..63ad3180eca5
--- /dev/null
+++ b/sw/qa/extras/layout/data/nbsp-moved-to-new-line.fodt
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit.
,
+
+
+
\ No newline at end of file
diff --git a/sw/qa/extras/layout/layout6.cxx b/sw/qa/extras/layout/layout6.cxx
index 8a2a23af6634..1217dc28ff87 100644
--- a/sw/qa/extras/layout/layout6.cxx
+++ b/sw/qa/extras/layout/layout6.cxx
@@ -2144,6 +2144,45 @@ CPPUNIT_TEST_FIXTURE(SwLayoutWriter6, testTdf170630)
assertXPath(pXmlDoc, "//page[1]/body/txt[3]/anchored/fly", 2);
}
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter6, testTdf167946)
+{
+// Given a document with a sequence of non-breaking spaces after a text
and a space, which
+// sequence doesn't fit the rest of the line. Before the fix, only the
comma was moved to the
+// second line, and all NBSPs were elided as a hole portion at the end of
the first line:
+createSwDoc("nbsp-moved-to-new-line.fodt");
+
+xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+CPPUNIT_ASSERT(pXmlDoc);
+
+// One page:
+assertXPath(pXmlDoc, "//page", 1);
+
+// One paragraph:
+assertXPath(pXmlDoc, "//txt", 1);
+
+// Two lines in the paragraph:
+assertXPath(pXmlDoc, "//txt/SwParaPortion/SwLineLayout", 2);
+assertXPath(pXmlDoc, "//SwLineLayout", 2);
+
+// First line consists of a text portion with, and a hole portion for the
following space:
+assertXPath(pXmlDoc, "//SwLineLayout[1]/child::*[1]", "type",
u"PortionType::Text");
+assertXPath(pXmlDoc, "//SwLineLayout[1]/child::*[1]", "portion",
+u"Lorem ipsum dolor sit amet, consectetur adipiscing elit.");
+assertXPath(pXmlDoc, "//SwLineLayout[1]/child::*[2]", "type",
u"PortionType::Hole");
+assertXPath(pXmlDoc, "//SwLineLayout[1]/child::*[2]", "portion", u" ");
+
+// Second line has blank portions for the leading NBSPs, followed by a
text portion with comma:
+assertXPathChildren(pXmlDoc, "//SwLineLayout[2]", 81);
+for (int i = 1; i <= 80; ++i)
+{
+OString aXPath = "//SwLineLayout[2]/child::*[" + OString::number(i) +
"]";
+assertXPath(pXmlDoc, aXPath, "type", u"PortionType::Blank");
+assertXPath(pXmlDoc, aXPath, "portion", u"\xA0");
+}
+assertXPath(pXmlDoc, "//SwLineLayout[2]/child::*[81]", "type",
u"PortionType::Text");
+assertXPath(pXmlDoc, "//SwLineLayout[2]/child::*[81]", "portion", u",");
+}
+
} // end of anonymous namespace
CPPUNIT_PLUGIN_IMPLEMENT
core.git: Branch 'libreoffice-26-2' - sw/qa sw/source
sw/qa/core/layout/flycnt.cxx | 13 +-
sw/source/core/inc/sortedobjs.hxx|2 +
sw/source/core/layout/sortedobjs.cxx | 44 ++-
3 files changed, 57 insertions(+), 2 deletions(-)
New commits:
commit d2367dd71f4511c147339b059f23af5829e97195
Author: Mike Kaganski
AuthorDate: Fri Feb 6 19:23:28 2026 +0500
Commit: Xisco Fauli
CommitDate: Tue Feb 10 14:41:46 2026 +0100
sw floattable: sort split fly frames in SwSortedObjs from start to end
Previously, there was no rule in ObjAnchorOrder to define the order
of such split fly frames, that share everything that was previously
compared there. Therefore, new follows were added to front of the
vector, making it effectively sorted in the reverse direction. That
meant, that all loops over elements of GetSortedObjs() handled the
objects in the opposite direction (first follows, then precedes).
This change adds a sorting rule to handle related split fly frames.
To handle previous follow flys, that have already been removed from
the chain, but are still in a sorted list, the sorting rules move
these to the end, to guarantee correct partitioning.
Incidentally, this allowed to fix a pre-existing unit test to test
page 3, as intended.
Change-Id: I79f3a3f32630d72f098d34a2b1ade1262b1c66b8
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/198842
Reviewed-by: Mike Kaganski
Tested-by: Jenkins
Signed-off-by: Xisco Fauli
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/198953
diff --git a/sw/qa/core/layout/flycnt.cxx b/sw/qa/core/layout/flycnt.cxx
index e390f0a7e3c9..0eb297393502 100644
--- a/sw/qa/core/layout/flycnt.cxx
+++ b/sw/qa/core/layout/flycnt.cxx
@@ -229,7 +229,7 @@ CPPUNIT_TEST_FIXTURE(Test, testSplitFly3Pages)
// No vert offset on the second page:
CPPUNIT_ASSERT_EQUAL(static_cast(0), nPage2FlyTop -
nPage2AnchorTop);
// 3rd page:
-auto pPage3 = dynamic_cast(pPage1->GetNext());
+auto pPage3 = dynamic_cast(pPage2->GetNext());
CPPUNIT_ASSERT(pPage3);
const SwSortedObjs& rPage3Objs = *pPage3->GetSortedObjs();
CPPUNIT_ASSERT_EQUAL(static_cast(1), rPage3Objs.size());
@@ -241,6 +241,17 @@ CPPUNIT_TEST_FIXTURE(Test, testSplitFly3Pages)
SwTwips nPage3FlyTop = pPage3Fly->getFrameArea().Top();
// No vert offset on the 3rd page:
CPPUNIT_ASSERT_EQUAL(static_cast(0), nPage3FlyTop -
nPage3AnchorTop);
+
+// Check that the split fly frames, that are all registered at the same
(first fly's) anchor,
+// are sorted there from precede to follow (previously, the order was the
opposite):
+auto pRootAnchor = pPage3Fly->GetAnchorFrame(); // Unlike
FindAnchorCharFrame, this gives root
+CPPUNIT_ASSERT(pRootAnchor);
+CPPUNIT_ASSERT_EQUAL(pPage1Fly->GetAnchorFrame(), pRootAnchor);
+const auto& rAnchoredObjects = *pRootAnchor->GetDrawObjs();
+CPPUNIT_ASSERT_EQUAL(size_t(3), rAnchoredObjects.size());
+CPPUNIT_ASSERT_EQUAL(static_cast(pPage1Fly),
rAnchoredObjects[0]);
+CPPUNIT_ASSERT_EQUAL(static_cast(pPage2Fly),
rAnchoredObjects[1]);
+CPPUNIT_ASSERT_EQUAL(static_cast(pPage3Fly),
rAnchoredObjects[2]);
}
CPPUNIT_TEST_FIXTURE(Test, testSplitFlyRow)
diff --git a/sw/source/core/inc/sortedobjs.hxx
b/sw/source/core/inc/sortedobjs.hxx
index 4ffcd69b3a24..395d7831a922 100644
--- a/sw/source/core/inc/sortedobjs.hxx
+++ b/sw/source/core/inc/sortedobjs.hxx
@@ -42,6 +42,8 @@ class SwAnchoredObject;
- order 1: to-paragraph, 2: to-character, 3: as-character
- anchor node position
- internal anchor order number
+- split fly precede / follow relation
+ - order 1: precede, 2: follow
If one of the sort criteria attributes of an anchored object changes,
the sorting has to be updated - use method
*/
diff --git a/sw/source/core/layout/sortedobjs.cxx
b/sw/source/core/layout/sortedobjs.cxx
index d15c4e79db2b..f74ba71b675b 100644
--- a/sw/source/core/layout/sortedobjs.cxx
+++ b/sw/source/core/layout/sortedobjs.cxx
@@ -21,6 +21,8 @@
#include
#include
+#include
+#include
#include
#include
#include
@@ -198,7 +200,47 @@ struct ObjAnchorOrder
// objects anchored at the same content position/page/fly with same
// wrap influence.
// Thus, compare anchor order number
-return pAnchorListed->GetOrder() < pAnchorNew->GetOrder();
+if (pAnchorListed->GetOrder() != pAnchorNew->GetOrder())
+return pAnchorListed->GetOrder() < pAnchorNew->GetOrder();
+
+// return true, when _pListedAnchoredObj is a precede split fly of
_pNewAnchoredObj
+{
+auto pLHFly = _pListedAnchoredObj->DynCastFlyFrame();
+auto pLHSplitFly = pLHFly && pLHFly->IsFlySplitAllowed()
+ ? static_cast(pLHFly)
+ : nullptr;
+auto pRHFly = _pNewAnchoredObj->DynCastFlyFrame();
+
core.git: Branch 'libreoffice-26-2' - sw/qa sw/source
sw/qa/extras/ooxmlexport/data/redline-range-comment.docx |binary
sw/qa/extras/ooxmlexport/ooxmlexport25.cxx | 17 +++
sw/source/filter/ww8/docxattributeoutput.cxx | 13 +++
3 files changed, 30 insertions(+)
New commits:
commit 4a7ea75108c842e95af769cd246fccf5064fd1f7
Author: Miklos Vajna
AuthorDate: Thu Jan 29 08:37:37 2026 +0100
Commit: Adolfo Jayme Barrientos
CommitDate: Mon Feb 2 15:21:23 2026 +0100
cool#13988 DOCX export: fix missing delete flag on deleted comments with
ranges
Load the bugdoc, see that the comment is marked as deleted (is anchored
inside a delete redline), save, reload, the comment is no longer
deleted.
This is because we write redline start/end around DOCX "runs", and the
comment reference dummy character is one such dummy run. So unless
explicit redline markup is written, comments (their comment reference)
is not inside a redline.
Fix this by checking if we'll write comment references (part of comment
end) in DocxAttributeOutput::EndRun(). If we're inside a delete, write
the delete redline markup around the comment reference, too.
Doing the same for inserts would make sense, but then first we would
need to tweak Writer's UI to insert new comments outside redlines,
otherwise UITest_writer_tests7's
tdf90401.tdf90401.test_tdf142902_remove_personal_info_in_DOCX would
break.
Change-Id: I8d6db05c8a9fd1d5e1e619843967e3f21fb43e59
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/198353
Tested-by: Jenkins
Reviewed-by: Miklos Vajna
(cherry picked from commit a72506da82d2e0334e23a8c3c1cd07282fdd7ca3)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/198364
Reviewed-by: Adolfo Jayme Barrientos
diff --git a/sw/qa/extras/ooxmlexport/data/redline-range-comment.docx
b/sw/qa/extras/ooxmlexport/data/redline-range-comment.docx
new file mode 100644
index ..1110971401f5
Binary files /dev/null and
b/sw/qa/extras/ooxmlexport/data/redline-range-comment.docx differ
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport25.cxx
b/sw/qa/extras/ooxmlexport/ooxmlexport25.cxx
index 407daf14dc9a..4649e916b556 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport25.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport25.cxx
@@ -290,6 +290,23 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf167082)
CPPUNIT_ASSERT_EQUAL(OUString("Heading 1"), aStyleName);
}
+CPPUNIT_TEST_FIXTURE(Test, testRangeCommentInDeleteDocxExport)
+{
+// Given a document with a comment that is inside a delete redline:
+createSwDoc("redline-range-comment.docx");
+
+// When exporting that to DOCX:
+save(TestFilter::DOCX);
+
+// Then make sure that the comment reference is inside the delete redline:
+xmlDocUniquePtr pXmlDoc = parseExport(u"word/document.xml"_ustr);
+// Without the accompanying fix in place, this test would have failed with:
+// - Expected: 1
+// - Actual : 0
+// i.e. the comment anchor was outside the redline.
+CPPUNIT_ASSERT_EQUAL(1, countXPathNodes(pXmlDoc,
"//w:del/w:r/w:commentReference"));
+}
+
CPPUNIT_TEST_FIXTURE(Test, testFloatingTableAnchorPosExport)
{
// Given a document with two floating tables after each other:
diff --git a/sw/source/filter/ww8/docxattributeoutput.cxx
b/sw/source/filter/ww8/docxattributeoutput.cxx
index 286ce3a077da..79dc73574236 100644
--- a/sw/source/filter/ww8/docxattributeoutput.cxx
+++ b/sw/source/filter/ww8/docxattributeoutput.cxx
@@ -1999,7 +1999,20 @@ void DocxAttributeOutput::EndRun(const SwTextNode*
pNode, sal_Int32 nPos, sal_In
DoWriteBookmarksStart(m_rBookmarksStart, m_pMoveRedlineData);
DoWriteBookmarksEnd(m_rBookmarksEnd, false, false); // Write non-moverange
bookmarks
DoWritePermissionsStart();
+
+// Surround annotation references with redline start/end markup if we're
inside a delete.
+bool bHasAnnotationMarkReferencesInDel = !m_rAnnotationMarksEnd.empty() &&
m_pRedlineData
+ && m_pRedlineData->GetType() ==
RedlineType::Delete;
+if (bHasAnnotationMarkReferencesInDel)
+{
+StartRedline(m_pRedlineData, bLastRun);
+}
DoWriteAnnotationMarks();
+if (bHasAnnotationMarkReferencesInDel)
+{
+EndRedline(m_pRedlineData, bLastRun);
+}
+
// if there is some redlining in the document, output it
bool bSkipRedline = false;
if (nLen == 1)
core.git: Branch 'libreoffice-26-2' - sw/qa sw/source
sw/qa/writerfilter/dmapper/DomainMapper_Impl.cxx | 23 +
sw/qa/writerfilter/dmapper/data/redline-range-comment.docx |binary
sw/source/writerfilter/dmapper/DomainMapper_Impl.cxx |2 -
3 files changed, 24 insertions(+), 1 deletion(-)
New commits:
commit 7c4b2ab0883f9dec96aedc9d75ff127363e94915
Author: Miklos Vajna
AuthorDate: Fri Jan 23 09:31:05 2026 +0100
Commit: Adolfo Jayme Barrientos
CommitDate: Mon Feb 2 15:06:36 2026 +0100
cool#13988 DOCX import: fix missing delete flag on deleted comments with
ranges
The bugdoc has commented text range inside a deletion. The import result
doesn't mark the comment as deleted.
Comments with a single anchor point work fine, since commit
419b70b5d4db227509614bdea5b4b89bcf7a6032 (tdf#105485 DOCX: import
deleted comments as deleted, 2019-08-26).
Fix the problem by invoking CheckRedline() in
DomainMapper_Impl::PopAnnotation() in the range case as well, that
correctly creates the delete redline for the comment anchor.
This is shared code for any redlines, so fixes inserts, too.
Change-Id: I620e1419cee79ab729ea7be248f1ad079a9fc536
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/198125
Reviewed-by: Miklos Vajna
Tested-by: Jenkins
(cherry picked from commit 5d101c9919c7c0798df092bd31b828d0a144abba)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/198145
Reviewed-by: Adolfo Jayme Barrientos
diff --git a/sw/qa/writerfilter/dmapper/DomainMapper_Impl.cxx
b/sw/qa/writerfilter/dmapper/DomainMapper_Impl.cxx
index a98f682ab6c4..9349fae282ef 100644
--- a/sw/qa/writerfilter/dmapper/DomainMapper_Impl.cxx
+++ b/sw/qa/writerfilter/dmapper/DomainMapper_Impl.cxx
@@ -29,6 +29,8 @@
#include
#include
#include
+#include
+#include
using namespace ::com::sun::star;
@@ -532,6 +534,27 @@ CPPUNIT_TEST_FIXTURE(Test, testFieldCharHeightHeaderToC)
= rAutoFormat.GetStyleHandle()->Get(RES_CHRATR_FONTSIZE);
CPPUNIT_ASSERT_EQUAL(static_cast(480),
rFontHeightItem.GetHeight());
}
+
+CPPUNIT_TEST_FIXTURE(Test, testAnnotationMarkRedline)
+{
+// Given a document with a commented text range, inside a redline:
+// When importing that document:
+createSwDoc("redline-range-comment.docx");
+
+// Then make sure the comment anchor is inside a redline:
+SwDocShell* pDocShell = getSwDocShell();
+SwWrtShell* pWrtShell = pDocShell->GetWrtShell();
+pWrtShell->SttEndDoc(/*bStt=*/true);
+pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/false, 7,
/*bBasicCall=*/false);
+SwPaM* pCursor = pWrtShell->GetCursor();
+SwField* pField = SwCursorShell::GetFieldAtCursor(pCursor, true);
+CPPUNIT_ASSERT_EQUAL(SwFieldIds::Postit, pField->Which());
+const IDocumentRedlineAccess& rIDRA =
pDocShell->GetDoc()->getIDocumentRedlineAccess();
+const SwRangeRedline* pRedline = rIDRA.GetRedline(*pCursor->Start(),
nullptr);
+// Without the accompanying fix in place, this test would have failed, the
anchor point was not
+// inside a redline.
+CPPUNIT_ASSERT(pRedline);
+}
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/qa/writerfilter/dmapper/data/redline-range-comment.docx
b/sw/qa/writerfilter/dmapper/data/redline-range-comment.docx
new file mode 100644
index ..1110971401f5
Binary files /dev/null and
b/sw/qa/writerfilter/dmapper/data/redline-range-comment.docx differ
diff --git a/sw/source/writerfilter/dmapper/DomainMapper_Impl.cxx
b/sw/source/writerfilter/dmapper/DomainMapper_Impl.cxx
index 0499b42776f1..69a7d810488a 100644
--- a/sw/source/writerfilter/dmapper/DomainMapper_Impl.cxx
+++ b/sw/source/writerfilter/dmapper/DomainMapper_Impl.cxx
@@ -4793,7 +4793,6 @@ void DomainMapper_Impl::PopAnnotation()
{
uno::Sequence< beans::PropertyValue > aEmptyProperties;
appendTextContent( m_xAnnotationField, aEmptyProperties );
-CheckRedline( m_xAnnotationField->getAnchor( ) );
}
else
{
@@ -4855,6 +4854,7 @@ void DomainMapper_Impl::PopAnnotation()
xCursor->setString(OUString());
}
}
+CheckRedline(m_xAnnotationField->getAnchor());
m_aAnnotationPositions.erase( m_nAnnotationId );
}
catch (uno::Exception const&)
core.git: Branch 'libreoffice-26-2' - sw/qa sw/source
sw/qa/extras/uiwriter/uiwriter4.cxx | 62
sw/source/uibase/uiview/view0.cxx | 18 ++
sw/source/uibase/uiview/viewmdi.cxx |3 +
3 files changed, 83 insertions(+)
New commits:
commit 0157169190883943b1711094e4384a19a2febd4f
Author: Andreas Heinisch
AuthorDate: Thu Jan 15 18:08:26 2026 +0100
Commit: Adolfo Jayme Barrientos
CommitDate: Sun Feb 1 10:34:34 2026 +0100
tdf#98446 - Switch to single-page view mode when hiding whitespace
Switch to single-page view mode when hiding whitespace while in
multi-page or book view since hiding whitespace is only possible in
single-view page mode.
Change-Id: Ib3a4281aa0ff1d47a8821aef765003de9ed99173
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/197381
Tested-by: Jenkins
Reviewed-by: Andreas Heinisch
(cherry picked from commit ef562a3be1d9a283966ad2289fa3a24a7014c774)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/198478
Reviewed-by: Adolfo Jayme Barrientos
diff --git a/sw/qa/extras/uiwriter/uiwriter4.cxx
b/sw/qa/extras/uiwriter/uiwriter4.cxx
index 914586cade41..a4c3b4a62bbc 100644
--- a/sw/qa/extras/uiwriter/uiwriter4.cxx
+++ b/sw/qa/extras/uiwriter/uiwriter4.cxx
@@ -113,6 +113,68 @@ CPPUNIT_TEST_FIXTURE(SwUiWriterTest4, testTdf96515)
CPPUNIT_ASSERT_EQUAL(1, getPages());
}
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest4,
testTdf98446_switch_to_single_page_on_hide_whitespace)
+{
+createSwDoc();
+
+SwDocShell* pDocShell = getSwDocShell();
+SwWrtShell* pWrtShell = pDocShell->GetWrtShell();
+SwView* pView = pDocShell->GetView();
+
+// Enable hide whitespace view mode
+SwViewOption aViewOptions(*pWrtShell->GetViewOptions());
+aViewOptions.SetHideWhitespaceMode(true);
+pWrtShell->ApplyViewOptions(aViewOptions);
+CPPUNIT_ASSERT(pWrtShell->GetViewOptions()->IsWhitespaceHidden());
+
+// --- Case 1: Multiple pages per row -
+
+// Switch to multiple pages per row
+dispatchCommand(mxComponent, u".uno:MultiplePagesPerRow"_ustr, {});
+// Without the fix in place, this test would have failed with
+// - Expected: Switching back to single-page layout when hiding whitespace
+// - Actual : Layout remained in multi-column mode
+CPPUNIT_ASSERT(!pWrtShell->GetViewOptions()->IsWhitespaceHidden());
+
+// Re-apply hide whitespace in multi-column mode
+pWrtShell->ApplyViewOptions(aViewOptions);
+
+// Without the fix in place, this test would have failed with
+// - Expected: Switching back to single-page layout when hiding whitespace
+// - Actual : Layout remained in multi-column mode
+CPPUNIT_ASSERT(!pWrtShell->GetViewOptions()->IsViewLayoutBookMode());
+CPPUNIT_ASSERT_EQUAL(sal_uInt16(1),
pWrtShell->GetViewOptions()->GetViewLayoutColumns());
+
+// --- Case 2: Book mode --
+dispatchCommand(mxComponent, u".uno:BookView"_ustr, {});
+// Without the fix in place, this test would have failed with
+// - Expected: Switching back to single-page layout when hiding whitespace
+// - Actual : Layout remained in book mode
+CPPUNIT_ASSERT(!pWrtShell->GetViewOptions()->IsWhitespaceHidden());
+
+// Re-apply hide whitespace in multi-column mode
+pWrtShell->ApplyViewOptions(aViewOptions);
+// Without the fix in place, this test would have failed with
+// - Expected: Switching back to single-page layout when hiding whitespace
+// - Actual : Layout remained in book mode
+CPPUNIT_ASSERT(!pWrtShell->GetViewOptions()->IsViewLayoutBookMode());
+CPPUNIT_ASSERT_EQUAL(sal_uInt16(1),
pWrtShell->GetViewOptions()->GetViewLayoutColumns());
+
+// --- Case 3: Manual layout changes --
+
+// Without the fix in place, this test would have failed with
+// - Expected: Multi-column layout disables whitespace hiding
+// - Actual : Whitespace remained hidden in multi-column mode
+pView->SetViewLayout(/*nColumns=*/0, /*bBookMode=*/false);
+CPPUNIT_ASSERT(!pWrtShell->GetViewOptions()->IsWhitespaceHidden());
+
+// Without the fix in place, this test would have failed with
+// - Expected: Multi-column layout disables whitespace hiding
+// - Actual : Whitespace remained hidden in multi-column mode
+pView->SetViewLayout(/*nColumns=*/2, /*bBookMode=*/true);
+CPPUNIT_ASSERT(!pWrtShell->GetViewOptions()->IsWhitespaceHidden());
+}
+
static OUString lcl_translitTest(SwDoc& rDoc, const SwPaM& rPaM,
TransliterationFlags const nType)
{
utl::TransliterationWrapper
aTrans(::comphelper::getProcessComponentContext(), nType);
diff --git a/sw/source/uibase/uiview/view0.cxx
b/sw/source/uibase/uiview/view0.cxx
index 0ee6dab7bf72..842f12aa3241 100644
--- a/sw/source/uibase/uiview/view0.cxx
+++ b/sw/source/uibase/uiview/view0.cxx
@@ -523,6 +523,13 @@ void SwView::ExecViewOptions(SfxRequest &rReq)
core.git: Branch 'libreoffice-26-2' - sw/qa sw/source
sw/qa/core/text/itrpaint.cxx | 11 +++ sw/source/core/layout/fly.cxx |7 ++- 2 files changed, 17 insertions(+), 1 deletion(-) New commits: commit 0a7c43f113946cc9f7c92f06d0cb4141cd84bbb7 Author: Miklos Vajna AuthorDate: Wed Jan 28 15:26:24 2026 +0100 Commit: Adolfo Jayme Barrientos CommitDate: Sun Feb 1 04:33:25 2026 +0100 cool#13988 sw redline render mode: add colored border for inline images Anchored images conditionally got a red/green frame in since commit afdf4e287fb91c7baf2767eb95b0565472b8343d (cool#13988 sw redline render mode: add colored border for anchored images, 2026-01-27), but the same didn't work for inline images. Seems what happens is that SwFlyFrame::GetRedlineRenderModeFrame() sets the border line with to 1 (in twips, so some minimal value), but then this is turned into a SdrFrameBorderPrimitive2D, which gets decomposed into a PolygonStrokePrimitive2D. At this point the cairo pixel processor handles it at CairoPixelProcessor2D::processPolygonStrokePrimitive2D(), which assumes that hairline is when the width is 0. The trouble is that in case Writer sets the width to 0, then we don't even start processing such stroke primitives. Solve the problem by going with a larger border width: just query from output device what would be a logic value for a 1px border, so it does show up reliably. This is similar to how SwViewOption::s_nPixelTwips gets configured already. Note that the testcase just asserts these polygons are painted, but it can't verify how CairoPixelProcessor2D simply didn't paint the old bad width. Change-Id: I83f7aec4d09fc628dd314e1325bfd47af07308ca Reviewed-on: https://gerrit.libreoffice.org/c/core/+/198339 Reviewed-by: Miklos Vajna Tested-by: Jenkins (cherry picked from commit 36a6ca3e6de7106bdd0497386d29a05337c5a90c) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/198352 Reviewed-by: Adolfo Jayme Barrientos diff --git a/sw/qa/core/text/itrpaint.cxx b/sw/qa/core/text/itrpaint.cxx index 14c1af1a02b4..b5debdfa8f83 100644 --- a/sw/qa/core/text/itrpaint.cxx +++ b/sw/qa/core/text/itrpaint.cxx @@ -290,6 +290,9 @@ CPPUNIT_TEST_FIXTURE(Test, testInlineImageRedlineRenderModeOmitInsertDelete) CPPUNIT_ASSERT(!IsGrayScale(aImages[0])); CPPUNIT_ASSERT(!IsGrayScale(aImages[1])); CPPUNIT_ASSERT(!IsGrayScale(aImages[2])); +std::vector aPolygons = GetMetaFilePolylines(*xMetaFile); +// No frames around images. +CPPUNIT_ASSERT(aPolygons.empty()); // Omit insert: default, default, grayscale. SwWrtShell* pWrtShell = pDocShell->GetWrtShell(); @@ -306,6 +309,10 @@ CPPUNIT_TEST_FIXTURE(Test, testInlineImageRedlineRenderModeOmitInsertDelete) // Without the accompanying fix in place, this test would have failed, the image's center pixel // wasn't gray. CPPUNIT_ASSERT(IsGrayScale(aImages[2])); +aPolygons = GetMetaFilePolylines(*xMetaFile); +// Frame around the deleted image: no polygons -> no frame. +CPPUNIT_ASSERT(!aPolygons.empty()); +CPPUNIT_ASSERT(RectangleContainsPolygons(aImages[1].m_aRectangle, aPolygons)); // Omit deletes: default, grayscale, default. aOpt.SetRedlineRenderMode(SwRedlineRenderMode::OmitDeletes); @@ -318,6 +325,10 @@ CPPUNIT_TEST_FIXTURE(Test, testInlineImageRedlineRenderModeOmitInsertDelete) CPPUNIT_ASSERT(!IsGrayScale(aImages[0])); CPPUNIT_ASSERT(IsGrayScale(aImages[1])); CPPUNIT_ASSERT(!IsGrayScale(aImages[2])); +aPolygons = GetMetaFilePolylines(*xMetaFile); +// Frame around the inserted image. +CPPUNIT_ASSERT(!aPolygons.empty()); +CPPUNIT_ASSERT(RectangleContainsPolygons(aImages[2].m_aRectangle, aPolygons)); } } diff --git a/sw/source/core/layout/fly.cxx b/sw/source/core/layout/fly.cxx index 8e02d478681a..ff838a1e5763 100644 --- a/sw/source/core/layout/fly.cxx +++ b/sw/source/core/layout/fly.cxx @@ -2364,7 +2364,12 @@ bool SwFlyFrame::GetRedlineRenderModeFrame(SvxBoxItem& rBoxItem) const } editeng::SvxBorderLine aBorderLine; -aBorderLine.SetWidth(1); + +// Set a logic value which is roughly 1 pixel wide, so the border is visible. +vcl::RenderContext* pOut = pViewShell->GetOut(); +tools::Long nWidth = pOut ? pOut->PixelToLogic(Size(1, 1)).Width() : 0; +aBorderLine.SetWidth(nWidth); + aBorderLine.SetBorderLineStyle(SvxBorderLineStyle::SOLID); aBorderLine.SetColor(*oColor); rBoxItem.SetLine(&aBorderLine, SvxBoxItemLine::LEFT);
core.git: Branch 'libreoffice-26-2' - sw/qa sw/source
sw/qa/extras/layout/data/tdf170477.docx |binary
sw/qa/extras/layout/layout6.cxx |7 +++
sw/source/core/layout/tabfrm.cxx| 27 +--
3 files changed, 32 insertions(+), 2 deletions(-)
New commits:
commit c2c33dbdbc686e7c0eb349efdd15a8c97d8f00a7
Author: Mike Kaganski
AuthorDate: Mon Jan 26 10:46:04 2026 +0500
Commit: Xisco Fauli
CommitDate: Thu Jan 29 10:18:59 2026 +0100
tdf#170477: detect MoveFwd oscillation in SwTabFrame::MakeAll
In the specific bug document, some constellation of factors created
the case where the last inner floating table tries to move forward,
in the process creates a split fly on the same page (subject to move
forward later, when outer table formats), moves to it (leaving the
old fly empty), which recreates the same layout as before; and that
oscillated infinitely.
The fix implemented here is to detect when MoveFwd call created the
described situation, and then use SwLayouter::InsertMovedFwdFrame
to break the oscillation.
I was not able to reproduce this problem anew; test document was
simplified from the original, with the aim to minimize instability
(but still, the resulting layout in Writer differs from Word's,
which makes the test likely to stop testing the intended code path
in the future - sigh).
Change-Id: Iaa796a169cb9f97d5a6b3dd7ebf1cf5b726ca568
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/198116
Reviewed-by: Mike Kaganski
Tested-by: Jenkins
(cherry picked from commit a5832581691c3532f24d754ed0488e7df43d82c0)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/198134
Reviewed-by: Xisco Fauli
diff --git a/sw/qa/extras/layout/data/tdf170477.docx
b/sw/qa/extras/layout/data/tdf170477.docx
new file mode 100644
index ..646d7191c92d
Binary files /dev/null and b/sw/qa/extras/layout/data/tdf170477.docx differ
diff --git a/sw/qa/extras/layout/layout6.cxx b/sw/qa/extras/layout/layout6.cxx
index e69f42a197ab..88fd08d5113e 100644
--- a/sw/qa/extras/layout/layout6.cxx
+++ b/sw/qa/extras/layout/layout6.cxx
@@ -2042,6 +2042,13 @@ CPPUNIT_TEST_FIXTURE(SwLayoutWriter6,
testTdf170381_split_float_table_in_float_t
assertCellLines(2, r + 1, page2cells[r]);
}
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter6, testTdf170477)
+{
+// This document must not hang on layout:
+createSwDoc("tdf170477.docx");
+calcLayout();
+}
+
} // end of anonymous namespace
CPPUNIT_PLUGIN_IMPLEMENT();
diff --git a/sw/source/core/layout/tabfrm.cxx b/sw/source/core/layout/tabfrm.cxx
index a5bbd2b3710b..69b37c37a47d 100644
--- a/sw/source/core/layout/tabfrm.cxx
+++ b/sw/source/core/layout/tabfrm.cxx
@@ -3277,6 +3277,7 @@ void SwTabFrame::MakeAll(vcl::RenderContext*
pRenderContext)
// #i29771# Reset bTryToSplit flag on change of upper
const SwFrame* pOldUpper = GetUpper();
+const SwPageFrame* pOldUpperPage = nullptr;
//Let's see if we find some place anywhere...
if (!bMovedFwd)
@@ -3291,6 +3292,7 @@ void SwTabFrame::MakeAll(vcl::RenderContext*
pRenderContext)
// If the anchor of the split has a previous frame,
MoveFwd() is allowed to move
// forward.
bMoveAlways = true;
+pOldUpperPage = pFlyFrame->FindPageFrame();
}
}
// don't make the effort to move fwd if its known
@@ -3304,8 +3306,29 @@ void SwTabFrame::MakeAll(vcl::RenderContext*
pRenderContext)
// #i29771# Reset bSplitError flag on change of upper
if ( GetUpper() != pOldUpper )
{
-bTryToSplit = true;
-nUnSplitted = 5;
+bool bResetSplit = true;
+if (GetUpper() && GetUpper()->IsFlyFrame() && pOldUpperPage
+&& GetUpper()->FindPageFrame() == pOldUpperPage
+&& static_cast(GetUpper())->IsFlySplitAllowed())
+{
+// MoveFwd created a split (follow) fly on the same page, that
was intended to move
+// to the next page together with its anchor. Then the content
was moved to the new
+// fly, and the old fly became empty; it called its
DelEmpty(). The new fly is now
+// basically a copy of the old one; move to the next page and
prevent oscillation.
+SwTextFrame* pAnchor =
static_cast(GetUpper())->FindAnchorCharFrame();
+if (pAnchor)
+{
+bResetSplit = false;
+SwPageFrame* pPage = pAnchor->FindPageFrame();
+
SwLayouter::InsertMovedFwdFrame(pPage->GetFormat()->GetDoc(), *pAnchor,
+pPage->GetPhyPageNum() +
1);
+}
+}
+if (bResetSplit)
+{
+bTryToSplit = true;
+nUnSplitted = 5;
+
core.git: Branch 'libreoffice-26-2' - sw/qa sw/source
sw/qa/extras/ooxmlexport/data/tdf165359_SdtWithDrawing.docx |binary
sw/qa/extras/ooxmlexport/data/tdf165359_SdtWithInline.docx |binary
sw/qa/extras/ooxmlexport/ooxmlexport25.cxx | 22
sw/source/writerfilter/dmapper/DomainMapper_Impl.cxx|7 +++
4 files changed, 29 insertions(+)
New commits:
commit 09558848c5b5b26e167dd85512935309d5d67723
Author: Justin Luth
AuthorDate: Mon Jan 26 10:21:04 2026 -0500
Commit: Justin Luth
CommitDate: Tue Jan 27 18:04:24 2026 +0100
tdf#165359 writerfilter: import graphic into richText, not plainText
MS Word complains that documents are corrupt
if a w:sdt (plainText content control)
contains a picture.
make CppunitTest_sw_ooxmlexport25 \
CPPUNIT_TEST_NAME=testTdf165359_SdtWithInline
make CppunitTest_sw_ooxmlexport25 \
CPPUNIT_TEST_NAME=testTdf165359_SdtWithDrawing
Change-Id: Ic1533a71fb4e28e7e00976f5c8f747cf2d6b
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/198049
Tested-by: Jenkins
Reviewed-by: Justin Luth
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/198198
diff --git a/sw/qa/extras/ooxmlexport/data/tdf165359_SdtWithDrawing.docx
b/sw/qa/extras/ooxmlexport/data/tdf165359_SdtWithDrawing.docx
new file mode 100644
index ..31a37c5e8f2f
Binary files /dev/null and
b/sw/qa/extras/ooxmlexport/data/tdf165359_SdtWithDrawing.docx differ
diff --git a/sw/qa/extras/ooxmlexport/data/tdf165359_SdtWithInline.docx
b/sw/qa/extras/ooxmlexport/data/tdf165359_SdtWithInline.docx
new file mode 100644
index ..87d4ddac6770
Binary files /dev/null and
b/sw/qa/extras/ooxmlexport/data/tdf165359_SdtWithInline.docx differ
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport25.cxx
b/sw/qa/extras/ooxmlexport/ooxmlexport25.cxx
index 49e0ba96bedc..407daf14dc9a 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport25.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport25.cxx
@@ -159,6 +159,28 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf170438_dropdown)
assertXPath(pXmlDoc, "//w:listItem[1]", "value", u""); // value may be
empty
}
+CPPUNIT_TEST_FIXTURE(Test, testTdf165359_SdtWithInline)
+{
+createSwDoc("tdf165359_SdtWithInline.docx");
+
+save(TestFilter::DOCX);
+
+xmlDocUniquePtr pXmlDoc = parseExport(u"word/document.xml"_ustr);
+// MS Word reports document as corrupt if a picture is inside of a
plainText content control
+assertXPath(pXmlDoc, "//w:sdtPr/w:text", 0);
+}
+
+CPPUNIT_TEST_FIXTURE(Test, testTdf165359_SdtWithDrawing)
+{
+createSwDoc("tdf165359_SdtWithDrawing.docx");
+
+save(TestFilter::DOCX);
+
+xmlDocUniquePtr pXmlDoc = parseExport(u"word/document.xml"_ustr);
+// MS Word reports document as corrupt if a picture is inside of a
plainText content control
+assertXPath(pXmlDoc, "//w:sdtPr/w:text", 0);
+}
+
CPPUNIT_TEST_FIXTURE(Test, testTdf170389_manyTabstops)
{
createSwDoc("tdf170389_manyTabstops.odt");
diff --git a/sw/source/writerfilter/dmapper/DomainMapper_Impl.cxx
b/sw/source/writerfilter/dmapper/DomainMapper_Impl.cxx
index 8d461f0a4c12..0499b42776f1 100644
--- a/sw/source/writerfilter/dmapper/DomainMapper_Impl.cxx
+++ b/sw/source/writerfilter/dmapper/DomainMapper_Impl.cxx
@@ -9667,6 +9667,13 @@ void DomainMapper_Impl::ImportGraphic(const
writerfilter::Reference
}
}
+if (m_pSdtHelper->getControlType() == SdtControlType::plainText
+&& (m_StreamStateStack.top().bSdt || GetSdtStarts().size()))
+{
+// plainText controls cannot contain pictures or shapes
+m_pSdtHelper->setControlType(SdtControlType::richText);
+}
+
// Update the shape properties if it is embedded object.
if (m_StreamStateStack.top().xEmbedded.is())
core.git: Branch 'libreoffice-26-2' - sw/qa sw/source
sw/qa/extras/ooxmlexport/ooxmlexport26.cxx | 15 +++ sw/source/filter/ww8/docxexport.cxx|4 ++-- 2 files changed, 17 insertions(+), 2 deletions(-) New commits: commit f59f90465e0187f15217c909ada3fabf811a571d Author: Justin Luth AuthorDate: Fri Jan 23 11:12:52 2026 -0500 Commit: Justin Luth CommitDate: Mon Jan 26 17:57:03 2026 +0100 tdf#170457 docx export: only export comment start/end for this node Without this patch, LO was creating documents that not only had the comment indicators in the wrong position, but was also being (correctly) reported as corrupt by MS Word. make CppunitTest_sw_ooxmlexport26 CPPUNIT_TEST_NAME=testwDateValueFormat Change-Id: I237742274d5d57bb491e25116d90d42c231faced Reviewed-on: https://gerrit.libreoffice.org/c/core/+/198018 Reviewed-by: Justin Luth Tested-by: Jenkins Reviewed-on: https://gerrit.libreoffice.org/c/core/+/198158 diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport26.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport26.cxx index ee0014db29fd..055a49636e68 100644 --- a/sw/qa/extras/ooxmlexport/ooxmlexport26.cxx +++ b/sw/qa/extras/ooxmlexport/ooxmlexport26.cxx @@ -42,6 +42,21 @@ CPPUNIT_TEST_FIXTURE(Test, testwDateValueFormat) // - Actual : 44 // - validation error in OOXML export: Errors: 44 saveAndReload(TestFilter::DOCX); + +// tdf#170457: round-tripped document was indicated as corrupt by MS Word +xmlDocUniquePtr pXmlDoc = parseExport(u"word/document.xml"_ustr); +xmlDocUniquePtr pXmlComments = parseExport(u"word/comments.xml"_ustr); + +int nComments = countXPathNodes(pXmlComments, "//w:comment"); + +int nCommentReferences = countXPathNodes(pXmlDoc, "//w:commentReference"); +// Each comment is referenced - the counts must match +CPPUNIT_ASSERT_EQUAL(nComments, nCommentReferences); +CPPUNIT_ASSERT_EQUAL(int(22), nCommentReferences); + +int nCommentStarts = countXPathNodes(pXmlDoc, "//w:commentRangeStart"); +int nCommentEnds = countXPathNodes(pXmlDoc, "//w:commentRangeEnd"); +CPPUNIT_ASSERT_EQUAL(nCommentStarts, nCommentEnds); } CPPUNIT_TEST_FIXTURE(Test, testTdf124491) diff --git a/sw/source/filter/ww8/docxexport.cxx b/sw/source/filter/ww8/docxexport.cxx index 7a0b872ebcc1..63a084c6a44a 100644 --- a/sw/source/filter/ww8/docxexport.cxx +++ b/sw/source/filter/ww8/docxexport.cxx @@ -215,10 +215,10 @@ void DocxExport::AppendAnnotationMarks( const SwWW8AttrIter& rAttrs, sal_Int32 n const sal_Int32 nStart = pMark->GetMarkStart().GetContentIndex(); const sal_Int32 nEnd = pMark->GetMarkEnd().GetContentIndex(); -if ( nStart == nCurrentPos ) +if (nStart == nCurrentPos && rAttrs.GetNode() == pMark->GetMarkStart().GetNode()) aStarts.push_back( pMark->GetName() ); -if ( nEnd == nCurrentPos ) +if (nEnd == nCurrentPos && rAttrs.GetNode() == pMark->GetMarkEnd().GetNode()) aEnds.push_back( pMark->GetName() ); } }
core.git: Branch 'libreoffice-26-2' - sw/qa sw/source
sw/qa/extras/ooxmlexport/data/tdf170438_dropdown.odt |binary
sw/qa/extras/ooxmlexport/ooxmlexport25.cxx | 12
sw/source/filter/ww8/docxattributeoutput.cxx |6 --
3 files changed, 16 insertions(+), 2 deletions(-)
New commits:
commit f5e64db444b5cd5cdb53b2744bd52d441a0169b3
Author: Justin Luth
AuthorDate: Thu Jan 22 13:22:17 2026 -0500
Commit: Justin Luth
CommitDate: Mon Jan 26 17:56:25 2026 +0100
tdf#170438 docx export: never export listItem with empty displayText
MS Word was reporting a document as corrupt
after LO round-tripped it with an empty displayText.
However, an empty w:value is not a 'corrupt' result.
Note that this situation was possible also while importing DOCX.
We don't always import SDTs as content controls (e.g. in tables).
In that case, only the 'values' are considered,
so an empty entry is created in the dropdown field,
which then got exported as an empty displayText.
make CppunitTest_sw_ooxmlexport25 \
CPPUNIT_TEST_NAME=testTdf170438_dropdown
ooxmlexport13's tdf119809.docx is a VML shape
that is called a combobox, and it exported
with WritePostponedFormControl.
It sounds like it can only have 'values' when added by a VBA macro,
so likely these will almost never output a displayText.
Change-Id: Ib306eda90b4179e10402c276e7c47a3bfdd83cfb
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/197867
Reviewed-by: Justin Luth
Tested-by: Jenkins
(cherry picked from commit 270fe90efbb275a670d063303e395ed88ea4731d)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/198157
diff --git a/sw/qa/extras/ooxmlexport/data/tdf170438_dropdown.odt
b/sw/qa/extras/ooxmlexport/data/tdf170438_dropdown.odt
new file mode 100644
index ..02bbe360bf25
Binary files /dev/null and
b/sw/qa/extras/ooxmlexport/data/tdf170438_dropdown.odt differ
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport25.cxx
b/sw/qa/extras/ooxmlexport/ooxmlexport25.cxx
index 009b92a1d663..49e0ba96bedc 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport25.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport25.cxx
@@ -147,6 +147,18 @@ DECLARE_OOXMLEXPORT_TEST(testTdf165478_bottomAligned,
"tdf165478_bottomAligned.d
CPPUNIT_ASSERT_EQUAL(sal_Int32(1887), nFlyTop);
}
+CPPUNIT_TEST_FIXTURE(Test, testTdf170438_dropdown)
+{
+createSwDoc("tdf170438_dropdown.odt");
+
+saveAndReload(TestFilter::DOCX);
+
+xmlDocUniquePtr pXmlDoc = parseExport(u"word/document.xml"_ustr);
+// MS Word reports document as corrupt if displayText is empty
+assertXPath(pXmlDoc, "//w:listItem[1]", "displayText", u" ");
+assertXPath(pXmlDoc, "//w:listItem[1]", "value", u""); // value may be
empty
+}
+
CPPUNIT_TEST_FIXTURE(Test, testTdf170389_manyTabstops)
{
createSwDoc("tdf170389_manyTabstops.odt");
diff --git a/sw/source/filter/ww8/docxattributeoutput.cxx
b/sw/source/filter/ww8/docxattributeoutput.cxx
index 0673a2601423..286ce3a077da 100644
--- a/sw/source/filter/ww8/docxattributeoutput.cxx
+++ b/sw/source/filter/ww8/docxattributeoutput.cxx
@@ -2887,9 +2887,10 @@ void DocxAttributeOutput::WriteSdtDropDownStart(
for (auto const& rItem : rListItems)
{
auto const item(OUStringToOString(rItem, RTL_TEXTENCODING_UTF8));
+OString sDisplayText = item.isEmpty() ? " "_ostr : item; //
displayText must not be empty
m_pSerializer->singleElementNS(XML_w, XML_listItem,
FSNS(XML_w, XML_value), item,
-FSNS(XML_w, XML_displayText), item);
+FSNS(XML_w, XML_displayText), sDisplayText);
}
m_pSerializer->endElementNS(XML_w, XML_dropDownList);
@@ -5899,8 +5900,9 @@ void DocxAttributeOutput::WritePostponedFormControl(const
SdrObject* pObject)
for (const auto& rItem : aItems)
{
+OUString sDisplayText = rItem.isEmpty() ? " " : rItem; //
displayText must not be empty
m_pSerializer->singleElementNS(XML_w, XML_listItem,
- FSNS(XML_w, XML_displayText), rItem,
+ FSNS(XML_w, XML_displayText),
sDisplayText,
FSNS(XML_w, XML_value), rItem);
}
core.git: Branch 'libreoffice-26-2' - sw/qa sw/source xmloff/qa
sw/qa/extras/layout/layout5.cxx | 52 ---
sw/source/core/doc/textboxhelper.cxx |8 ++---
xmloff/qa/unit/draw.cxx |2 -
3 files changed, 41 insertions(+), 21 deletions(-)
New commits:
commit de070c8e5932f1bf338f6eb9a2d9b43582fb9d5a
Author: Michael Stahl
AuthorDate: Mon Jan 19 16:16:15 2026 +0100
Commit: Xisco Fauli
CommitDate: Thu Jan 22 19:55:51 2026 +0100
sw: fix setting mbIsTextBox on shapes' text boxes
SetTextBoxes(true) is never called because it checks the shape's
SdrObject, while previously this code was in
SwFrameFormat::SetOtherTextBoxFormat() where it would be called a second
time for the text box SwFrameFormat which has the SwFlyDrawObj.
SwTextBoxHelper::getCount() can't ignore the text boxes if the flag is
never set; this ends up confusing
ShapeGroupContext::popGroupAndPostProcess() (and causes tests like
testTdf84695 to fail with the fixes in the next commit).
Because shapes with text boxes are sorted again with this, testTdf146272
and testTextBoxLoss need adapting.
(regression from commit 504d78acb866495fd954fcd6db22ea68f174a5ab)
Change-Id: I80c375d900a10f1956cd112d60c3f25332c485ac
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/197594
Reviewed-by: Michael Stahl
Tested-by: Jenkins CollaboraOffice
(cherry picked from commit 327e92c78603e011e7ddf8b57749dfd9e690addf)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/197729
Tested-by: Jenkins
(cherry picked from commit 9e0d4c693ce3ac496372906da2c452c82c3d5d3d)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/197772
Reviewed-by: Xisco Fauli
diff --git a/sw/qa/extras/layout/layout5.cxx b/sw/qa/extras/layout/layout5.cxx
index b1b6c9cc9d23..0fcf72b12bf0 100644
--- a/sw/qa/extras/layout/layout5.cxx
+++ b/sw/qa/extras/layout/layout5.cxx
@@ -66,21 +66,43 @@ CPPUNIT_TEST_FIXTURE(SwLayoutWriter5, testTdf146272)
{
createSwDoc("tdf146272.odt");
-uno::Reference xPicture(getShape(2), uno::UNO_QUERY);
-uno::Reference xDrawing(getShape(1), uno::UNO_QUERY);
-uno::Reference
xFrame(xDrawing->getPropertyValue(u"TextBoxContent"_ustr),
- uno::UNO_QUERY);
-
-CPPUNIT_ASSERT(xPicture);
-CPPUNIT_ASSERT(xDrawing);
-CPPUNIT_ASSERT(xFrame);
-
-const sal_uInt64 nPitureZorder =
xPicture->getPropertyValue(u"ZOrder"_ustr).get();
-const sal_uInt64 nDrawingZorder =
xDrawing->getPropertyValue(u"ZOrder"_ustr).get();
-const sal_uInt64 nFrameZorder =
xFrame->getPropertyValue(u"ZOrder"_ustr).get();
-
-CPPUNIT_ASSERT_MESSAGE("Bad ZOrder!", nDrawingZorder < nFrameZorder);
-CPPUNIT_ASSERT_MESSAGE("Bad ZOrder!", nFrameZorder < nPitureZorder);
+uno::Reference
xPicture1(getShapeByName(u"graphics1"), uno::UNO_QUERY);
+uno::Reference xDrawing1(getShapeByName(u"Shape2"),
uno::UNO_QUERY);
+uno::Reference
xFrame1(xDrawing1->getPropertyValue(u"TextBoxContent"_ustr),
+uno::UNO_QUERY);
+
+CPPUNIT_ASSERT(xPicture1);
+CPPUNIT_ASSERT(xDrawing1);
+CPPUNIT_ASSERT(xFrame1);
+
+const sal_uInt64 nPicture1Zorder
+= xPicture1->getPropertyValue(u"ZOrder"_ustr).get();
+const sal_uInt64 nDrawing1Zorder
+= xDrawing1->getPropertyValue(u"ZOrder"_ustr).get();
+const sal_uInt64 nFrame1Zorder =
xFrame1->getPropertyValue(u"ZOrder"_ustr).get();
+
+CPPUNIT_ASSERT_MESSAGE("Bad ZOrder!", nDrawing1Zorder < nFrame1Zorder);
+CPPUNIT_ASSERT_MESSAGE("Bad ZOrder!", nFrame1Zorder < nPicture1Zorder);
+
+uno::Reference xPicture2(getShapeByName(u"Image423"),
uno::UNO_QUERY);
+uno::Reference xDrawing2(getShapeByName(u"Shape3"),
uno::UNO_QUERY);
+uno::Reference
xFrame2(xDrawing2->getPropertyValue(u"TextBoxContent"_ustr),
+uno::UNO_QUERY);
+
+CPPUNIT_ASSERT(xPicture2);
+CPPUNIT_ASSERT(xDrawing2);
+CPPUNIT_ASSERT(xFrame2);
+
+const sal_uInt64 nPicture2Zorder
+= xPicture2->getPropertyValue(u"ZOrder"_ustr).get();
+const sal_uInt64 nDrawing2Zorder
+= xDrawing2->getPropertyValue(u"ZOrder"_ustr).get();
+const sal_uInt64 nFrame2Zorder =
xFrame2->getPropertyValue(u"ZOrder"_ustr).get();
+
+CPPUNIT_ASSERT_MESSAGE("Bad ZOrder!", nDrawing2Zorder < nFrame2Zorder);
+CPPUNIT_ASSERT_MESSAGE("Bad ZOrder!", nFrame2Zorder < nPicture2Zorder);
+CPPUNIT_ASSERT_MESSAGE("Bad ZOrder!", nDrawing1Zorder < nDrawing2Zorder);
+CPPUNIT_ASSERT_MESSAGE("Bad ZOrder!", nDrawing2Zorder < nPicture1Zorder);
}
CPPUNIT_TEST_FIXTURE(SwLayoutWriter5, testTdf138773)
diff --git a/sw/source/core/doc/textboxhelper.cxx
b/sw/source/core/doc/textboxhelper.cxx
index 59798f4f0afc..bc4f11515187 100644
--- a/sw/source/core/doc/textboxhelper.cxx
+++ b/sw/source/core/doc/textboxhelper.cxx
@@ -1803,11 +1803,9 @@ void SwTextBoxNode::AddTextBox(SdrObject* pDrawO
core.git: Branch 'libreoffice-26-2' - sw/qa sw/source
sw/qa/extras/ooxmlexport/ooxmlexport17.cxx |5 +
sw/source/filter/ww8/docxattributeoutput.cxx |5 ++---
2 files changed, 7 insertions(+), 3 deletions(-)
New commits:
commit be476e289f4bee48bee526d2d039831a38b6d538
Author: Noel Grandin
AuthorDate: Thu Jan 22 10:04:24 2026 +0100
Commit: Xisco Fauli
CommitDate: Thu Jan 22 19:19:02 2026 +0100
Revert "officeotron: tabIndex=-1 is invalid"
This reverts commit 1bc98666e33c608d81c75fedcd51bba3745a32ed.
It appears that the tabIndex==-1 is us deliberately disobeying the
spec so that we can round-trip a value that MSOffice does not
really care about.
See
commit 52d5f2e247e6b0a638fc0ec83f5dbc1cca30d1cd
Author: Justin Luth
Date: Wed Jan 25 16:29:55 2023 -0500
tdf#151548 sw content controls: preserve tabIndex val="-1"
Change-Id: I1383386ebc3d4d3f47ba427f33aa22215f10ac8e
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/197794
Tested-by: Jenkins
Reviewed-by: Noel Grandin
(cherry picked from commit 21bba2556bb5b0476546142109045ba11107d61b)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/197851
Reviewed-by: Xisco Fauli
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport17.cxx
b/sw/qa/extras/ooxmlexport/ooxmlexport17.cxx
index 3a42e7e3890d..9d11d309a074 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport17.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport17.cxx
@@ -427,10 +427,14 @@ 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(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);
@@ -453,6 +457,7 @@ 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 a695f51221c7..73d23da6d7a9 100644
--- a/sw/source/filter/ww8/docxattributeoutput.cxx
+++ b/sw/source/filter/ww8/docxattributeoutput.cxx
@@ -2722,9 +2722,8 @@ 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(m_pContentControl->GetTabIndex());
-if (nTabIndex != -1)
-m_pSerializer->singleElementNS(XML_w, XML_tabIndex, FSNS(XML_w,
XML_val),
- OString::number(nTabIndex));
+m_pSerializer->singleElementNS(XML_w, XML_tabIndex, FSNS(XML_w,
XML_val),
+ OString::number(nTabIndex));
}
if (m_pContentControl->GetPicture())
core.git: Branch 'libreoffice-26-2' - sw/qa sw/source
sw/qa/extras/ooxmlexport/ooxmlexport25.cxx | 13 ++--- sw/source/filter/ww8/docxattributeoutput.cxx |6 +- 2 files changed, 15 insertions(+), 4 deletions(-) New commits: commit cdbebd3086498673ee5b668e3aaeb5c90674825f Author: [email protected] AuthorDate: Mon Jan 19 14:15:36 2026 -0500 Commit: Justin Luth CommitDate: Thu Jan 22 16:06:46 2026 +0100 tdf#170389 docx export: don't spam inherited tabstops Until now, we had been duplicating inherited tabstops into the current style or paragraph. While not a terrible thing, it is unnecessary (assuming inheritance is working properly). Also, it COULD (though unlikely) contribute to hitting the 64 maximum tabstop limit that DOCX has. That could have resulted in discarding necessary tabstops. make CppunitTest_sw_ooxmlexport25 \ CPPUNIT_TEST_NAME=testTdf170389_manyTabstops Change-Id: Ib68eaf043af15384fea2cd96e8fe178a505ec1db Reviewed-on: https://gerrit.libreoffice.org/c/core/+/197607 Reviewed-by: Justin Luth Tested-by: Jenkins (cherry picked from commit bef77a531f0a8d270a61e908149312ee243182e6) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/197779 diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport25.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport25.cxx index f6feec1fa36a..009b92a1d663 100644 --- a/sw/qa/extras/ooxmlexport/ooxmlexport25.cxx +++ b/sw/qa/extras/ooxmlexport/ooxmlexport25.cxx @@ -151,12 +151,19 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf170389_manyTabstops) { createSwDoc("tdf170389_manyTabstops.odt"); -save(TestFilter::DOCX); +saveAndReload(TestFilter::DOCX); xmlDocUniquePtr pXmlDoc = parseExport(u"word/document.xml"_ustr); // MS Word reports document as corrupt if it has more than 64 tabstops defined -// The paragraph itself defines 40, and inherits 40. Without the fix, this was 80 -assertXPath(pXmlDoc, "//w:tabs/w:tab", 64); +// The paragraph itself defines 40, and inherits 40. Without the fixes, this was 80 or 64 +assertXPath(pXmlDoc, "//w:tabs/w:tab", 40); + +xmlDocUniquePtr pLayout = parseLayoutDump(); +sal_Int32 nSize += getXPath(pLayout, "//SwFixPortion[@type='PortionType::TabLeft']", "width").toInt32(); +// The word 'tabstop' should be almost at the very end of the line, starting at 6 inches. +// Without the fix, the tabstop's width was a tiny 247, now it is 1797. +CPPUNIT_ASSERT_GREATER(sal_Int32(1500), nSize); // nSize > 1500 } CPPUNIT_TEST_FIXTURE(Test, testInvalidDatetimeInProps) diff --git a/sw/source/filter/ww8/docxattributeoutput.cxx b/sw/source/filter/ww8/docxattributeoutput.cxx index 9398166c8c8a..a695f51221c7 100644 --- a/sw/source/filter/ww8/docxattributeoutput.cxx +++ b/sw/source/filter/ww8/docxattributeoutput.cxx @@ -9280,6 +9280,8 @@ void DocxAttributeOutput::ParaTabStop( const SvxTabStopItem& rTabStop ) // may contain 64 entries at most, or else MS Word reports the file as corrupt sal_uInt32 nWrittenTabs = 0; +// do not output inherited tabs multiple times (inside styles and inside inline properties) +std::vector vInherited(nCount, false); // Get offset for tabs // In DOCX, w:pos specifies the position of the current custom tab stop with respect to the current page margins. @@ -9303,13 +9305,15 @@ void DocxAttributeOutput::ParaTabStop( const SvxTabStopItem& rTabStop ) FSNS( XML_w, XML_pos ), OString::number(pInheritedTabs->At(i).GetTabPos()) ); ++nWrittenTabs; } +else if (pInheritedTabs->At(i) == rTabStop[nCurrTab]) +vInherited[nCurrTab] = true; } for (sal_uInt16 i = 0; i < nCount; i++ ) { if( rTabStop[i].GetAdjustment() != SvxTabAdjust::Default ) { -if (nWrittenTabs < 64) +if (!vInherited[i] && nWrittenTabs < 64) { impl_WriteTabElement( m_pSerializer, rTabStop[i], tabsOffset ); ++nWrittenTabs;
core.git: Branch 'libreoffice-26-2' - sw/qa sw/source
sw/qa/extras/ooxmlexport/data/tdf170389_manyTabstops.odt |binary sw/qa/extras/ooxmlexport/ooxmlexport25.cxx | 12 sw/source/filter/ww8/docxattributeoutput.cxx | 15 ++- 3 files changed, 26 insertions(+), 1 deletion(-) New commits: commit 34a977af1ef7ae48604a879bf3a47b4d03b0c2c9 Author: [email protected] AuthorDate: Mon Jan 19 11:34:38 2026 -0500 Commit: Justin Luth CommitDate: Thu Jan 22 14:08:55 2026 +0100 tdf#170389 docx export: limit w:tabs to 64 entries Files with more than 64 tabstops are reported as corrupt by MS Word. Exposed by 6.2 commit 2bc84658cce1df5050fe788dd0c8a0906a1ca2c3 Author: Justin Luth on Wed Jul 18 07:37:41 2018 +0200 related tdf#63561 docx: styles inherit tabstops too Reviewed-on: https://gerrit.libreoffice.org/57278 Probably dedup needs to happen here, since AFAICS we accumulate the inherited tabs in each successive inherted style/paragraph. make CppunitTest_sw_ooxmlexport25 \ CPPUNIT_TEST_NAME=testTdf170389_manyTabstops Change-Id: I2fa356184e5322f9483678c9a50ae6ce96599920 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/197606 Tested-by: Jenkins Reviewed-by: Justin Luth (cherry picked from commit f8b72d104ad32171ab637fc83c6f942a930b69e1) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/197780 diff --git a/sw/qa/extras/ooxmlexport/data/tdf170389_manyTabstops.odt b/sw/qa/extras/ooxmlexport/data/tdf170389_manyTabstops.odt new file mode 100644 index ..a54dba2ccf8b Binary files /dev/null and b/sw/qa/extras/ooxmlexport/data/tdf170389_manyTabstops.odt differ diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport25.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport25.cxx index 8b47bd172393..f6feec1fa36a 100644 --- a/sw/qa/extras/ooxmlexport/ooxmlexport25.cxx +++ b/sw/qa/extras/ooxmlexport/ooxmlexport25.cxx @@ -147,6 +147,18 @@ DECLARE_OOXMLEXPORT_TEST(testTdf165478_bottomAligned, "tdf165478_bottomAligned.d CPPUNIT_ASSERT_EQUAL(sal_Int32(1887), nFlyTop); } +CPPUNIT_TEST_FIXTURE(Test, testTdf170389_manyTabstops) +{ +createSwDoc("tdf170389_manyTabstops.odt"); + +save(TestFilter::DOCX); + +xmlDocUniquePtr pXmlDoc = parseExport(u"word/document.xml"_ustr); +// MS Word reports document as corrupt if it has more than 64 tabstops defined +// The paragraph itself defines 40, and inherits 40. Without the fix, this was 80 +assertXPath(pXmlDoc, "//w:tabs/w:tab", 64); +} + CPPUNIT_TEST_FIXTURE(Test, testInvalidDatetimeInProps) { createSwDoc("invalidDatetimeInProps.fodt"); diff --git a/sw/source/filter/ww8/docxattributeoutput.cxx b/sw/source/filter/ww8/docxattributeoutput.cxx index 0a2e440eb2c4..9398166c8c8a 100644 --- a/sw/source/filter/ww8/docxattributeoutput.cxx +++ b/sw/source/filter/ww8/docxattributeoutput.cxx @@ -9278,6 +9278,9 @@ void DocxAttributeOutput::ParaTabStop( const SvxTabStopItem& rTabStop ) m_pSerializer->startElementNS(XML_w, XML_tabs); +// may contain 64 entries at most, or else MS Word reports the file as corrupt +sal_uInt32 nWrittenTabs = 0; + // Get offset for tabs // In DOCX, w:pos specifies the position of the current custom tab stop with respect to the current page margins. // But in ODT, zero position could be page margins or paragraph indent according to used settings. @@ -9287,6 +9290,9 @@ void DocxAttributeOutput::ParaTabStop( const SvxTabStopItem& rTabStop ) sal_Int32 nCurrTab = 0; for ( sal_uInt16 i = 0; i < nInheritedTabCount; ++i ) { +if (nWrittenTabs == 64) +break; // maximum allowed number of entries reached + while ( nCurrTab < nCount && rTabStop[nCurrTab] < pInheritedTabs->At(i) ) ++nCurrTab; @@ -9295,13 +9301,20 @@ void DocxAttributeOutput::ParaTabStop( const SvxTabStopItem& rTabStop ) m_pSerializer->singleElementNS( XML_w, XML_tab, FSNS( XML_w, XML_val ), "clear", FSNS( XML_w, XML_pos ), OString::number(pInheritedTabs->At(i).GetTabPos()) ); +++nWrittenTabs; } } for (sal_uInt16 i = 0; i < nCount; i++ ) { if( rTabStop[i].GetAdjustment() != SvxTabAdjust::Default ) -impl_WriteTabElement( m_pSerializer, rTabStop[i], tabsOffset ); +{ +if (nWrittenTabs < 64) +{ +impl_WriteTabElement( m_pSerializer, rTabStop[i], tabsOffset ); +++nWrittenTabs; +} +} else GetExport().setDefaultTabStop( rTabStop[i].GetTabPos()); }
core.git: Branch 'libreoffice-26-2' - sw/qa sw/source
sw/qa/extras/ooxmlexport/ooxmlexport12.cxx | 15 +--
sw/source/filter/ww8/docxattributeoutput.cxx |3 +++
2 files changed, 8 insertions(+), 10 deletions(-)
New commits:
commit d42929881d986ef34c023e5bce3348d2364f0a4b
Author: Noel Grandin
AuthorDate: Thu Jan 15 15:06:13 2026 +0200
Commit: Xisco Fauli
CommitDate: Tue Jan 20 11:27:37 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
Tested-by: Jenkins
(cherry picked from commit 232b4ff3d530080efe28b8212c44952a625f8bc9)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/197561
Reviewed-by: Xisco Fauli
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 c59580cb3714..0a2e440eb2c4 100644
--- a/sw/source/filter/ww8/docxattributeoutput.cxx
+++ b/sw/source/filter/ww8/docxattributeoutput.cxx
@@ -2261,6 +2261,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 );
core.git: Branch 'libreoffice-26-2' - sw/qa sw/source
sw/qa/extras/ooxmlexport/ooxmlexport11.cxx |3 ---
sw/source/filter/ww8/docxtableexport.cxx |7 +++
2 files changed, 7 insertions(+), 3 deletions(-)
New commits:
commit f0cdf8d8412fc5088ac5657bbcc671507476641a
Author: Noel Grandin
AuthorDate: Mon Jan 19 16:56:32 2026 +0200
Commit: Xisco Fauli
CommitDate: Tue Jan 20 11:05:49 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
(cherry picked from commit 6ebb696bebb04e2296a6dfcba769ec4bd3f8ed88)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/197636
Reviewed-by: Xisco Fauli
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(getParagraph(1), u"FillStyle"_ustr));
CPPUNIT_ASSERT_EQUAL(COL_LIGHTMAGENTA, getProperty(getParagraph(1),
u"ParaBackColor"_ustr));
CPPUNIT_ASSERT_EQUAL(drawing::FillStyle_NONE,
getProperty(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 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());
else if (name == "val")
+{
AddToAttrList(pAttrList, FSNS(XML_w, XML_val),
val.get());
+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);
}
}
core.git: Branch 'libreoffice-26-2' - sw/qa sw/source
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/layout6.cxx | 271 ++ 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 13 files changed, 382 insertions(+), 19 deletions(-) New commits: commit a5ee5df9ac0db0f34c653ff20cb2c7e554a99f48 Author: Mike Kaganski AuthorDate: Sun Jan 18 20:41:01 2026 +0500 Commit: Xisco Fauli CommitDate: Tue Jan 20 10:59:12 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 (cherry picked from commit fb203eb57a1ba0accf1894672d35ddf9430d8b05) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/197548 Reviewed-by: Xisco Fauli 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, t
core.git: Branch 'libreoffice-26-2' - sw/qa sw/source
sw/qa/extras/layout/layout4.cxx | 10 +- sw/source/filter/ww8/docxattributeoutput.cxx |3 +++ 2 files changed, 8 insertions(+), 5 deletions(-) New commits: commit 3c552ee36404717735c1b4fa68ee4e50516592d7 Author: [email protected] AuthorDate: Thu Jan 15 16:58:32 2026 -0500 Commit: Justin Luth CommitDate: Tue Jan 20 02:30:25 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 Tested-by: Jenkins Signed-off-by: Xisco Fauli 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 3423547728d3..a74fdf4a5b69 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); }
core.git: Branch 'libreoffice-26-2' - sw/qa sw/source
sw/qa/extras/layout/layout4.cxx |7 +++ sw/source/writerfilter/dmapper/DomainMapper.cxx |2 ++ 2 files changed, 9 insertions(+) New commits: commit f4f038fc9a7602e62065ee820f7a3e4fc78f290f Author: Justin Luth AuthorDate: Wed Jan 14 15:16:10 2026 -0500 Commit: Justin Luth CommitDate: Mon Jan 19 21:16:05 2026 +0100 tdf#170322 writerfilter: import fields into richText, not plainText MS Word complains that documents are corrupt if a 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 ... Change-Id: Ib393f8eaf024d332be3f3603f72ef644fb5fe8c7 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/197300 Tested-by: Jenkins Reviewed-by: Justin Luth Signed-off-by: Xisco Fauli 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); }
core.git: Branch 'libreoffice-26-2' - sw/qa sw/source
sw/qa/extras/ooxmlexport/ooxmlexport17.cxx |5 -
sw/source/filter/ww8/docxattributeoutput.cxx |5 +++--
2 files changed, 3 insertions(+), 7 deletions(-)
New commits:
commit 6fe1af9d2edd438a45bc54e13ba8a01f5dfcc648
Author: Noel Grandin
AuthorDate: Wed Jan 14 19:02:11 2026 +0200
Commit: Xisco Fauli
CommitDate: Mon Jan 19 10:08:11 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
Signed-off-by: Xisco Fauli
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(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 8f8a3c40b52c..1ed6e6d062c7 100644
--- a/sw/source/filter/ww8/docxattributeoutput.cxx
+++ b/sw/source/filter/ww8/docxattributeoutput.cxx
@@ -2716,8 +2716,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(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())
core.git: Branch 'libreoffice-26-2' - sw/qa sw/source
sw/qa/extras/ooxmlexport/ooxmlexport7.cxx |5 -
sw/source/filter/ww8/docxexport.cxx |5 +
sw/source/filter/ww8/docxexport.hxx |2 ++
sw/source/filter/ww8/rtfexport.hxx|2 ++
sw/source/filter/ww8/wrtw8sty.cxx |1 +
sw/source/filter/ww8/wrtww8.hxx |4
6 files changed, 14 insertions(+), 5 deletions(-)
New commits:
commit 71928bd740832dbc75b856252edf999afd678d70
Author: Noel Grandin
AuthorDate: Wed Jan 14 11:07:21 2026 +0200
Commit: Xisco Fauli
CommitDate: Thu Jan 15 08:54:56 2026 +0100
officeotron: fillcolor is not valid for framePr
because of some generic code adding attributes to the flyAttrList, we end
with:
...
inside word/header1.xml, which is not valid.
Unfortunately, the code structure here does not allow an elegant solution,
we need a new virtual method to be able to get at the flyAttrList and clear
it,
to prevent attributes leaking into elements they are not meant for.
Change-Id: I2bfd6b1afd78e722a29d79cf853a4678b7d0dea4
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/197248
Tested-by: Jenkins
Reviewed-by: Michael Stahl
(cherry picked from commit f748949b6ffe6e8ef2a7fd27c70669efe8ac3818)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/197297
Reviewed-by: Xisco Fauli
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport7.cxx
b/sw/qa/extras/ooxmlexport/ooxmlexport7.cxx
index 1e1057be6661..dd7e7b2e6ae8 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport7.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport7.cxx
@@ -667,9 +667,6 @@ DECLARE_OOXMLEXPORT_TEST(testTdf77219_backgroundShape,
"tdf77219_backgroundShape
DECLARE_OOXMLEXPORT_TEST(testTdf126533_axialAngle, "tdf126533_axialAngle.docx")
{
-//FIXME: validation error in OOXML export: Errors: 1
-skipValidation();
-
// axial gradient is purple foreground/lime background in the middle
(top-left to bottom-right)
uno::Reference
xPageStyle(getStyles(u"PageStyles"_ustr)->getByName(u"Standard"_ustr),
uno::UNO_QUERY);
@@ -685,8 +682,6 @@ DECLARE_OOXMLEXPORT_TEST(testTdf126533_axialAngle,
"tdf126533_axialAngle.docx")
DECLARE_OOXMLEXPORT_TEST(testTdf126533_axialAngle2,
"tdf126533_axialAngle2.docx")
{
-//FIXME: validation error in OOXML export: Errors: 1
-skipValidation();
// axial gradient is purple foreground/lime background in the middle
(top-right to bottom-left)
uno::Reference
xPageStyle(getStyles(u"PageStyles"_ustr)->getByName(u"Standard"_ustr),
uno::UNO_QUERY);
diff --git a/sw/source/filter/ww8/docxexport.cxx
b/sw/source/filter/ww8/docxexport.cxx
index 00d9a1005d8f..7a0b872ebcc1 100644
--- a/sw/source/filter/ww8/docxexport.cxx
+++ b/sw/source/filter/ww8/docxexport.cxx
@@ -740,6 +740,11 @@ void DocxExport::PrepareNewPageDesc( const SfxItemSet*
pSet,
}
+void DocxExport::ClearFlyAttrList()
+{
+SdrExporter().getFlyAttrList().clear();
+}
+
void DocxExport::InitStyles()
{
m_pStyles.reset(new MSWordStyles( *this, /*bListStyles =*/ true ));
diff --git a/sw/source/filter/ww8/docxexport.hxx
b/sw/source/filter/ww8/docxexport.hxx
index ac5700d77867..a51285b6dab7 100644
--- a/sw/source/filter/ww8/docxexport.hxx
+++ b/sw/source/filter/ww8/docxexport.hxx
@@ -240,6 +240,8 @@ protected:
const SwPageDesc* pNewPgDesc,
bool bExtraPageBreak = false) override;
+virtual void ClearFlyAttrList() override;
+
private:
/// Setup pStyles and write styles.xml
void InitStyles();
diff --git a/sw/source/filter/ww8/rtfexport.hxx
b/sw/source/filter/ww8/rtfexport.hxx
index 6b285dbab485..2187a789993a 100644
--- a/sw/source/filter/ww8/rtfexport.hxx
+++ b/sw/source/filter/ww8/rtfexport.hxx
@@ -147,6 +147,8 @@ protected:
void AppendSection(const SwPageDesc* pPageDesc, const SwSectionFormat*
pFormat,
sal_uLong nLnNum) override;
+void ClearFlyAttrList() override {}
+
public:
/// Pass the pDocument, pCurrentPam and pOriginalPam to the base class.
RtfExport(RtfExportFilter* pFilter, SwDoc& rDocument,
std::shared_ptr& pCurrentPam,
diff --git a/sw/source/filter/ww8/wrtw8sty.cxx
b/sw/source/filter/ww8/wrtw8sty.cxx
index aa25d5945962..2986f84f432f 100644
--- a/sw/source/filter/ww8/wrtw8sty.cxx
+++ b/sw/source/filter/ww8/wrtw8sty.cxx
@@ -1867,6 +1867,7 @@ void MSWordExportBase::SectionProperties( const
WW8_SepInfo& rSepInfo, WW8_PdAtt
}
AttrOutput().OutputStyleItemSet( pPdFormat->GetAttrSet(), false );
+ClearFlyAttrList(); // so they do not leak into other elements
if (titlePage)
{
diff --git a/sw/source/filter/ww8/wrtww8.hxx b/sw/source/filter/ww8/wrtww8.hxx
index 6aa9cacac1ea..5aa6c208235b 100644
--- a/sw/source/filter/ww8/wrtww8.hxx
+++
core.git: Branch 'libreoffice-26-2' - sw/qa sw/source
sw/qa/extras/ooxmlexport/ooxmlexport8.cxx |6 sw/qa/extras/ooxmlexport/ooxmlfieldexport.cxx | 12 + sw/source/writerfilter/dmapper/DomainMapperTableHandler.cxx | 15 +--- 3 files changed, 30 insertions(+), 3 deletions(-) New commits: commit 459530b73eb5650b2c5651a513a2d1e3541e689c Author: Justin Luth AuthorDate: Tue Jan 6 14:52:06 2026 -0500 Commit: Justin Luth CommitDate: Mon Jan 12 23:44:55 2026 +0100 tdf#170208 docx import: conjure up a tblInd if none is provided This has always been wrong for compat14, but some documents have accidentally 'looked right' at various times, most recently before 25.8.3 commit a80d7ba9c01c8c5c95bf01960d969b82dc7edffc Author: Aron Budea on Mon Sep 29 14:59:18 2025 +0930 tdf#168598 Fix for tdf#148578 should only apply to RTF Reviewed-on: https://gerrit.libreoffice.org/c/core/+/191587 This patch is based solely on experimentation. MS docuemntation gives no indication that it just makes up an 'indent from left' when none is provided. Even their errata documentation doesn't mention this. As noted in the bug report, this made-up value is different in Word 2024 than in Word 2010. Fortunately the 'modern' version is a simple calculation. make CppunitTest_sw_ooxmlexport8 CPPUNIT_TEST_NAME=testN780853 make CppunitTest_sw_ooxmlfieldexport CPPUNIT_TEST_NAME=testfdo78886 Change-Id: Iba59184b988a48fe4d7176e6d3b1000870c87dab Reviewed-on: https://gerrit.libreoffice.org/c/core/+/196625 Reviewed-by: Justin Luth Tested-by: Jenkins (cherry picked from commit d0fbf21288d27833a3d3c6b95252c68bcedd1d5c) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/197134 diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport8.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport8.cxx index 3cac5f867bd7..12fcb07b6388 100644 --- a/sw/qa/extras/ooxmlexport/ooxmlexport8.cxx +++ b/sw/qa/extras/ooxmlexport/ooxmlexport8.cxx @@ -529,6 +529,12 @@ DECLARE_OOXMLEXPORT_TEST(testN780853, "n780853.docx") //tdf#102619 - I would have expected this to be "Standard", but MSO 2013/2010/2003 all give FollowStyle==Date uno::Reference< beans::XPropertySet > properties(getStyles(u"ParagraphStyles"_ustr)->getByName(u"Date"_ustr), uno::UNO_QUERY); CPPUNIT_ASSERT_EQUAL(u"Date"_ustr, getProperty(properties, u"FollowStyle"_ustr)); + +// tdf#170208: compatibilityMode12 document - emulate table placement +// MS Word conjures up an 'indent from left' tblInd that cancels out the 'shift by cell margin'. +// Without the fix, it spilled into the left margin by the border spacing distance (-203/0.2cm) +uno::Reference xTable(xIndexAccess->getByIndex(0), uno::UNO_QUERY); +CPPUNIT_ASSERT_EQUAL(static_cast(0), getProperty(xTable, "LeftMargin")); } DECLARE_OOXMLEXPORT_TEST(testN780843, "n780843.docx") diff --git a/sw/qa/extras/ooxmlexport/ooxmlfieldexport.cxx b/sw/qa/extras/ooxmlexport/ooxmlfieldexport.cxx index a8f92ffdb5f5..4b65fe4b8fc8 100644 --- a/sw/qa/extras/ooxmlexport/ooxmlfieldexport.cxx +++ b/sw/qa/extras/ooxmlexport/ooxmlfieldexport.cxx @@ -459,6 +459,18 @@ CPPUNIT_TEST_FIXTURE(Test, testfdo78886) xmlDocUniquePtr pXmlDoc = parseExport(u"word/document.xml"_ustr); assertXPath(pXmlDoc, "/w:document[1]/w:body[1]/w:tbl[2]/w:tr[1]/w:tc[1]/w:p[1]/w:hyperlink[1]/w:r[2]/w:fldChar[1]", 0); + +// tdf#170208: compatibilityMode12 document - emulate table placement +// TableGrid style defines tblInd - which we adjust by the border spacing to emulate positioning +uno::Reference xTextTablesSupplier(mxComponent, uno::UNO_QUERY); +uno::Reference xIndexAccess(xTextTablesSupplier->getTextTables(), uno::UNO_QUERY); +uno::Reference xTable(xIndexAccess->getByIndex(0), uno::UNO_QUERY); +// The left margin (1619 / 1.62cm) is adjusted by the border spacing (203 / 0.2cm) +CPPUNIT_ASSERT_EQUAL(static_cast(1416), getProperty(xTable, "LeftMargin")); + +xTable.set(xIndexAccess->getByIndex(1), uno::UNO_QUERY); +// Without the fix, this was -191 (DEF_BORDER_DIST) +CPPUNIT_ASSERT_EQUAL(static_cast(-203), getProperty(xTable, "LeftMargin")); } CPPUNIT_TEST_FIXTURE(Test, testFdo78910) diff --git a/sw/source/writerfilter/dmapper/DomainMapperTableHandler.cxx b/sw/source/writerfilter/dmapper/DomainMapperTableHandler.cxx index 6c282aed8935..9d0df3dbf5f1 100644 --- a/sw/source/writerfilter/dmapper/DomainMapperTableHandler.cxx +++ b/sw/source/writerfilter/dmapper/DomainMapperTableHandler.cxx @@ -491,14 +491,16 @@ TableStyleSheetEntry * DomainMapperTableHandler::endTableGetTableStyle(TableInfo m_aTableProperties->Insert( PROP_TABLE_INTEROP_GRAB_BAG, uno::Any( aGrabBag.getAsConstPropertyValueList() ) ); } +bool bLeftMarginProvided = false; std::optional oLeftMarginFromStyle = m_aTableProperties->getProperty(PROP_LEFT_MARGIN); if (oLe
core.git: Branch 'libreoffice-26-2' - sw/qa sw/source
sw/qa/extras/ooxmlexport/ooxmlexport.cxx |3 -
sw/source/filter/ww8/docxtablestyleexport.cxx | 61 +-
2 files changed, 32 insertions(+), 32 deletions(-)
New commits:
commit 8529f19183c6e1605e4334da992fbf094c1fcacc
Author: Noel Grandin
AuthorDate: Sat Jan 10 17:47:36 2026 +0200
Commit: Xisco Fauli
CommitDate: Mon Jan 12 19:07:55 2026 +0100
officeotron: fix order of sub-elements of tblCellMar and tblBorders
Change-Id: I05b4cacb310cdb78f9683cdb0472de383432c6d1
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/196974
Reviewed-by: Michael Stahl
Tested-by: Jenkins
(cherry picked from commit a83869739c5ea8c3ac7d2f1515ffdfb874ccc597)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/197110
Reviewed-by: Xisco Fauli
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport.cxx
b/sw/qa/extras/ooxmlexport/ooxmlexport.cxx
index 4bc3eaa4d005..019ba34c476b 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport.cxx
@@ -814,9 +814,6 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf88583)
DECLARE_OOXMLEXPORT_TEST(testTdf97090, "tdf97090.docx")
{
-// FIXME: validation error in OOXML export: Errors: 39
-skipValidation();
-
uno::Reference xTablesSupplier(mxComponent,
uno::UNO_QUERY);
uno::Reference
xTables(xTablesSupplier->getTextTables(), uno::UNO_QUERY);
uno::Reference xTable(xTables->getByIndex(0),
uno::UNO_QUERY);
diff --git a/sw/source/filter/ww8/docxtablestyleexport.cxx
b/sw/source/filter/ww8/docxtablestyleexport.cxx
index 0e864b095015..b1dd48005fdd 100644
--- a/sw/source/filter/ww8/docxtablestyleexport.cxx
+++ b/sw/source/filter/ww8/docxtablestyleexport.cxx
@@ -154,27 +154,29 @@ void DocxTableStyleExport::TableStyles(sal_Int32
nCountStylesToWrite)
void DocxTableStyleExport::Impl::tableStyleTableCellMar(
const uno::Sequence& rTableCellMar, sal_Int32 nType)
{
-static DocxStringTokenMap const aTableCellMarTokens[]
-= { { "left", XML_left }, { "right", XML_right }, { "start", XML_start
},
-{ "end", XML_end }, { "top", XML_top }, { "bottom",
XML_bottom },
-{ nullptr, 0 } };
+// use table to output elements in correct order
+static std::pair constexpr aTableCellOrder[]{
+{ u"top"_ustr, XML_top }, { u"start"_ustr, XML_start }, {
u"left"_ustr, XML_left },
+{ u"bottom"_ustr, XML_bottom }, { u"end"_ustr, XML_end }, {
u"right"_ustr, XML_right }
+};
if (!rTableCellMar.hasElements())
return;
m_pSerializer->startElementNS(XML_w, nType);
-for (const auto& rProp : rTableCellMar)
+
+comphelper::SequenceAsHashMap aCellMap(rTableCellMar);
+for (const std::pair& rPair : aTableCellOrder)
{
-if (sal_Int32 nToken = DocxStringGetToken(aTableCellMarTokens,
rProp.Name))
-{
-comphelper::SequenceAsHashMap aMap(
-rProp.Value.get>());
-m_pSerializer->singleElementNS(XML_w, nToken, FSNS(XML_w, XML_w),
-
OString::number(aMap[u"w"_ustr].get()),
- FSNS(XML_w, XML_type),
- aMap[u"type"_ustr].get());
-}
+uno::Any aAny = aCellMap.getValue(rPair.first);
+if (!aAny.hasValue())
+continue;
+comphelper::SequenceAsHashMap
aMap(aAny.get>());
+m_pSerializer->singleElementNS(XML_w, rPair.second, FSNS(XML_w, XML_w),
+
OString::number(aMap[u"w"_ustr].get()),
+ FSNS(XML_w, XML_type),
aMap[u"type"_ustr].get());
}
+
m_pSerializer->endElementNS(XML_w, nType);
}
@@ -204,26 +206,27 @@ void DocxTableStyleExport::Impl::tableStyleTcBorder(
void DocxTableStyleExport::Impl::tableStyleTcBorders(
const uno::Sequence& rTcBorders, sal_Int32 nToken)
{
-static DocxStringTokenMap const aTcBordersTokens[] = { { "left", XML_left
},
- { "right",
XML_right },
- { "start",
XML_start },
- { "end", XML_end },
- { "top", XML_top },
- { "bottom",
XML_bottom },
- { "insideH",
XML_insideH },
- { "insideV",
XML_insideV },
- { "tl2br",
XML_tl2br },
- { "tr2bl",
XML_tr2bl },
- { nullptr, 0 } };
+// use table to output elements in correct order
+static std::pair constexpr aBordersOrder[]{
+{ u"top"_u
core.git: Branch 'libreoffice-26-2' - sw/qa sw/source
sw/qa/extras/ooxmlexport/ooxmlfieldexport.cxx |6 +
sw/source/writerfilter/dmapper/DomainMapperTableHandler.cxx | 13 +++-
2 files changed, 18 insertions(+), 1 deletion(-)
New commits:
commit 587c9ea7239d2c715975294d0b1d94b06550e74e
Author: Justin Luth
AuthorDate: Sat Jan 3 17:02:37 2026 -0500
Commit: Justin Luth
CommitDate: Mon Jan 12 18:21:59 2026 +0100
tdf#170208 docx import: shift table by cell margin, not style margin
This has always been wrong,
but some documents have accidentally 'looked right' at various times,
most recently before 25.8.3
commit a80d7ba9c01c8c5c95bf01960d969b82dc7edffc
Author: Aron Budea on Mon Sep 29 14:59:18 2025 +0930
tdf#168598 Fix for tdf#148578 should only apply to RTF
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/191587
make CppunitTest_sw_ooxmlfieldexport \
CPPUNIT_TEST_NAME=testTdf158661_blockSDT
Another nice example was nested-floating-table.docx
Change-Id: Ie4e256dc0651ade8d302bf78646b2a5215414bc6
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/196462
Tested-by: Jenkins
Reviewed-by: Justin Luth
(cherry picked from commit a63534ef77c620c06d41be9ad29c283fe636bf7f)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/196587
Tested-by: Jenkins CollaboraOffice
Reviewed-by: Miklos Vajna
(cherry picked from commit 53e53fa279c11ba28829164ca68a159e614368d3)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/197121
diff --git a/sw/qa/extras/ooxmlexport/ooxmlfieldexport.cxx
b/sw/qa/extras/ooxmlexport/ooxmlfieldexport.cxx
index 7ed052daf2c3..a8f92ffdb5f5 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlfieldexport.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlfieldexport.cxx
@@ -564,6 +564,12 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf158661_blockSDT)
xContentControlEnum = xContentControlEnumAccess->createEnumeration();
xTextPortionRange.set(xContentControlEnum->nextElement(), uno::UNO_QUERY);
CPPUNIT_ASSERT_EQUAL(u"Test"_ustr, xTextPortionRange->getString());
+
+// tdf#170208: compatibilityMode14 document - emulate table placement
+// TODO: the first table is also not correctly positioned
+
+// table2's cell margin (in the first row) is zero, so compat14 table
shifts by zero, not .19cm
+CPPUNIT_ASSERT_EQUAL(static_cast(0),
getProperty(xTable, "LeftMargin"));
}
CPPUNIT_TEST_FIXTURE(Test, testSdt2Run)
diff --git a/sw/source/writerfilter/dmapper/DomainMapperTableHandler.cxx
b/sw/source/writerfilter/dmapper/DomainMapperTableHandler.cxx
index b4e2174f52dc..6c282aed8935 100644
--- a/sw/source/writerfilter/dmapper/DomainMapperTableHandler.cxx
+++ b/sw/source/writerfilter/dmapper/DomainMapperTableHandler.cxx
@@ -603,7 +603,18 @@ TableStyleSheetEntry *
DomainMapperTableHandler::endTableGetTableStyle(TableInfo
if (m_rDMapper_Impl.IsOOXMLImport() && (nMode < 0 || (0 < nMode &&
nMode <= 14)) && rInfo.nNestLevel == 1)
{
-const sal_Int32 nAdjustedMargin = nLeftMargin -
rInfo.nLeftBorderDistance;
+const sal_Int32 nMinLeftBorderDistance = aLeftBorder.LineWidth / 2;
+sal_Int32 nLeftBorderDistance = rInfo.nLeftBorderDistance;
+if (!m_aCellProperties.empty() && !m_aCellProperties[0].empty())
+{
+// only the border spacing of the first row affects the
placement of the table
+std::optional aCellLeftBorderDistance
+=
m_aCellProperties[0][0]->getProperty(PROP_LEFT_BORDER_DISTANCE);
+if (aCellLeftBorderDistance)
+aCellLeftBorderDistance->second >>= nLeftBorderDistance;
+}
+nLeftBorderDistance = std::max(nMinLeftBorderDistance,
nLeftBorderDistance);
+const sal_Int32 nAdjustedMargin = nLeftMargin -
nLeftBorderDistance;
m_aTableProperties->Insert( PROP_LEFT_MARGIN, uno::Any(
nAdjustedMargin ) );
}
else
core.git: Branch 'libreoffice-26-2' - sw/qa sw/source
sw/qa/core/doc/DocumentRedlineManager.cxx | 45 ++
sw/qa/core/doc/data/fmt.docx |binary
sw/source/core/doc/DocumentRedlineManager.cxx |6 +++
3 files changed, 51 insertions(+)
New commits:
commit 12cad7b5add0c8371be6eec525675cf64383e75d
Author: Miklos Vajna
AuthorDate: Tue Jan 6 08:32:50 2026 +0100
Commit: Xisco Fauli
CommitDate: Mon Jan 12 09:36:58 2026 +0100
Related: tdf#168751 sw interdependent redlines, format on del: fix
del-on-fmt
Open the bugdoc which has a BBBCCCDDD redline. Select
CCC, press delete: now CCC is only covered by a delete redline instead
of a format-on-delete redline.
The trouble is in sw::DocumentRedlineManager::PreAppendDeleteRedline(),
where rCtx.pRedl (format redline) initially covers BBBCCCDDD, but then
it gets reduced to cover only BBB. We also create a pNew redline to
cover only DDD. This gives Writer a way to set rCtx.pNewRedl's range to
CCC without an overlap. The downside is that now reject-all doesn't
modify the format of CCC anymore, so some unexpected boldness remains in
the document.
Fix this similar to how
sw::DocumentRedlineManager::PreAppendForeignRedline()'s rCtx.eCmpPos ==
SwComparePosition::Inside case works: except there PushData() is used to
push the redline data "over" (before creating pNew), and here we use
PushData() to push the redline data "under".
This way UI creates a model where format is always on top of delete (and
not the other way around), which is useful, since DOCX only has markup
for that order and ODT explicitly says this order should be used in
files.
Change-Id: Ida640ad7bb99ed89faef306dac2b840ae3be53f3
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/196918
Reviewed-by: Miklos Vajna
Tested-by: Jenkins
(cherry picked from commit dcccb89dd37cc8fee40d70eec434688a24b7d629)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/196958
Reviewed-by: Xisco Fauli
diff --git a/sw/qa/core/doc/DocumentRedlineManager.cxx
b/sw/qa/core/doc/DocumentRedlineManager.cxx
index d2a51a18d897..11194129f49e 100644
--- a/sw/qa/core/doc/DocumentRedlineManager.cxx
+++ b/sw/qa/core/doc/DocumentRedlineManager.cxx
@@ -392,6 +392,51 @@ CPPUNIT_TEST_FIXTURE(Test, testDelThenFormatOwn)
CPPUNIT_ASSERT(!rRedlineData.Next());
}
}
+
+CPPUNIT_TEST_FIXTURE(Test, testFormatThenDel)
+{
+// Given an "AAA BBB CCC DDD EEE" document:
+createSwDoc("fmt.docx");
+SwDocShell* pDocShell = getSwDocShell();
+SwWrtShell* pWrtShell = pDocShell->GetWrtShell();
+pWrtShell->SttEndDoc(/*bStt=*/true);
+// Skip "AAA BBB ".
+pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/false, 8,
/*bBasicCall=*/false);
+// Select "CCC".
+pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/true, 3,
/*bBasicCall=*/false);
+
+// When deleting CCC:
+pWrtShell->DelLeft();
+
+// Then make sure the resulting new "delete" redline still tracks
formatting:
+SwDoc* pDoc = pDocShell->GetDoc();
+IDocumentRedlineAccess& rIDRA = pDoc->getIDocumentRedlineAccess();
+SwRedlineTable& rRedlines = rIDRA.GetRedlineTable();
+CPPUNIT_ASSERT_EQUAL(static_cast(3), rRedlines.size());
+{
+const SwRedlineData& rRedlineData = rRedlines[0]->GetRedlineData(0);
+CPPUNIT_ASSERT_EQUAL(RedlineType::Format, rRedlineData.GetType());
+CPPUNIT_ASSERT(!rRedlineData.Next());
+}
+{
+const SwRedlineData& rRedlineData = rRedlines[1]->GetRedlineData(0);
+// Without the accompanying fix in place, this test would have failed
with:
+// - Expected: 2 (Format)
+// - Actual : 1 (Delete)
+// i.e. the middle redline was just "delete", not "format-on-delete",
so formatting remained
+// in the document after reject-all.
+CPPUNIT_ASSERT_EQUAL(RedlineType::Format, rRedlineData.GetType());
+CPPUNIT_ASSERT(rRedlineData.Next());
+const SwRedlineData& rRedlineData2 = rRedlines[1]->GetRedlineData(1);
+CPPUNIT_ASSERT_EQUAL(RedlineType::Delete, rRedlineData2.GetType());
+CPPUNIT_ASSERT(!rRedlineData2.Next());
+}
+{
+const SwRedlineData& rRedlineData = rRedlines[2]->GetRedlineData(0);
+CPPUNIT_ASSERT_EQUAL(RedlineType::Format, rRedlineData.GetType());
+CPPUNIT_ASSERT(!rRedlineData.Next());
+}
+}
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/qa/core/doc/data/fmt.docx b/sw/qa/core/doc/data/fmt.docx
new file mode 100644
index ..29fa091007e2
Binary files /dev/null and b/sw/qa/core/doc/data/fmt.docx differ
diff --git a/sw/source/core/doc/DocumentRedlineManager.cxx
b/sw/source/core/doc/DocumentRedlineManager.cxx
index 1e11885d6e5a..ef0cf2f83ac9 100644
--- a/sw/source/core/doc/DocumentRedlineManager.cxx
+++ b/sw/source/core/doc/DocumentRedlineManager.cxx
@@ -2226,6 +2226,12 @@ void
DocumentRedlineMana
core.git: Branch 'libreoffice-26-2' - sw/qa sw/source
sw/qa/extras/ooxmlexport/ooxmlexport13.cxx | 20
sw/source/filter/ww8/docxattributeoutput.cxx |7 +--
2 files changed, 21 insertions(+), 6 deletions(-)
New commits:
commit 47df7f0b5c7509b084ee6916a9c98283e54332a2
Author: Aron Budea
AuthorDate: Mon Dec 1 00:27:05 2025 +1030
Commit: Xisco Fauli
CommitDate: Mon Jan 12 08:34:35 2026 +0100
tdf#142693 tdf#169419 sw: Limit page size exported to DOCX
Based on what Word can roundtrip, which is 64K twips,
even though that size (115 cm) can't be set via the UI.
Officially 2.1.220 Part 1 Section 17.6.13 of [MS-OI29500]
says 31680 twips, but let's keep to the value that works
in practice.
Don't adjust size on import, only on export, Writer can
handle much larger sizes.
446771fe3e91eb7d154e86b9c6a614374110e9ca was related.
Change-Id: I88540e80f4e94fd8fc3703c18d8b151a7bd270e6
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/194843
Tested-by: Jenkins
Reviewed-by: Aron Budea
(cherry picked from commit 714c2ee044b31f6974cbedc3f8cb3f80db475afc)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/197012
Reviewed-by: Xisco Fauli
diff --git a/sw/qa/extras/ooxmlexport/data/tdf142693_hugePaperSizeImport.docx
b/sw/qa/extras/ooxmlexport/data/tdf142693_hugePaperSize.docx
similarity index 100%
rename from sw/qa/extras/ooxmlexport/data/tdf142693_hugePaperSizeImport.docx
rename to sw/qa/extras/ooxmlexport/data/tdf142693_hugePaperSize.docx
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport13.cxx
b/sw/qa/extras/ooxmlexport/ooxmlexport13.cxx
index e88f5cd76fac..6b61b86c6bfa 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport13.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport13.cxx
@@ -1347,13 +1347,25 @@ DECLARE_OOXMLEXPORT_TEST(testTdf127741,
"tdf127741.docx")
CPPUNIT_ASSERT(visitedStyleName.equalsIgnoreAsciiCase("Visited Internet
Link"));
}
-CPPUNIT_TEST_FIXTURE(Test, testTdf142693_hugePaperSizeImport)
+CPPUNIT_TEST_FIXTURE(Test, testTdf142693_hugePaperSize)
{
-createSwDoc("tdf142693_hugePaperSizeImport.docx");
+createSwDoc("tdf142693_hugePaperSize.docx");
+
+// Verify that original page size is imported as is, since Writer can
handle
+// large page sizes
+uno::Reference xPageStyle(
+getStyles(u"PageStyles"_ustr)->getByName(u"Standard"_ustr),
uno::UNO_QUERY);
+CPPUNIT_ASSERT_EQUAL_MESSAGE("Page Width (mm) ", sal_Int32(1594),
+ getProperty(xPageStyle,
u"Width"_ustr) / 100);
+CPPUNIT_ASSERT_EQUAL_MESSAGE("Page Height (mm)", sal_Int32(1841),
+ getProperty(xPageStyle,
u"Height"_ustr) / 100);
+
save(TestFilter::DOCX);
xmlDocUniquePtr pXmlDoc = parseExport(u"word/document.xml"_ustr);
-assertXPath(pXmlDoc, "/w:document/w:body/w:sectPr/w:pgSz", "w", u"90369");
-assertXPath(pXmlDoc, "/w:document/w:body/w:sectPr/w:pgSz", "h", u"104372");
+// Verify that exported page size is limited to conform to max page
dimensions
+// Word can handle in practice, which is about 64k twips (as opposed to
31680 in [MS-OI29500])
+assertXPath(pXmlDoc, "/w:document/w:body/w:sectPr/w:pgSz", "w", u"65500");
+assertXPath(pXmlDoc, "/w:document/w:body/w:sectPr/w:pgSz", "h", u"65500");
}
CPPUNIT_TEST_FIXTURE(Test, testTdf127925)
diff --git a/sw/source/filter/ww8/docxattributeoutput.cxx
b/sw/source/filter/ww8/docxattributeoutput.cxx
index 2288f8b42310..3317e134824d 100644
--- a/sw/source/filter/ww8/docxattributeoutput.cxx
+++ b/sw/source/filter/ww8/docxattributeoutput.cxx
@@ -9412,8 +9412,11 @@ void DocxAttributeOutput::FormatFrameSize( const
SwFormatFrameSize& rSize )
if ( m_rExport.m_pCurrentPageDesc->GetLandscape( ) )
attrList->add( FSNS( XML_w, XML_orient ), "landscape" );
-attrList->add( FSNS( XML_w, XML_w ), OString::number( rSize.GetWidth(
) ) );
-attrList->add( FSNS( XML_w, XML_h ), OString::number( rSize.GetHeight(
) ) );
+// Max page dimensions in practice, (2.1.220 Part 1 Section 17.6.13 in
[MS-OI29500] says 31680,
+// but Word can handle about 64k twips
+const tools::Long nMaxSize = 65500;
+attrList->add( FSNS( XML_w, XML_w ), OString::number( std::min(
nMaxSize, rSize.GetWidth( ) ) ) );
+attrList->add( FSNS( XML_w, XML_h ), OString::number( std::min(
nMaxSize, rSize.GetHeight( ) ) ) );
m_pSerializer->singleElementNS( XML_w, XML_pgSz, attrList );
}
core.git: Branch 'libreoffice-26-2' - sw/qa sw/source
sw/qa/extras/ooxmlexport/ooxmlexport4.cxx|5 +
sw/source/filter/ww8/docxattributeoutput.cxx | 12
2 files changed, 5 insertions(+), 12 deletions(-)
New commits:
commit 759d287599185955a24f59edef37440bc76aa5cf
Author: Noel Grandin
AuthorDate: Wed Jan 7 19:33:42 2026 +0200
Commit: Xisco Fauli
CommitDate: Sat Jan 10 08:20:26 2026 +0100
officeotron: w:pPrChange is incomplete
the schema says w:pPr is required under w:pPrChange.
I dont understand the unit test I changed here, it was
added with:
commit c2c08b7d0e87f8cd1c97028c9363826f4bd22dcb
Author: Adam Co
Date: Mon Jan 13 13:55:05 2014 +0200
Add unit-test for 'track changes - paragraph properties changed'
preservation
But seems to be asserting that we __dont__ preserve w:pPr,
which makes no sense.
So fix that.
Change-Id: I536992bd3aec232f8dffda4f753c887148a877f2
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/196786
Tested-by: Jenkins
Reviewed-by: Michael Stahl
(cherry picked from commit a520104c86703c9a697166c1b8d703060435b59c)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/196943
Reviewed-by: Xisco Fauli
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport4.cxx
b/sw/qa/extras/ooxmlexport/ooxmlexport4.cxx
index c5aeb4d1342d..1be442124c76 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport4.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport4.cxx
@@ -190,12 +190,9 @@ CPPUNIT_TEST_FIXTURE(Test,
testTrackChangesParagraphProperties)
{
createSwDoc("testTrackChangesParagraphProperties.docx");
-//FIXME: validation error in OOXML export: Errors: 1
-skipValidation();
-
save(TestFilter::DOCX);
xmlDocUniquePtr pXmlDoc = parseExport(u"word/document.xml"_ustr);
-assertXPathChildren(pXmlDoc,
"/w:document/w:body/w:p[1]/w:pPr/w:pPrChange", 0);
+assertXPathChildren(pXmlDoc,
"/w:document/w:body/w:p[1]/w:pPr/w:pPrChange", 1);
}
CPPUNIT_TEST_FIXTURE(Test, testMsoSpt180)
diff --git a/sw/source/filter/ww8/docxattributeoutput.cxx
b/sw/source/filter/ww8/docxattributeoutput.cxx
index 511336cc4f86..2288f8b42310 100644
--- a/sw/source/filter/ww8/docxattributeoutput.cxx
+++ b/sw/source/filter/ww8/docxattributeoutput.cxx
@@ -4282,6 +4282,8 @@ void DocxAttributeOutput::Redline( const SwRedlineData*
pRedlineData)
if (!bNoDate)
pAttributeList->add(FSNS( XML_w, XML_date ), DateTimeToOString(
aDateTime ));
m_pSerializer->startElementNS( XML_w, XML_pPrChange, pAttributeList );
+m_pSerializer->startElementNS(XML_w, XML_pPr);
+m_pSerializer->mark(Tag_Redline_2,
comphelper::containerToSequence(aParagraphPropertiesOrder));
// Check if there is any extra data stored in the redline object
if (pRedlineData->GetExtraData())
@@ -4297,10 +4299,6 @@ void DocxAttributeOutput::Redline( const SwRedlineData*
pRedlineData)
const UIName & sParaStyleName =
pFormattingChanges->GetFormatName();
if (pChangesSet || !sParaStyleName.isEmpty())
{
-m_pSerializer->startElementNS(XML_w, XML_pPr);
-
-m_pSerializer->mark(Tag_Redline_2,
comphelper::containerToSequence(aParagraphPropertiesOrder));
-
if (!sParaStyleName.isEmpty())
{
OString sStyleName;
@@ -4336,13 +4334,11 @@ void DocxAttributeOutput::Redline( const SwRedlineData*
pRedlineData)
m_rExport.SdrExporter().getFlyAttrList() =
std::move(pFlyAttrList_Original);
m_pLRSpaceAttrList = std::move(pLRSpaceAttrList_Original);
m_pParagraphSpacingAttrList =
std::move(pParagraphSpacingAttrList_Original);
-
-m_pSerializer->mergeTopMarks(Tag_Redline_2);
-
-m_pSerializer->endElementNS( XML_w, XML_pPr );
}
}
}
+m_pSerializer->mergeTopMarks(Tag_Redline_2);
+m_pSerializer->endElementNS( XML_w, XML_pPr );
m_pSerializer->endElementNS( XML_w, XML_pPrChange );
break;
}
core.git: Branch 'libreoffice-26-2' - sw/qa sw/source
sw/qa/extras/uiwriter/data/tdf131124_Extruded_Shape_Selection.odt |binary
sw/qa/extras/uiwriter/data/tdf131124_Fontwork_Selection.odt |binary
sw/qa/extras/uiwriter/uiwriter11.cxx | 28
++
sw/source/uibase/uno/unotxvw.cxx |2
4 files changed, 30 insertions(+)
New commits:
commit 61d5485b76a323acf1c8c2d7eedb2637edea3be6
Author: Regina Henschel
AuthorDate: Sat Jan 3 16:19:51 2026 +0100
Commit: Xisco Fauli
CommitDate: Fri Jan 9 22:08:26 2026 +0100
tdf#131124 Handle ExtrudedCustomShape and FontWork
ShellMode::ExtrudedCustomShape and ShellMode::FontWork were missing in
the switch. Thus mxShapes and mxShape in the Export dialog were empty
and maSize was zero.
Change-Id: If4c2137ce85a0aef0613d9d79979cc9a58b5c115
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/196456
Tested-by: Jenkins
Reviewed-by: Regina Henschel
(cherry picked from commit a8babea4ce7f07f969e58594ab5a5afc2d190e69)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/196881
Reviewed-by: Xisco Fauli
diff --git a/sw/qa/extras/uiwriter/data/tdf131124_Extruded_Shape_Selection.odt
b/sw/qa/extras/uiwriter/data/tdf131124_Extruded_Shape_Selection.odt
new file mode 100644
index ..5d2d0ef7bd1a
Binary files /dev/null and
b/sw/qa/extras/uiwriter/data/tdf131124_Extruded_Shape_Selection.odt differ
diff --git a/sw/qa/extras/uiwriter/data/tdf131124_Fontwork_Selection.odt
b/sw/qa/extras/uiwriter/data/tdf131124_Fontwork_Selection.odt
new file mode 100644
index ..8b3090d1ae65
Binary files /dev/null and
b/sw/qa/extras/uiwriter/data/tdf131124_Fontwork_Selection.odt differ
diff --git a/sw/qa/extras/uiwriter/uiwriter11.cxx
b/sw/qa/extras/uiwriter/uiwriter11.cxx
index cc642e56d600..dfe2c60ffee5 100644
--- a/sw/qa/extras/uiwriter/uiwriter11.cxx
+++ b/sw/qa/extras/uiwriter/uiwriter11.cxx
@@ -397,6 +397,34 @@ CPPUNIT_TEST_FIXTURE(SwUiWriterTest11,
testTdf162120AutoRTL)
getProperty(getRun(getParagraph(1), 1),
u"WritingMode"_ustr));
}
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest11, testFontworkSelection)
+{
+// Load a document with a Fontwork shape and select the shape
+createSwDoc("tdf131124_Fontwork_Selection.odt");
+dispatchCommand(mxComponent, u".uno:JumpToNextFrame"_ustr, {});
+
+// Make sure the CurrentSelection of the document is not empty.
+uno::Reference xModel(mxComponent, uno::UNO_QUERY);
+uno::Reference
xSelections(xModel->getCurrentSelection(),
+uno::UNO_QUERY);
+// Without fix, this test would have failed here
+CPPUNIT_ASSERT(xSelections.is());
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest11, testExtrudedShapeSelection)
+{
+// Load a document with an extruded custom shape and select the shape
+createSwDoc("tdf131124_Extruded_Shape_Selection.odt");
+dispatchCommand(mxComponent, u".uno:JumpToNextFrame"_ustr, {});
+
+// Make sure the CurrentSelection of the document is not empty.
+uno::Reference xModel(mxComponent, uno::UNO_QUERY);
+uno::Reference
xSelections(xModel->getCurrentSelection(),
+uno::UNO_QUERY);
+// Without fix, this test would have failed here
+CPPUNIT_ASSERT(xSelections.is());
+}
+
} // end of anonymous namespace
CPPUNIT_PLUGIN_IMPLEMENT();
diff --git a/sw/source/uibase/uno/unotxvw.cxx b/sw/source/uibase/uno/unotxvw.cxx
index e8577548ac34..12e6f0c562dc 100644
--- a/sw/source/uibase/uno/unotxvw.cxx
+++ b/sw/source/uibase/uno/unotxvw.cxx
@@ -321,6 +321,8 @@ uno::Any SwXTextView::getSelection()
case ShellMode::DrawForm:
case ShellMode::DrawText:
case ShellMode::Bezier :
+case ShellMode::ExtrudedCustomShape:
+case ShellMode::FontWork:
{
uno::Reference< drawing::XShapes > xShCol =
drawing::ShapeCollection::create(
comphelper::getProcessComponentContext());
core.git: Branch 'libreoffice-26-2' - sw/qa sw/source
sw/qa/extras/uiwriter/uiwriter9.cxx |2 +- sw/source/core/crsr/crsrsh.cxx |1 - 2 files changed, 1 insertion(+), 2 deletions(-) New commits: commit 3c79df26bb00ac886e0ab0b66cc9420dc73ce5a0 Author: Justin Luth AuthorDate: Mon Jan 5 16:08:42 2026 -0500 Commit: Justin Luth CommitDate: Thu Jan 8 15:20:34 2026 +0100 tdf#166945 Revert "tdf#111969 sw: ... last half-character for context menu" This reverts 24.8 commit 242f6985556af7aac77b68c6dfea20d4b32c5f52. The problem is that the context menu simply cannot know which choice the user will choose. Some actions DEPEND on the cursor being 'closest to the mouse point' and some actions DEPEND on the cursor being 'inside the character run properties or field'. That 'mouse point at right click' is completely lost as soon as the mouse moves to chose something from the context menu, so it is not available to be re-used by SID_PASTE etc to re-position the cursor before executing the paste. The benefits of the now-being-reverted-patch do not dwarf the already-discovered-downsides, so I'm just reverting to extract myself from this situation. Change-Id: I03943cf899e1d18fb3e736d4cccea159abe7332e Reviewed-on: https://gerrit.libreoffice.org/c/core/+/196586 Reviewed-by: Justin Luth Tested-by: Jenkins (cherry picked from commit f656be88a35e1d4e51ddf131f1454acac4f03739) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/196685 diff --git a/sw/qa/extras/uiwriter/uiwriter9.cxx b/sw/qa/extras/uiwriter/uiwriter9.cxx index 79f707aff78e..b6ab384433b9 100644 --- a/sw/qa/extras/uiwriter/uiwriter9.cxx +++ b/sw/qa/extras/uiwriter/uiwriter9.cxx @@ -758,7 +758,7 @@ CPPUNIT_TEST_FIXTURE(SwUiWriterTest9, testTdf111969B) // the test: simulate a right-click of a mouse (at the end-edge of the second N-dash) // which sets the cursor and then acts on that pos. pWrtShell->SwCursorShell::SetCursor(aLogicL, false, /*Block=*/false, /*FieldInfo=*/true); -CPPUNIT_ASSERT(!pWrtShell->GetCurField(true)); +//CPPUNIT_ASSERT(!pWrtShell->GetCurField(true)); } CPPUNIT_TEST_FIXTURE(SwUiWriterTest9, testPDFExportCrash) diff --git a/sw/source/core/crsr/crsrsh.cxx b/sw/source/core/crsr/crsrsh.cxx index b84d6376fff1..e4ec668f68bc 100644 --- a/sw/source/core/crsr/crsrsh.cxx +++ b/sw/source/core/crsr/crsrsh.cxx @@ -1072,7 +1072,6 @@ int SwCursorShell::SetCursor(const Point& rLPt, bool bOnlyText, bool bBlock, bOnlyText ? CursorMoveState::SetOnlyText : CursorMoveState::NONE ); aTmpState.m_bSetInReadOnly = IsReadOnlyAvailable(); aTmpState.m_bFieldInfo = bFieldInfo; // always set cursor at field-start if point is over field -aTmpState.m_bPosMatchesBounds = bFieldInfo; // always set cursor at character-start if over char SwTextNode const*const pTextNd = sw::GetParaPropsNode(*GetLayout(), pCursor->GetPoint()->GetNode());
core.git: Branch 'libreoffice-26-2' - sw/qa sw/source
sw/qa/extras/ooxmlexport/ooxmlexport4.cxx| 15 ---
sw/source/filter/ww8/docxattributeoutput.cxx |8
2 files changed, 4 insertions(+), 19 deletions(-)
New commits:
commit 6fabd7435e407e0c9329ca83a703b49e2f4e
Author: Noel Grandin
AuthorDate: Tue Jan 6 20:03:52 2026 +0200
Commit: Xisco Fauli
CommitDate: Wed Jan 7 10:34:16 2026 +0100
officeotron: w:del in wrong order
under the w:rPr element, w:del needs to come before w:lang
Change-Id: I0bf6ddc554c44ef978b62565d5be664af406a693
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/196624
Tested-by: Jenkins
Reviewed-by: Noel Grandin
(cherry picked from commit 40a68f926079cbe3082cacdabffac4846f342139)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/196657
Reviewed-by: Xisco Fauli
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport4.cxx
b/sw/qa/extras/ooxmlexport/ooxmlexport4.cxx
index 00cefa910c2d..c5aeb4d1342d 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport4.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport4.cxx
@@ -106,9 +106,6 @@ CPPUNIT_TEST_FIXTURE(Test,
testTrackChangesDeletedParagraphMark)
{
createSwDoc("testTrackChangesDeletedParagraphMark.docx");
-//FIXME: validation error in OOXML export: Errors: 1
-skipValidation();
-
save(TestFilter::DOCX);
xmlDocUniquePtr pXmlDoc = parseExport(u"word/document.xml"_ustr);
assertXPath(pXmlDoc, "/w:document/w:body/w:p[1]/w:pPr/w:rPr/w:del");
@@ -118,9 +115,6 @@ CPPUNIT_TEST_FIXTURE(Test,
testTrackChangesInsertedParagraphMark)
{
createSwDoc("testTrackChangesInsertedParagraphMark.docx");
-//FIXME: validation error in OOXML export: Errors: 1
-skipValidation();
-
save(TestFilter::DOCX);
xmlDocUniquePtr pXmlDoc = parseExport(u"word/document.xml"_ustr);
assertXPath(pXmlDoc, "/w:document/w:body/w:p[1]/w:pPr/w:rPr/w:ins");
@@ -146,9 +140,6 @@ CPPUNIT_TEST_FIXTURE(Test, testTrackChangesDeletedTableCell)
{
createSwDoc("testTrackChangesDeletedTableCell.docx");
-//FIXME: validation error in OOXML export: Errors: 1
-skipValidation();
-
save(TestFilter::DOCX);
xmlDocUniquePtr pXmlDoc = parseExport(u"word/document.xml"_ustr);
assertXPath(pXmlDoc,
"/w:document/w:body/w:tbl/w:tr[3]/w:tc/w:tcPr/w:cellDel");
@@ -158,9 +149,6 @@ CPPUNIT_TEST_FIXTURE(Test,
testTrackChangesInsertedTableCell)
{
createSwDoc("testTrackChangesInsertedTableCell.docx");
-//FIXME: validation error in OOXML export: Errors: 1
-skipValidation();
-
save(TestFilter::DOCX);
xmlDocUniquePtr pXmlDoc = parseExport(u"word/document.xml"_ustr);
assertXPath(pXmlDoc,
"/w:document/w:body/w:tbl/w:tr[3]/w:tc/w:tcPr/w:cellIns");
@@ -185,9 +173,6 @@ CPPUNIT_TEST_FIXTURE(Test, testFDO73034)
{
createSwDoc("FDO73034.docx");
-//FIXME: validation error in OOXML export: Errors: 1
-skipValidation();
-
save(TestFilter::DOCX);
xmlDocUniquePtr pXmlDoc = parseExport(u"word/document.xml"_ustr);
CPPUNIT_ASSERT(getXPath(pXmlDoc,
"/w:document/w:body/w:p[1]/w:pPr/w:rPr/w:u", "val").match("single"));
diff --git a/sw/source/filter/ww8/docxattributeoutput.cxx
b/sw/source/filter/ww8/docxattributeoutput.cxx
index 33f009f9a60d..511336cc4f86 100644
--- a/sw/source/filter/ww8/docxattributeoutput.cxx
+++ b/sw/source/filter/ww8/docxattributeoutput.cxx
@@ -3366,6 +3366,10 @@ void DocxAttributeOutput::InitCollectedRunProperties()
// Write the elements in the spec order
static const sal_Int32 aOrder[] =
{
+FSNS( XML_w, XML_del ),
+FSNS( XML_w, XML_ins ),
+FSNS( XML_w, XML_moveFrom ),
+FSNS( XML_w, XML_moveTo ),
FSNS( XML_w, XML_rStyle ),
FSNS( XML_w, XML_rFonts ),
FSNS( XML_w, XML_b ),
@@ -3406,10 +3410,6 @@ void DocxAttributeOutput::InitCollectedRunProperties()
FSNS( XML_w, XML_specVanish ),
FSNS( XML_w, XML_oMath ),
FSNS( XML_w, XML_rPrChange ),
-FSNS( XML_w, XML_del ),
-FSNS( XML_w, XML_ins ),
-FSNS( XML_w, XML_moveFrom ),
-FSNS( XML_w, XML_moveTo ),
FSNS( XML_w14, XML_glow ),
FSNS( XML_w14, XML_shadow ),
FSNS( XML_w14, XML_reflection ),
core.git: Branch 'libreoffice-26-2' - sw/qa sw/source
sw/qa/extras/ooxmlexport/data/tdf170119_bottomSpacing.docx |binary
sw/qa/extras/ooxmlexport/ooxmlexport25.cxx |8 +
sw/source/writerfilter/dmapper/PropertyMap.cxx | 19 +
3 files changed, 27 insertions(+)
New commits:
commit ed5aaa03906807c93fe6847f66f84700174d
Author: Justin Luth
AuthorDate: Wed Dec 24 17:13:44 2025 -0500
Commit: Justin Luth
CommitDate: Sun Dec 28 01:55:16 2025 +0100
tdf#170119 writerfilter: cont-sectPr w/ pgbrk == emulate bottomSpacing
Technically this is a 25.2.6 regression from my
commit 74c29345a7c179b048c157582a1145e381616e5c
tdf#167657 writerfilter: only move sectPr bottomMargin after pageBreak
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/188298
It was just lucky before that it worked for that particular instance,
and it only worked since 25.2.4...
make CppunitTest_sw_ooxmlexport25 \
CPPUNIT_TEST_NAME=testTdf170119_bottomSpacing
Change-Id: I9d0ae90087d79887489357eaa00eeabe53bcb58c
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/196205
Tested-by: Jenkins
Reviewed-by: Justin Luth
(cherry picked from commit 582c032aaa4524abf5b5a630400cf7ecdc2acf8a)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/196247
diff --git a/sw/qa/extras/ooxmlexport/data/tdf170119_bottomSpacing.docx
b/sw/qa/extras/ooxmlexport/data/tdf170119_bottomSpacing.docx
new file mode 100644
index ..74e9de4f5272
Binary files /dev/null and
b/sw/qa/extras/ooxmlexport/data/tdf170119_bottomSpacing.docx differ
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport25.cxx
b/sw/qa/extras/ooxmlexport/ooxmlexport25.cxx
index 1dfc47a7c51c..94a07d25bd91 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport25.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport25.cxx
@@ -104,6 +104,14 @@ DECLARE_OOXMLEXPORT_TEST(testTdf170003_bottomSpacing,
"tdf170003_bottomSpacing.d
getProperty(getParagraph(1),
u"ParaBottomMargin"_ustr));
}
+DECLARE_OOXMLEXPORT_TEST(testTdf170119_bottomSpacing,
"tdf170119_bottomSpacing.docx")
+{
+// Given a document with a page break and a sectPr with a huge bottom
spacing
+
+// Without the fix, page 2 started with a 150pt gap, pushing content to
the third page.
+CPPUNIT_ASSERT_EQUAL(2, getPages());
+}
+
DECLARE_OOXMLEXPORT_TEST(testTdf167657_sectPr_bottomSpacing,
"tdf167657_sectPr_bottomSpacing.docx")
{
// given with a continuous break sectPr with no belowSpacing
diff --git a/sw/source/writerfilter/dmapper/PropertyMap.cxx
b/sw/source/writerfilter/dmapper/PropertyMap.cxx
index 6d7c2d4768fa..9125c36c69ce 100644
--- a/sw/source/writerfilter/dmapper/PropertyMap.cxx
+++ b/sw/source/writerfilter/dmapper/PropertyMap.cxx
@@ -1560,6 +1560,22 @@ void
SectionPropertyMap::EmulateSectPrBelowSpacing(DomainMapper_Impl& rDM_Impl)
// to the last paragraph in that section.
// This emulation works because below spacing before a page break normally
has no relevance.
+if (m_nBreakType == NS_ooxml::LN_Value_ST_SectionMark_continuous)
+{
+// The complication with continuous breaks is that the below spacing
of a sectPr
+// does NOT directly affect the layout.
+// [So (except before a page break) it MUST NOT be applied to the
previous para.]
+// However, it IS (indirectly) used to reduce the top margin of the
following paragraph.
+uno::Reference const xPSet(m_xStartingRange,
uno::UNO_QUERY);
+if (!xPSet)
+return; // TODO tdf#170119: emulation not possible without a page
break
+
+style::BreakType eBreakType(style::BreakType_NONE);
+xPSet->getPropertyValue(u"BreakType"_ustr) >>= eBreakType;
+if (eBreakType != style::BreakType_PAGE_BEFORE)
+return; // emulation not possible without a page break.
+}
+
// m_xPreStartingRange may have skipped over a table as the last thing
before the break!
// If so, then the below spacing can be ignored since tables don't have
below spacing.
// Also, if m_xStartingRange starts with a table (which also doesn't have
above spacing)
@@ -1690,6 +1706,9 @@ void SectionPropertyMap::CloseSectionGroup(
DomainMapper_Impl& rDM_Impl )
setHeaderFooterProperties(rDM_Impl);
InheritOrFinalizePageStyles( rDM_Impl );
ApplySectionProperties( xSection, rDM_Impl ); //depends on
InheritOrFinalizePageStyles
+
+EmulateSectPrBelowSpacing(rDM_Impl);
+
uno::Reference< beans::XPropertySet > xRangeProperties(
lcl_GetRangeProperties( m_bIsFirstSection, rDM_Impl, m_xStartingRange ) );
if ( m_bIsFirstSection && !m_sPageStyleName.isEmpty() &&
xRangeProperties.is() )
{
core.git: Branch 'libreoffice-26-2' - sw/qa sw/source
sw/qa/extras/ooxmlexport/data/tdf170003_bottomSpacing.docx |binary
sw/qa/extras/ooxmlexport/ooxmlexport19.cxx |5
sw/qa/extras/ooxmlexport/ooxmlexport25.cxx |9 +
sw/source/writerfilter/dmapper/PropertyMap.cxx | 71 +
sw/source/writerfilter/dmapper/PropertyMap.hxx |2
5 files changed, 67 insertions(+), 20 deletions(-)
New commits:
commit e983fc5918d399226b7aba17d0488e9720453571
Author: Justin Luth
AuthorDate: Mon Dec 22 20:22:11 2025 -0500
Commit: Justin Luth
CommitDate: Sat Dec 27 16:24:02 2025 +0100
tdf#170003 writerfilter: no belowSpacing emulation with tables
This patch fixes my 25.2.6
commit 1326e09d019f05a82265f15c26288b4ffb7dc0c2
tdf#167657 writerfilter: only move sectPr bottomMargin after pageBreak
It turns out that m_xPreStartingRange is a bit of a disaster.
First of all, it seems to be set even if gotoPreviousParagraph fails.
That can't be a good thing.
Secondly (and the problem for me), it jumps over tables,
so if the page break happens after a table
then I was applying the below spacing to an earlier paragraph.
make CppunitTest_sw_ooxmlexport25 \
CPPUNIT_TEST_NAME=testTdf170003_bottomSpacing
Change-Id: I883dd397413001fafb000e1e90c0e6738b3b4c87
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/196143
Reviewed-by: Justin Luth
Tested-by: Jenkins
(cherry picked from commit 82936569e0e17705b6de501d7151549c51520fb9)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/196246
diff --git a/sw/qa/extras/ooxmlexport/data/tdf170003_bottomSpacing.docx
b/sw/qa/extras/ooxmlexport/data/tdf170003_bottomSpacing.docx
new file mode 100644
index ..9f48b950122b
Binary files /dev/null and
b/sw/qa/extras/ooxmlexport/data/tdf170003_bottomSpacing.docx differ
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport19.cxx
b/sw/qa/extras/ooxmlexport/ooxmlexport19.cxx
index 5dd58fd57536..523169f604ee 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport19.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport19.cxx
@@ -1097,6 +1097,11 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf155945)
// - Actual : 423
CPPUNIT_ASSERT_EQUAL(sal_Int32(0),
getProperty(getParagraph(2),
u"ParaBottomMargin"_ustr));
+
+//tdf#170003: sectPr emulation: move sectPr bottom margin to the paragraph
before the page break
+// Without a fix in place, this was 0.
+CPPUNIT_ASSERT_EQUAL(sal_Int32(423),
+ getProperty(getParagraph(1),
u"ParaBottomMargin"_ustr));
}
CPPUNIT_TEST_FIXTURE(Test, testTdf133560)
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport25.cxx
b/sw/qa/extras/ooxmlexport/ooxmlexport25.cxx
index 714329a85aa6..1dfc47a7c51c 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport25.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport25.cxx
@@ -95,6 +95,15 @@ DECLARE_OOXMLEXPORT_TEST(testTdf169986_bottomSpacing,
"tdf169986_bottomSpacing.d
CPPUNIT_ASSERT_EQUAL(1, getPages());
}
+DECLARE_OOXMLEXPORT_TEST(testTdf170003_bottomSpacing,
"tdf170003_bottomSpacing.docx")
+{
+// Given a document with a table before the page break, and a sectPr with
a huge bottom spacing
+
+// This must be 200 twips / 0.35cm / 353 mm100, not sectPr's 2000 twips /
3.53 cm / 3530 mm100
+CPPUNIT_ASSERT_EQUAL(sal_Int32(353),
+ getProperty(getParagraph(1),
u"ParaBottomMargin"_ustr));
+}
+
DECLARE_OOXMLEXPORT_TEST(testTdf167657_sectPr_bottomSpacing,
"tdf167657_sectPr_bottomSpacing.docx")
{
// given with a continuous break sectPr with no belowSpacing
diff --git a/sw/source/writerfilter/dmapper/PropertyMap.cxx
b/sw/source/writerfilter/dmapper/PropertyMap.cxx
index d04774fbab64..6d7c2d4768fa 100644
--- a/sw/source/writerfilter/dmapper/PropertyMap.cxx
+++ b/sw/source/writerfilter/dmapper/PropertyMap.cxx
@@ -1542,6 +1542,54 @@ void
SectionPropertyMap::CreateEvenOddPageStyleCopy(DomainMapper_Impl& rDM_Impl,
m_sPageStyleName = evenOddStyleName; // And use it instead of the original
one (which is set as follow of this one).
}
+void SectionPropertyMap::EmulateSectPrBelowSpacing(DomainMapper_Impl& rDM_Impl)
+{
+if (!m_xStartingRange || !m_xPreStartingRange ||
rDM_Impl.IsInFootOrEndnote())
+return;
+
+SectionPropertyMap* pPrevSection = rDM_Impl.GetLastSectionContext();
+if (!pPrevSection || !pPrevSection->GetBelowSpacing().has_value())
+return; // no consolidated spacing to emulate
+
+// MS Word is excessively consistent about consolidating paragraph top and
bottom spacing.
+// They even consolidate spacing between section breaks!
+// The complication here is that the sectPr paragraph (which Writer needs
to discard)
+// defines the bottom spacing that exists at the end of the section.
+
+// Emulation: apply the previous section's belowSpacing
+// to the last paragraph in that section.
+// This emulation works b
core.git: Branch 'libreoffice-26-2' - sw/qa sw/source
sw/qa/extras/layout/data/footnote-in-2-column-section.fodt | 25 +
sw/qa/extras/layout/layout6.cxx| 15 +++
sw/source/core/layout/ftnfrm.cxx |3 +
3 files changed, 42 insertions(+), 1 deletion(-)
New commits:
commit 185f6d85784e9f3ccd5fa8566b65d006166b238b
Author: Mike Kaganski
AuthorDate: Sat Dec 20 11:48:55 2025 +0500
Commit: Xisco Fauli
CommitDate: Mon Dec 22 13:03:58 2025 +0100
tdf#155306: find the correct footnote boss frame
SwFrame::FindFootnoteBossFrame may return different results depending
on the note type (footnoote / endnote). Unfortunately, there is a mess
in the code, where in most places, the method is called with default
argument value. This change fixes one such place.
Change-Id: I66f18b88f4ceeb7b9626a66d6459ef376c03eb07
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/195948
Tested-by: Jenkins
Reviewed-by: Mike Kaganski
(cherry picked from commit 4ec14ca63fc02240c6df862dd78616c140606a24)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/196072
Reviewed-by: Xisco Fauli
diff --git a/sw/qa/extras/layout/data/footnote-in-2-column-section.fodt
b/sw/qa/extras/layout/data/footnote-in-2-column-section.fodt
new file mode 100644
index ..1bdd8bd71ad1
--- /dev/null
+++ b/sw/qa/extras/layout/data/footnote-in-2-column-section.fodt
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ foo1
+ First
+
+bar2
+ Second
+
+
+
+
\ No newline at end of file
diff --git a/sw/qa/extras/layout/layout6.cxx b/sw/qa/extras/layout/layout6.cxx
index 847da858f3ee..59571bcdf03f 100644
--- a/sw/qa/extras/layout/layout6.cxx
+++ b/sw/qa/extras/layout/layout6.cxx
@@ -1756,6 +1756,21 @@ CPPUNIT_TEST_FIXTURE(SwLayoutWriter6, testTdf169399)
assertXPath(pXmlDoc, "/root/page", 1);
}
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter6, testTdf155306)
+{
+// Given a document defining footnotes counted per page, with a 2-column
section containing
+// a footnote:
+createSwDoc("footnote-in-2-column-section.fodt");
+
+xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+// The footnote must have the correct number. Without the fix, the
following tests both failed,
+// because the number was 0:
+assertXPath(pXmlDoc,
"//body/section/column[1]//SwFieldPortion[@type='PortionType::Footnote']",
+"expand", u"2");
+assertXPath(pXmlDoc,
"//ftncont/ftn[2]//SwFieldPortion[@type='PortionType::FootnoteNum']",
+"expand", u"2");
+}
+
} // end of anonymous namespace
CPPUNIT_PLUGIN_IMPLEMENT();
diff --git a/sw/source/core/layout/ftnfrm.cxx b/sw/source/core/layout/ftnfrm.cxx
index 308f9deddb08..613237bc0c2c 100644
--- a/sw/source/core/layout/ftnfrm.cxx
+++ b/sw/source/core/layout/ftnfrm.cxx
@@ -1277,7 +1277,8 @@ const SwFootnoteFrame
*SwFootnoteBossFrame::FindFirstFootnote( SwContentFrame co
}
if ( pRet )
{
-const SwFootnoteBossFrame* pBoss =
pRet->GetRef()->FindFootnoteBossFrame();
+const SwFootnoteBossFrame* pBoss =
pRet->GetRef()->FindFootnoteBossFrame(
+!pRet->GetAttr()->GetFootnote().IsEndNote());
if( !pBoss || pBoss->GetPhyPageNum() != nPageNum ||
nColNum != lcl_ColumnNum( pBoss ) )
pRet = nullptr;
core.git: Branch 'libreoffice-26-2' - sw/qa sw/source
sw/qa/uitest/data/reject-all-overlap.docx |binary
sw/qa/uitest/writer_tests/trackedChanges.py | 17 +
sw/source/uibase/misc/redlndlg.cxx | 22 ++
3 files changed, 19 insertions(+), 20 deletions(-)
New commits:
commit 89e7434732fbb74aaeef594f603b922d76b9b55d
Author: Miklos Vajna
AuthorDate: Tue Dec 16 08:34:26 2025 +0100
Commit: Xisco Fauli
CommitDate: Wed Dec 17 15:03:01 2025 +0100
tdf#168737 sw interdependent redlines, reject all: fix ignored format
redlines
The bugdoc has two redlines: a format and a delete redline. Pressing
"reject all" in the manage changes dialog doesn't reject the format
redline.
Seems this happens because of code added in commit
9c4eef7d809ad7d283860c7b47b0f561aa240906 (tdf#52391 reject/clear
formatting of format-only changes, 2019-01-29), which argued that
rejection of format redlines isn't really implemented, so better if a
"mass reject all" doesn't reject them, unless only format redlines are
found in the document. But in the meantime I worked on improving
support for rejecting format redlines, most recently commit
47cb60bc534104852a219426e3beb4c714b6cf07 (tdf#167761 sw format redline,
char props: implement ODF import, 2025-08-18) added ODF import/export of
these old direct character properties, so it now makes less sense to
filter out format redlines while rejecting all of them. Also, the
matching toolbar button didn't ignore such format redlines.
Fix the problem by simplifying SwRedlineAcceptDlg::CallAcceptReject():
unless the redline data for the iterator says this item is disabled,
always add it to the list of redlines, similar to what the "reject the
selected redline(s)" case did already.
Note that the original 2019 commit was less confusing, as it did change
the label of the reject all button, but that was removed in commit
2e1a38ceb6866248ec30f6fe58cd3adc1b910eec (tdf#146893 Rework Manage
Changes dialog, 2022-01-24).
Change-Id: I69e7247d068cea2a25cd46ad2d5b41fccfa54919
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/195772
Reviewed-by: Miklos Vajna
Tested-by: Jenkins
(cherry picked from commit 8e513d30d1bd4c69e2e155e72f61897fc830adbb)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/195783
Reviewed-by: Xisco Fauli
diff --git a/sw/qa/uitest/data/reject-all-overlap.docx
b/sw/qa/uitest/data/reject-all-overlap.docx
new file mode 100644
index ..6263df4e0f6f
Binary files /dev/null and b/sw/qa/uitest/data/reject-all-overlap.docx differ
diff --git a/sw/qa/uitest/writer_tests/trackedChanges.py
b/sw/qa/uitest/writer_tests/trackedChanges.py
index 967d2d401499..b2ce09d8e1b9 100644
--- a/sw/qa/uitest/writer_tests/trackedChanges.py
+++ b/sw/qa/uitest/writer_tests/trackedChanges.py
@@ -625,6 +625,23 @@ class trackedchanges(UITestCase):
# i.e. a change was not accepted.
self.assertEqual(document.Redlines.Count, 0)
+def test_reject_all_deletes_all_redlines(self):
+# Given a document with overlapping changes:
+with
self.ui_test.load_file(get_url_for_data_file("reject-all-overlap.docx")) as
document:
+xWriterDoc = self.xUITest.getTopFocusWindow()
+xWriterEdit = xWriterDoc.getChild("writer_edit")
+
+# When rejecting all changes using the manage changes dialog:
+with
self.ui_test.execute_modeless_dialog_through_command(".uno:AcceptTrackedChanges",
close_button="close") as xTrackDlg:
+xAccBtn = xTrackDlg.getChild("rejectall")
+xAccBtn.executeAction("CLICK", tuple())
+
+# Then make sure all changes are rejected:
+# Without the accompanying fix in place, this failed with:
+# AssertionError: 1 != 0
+# i.e. a change was not rejected.
+self.assertEqual(document.Redlines.Count, 0)
+
def test_tdf155847_multiple_tracked_columns_crash(self):
with
self.ui_test.load_file(get_url_for_data_file("TC-table-del-add.docx")) as
document:
diff --git a/sw/source/uibase/misc/redlndlg.cxx
b/sw/source/uibase/misc/redlndlg.cxx
index afdad416adec..3f7fd3a5cde8 100644
--- a/sw/source/uibase/misc/redlndlg.cxx
+++ b/sw/source/uibase/misc/redlndlg.cxx
@@ -1109,7 +1109,7 @@ void SwRedlineAcceptDlg::CallAcceptReject( bool bSelect,
bool bAccept )
weld::TreeView& rTreeView = m_pTable->GetWidget();
-auto lambda = [this, pSh, bSelect, bAccept, &rTreeView, &nPos,
&aRedlines](weld::TreeIter& rEntry) {
+auto lambda = [bSelect, &rTreeView, &nPos, &aRedlines](weld::TreeIter&
rEntry) {
if (!rTreeView.get_iter_depth(rEntry))
{
if (bSelect && nPos == -1)
@@ -1117,25 +1117,7 @@ void SwRedlineAcceptDlg::CallAcceptReject( bool bSelect,
bool bAccept )
RedlinData *pData =
weld::fromId(rTreeView.get_id(rEntry));
-boo
core.git: Branch 'libreoffice-26-2' - sw/qa sw/source
sw/qa/extras/ooxmlexport/ooxmlexport5.cxx|3
sw/source/filter/ww8/docxattributeoutput.cxx | 92 +--
2 files changed, 46 insertions(+), 49 deletions(-)
New commits:
commit a9c6b1ab8c4ce1e3cc685cde8de2d0b33d641b57
Author: Noel Grandin
AuthorDate: Wed Dec 10 18:16:30 2025 +0200
Commit: Xisco Fauli
CommitDate: Tue Dec 16 09:57:13 2025 +0100
officeotron: elements under w:pPr element in wrong order
re-use existing logic to make this output in spec-compliant order
Change-Id: I5de88629295ee3eac1852fe67eedea4f1be7c087
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/195394
Tested-by: Jenkins
Reviewed-by: Michael Stahl
(cherry picked from commit 2b6e7bf5c0e9166fb10082267136ed6b899c7582)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/195561
Reviewed-by: Xisco Fauli
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport5.cxx
b/sw/qa/extras/ooxmlexport/ooxmlexport5.cxx
index 368f69ce6b99..88442c16e091 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport5.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport5.cxx
@@ -692,9 +692,6 @@ CPPUNIT_TEST_FIXTURE(Test, testfdo79668)
{
createSwDoc("fdo79668.docx");
-//FIXME: validation error in OOXML export: Errors: 12
-skipValidation();
-
saveAndReload(TestFilter::DOCX);
// fdo#79668: Document was Crashing on DebugUtil build while Saving
// because of repeated attribute value in same element.
diff --git a/sw/source/filter/ww8/docxattributeoutput.cxx
b/sw/source/filter/ww8/docxattributeoutput.cxx
index 3fd2af4b63ad..33f009f9a60d 100644
--- a/sw/source/filter/ww8/docxattributeoutput.cxx
+++ b/sw/source/filter/ww8/docxattributeoutput.cxx
@@ -1476,55 +1476,55 @@ void DocxAttributeOutput::StartParagraphProperties()
}
}
+// Write the elements in the spec order
+const sal_Int32 aParagraphPropertiesOrder[] =
+{
+FSNS( XML_w, XML_pStyle ),
+FSNS( XML_w, XML_keepNext ),
+FSNS( XML_w, XML_keepLines ),
+FSNS( XML_w, XML_pageBreakBefore ),
+FSNS( XML_w, XML_framePr ),
+FSNS( XML_w, XML_widowControl ),
+FSNS( XML_w, XML_numPr ),
+FSNS( XML_w, XML_suppressLineNumbers ),
+FSNS( XML_w, XML_pBdr ),
+FSNS( XML_w, XML_shd ),
+FSNS( XML_w, XML_tabs ),
+FSNS( XML_w, XML_suppressAutoHyphens ),
+FSNS( XML_w, XML_kinsoku ),
+FSNS( XML_w, XML_wordWrap ),
+FSNS( XML_w, XML_overflowPunct ),
+FSNS( XML_w, XML_topLinePunct ),
+FSNS( XML_w, XML_autoSpaceDE ),
+FSNS( XML_w, XML_autoSpaceDN ),
+FSNS( XML_w, XML_bidi ),
+FSNS( XML_w, XML_adjustRightInd ),
+FSNS( XML_w, XML_snapToGrid ),
+FSNS( XML_w, XML_spacing ),
+FSNS( XML_w, XML_ind ),
+FSNS( XML_w, XML_contextualSpacing ),
+FSNS( XML_w, XML_mirrorIndents ),
+FSNS( XML_w, XML_suppressOverlap ),
+FSNS( XML_w, XML_jc ),
+FSNS( XML_w, XML_textDirection ),
+FSNS( XML_w, XML_textAlignment ),
+FSNS( XML_w, XML_textboxTightWrap ),
+FSNS( XML_w, XML_outlineLvl ),
+FSNS( XML_w, XML_divId ),
+FSNS( XML_w, XML_cnfStyle ),
+FSNS( XML_w, XML_rPr ),
+FSNS( XML_w, XML_sectPr ),
+FSNS( XML_w, XML_pPrChange )
+};
+
void DocxAttributeOutput::InitCollectedParagraphProperties()
{
m_pLRSpaceAttrList.clear();
m_pParagraphSpacingAttrList.clear();
-// Write the elements in the spec order
-static const sal_Int32 aOrder[] =
-{
-FSNS( XML_w, XML_pStyle ),
-FSNS( XML_w, XML_keepNext ),
-FSNS( XML_w, XML_keepLines ),
-FSNS( XML_w, XML_pageBreakBefore ),
-FSNS( XML_w, XML_framePr ),
-FSNS( XML_w, XML_widowControl ),
-FSNS( XML_w, XML_numPr ),
-FSNS( XML_w, XML_suppressLineNumbers ),
-FSNS( XML_w, XML_pBdr ),
-FSNS( XML_w, XML_shd ),
-FSNS( XML_w, XML_tabs ),
-FSNS( XML_w, XML_suppressAutoHyphens ),
-FSNS( XML_w, XML_kinsoku ),
-FSNS( XML_w, XML_wordWrap ),
-FSNS( XML_w, XML_overflowPunct ),
-FSNS( XML_w, XML_topLinePunct ),
-FSNS( XML_w, XML_autoSpaceDE ),
-FSNS( XML_w, XML_autoSpaceDN ),
-FSNS( XML_w, XML_bidi ),
-FSNS( XML_w, XML_adjustRightInd ),
-FSNS( XML_w, XML_snapToGrid ),
-FSNS( XML_w, XML_spacing ),
-FSNS( XML_w, XML_ind ),
-FSNS( XML_w, XML_contextualSpacing ),
-FSNS( XML_w, XML_mirrorIndents ),
-FSNS( XML_w, XML_suppressOverlap ),
-FSNS( XML_w, XML_jc ),
-FSNS( XML_w, XML_textDirection ),
-FSNS( XML_w, XML_textAlignment ),
-FSNS( XML_w, XML_textboxTightWrap ),
-FSNS( XML_w, XML_outlineLvl ),
-FSNS( XML_w, XML_divId ),
-FSNS( XML_w, XML_cnfStyle ),
-FSNS( XML_w, XML_rPr ),
-FSNS( XML_w, XML_sectPr ),
-FSNS( XML_w, XML_pPrChange )
-};
-
// postpone the output so that we can later [in EndParagraphProperties()]
// prepend the properties before the run
-m_pSerializer->mark(Tag_InitCol
core.git: Branch 'libreoffice-26-2' - sw/qa sw/source
sw/qa/extras/ooxmlexport/data/tdf169986_bottomSpacing.docx |binary
sw/qa/extras/ooxmlexport/ooxmlexport25.cxx |6 ++
sw/source/writerfilter/dmapper/PropertyMap.cxx |3 ++-
3 files changed, 8 insertions(+), 1 deletion(-)
New commits:
commit b5637481d060d01094e7ceef959744d96547cdbb
Author: Justin Luth
AuthorDate: Mon Dec 15 15:50:45 2025 -0500
Commit: Justin Luth
CommitDate: Tue Dec 16 00:21:55 2025 +0100
tdf#169986 docx import: no sectPr belowSpacing to footnotes
This fixes my 25.2.6 regression from
commit 1326e09d019f05a82265f15c26288b4ffb7dc0c2
Author: Justin Luth on Mon Jul 28 09:29:51 2025
tdf#167657 only move sectPr bottomMargin after pageBreak
Footnotes (and comments) are always a CloseSectionGroup disaster.
The section's belowSpacing should never transfer
into a footnote - it only applies to the body paragraphs.
Interestingly, the unit test does NOT fail,
even though 2 pages are clearly seen interactively.
I include it simply for possible future benefit.
make CppunitTest_sw_ooxmlexport25 \
CPPUNIT_TEST_NAME=testTdf169986_bottomSpacing
Change-Id: I848ccda2ce2bb84de951c6da8f4001d217b8b38a
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/195683
Tested-by: Jenkins
Reviewed-by: Justin Luth
(cherry picked from commit 01c39dcb622256a66bd11695729db9b27468)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/195684
diff --git a/sw/qa/extras/ooxmlexport/data/tdf169986_bottomSpacing.docx
b/sw/qa/extras/ooxmlexport/data/tdf169986_bottomSpacing.docx
new file mode 100644
index ..c4e50257b58c
Binary files /dev/null and
b/sw/qa/extras/ooxmlexport/data/tdf169986_bottomSpacing.docx differ
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport25.cxx
b/sw/qa/extras/ooxmlexport/ooxmlexport25.cxx
index 634c1602863e..714329a85aa6 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport25.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport25.cxx
@@ -89,6 +89,12 @@ DECLARE_OOXMLEXPORT_TEST(testTdf166510_sectPr_bottomSpacing,
"tdf166510_sectPr_b
CPPUNIT_ASSERT_EQUAL(sal_Int32(4253), nHeight);
}
+DECLARE_OOXMLEXPORT_TEST(testTdf169986_bottomSpacing,
"tdf169986_bottomSpacing.docx")
+{
+// given with a continuous section break with a lot of below spacing, and
several footnotes...
+CPPUNIT_ASSERT_EQUAL(1, getPages());
+}
+
DECLARE_OOXMLEXPORT_TEST(testTdf167657_sectPr_bottomSpacing,
"tdf167657_sectPr_bottomSpacing.docx")
{
// given with a continuous break sectPr with no belowSpacing
diff --git a/sw/source/writerfilter/dmapper/PropertyMap.cxx
b/sw/source/writerfilter/dmapper/PropertyMap.cxx
index e54091ae22f4..d04774fbab64 100644
--- a/sw/source/writerfilter/dmapper/PropertyMap.cxx
+++ b/sw/source/writerfilter/dmapper/PropertyMap.cxx
@@ -1945,7 +1945,8 @@ void SectionPropertyMap::CloseSectionGroup(
DomainMapper_Impl& rDM_Impl )
// to the last paragraph before the section page break
// so that the consolidation/collapse of this section's 1st
paragraph's aboveSpacing
// works correctly (since below spacing before a page break otherwise
has no relevance).
-if (pPrevSection && pPrevSection->GetBelowSpacing().has_value() &&
m_xPreStartingRange.is())
+if (pPrevSection && pPrevSection->GetBelowSpacing().has_value() &&
m_xPreStartingRange.is()
+&& !rDM_Impl.IsInFootOrEndnote())
{
try
{
core.git: Branch 'libreoffice-26-2' - sw/qa sw/source
sw/qa/uitest/data/accept-all-overlap.docx |binary
sw/qa/uitest/writer_tests/trackedChanges.py | 17 +
sw/source/uibase/misc/redlndlg.cxx |9 +++--
3 files changed, 24 insertions(+), 2 deletions(-)
New commits:
commit 3a83575afce7376d7ea91d4d15d6e82ba91d9fbf
Author: Miklos Vajna
AuthorDate: Thu Dec 11 08:27:09 2025 +0100
Commit: Adolfo Jayme Barrientos
CommitDate: Sat Dec 13 08:45:01 2025 +0100
tdf#168737 sw interdependent redlines, direct: fix accept-all from dialog
Open the bugdoc, open the manage changes dialog, accept all changes by
clicking on the "accept all" button, not all changes are accepted.
This went wrong in commit 55640f9f0a4741f8e4b5b98096af822cee71da2c
(tdf#166319 sw interdependent redlines: allow accept/reject for fmt on
ins/del, 2025-09-29), which made it possible to only accept the directly
selectable part of a hierarchical redline (e.g. only the format of a
format-on-insert construct). Notice how clicking on the "accept all"
toolbar button works fine, since it doesn't enable the "direct" mode.
Fix the problem by extending SwRedlineAcceptDlg::CallAcceptReject(), so
it keeps asking Writer core to accept/reject changes directly when one
or more redline is selected, but go back to the default non-direct mode
when accepting or rejecting all redlines.
Cover this with a UI test, since the problem here is not around how
these redlines are rejected, but it's about how UI code invokes performs
these operations using SwWrtShell.
Change-Id: I8024d352b1512bd8d5c608087b684502624fd0a6
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/195511
Tested-by: Jenkins
Reviewed-by: Miklos Vajna
(cherry picked from commit 874d2fc70bd9e90259f2e1119577a9339b7fc457)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/195553
Reviewed-by: Adolfo Jayme Barrientos
diff --git a/sw/qa/uitest/data/accept-all-overlap.docx
b/sw/qa/uitest/data/accept-all-overlap.docx
new file mode 100644
index ..851d3b7edc2d
Binary files /dev/null and b/sw/qa/uitest/data/accept-all-overlap.docx differ
diff --git a/sw/qa/uitest/writer_tests/trackedChanges.py
b/sw/qa/uitest/writer_tests/trackedChanges.py
index 1e21a3a703ff..967d2d401499 100644
--- a/sw/qa/uitest/writer_tests/trackedChanges.py
+++ b/sw/qa/uitest/writer_tests/trackedChanges.py
@@ -608,6 +608,23 @@ class trackedchanges(UITestCase):
self.assertEqual(0, len(changesList.getChildren()))
+def test_accept_all_deletes_all_redlines(self):
+# Given a document with overlapping changes:
+with
self.ui_test.load_file(get_url_for_data_file("accept-all-overlap.docx")) as
document:
+xWriterDoc = self.xUITest.getTopFocusWindow()
+xWriterEdit = xWriterDoc.getChild("writer_edit")
+
+# When accepting all changes using the manage changes dialog:
+with
self.ui_test.execute_modeless_dialog_through_command(".uno:AcceptTrackedChanges",
close_button="close") as xTrackDlg:
+xAccBtn = xTrackDlg.getChild("acceptall")
+xAccBtn.executeAction("CLICK", tuple())
+
+# Then make sure all changes are accepted:
+# Without the accompanying fix in place, this failed with:
+# AssertionError: 1 != 0
+# i.e. a change was not accepted.
+self.assertEqual(document.Redlines.Count, 0)
+
def test_tdf155847_multiple_tracked_columns_crash(self):
with
self.ui_test.load_file(get_url_for_data_file("TC-table-del-add.docx")) as
document:
diff --git a/sw/source/uibase/misc/redlndlg.cxx
b/sw/source/uibase/misc/redlndlg.cxx
index 4e5609886585..afdad416adec 100644
--- a/sw/source/uibase/misc/redlndlg.cxx
+++ b/sw/source/uibase/misc/redlndlg.cxx
@@ -1193,8 +1193,13 @@ void SwRedlineAcceptDlg::CallAcceptReject( bool bSelect,
bool bAccept )
for (const auto& rRedLine : aRedlines)
{
SwRedlineTable::size_type nPosition = GetRedlinePos( *rRedLine );
+
+// bSelect is false for "accept/reject all", true when only
accepting/rejecting one or more
+// selected changes. Only use direct accept/reject for explicitly
selected changes.
+bool bDirect = bSelect;
+
if( nPosition != SwRedlineTable::npos )
-(pSh->*FnAccRej)( nPosition, /*bDirect=*/true );
+(pSh->*FnAccRej)( nPosition, bDirect );
// handle redlines of table rows, stored as children of the item
associated
// to the deleted/inserted table row(s)
@@ -1209,7 +1214,7 @@ void SwRedlineAcceptDlg::CallAcceptReject( bool bSelect,
bool bAccept )
{
nPosition = GetRedlinePos( *xChild );
if( nPosition != SwRedlineTable::npos )
-(pSh->*FnAccRej)( nPosition, /*bDirect=*/true );
+(pSh->*FnAccRej)( nPosition, bDirect );
core.git: Branch 'libreoffice-26-2' - sw/qa sw/source
sw/qa/core/data/odt/tdf155780_filename_display_format.odt |binary
sw/qa/core/macros-test.cxx| 14 ++
sw/source/core/unocore/unofield.cxx |6 --
3 files changed, 14 insertions(+), 6 deletions(-)
New commits:
commit 2a6ab94b377443cae0514cbd9049508379a0ac40
Author: Andreas Heinisch
AuthorDate: Tue Dec 2 20:08:07 2025 +0100
Commit: Xisco Fauli
CommitDate: Fri Dec 12 09:54:27 2025 +0100
tdf#155780 - Provide correct value for FileName field
Removed the invalid and redundant value assignment to the filename
field, since the value had already been correctly set by the prior cast
to SwFileNameFormat.
Change-Id: Id04bd32ebe91fc1e15305886461d210246984918
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/194935
Tested-by: Jenkins
Reviewed-by: Andreas Heinisch
(cherry picked from commit c44271e0f026674be9d89a33e5c37adf4f0a3568)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/195488
Reviewed-by: Xisco Fauli
diff --git a/sw/qa/core/data/odt/tdf155780_filename_display_format.odt
b/sw/qa/core/data/odt/tdf155780_filename_display_format.odt
new file mode 100644
index ..8331957427cb
Binary files /dev/null and
b/sw/qa/core/data/odt/tdf155780_filename_display_format.odt differ
diff --git a/sw/qa/core/macros-test.cxx b/sw/qa/core/macros-test.cxx
index 6d4512bfb0f0..d6c260992de2 100644
--- a/sw/qa/core/macros-test.cxx
+++ b/sw/qa/core/macros-test.cxx
@@ -69,6 +69,7 @@ public:
void testBookmarkDeleteTdf90816();
void testControlShapeGrouping();
void testTdf151846();
+void testTdf155780_filename_display_format();
void testTdf162431();
void testFdo55289();
void testFdo68983();
@@ -82,6 +83,7 @@ public:
CPPUNIT_TEST(testBookmarkDeleteTdf90816);
CPPUNIT_TEST(testControlShapeGrouping);
CPPUNIT_TEST(testTdf151846);
+CPPUNIT_TEST(testTdf155780_filename_display_format);
CPPUNIT_TEST(testTdf162431);
CPPUNIT_TEST(testFdo55289);
CPPUNIT_TEST(testFdo68983);
@@ -359,6 +361,18 @@ void SwMacrosTest::testTdf151846()
CPPUNIT_ASSERT_EQUAL(sal_Int32(1), aSeq.getLength());
}
+void SwMacrosTest::testTdf155780_filename_display_format()
+{
+loadFromFile(u"odt/tdf155780_filename_display_format.odt");
+
+uno::Any aRet = executeMacro(
+
u"vnd.sun.Star.script:Standard.Module1.testFilenameDisplayFormat?language=Basic&location=document"_ustr);
+
+OUString aStringRes;
+CPPUNIT_ASSERT(aRet >>= aStringRes);
+CPPUNIT_ASSERT_EQUAL(u"OK"_ustr, aStringRes);
+}
+
void SwMacrosTest::testTdf162431()
{
loadFromFile(u"odt/tdf162431.odt");
diff --git a/sw/source/core/unocore/unofield.cxx
b/sw/source/core/unocore/unofield.cxx
index 63334c2cfc86..8ffc5e468058 100644
--- a/sw/source/core/unocore/unofield.cxx
+++ b/sw/source/core/unocore/unofield.cxx
@@ -1452,9 +1452,6 @@ void SAL_CALL SwXTextField::attach(
xField.reset(pFNField);
if (!m_pImpl->m_pProps->sPar3.isEmpty())
pFNField->SetExpansion(m_pImpl->m_pProps->sPar3);
-uno::Any aFormat;
-aFormat <<= m_pImpl->m_pProps->nFormat;
-xField->PutValue( aFormat, FIELD_PROP_FORMAT );
}
break;
case SwServiceType::FieldTypeTemplateName:
@@ -1462,9 +1459,6 @@ void SAL_CALL SwXTextField::attach(
SwFieldType* pFieldType =
pDoc->getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::TemplateName);
xField.reset(new
SwTemplNameField(static_cast(pFieldType),
static_cast(m_pImpl->m_pProps->nFormat)));
-uno::Any aFormat;
-aFormat <<= m_pImpl->m_pProps->nFormat;
-xField->PutValue(aFormat, FIELD_PROP_FORMAT);
}
break;
case SwServiceType::FieldTypeChapter:
core.git: Branch 'libreoffice-26-2' - sw/qa sw/source
sw/qa/extras/ooxmlexport/data/tdf166325_scheme_color.docx |binary
sw/qa/extras/ooxmlexport/ooxmlexport17.cxx| 12 ++
sw/source/filter/ww8/docxattributeoutput.cxx | 59 +-
3 files changed, 51 insertions(+), 20 deletions(-)
New commits:
commit 43b74c0037b0d1b2ea2f4dc4faa42283addcf483
Author: Aron Budea
AuthorDate: Wed Dec 10 13:01:58 2025 +1030
Commit: Xisco Fauli
CommitDate: Fri Dec 12 09:01:42 2025 +0100
tdf#166325 sw: use correct type when exporting scheme color to DOCX
OOXML schemeClr element has a different type (ST_SchemeColorVal)
compared to themeColor's ST_ThemeColor type, eg. dk1 instead
of dark1, but commit baf179188d8b9e29188387fd4b7d887dd3168792
mapped them to the same values on export, causing some affected
files to become invalid.
Change-Id: Iff63aea8943be905c9afbb68012db320cc94eeb9
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/195346
Reviewed-by: Mike Kaganski
Tested-by: Jenkins
Reviewed-by: Aron Budea
(cherry picked from commit d5136e0b6059435356351cf2c54af61401e45fc2)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/195507
Reviewed-by: Xisco Fauli
diff --git a/sw/qa/extras/ooxmlexport/data/tdf166325_scheme_color.docx
b/sw/qa/extras/ooxmlexport/data/tdf166325_scheme_color.docx
new file mode 100644
index ..76928834f205
Binary files /dev/null and
b/sw/qa/extras/ooxmlexport/data/tdf166325_scheme_color.docx differ
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport17.cxx
b/sw/qa/extras/ooxmlexport/ooxmlexport17.cxx
index 0afb04e1ca98..ca5f5d35c0fe 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport17.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport17.cxx
@@ -1112,6 +1112,18 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf149200)
assertXPath(pXmlDoc, "/w:document/w:body/w:p[1]/w:r[1]/w:rPr/w:color",
"themeColor", u"dark1");
}
+CPPUNIT_TEST_FIXTURE(Test, testTdf166325_scheme_color)
+{
+createSwDoc("tdf166325_scheme_color.docx");
+save(TestFilter::DOCX);
+
+xmlDocUniquePtr pXmlDoc = parseExport(u"word/document.xml"_ustr);
+// Without the fix this would be "light2" (type ST_ThemeColor instead of
ST_SchemeColorVal)
+assertXPath(pXmlDoc,
+
"/w:document/w:body/w:p/w:r/w:rPr/w14:textFill/w14:solidFill/w14:schemeClr",
"val",
+u"lt2");
+}
+
DECLARE_OOXMLEXPORT_TEST(testTdf149313, "tdf149313.docx")
{
// only 2, but not 3 pages in document
diff --git a/sw/source/filter/ww8/docxattributeoutput.cxx
b/sw/source/filter/ww8/docxattributeoutput.cxx
index f0986e3353de..3fd2af4b63ad 100644
--- a/sw/source/filter/ww8/docxattributeoutput.cxx
+++ b/sw/source/filter/ww8/docxattributeoutput.cxx
@@ -301,50 +301,69 @@ auto
detachFrom(rtl::Reference& src)
return rtl::Reference(std::move(src));
}
-constexpr auto constThemeColorTypeTokenMap =
frozen::make_unordered_map({
-{ model::ThemeColorType::Dark1, "dark1" },
-{ model::ThemeColorType::Light1, "light1" },
-{ model::ThemeColorType::Dark2, "dark2" },
-{ model::ThemeColorType::Light2, "light2" },
-{ model::ThemeColorType::Accent1, "accent1" },
-{ model::ThemeColorType::Accent2, "accent2" },
-{ model::ThemeColorType::Accent3, "accent3" },
-{ model::ThemeColorType::Accent4, "accent4" },
-{ model::ThemeColorType::Accent5, "accent5" },
-{ model::ThemeColorType::Accent6, "accent6" },
-{ model::ThemeColorType::Hyperlink, "hyperlink" },
-{ model::ThemeColorType::FollowedHyperlink, "followedHyperlink" }
+struct ThemeSchemeName
+{
+const char* ThemeName;
+const char* SchemeName;
+};
+
+constexpr auto constThemeColorTypeTokenMap =
frozen::make_unordered_map({
+{ model::ThemeColorType::Dark1, { "dark1", "dk1" } },
+{ model::ThemeColorType::Light1, { "light1", "lt1" } },
+{ model::ThemeColorType::Dark2, {"dark2", "dk2" } },
+{ model::ThemeColorType::Light2, {"light2", "lt2" } },
+{ model::ThemeColorType::Accent1, {"accent1", "accent1" } },
+{ model::ThemeColorType::Accent2, {"accent2", "accent2" } },
+{ model::ThemeColorType::Accent3, {"accent3", "accent3" } },
+{ model::ThemeColorType::Accent4, {"accent4", "accent4" } },
+{ model::ThemeColorType::Accent5, {"accent5", "accent5" } },
+{ model::ThemeColorType::Accent6, {"accent6", "accent6" } },
+{ model::ThemeColorType::Hyperlink, {"hyperlink", "hlink" } },
+{ model::ThemeColorType::FollowedHyperlink, {"followedHyperlink",
"folHlink" } }
});
-OString lclGetSchemeType(model::ComplexColor const& rComplexColor)
+// Returns colors as OOXML ST_ThemeColor or ST_SchemeColorVal types, which are
very similar
+OString lclGetThemeOrSchemeType(model::ComplexColor const& rComplexColor, bool
bIsTheme = true)
{
const auto iter =
constThemeColorTypeTokenMap.find(rComplexColor.getThemeColorType());
assert(iter != constThemeColorTypeTokenMap.end());
-OString sSchemeType = iter->second;
+OString sSchemeType = bIs
core.git: Branch 'libreoffice-26-2' - sw/qa sw/source
sw/qa/extras/ooxmlexport/ooxmlexport6.cxx|3 ---
sw/source/filter/ww8/docxattributeoutput.cxx |5 -
sw/source/filter/ww8/docxtableexport.cxx | 20 ++--
3 files changed, 18 insertions(+), 10 deletions(-)
New commits:
commit b4d1d38c1a14323b3bc6babf91a4929199bd6a4b
Author: Noel Grandin
AuthorDate: Tue Dec 9 19:03:51 2025 +0200
Commit: Xisco Fauli
CommitDate: Thu Dec 11 22:42:12 2025 +0100
officeotron: "0-00-00T00:00:00Z" is not a valid date
similar to commit 8c5683ee74230931c9375a22981239ca1a2c1a2e
Change-Id: I3de3e44ea4955a159d8e23932108d623fbb27c60
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/195331
Reviewed-by: Michael Stahl
Tested-by: Jenkins
(cherry picked from commit f4ff6e8d27b44ac59c54eb130179b3da0e01a9e9)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/195362
Reviewed-by: Xisco Fauli
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport6.cxx
b/sw/qa/extras/ooxmlexport/ooxmlexport6.cxx
index f179b63c3ea3..cfa33e8b461b 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport6.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport6.cxx
@@ -508,9 +508,6 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf127814)
{
createSwDoc("tdf127814.docx");
-// FIXME: validation error in OOXML export: Errors: 136
-skipValidation();
-
save(TestFilter::DOCX);
// Paragraph top margin was 0 in a table started on a new page
xmlDocUniquePtr pXmlDoc = parseExport(u"word/document.xml"_ustr);
diff --git a/sw/source/filter/ww8/docxattributeoutput.cxx
b/sw/source/filter/ww8/docxattributeoutput.cxx
index 8d3e9369592b..f0986e3353de 100644
--- a/sw/source/filter/ww8/docxattributeoutput.cxx
+++ b/sw/source/filter/ww8/docxattributeoutput.cxx
@@ -4359,7 +4359,10 @@ void DocxAttributeOutput::StartRedline(const
SwRedlineData* pRedlineData, bool b
const DateTime& aDateTime = pRedlineData->GetTimeStamp();
bool bNoDate = bRemovePersonalInfo ||
-( aDateTime.GetYear() == 1970 && aDateTime.GetMonth() == 1 &&
aDateTime.GetDay() == 1 );
+( aDateTime.GetYear() == 1970 && aDateTime.GetMonth() == 1 &&
aDateTime.GetDay() == 1 ) ||
+// The officeotron validator does not think year 0 is valid, so just
dont put anything,
+// a zero year is not useful anyway.
+( aDateTime.GetYear() == 0 && aDateTime.GetMonth() == 0 &&
aDateTime.GetDay() == 0 );
bool isInMoveBookmark = false;
for (const auto& openedBookmark : m_rOpenedBookmarksIds)
{
diff --git a/sw/source/filter/ww8/docxtableexport.cxx
b/sw/source/filter/ww8/docxtableexport.cxx
index fa640b4700ae..6be12ab131ef 100644
--- a/sw/source/filter/ww8/docxtableexport.cxx
+++ b/sw/source/filter/ww8/docxtableexport.cxx
@@ -661,9 +661,13 @@ void DocxAttributeOutput::TableRowRedline(
RTL_TEXTENCODING_UTF8));
const DateTime aDateTime = aRedlineData.GetTimeStamp();
-bool bNoDate = bRemovePersonalInfo
- || (aDateTime.GetYear() == 1970 && aDateTime.GetMonth()
== 1
- && aDateTime.GetDay() == 1);
+bool bNoDate
+= bRemovePersonalInfo
+ || (aDateTime.GetYear() == 1970 && aDateTime.GetMonth() == 1
+ && aDateTime.GetDay() == 1)
+ // The officeotron validator does not think year 0 is valid, so
just dont put anything,
+ // a zero year is not useful anyway.
+ || (aDateTime.GetYear() == 0 && aDateTime.GetMonth() == 0 &&
aDateTime.GetDay() == 0);
if (bNoDate)
m_pSerializer->singleElementNS(
@@ -732,9 +736,13 @@ void DocxAttributeOutput::TableCellRedline(
RTL_TEXTENCODING_UTF8));
const DateTime aDateTime = aRedlineData.GetTimeStamp();
-bool bNoDate = bRemovePersonalInfo
- || (aDateTime.GetYear() == 1970 && aDateTime.GetMonth()
== 1
- && aDateTime.GetDay() == 1);
+bool bNoDate
+= bRemovePersonalInfo
+ || (aDateTime.GetYear() == 1970 && aDateTime.GetMonth() == 1
+ && aDateTime.GetDay() == 1)
+ // The officeotron validator does not think year 0 is valid, so
just dont put anything,
+ // a zero year is not useful anyway.
+ || (aDateTime.GetYear() == 0 && aDateTime.GetMonth() == 0 &&
aDateTime.GetDay() == 0);
if (bNoDate)
m_pSerializer->singleElementNS(
core.git: Branch 'libreoffice-26-2' - sw/qa sw/source
sw/qa/extras/ooxmlexport/ooxmlexport5.cxx | 15 ---
sw/source/filter/ww8/docxtablestyleexport.cxx |2 +-
2 files changed, 1 insertion(+), 16 deletions(-)
New commits:
commit afde43acbc97304bacc1038232cc25bc14c71e1d
Author: Noel Grandin
AuthorDate: Tue Dec 9 21:20:13 2025 +0200
Commit: Xisco Fauli
CommitDate: Thu Dec 11 09:31:34 2025 +0100
officeotron: w:spacing in wrong order
found by converting the document in testNumberedList to docx,
then running officeotron on it.
Change-Id: I090b38a9a2e74dd8ec1c64aa818d976e319f59b2
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/195333
Tested-by: Jenkins
Reviewed-by: Michael Stahl
(cherry picked from commit 31ed14cdc3880d3cd04fe70724ec62c286eca715)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/195365
Reviewed-by: Xisco Fauli
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport5.cxx
b/sw/qa/extras/ooxmlexport/ooxmlexport5.cxx
index eda09dcc49ce..368f69ce6b99 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport5.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport5.cxx
@@ -235,9 +235,6 @@ CPPUNIT_TEST_FIXTURE(Test, testNumberedList)
{
createSwDoc("NumberedList.docx");
-//FIXME: validation error in OOXML export: Errors: 8
-skipValidation();
-
saveAndReload(TestFilter::DOCX);
//fdo74150:In document.xml, for pStyle = "NumberedList1", iLvl and numId
was not preserved
xmlDocUniquePtr pXmlDoc = parseExport(u"word/document.xml"_ustr);
@@ -254,9 +251,6 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf131819)
{
createSwDoc("NumberedList.docx");
-//FIXME: validation error in OOXML export: Errors: 8
-skipValidation();
-
save(TestFilter::DOCX);
// keep width of fixed size cells in the nested table
xmlDocUniquePtr pXmlDoc = parseExport(u"word/document.xml"_ustr);
@@ -269,9 +263,6 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf131959)
{
createSwDoc("NumberedList.docx");
-//FIXME: validation error in OOXML export: Errors: 8
-skipValidation();
-
save(TestFilter::DOCX);
// import tblInd from table style
xmlDocUniquePtr pXmlDoc = parseExport(u"word/document.xml"_ustr);
@@ -310,9 +301,6 @@ CPPUNIT_TEST_FIXTURE(Test, testContentTypeTIF)
DECLARE_OOXMLEXPORT_TEST(testFDO77117, "fdo77117.docx")
{
-//FIXME: validation error in OOXML export: Errors: 6
-skipValidation();
-
uno::Reference xGroup(getShape(1), uno::UNO_QUERY);
uno::Reference xShape(xGroup->getByIndex(0),
uno::UNO_QUERY);
// This checks textbox textrun size of font which is in group shape.
@@ -364,9 +352,6 @@ CPPUNIT_TEST_FIXTURE(Test, testOldComplexMergeTableInTable)
{
createSwDoc("ooo96040-2.odt");
-//FIXME: validation error in OOXML export: Errors: 3
-skipValidation();
-
save(TestFilter::DOCX);
parseExport(u"word/document.xml"_ustr);
diff --git a/sw/source/filter/ww8/docxtablestyleexport.cxx
b/sw/source/filter/ww8/docxtablestyleexport.cxx
index 9397ea3e57bb..0e864b095015 100644
--- a/sw/source/filter/ww8/docxtablestyleexport.cxx
+++ b/sw/source/filter/ww8/docxtablestyleexport.cxx
@@ -497,9 +497,9 @@ void DocxTableStyleExport::Impl::tableStylePPr(const
uno::SequencesingleElementNS(XML_w, XML_wordWrap);
-tableStylePInd(aInd);
handleBoolean(aSnapToGrid, XML_snapToGrid);
tableStylePSpacing(aSpacing);
+tableStylePInd(aInd);
if (!aJc.isEmpty())
m_pSerializer->singleElementNS(XML_w, XML_jc, FSNS(XML_w, XML_val),
aJc);
