sw/qa/extras/ooxmlexport/data/tdf159158_zOrder_headerBehind.odt |binary sw/qa/extras/ooxmlexport/ooxmlexport18.cxx | 15 ++ writerfilter/inc/dmapper/GraphicZOrderHelper.hxx | 10 + writerfilter/source/dmapper/DomainMapper_Impl.cxx | 8 + writerfilter/source/dmapper/GraphicHelpers.cxx | 58 +++++++++- writerfilter/source/dmapper/GraphicImport.cxx | 27 ---- 6 files changed, 89 insertions(+), 29 deletions(-)
New commits: commit 9aa7dc185af99a540926cc699193449177050386 Author: Justin Luth <justin.l...@collabora.com> AuthorDate: Tue Feb 27 19:56:32 2024 -0500 Commit: Justin Luth <jl...@mail.com> CommitDate: Fri Mar 1 01:36:30 2024 +0100 tdf#159158 writerfilter: headers always under other z-orders I guessed that a negative z-order will be below a positive relativeHeight when it is in the header. It seems like a reasonable assumption. No unit tests hit this situation AFAIK. make CppunitTest_sw_ooxmlexport18 \ CPPUNIT_TEST_NAME=testTdf159158_zOrder_headerBehind Change-Id: I06e31f3df413ad9c32791c8f4b940831630131dd Reviewed-on: https://gerrit.libreoffice.org/c/core/+/164105 Tested-by: Jenkins Reviewed-by: Justin Luth <jl...@mail.com> diff --git a/sw/qa/extras/ooxmlexport/data/tdf159158_zOrder_headerBehind.odt b/sw/qa/extras/ooxmlexport/data/tdf159158_zOrder_headerBehind.odt new file mode 100644 index 000000000000..a4bf98a1f7aa Binary files /dev/null and b/sw/qa/extras/ooxmlexport/data/tdf159158_zOrder_headerBehind.odt differ diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport18.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport18.cxx index 084863c144f4..91fa26ea0a47 100644 --- a/sw/qa/extras/ooxmlexport/ooxmlexport18.cxx +++ b/sw/qa/extras/ooxmlexport/ooxmlexport18.cxx @@ -1032,6 +1032,21 @@ DECLARE_OOXMLEXPORT_TEST(testTdf159158_zOrder_behindDocB, "tdf159158_zOrder_behi CPPUNIT_ASSERT_EQUAL(OUString("5-Point Star Blue"), getProperty<OUString>(zOrder1,"Name")); } +DECLARE_OOXMLEXPORT_TEST(testTdf159158_zOrder_headerBehind, "tdf159158_zOrder_headerBehind.odt") +{ + // given a blue star (not marked as behind text) anchored in the header + // and an overlapping yellow rectangle anchored in the body text. + // (note that in ODT format the star is on top, but for DOCX format it must be behind (hidden) + uno::Reference<beans::XPropertySet> zOrder0(getShape(1), uno::UNO_QUERY); + uno::Reference<beans::XPropertySet> zOrder1(getShape(2), 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")); // higher + // I don't know why the star is the lowest order in ODT import (maybe header weirdness), + // but it certainly needs to be the lowest on docx round-trip (also for header weirdness) + CPPUNIT_ASSERT_EQUAL(OUString("StarInHeader"), getProperty<OUString>(zOrder0, "Name")); + CPPUNIT_ASSERT_EQUAL(OUString("RectangleInBody"), getProperty<OUString>(zOrder1,"Name")); +} + DECLARE_OOXMLEXPORT_TEST(testTdf155903, "tdf155903.odt") { // Without the accompanying fix in place, this test would have crashed, diff --git a/writerfilter/inc/dmapper/GraphicZOrderHelper.hxx b/writerfilter/inc/dmapper/GraphicZOrderHelper.hxx index c6a308ddf819..b5bb27eea81b 100644 --- a/writerfilter/inc/dmapper/GraphicZOrderHelper.hxx +++ b/writerfilter/inc/dmapper/GraphicZOrderHelper.hxx @@ -17,11 +17,15 @@ class GraphicZOrderHelper { public: void addItem(css::uno::Reference<css::beans::XPropertySet> const& props, - sal_Int32 relativeHeight); - sal_Int32 findZOrder(sal_Int32 relativeHeight, bool bOldStyle = false); + sal_Int64 relativeHeight); + + // must run adjustRelativeHeight before findZOrder - to set zOrder priorities + static void adjustRelativeHeight(sal_Int64& rRelativeHeight, bool bIsZIndex, bool bIsBehindText, + bool bIsInHeader); + sal_Int32 findZOrder(sal_Int64 relativeHeight, bool bOldStyle = false); private: - using Items = std::map<sal_Int32, css::uno::Reference<css::beans::XPropertySet>>; + using Items = std::map<sal_Int64, css::uno::Reference<css::beans::XPropertySet>>; Items m_items; }; diff --git a/writerfilter/source/dmapper/DomainMapper_Impl.cxx b/writerfilter/source/dmapper/DomainMapper_Impl.cxx index 45ff00958083..bbdfbd35c34a 100644 --- a/writerfilter/source/dmapper/DomainMapper_Impl.cxx +++ b/writerfilter/source/dmapper/DomainMapper_Impl.cxx @@ -4763,8 +4763,10 @@ void DomainMapper_Impl::PushShapeContext( const uno::Reference< drawing::XShape if (rProp.Name == "VML-Z-ORDER") { GraphicZOrderHelper& rZOrderHelper = m_rDMapper.graphicZOrderHelper(); - sal_Int32 zOrder(0); + sal_Int64 zOrder(0); rProp.Value >>= zOrder; + GraphicZOrderHelper::adjustRelativeHeight(zOrder, /*IsZIndex=*/true, + zOrder < 0, IsInHeaderFooter()); xShapePropertySet->setPropertyValue("ZOrder", uno::Any(rZOrderHelper.findZOrder(zOrder, /*LastDuplicateWins*/true))); rZOrderHelper.addItem(xShapePropertySet, zOrder); @@ -4808,8 +4810,10 @@ void DomainMapper_Impl::PushShapeContext( const uno::Reference< drawing::XShape if (rProp.Name == "VML-Z-ORDER") { GraphicZOrderHelper& rZOrderHelper = m_rDMapper.graphicZOrderHelper(); - sal_Int32 zOrder(0); + sal_Int64 zOrder(0); rProp.Value >>= zOrder; + GraphicZOrderHelper::adjustRelativeHeight(zOrder, /*IsZIndex=*/true, + zOrder < 0, IsInHeaderFooter()); xShapePropertySet->setPropertyValue("ZOrder", uno::Any(rZOrderHelper.findZOrder(zOrder, /*LastDuplicateWins*/true))); rZOrderHelper.addItem(xShapePropertySet, zOrder); diff --git a/writerfilter/source/dmapper/GraphicHelpers.cxx b/writerfilter/source/dmapper/GraphicHelpers.cxx index d01ac5be3800..32e13b04bec8 100644 --- a/writerfilter/source/dmapper/GraphicHelpers.cxx +++ b/writerfilter/source/dmapper/GraphicHelpers.cxx @@ -270,11 +270,65 @@ text::WrapTextMode WrapHandler::getWrapMode( ) const } -void GraphicZOrderHelper::addItem(uno::Reference<beans::XPropertySet> const& props, sal_Int32 const relativeHeight) +void GraphicZOrderHelper::addItem(uno::Reference<beans::XPropertySet> const& props, + sal_Int64 const relativeHeight) { m_items[ relativeHeight ] = props; } +void GraphicZOrderHelper::adjustRelativeHeight(sal_Int64& rRelativeHeight, bool bIsZIndex, + bool bIsBehindText, bool bIsInHeader) +{ + // zOrder can be defined either by z-index (VML) or by relativeHeight (DML). + // z-index indicates background with a negative value, + // while relativeHeight indicates background with behindDoc = true. + // + // In general, all z-index-defined shapes appear on top of relativeHeight graphics + // regardless of the value. + + // priority order + // above text: positive sal_Int32 z-index (opaque/in front of text) + // relativeHeight (represented here as a negative sal_Int32, but still opaque) + // behind body: negative sal_Int32 z-index (!opaque/in the background) + // behindText relativeHeight + // (in header) positive z-index + // (in header) relativeHeight + // (in header) negative z-index + // (in header) behindText + + const sal_Int64 nMaxUnsignedInt32 = SAL_MAX_UINT32; + if (!bIsInHeader) + { + if (bIsZIndex) + { + // this number is already fine + // positive values are the only positive values coming into zOrder, + // and negative values will be !opaque (below text) + } + else if (!bIsBehindText) + { + assert (rRelativeHeight < 0); + // this number is already fine - will be above text, but relativeHeight is negative + } + else + { + // reduce to negative level 1 to force below a negative z-index + rRelativeHeight -= nMaxUnsignedInt32; + } + } + else // bIsInHeader + { + if (bIsZIndex && !bIsBehindText) + rRelativeHeight -= nMaxUnsignedInt32 * 2; // reduce to negative level 2 + else if (!bIsBehindText) + rRelativeHeight -= nMaxUnsignedInt32 * 3; // reduce to negative level 3 + else if (bIsZIndex) + rRelativeHeight -= nMaxUnsignedInt32 * 4; // reduce to negative level 4 + else + rRelativeHeight -= nMaxUnsignedInt32 * 5; // reduce to negative level 5 + } +} + // The relativeHeight value in .docx is an arbitrary number, where only the relative ordering matters. // But in Writer, the z-order is index in 0..(numitems-1) range, so whenever a new item needs to be // added in the proper z-order, it is necessary to find the proper index. @@ -282,7 +336,7 @@ void GraphicZOrderHelper::addItem(uno::Reference<beans::XPropertySet> const& pro // The key to this function is that later on, when setPropertyValue("ZOrder", <returned sal_Int32>), // SW also automatically increments ALL zOrders >= the one returned for this fly. // Thus, getProperty PROP_Z_ORDER for relativeHeight "x" can return different values for itemZOrder. -sal_Int32 GraphicZOrderHelper::findZOrder( sal_Int32 relativeHeight, bool bOldStyle ) +sal_Int32 GraphicZOrderHelper::findZOrder(sal_Int64 relativeHeight, bool bOldStyle) { // std::map is iterated sorted by key auto it = std::find_if(m_items.cbegin(), m_items.cend(), diff --git a/writerfilter/source/dmapper/GraphicImport.cxx b/writerfilter/source/dmapper/GraphicImport.cxx index bee20740278f..386ac693d81b 100644 --- a/writerfilter/source/dmapper/GraphicImport.cxx +++ b/writerfilter/source/dmapper/GraphicImport.cxx @@ -210,7 +210,7 @@ public: sal_Int32 m_nTopPosition; bool m_bUseSimplePos; - std::optional<sal_Int32> m_oZOrder; + std::optional<sal_Int64> m_oZOrder; sal_Int16 m_nHoriOrient; sal_Int16 m_nHoriRelation; @@ -390,36 +390,19 @@ public: void applyZOrder(uno::Reference<beans::XPropertySet> const & xGraphicObjectProperties) const { - std::optional<sal_Int32> oZOrder = m_oZOrder; + std::optional<sal_Int64> oZOrder = m_oZOrder; if (m_rGraphicImportType == GraphicImportType::IMPORT_AS_DETECTED_INLINE && !m_rDomainMapper.IsInShape()) { - oZOrder = SAL_MIN_INT32; + oZOrder = SAL_MIN_INT64; } else if (!oZOrder) return; else { - // tdf#120760 Send objects with behinddoc=true to the back. - - // zOrder can be defined either by z-index or by relativeHeight. - // z-index indicates background with a negative value, - // while relativeHeight indicates background with BehindDoc = true. - // - // In general, all z-index-defined shapes appear on top of relativeHeight graphics - // regardless of the value. - // So we have to try to put all relativeHeights as far back as possible, - // and this has already partially happened because they were already made to be negative - // but now the behindDoc relativeHeights need to be forced to the very back. - // - // Subtract even more so behindDoc relativeHeights will be behind - // foreground relativeHeights and also behind all of the negative z-indexes - // (especially needed for IsInHeaderFooter, as EVERYTHING is forced to the background). - // - // relativeHeight already removed 0x1E00 0000, so can subtract another 0x6200 0000 const bool bBehindText = m_bBehindDoc && !m_bOpaque; - if (bBehindText) - oZOrder = *oZOrder - 0x62000000; + GraphicZOrderHelper::adjustRelativeHeight(*oZOrder, /*IsZIndex=*/false, bBehindText, + m_rDomainMapper.IsInHeaderFooter()); } // TODO: it is possible that RTF has been wrong all along as well. Always true here?