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 <jluth@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 0000000..a4bf98a
--- /dev/null
+++ b/sw/qa/extras/ooxmlexport/data/tdf159158_zOrder_headerBehind.odt
Binary files differ
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport18.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport18.cxx
index 084863c..91fa26e 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 c6a308d..b5bb27e 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 45ff009..bbdfbd3 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 d01ac5b..32e13b0 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 bee2074..386ac69 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?