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();

Reply via email to