sw/qa/extras/ooxmlexport/data/tdf100037_inlineZOrder2.docx |binary sw/qa/extras/ooxmlexport/data/tdf100037_inlineZOrder3.docx |binary sw/qa/extras/ooxmlexport/ooxmlexport18.cxx | 44 +++++++++++ sw/source/writerfilter/dmapper/DomainMapper_Impl.cxx | 48 +++++++++---- sw/source/writerfilter/dmapper/DomainMapper_Impl.hxx | 4 - 5 files changed, 81 insertions(+), 15 deletions(-)
New commits: commit f714fb262ad8561afcababf4b7a97dedb1b72c15 Author: Justin Luth <jl...@mail.com> AuthorDate: Fri Jun 7 15:37:32 2024 -0400 Commit: Justin Luth <jl...@mail.com> CommitDate: Sat Jun 8 00:43:45 2024 +0200 tdf#100037 vml import: set fly-anchored AS_CHARs above fly's zOrder Normally an AS_CHAR is the lowest in the heaven-layer zOrder, but if it is inside a fly, it should be just above the fly's zOrder. In order to get at the parent's properties, I had to remove the stack abstraction so I changed the stack to a vector. That seemed a lot saner than popping and re-pushing. make CppunitTest_sw_ooxmlexport18 \ CPPUNIT_TEST_NAME=testTdf100037_inlineZOrder2 make CppunitTest_sw_ooxmlexport18 \ CPPUNIT_TEST_NAME=testTdf100037_inlineZOrder3 Change-Id: Idc159e8203b3f304133a9b110c135e4d0f001dbc Reviewed-on: https://gerrit.libreoffice.org/c/core/+/168565 Reviewed-by: Justin Luth <jl...@mail.com> Tested-by: Jenkins diff --git a/sw/qa/extras/ooxmlexport/data/tdf100037_inlineZOrder2.docx b/sw/qa/extras/ooxmlexport/data/tdf100037_inlineZOrder2.docx new file mode 100644 index 000000000000..e30824ba10c8 Binary files /dev/null and b/sw/qa/extras/ooxmlexport/data/tdf100037_inlineZOrder2.docx differ diff --git a/sw/qa/extras/ooxmlexport/data/tdf100037_inlineZOrder3.docx b/sw/qa/extras/ooxmlexport/data/tdf100037_inlineZOrder3.docx new file mode 100644 index 000000000000..d97bd7ecb291 Binary files /dev/null and b/sw/qa/extras/ooxmlexport/data/tdf100037_inlineZOrder3.docx differ diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport18.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport18.cxx index 50a9dc33e9e5..ff3e7a3bf6f6 100644 --- a/sw/qa/extras/ooxmlexport/ooxmlexport18.cxx +++ b/sw/qa/extras/ooxmlexport/ooxmlexport18.cxx @@ -1076,6 +1076,50 @@ DECLARE_OOXMLEXPORT_TEST(testTdf100037_inlineZOrder, "tdf100037_inlineZOrder.doc CPPUNIT_ASSERT_EQUAL(OUString("Frame1"), getProperty<OUString>(zOrder1, "LinkDisplayName")); } +DECLARE_OOXMLEXPORT_TEST(testTdf100037_inlineZOrder2, "tdf100037_inlineZOrder2.docx") +{ + // given a yellow floating textbox-with-image overlapped by a blue textbox-with-image, + // the inline image should take its zOrder from the textbox it is in. + if (isExported()) + return; // we don't export images inside of draw textboxes I guess + + uno::Reference<beans::XPropertySet> zOrder0(getShape(1), uno::UNO_QUERY); + uno::Reference<beans::XPropertySet> zOrder1(getShape(2), uno::UNO_QUERY); + uno::Reference<beans::XPropertySet> zOrder2(getShape(3), uno::UNO_QUERY); + uno::Reference<beans::XPropertySet> zOrder3(getShape(4), uno::UNO_QUERY); + CPPUNIT_ASSERT_EQUAL(sal_Int32(0), getProperty<sal_Int32>(zOrder0, "ZOrder")); // lower + CPPUNIT_ASSERT_EQUAL(sal_Int32(1), getProperty<sal_Int32>(zOrder1, "ZOrder")); + CPPUNIT_ASSERT_EQUAL(sal_Int32(2), getProperty<sal_Int32>(zOrder2, "ZOrder")); + CPPUNIT_ASSERT_EQUAL(sal_Int32(3), getProperty<sal_Int32>(zOrder3, "ZOrder")); // higher + // yellow textbox (Frame1) is the lowest + CPPUNIT_ASSERT_EQUAL(OUString("Frame1"), getProperty<OUString>(zOrder0, "LinkDisplayName")); + //CPPUNIT_ASSERT_EQUAL(OUString("Image1"), getProperty<OUString>(zOrder1, "Name")); + CPPUNIT_ASSERT_EQUAL(OUString("Frame2"), getProperty<OUString>(zOrder2, "LinkDisplayName")); + // CPPUNIT_ASSERT_EQUAL(OUString("Image2"), getProperty<OUString>(zOrder3, "LinkDisplayName")); +} + +DECLARE_OOXMLEXPORT_TEST(testTdf100037_inlineZOrder3, "tdf100037_inlineZOrder3.docx") +{ + // given a yellow floating textbox-with-image that overlaps a blue textbox-with-image, + // the inline image should take its zOrder from the textbox it is in. + if (isExported()) + return; // we don't export images inside of draw textboxes I guess + + uno::Reference<beans::XPropertySet> zOrder0(getShape(1), uno::UNO_QUERY); + uno::Reference<beans::XPropertySet> zOrder1(getShape(2), uno::UNO_QUERY); + uno::Reference<beans::XPropertySet> zOrder2(getShape(3), uno::UNO_QUERY); + uno::Reference<beans::XPropertySet> zOrder3(getShape(4), uno::UNO_QUERY); + CPPUNIT_ASSERT_EQUAL(sal_Int32(0), getProperty<sal_Int32>(zOrder0, "ZOrder")); // lower + CPPUNIT_ASSERT_EQUAL(sal_Int32(1), getProperty<sal_Int32>(zOrder1, "ZOrder")); + CPPUNIT_ASSERT_EQUAL(sal_Int32(2), getProperty<sal_Int32>(zOrder2, "ZOrder")); + CPPUNIT_ASSERT_EQUAL(sal_Int32(3), getProperty<sal_Int32>(zOrder3, "ZOrder")); // higher + // blue textbox (Frame2) is the lowest + CPPUNIT_ASSERT_EQUAL(OUString("Frame2"), getProperty<OUString>(zOrder0, "LinkDisplayName")); + // CPPUNIT_ASSERT_EQUAL(OUString("Image2"), getProperty<OUString>(zOrder1, "LinkDisplayName")); + CPPUNIT_ASSERT_EQUAL(OUString("Frame1"), getProperty<OUString>(zOrder2, "LinkDisplayName")); + // CPPUNIT_ASSERT_EQUAL(OUString("Image1"), getProperty<OUString>(zOrder3, "LinkDisplayName")); +} + DECLARE_OOXMLEXPORT_TEST(testTdf155903, "tdf155903.odt") { // Without the accompanying fix in place, this test would have crashed, diff --git a/sw/source/writerfilter/dmapper/DomainMapper_Impl.cxx b/sw/source/writerfilter/dmapper/DomainMapper_Impl.cxx index 25612d082e5f..7f026444894d 100644 --- a/sw/source/writerfilter/dmapper/DomainMapper_Impl.cxx +++ b/sw/source/writerfilter/dmapper/DomainMapper_Impl.cxx @@ -3469,9 +3469,9 @@ void DomainMapper_Impl::appendOLE( const OUString& rStreamName, const std::share // gives a better ( visually ) result xOLE->setPropertyValue(getPropertyName( PROP_ANCHOR_TYPE ), uno::Any( text::TextContentAnchorType_AS_CHARACTER ) ); // remove ( if valid ) associated shape ( used for graphic replacement ) - SAL_WARN_IF(m_aAnchoredStack.empty(), "writerfilter.dmapper", "no anchor stack"); - if (!m_aAnchoredStack.empty()) - m_aAnchoredStack.top( ).bToRemove = true; + SAL_WARN_IF(m_vAnchoredStack.empty(), "writerfilter.dmapper", "no anchor stack"); + if (!m_vAnchoredStack.empty()) + m_vAnchoredStack.back().bToRemove = true; RemoveLastParagraph(); SAL_WARN_IF(m_aTextAppendStack.empty(), "writerfilter.dmapper", "no text stack"); if (!m_aTextAppendStack.empty()) @@ -4737,14 +4737,14 @@ void DomainMapper_Impl::PushShapeContext( const uno::Reference< drawing::XShape // shapes for OLE objects. m_aTextAppendStack.push(TextAppendContext(uno::Reference<text::XTextAppend>(xShape, uno::UNO_QUERY), uno::Reference<text::XTextCursor>())); uno::Reference<text::XTextContent> xTxtContent(xShape, uno::UNO_QUERY); - m_aAnchoredStack.push(AnchoredContext(xTxtContent)); + m_vAnchoredStack.push_back(AnchoredContext(xTxtContent)); } else if (xSInfo->supportsService(u"com.sun.star.drawing.OLE2Shape"_ustr)) { // OLE2Shape from oox should be converted to a TextEmbeddedObject for sw. m_aTextAppendStack.push(TextAppendContext(uno::Reference<text::XTextAppend>(xShape, uno::UNO_QUERY), uno::Reference<text::XTextCursor>())); uno::Reference<text::XTextContent> xTextContent(xShape, uno::UNO_QUERY); - m_aAnchoredStack.push(AnchoredContext(xTextContent)); + m_vAnchoredStack.push_back(AnchoredContext(xTextContent)); uno::Reference<beans::XPropertySet> xShapePropertySet(xShape, uno::UNO_QUERY); rtl::Reference<SwXTextEmbeddedObject> xEmbedded = m_xTextDocument->createTextEmbeddedObject(); @@ -4752,7 +4752,7 @@ void DomainMapper_Impl::PushShapeContext( const uno::Reference< drawing::XShape xEmbedded->setPropertyValue(getPropertyName(PROP_EMBEDDED_OBJECT), xShapePropertySet->getPropertyValue(getPropertyName(PROP_EMBEDDED_OBJECT))); xEmbedded->setPropertyValue(getPropertyName(PROP_ANCHOR_TYPE), uno::Any(text::TextContentAnchorType_AS_CHARACTER)); // So that the original bitmap-only shape will be replaced by the embedded object. - m_aAnchoredStack.top().bToRemove = true; + m_vAnchoredStack.back().bToRemove = true; m_aTextAppendStack.pop(); appendTextContent(m_StreamStateStack.top().xEmbedded, uno::Sequence<beans::PropertyValue>()); } @@ -4772,7 +4772,7 @@ void DomainMapper_Impl::PushShapeContext( const uno::Reference< drawing::XShape // Add the shape to the anchored objects stack uno::Reference< text::XTextContent > xTxtContent( xShape, uno::UNO_QUERY_THROW ); - m_aAnchoredStack.push( AnchoredContext(xTxtContent) ); + m_vAnchoredStack.push_back(AnchoredContext(xTxtContent)); uno::Reference<beans::XPropertySet> xShapePropertySet(xShape, uno::UNO_QUERY_THROW); #ifdef DBG_UTIL @@ -4832,7 +4832,29 @@ void DomainMapper_Impl::PushShapeContext( const uno::Reference< drawing::XShape xShapePropertySet->setPropertyValue(getPropertyName(PROP_BOTTOM_MARGIN), aPropMargin->second); - sal_Int64 zOrder = SAL_MIN_INT64; + sal_Int64 zOrder = SAL_MIN_INT64; // lowest in heaven-layer: AS_CHAR in body text + // AS_CHARs anchored inside a fly should be just above the fly's zOrder + if (m_vAnchoredStack.size() > 1) + { + uno::Reference<beans::XPropertySet> xParentPropertySet( + m_vAnchoredStack[m_vAnchoredStack.size() - 2].xTextContent, + uno::UNO_QUERY_THROW); + uno::Sequence<beans::PropertyValue> aGrabBag; + xParentPropertySet->getPropertyValue(u"FrameInteropGrabBag"_ustr) >>= aGrabBag; + for (const auto& rProp : aGrabBag) + { + if (rProp.Name == "VML-Z-ORDER") + { + rProp.Value >>= zOrder; + ++zOrder; + GraphicZOrderHelper::adjustRelativeHeight(zOrder, /*IsZIndex=*/true, + zOrder < 0, + IsInHeaderFooter()); + xShapePropertySet->setPropertyValue(getPropertyName(PROP_OPAQUE), + uno::Any(zOrder >= 0)); + } + } + } xShapePropertySet->setPropertyValue(u"ZOrder"_ustr, uno::Any(rZOrderHelper.findZOrder(zOrder, /*LastDuplicateWins*/true))); rZOrderHelper.addItem(xShapePropertySet, zOrder); @@ -4933,19 +4955,19 @@ void DomainMapper_Impl::PopShapeContext() getTableManager().endLevel(); popTableManager(); } - if ( m_aAnchoredStack.empty() ) + if (m_vAnchoredStack.empty()) return; // For OLE object replacement shape, the text append context was already removed // or the OLE object couldn't be inserted. - if ( !m_aAnchoredStack.top().bToRemove ) + if (!m_vAnchoredStack.back().bToRemove) { RemoveLastParagraph(); if (!m_aTextAppendStack.empty()) m_aTextAppendStack.pop(); } - uno::Reference< text::XTextContent > xObj = m_aAnchoredStack.top( ).xTextContent; + uno::Reference<text::XTextContent> xObj = m_vAnchoredStack.back().xTextContent; try { appendTextContent( xObj, uno::Sequence< beans::PropertyValue >() ); @@ -4958,7 +4980,7 @@ void DomainMapper_Impl::PopShapeContext() const uno::Reference<drawing::XShape> xShape( xObj, uno::UNO_QUERY_THROW ); // Remove the shape if required (most likely replacement shape for OLE object) // or anchored to a discarded header or footer - if ( m_xTextDocument && (m_aAnchoredStack.top().bToRemove || m_bDiscardHeaderFooter) ) + if (m_xTextDocument && (m_vAnchoredStack.back().bToRemove || m_bDiscardHeaderFooter)) { try { @@ -4995,7 +5017,7 @@ void DomainMapper_Impl::PopShapeContext() } } - m_aAnchoredStack.pop(); + m_vAnchoredStack.pop_back(); } bool DomainMapper_Impl::IsSdtEndBefore() diff --git a/sw/source/writerfilter/dmapper/DomainMapper_Impl.hxx b/sw/source/writerfilter/dmapper/DomainMapper_Impl.hxx index f60411128bcf..c76d1f6dd5ba 100644 --- a/sw/source/writerfilter/dmapper/DomainMapper_Impl.hxx +++ b/sw/source/writerfilter/dmapper/DomainMapper_Impl.hxx @@ -555,7 +555,7 @@ private: css::uno::Reference<css::text::XText> m_xBodyText; std::stack<TextAppendContext> m_aTextAppendStack; - std::stack<AnchoredContext> m_aAnchoredStack; + std::vector<AnchoredContext> m_vAnchoredStack; public: // DomainMapper needs it std::stack<SubstreamContext> m_StreamStateStack; private: @@ -906,7 +906,7 @@ public: bool IsNumberingImport() const { return m_bInNumberingImport;} void SetAnyTableImport( bool bSet ) { m_bInAnyTableImport = bSet;} bool IsAnyTableImport()const { return m_bInAnyTableImport;} - bool IsInShape()const { return m_aAnchoredStack.size() > 0;} + bool IsInShape()const { return m_vAnchoredStack.size() > 0;} void PushShapeContext(const css::uno::Reference<css::drawing::XShape>& xShape); void PopShapeContext();