1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
10 #include <sal/config.h>
11 #include <config_oox.h>
12 #include <cppunit/TestAssert.h>
13 #include <cppunit/TestFixture.h>
14 #include <cppunit/extensions/HelperMacros.h>
15 #include <cppunit/plugin/TestPlugIn.h>
17 #include <com/sun/star/beans/PropertyValue.hpp>
19 #include <vcl/bitmapaccess.hxx>
20 #include <vcl/graph.hxx>
21 #include <vcl/graphicfilter.hxx>
22 #include <tools/stream.hxx>
23 #include <unotest/directories.hxx>
24 #include <comphelper/DirectoryHelper.hxx>
25 #include <comphelper/hash.hxx>
26 #include <unotools/ucbstreamhelper.hxx>
28 #include <impgraph.hxx>
38 class GraphicTest
: public CppUnit::TestFixture
44 void testUnloadedGraphic();
45 void testUnloadedGraphicLoading();
46 void testUnloadedGraphicWmf();
47 void testUnloadedGraphicAlpha();
48 void testUnloadedGraphicSizeUnit();
50 void testSwappingVectorGraphic();
51 void testSwappingPageNumber();
53 CPPUNIT_TEST_SUITE(GraphicTest
);
54 CPPUNIT_TEST(testUnloadedGraphic
);
55 CPPUNIT_TEST(testUnloadedGraphicLoading
);
56 CPPUNIT_TEST(testUnloadedGraphicWmf
);
57 CPPUNIT_TEST(testUnloadedGraphicAlpha
);
58 CPPUNIT_TEST(testUnloadedGraphicSizeUnit
);
59 CPPUNIT_TEST(testSwapping
);
60 CPPUNIT_TEST(testSwappingVectorGraphic
);
61 CPPUNIT_TEST(testSwappingPageNumber
);
62 CPPUNIT_TEST_SUITE_END();
65 GraphicTest::~GraphicTest()
72 BitmapEx
createBitmap(bool alpha
= false)
74 Bitmap
aBitmap(Size(120, 100), 24);
75 aBitmap
.Erase(COL_LIGHTRED
);
77 aBitmap
.SetPrefSize(Size(6000, 5000));
78 aBitmap
.SetPrefMapMode(MapMode(MapUnit::Map100thMM
));
82 sal_uInt8 uAlphaValue
= 0x80;
83 AlphaMask
aAlphaMask(Size(120, 100), &uAlphaValue
);
85 return BitmapEx(aBitmap
, aAlphaMask
);
89 return BitmapEx(aBitmap
);
93 void createBitmapAndExportForType(SvStream
& rStream
, std::u16string_view sType
, bool alpha
)
95 BitmapEx aBitmapEx
= createBitmap(alpha
);
97 uno::Sequence
<beans::PropertyValue
> aFilterData
;
98 GraphicFilter
& rGraphicFilter
= GraphicFilter::GetGraphicFilter();
99 sal_uInt16 nFilterFormat
= rGraphicFilter
.GetExportFormatNumberForShortName(sType
);
100 rGraphicFilter
.ExportGraphic(aBitmapEx
, "none", rStream
, nFilterFormat
, &aFilterData
);
102 rStream
.Seek(STREAM_SEEK_TO_BEGIN
);
105 Graphic
makeUnloadedGraphic(std::u16string_view sType
, bool alpha
= false)
107 SvMemoryStream aStream
;
108 GraphicFilter
& rGraphicFilter
= GraphicFilter::GetGraphicFilter();
109 createBitmapAndExportForType(aStream
, sType
, alpha
);
110 return rGraphicFilter
.ImportUnloadedGraphic(aStream
);
113 std::string
toHexString(const std::vector
<unsigned char>& a
)
115 std::stringstream aStrm
;
118 aStrm
<< std::setw(2) << std::setfill('0') << std::hex
<< static_cast<int>(i
);
124 std::unique_ptr
<SvStream
> createStream(OUString
const& rSwapFileURL
)
126 std::unique_ptr
<SvStream
> xStream
;
130 xStream
= ::utl::UcbStreamHelper::CreateStream(
131 rSwapFileURL
, StreamMode::READWRITE
| StreamMode::SHARE_DENYWRITE
);
133 catch (const css::uno::Exception
&)
140 std::vector
<unsigned char> calculateHash(std::unique_ptr
<SvStream
>& rStream
)
142 comphelper::Hash
aHashEngine(comphelper::HashType::SHA1
);
143 const sal_uInt32
nSize(rStream
->remainingSize());
144 std::vector
<sal_uInt8
> aData(nSize
);
145 aHashEngine
.update(aData
.data(), nSize
);
146 return aHashEngine
.finalize();
149 bool checkBitmap(Graphic
& rGraphic
)
153 Bitmap
aBitmap(rGraphic
.GetBitmapEx().GetBitmap());
155 Bitmap::ScopedReadAccess
pReadAccess(aBitmap
);
156 for (tools::Long y
= 0; y
< rGraphic
.GetSizePixel().Height(); y
++)
158 for (tools::Long x
= 0; x
< rGraphic
.GetSizePixel().Width(); x
++)
160 if (pReadAccess
->HasPalette())
162 sal_uInt32 nIndex
= pReadAccess
->GetPixelIndex(y
, x
);
163 Color aColor
= pReadAccess
->GetPaletteColor(nIndex
);
164 bResult
&= (aColor
== Color(0xff, 0x00, 0x00));
168 Color aColor
= pReadAccess
->GetPixel(y
, x
);
169 bResult
&= (aColor
== Color(0xff, 0x00, 0x00));
178 char const DATA_DIRECTORY
[] = "/vcl/qa/cppunit/data/";
179 char const PDFEXPORT_DATA_DIRECTORY
[] = "/vcl/qa/cppunit/pdfexport/data/";
181 void GraphicTest::testUnloadedGraphic()
183 // make unloaded test graphic
184 Graphic aGraphic
= makeUnloadedGraphic(u
"png");
185 Graphic aGraphic2
= aGraphic
;
188 CPPUNIT_ASSERT_EQUAL(false, aGraphic
.isAvailable());
189 CPPUNIT_ASSERT_EQUAL(false, aGraphic2
.isAvailable());
191 CPPUNIT_ASSERT_EQUAL(true, aGraphic2
.makeAvailable());
192 CPPUNIT_ASSERT_EQUAL(true, aGraphic
.isAvailable());
193 CPPUNIT_ASSERT_EQUAL(true, aGraphic2
.isAvailable());
195 // check GetSizePixel doesn't load graphic
196 aGraphic
= makeUnloadedGraphic(u
"png");
197 CPPUNIT_ASSERT_EQUAL(false, aGraphic
.isAvailable());
198 CPPUNIT_ASSERT_EQUAL(tools::Long(120), aGraphic
.GetSizePixel().Width());
199 CPPUNIT_ASSERT_EQUAL(tools::Long(100), aGraphic
.GetSizePixel().Height());
200 CPPUNIT_ASSERT_EQUAL(false, aGraphic
.isAvailable());
202 // check GetPrefSize doesn't load graphic
203 CPPUNIT_ASSERT_EQUAL(tools::Long(6000), aGraphic
.GetPrefSize().Width());
204 CPPUNIT_ASSERT_EQUAL(tools::Long(5000), aGraphic
.GetPrefSize().Height());
205 CPPUNIT_ASSERT_EQUAL(false, aGraphic
.isAvailable());
207 // check GetSizeBytes loads graphic
208 CPPUNIT_ASSERT_EQUAL(false, aGraphic
.isAvailable());
209 CPPUNIT_ASSERT(aGraphic
.GetSizeBytes() > 0);
210 CPPUNIT_ASSERT_EQUAL(true, aGraphic
.isAvailable());
213 aGraphic
= makeUnloadedGraphic(u
"png");
214 CPPUNIT_ASSERT_EQUAL(false, aGraphic
.isAvailable());
215 CPPUNIT_ASSERT_EQUAL(GraphicType::Bitmap
, aGraphic
.GetType());
216 CPPUNIT_ASSERT_EQUAL(true, aGraphic
.makeAvailable());
217 CPPUNIT_ASSERT_EQUAL(true, aGraphic
.isAvailable());
218 CPPUNIT_ASSERT_EQUAL(GraphicType::Bitmap
, aGraphic
.GetType());
221 void GraphicTest::testUnloadedGraphicLoading()
223 const OUString aFormats
[] = { "png", "gif", "jpg" };
225 for (OUString
const& sFormat
: aFormats
)
227 Graphic aGraphic
= makeUnloadedGraphic(sFormat
);
230 CPPUNIT_ASSERT_EQUAL(false, aGraphic
.isAvailable());
231 CPPUNIT_ASSERT_EQUAL(tools::Long(120), aGraphic
.GetSizePixel().Width());
232 CPPUNIT_ASSERT_EQUAL(tools::Long(100), aGraphic
.GetSizePixel().Height());
233 CPPUNIT_ASSERT_EQUAL(false, aGraphic
.isAvailable());
234 CPPUNIT_ASSERT(aGraphic
.GetSizeBytes() > 0);
235 CPPUNIT_ASSERT_EQUAL(true, aGraphic
.isAvailable());
237 if (sFormat
!= "jpg")
238 CPPUNIT_ASSERT_EQUAL(true, checkBitmap(aGraphic
));
242 void GraphicTest::testUnloadedGraphicWmf()
244 // Create some in-memory WMF data, set its own preferred size to 99x99.
245 BitmapEx aBitmapEx
= createBitmap();
246 SvMemoryStream aStream
;
247 GraphicFilter
& rGraphicFilter
= GraphicFilter::GetGraphicFilter();
248 sal_uInt16 nFilterFormat
= rGraphicFilter
.GetExportFormatNumberForShortName(u
"wmf");
249 Graphic
aGraphic(aBitmapEx
);
250 aGraphic
.SetPrefSize(Size(99, 99));
251 aGraphic
.SetPrefMapMode(MapMode(MapUnit::Map100thMM
));
252 rGraphicFilter
.ExportGraphic(aGraphic
, "none", aStream
, nFilterFormat
);
253 aStream
.Seek(STREAM_SEEK_TO_BEGIN
);
255 // Now lazy-load this WMF data, with a custom preferred size of 42x42.
256 Size
aMtfSize100(42, 42);
257 aGraphic
= rGraphicFilter
.ImportUnloadedGraphic(aStream
, 0, &aMtfSize100
);
258 aGraphic
.makeAvailable();
260 // Without the accompanying fix in place, this test would have failed with:
263 // i.e. the custom preferred size was lost after lazy-load.
264 CPPUNIT_ASSERT_EQUAL(Size(42, 42), aGraphic
.GetPrefSize());
267 void GraphicTest::testUnloadedGraphicAlpha()
269 // make unloaded test graphic with alpha
270 Graphic aGraphic
= makeUnloadedGraphic(u
"png", true);
272 CPPUNIT_ASSERT_EQUAL(true, aGraphic
.IsAlpha());
273 CPPUNIT_ASSERT_EQUAL(true, aGraphic
.IsTransparent());
274 CPPUNIT_ASSERT_EQUAL(false, aGraphic
.isAvailable());
276 // make unloaded test graphic without alpha
277 aGraphic
= makeUnloadedGraphic(u
"png", false);
279 CPPUNIT_ASSERT_EQUAL(false, aGraphic
.IsAlpha());
280 CPPUNIT_ASSERT_EQUAL(false, aGraphic
.IsTransparent());
281 CPPUNIT_ASSERT_EQUAL(false, aGraphic
.isAvailable());
284 void GraphicTest::testUnloadedGraphicSizeUnit()
286 GraphicFilter
& rGraphicFilter
= GraphicFilter::GetGraphicFilter();
287 test::Directories aDirectories
;
288 OUString aURL
= aDirectories
.getURLFromSrc(DATA_DIRECTORY
) + "inch-size.emf";
289 Size
aMtfSize100(42, 42);
290 SvFileStream
aStream(aURL
, StreamMode::READ
);
291 Graphic aGraphic
= rGraphicFilter
.ImportUnloadedGraphic(aStream
, 0, &aMtfSize100
);
292 aGraphic
.makeAvailable();
294 // Without the accompanying fix in place, this test would have failed with:
295 // - Expected: 400x363
297 // i.e. a mm100 size was used as a hint and the inch size was set for a non-matching unit.
298 CPPUNIT_ASSERT_EQUAL(Size(400, 363), aGraphic
.GetPrefSize());
301 void GraphicTest::testSwapping()
303 // Prepare Graphic from a PNG image first
304 Graphic aGraphic
= makeUnloadedGraphic(u
"png");
306 CPPUNIT_ASSERT_EQUAL(GraphicType::Bitmap
, aGraphic
.GetType());
307 CPPUNIT_ASSERT_EQUAL(true, aGraphic
.makeAvailable());
308 CPPUNIT_ASSERT_EQUAL(true, aGraphic
.isAvailable());
310 CPPUNIT_ASSERT_EQUAL(tools::Long(120), aGraphic
.GetSizePixel().Width());
311 CPPUNIT_ASSERT_EQUAL(tools::Long(100), aGraphic
.GetSizePixel().Height());
313 BitmapChecksum aChecksumBeforeSwapping
= aGraphic
.GetChecksum();
315 CPPUNIT_ASSERT_EQUAL(sal_uInt32(319), aGraphic
.GetGfxLink().GetDataSize());
317 // We loaded the Graphic and made it available
318 CPPUNIT_ASSERT_EQUAL(false, aGraphic
.ImplGetImpGraphic()->isSwappedOut());
319 // Get the declared byte size of the graphic
320 sal_uLong rByteSize
= aGraphic
.GetSizeBytes();
321 OUString rSwapFileURL
= aGraphic
.ImplGetImpGraphic()->getSwapFileURL();
322 CPPUNIT_ASSERT_EQUAL(true, rSwapFileURL
.isEmpty());
325 CPPUNIT_ASSERT_EQUAL(true, aGraphic
.ImplGetImpGraphic()->swapOut());
326 CPPUNIT_ASSERT_EQUAL(true, aGraphic
.ImplGetImpGraphic()->isSwappedOut());
327 CPPUNIT_ASSERT_EQUAL(false, aGraphic
.isAvailable());
329 // Byte size doesn't change when we swapped out
330 CPPUNIT_ASSERT_EQUAL(rByteSize
, aGraphic
.GetSizeBytes());
332 // Let's check the swap file
333 rSwapFileURL
= aGraphic
.ImplGetImpGraphic()->getSwapFileURL();
334 CPPUNIT_ASSERT_EQUAL(true, comphelper::DirectoryHelper::fileExists(rSwapFileURL
));
336 { // Check the swap file content
337 std::unique_ptr
<SvStream
> xStream
= createStream(rSwapFileURL
);
338 CPPUNIT_ASSERT_EQUAL(true, bool(xStream
));
340 // Check size of the stream
341 CPPUNIT_ASSERT_EQUAL(sal_uInt64(449), xStream
->remainingSize());
343 std::vector
<unsigned char> aHash
= calculateHash(xStream
);
344 CPPUNIT_ASSERT_EQUAL(std::string("878281e583487b29ae09078e8040c01791c7649a"),
349 CPPUNIT_ASSERT_EQUAL(false, aGraphic
.isAvailable());
350 CPPUNIT_ASSERT_EQUAL(true, aGraphic
.makeAvailable());
351 CPPUNIT_ASSERT_EQUAL(true, aGraphic
.isAvailable());
352 CPPUNIT_ASSERT_EQUAL(false, aGraphic
.ImplGetImpGraphic()->isSwappedOut());
354 CPPUNIT_ASSERT_EQUAL(aChecksumBeforeSwapping
, aGraphic
.GetChecksum());
356 // File shouldn't be available anymore
357 CPPUNIT_ASSERT_EQUAL(false, comphelper::DirectoryHelper::fileExists(rSwapFileURL
));
360 CPPUNIT_ASSERT_EQUAL(tools::Long(120), aGraphic
.GetSizePixel().Width());
361 CPPUNIT_ASSERT_EQUAL(tools::Long(100), aGraphic
.GetSizePixel().Height());
362 CPPUNIT_ASSERT_EQUAL(true, checkBitmap(aGraphic
));
363 CPPUNIT_ASSERT_EQUAL(true, checkBitmap(aGraphic
));
366 void GraphicTest::testSwappingVectorGraphic()
368 test::Directories aDirectories
;
369 OUString aURL
= aDirectories
.getURLFromSrc(DATA_DIRECTORY
) + "SimpleExample.svg";
370 SvFileStream
aStream(aURL
, StreamMode::READ
);
371 GraphicFilter
& rGraphicFilter
= GraphicFilter::GetGraphicFilter();
372 Graphic aGraphic
= rGraphicFilter
.ImportUnloadedGraphic(aStream
);
374 CPPUNIT_ASSERT_EQUAL(GraphicType::Bitmap
, aGraphic
.GetType());
375 CPPUNIT_ASSERT_EQUAL(false, aGraphic
.isAvailable());
377 // Load the vector graphic
378 CPPUNIT_ASSERT_EQUAL(true, bool(aGraphic
.getVectorGraphicData()));
379 CPPUNIT_ASSERT_EQUAL(sal_uInt32(223),
380 aGraphic
.getVectorGraphicData()->getVectorGraphicDataArrayLength());
381 CPPUNIT_ASSERT_EQUAL(true, aGraphic
.isAvailable());
382 CPPUNIT_ASSERT_EQUAL(sal_uInt32(223), aGraphic
.GetGfxLink().GetDataSize());
383 CPPUNIT_ASSERT_EQUAL(true, aGraphic
.isAvailable());
385 BitmapChecksum aBitmapChecksumBeforeSwapping
= aGraphic
.GetBitmapEx().GetChecksum();
387 CPPUNIT_ASSERT_EQUAL(false, aGraphic
.ImplGetImpGraphic()->isSwappedOut());
389 // Get the declared byte size of the graphic
390 sal_uLong rByteSize
= aGraphic
.GetSizeBytes();
391 CPPUNIT_ASSERT_EQUAL(sal_uLong(223), rByteSize
);
392 OUString rSwapFileURL
= aGraphic
.ImplGetImpGraphic()->getSwapFileURL();
393 CPPUNIT_ASSERT_EQUAL(true, rSwapFileURL
.isEmpty());
396 CPPUNIT_ASSERT_EQUAL(true, aGraphic
.ImplGetImpGraphic()->swapOut());
397 CPPUNIT_ASSERT_EQUAL(true, aGraphic
.ImplGetImpGraphic()->isSwappedOut());
398 CPPUNIT_ASSERT_EQUAL(false, aGraphic
.isAvailable());
400 // Byte size doesn't change when we swapped out
401 // TODO: In case we don't trigger GetBitmapEx (above) the size is 0
402 CPPUNIT_ASSERT_EQUAL(rByteSize
, aGraphic
.GetSizeBytes());
404 // Let's check the swap file
405 rSwapFileURL
= aGraphic
.ImplGetImpGraphic()->getSwapFileURL();
406 CPPUNIT_ASSERT_EQUAL(true, comphelper::DirectoryHelper::fileExists(rSwapFileURL
));
408 { // Check the swap file content
409 std::unique_ptr
<SvStream
> xStream
= createStream(rSwapFileURL
);
410 CPPUNIT_ASSERT_EQUAL(true, bool(xStream
));
412 // Check size of the stream
413 CPPUNIT_ASSERT_EQUAL(sal_uInt64(353), xStream
->remainingSize());
415 std::vector
<unsigned char> aHash
= calculateHash(xStream
);
416 CPPUNIT_ASSERT_EQUAL(std::string("6ae83fc9c06ca253ada0b156d6e4700a4a028c34"),
421 CPPUNIT_ASSERT_EQUAL(false, aGraphic
.isAvailable());
422 CPPUNIT_ASSERT_EQUAL(true, aGraphic
.makeAvailable());
423 CPPUNIT_ASSERT_EQUAL(true, aGraphic
.isAvailable());
424 CPPUNIT_ASSERT_EQUAL(false, aGraphic
.ImplGetImpGraphic()->isSwappedOut());
426 CPPUNIT_ASSERT_EQUAL(aBitmapChecksumBeforeSwapping
, aGraphic
.GetBitmapEx().GetChecksum());
428 // File shouldn't be available anymore
429 CPPUNIT_ASSERT_EQUAL(false, comphelper::DirectoryHelper::fileExists(rSwapFileURL
));
432 void GraphicTest::testSwappingPageNumber()
434 test::Directories aDirectories
;
435 OUString aURL
= aDirectories
.getURLFromSrc(PDFEXPORT_DATA_DIRECTORY
) + "SimpleMultiPagePDF.pdf";
436 SvFileStream
aStream(aURL
, StreamMode::READ
);
437 GraphicFilter
& rGraphicFilter
= GraphicFilter::GetGraphicFilter();
438 Graphic aGraphic
= rGraphicFilter
.ImportUnloadedGraphic(aStream
);
440 CPPUNIT_ASSERT_EQUAL(GraphicType::Bitmap
, aGraphic
.GetType());
441 CPPUNIT_ASSERT_EQUAL(false, aGraphic
.isAvailable());
443 // Load the vector graphic
444 CPPUNIT_ASSERT_EQUAL(true, bool(aGraphic
.getVectorGraphicData()));
445 // Set the page index
446 aGraphic
.getVectorGraphicData()->setPageIndex(1);
448 CPPUNIT_ASSERT_EQUAL(VectorGraphicDataType::Pdf
,
449 aGraphic
.getVectorGraphicData()->getVectorGraphicDataType());
450 CPPUNIT_ASSERT_EQUAL(sal_uInt32(17693),
451 aGraphic
.getVectorGraphicData()->getVectorGraphicDataArrayLength());
452 CPPUNIT_ASSERT_EQUAL(true, aGraphic
.isAvailable());
453 CPPUNIT_ASSERT_EQUAL(sal_Int32(1), aGraphic
.getVectorGraphicData()->getPageIndex());
455 CPPUNIT_ASSERT_EQUAL(false, aGraphic
.ImplGetImpGraphic()->isSwappedOut());
458 CPPUNIT_ASSERT_EQUAL(true, aGraphic
.ImplGetImpGraphic()->swapOut());
459 CPPUNIT_ASSERT_EQUAL(true, aGraphic
.ImplGetImpGraphic()->isSwappedOut());
460 CPPUNIT_ASSERT_EQUAL(false, aGraphic
.isAvailable());
463 CPPUNIT_ASSERT_EQUAL(false, aGraphic
.isAvailable());
464 CPPUNIT_ASSERT_EQUAL(true, aGraphic
.makeAvailable());
465 CPPUNIT_ASSERT_EQUAL(true, aGraphic
.isAvailable());
466 CPPUNIT_ASSERT_EQUAL(false, aGraphic
.ImplGetImpGraphic()->isSwappedOut());
468 CPPUNIT_ASSERT_EQUAL(sal_Int32(1), aGraphic
.getVectorGraphicData()->getPageIndex());
473 CPPUNIT_TEST_SUITE_REGISTRATION(GraphicTest
);
475 CPPUNIT_PLUGIN_IMPLEMENT();
477 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */