summaryrefslogtreecommitdiffstats
path: root/writerfilter
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 09:44:04 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 09:44:04 +0000
commiteb358d77291eba677141bab113dc27d7aabb0f3e (patch)
tree2e96f3b5d0c79beaeb536bbf05c3b8564846e65f /writerfilter
parentAdding debian version 4:24.2.1-4. (diff)
downloadlibreoffice-eb358d77291eba677141bab113dc27d7aabb0f3e.tar.xz
libreoffice-eb358d77291eba677141bab113dc27d7aabb0f3e.zip
Merging upstream version 4:24.2.2.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'writerfilter')
-rw-r--r--writerfilter/CppunitTest_writerfilter_rtftok.mk1
-rw-r--r--writerfilter/qa/cppunittests/dmapper/DomainMapperTableHandler.cxx38
-rw-r--r--writerfilter/qa/cppunittests/dmapper/DomainMapper_Impl.cxx45
-rw-r--r--writerfilter/qa/cppunittests/dmapper/data/floattable-header-overlap.docxbin0 -> 23584 bytes
-rw-r--r--writerfilter/qa/cppunittests/dmapper/data/redlined-shape-sdt.docxbin0 -> 13837 bytes
-rw-r--r--writerfilter/qa/cppunittests/rtftok/data/floattable-then-sect-break.rtf12
-rw-r--r--writerfilter/qa/cppunittests/rtftok/rtfdispatchsymbol.cxx26
-rw-r--r--writerfilter/source/dmapper/DomainMapperTableHandler.cxx20
-rw-r--r--writerfilter/source/dmapper/DomainMapper_Impl.cxx122
-rw-r--r--writerfilter/source/dmapper/DomainMapper_Impl.hxx5
-rw-r--r--writerfilter/source/dmapper/GraphicImport.cxx12
-rw-r--r--writerfilter/source/dmapper/PropertyMap.cxx34
-rw-r--r--writerfilter/source/rtftok/rtfdispatchsymbol.cxx16
-rw-r--r--writerfilter/source/rtftok/rtfdocumentimpl.cxx16
-rw-r--r--writerfilter/source/rtftok/rtfsdrimport.hxx1
-rw-r--r--writerfilter/source/rtftok/rtfvalue.cxx9
-rw-r--r--writerfilter/source/rtftok/rtfvalue.hxx6
17 files changed, 314 insertions, 49 deletions
diff --git a/writerfilter/CppunitTest_writerfilter_rtftok.mk b/writerfilter/CppunitTest_writerfilter_rtftok.mk
index 2c91cbb8cb..d0a1e37bc1 100644
--- a/writerfilter/CppunitTest_writerfilter_rtftok.mk
+++ b/writerfilter/CppunitTest_writerfilter_rtftok.mk
@@ -13,6 +13,7 @@ $(eval $(call gb_CppunitTest_CppunitTest,writerfilter_rtftok))
$(eval $(call gb_CppunitTest_use_externals,writerfilter_rtftok,\
boost_headers \
+ libxml2 \
))
$(eval $(call gb_CppunitTest_add_exception_objects,writerfilter_rtftok, \
diff --git a/writerfilter/qa/cppunittests/dmapper/DomainMapperTableHandler.cxx b/writerfilter/qa/cppunittests/dmapper/DomainMapperTableHandler.cxx
index b72b819139..5924fb90bc 100644
--- a/writerfilter/qa/cppunittests/dmapper/DomainMapperTableHandler.cxx
+++ b/writerfilter/qa/cppunittests/dmapper/DomainMapperTableHandler.cxx
@@ -7,7 +7,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
-#include <test/unoapi_test.hxx>
+#include <test/unoapixml_test.hxx>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/table/BorderLine2.hpp>
@@ -19,17 +19,18 @@
#include <com/sun/star/style/BreakType.hpp>
#include <com/sun/star/text/XTextViewCursorSupplier.hpp>
#include <com/sun/star/text/XPageCursor.hpp>
+#include <com/sun/star/qa/XDumper.hpp>
using namespace ::com::sun::star;
namespace
{
/// Tests for writerfilter/source/dmapper/DomainMapperTableHandler.cxx.
-class Test : public UnoApiTest
+class Test : public UnoApiXmlTest
{
public:
Test()
- : UnoApiTest("/writerfilter/qa/cppunittests/dmapper/data/")
+ : UnoApiXmlTest("/writerfilter/qa/cppunittests/dmapper/data/")
{
}
};
@@ -197,6 +198,37 @@ CPPUNIT_TEST_FIXTURE(Test, testDOCXFloatingTableFootnoteRedline)
uno::Reference<drawing::XDrawPage> xDrawPage = xModel->getDrawPage();
CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(0), xDrawPage->getCount());
}
+
+CPPUNIT_TEST_FIXTURE(Test, testDOCXFloatingTableHeaderBodyOverlap)
+{
+ // Given a document with a floating table in a header, the floating table extends the header
+ // frame:
+ // When importing that document:
+ loadFromFile(u"floattable-header-overlap.docx");
+
+ // Then make sure the fly bottom is less than the top of the body text:
+ uno::Reference<frame::XModel> xModel(mxComponent, uno::UNO_QUERY);
+ css::uno::Reference<qa::XDumper> xDumper(xModel->getCurrentController(), uno::UNO_QUERY);
+ OString aDump = xDumper->dump("layout").toUtf8();
+ auto pCharBuffer = reinterpret_cast<const xmlChar*>(aDump.getStr());
+ xmlDocUniquePtr pXmlDoc(xmlParseDoc(pCharBuffer));
+ sal_Int32 nFlyBottom = getXPath(pXmlDoc, "//fly/infos/bounds"_ostr, "bottom"_ostr).toInt32();
+ // Body text top is body top + height of the first line, that's just a fly portion (kind of a
+ // top margin).
+ sal_Int32 nBodyTop
+ = getXPath(pXmlDoc, "//page[1]/body/txt[1]/infos/bounds"_ostr, "top"_ostr).toInt32();
+ // Without the accompanying fix in place, this test would have failed, the first line was not a
+ // fly portion but it was actual text, above the floating table.
+ assertXPath(pXmlDoc, "//page[1]/body/txt[1]/SwParaPortion/SwLineLayout[1]/child::*"_ostr,
+ "type"_ostr, "PortionType::Fly");
+ sal_Int32 nBodyFlyPortionHeight
+ = getXPath(pXmlDoc, "//page[1]/body/txt[1]/SwParaPortion/SwLineLayout[1]"_ostr,
+ "height"_ostr)
+ .toInt32();
+ sal_Int32 nBodyTextTop = nBodyTop + nBodyFlyPortionHeight;
+ // Fly bottom was 3063, body text top was 7148.
+ CPPUNIT_ASSERT_LESS(nBodyTextTop, nFlyBottom);
+}
}
CPPUNIT_PLUGIN_IMPLEMENT();
diff --git a/writerfilter/qa/cppunittests/dmapper/DomainMapper_Impl.cxx b/writerfilter/qa/cppunittests/dmapper/DomainMapper_Impl.cxx
index d0693746f2..16aa5cbfb2 100644
--- a/writerfilter/qa/cppunittests/dmapper/DomainMapper_Impl.cxx
+++ b/writerfilter/qa/cppunittests/dmapper/DomainMapper_Impl.cxx
@@ -366,6 +366,51 @@ CPPUNIT_TEST_FIXTURE(Test, testFloattableSectend)
// i.e. the first table was lost.
CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(2), xTables->getCount());
}
+
+CPPUNIT_TEST_FIXTURE(Test, testRedlinedShapeThenSdt)
+{
+ // Given a file with a second paragraph where text is followed by a redline, then an SDT:
+ // When importing that document:
+ loadFromFile(u"redlined-shape-sdt.docx");
+
+ // Then make sure the content control doesn't start at para start:
+ uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY);
+ uno::Reference<container::XEnumerationAccess> xParaEnumAccess(xTextDocument->getText(),
+ uno::UNO_QUERY);
+ uno::Reference<container::XEnumeration> xParaEnum = xParaEnumAccess->createEnumeration();
+ xParaEnum->nextElement();
+ uno::Reference<container::XEnumerationAccess> xPortionEnumAccess(xParaEnum->nextElement(),
+ uno::UNO_QUERY);
+ uno::Reference<container::XEnumeration> xPortionEnum = xPortionEnumAccess->createEnumeration();
+
+ uno::Reference<beans::XPropertySet> xPortion(xPortionEnum->nextElement(), uno::UNO_QUERY);
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected: Text
+ // - Actual : ContentControl
+ // i.e. the content control started at para start.
+ CPPUNIT_ASSERT_EQUAL(u"Text"_ustr,
+ xPortion->getPropertyValue("TextPortionType").get<OUString>());
+ xPortion.set(xPortionEnum->nextElement(), uno::UNO_QUERY);
+ // Redline start+end pair, containing a pair of text portions with an anchored object in the
+ // middle.
+ CPPUNIT_ASSERT_EQUAL(u"Redline"_ustr,
+ xPortion->getPropertyValue("TextPortionType").get<OUString>());
+ xPortion.set(xPortionEnum->nextElement(), uno::UNO_QUERY);
+ CPPUNIT_ASSERT_EQUAL(u"Text"_ustr,
+ xPortion->getPropertyValue("TextPortionType").get<OUString>());
+ xPortion.set(xPortionEnum->nextElement(), uno::UNO_QUERY);
+ CPPUNIT_ASSERT_EQUAL(u"Frame"_ustr,
+ xPortion->getPropertyValue("TextPortionType").get<OUString>());
+ xPortion.set(xPortionEnum->nextElement(), uno::UNO_QUERY);
+ CPPUNIT_ASSERT_EQUAL(u"Text"_ustr,
+ xPortion->getPropertyValue("TextPortionType").get<OUString>());
+ xPortion.set(xPortionEnum->nextElement(), uno::UNO_QUERY);
+ CPPUNIT_ASSERT_EQUAL(u"Redline"_ustr,
+ xPortion->getPropertyValue("TextPortionType").get<OUString>());
+ xPortion.set(xPortionEnum->nextElement(), uno::UNO_QUERY);
+ CPPUNIT_ASSERT_EQUAL(u"ContentControl"_ustr,
+ xPortion->getPropertyValue("TextPortionType").get<OUString>());
+}
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/qa/cppunittests/dmapper/data/floattable-header-overlap.docx b/writerfilter/qa/cppunittests/dmapper/data/floattable-header-overlap.docx
new file mode 100644
index 0000000000..1230b9f4e0
--- /dev/null
+++ b/writerfilter/qa/cppunittests/dmapper/data/floattable-header-overlap.docx
Binary files differ
diff --git a/writerfilter/qa/cppunittests/dmapper/data/redlined-shape-sdt.docx b/writerfilter/qa/cppunittests/dmapper/data/redlined-shape-sdt.docx
new file mode 100644
index 0000000000..ea7f4a5bd6
--- /dev/null
+++ b/writerfilter/qa/cppunittests/dmapper/data/redlined-shape-sdt.docx
Binary files differ
diff --git a/writerfilter/qa/cppunittests/rtftok/data/floattable-then-sect-break.rtf b/writerfilter/qa/cppunittests/rtftok/data/floattable-then-sect-break.rtf
new file mode 100644
index 0000000000..7ad179608c
--- /dev/null
+++ b/writerfilter/qa/cppunittests/rtftok/data/floattable-then-sect-break.rtf
@@ -0,0 +1,12 @@
+{\rtf1
+\paperw11907\paperh16840\margl896\margr2104\margt1440\margb720
+\pard\plain doc start\par
+\trowd\tpvpara\tphmrg\tposnegy-922\tdfrmtxtLeft180\tdfrmtxtRight180\cellx5670
+\pard\intbl A1\cell
+\row
+\trowd\tpvpara\tphmrg\tposnegy-922\tdfrmtxtLeft180\tdfrmtxtRight180\cellx5670
+\pard\intbl A2\cell
+\row
+\pard\sect
+\pard\plain doc end\par
+}
diff --git a/writerfilter/qa/cppunittests/rtftok/rtfdispatchsymbol.cxx b/writerfilter/qa/cppunittests/rtftok/rtfdispatchsymbol.cxx
index 8317e50824..bf3ecc937e 100644
--- a/writerfilter/qa/cppunittests/rtftok/rtfdispatchsymbol.cxx
+++ b/writerfilter/qa/cppunittests/rtftok/rtfdispatchsymbol.cxx
@@ -7,22 +7,23 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
-#include <test/unoapi_test.hxx>
+#include <test/unoapixml_test.hxx>
#include <com/sun/star/text/XTextDocument.hpp>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/style/ParagraphAdjust.hpp>
+#include <com/sun/star/qa/XDumper.hpp>
using namespace ::com::sun::star;
namespace
{
/// Tests for writerfilter/source/rtftok/rtfdispatchsymbol.cxx.
-class Test : public UnoApiTest
+class Test : public UnoApiXmlTest
{
public:
Test()
- : UnoApiTest("/writerfilter/qa/cppunittests/rtftok/data/")
+ : UnoApiXmlTest("/writerfilter/qa/cppunittests/rtftok/data/")
{
}
};
@@ -64,6 +65,25 @@ CPPUNIT_TEST_FIXTURE(Test, testCenterAfterPage)
// i.e. the paragraph alignment on the second page was lost.
CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int16>(style::ParagraphAdjust_CENTER), eActual);
}
+
+CPPUNIT_TEST_FIXTURE(Test, testFloattableThenSectBreak)
+{
+ // Given a document with a floating table, immediately followed by \sect:
+ // When loading that file:
+ loadFromFile(u"floattable-then-sect-break.rtf");
+
+ // Then make sure that the floating table is on the first page:
+ uno::Reference<frame::XModel> xModel(mxComponent, uno::UNO_QUERY);
+ css::uno::Reference<qa::XDumper> xDumper(xModel->getCurrentController(), uno::UNO_QUERY);
+ OString aDump = xDumper->dump("layout").toUtf8();
+ auto pCharBuffer = reinterpret_cast<const xmlChar*>(aDump.getStr());
+ xmlDocUniquePtr pXmlDoc(xmlParseDoc(pCharBuffer));
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected: 1
+ // - Actual : 0
+ // i.e. the floating table was on the 2nd page, not on the 1st page.
+ assertXPath(pXmlDoc, "/root/page[1]/sorted_objs/fly"_ostr, 1);
+}
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/DomainMapperTableHandler.cxx b/writerfilter/source/dmapper/DomainMapperTableHandler.cxx
index e63d1cf961..0ecc670155 100644
--- a/writerfilter/source/dmapper/DomainMapperTableHandler.cxx
+++ b/writerfilter/source/dmapper/DomainMapperTableHandler.cxx
@@ -39,6 +39,7 @@
#include <com/sun/star/text/WritingMode2.hpp>
#include <com/sun/star/text/XTextField.hpp>
#include <com/sun/star/text/XTextRangeCompare.hpp>
+#include <com/sun/star/text/RelOrientation.hpp>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/beans/XPropertyState.hpp>
#include <com/sun/star/container/XEnumeration.hpp>
@@ -1585,8 +1586,25 @@ void DomainMapperTableHandler::endTable(unsigned int nestedTableLevel)
// Floating tables inside a table always stay inside the cell.
// Also extend the header/footer area if needed, so an in-header floating table
// typically doesn't overlap with body test.
+ bool bIsFollowingTextFlow = true;
+
+ sal_Int16 nVertOrientRelation{};
+ auto it = std::find_if(aFrameProperties.begin(), aFrameProperties.end(),
+ [](const beans::PropertyValue& rPropertyValue) -> bool
+ { return rPropertyValue.Name == "VertOrientRelation"; });
+ if (it != aFrameProperties.end())
+ {
+ it->Value >>= nVertOrientRelation;
+ if (nVertOrientRelation == text::RelOrientation::PAGE_FRAME)
+ {
+ // If vertical relation is page, follow-text-flow is not useful and causes
+ // unwanted wrap of body text around in-header floating table, so avoid it.
+ bIsFollowingTextFlow = false;
+ }
+ }
+
aFrameProperties.push_back(
- comphelper::makePropertyValue("IsFollowingTextFlow", true));
+ comphelper::makePropertyValue("IsFollowingTextFlow", bIsFollowingTextFlow));
}
// A text frame created for floating tables is always allowed to split.
diff --git a/writerfilter/source/dmapper/DomainMapper_Impl.cxx b/writerfilter/source/dmapper/DomainMapper_Impl.cxx
index 58568a9105..05f3fae675 100644
--- a/writerfilter/source/dmapper/DomainMapper_Impl.cxx
+++ b/writerfilter/source/dmapper/DomainMapper_Impl.cxx
@@ -1003,6 +1003,10 @@ void DomainMapper_Impl::PushSdt()
return;
}
+ // This may delete text, so call it early, before we would set our start position, which may be
+ // invalidated by a delete.
+ MergeAtContentImageRedlineWithNext(xTextAppend);
+
uno::Reference<text::XText> xText = xTextAppend->getText();
if (!xText.is())
{
@@ -1033,7 +1037,41 @@ void DomainMapper_Impl::PopSdt()
uno::Reference<text::XTextRange> xStart = aPosition.m_xTextRange;
uno::Reference<text::XTextRange> xEnd = GetTopTextAppend()->getEnd();
uno::Reference<text::XText> xText = xEnd->getText();
- uno::Reference<text::XTextCursor> xCursor = xText->createTextCursorByRange(xStart);
+
+ uno::Reference<text::XTextCursor> xCursor;
+ try
+ {
+ xCursor = xText->createTextCursorByRange(xStart);
+ }
+ catch (const uno::RuntimeException&)
+ {
+ TOOLS_WARN_EXCEPTION("writerfilter", "DomainMapper_Impl::DomainMapper_Impl::PopSdt: createTextCursorByRange() failed");
+ // We redline form controls and that gets us confused when
+ // we process the SDT around the placeholder. What seems to
+ // happen is we lose the text-range when we pop the SDT position.
+ // Here, we reset the text-range when we fail to create the
+ // cursor from the top SDT position.
+ if (m_aTextAppendStack.empty())
+ {
+ return;
+ }
+
+ uno::Reference<text::XTextAppend> xTextAppend = m_aTextAppendStack.top().xTextAppend;
+ if (!xTextAppend.is())
+ {
+ return;
+ }
+
+ uno::Reference<text::XText> xText2 = xTextAppend->getText();
+ if (!xText2.is())
+ {
+ return;
+ }
+
+ // Reset to the start.
+ xCursor = xText2->createTextCursorByRange(xTextAppend->getStart());
+ }
+
if (!xCursor)
{
SAL_WARN("writerfilter.dmapper", "DomainMapper_Impl::PopSdt: no start position");
@@ -2553,6 +2591,7 @@ void DomainMapper_Impl::finishParagraph( const PropertyMapPtr& pPropertyMap, con
lcl_AddRange(pToBeSavedProperties, xTextAppend, rAppendContext);
}
}
+ applyToggleAttributes(pPropertyMap); // for paragraph marker formatting
std::vector<beans::PropertyValue> aProperties;
if (pPropertyMap)
{
@@ -3026,6 +3065,7 @@ void DomainMapper_Impl::finishParagraph( const PropertyMapPtr& pPropertyMap, con
}
+// TODO this does not yet take table styles into account
void DomainMapper_Impl::applyToggleAttributes(const PropertyMapPtr& pPropertyMap)
{
std::optional<PropertyMap::Property> charStyleProperty = pPropertyMap->getProperty(PROP_CHAR_STYLE_NAME);
@@ -3166,6 +3206,42 @@ void DomainMapper_Impl::applyToggleAttributes(const PropertyMapPtr& pPropertyMap
}
}
+void DomainMapper_Impl::MergeAtContentImageRedlineWithNext(const css::uno::Reference<css::text::XTextAppend>& xTextAppend)
+{
+ // remove workaround for change tracked images, if they are part of a redline,
+ // i.e. if the next run is a tracked change with the same type, author and date,
+ // as in the change tracking of the image.
+ if ( m_bRedlineImageInPreviousRun )
+ {
+ auto pCurrentRedline = m_aRedlines.top().size() > 0
+ ? m_aRedlines.top().back()
+ : GetTopContextOfType(CONTEXT_CHARACTER) &&
+ GetTopContextOfType(CONTEXT_CHARACTER)->Redlines().size() > 0
+ ? GetTopContextOfType(CONTEXT_CHARACTER)->Redlines().back()
+ : nullptr;
+ if ( m_previousRedline && pCurrentRedline &&
+ // same redline
+ (m_previousRedline->m_nToken & 0xffff) == (pCurrentRedline->m_nToken & 0xffff) &&
+ m_previousRedline->m_sAuthor == pCurrentRedline->m_sAuthor &&
+ m_previousRedline->m_sDate == pCurrentRedline->m_sDate )
+ {
+ uno::Reference< text::XTextCursor > xCursor = xTextAppend->getEnd()->getText( )->createTextCursor( );
+ assert(xCursor.is());
+ xCursor->gotoEnd(false);
+ xCursor->goLeft(2, true);
+ if ( xCursor->getString() == u"​​" )
+ {
+ xCursor->goRight(1, true);
+ xCursor->setString("");
+ xCursor->gotoEnd(false);
+ xCursor->goLeft(1, true);
+ xCursor->setString("");
+ }
+ }
+
+ m_bRedlineImageInPreviousRun = false;
+ }
+}
void DomainMapper_Impl::appendTextPortion( const OUString& rString, const PropertyMapPtr& pPropertyMap )
{
@@ -3195,39 +3271,7 @@ void DomainMapper_Impl::applyToggleAttributes(const PropertyMapPtr& pPropertyMap
rValue.Value <<= false;
}
- // remove workaround for change tracked images, if they are part of a redline,
- // i.e. if the next run is a tracked change with the same type, author and date,
- // as in the change tracking of the image.
- if ( m_bRedlineImageInPreviousRun )
- {
- auto pCurrentRedline = m_aRedlines.top().size() > 0
- ? m_aRedlines.top().back()
- : GetTopContextOfType(CONTEXT_CHARACTER) &&
- GetTopContextOfType(CONTEXT_CHARACTER)->Redlines().size() > 0
- ? GetTopContextOfType(CONTEXT_CHARACTER)->Redlines().back()
- : nullptr;
- if ( m_previousRedline && pCurrentRedline &&
- // same redline
- (m_previousRedline->m_nToken & 0xffff) == (pCurrentRedline->m_nToken & 0xffff) &&
- m_previousRedline->m_sAuthor == pCurrentRedline->m_sAuthor &&
- m_previousRedline->m_sDate == pCurrentRedline->m_sDate )
- {
- uno::Reference< text::XTextCursor > xCursor = xTextAppend->getEnd()->getText( )->createTextCursor( );
- assert(xCursor.is());
- xCursor->gotoEnd(false);
- xCursor->goLeft(2, true);
- if ( xCursor->getString() == u"​​" )
- {
- xCursor->goRight(1, true);
- xCursor->setString("");
- xCursor->gotoEnd(false);
- xCursor->goLeft(1, true);
- xCursor->setString("");
- }
- }
-
- m_bRedlineImageInPreviousRun = false;
- }
+ MergeAtContentImageRedlineWithNext(xTextAppend);
uno::Reference< text::XTextRange > xTextRange;
if (m_aTextAppendStack.top().xInsertPosition.is())
@@ -3880,7 +3924,12 @@ void DomainMapper_Impl::prepareHeaderFooterContent(uno::Reference<beans::XProper
m_aHeaderFooterTextAppendStack.push(std::make_pair(TextAppendContext(xTextAppend, xTextCursor), ePagePartType));
}
-/** Checks if the header and footer content on the text appennd stack is empty.
+bool DomainMapper_Impl::SeenHeaderFooter(PagePartType const partType, PageType const pageType) const
+{
+ return m_HeaderFooterSeen.find({partType, pageType}) != m_HeaderFooterSeen.end();
+}
+
+/** Checks if the header and footer content on the text appended stack is empty.
*/
void DomainMapper_Impl::checkIfHeaderFooterIsEmpty(PagePartType ePagePartType, PageType eType)
{
@@ -3938,7 +3987,10 @@ void DomainMapper_Impl::PopPageHeaderFooter(PagePartType ePagePartType, PageType
// content is not copied from the previous section
SectionPropertyMap* pSectionContext = GetSectionContext();
if (pSectionContext)
+ {
pSectionContext->clearHeaderFooterLinkToPrevious(ePagePartType, eType);
+ m_HeaderFooterSeen.emplace(ePagePartType, eType);
+ }
if (!m_aTextAppendStack.empty())
{
diff --git a/writerfilter/source/dmapper/DomainMapper_Impl.hxx b/writerfilter/source/dmapper/DomainMapper_Impl.hxx
index 082eda8fc5..8bd51d0add 100644
--- a/writerfilter/source/dmapper/DomainMapper_Impl.hxx
+++ b/writerfilter/source/dmapper/DomainMapper_Impl.hxx
@@ -638,6 +638,8 @@ private:
css::uno::Reference< css::text::XTextCursor > m_xTOCMarkerCursor;
+ ::std::set<::std::pair<PagePartType, PageType>> m_HeaderFooterSeen;
+
//annotation import
css::uno::Reference< css::beans::XPropertySet > m_xAnnotationField;
sal_Int32 m_nAnnotationId;
@@ -793,6 +795,7 @@ public:
void finishParagraph( const PropertyMapPtr& pPropertyMap, const bool bRemove = false, const bool bNoNumbering = false);
void applyToggleAttributes( const PropertyMapPtr& pPropertyMap );
+ void MergeAtContentImageRedlineWithNext(const css::uno::Reference<css::text::XTextAppend>& xTextAppend);
void appendTextPortion( const OUString& rString, const PropertyMapPtr& pPropertyMap );
void appendTextContent(const css::uno::Reference<css::text::XTextContent>&, const css::uno::Sequence<css::beans::PropertyValue>&);
void appendOLE( const OUString& rStreamName, const std::shared_ptr<OLEHandler>& pOleHandler );
@@ -1213,6 +1216,8 @@ public:
bool IsParaWithInlineObject() const { return m_StreamStateStack.top().bParaWithInlineObject; }
+ bool SeenHeaderFooter(PagePartType, PageType) const;
+
css::uno::Reference< css::embed::XStorage > m_xDocumentStorage;
/// Handles <w:altChunk>.
diff --git a/writerfilter/source/dmapper/GraphicImport.cxx b/writerfilter/source/dmapper/GraphicImport.cxx
index eebd1b8228..dc2a7935eb 100644
--- a/writerfilter/source/dmapper/GraphicImport.cxx
+++ b/writerfilter/source/dmapper/GraphicImport.cxx
@@ -270,6 +270,7 @@ public:
std::optional<sal_Int32> m_oEffectExtentTop;
std::optional<sal_Int32> m_oEffectExtentRight;
std::optional<sal_Int32> m_oEffectExtentBottom;
+ std::optional<text::GraphicCrop> m_oCrop;
GraphicImport_Impl(GraphicImportType & rImportType, DomainMapper& rDMapper,
std::pair<OUString, OUString>& rPositionOffsets,
@@ -795,6 +796,9 @@ void GraphicImport::lcl_attribute(Id nName, Value& rValue)
case NS_ooxml::LN_CT_WrapSquare_wrapText: //90928;
handleWrapTextValue(rValue.getInt());
break;
+ case NS_ooxml::LN_CT_BlipFillProperties_srcRect:
+ m_pImpl->m_oCrop.emplace(rValue.getAny().get<text::GraphicCrop>());
+ break;
case NS_ooxml::LN_shape:
{
uno::Reference< drawing::XShape> xShape;
@@ -857,7 +861,13 @@ void GraphicImport::lcl_attribute(Id nName, Value& rValue)
text::GraphicCrop aGraphicCrop( 0, 0, 0, 0 );
uno::Reference< beans::XPropertySet > xSourceGraphProps( xShape, uno::UNO_QUERY );
uno::Any aAny = xSourceGraphProps->getPropertyValue("GraphicCrop");
- if(aAny >>= aGraphicCrop) {
+ if (m_pImpl->m_oCrop)
+ { // RTF: RTFValue from resolvePict()
+ xGraphProps->setPropertyValue("GraphicCrop",
+ uno::Any(*m_pImpl->m_oCrop));
+ }
+ else if (aAny >>= aGraphicCrop)
+ { // DOCX: imported in oox BlipFillContext
xGraphProps->setPropertyValue("GraphicCrop",
uno::Any( aGraphicCrop ) );
}
diff --git a/writerfilter/source/dmapper/PropertyMap.cxx b/writerfilter/source/dmapper/PropertyMap.cxx
index 9c3e031e40..8f7a301986 100644
--- a/writerfilter/source/dmapper/PropertyMap.cxx
+++ b/writerfilter/source/dmapper/PropertyMap.cxx
@@ -514,7 +514,7 @@ void SectionPropertyMap::removeXTextContent(uno::Reference<text::XText> const& r
xParagraph->dispose();
}
-/** Set the header/footer sharing as defined by titlePage and eveoAndOdd flags
+/** Set the header/footer sharing as defined by titlePage and evenAndOdd flags
* in the document and clear the content of anything not written during the import.
*/
void SectionPropertyMap::setHeaderFooterProperties(DomainMapper_Impl& rDM_Impl)
@@ -914,7 +914,9 @@ void copyHeaderFooterTextProperty(const uno::Reference<beans::XPropertySet>& xSo
}
// Copies all the header and footer content and relevant flags from the source style to the target.
-void completeCopyHeaderFooter(const uno::Reference<beans::XPropertySet>& xSourceStyle, const uno::Reference<beans::XPropertySet>& xTargetStyle)
+void completeCopyHeaderFooter(const uno::Reference<beans::XPropertySet>& xSourceStyle,
+ const uno::Reference<beans::XPropertySet>& xTargetStyle,
+ bool const bMissingHeader, bool const bMissingFooter)
{
if (!xSourceStyle.is() || !xTargetStyle.is())
return;
@@ -960,6 +962,25 @@ void completeCopyHeaderFooter(const uno::Reference<beans::XPropertySet>& xSource
copyHeaderFooterTextProperty(xSourceStyle, xTargetStyle, PROP_FOOTER_TEXT_LEFT);
copyHeaderFooterTextProperty(xSourceStyle, xTargetStyle, PROP_FOOTER_TEXT);
}
+ // tdf#153196 the copy is used for the first page, the source will be used
+ // on subsequent pages, so clear source's first page header/footer
+ if (!bSourceFirstIsShared)
+ {
+ xSourceStyle->setPropertyValue(sFirstIsShared, uno::Any(true));
+ }
+ // if there is *only* a first-footer, and no previous section from which
+ // to inherit a footer, the import process has created an empty footer
+ // that didn't exist in the file; remove it
+ if (bSourceHasHeader && bMissingHeader)
+ {
+ xSourceStyle->setPropertyValue(sHeaderIsOn, uno::Any(false));
+ }
+ if (bSourceHasFooter && bMissingFooter)
+ {
+ // setting "FooterIsShared" to true here does nothing, because it causes
+ // left footer to be stashed, which means it will be exported anyway
+ xSourceStyle->setPropertyValue(sFooterIsOn, uno::Any(false));
+ }
}
// Copy headers and footers from the previous page style.
@@ -1429,7 +1450,14 @@ void SectionPropertyMap::CreateEvenOddPageStyleCopy(DomainMapper_Impl& rDM_Impl,
rDM_Impl.GetPageStyles()->insertByName(evenOddStyleName, uno::Any(evenOddStyle));
if (rDM_Impl.IsNewDoc())
- completeCopyHeaderFooter(pageProperties, evenOddStyle);
+ {
+ bool const bEvenAndOdd(rDM_Impl.GetSettingsTable()->GetEvenAndOddHeaders());
+ completeCopyHeaderFooter(pageProperties, evenOddStyle,
+ !rDM_Impl.SeenHeaderFooter(PagePartType::Header, PageType::RIGHT)
+ && (!bEvenAndOdd || !rDM_Impl.SeenHeaderFooter(PagePartType::Header, PageType::LEFT)),
+ !rDM_Impl.SeenHeaderFooter(PagePartType::Footer, PageType::RIGHT)
+ && (!bEvenAndOdd || !rDM_Impl.SeenHeaderFooter(PagePartType::Footer, PageType::LEFT)));
+ }
if (eBreakType == PageBreakType::Even)
evenOddStyle->setPropertyValue(getPropertyName(PROP_PAGE_STYLE_LAYOUT), uno::Any(style::PageStyleLayout_LEFT));
diff --git a/writerfilter/source/rtftok/rtfdispatchsymbol.cxx b/writerfilter/source/rtftok/rtfdispatchsymbol.cxx
index 0f01d79f5c..b40fd55dde 100644
--- a/writerfilter/source/rtftok/rtfdispatchsymbol.cxx
+++ b/writerfilter/source/rtftok/rtfdispatchsymbol.cxx
@@ -140,10 +140,21 @@ RTFError RTFDocumentImpl::dispatchSymbol(RTFKeyword nKeyword)
}
else
{
- if (m_bNeedCr)
+ bool bPendingFloatingTable = false;
+ RTFValue::Pointer_t pTblpPr
+ = m_aStates.top().getTableRowSprms().find(NS_ooxml::LN_CT_TblPrBase_tblpPr);
+ if (pTblpPr)
+ {
+ // We have a pending floating table, provide an anchor for it still in this
+ // section.
+ bPendingFloatingTable = true;
+ }
+
+ if (m_bNeedCr || bPendingFloatingTable)
{ // tdf#158586 don't dispatch \par here, it eats deferred page breaks
setNeedPar(true);
}
+
sectBreak();
if (m_nResetBreakOnSectBreak != RTFKeyword::invalid)
{
@@ -371,7 +382,8 @@ RTFError RTFDocumentImpl::dispatchSymbol(RTFKeyword nKeyword)
= m_aStates.top().getSectionSprms().find(NS_ooxml::LN_EG_SectPrContents_titlePg);
if (((pBreak
&& pBreak->getInt()
- == static_cast<sal_Int32>(NS_ooxml::LN_Value_ST_SectionMark_continuous))
+ == static_cast<sal_Int32>(NS_ooxml::LN_Value_ST_SectionMark_continuous)
+ && m_bHadSect) // tdf#158983 before first \sect, ignore \sbknone!
|| m_nResetBreakOnSectBreak == RTFKeyword::SBKNONE)
&& !(pTitlePg && pTitlePg->getInt()))
{
diff --git a/writerfilter/source/rtftok/rtfdocumentimpl.cxx b/writerfilter/source/rtftok/rtfdocumentimpl.cxx
index e82674930b..f6165736f3 100644
--- a/writerfilter/source/rtftok/rtfdocumentimpl.cxx
+++ b/writerfilter/source/rtftok/rtfdocumentimpl.cxx
@@ -1069,6 +1069,16 @@ void RTFDocumentImpl::resolvePict(bool const bInline, uno::Reference<drawing::XS
RTFSprms aAttributes;
// shape attribute
RTFSprms aPicAttributes;
+ if (m_aStates.top().getPicture().nCropT != 0 || m_aStates.top().getPicture().nCropB != 0
+ || m_aStates.top().getPicture().nCropL != 0 || m_aStates.top().getPicture().nCropR != 0)
+ {
+ text::GraphicCrop const crop{ m_aStates.top().getPicture().nCropT,
+ m_aStates.top().getPicture().nCropB,
+ m_aStates.top().getPicture().nCropL,
+ m_aStates.top().getPicture().nCropR };
+ auto const pCrop = new RTFValue(crop);
+ aPicAttributes.set(NS_ooxml::LN_CT_BlipFillProperties_srcRect, pCrop);
+ }
auto pShapeValue = new RTFValue(xShape);
aPicAttributes.set(NS_ooxml::LN_shape, pShapeValue);
// pic sprm
@@ -2967,7 +2977,11 @@ RTFError RTFDocumentImpl::beforePopState(RTFParserState& rState)
case Destination::SHAPE:
m_bNeedFinalPar = true;
m_bNeedCr = m_bNeedCrOrig;
- if (rState.getFrame().hasProperties())
+ // tdf#47036 insert paragraph break for graphic object inside text
+ // frame at start of document - TODO: the object may actually be
+ // anchored inside the text frame and this ends up putting the
+ // anchor in the body, but better than losing the shape...
+ if (rState.getFrame().hasProperties() && m_pSdrImport->isTextGraphicObject())
{
// parBreak() modifies m_aStates.top() so we can't apply resetFrame() directly on aState
resetFrame();
diff --git a/writerfilter/source/rtftok/rtfsdrimport.hxx b/writerfilter/source/rtftok/rtfsdrimport.hxx
index 16f7f9c319..b06803bd0f 100644
--- a/writerfilter/source/rtftok/rtfsdrimport.hxx
+++ b/writerfilter/source/rtftok/rtfsdrimport.hxx
@@ -77,6 +77,7 @@ public:
void popParent();
css::uno::Reference<css::drawing::XShape> const& getCurrentShape() const { return m_xShape; }
bool isFakePict() const { return m_bFakePict; }
+ bool isTextGraphicObject() const { return m_bTextGraphicObject; }
private:
void createShape(const OUString& rService, css::uno::Reference<css::drawing::XShape>& xShape,
diff --git a/writerfilter/source/rtftok/rtfvalue.cxx b/writerfilter/source/rtftok/rtfvalue.cxx
index 7a9137f5db..6654a3f2ec 100644
--- a/writerfilter/source/rtftok/rtfvalue.cxx
+++ b/writerfilter/source/rtftok/rtfvalue.cxx
@@ -85,6 +85,11 @@ RTFValue::RTFValue(const RTFPicture& rPicture)
{
}
+RTFValue::RTFValue(text::GraphicCrop const& rCrop)
+ : m_oCrop(rCrop)
+{
+}
+
RTFValue::~RTFValue() = default;
int RTFValue::getInt() const { return m_nValue; }
@@ -110,6 +115,10 @@ uno::Any RTFValue::getAny() const
ret <<= m_xStream;
else if (m_xObject.is())
ret <<= m_xObject;
+ else if (m_oCrop)
+ {
+ ret <<= *m_oCrop;
+ }
else
ret <<= static_cast<sal_Int32>(m_nValue);
return ret;
diff --git a/writerfilter/source/rtftok/rtfvalue.hxx b/writerfilter/source/rtftok/rtfvalue.hxx
index 4f37a5dcbc..6c87c61a1d 100644
--- a/writerfilter/source/rtftok/rtfvalue.hxx
+++ b/writerfilter/source/rtftok/rtfvalue.hxx
@@ -11,6 +11,10 @@
#include <dmapper/resourcemodel.hxx>
+#include <com/sun/star/text/GraphicCrop.hpp>
+
+#include <optional>
+
namespace com::sun::star
{
namespace embed
@@ -49,6 +53,7 @@ public:
explicit RTFValue(css::uno::Reference<css::embed::XEmbeddedObject> xObject);
explicit RTFValue(const RTFShape& aShape);
explicit RTFValue(const RTFPicture& rPicture);
+ explicit RTFValue(css::text::GraphicCrop const& rCrop);
~RTFValue() override;
void setString(const OUString& sValue);
int getInt() const override;
@@ -79,6 +84,7 @@ private:
bool m_bForceString = false;
mutable tools::SvRef<RTFShape> m_pShape;
mutable tools::SvRef<RTFPicture> m_pPicture;
+ ::std::optional<css::text::GraphicCrop> m_oCrop;
};
} // namespace writerfilter::rtftok