comphelper/qa/unit/test_hash.cxx
| 26 ---
comphelper/source/misc/hash.cxx
| 13 +
include/comphelper/hash.hxx
| 2
sd/qa/unit/tiledrendering/data/SlideRenderingTest_Animations_DifferentKindOfTextBox.odp
|binary
sd/qa/unit/tiledrendering/tiledrendering.cxx
| 82 ++++++++++
sd/source/ui/tools/SlideshowLayerRenderer.cxx
| 25 +--
6 files changed, 118 insertions(+), 30 deletions(-)
New commits:
commit 9fef2827aa234c893ec5cc2d608181890e24cdd1
Author: Tomaž Vajngerl <[email protected]>
AuthorDate: Wed Oct 16 09:30:11 2024 +0200
Commit: Szymon Kłos <[email protected]>
CommitDate: Mon Oct 21 10:33:30 2024 +0200
slideshow: fix para. rendering when using SdrBlockTextPrimitive2D
If using an empty ViewInformation, it is possible that the prim.
decomposition is redone, when a different ViewInformation is used.
This removes all visibility flags for the paragraphs, so the whole
text is rendered. This fixes the issue by using the same instance
of the ViewInformation that is also used later for rendering the
primitives to the VirtualDevice, so the buffered decomposition is
used.
Also a test is added - it checks the hash of the buffer, which
should be different for all 3 layers. For this the tostring has
to be moved to the common hash.hxx, so we can reuse it in out
tests.
Change-Id: I4c951215a52e302d3b7b60a30c1b995002e53a4b
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/174989
Tested-by: Jenkins CollaboraOffice <[email protected]>
Reviewed-by: Szymon Kłos <[email protected]>
diff --git a/comphelper/qa/unit/test_hash.cxx b/comphelper/qa/unit/test_hash.cxx
index 64815ee56dc8..d5e8be2646b0 100644
--- a/comphelper/qa/unit/test_hash.cxx
+++ b/comphelper/qa/unit/test_hash.cxx
@@ -13,7 +13,6 @@
#include <comphelper/docpasswordhelper.hxx>
#include <rtl/ustring.hxx>
-#include <iomanip>
#include <cppunit/TestFixture.h>
#include <cppunit/extensions/HelperMacros.h>
@@ -48,21 +47,6 @@ public:
CPPUNIT_TEST_SUITE_END();
};
-namespace {
-
-std::string tostring(const std::vector<unsigned char>& a)
-{
- std::stringstream aStrm;
- for (auto& i:a)
- {
- aStrm << std::setw(2) << std::setfill('0') << std::hex <<
static_cast<int>(i);
- }
-
- return aStrm.str();
-}
-
-}
-
void TestHash::testMD5()
{
comphelper::Hash aHash(comphelper::HashType::MD5);
@@ -70,7 +54,7 @@ void TestHash::testMD5()
aHash.update(reinterpret_cast<const unsigned char*>(pInput), 0);
std::vector<unsigned char> calculate_hash = aHash.finalize();
CPPUNIT_ASSERT_EQUAL(size_t(16), calculate_hash.size());
- CPPUNIT_ASSERT_EQUAL(std::string("d41d8cd98f00b204e9800998ecf8427e"),
tostring(calculate_hash));
+ CPPUNIT_ASSERT_EQUAL(std::string("d41d8cd98f00b204e9800998ecf8427e"),
comphelper::hashToString(calculate_hash));
}
void TestHash::testSHA1()
@@ -80,7 +64,7 @@ void TestHash::testSHA1()
aHash.update(reinterpret_cast<const unsigned char*>(pInput), 0);
std::vector<unsigned char> calculate_hash = aHash.finalize();
CPPUNIT_ASSERT_EQUAL(size_t(20), calculate_hash.size());
-
CPPUNIT_ASSERT_EQUAL(std::string("da39a3ee5e6b4b0d3255bfef95601890afd80709"),
tostring(calculate_hash));
+
CPPUNIT_ASSERT_EQUAL(std::string("da39a3ee5e6b4b0d3255bfef95601890afd80709"),
comphelper::hashToString(calculate_hash));
}
void TestHash::testSHA256()
@@ -90,7 +74,7 @@ void TestHash::testSHA256()
aHash.update(reinterpret_cast<const unsigned char*>(pInput), 0);
std::vector<unsigned char> calculate_hash = aHash.finalize();
CPPUNIT_ASSERT_EQUAL(size_t(32), calculate_hash.size());
-
CPPUNIT_ASSERT_EQUAL(std::string("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"),
tostring(calculate_hash));
+
CPPUNIT_ASSERT_EQUAL(std::string("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"),
comphelper::hashToString(calculate_hash));
}
void TestHash::testSHA512()
@@ -101,7 +85,7 @@ void TestHash::testSHA512()
std::vector<unsigned char> calculate_hash = aHash.finalize();
CPPUNIT_ASSERT_EQUAL(size_t(64), calculate_hash.size());
std::string
aStr("cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e");
- CPPUNIT_ASSERT_EQUAL(aStr, tostring(calculate_hash));
+ CPPUNIT_ASSERT_EQUAL(aStr, comphelper::hashToString(calculate_hash));
}
// Must be identical to testSHA512()
@@ -113,7 +97,7 @@ void TestHash::testSHA512_NoSaltNoSpin()
nullptr, 0, 0, comphelper::Hash::IterCount::NONE,
comphelper::HashType::SHA512);
CPPUNIT_ASSERT_EQUAL(size_t(64), calculate_hash.size());
std::string
aStr("cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e");
- CPPUNIT_ASSERT_EQUAL(aStr, tostring(calculate_hash));
+ CPPUNIT_ASSERT_EQUAL(aStr, comphelper::hashToString(calculate_hash));
}
// Password, salt, hash and spin count taken from OOXML sheetProtection of
diff --git a/comphelper/source/misc/hash.cxx b/comphelper/source/misc/hash.cxx
index 25b93ad87e54..96e125cac23d 100644
--- a/comphelper/source/misc/hash.cxx
+++ b/comphelper/source/misc/hash.cxx
@@ -15,6 +15,8 @@
#include <rtl/alloc.h>
#include <osl/endian.h>
#include <config_oox.h>
+#include <sstream>
+#include <iomanip>
#if USE_TLS_NSS
#include <nss.h>
@@ -27,6 +29,17 @@
namespace comphelper {
+std::string hashToString(const std::vector<unsigned char>& rHash)
+{
+ std::stringstream aStringStream;
+ for (auto& i: rHash)
+ {
+ aStringStream << std::setw(2) << std::setfill('0') << std::hex <<
int(i);
+ }
+
+ return aStringStream.str();
+}
+
struct HashImpl
{
diff --git a/include/comphelper/hash.hxx b/include/comphelper/hash.hxx
index a3ad468d3eb5..1007e8dfa464 100644
--- a/include/comphelper/hash.hxx
+++ b/include/comphelper/hash.hxx
@@ -39,6 +39,8 @@ const sal_uInt32 SHA512_HASH_LENGTH = 64;
struct HashImpl;
+COMPHELPER_DLLPUBLIC std::string hashToString(const std::vector<unsigned
char>& rHash);
+
class COMPHELPER_DLLPUBLIC Hash
{
private:
diff --git
a/sd/qa/unit/tiledrendering/data/SlideRenderingTest_Animations_DifferentKindOfTextBox.odp
b/sd/qa/unit/tiledrendering/data/SlideRenderingTest_Animations_DifferentKindOfTextBox.odp
new file mode 100644
index 000000000000..47777662805f
Binary files /dev/null and
b/sd/qa/unit/tiledrendering/data/SlideRenderingTest_Animations_DifferentKindOfTextBox.odp
differ
diff --git a/sd/qa/unit/tiledrendering/tiledrendering.cxx
b/sd/qa/unit/tiledrendering/tiledrendering.cxx
index 0003f5378d9a..8442f9332c79 100644
--- a/sd/qa/unit/tiledrendering/tiledrendering.cxx
+++ b/sd/qa/unit/tiledrendering/tiledrendering.cxx
@@ -18,6 +18,7 @@
#include <comphelper/propertysequence.hxx>
#include <comphelper/propertyvalue.hxx>
#include <comphelper/string.hxx>
+#include <comphelper/hash.hxx>
#include <editeng/eeitem.hxx>
#include <editeng/editids.hrc>
#include <editeng/editobj.hxx>
@@ -3801,6 +3802,87 @@ CPPUNIT_TEST_FIXTURE(SdTiledRenderingTest,
testSlideshowLayeredRendering_Animati
pXImpressDocument->postSlideshowCleanup();
}
+namespace
+{
+template <typename T>
+bool is_unique(std::vector<T> vec)
+{
+ std::sort(vec.begin(), vec.end());
+ return std::unique(vec.begin(), vec.end()) == vec.end();
+}
+}
+
+CPPUNIT_TEST_FIXTURE(SdTiledRenderingTest,
testSlideshowLayeredRendering_Animation_DifferentKindOfTextBox)
+{
+ SdXImpressDocument* pXImpressDocument =
createDoc("SlideRenderingTest_Animations_DifferentKindOfTextBox.odp");
+
pXImpressDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
+ sd::ViewShell* pViewShell =
pXImpressDocument->GetDocShell()->GetViewShell();
+ CPPUNIT_ASSERT(pViewShell);
+ SdPage* pPage = pViewShell->GetActualPage();
+ CPPUNIT_ASSERT(pPage);
+ sal_Int32 nViewWidth = 2000;
+ sal_Int32 nViewHeight = 2000;
+
+ std::string sHash = GetInterfaceHash(GetXDrawPageForSdrPage(pPage));
+ CPPUNIT_ASSERT(pXImpressDocument->createSlideRenderer(sHash.c_str(), 0,
nViewWidth, nViewHeight, true, true));
+ CPPUNIT_ASSERT_EQUAL(2000, nViewWidth);
+ CPPUNIT_ASSERT_EQUAL(1500, nViewHeight);
+
+ std::vector<std::string> aHashes;
+
+ {
+ std::vector<sal_uInt8> pBuffer(nViewWidth * nViewHeight * 4);
+ bool bIsBitmapLayer = false;
+ OUString rJsonMsg;
+
CPPUNIT_ASSERT(!pXImpressDocument->renderNextSlideLayer(pBuffer.data(),
bIsBitmapLayer, rJsonMsg));
+
+ // Remember the hash of the buffer for uniqueness check
+ auto aBufferHash = comphelper::Hash::calculateHash(pBuffer.data(),
pBuffer.size(), comphelper::HashType::SHA1);
+ aHashes.push_back(comphelper::hashToString(aBufferHash));
+
+ debugWriteImageToFile(1, pBuffer, nViewWidth, nViewHeight,
rJsonMsg.toUtf8().getStr());
+ }
+
+ {
+ std::vector<sal_uInt8> pBuffer(nViewWidth * nViewHeight * 4);
+ bool bIsBitmapLayer = false;
+ OUString rJsonMsg;
+
CPPUNIT_ASSERT(!pXImpressDocument->renderNextSlideLayer(pBuffer.data(),
bIsBitmapLayer, rJsonMsg));
+
+ // Remember the hash of the buffer for uniqueness check
+ auto aBufferHash = comphelper::Hash::calculateHash(pBuffer.data(),
pBuffer.size(), comphelper::HashType::SHA1);
+ aHashes.push_back(comphelper::hashToString(aBufferHash));
+
+ debugWriteImageToFile(2, pBuffer, nViewWidth, nViewHeight,
rJsonMsg.toUtf8().getStr());
+ }
+
+ {
+ std::vector<sal_uInt8> pBuffer(nViewWidth * nViewHeight * 4);
+ bool bIsBitmapLayer = false;
+ OUString rJsonMsg;
+
CPPUNIT_ASSERT(!pXImpressDocument->renderNextSlideLayer(pBuffer.data(),
bIsBitmapLayer, rJsonMsg));
+
+ // Remember the hash of the buffer for uniqueness check
+ auto aBufferHash = comphelper::Hash::calculateHash(pBuffer.data(),
pBuffer.size(), comphelper::HashType::SHA1);
+ aHashes.push_back(comphelper::hashToString(aBufferHash));
+
+ debugWriteImageToFile(3, pBuffer, nViewWidth, nViewHeight,
rJsonMsg.toUtf8().getStr());
+ }
+
+ // check if all hashes are unique
+ CPPUNIT_ASSERT(is_unique(aHashes));
+
+ // Check we are done
+ {
+ std::vector<sal_uInt8> pBuffer(nViewWidth * nViewHeight * 4);
+ bool bIsBitmapLayer = false;
+ OUString rJsonMsg;
+ CPPUNIT_ASSERT(pXImpressDocument->renderNextSlideLayer(pBuffer.data(),
bIsBitmapLayer, rJsonMsg));
+ }
+
+ pXImpressDocument->postSlideshowCleanup();
+}
+
CPPUNIT_PLUGIN_IMPLEMENT();
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sd/source/ui/tools/SlideshowLayerRenderer.cxx
b/sd/source/ui/tools/SlideshowLayerRenderer.cxx
index 39adde615331..6ec95a2030be 100644
--- a/sd/source/ui/tools/SlideshowLayerRenderer.cxx
+++ b/sd/source/ui/tools/SlideshowLayerRenderer.cxx
@@ -139,7 +139,8 @@ void
changeBackground(drawinglayer::primitive2d::Primitive2DContainer const& rCo
}
drawinglayer::primitive2d::TextHierarchyBlockPrimitive2D*
-findTextBlock(drawinglayer::primitive2d::Primitive2DContainer const&
rContainer)
+findTextBlock(drawinglayer::primitive2d::Primitive2DContainer const&
rContainer,
+ drawinglayer::geometry::ViewInformation2D const&
rViewInformation2D)
{
for (size_t i = 0; i < rContainer.size(); i++)
{
@@ -158,7 +159,8 @@
findTextBlock(drawinglayer::primitive2d::Primitive2DContainer const& rContainer)
=
dynamic_cast<drawinglayer::primitive2d::GroupPrimitive2D*>(pBasePrimitive);
if (pGroupPrimitive)
{
- auto* pTextBlock =
findTextBlock(pGroupPrimitive->getChildren());
+ auto* pTextBlock
+ = findTextBlock(pGroupPrimitive->getChildren(),
rViewInformation2D);
if (pTextBlock)
return pTextBlock;
}
@@ -170,9 +172,8 @@
findTextBlock(drawinglayer::primitive2d::Primitive2DContainer const& rContainer)
{
// try to decompose
drawinglayer::primitive2d::Primitive2DContainer
aPrimitiveContainer;
- pBasePrimitive->get2DDecomposition(aPrimitiveContainer,
-
drawinglayer::geometry::ViewInformation2D());
- auto* pTextBlock = findTextBlock(aPrimitiveContainer);
+ pBasePrimitive->get2DDecomposition(aPrimitiveContainer,
rViewInformation2D);
+ auto* pTextBlock = findTextBlock(aPrimitiveContainer,
rViewInformation2D);
if (pTextBlock)
return pTextBlock;
}
@@ -182,9 +183,10 @@
findTextBlock(drawinglayer::primitive2d::Primitive2DContainer const& rContainer)
}
void modifyParagraphs(drawinglayer::primitive2d::Primitive2DContainer&
rContainer,
+ drawinglayer::geometry::ViewInformation2D const&
rViewInformation2D,
std::deque<sal_Int32> const& rPreserveIndices, bool
bRenderObject)
{
- auto* pTextBlock = findTextBlock(rContainer);
+ auto* pTextBlock = findTextBlock(rContainer, rViewInformation2D);
if (pTextBlock)
{
@@ -244,6 +246,10 @@ public:
return;
SdrObject* pObject = rOriginal.GetViewContact().TryToGetSdrObject();
+
+ drawinglayer::geometry::ViewInformation2D const& rViewInformation2D
+ = rOriginal.GetObjectContact().getViewInformation2D();
+
// Check if we are rendering an object that is valid to render
(exists, and not empty)
if (pObject == nullptr || pObject->IsEmptyPresObj())
return;
@@ -339,7 +345,8 @@ public:
if (bRenderOtherParagraphs)
{
- modifyParagraphs(rContainer, nOtherParagraphs, true); //
render the object
+ modifyParagraphs(rContainer, rViewInformation2D,
nOtherParagraphs,
+ true); // render the object
mrRenderState.mnCurrentTargetParagraph = -1;
}
else
@@ -349,8 +356,8 @@ public:
std::deque<sal_Int32> aPreserveParagraphs{ nParagraph };
mrRenderState.mnCurrentTargetParagraph = nParagraph;
- modifyParagraphs(rContainer, aPreserveParagraphs,
- false); // render only the paragraphs
+ // render only the paragraphs
+ modifyParagraphs(rContainer, rViewInformation2D,
aPreserveParagraphs, false);
}
}