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 <jluth@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 0000000..e30824b
--- /dev/null
+++ b/sw/qa/extras/ooxmlexport/data/tdf100037_inlineZOrder2.docx
Binary files 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 0000000..d97bd7e
--- /dev/null
+++ b/sw/qa/extras/ooxmlexport/data/tdf100037_inlineZOrder3.docx
Binary files differ
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport18.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport18.cxx
index 50a9dc3..ff3e7a3b 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 25612d082..7f02644 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 f6041112..c76d1f6 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();