summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLászló Németh <nemeth@numbertext.org>2021-03-26 16:28:03 +0100
committerLászló Németh <nemeth@numbertext.org>2021-03-28 14:49:08 +0200
commite11c51eefe8c3210cef2b5850f401ba67a401d01 (patch)
treeda3c189d933082328da69654e0e8fd3ae57e9d7b
parent27377e3db2792d1bf6eb733492462701defdeffc (diff)
tdf#95806 tdf#125877 tdf#141172 DOCX: fix tables in footnotes
and endnotes by converting them to floating tables during the import, and removing floating at the DOCX export. Writer core doesn't support non-floating tables in footnotes and endnotes officially, (flowfrm:cxx: "Tables in footnotes are not truly supported"), so their DOCX import resulted serious problems: – missing table paint (tdf#95806); – table loss during saving to ODT (tdf#125877); – table loss during copying them or their footnotes and endnotes in the document (this resulted the regression of the optimized footnote and endnote import: tdf#141172); – table loss during changing the order of the chapters in the Navigator. Change-Id: Ife8af41936ae3ab003a3a9ad33b98c1d813e9c82 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/113162 Tested-by: László Németh <nemeth@numbertext.org> Reviewed-by: László Németh <nemeth@numbertext.org>
-rw-r--r--sw/qa/extras/odfexport/data/tdf95806.docxbin0 -> 16907 bytes
-rw-r--r--sw/qa/extras/odfexport/odfexport.cxx17
-rw-r--r--sw/qa/extras/ooxmlexport/data/tdf141172.docxbin0 -> 16286 bytes
-rw-r--r--sw/qa/extras/ooxmlexport/ooxmlexport14.cxx8
-rw-r--r--sw/source/filter/ww8/docxattributeoutput.cxx5
-rw-r--r--writerfilter/source/dmapper/DomainMapperTableHandler.cxx61
-rw-r--r--writerfilter/source/dmapper/DomainMapperTableHandler.hxx4
-rw-r--r--writerfilter/source/dmapper/DomainMapper_Impl.hxx7
-rw-r--r--writerfilter/source/dmapper/PropertyMap.cxx8
9 files changed, 101 insertions, 9 deletions
diff --git a/sw/qa/extras/odfexport/data/tdf95806.docx b/sw/qa/extras/odfexport/data/tdf95806.docx
new file mode 100644
index 000000000000..65bfaae3e423
--- /dev/null
+++ b/sw/qa/extras/odfexport/data/tdf95806.docx
Binary files differ
diff --git a/sw/qa/extras/odfexport/odfexport.cxx b/sw/qa/extras/odfexport/odfexport.cxx
index 841019b3073b..f570e9f5d40a 100644
--- a/sw/qa/extras/odfexport/odfexport.cxx
+++ b/sw/qa/extras/odfexport/odfexport.cxx
@@ -263,6 +263,23 @@ DECLARE_ODFEXPORT_TEST(testTdf139126, "tdf139126.odt")
CPPUNIT_ASSERT_EQUAL(OUString("** Expression is faulty **"), xE2->getString());
}
+DECLARE_ODFEXPORT_TEST(testTdf125877, "tdf95806.docx")
+{
+ CPPUNIT_ASSERT_EQUAL(1, getPages());
+ uno::Reference<text::XTextTablesSupplier> xSupplier(mxComponent, uno::UNO_QUERY);
+ uno::Reference<container::XIndexAccess> xTables(xSupplier->getTextTables(), uno::UNO_QUERY);
+ uno::Reference<text::XTextTablesSupplier> xTextTablesSupplier(mxComponent, uno::UNO_QUERY);
+
+ // This was 0 (lost table during ODT export in footnotes)
+ // Note: fix also tdf#95806: painting table layout is correct
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xTables->getCount());
+
+ // floating table: there is a frame now
+ uno::Reference<text::XTextFramesSupplier> xTextFramesSupplier(mxComponent, uno::UNO_QUERY);
+ uno::Reference<container::XIndexAccess> xIndexAccess(xTextFramesSupplier->getTextFrames(), uno::UNO_QUERY);
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xIndexAccess->getCount());
+}
+
DECLARE_ODFEXPORT_TEST(testTdf103567, "tdf103567.odt")
{
CPPUNIT_ASSERT_EQUAL(1, getShapes());
diff --git a/sw/qa/extras/ooxmlexport/data/tdf141172.docx b/sw/qa/extras/ooxmlexport/data/tdf141172.docx
new file mode 100644
index 000000000000..0e1647ae27e1
--- /dev/null
+++ b/sw/qa/extras/ooxmlexport/data/tdf141172.docx
Binary files differ
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport14.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport14.cxx
index 77fcb3fd1091..cd3ca2f39e46 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport14.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport14.cxx
@@ -1169,6 +1169,14 @@ DECLARE_OOXMLEXPORT_EXPORTONLY_TEST(testTdf123757, "tdf123757.docx")
assertXPath(pXml, "/w:document/w:body/w:tbl", 2);
}
+DECLARE_OOXMLEXPORT_EXPORTONLY_TEST(testTdf141172, "tdf141172.docx")
+{
+ xmlDocUniquePtr pXml = parseExport("word/endnotes.xml");
+ CPPUNIT_ASSERT(pXml);
+ // This was 1 (lost table during copying endnote content)
+ assertXPath(pXml, "/w:endnotes/w:endnote/w:tbl", 2);
+}
+
DECLARE_OOXMLEXPORT_TEST(testContSectBreakHeaderFooter, "cont-sect-break-header-footer.docx")
{
// Load a document with a continuous section break on page 2.
diff --git a/sw/source/filter/ww8/docxattributeoutput.cxx b/sw/source/filter/ww8/docxattributeoutput.cxx
index 4b9c72829c3b..f4688e5f6a01 100644
--- a/sw/source/filter/ww8/docxattributeoutput.cxx
+++ b/sw/source/filter/ww8/docxattributeoutput.cxx
@@ -3982,7 +3982,10 @@ void DocxAttributeOutput::TableDefinition( ww8::WW8TableNodeInfoInner::Pointer_t
m_pSerializer->singleElementNS(XML_w, XML_tblLook, pAttributeList);
}
- else if (rGrabBagElement.first == "TablePosition" )
+ else if (rGrabBagElement.first == "TablePosition" &&
+ // skip empty table position (tables in footnotes converted to
+ // floating tables temporarily, don't export this)
+ rGrabBagElement.second != uno::Any() )
{
rtl::Reference<FastAttributeList> attrListTablePos = FastSerializerHelper::createAttrList( );
const uno::Sequence<beans::PropertyValue> aTablePosition = rGrabBagElement.second.get<uno::Sequence<beans::PropertyValue> >();
diff --git a/writerfilter/source/dmapper/DomainMapperTableHandler.cxx b/writerfilter/source/dmapper/DomainMapperTableHandler.cxx
index fd21827211bb..55c48740f0ec 100644
--- a/writerfilter/source/dmapper/DomainMapperTableHandler.cxx
+++ b/writerfilter/source/dmapper/DomainMapperTableHandler.cxx
@@ -32,6 +32,7 @@
#include <com/sun/star/table/XCellRange.hpp>
#include <com/sun/star/text/HoriOrientation.hpp>
#include <com/sun/star/text/SizeType.hpp>
+#include <com/sun/star/text/TextContentAnchorType.hpp>
#include <com/sun/star/text/XTextField.hpp>
#include <com/sun/star/text/XTextRangeCompare.hpp>
#include <com/sun/star/beans/XPropertySet.hpp>
@@ -339,9 +340,43 @@ void lcl_adjustBorderDistance(TableInfo& rInfo, const table::BorderLine2& rLeftB
rInfo.nRightBorderDistance = nActualR;
}
+void lcl_fillEmptyFrameProperties(std::vector<beans::PropertyValue>& rFrameProperties)
+{
+ // fill empty frame properties to create an invisible frame around the table:
+ // hide frame borders and zero inner and outer frame margins
+ beans::PropertyValue aValue;
+ aValue.Name = getPropertyName( PROP_ANCHOR_TYPE );
+ aValue.Value <<= text::TextContentAnchorType_AS_CHARACTER;
+ rFrameProperties.push_back(aValue);
+
+ table::BorderLine2 aEmptyBorder;
+ static const std::vector<std::u16string_view> aBorderNames
+ = { u"TopBorder", u"LeftBorder", u"BottomBorder", u"RightBorder" };
+ for (size_t i = 0; i < aBorderNames.size(); ++i)
+ {
+ beans::PropertyValue aBorderValue;
+ aBorderValue.Name = aBorderNames[i];
+ aBorderValue.Value <<= aEmptyBorder;
+ rFrameProperties.push_back(aBorderValue);
+ }
+ static const std::vector<std::u16string_view> aMarginNames
+ = { u"TopBorderDistance", u"LeftBorderDistance",
+ u"BottomBorderDistance", u"RightBorderDistance",
+ u"TopMargin", u"LeftMargin", u"BottomMargin", u"RightMargin" };
+ for (size_t i = 0; i < aMarginNames.size(); ++i)
+ {
+ beans::PropertyValue aMarginValue;
+ aMarginValue.Name = aMarginNames[i];
+ aMarginValue.Value <<= sal_Int32(10);
+ rFrameProperties.push_back(aMarginValue);
+ }
+}
+
}
-TableStyleSheetEntry * DomainMapperTableHandler::endTableGetTableStyle(TableInfo & rInfo, std::vector<beans::PropertyValue>& rFrameProperties)
+TableStyleSheetEntry * DomainMapperTableHandler::endTableGetTableStyle(TableInfo & rInfo,
+ std::vector<beans::PropertyValue>& rFrameProperties,
+ bool bConvertToFloatingInFootnote)
{
// will receive the table style if any
TableStyleSheetEntry* pTableStyle = nullptr;
@@ -394,6 +429,11 @@ TableStyleSheetEntry * DomainMapperTableHandler::endTableGetTableStyle(TableInfo
aGrabBag["TablePosition"] <<= aGrabBagTS;
}
+ else if (bConvertToFloatingInFootnote)
+ {
+ // define empty "TablePosition" to avoid export temporary floating
+ aGrabBag["TablePosition"] = uno::Any();
+ }
std::optional<PropertyMap::Property> aTableStyleVal = m_aTableProperties->getProperty(META_PROP_TABLE_STYLE_NAME);
if(aTableStyleVal)
@@ -1375,7 +1415,15 @@ void DomainMapperTableHandler::endTable(unsigned int nestedTableLevel, bool bTab
(m_rDMapper_Impl.getTableManager().getCurrentTablePosition());
TableInfo aTableInfo;
aTableInfo.nNestLevel = nestedTableLevel;
- aTableInfo.pTableStyle = endTableGetTableStyle(aTableInfo, aFrameProperties);
+
+ // non-floating tables need floating in footnotes and endnotes, because
+ // Writer core cannot handle (i.e. save in ODT, copy, edit etc.) them otherwise
+ bool bConvertToFloating = aFrameProperties.empty() &&
+ nestedTableLevel <= 1 &&
+ m_rDMapper_Impl.IsInFootOrEndnote();
+ bool bFloating = !aFrameProperties.empty() || bConvertToFloating;
+
+ aTableInfo.pTableStyle = endTableGetTableStyle(aTableInfo, aFrameProperties, bConvertToFloating);
// expands to uno::Sequence< Sequence< beans::PropertyValues > >
std::vector<HorizontallyMergedCell> aMerges;
@@ -1392,7 +1440,8 @@ void DomainMapperTableHandler::endTable(unsigned int nestedTableLevel, bool bTab
uno::Reference<text::XTextRange> xStart;
uno::Reference<text::XTextRange> xEnd;
- bool bFloating = !aFrameProperties.empty();
+ if ( bConvertToFloating )
+ lcl_fillEmptyFrameProperties(aFrameProperties);
// OOXML table style may contain paragraph properties, apply these on cell paragraphs
if ( m_aTableRanges[0].hasElements() && m_aTableRanges[0][0].hasElements() )
@@ -1585,7 +1634,11 @@ void DomainMapperTableHandler::endTable(unsigned int nestedTableLevel, bool bTab
sal_Int32 nTableWidthType = text::SizeType::FIX;
m_aTableProperties->getValue(TablePropertyMap::TABLE_WIDTH_TYPE, nTableWidthType);
if (m_rDMapper_Impl.GetSectionContext() && nestedTableLevel <= 1 && !m_rDMapper_Impl.IsInHeaderFooter())
- m_rDMapper_Impl.m_aPendingFloatingTables.emplace_back(xStart, xEnd, comphelper::containerToSequence(aFrameProperties), nTableWidth, nTableWidthType);
+ {
+ m_rDMapper_Impl.m_aPendingFloatingTables.emplace_back(xStart, xEnd,
+ comphelper::containerToSequence(aFrameProperties),
+ nTableWidth, nTableWidthType, bConvertToFloating);
+ }
else
{
// m_xText points to the body text, get the current xText from m_rDMapper_Impl, in case e.g. we would be in a header.
diff --git a/writerfilter/source/dmapper/DomainMapperTableHandler.hxx b/writerfilter/source/dmapper/DomainMapperTableHandler.hxx
index 900ff747f4cc..5a2a37acd90f 100644
--- a/writerfilter/source/dmapper/DomainMapperTableHandler.hxx
+++ b/writerfilter/source/dmapper/DomainMapperTableHandler.hxx
@@ -70,7 +70,9 @@ class DomainMapperTableHandler final : public virtual SvRefBase
/// Did we have a foot or endnote in this table?
bool m_bHadFootOrEndnote;
- TableStyleSheetEntry * endTableGetTableStyle(TableInfo & rInfo, std::vector<css::beans::PropertyValue>& rFrameProperties);
+ TableStyleSheetEntry * endTableGetTableStyle(TableInfo & rInfo,
+ std::vector<css::beans::PropertyValue>& rFrameProperties,
+ bool bConvertToFloating);
CellPropertyValuesSeq_t endTableGetCellProperties(TableInfo & rInfo, std::vector<HorizontallyMergedCell>& rMerges);
css::uno::Sequence<css::beans::PropertyValues> endTableGetRowProperties();
diff --git a/writerfilter/source/dmapper/DomainMapper_Impl.hxx b/writerfilter/source/dmapper/DomainMapper_Impl.hxx
index 6aa33913d924..1b29e96cbc27 100644
--- a/writerfilter/source/dmapper/DomainMapper_Impl.hxx
+++ b/writerfilter/source/dmapper/DomainMapper_Impl.hxx
@@ -395,16 +395,19 @@ struct FloatingTableInfo
sal_Int32 m_nTableWidthType;
/// Break type of the section that contains this table.
sal_Int32 m_nBreakType = -1;
+ /// Tables in footnotes and endnotes are always floating
+ bool m_bConvertToFloatingInFootnote = false;
FloatingTableInfo(css::uno::Reference<css::text::XTextRange> const& xStart,
css::uno::Reference<css::text::XTextRange> const& xEnd,
const css::uno::Sequence<css::beans::PropertyValue>& aFrameProperties,
- sal_Int32 nTableWidth, sal_Int32 nTableWidthType)
+ sal_Int32 nTableWidth, sal_Int32 nTableWidthType, bool bConvertToFloatingInFootnote)
: m_xStart(xStart),
m_xEnd(xEnd),
m_aFrameProperties(aFrameProperties),
m_nTableWidth(nTableWidth),
- m_nTableWidthType(nTableWidthType)
+ m_nTableWidthType(nTableWidthType),
+ m_bConvertToFloatingInFootnote(bConvertToFloatingInFootnote)
{
}
css::uno::Any getPropertyValue(std::u16string_view propertyName);
diff --git a/writerfilter/source/dmapper/PropertyMap.cxx b/writerfilter/source/dmapper/PropertyMap.cxx
index f7a8f800d7d0..cc556ddc6330 100644
--- a/writerfilter/source/dmapper/PropertyMap.cxx
+++ b/writerfilter/source/dmapper/PropertyMap.cxx
@@ -1138,6 +1138,9 @@ void SectionPropertyMap::HandleMarginsHeaderFooter( bool bFirstPage, DomainMappe
bool SectionPropertyMap::FloatingTableConversion( const DomainMapper_Impl& rDM_Impl, FloatingTableInfo& rInfo )
{
+ // always convert non-floating tables to floating ones in footnotes and endnotes
+ if ( rInfo.m_bConvertToFloatingInFootnote )
+ return true;
// This is OOXML version of the code deciding if the table needs to be
// in a floating frame.
// For ww8 code, see SwWW8ImplReader::FloatingTableConversion in
@@ -1388,12 +1391,15 @@ void SectionPropertyMap::CloseSectionGroup( DomainMapper_Impl& rDM_Impl )
// Text area width is known at the end of a section: decide if tables should be converted or not.
std::vector<FloatingTableInfo>& rPendingFloatingTables = rDM_Impl.m_aPendingFloatingTables;
- uno::Reference<text::XTextAppendAndConvert> xBodyText( rDM_Impl.GetBodyText(), uno::UNO_QUERY );
for ( FloatingTableInfo & rInfo : rPendingFloatingTables )
{
rInfo.m_nBreakType = m_nBreakType;
if ( FloatingTableConversion( rDM_Impl, rInfo ) )
{
+ uno::Reference<text::XTextAppendAndConvert> xBodyText(
+ rInfo.m_bConvertToFloatingInFootnote
+ ? rInfo.m_xStart->getText()
+ : rDM_Impl.GetBodyText(), uno::UNO_QUERY );
std::deque<css::uno::Any> aFramedRedlines = rDM_Impl.m_aStoredRedlines[StoredRedlines::FRAME];
try
{