graphic: Rework swapping algorithm to take GfxLink into account

This reworks the Graphic swap algorithm to not swap to a file when
GfxLink is available and leaves the compressed data in memory.
With such a sheme, at swap-out we just remember the need specific
properties of the Graphic, and delete the graphic content (Bitmap,
Animation, VectorGraphic, Metafile). At swap-in use the GfxLink
data to decompress and recreate the graphic content, then set
the properties back as they were before (if needed).

If a GfxLink is not available it swaps out to a file and back, but
uses a simpler data format that is specific for swapping only. In
the future this case can be removed, when we switch to automatic
creation of GfxLink if that one is not present.

For reworking of swapping it was also necessary to extensively add
and extend the tests that check various swapping scenarios.

Change-Id: I135a224f74aa48e6006f48dd2be74b8026614728
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/107287
Tested-by: Jenkins
Reviewed-by: Tomaž Vajngerl <quikee@gmail.com>
This commit is contained in:
Tomaž Vajngerl
2020-12-06 21:14:07 +09:00
committed by Tomaž Vajngerl
parent 3dabbb6a5e
commit def31e135e
5 changed files with 926 additions and 458 deletions

View File

@@ -32,6 +32,11 @@ constexpr sal_uInt32 createMagic(char char1, char char2, char char3, char char4)
| (static_cast<sal_uInt32>(char3) << 8) | (static_cast<sal_uInt32>(char4) << 0); | (static_cast<sal_uInt32>(char3) << 8) | (static_cast<sal_uInt32>(char4) << 0);
} }
constexpr sal_uInt32 constSvgMagic = createMagic('s', 'v', 'g', '0');
constexpr sal_uInt32 constWmfMagic = createMagic('w', 'm', 'f', '0');
constexpr sal_uInt32 constEmfMagic = createMagic('e', 'm', 'f', '0');
constexpr sal_uInt32 constPdfMagic = createMagic('p', 'd', 'f', '0');
class VCL_DLLPUBLIC TypeSerializer : public tools::GenericTypeSerializer class VCL_DLLPUBLIC TypeSerializer : public tools::GenericTypeSerializer
{ {
public: public:

View File

@@ -47,6 +47,13 @@ class ImpSwapFile;
class GraphicConversionParameters; class GraphicConversionParameters;
class ImpGraphic; class ImpGraphic;
enum class GraphicContentType : sal_Int32
{
Bitmap,
Animation,
Vector
};
class VCL_DLLPUBLIC ImpGraphic final class VCL_DLLPUBLIC ImpGraphic final
{ {
friend class Graphic; friend class Graphic;
@@ -115,7 +122,9 @@ private:
return mpGraphicID->getIDString(); return mpGraphicID->getIDString();
} }
void createSwapInfo(); void createSwapInfo();
void restoreFromSwapInfo();
void ImplClearGraphics(); void ImplClearGraphics();
void ImplClear(); void ImplClear();
@@ -170,7 +179,7 @@ private:
private: private:
// swapping methods // swapping methods
bool swapInFromStream(SvStream& rStream); bool swapInFromStream(SvStream& rStream);
void swapInGraphic(SvStream& rStream); bool swapInGraphic(SvStream& rStream);
bool swapInContent(SvStream& rStream); bool swapInContent(SvStream& rStream);
bool swapOutContent(SvStream& rStream); bool swapOutContent(SvStream& rStream);
@@ -205,6 +214,7 @@ private:
sal_Int32 getPageNumber() const; sal_Int32 getPageNumber() const;
public: public:
void resetChecksum() { mnChecksum = 0; }
bool swapIn(); bool swapIn();
bool swapOut(); bool swapOut();
bool isSwappedOut() const { return mbSwapOut; } bool isSwappedOut() const { return mbSwapOut; }

View File

@@ -50,23 +50,50 @@ private:
void testUnloadedGraphicWmf(); void testUnloadedGraphicWmf();
void testUnloadedGraphicAlpha(); void testUnloadedGraphicAlpha();
void testUnloadedGraphicSizeUnit(); void testUnloadedGraphicSizeUnit();
void testSwapping();
void testSwappingVectorGraphic();
void testSwappingPageNumber();
void testWMFRoundtrip(); void testWMFRoundtrip();
void testEmfToWmfConversion(); void testEmfToWmfConversion();
void testSwappingGraphic_PNG_WithGfxLink();
void testSwappingGraphic_PNG_WithoutGfxLink();
void testSwappingGraphicProperties_PNG_WithGfxLink();
void testSwappingGraphicProperties_PNG_WithoutGfxLink();
void testSwappingVectorGraphic_SVG_WithGfxLink();
void testSwappingVectorGraphic_SVG_WithoutGfxLink();
void testSwappingGraphicProperties_SVG_WithGfxLink();
void testSwappingGraphicProperties_SVG_WithoutGfxLink();
void testSwappingVectorGraphic_PDF_WithGfxLink();
void testSwappingVectorGraphic_PDF_WithoutGfxLink();
void testSwappingAnimationGraphic_GIF_WithGfxLink();
void testSwappingAnimationGraphic_GIF_WithoutGfxLink();
CPPUNIT_TEST_SUITE(GraphicTest); CPPUNIT_TEST_SUITE(GraphicTest);
CPPUNIT_TEST(testUnloadedGraphic); CPPUNIT_TEST(testUnloadedGraphic);
CPPUNIT_TEST(testUnloadedGraphicLoading); CPPUNIT_TEST(testUnloadedGraphicLoading);
CPPUNIT_TEST(testUnloadedGraphicWmf); CPPUNIT_TEST(testUnloadedGraphicWmf);
CPPUNIT_TEST(testUnloadedGraphicAlpha); CPPUNIT_TEST(testUnloadedGraphicAlpha);
CPPUNIT_TEST(testUnloadedGraphicSizeUnit); CPPUNIT_TEST(testUnloadedGraphicSizeUnit);
CPPUNIT_TEST(testSwapping);
CPPUNIT_TEST(testSwappingVectorGraphic);
CPPUNIT_TEST(testSwappingPageNumber);
CPPUNIT_TEST(testWMFRoundtrip); CPPUNIT_TEST(testWMFRoundtrip);
CPPUNIT_TEST(testEmfToWmfConversion); CPPUNIT_TEST(testEmfToWmfConversion);
CPPUNIT_TEST(testSwappingGraphic_PNG_WithGfxLink);
CPPUNIT_TEST(testSwappingGraphic_PNG_WithoutGfxLink);
CPPUNIT_TEST(testSwappingGraphicProperties_PNG_WithGfxLink);
CPPUNIT_TEST(testSwappingGraphicProperties_PNG_WithoutGfxLink);
CPPUNIT_TEST(testSwappingVectorGraphic_SVG_WithGfxLink);
CPPUNIT_TEST(testSwappingVectorGraphic_SVG_WithoutGfxLink);
CPPUNIT_TEST(testSwappingGraphicProperties_SVG_WithGfxLink);
CPPUNIT_TEST(testSwappingGraphicProperties_SVG_WithoutGfxLink);
CPPUNIT_TEST(testSwappingVectorGraphic_PDF_WithGfxLink);
CPPUNIT_TEST(testSwappingVectorGraphic_PDF_WithoutGfxLink);
CPPUNIT_TEST(testSwappingAnimationGraphic_GIF_WithGfxLink);
CPPUNIT_TEST(testSwappingAnimationGraphic_GIF_WithoutGfxLink);
CPPUNIT_TEST_SUITE_END(); CPPUNIT_TEST_SUITE_END();
}; };
@@ -306,6 +333,33 @@ void GraphicTest::testUnloadedGraphicSizeUnit()
CPPUNIT_ASSERT_EQUAL(Size(400, 363), aGraphic.GetPrefSize()); CPPUNIT_ASSERT_EQUAL(Size(400, 363), aGraphic.GetPrefSize());
} }
void GraphicTest::testWMFRoundtrip()
{
// Load a WMF file.
test::Directories aDirectories;
OUString aURL = aDirectories.getURLFromSrc("vcl/qa/cppunit/data/roundtrip.wmf");
SvFileStream aStream(aURL, StreamMode::READ);
sal_uInt64 nExpectedSize = aStream.TellEnd();
GraphicFilter& rGraphicFilter = GraphicFilter::GetGraphicFilter();
Graphic aGraphic = rGraphicFilter.ImportUnloadedGraphic(aStream);
// Save as WMF.
utl::TempFile aTempFile;
aTempFile.EnableKillingFile();
sal_uInt16 nFormat = rGraphicFilter.GetExportFormatNumberForShortName(u"WMF");
SvStream& rOutStream = *aTempFile.GetStream(StreamMode::READWRITE);
rGraphicFilter.ExportGraphic(aGraphic, OUString(), rOutStream, nFormat);
// Check if we preserved the WMF data perfectly.
sal_uInt64 nActualSize = rOutStream.TellEnd();
// Without the accompanying fix in place, this test would have failed with:
// - Expected: 6475
// - Actual : 2826
// i.e. we lost some of the WMF data on roundtrip.
CPPUNIT_ASSERT_EQUAL(nExpectedSize, nActualSize);
}
void GraphicTest::testEmfToWmfConversion() void GraphicTest::testEmfToWmfConversion()
{ {
// Load EMF data. // Load EMF data.
@@ -363,7 +417,7 @@ void GraphicTest::testEmfToWmfConversion()
CPPUNIT_ASSERT_LESSEQUAL(4, nCommentCount); CPPUNIT_ASSERT_LESSEQUAL(4, nCommentCount);
} }
void GraphicTest::testSwapping() void GraphicTest::testSwappingGraphic_PNG_WithGfxLink()
{ {
// Prepare Graphic from a PNG image first // Prepare Graphic from a PNG image first
Graphic aGraphic = makeUnloadedGraphic(u"png"); Graphic aGraphic = makeUnloadedGraphic(u"png");
@@ -377,10 +431,64 @@ void GraphicTest::testSwapping()
BitmapChecksum aChecksumBeforeSwapping = aGraphic.GetChecksum(); BitmapChecksum aChecksumBeforeSwapping = aGraphic.GetChecksum();
CPPUNIT_ASSERT_EQUAL(true, aGraphic.IsGfxLink());
CPPUNIT_ASSERT_EQUAL(sal_uInt32(319), aGraphic.GetGfxLink().GetDataSize()); CPPUNIT_ASSERT_EQUAL(sal_uInt32(319), aGraphic.GetGfxLink().GetDataSize());
// We loaded the Graphic and made it available // We loaded the Graphic and made it available
CPPUNIT_ASSERT_EQUAL(false, aGraphic.ImplGetImpGraphic()->isSwappedOut()); CPPUNIT_ASSERT_EQUAL(false, aGraphic.ImplGetImpGraphic()->isSwappedOut());
// Get the declared byte size of the graphic
sal_uLong rByteSize = aGraphic.GetSizeBytes();
// Check the swap file (shouldn't exist)
CPPUNIT_ASSERT_EQUAL(true, aGraphic.ImplGetImpGraphic()->getSwapFileURL().isEmpty());
// Swapping out
CPPUNIT_ASSERT_EQUAL(true, aGraphic.ImplGetImpGraphic()->swapOut());
CPPUNIT_ASSERT_EQUAL(true, aGraphic.ImplGetImpGraphic()->isSwappedOut());
CPPUNIT_ASSERT_EQUAL(false, aGraphic.isAvailable());
// Byte size doesn't change when we swapped out
CPPUNIT_ASSERT_EQUAL(rByteSize, aGraphic.GetSizeBytes());
// Check the swap file (still shouldn't exist)
CPPUNIT_ASSERT_EQUAL(true, aGraphic.ImplGetImpGraphic()->getSwapFileURL().isEmpty());
// Let's swap in
CPPUNIT_ASSERT_EQUAL(false, aGraphic.isAvailable());
CPPUNIT_ASSERT_EQUAL(true, aGraphic.makeAvailable());
CPPUNIT_ASSERT_EQUAL(true, aGraphic.isAvailable());
CPPUNIT_ASSERT_EQUAL(false, aGraphic.ImplGetImpGraphic()->isSwappedOut());
CPPUNIT_ASSERT_EQUAL(aChecksumBeforeSwapping, aGraphic.GetChecksum());
// Check the bitmap
CPPUNIT_ASSERT_EQUAL(tools::Long(120), aGraphic.GetSizePixel().Width());
CPPUNIT_ASSERT_EQUAL(tools::Long(100), aGraphic.GetSizePixel().Height());
CPPUNIT_ASSERT_EQUAL(true, checkBitmap(aGraphic));
}
void GraphicTest::testSwappingGraphic_PNG_WithoutGfxLink()
{
// Prepare Graphic from a PNG image first
// Make sure to construct the Graphic from BitmapEx, so that we
// don't have the GfxLink present.
Graphic aGraphic(makeUnloadedGraphic(u"png").GetBitmapEx());
CPPUNIT_ASSERT_EQUAL(GraphicType::Bitmap, aGraphic.GetType());
CPPUNIT_ASSERT_EQUAL(true, aGraphic.makeAvailable());
CPPUNIT_ASSERT_EQUAL(true, aGraphic.isAvailable());
CPPUNIT_ASSERT_EQUAL(tools::Long(120), aGraphic.GetSizePixel().Width());
CPPUNIT_ASSERT_EQUAL(tools::Long(100), aGraphic.GetSizePixel().Height());
BitmapChecksum aChecksumBeforeSwapping = aGraphic.GetChecksum();
CPPUNIT_ASSERT_EQUAL(false, aGraphic.IsGfxLink());
// We loaded the Graphic and made it available
CPPUNIT_ASSERT_EQUAL(false, aGraphic.ImplGetImpGraphic()->isSwappedOut());
// Get the declared byte size of the graphic // Get the declared byte size of the graphic
sal_uLong rByteSize = aGraphic.GetSizeBytes(); sal_uLong rByteSize = aGraphic.GetSizeBytes();
OUString rSwapFileURL = aGraphic.ImplGetImpGraphic()->getSwapFileURL(); OUString rSwapFileURL = aGraphic.ImplGetImpGraphic()->getSwapFileURL();
@@ -403,19 +511,21 @@ void GraphicTest::testSwapping()
CPPUNIT_ASSERT_EQUAL(true, bool(xStream)); CPPUNIT_ASSERT_EQUAL(true, bool(xStream));
// Check size of the stream // Check size of the stream
CPPUNIT_ASSERT_EQUAL(sal_uInt64(449), xStream->remainingSize()); CPPUNIT_ASSERT_EQUAL(sal_uInt64(36079), xStream->remainingSize());
std::vector<unsigned char> aHash = calculateHash(xStream); std::vector<unsigned char> aHash = calculateHash(xStream);
CPPUNIT_ASSERT_EQUAL(std::string("878281e583487b29ae09078e8040c01791c7649a"), CPPUNIT_ASSERT_EQUAL(std::string("9347511e3b80dfdfaadf91a3bdef55a8ae85552b"),
toHexString(aHash)); toHexString(aHash));
} }
// Let's swap in // SWAP IN
CPPUNIT_ASSERT_EQUAL(false, aGraphic.isAvailable()); CPPUNIT_ASSERT_EQUAL(false, aGraphic.isAvailable());
CPPUNIT_ASSERT_EQUAL(true, aGraphic.makeAvailable()); CPPUNIT_ASSERT_EQUAL(true, aGraphic.makeAvailable());
CPPUNIT_ASSERT_EQUAL(true, aGraphic.isAvailable()); CPPUNIT_ASSERT_EQUAL(true, aGraphic.isAvailable());
CPPUNIT_ASSERT_EQUAL(false, aGraphic.ImplGetImpGraphic()->isSwappedOut()); CPPUNIT_ASSERT_EQUAL(false, aGraphic.ImplGetImpGraphic()->isSwappedOut());
// reset the checksum to make sure we don't get the cached value
aGraphic.ImplGetImpGraphic()->resetChecksum();
CPPUNIT_ASSERT_EQUAL(aChecksumBeforeSwapping, aGraphic.GetChecksum()); CPPUNIT_ASSERT_EQUAL(aChecksumBeforeSwapping, aGraphic.GetChecksum());
// File shouldn't be available anymore // File shouldn't be available anymore
@@ -424,28 +534,192 @@ void GraphicTest::testSwapping()
// Check the bitmap // Check the bitmap
CPPUNIT_ASSERT_EQUAL(tools::Long(120), aGraphic.GetSizePixel().Width()); CPPUNIT_ASSERT_EQUAL(tools::Long(120), aGraphic.GetSizePixel().Width());
CPPUNIT_ASSERT_EQUAL(tools::Long(100), aGraphic.GetSizePixel().Height()); CPPUNIT_ASSERT_EQUAL(tools::Long(100), aGraphic.GetSizePixel().Height());
CPPUNIT_ASSERT_EQUAL(true, checkBitmap(aGraphic));
CPPUNIT_ASSERT_EQUAL(true, checkBitmap(aGraphic)); CPPUNIT_ASSERT_EQUAL(true, checkBitmap(aGraphic));
} }
void GraphicTest::testSwappingVectorGraphic() void GraphicTest::testSwappingGraphicProperties_PNG_WithGfxLink()
{
// Prepare Graphic from a PNG image
Graphic aGraphic = makeUnloadedGraphic(u"png");
CPPUNIT_ASSERT_EQUAL(GraphicType::Bitmap, aGraphic.GetType());
CPPUNIT_ASSERT_EQUAL(true, aGraphic.makeAvailable());
CPPUNIT_ASSERT_EQUAL(true, aGraphic.isAvailable());
// Origin URL
aGraphic.setOriginURL("Origin URL");
CPPUNIT_ASSERT_EQUAL(OUString("Origin URL"), aGraphic.getOriginURL());
//Set PrefMapMode
CPPUNIT_ASSERT_EQUAL(MapUnit::Map100thMM, aGraphic.GetPrefMapMode().GetMapUnit());
aGraphic.SetPrefMapMode(MapMode(MapUnit::MapTwip));
CPPUNIT_ASSERT_EQUAL(MapUnit::MapTwip, aGraphic.GetPrefMapMode().GetMapUnit());
// Set the PrefSize
CPPUNIT_ASSERT_EQUAL(tools::Long(6000), aGraphic.GetPrefSize().Width());
CPPUNIT_ASSERT_EQUAL(tools::Long(5000), aGraphic.GetPrefSize().Height());
aGraphic.SetPrefSize(Size(200, 100));
CPPUNIT_ASSERT_EQUAL(tools::Long(200), aGraphic.GetPrefSize().Width());
CPPUNIT_ASSERT_EQUAL(tools::Long(100), aGraphic.GetPrefSize().Height());
// SWAP OUT
CPPUNIT_ASSERT_EQUAL(true, aGraphic.isAvailable());
CPPUNIT_ASSERT_EQUAL(true, aGraphic.ImplGetImpGraphic()->swapOut());
CPPUNIT_ASSERT_EQUAL(true, aGraphic.ImplGetImpGraphic()->isSwappedOut());
CPPUNIT_ASSERT_EQUAL(false, aGraphic.isAvailable());
// Check properties
CPPUNIT_ASSERT_EQUAL(OUString("Origin URL"), aGraphic.getOriginURL());
CPPUNIT_ASSERT_EQUAL(MapUnit::MapTwip, aGraphic.GetPrefMapMode().GetMapUnit());
CPPUNIT_ASSERT_EQUAL(tools::Long(200), aGraphic.GetPrefSize().Width());
CPPUNIT_ASSERT_EQUAL(tools::Long(100), aGraphic.GetPrefSize().Height());
// SWAP IN
CPPUNIT_ASSERT_EQUAL(false, aGraphic.isAvailable());
CPPUNIT_ASSERT_EQUAL(true, aGraphic.makeAvailable());
CPPUNIT_ASSERT_EQUAL(true, aGraphic.isAvailable());
CPPUNIT_ASSERT_EQUAL(false, aGraphic.ImplGetImpGraphic()->isSwappedOut());
// Check properties
CPPUNIT_ASSERT_EQUAL(OUString("Origin URL"), aGraphic.getOriginURL());
CPPUNIT_ASSERT_EQUAL(MapUnit::MapTwip, aGraphic.GetPrefMapMode().GetMapUnit());
CPPUNIT_ASSERT_EQUAL(tools::Long(200), aGraphic.GetPrefSize().Width());
CPPUNIT_ASSERT_EQUAL(tools::Long(100), aGraphic.GetPrefSize().Height());
}
void GraphicTest::testSwappingGraphicProperties_PNG_WithoutGfxLink()
{
// Prepare Graphic from a PNG image
Graphic aGraphic(makeUnloadedGraphic(u"png").GetBitmapEx());
CPPUNIT_ASSERT_EQUAL(GraphicType::Bitmap, aGraphic.GetType());
CPPUNIT_ASSERT_EQUAL(true, aGraphic.makeAvailable());
CPPUNIT_ASSERT_EQUAL(true, aGraphic.isAvailable());
// Origin URL
aGraphic.setOriginURL("Origin URL");
CPPUNIT_ASSERT_EQUAL(OUString("Origin URL"), aGraphic.getOriginURL());
//Set PrefMapMode
CPPUNIT_ASSERT_EQUAL(MapUnit::Map100thMM, aGraphic.GetPrefMapMode().GetMapUnit());
aGraphic.SetPrefMapMode(MapMode(MapUnit::MapTwip));
CPPUNIT_ASSERT_EQUAL(MapUnit::MapTwip, aGraphic.GetPrefMapMode().GetMapUnit());
// Set the PrefSize
CPPUNIT_ASSERT_EQUAL(tools::Long(6000), aGraphic.GetPrefSize().Width());
CPPUNIT_ASSERT_EQUAL(tools::Long(5000), aGraphic.GetPrefSize().Height());
aGraphic.SetPrefSize(Size(200, 100));
CPPUNIT_ASSERT_EQUAL(tools::Long(200), aGraphic.GetPrefSize().Width());
CPPUNIT_ASSERT_EQUAL(tools::Long(100), aGraphic.GetPrefSize().Height());
// SWAP OUT
CPPUNIT_ASSERT_EQUAL(true, aGraphic.isAvailable());
CPPUNIT_ASSERT_EQUAL(true, aGraphic.ImplGetImpGraphic()->swapOut());
CPPUNIT_ASSERT_EQUAL(true, aGraphic.ImplGetImpGraphic()->isSwappedOut());
CPPUNIT_ASSERT_EQUAL(false, aGraphic.isAvailable());
// Check properties
CPPUNIT_ASSERT_EQUAL(OUString("Origin URL"), aGraphic.getOriginURL());
CPPUNIT_ASSERT_EQUAL(MapUnit::MapTwip, aGraphic.GetPrefMapMode().GetMapUnit());
CPPUNIT_ASSERT_EQUAL(tools::Long(200), aGraphic.GetPrefSize().Width());
CPPUNIT_ASSERT_EQUAL(tools::Long(100), aGraphic.GetPrefSize().Height());
// SWAP IN
CPPUNIT_ASSERT_EQUAL(false, aGraphic.isAvailable());
CPPUNIT_ASSERT_EQUAL(true, aGraphic.makeAvailable());
CPPUNIT_ASSERT_EQUAL(true, aGraphic.isAvailable());
CPPUNIT_ASSERT_EQUAL(false, aGraphic.ImplGetImpGraphic()->isSwappedOut());
// Check properties
CPPUNIT_ASSERT_EQUAL(OUString("Origin URL"), aGraphic.getOriginURL());
CPPUNIT_ASSERT_EQUAL(MapUnit::MapTwip, aGraphic.GetPrefMapMode().GetMapUnit());
CPPUNIT_ASSERT_EQUAL(tools::Long(200), aGraphic.GetPrefSize().Width());
CPPUNIT_ASSERT_EQUAL(tools::Long(100), aGraphic.GetPrefSize().Height());
}
void GraphicTest::testSwappingVectorGraphic_SVG_WithGfxLink()
{ {
test::Directories aDirectories; test::Directories aDirectories;
OUString aURL = aDirectories.getURLFromSrc(DATA_DIRECTORY) + "SimpleExample.svg"; OUString aURL = aDirectories.getURLFromSrc(DATA_DIRECTORY) + "SimpleExample.svg";
SvFileStream aStream(aURL, StreamMode::READ); SvFileStream aStream(aURL, StreamMode::READ);
GraphicFilter& rGraphicFilter = GraphicFilter::GetGraphicFilter(); GraphicFilter& rGraphicFilter = GraphicFilter::GetGraphicFilter();
Graphic aGraphic = rGraphicFilter.ImportUnloadedGraphic(aStream); Graphic aGraphic = rGraphicFilter.ImportUnloadedGraphic(aStream);
// Loaded into "prepared" state
// Check that the state is as expected
CPPUNIT_ASSERT_EQUAL(GraphicType::Bitmap, aGraphic.GetType()); CPPUNIT_ASSERT_EQUAL(GraphicType::Bitmap, aGraphic.GetType());
CPPUNIT_ASSERT_EQUAL(false, aGraphic.isAvailable()); CPPUNIT_ASSERT_EQUAL(false, aGraphic.isAvailable());
// Load the vector graphic // Load the vector graphic
auto pVectorData = aGraphic.getVectorGraphicData();
CPPUNIT_ASSERT_EQUAL(true, bool(pVectorData));
CPPUNIT_ASSERT_EQUAL(true, aGraphic.isAvailable());
CPPUNIT_ASSERT_EQUAL(sal_uInt32(223), pVectorData->getVectorGraphicDataArrayLength());
CPPUNIT_ASSERT_EQUAL(true, aGraphic.IsGfxLink());
CPPUNIT_ASSERT_EQUAL(sal_uInt32(223), aGraphic.GetGfxLink().GetDataSize());
// Remember checksum so we can compare after swapping back in again
BitmapChecksum aBitmapChecksumBeforeSwapping = aGraphic.GetBitmapEx().GetChecksum();
// Check we are not swapped out yet
CPPUNIT_ASSERT_EQUAL(false, aGraphic.ImplGetImpGraphic()->isSwappedOut());
// Get the declared byte size of the graphic
sal_uLong rByteSize = aGraphic.GetSizeBytes();
CPPUNIT_ASSERT_EQUAL(sal_uLong(223), rByteSize);
// Make sure we don't have a file
CPPUNIT_ASSERT_EQUAL(true, aGraphic.ImplGetImpGraphic()->getSwapFileURL().isEmpty());
// SWAP OUT the Graphic and make sure it's not available currently
CPPUNIT_ASSERT_EQUAL(true, aGraphic.ImplGetImpGraphic()->swapOut());
CPPUNIT_ASSERT_EQUAL(true, aGraphic.ImplGetImpGraphic()->isSwappedOut());
CPPUNIT_ASSERT_EQUAL(false, aGraphic.isAvailable());
// We use GfxLink so no swap file in this case
CPPUNIT_ASSERT_EQUAL(true, aGraphic.ImplGetImpGraphic()->getSwapFileURL().isEmpty());
// Byte size doesn't change when we swapped out
CPPUNIT_ASSERT_EQUAL(rByteSize, aGraphic.GetSizeBytes());
// SWAP IN
CPPUNIT_ASSERT_EQUAL(false, aGraphic.isAvailable());
CPPUNIT_ASSERT_EQUAL(true, aGraphic.makeAvailable());
CPPUNIT_ASSERT_EQUAL(true, aGraphic.isAvailable());
CPPUNIT_ASSERT_EQUAL(false, aGraphic.ImplGetImpGraphic()->isSwappedOut());
// Compare that the checksum of the bitmap is still the same
CPPUNIT_ASSERT_EQUAL(aBitmapChecksumBeforeSwapping, aGraphic.GetBitmapEx().GetChecksum());
// Byte size shouldn't change
CPPUNIT_ASSERT_EQUAL(rByteSize, aGraphic.GetSizeBytes());
}
void GraphicTest::testSwappingVectorGraphic_SVG_WithoutGfxLink()
{
test::Directories aDirectories;
OUString aURL = aDirectories.getURLFromSrc(DATA_DIRECTORY) + "SimpleExample.svg";
SvFileStream aStream(aURL, StreamMode::READ);
GraphicFilter& rGraphicFilter = GraphicFilter::GetGraphicFilter();
Graphic aInputGraphic = rGraphicFilter.ImportUnloadedGraphic(aStream);
CPPUNIT_ASSERT_EQUAL(sal_uInt32(223),
aInputGraphic.getVectorGraphicData()->getVectorGraphicDataArrayLength());
// Create graphic
Graphic aGraphic(aInputGraphic.getVectorGraphicData());
CPPUNIT_ASSERT_EQUAL(GraphicType::Bitmap, aGraphic.GetType());
CPPUNIT_ASSERT_EQUAL(true, bool(aGraphic.getVectorGraphicData())); CPPUNIT_ASSERT_EQUAL(true, bool(aGraphic.getVectorGraphicData()));
CPPUNIT_ASSERT_EQUAL(true, aGraphic.isAvailable());
CPPUNIT_ASSERT_EQUAL(sal_uInt32(223), CPPUNIT_ASSERT_EQUAL(sal_uInt32(223),
aGraphic.getVectorGraphicData()->getVectorGraphicDataArrayLength()); aGraphic.getVectorGraphicData()->getVectorGraphicDataArrayLength());
CPPUNIT_ASSERT_EQUAL(true, aGraphic.isAvailable());
CPPUNIT_ASSERT_EQUAL(sal_uInt32(223), aGraphic.GetGfxLink().GetDataSize()); CPPUNIT_ASSERT_EQUAL(false, aGraphic.IsGfxLink());
CPPUNIT_ASSERT_EQUAL(true, aGraphic.isAvailable());
BitmapChecksum aBitmapChecksumBeforeSwapping = aGraphic.GetBitmapEx().GetChecksum(); BitmapChecksum aBitmapChecksumBeforeSwapping = aGraphic.GetBitmapEx().GetChecksum();
@@ -454,6 +728,7 @@ void GraphicTest::testSwappingVectorGraphic()
// Get the declared byte size of the graphic // Get the declared byte size of the graphic
sal_uLong rByteSize = aGraphic.GetSizeBytes(); sal_uLong rByteSize = aGraphic.GetSizeBytes();
CPPUNIT_ASSERT_EQUAL(sal_uLong(223), rByteSize); CPPUNIT_ASSERT_EQUAL(sal_uLong(223), rByteSize);
OUString rSwapFileURL = aGraphic.ImplGetImpGraphic()->getSwapFileURL(); OUString rSwapFileURL = aGraphic.ImplGetImpGraphic()->getSwapFileURL();
CPPUNIT_ASSERT_EQUAL(true, rSwapFileURL.isEmpty()); CPPUNIT_ASSERT_EQUAL(true, rSwapFileURL.isEmpty());
@@ -468,17 +743,19 @@ void GraphicTest::testSwappingVectorGraphic()
// Let's check the swap file // Let's check the swap file
rSwapFileURL = aGraphic.ImplGetImpGraphic()->getSwapFileURL(); rSwapFileURL = aGraphic.ImplGetImpGraphic()->getSwapFileURL();
CPPUNIT_ASSERT_EQUAL(false, rSwapFileURL.isEmpty());
CPPUNIT_ASSERT_EQUAL(true, comphelper::DirectoryHelper::fileExists(rSwapFileURL)); CPPUNIT_ASSERT_EQUAL(true, comphelper::DirectoryHelper::fileExists(rSwapFileURL));
{ // Check the swap file content {
// Check the swap file content
std::unique_ptr<SvStream> xStream = createStream(rSwapFileURL); std::unique_ptr<SvStream> xStream = createStream(rSwapFileURL);
CPPUNIT_ASSERT_EQUAL(true, bool(xStream)); CPPUNIT_ASSERT_EQUAL(true, bool(xStream));
// Check size of the stream // Check size of the stream
CPPUNIT_ASSERT_EQUAL(sal_uInt64(353), xStream->remainingSize()); CPPUNIT_ASSERT_EQUAL(sal_uInt64(249), xStream->remainingSize());
std::vector<unsigned char> aHash = calculateHash(xStream); std::vector<unsigned char> aHash = calculateHash(xStream);
CPPUNIT_ASSERT_EQUAL(std::string("6ae83fc9c06ca253ada0b156d6e4700a4a028c34"), CPPUNIT_ASSERT_EQUAL(std::string("322da9ea0683f03ce35cf8a71e59b686b9be28e8"),
toHexString(aHash)); toHexString(aHash));
} }
@@ -486,15 +763,146 @@ void GraphicTest::testSwappingVectorGraphic()
CPPUNIT_ASSERT_EQUAL(false, aGraphic.isAvailable()); CPPUNIT_ASSERT_EQUAL(false, aGraphic.isAvailable());
CPPUNIT_ASSERT_EQUAL(true, aGraphic.makeAvailable()); CPPUNIT_ASSERT_EQUAL(true, aGraphic.makeAvailable());
CPPUNIT_ASSERT_EQUAL(true, aGraphic.isAvailable()); CPPUNIT_ASSERT_EQUAL(true, aGraphic.isAvailable());
// Check the Graphic
CPPUNIT_ASSERT_EQUAL(false, aGraphic.ImplGetImpGraphic()->isSwappedOut()); CPPUNIT_ASSERT_EQUAL(false, aGraphic.ImplGetImpGraphic()->isSwappedOut());
CPPUNIT_ASSERT_EQUAL(GraphicType::Bitmap, aGraphic.GetType());
CPPUNIT_ASSERT_EQUAL(true, bool(aGraphic.getVectorGraphicData()));
sal_uInt32 nVectorByteSize = aGraphic.getVectorGraphicData()->getVectorGraphicDataArrayLength();
CPPUNIT_ASSERT_EQUAL(sal_uInt32(223), nVectorByteSize);
CPPUNIT_ASSERT_EQUAL(false, aGraphic.IsGfxLink());
CPPUNIT_ASSERT_EQUAL(aBitmapChecksumBeforeSwapping, aGraphic.GetBitmapEx().GetChecksum()); CPPUNIT_ASSERT_EQUAL(aBitmapChecksumBeforeSwapping, aGraphic.GetBitmapEx().GetChecksum());
// File shouldn't be available anymore // File shouldn't be available anymore
CPPUNIT_ASSERT_EQUAL(false, comphelper::DirectoryHelper::fileExists(rSwapFileURL)); CPPUNIT_ASSERT_EQUAL(false, comphelper::DirectoryHelper::fileExists(rSwapFileURL));
} }
void GraphicTest::testSwappingPageNumber() void GraphicTest::testSwappingGraphicProperties_SVG_WithGfxLink()
{
// We check that Graphic properties like MapMode, PrefSize are properly
// restored through a swap cycle
test::Directories aDirectories;
OUString aURL = aDirectories.getURLFromSrc(DATA_DIRECTORY) + "SimpleExample.svg";
SvFileStream aStream(aURL, StreamMode::READ);
GraphicFilter& rGraphicFilter = GraphicFilter::GetGraphicFilter();
Graphic aGraphic = rGraphicFilter.ImportUnloadedGraphic(aStream);
// Loaded into "prepared" state
// Load the vector graphic
auto pVectorData = aGraphic.getVectorGraphicData();
CPPUNIT_ASSERT_EQUAL(true, bool(pVectorData));
CPPUNIT_ASSERT_EQUAL(true, aGraphic.isAvailable());
// Origin URL
aGraphic.setOriginURL("Origin URL");
CPPUNIT_ASSERT_EQUAL(OUString("Origin URL"), aGraphic.getOriginURL());
// Check size in pixels
CPPUNIT_ASSERT_EQUAL(tools::Long(51), aGraphic.GetSizePixel().Width());
CPPUNIT_ASSERT_EQUAL(tools::Long(51), aGraphic.GetSizePixel().Height());
// Set and check the PrefSize
CPPUNIT_ASSERT_EQUAL(tools::Long(1349), aGraphic.GetPrefSize().Width());
CPPUNIT_ASSERT_EQUAL(tools::Long(1349), aGraphic.GetPrefSize().Height());
aGraphic.SetPrefSize(Size(200, 100));
CPPUNIT_ASSERT_EQUAL(tools::Long(200), aGraphic.GetPrefSize().Width());
CPPUNIT_ASSERT_EQUAL(tools::Long(100), aGraphic.GetPrefSize().Height());
// SWAP OUT the Graphic and make sure it's not available currently
CPPUNIT_ASSERT_EQUAL(true, aGraphic.isAvailable());
CPPUNIT_ASSERT_EQUAL(true, aGraphic.ImplGetImpGraphic()->swapOut());
CPPUNIT_ASSERT_EQUAL(true, aGraphic.ImplGetImpGraphic()->isSwappedOut());
CPPUNIT_ASSERT_EQUAL(false, aGraphic.isAvailable());
// Check properties
CPPUNIT_ASSERT_EQUAL(OUString("Origin URL"), aGraphic.getOriginURL());
CPPUNIT_ASSERT_EQUAL(tools::Long(200), aGraphic.GetPrefSize().Width());
CPPUNIT_ASSERT_EQUAL(tools::Long(100), aGraphic.GetPrefSize().Height());
CPPUNIT_ASSERT_EQUAL(tools::Long(51), aGraphic.GetSizePixel().Width());
CPPUNIT_ASSERT_EQUAL(tools::Long(51), aGraphic.GetSizePixel().Height());
// SWAP IN
CPPUNIT_ASSERT_EQUAL(false, aGraphic.isAvailable());
CPPUNIT_ASSERT_EQUAL(true, aGraphic.makeAvailable());
CPPUNIT_ASSERT_EQUAL(true, aGraphic.isAvailable());
CPPUNIT_ASSERT_EQUAL(false, aGraphic.ImplGetImpGraphic()->isSwappedOut());
// Check properties
CPPUNIT_ASSERT_EQUAL(OUString("Origin URL"), aGraphic.getOriginURL());
CPPUNIT_ASSERT_EQUAL(tools::Long(200), aGraphic.GetPrefSize().Width());
CPPUNIT_ASSERT_EQUAL(tools::Long(100), aGraphic.GetPrefSize().Height());
CPPUNIT_ASSERT_EQUAL(tools::Long(51), aGraphic.GetSizePixel().Width());
CPPUNIT_ASSERT_EQUAL(tools::Long(51), aGraphic.GetSizePixel().Height());
}
void GraphicTest::testSwappingGraphicProperties_SVG_WithoutGfxLink()
{
test::Directories aDirectories;
OUString aURL = aDirectories.getURLFromSrc(DATA_DIRECTORY) + "SimpleExample.svg";
SvFileStream aStream(aURL, StreamMode::READ);
GraphicFilter& rGraphicFilter = GraphicFilter::GetGraphicFilter();
Graphic aInputGraphic = rGraphicFilter.ImportUnloadedGraphic(aStream);
CPPUNIT_ASSERT_EQUAL(sal_uInt32(223),
aInputGraphic.getVectorGraphicData()->getVectorGraphicDataArrayLength());
// Create graphic
Graphic aGraphic(aInputGraphic.getVectorGraphicData());
CPPUNIT_ASSERT_EQUAL(GraphicType::Bitmap, aGraphic.GetType());
CPPUNIT_ASSERT_EQUAL(true, bool(aGraphic.getVectorGraphicData()));
CPPUNIT_ASSERT_EQUAL(true, aGraphic.isAvailable());
CPPUNIT_ASSERT_EQUAL(false, aGraphic.IsGfxLink());
CPPUNIT_ASSERT_EQUAL(false, aGraphic.ImplGetImpGraphic()->isSwappedOut());
// Origin URL
aGraphic.setOriginURL("Origin URL");
CPPUNIT_ASSERT_EQUAL(OUString("Origin URL"), aGraphic.getOriginURL());
// Check size in pixels
CPPUNIT_ASSERT_EQUAL(tools::Long(51), aGraphic.GetSizePixel().Width());
CPPUNIT_ASSERT_EQUAL(tools::Long(51), aGraphic.GetSizePixel().Height());
// Set and check the PrefSize
CPPUNIT_ASSERT_EQUAL(tools::Long(1349), aGraphic.GetPrefSize().Width());
CPPUNIT_ASSERT_EQUAL(tools::Long(1349), aGraphic.GetPrefSize().Height());
aGraphic.SetPrefSize(Size(200, 100));
CPPUNIT_ASSERT_EQUAL(tools::Long(200), aGraphic.GetPrefSize().Width());
CPPUNIT_ASSERT_EQUAL(tools::Long(100), aGraphic.GetPrefSize().Height());
// SWAP OUT the Graphic and make sure it's not available currently
CPPUNIT_ASSERT_EQUAL(true, aGraphic.isAvailable());
CPPUNIT_ASSERT_EQUAL(true, aGraphic.ImplGetImpGraphic()->swapOut());
CPPUNIT_ASSERT_EQUAL(true, aGraphic.ImplGetImpGraphic()->isSwappedOut());
CPPUNIT_ASSERT_EQUAL(false, aGraphic.isAvailable());
CPPUNIT_ASSERT_EQUAL(OUString("Origin URL"), aGraphic.getOriginURL());
CPPUNIT_ASSERT_EQUAL(tools::Long(200), aGraphic.GetPrefSize().Width());
CPPUNIT_ASSERT_EQUAL(tools::Long(100), aGraphic.GetPrefSize().Height());
CPPUNIT_ASSERT_EQUAL(tools::Long(51), aGraphic.GetSizePixel().Width());
CPPUNIT_ASSERT_EQUAL(tools::Long(51), aGraphic.GetSizePixel().Height());
// SWAP IN
CPPUNIT_ASSERT_EQUAL(false, aGraphic.isAvailable());
CPPUNIT_ASSERT_EQUAL(true, aGraphic.makeAvailable());
CPPUNIT_ASSERT_EQUAL(true, aGraphic.isAvailable());
CPPUNIT_ASSERT_EQUAL(false, aGraphic.ImplGetImpGraphic()->isSwappedOut());
CPPUNIT_ASSERT_EQUAL(OUString("Origin URL"), aGraphic.getOriginURL());
CPPUNIT_ASSERT_EQUAL(tools::Long(200), aGraphic.GetPrefSize().Width());
CPPUNIT_ASSERT_EQUAL(tools::Long(100), aGraphic.GetPrefSize().Height());
CPPUNIT_ASSERT_EQUAL(tools::Long(51), aGraphic.GetSizePixel().Width());
CPPUNIT_ASSERT_EQUAL(tools::Long(51), aGraphic.GetSizePixel().Height());
}
void GraphicTest::testSwappingVectorGraphic_PDF_WithGfxLink()
{ {
test::Directories aDirectories; test::Directories aDirectories;
OUString aURL = aDirectories.getURLFromSrc(PDFEXPORT_DATA_DIRECTORY) + "SimpleMultiPagePDF.pdf"; OUString aURL = aDirectories.getURLFromSrc(PDFEXPORT_DATA_DIRECTORY) + "SimpleMultiPagePDF.pdf";
@@ -507,6 +915,7 @@ void GraphicTest::testSwappingPageNumber()
// Load the vector graphic // Load the vector graphic
CPPUNIT_ASSERT_EQUAL(true, bool(aGraphic.getVectorGraphicData())); CPPUNIT_ASSERT_EQUAL(true, bool(aGraphic.getVectorGraphicData()));
// Set the page index // Set the page index
aGraphic.getVectorGraphicData()->setPageIndex(1); aGraphic.getVectorGraphicData()->setPageIndex(1);
@@ -519,12 +928,12 @@ void GraphicTest::testSwappingPageNumber()
CPPUNIT_ASSERT_EQUAL(false, aGraphic.ImplGetImpGraphic()->isSwappedOut()); CPPUNIT_ASSERT_EQUAL(false, aGraphic.ImplGetImpGraphic()->isSwappedOut());
// Swapping out // SWAP OUT
CPPUNIT_ASSERT_EQUAL(true, aGraphic.ImplGetImpGraphic()->swapOut()); CPPUNIT_ASSERT_EQUAL(true, aGraphic.ImplGetImpGraphic()->swapOut());
CPPUNIT_ASSERT_EQUAL(true, aGraphic.ImplGetImpGraphic()->isSwappedOut()); CPPUNIT_ASSERT_EQUAL(true, aGraphic.ImplGetImpGraphic()->isSwappedOut());
CPPUNIT_ASSERT_EQUAL(false, aGraphic.isAvailable()); CPPUNIT_ASSERT_EQUAL(false, aGraphic.isAvailable());
// Let's swap in // SWAP IN
CPPUNIT_ASSERT_EQUAL(false, aGraphic.isAvailable()); CPPUNIT_ASSERT_EQUAL(false, aGraphic.isAvailable());
CPPUNIT_ASSERT_EQUAL(true, aGraphic.makeAvailable()); CPPUNIT_ASSERT_EQUAL(true, aGraphic.makeAvailable());
CPPUNIT_ASSERT_EQUAL(true, aGraphic.isAvailable()); CPPUNIT_ASSERT_EQUAL(true, aGraphic.isAvailable());
@@ -533,31 +942,176 @@ void GraphicTest::testSwappingPageNumber()
CPPUNIT_ASSERT_EQUAL(sal_Int32(1), aGraphic.getVectorGraphicData()->getPageIndex()); CPPUNIT_ASSERT_EQUAL(sal_Int32(1), aGraphic.getVectorGraphicData()->getPageIndex());
} }
void GraphicTest::testWMFRoundtrip() void GraphicTest::testSwappingVectorGraphic_PDF_WithoutGfxLink()
{ {
// Load a WMF file.
test::Directories aDirectories; test::Directories aDirectories;
OUString aURL = aDirectories.getURLFromSrc("vcl/qa/cppunit/data/roundtrip.wmf"); OUString aURL = aDirectories.getURLFromSrc(PDFEXPORT_DATA_DIRECTORY) + "SimpleMultiPagePDF.pdf";
SvFileStream aStream(aURL, StreamMode::READ);
GraphicFilter& rGraphicFilter = GraphicFilter::GetGraphicFilter();
Graphic aInputGraphic = rGraphicFilter.ImportUnloadedGraphic(aStream);
// Create graphic
Graphic aGraphic(aInputGraphic.getVectorGraphicData());
CPPUNIT_ASSERT_EQUAL(GraphicType::Bitmap, aGraphic.GetType());
CPPUNIT_ASSERT_EQUAL(true, aGraphic.isAvailable());
CPPUNIT_ASSERT_EQUAL(true, bool(aGraphic.getVectorGraphicData()));
// Set the page index
aGraphic.getVectorGraphicData()->setPageIndex(1);
CPPUNIT_ASSERT_EQUAL(VectorGraphicDataType::Pdf,
aGraphic.getVectorGraphicData()->getVectorGraphicDataType());
CPPUNIT_ASSERT_EQUAL(sal_uInt32(17693),
aGraphic.getVectorGraphicData()->getVectorGraphicDataArrayLength());
CPPUNIT_ASSERT_EQUAL(true, aGraphic.isAvailable());
CPPUNIT_ASSERT_EQUAL(sal_Int32(1), aGraphic.getVectorGraphicData()->getPageIndex());
CPPUNIT_ASSERT_EQUAL(false, aGraphic.ImplGetImpGraphic()->isSwappedOut());
// SWAP OUT
CPPUNIT_ASSERT_EQUAL(true, aGraphic.ImplGetImpGraphic()->swapOut());
CPPUNIT_ASSERT_EQUAL(true, aGraphic.ImplGetImpGraphic()->isSwappedOut());
CPPUNIT_ASSERT_EQUAL(false, aGraphic.isAvailable());
// SWAP IN
CPPUNIT_ASSERT_EQUAL(false, aGraphic.isAvailable());
CPPUNIT_ASSERT_EQUAL(true, aGraphic.makeAvailable());
CPPUNIT_ASSERT_EQUAL(true, aGraphic.isAvailable());
CPPUNIT_ASSERT_EQUAL(false, aGraphic.ImplGetImpGraphic()->isSwappedOut());
CPPUNIT_ASSERT_EQUAL(sal_Int32(1), aGraphic.getVectorGraphicData()->getPageIndex());
}
void GraphicTest::testSwappingAnimationGraphic_GIF_WithGfxLink()
{
test::Directories aDirectories;
OUString aURL = aDirectories.getURLFromSrc(DATA_DIRECTORY) + "123_Numbers.gif";
SvFileStream aStream(aURL, StreamMode::READ); SvFileStream aStream(aURL, StreamMode::READ);
sal_uInt64 nExpectedSize = aStream.TellEnd();
GraphicFilter& rGraphicFilter = GraphicFilter::GetGraphicFilter(); GraphicFilter& rGraphicFilter = GraphicFilter::GetGraphicFilter();
Graphic aGraphic = rGraphicFilter.ImportUnloadedGraphic(aStream); Graphic aGraphic = rGraphicFilter.ImportUnloadedGraphic(aStream);
// Loaded into "prepared" state
// Save as WMF. // Check that the state is as expected
utl::TempFile aTempFile; CPPUNIT_ASSERT_EQUAL(GraphicType::Bitmap, aGraphic.GetType());
aTempFile.EnableKillingFile(); CPPUNIT_ASSERT_EQUAL(false, aGraphic.isAvailable());
sal_uInt16 nFormat = rGraphicFilter.GetExportFormatNumberForShortName(u"WMF");
SvStream& rOutStream = *aTempFile.GetStream(StreamMode::READWRITE);
rGraphicFilter.ExportGraphic(aGraphic, OUString(), rOutStream, nFormat);
// Check if we preserved the WMF data perfectly. CPPUNIT_ASSERT_EQUAL(true, aGraphic.makeAvailable());
sal_uInt64 nActualSize = rOutStream.TellEnd(); CPPUNIT_ASSERT_EQUAL(true, aGraphic.isAvailable());
// Without the accompanying fix in place, this test would have failed with: CPPUNIT_ASSERT_EQUAL(true, aGraphic.IsGfxLink());
// - Expected: 6475
// - Actual : 2826 CPPUNIT_ASSERT_EQUAL(tools::Long(124), aGraphic.GetSizePixel().Width());
// i.e. we lost some of the WMF data on roundtrip. CPPUNIT_ASSERT_EQUAL(tools::Long(146), aGraphic.GetSizePixel().Height());
CPPUNIT_ASSERT_EQUAL(nExpectedSize, nActualSize);
CPPUNIT_ASSERT_EQUAL(sal_uInt32(1515), aGraphic.GetGfxLink().GetDataSize());
// Remember checksum so we can compare after swapping back in again
BitmapChecksum aBitmapChecksumBeforeSwapping = aGraphic.GetBitmapEx().GetChecksum();
// Check we are not swapped out yet
CPPUNIT_ASSERT_EQUAL(false, aGraphic.ImplGetImpGraphic()->isSwappedOut());
// Get the declared byte size of the graphic
sal_uLong rByteSize = aGraphic.GetSizeBytes();
CPPUNIT_ASSERT_EQUAL(sal_uLong(50373), rByteSize);
// Make sure we don't have a file
CPPUNIT_ASSERT_EQUAL(true, aGraphic.ImplGetImpGraphic()->getSwapFileURL().isEmpty());
// SWAP OUT the Graphic and make sure it's not available currently
CPPUNIT_ASSERT_EQUAL(true, aGraphic.ImplGetImpGraphic()->swapOut());
CPPUNIT_ASSERT_EQUAL(true, aGraphic.ImplGetImpGraphic()->isSwappedOut());
CPPUNIT_ASSERT_EQUAL(false, aGraphic.isAvailable());
// We use GfxLink so no swap file in this case
CPPUNIT_ASSERT_EQUAL(true, aGraphic.ImplGetImpGraphic()->getSwapFileURL().isEmpty());
// Byte size doesn't change when we swapped out
CPPUNIT_ASSERT_EQUAL(rByteSize, aGraphic.GetSizeBytes());
// SWAP IN
CPPUNIT_ASSERT_EQUAL(false, aGraphic.isAvailable());
CPPUNIT_ASSERT_EQUAL(true, aGraphic.makeAvailable());
CPPUNIT_ASSERT_EQUAL(true, aGraphic.isAvailable());
CPPUNIT_ASSERT_EQUAL(false, aGraphic.ImplGetImpGraphic()->isSwappedOut());
// Compare that the checksum of the bitmap is still the same
CPPUNIT_ASSERT_EQUAL(aBitmapChecksumBeforeSwapping, aGraphic.GetBitmapEx().GetChecksum());
// Byte size shouldn't change
CPPUNIT_ASSERT_EQUAL(rByteSize, aGraphic.GetSizeBytes());
}
void GraphicTest::testSwappingAnimationGraphic_GIF_WithoutGfxLink()
{
test::Directories aDirectories;
OUString aURL = aDirectories.getURLFromSrc(DATA_DIRECTORY) + "123_Numbers.gif";
SvFileStream aStream(aURL, StreamMode::READ);
GraphicFilter& rGraphicFilter = GraphicFilter::GetGraphicFilter();
Graphic aInputGraphic = rGraphicFilter.ImportUnloadedGraphic(aStream);
Graphic aGraphic(aInputGraphic.GetAnimation());
// Check animation graphic
CPPUNIT_ASSERT_EQUAL(GraphicType::Bitmap, aGraphic.GetType());
CPPUNIT_ASSERT_EQUAL(true, aGraphic.makeAvailable());
CPPUNIT_ASSERT_EQUAL(true, aGraphic.isAvailable());
CPPUNIT_ASSERT_EQUAL(true, aGraphic.IsAnimated());
CPPUNIT_ASSERT_EQUAL(tools::Long(124), aGraphic.GetSizePixel().Width());
CPPUNIT_ASSERT_EQUAL(tools::Long(146), aGraphic.GetSizePixel().Height());
CPPUNIT_ASSERT_EQUAL(false, aGraphic.IsGfxLink());
// We loaded the Graphic and made it available
CPPUNIT_ASSERT_EQUAL(false, aGraphic.ImplGetImpGraphic()->isSwappedOut());
// Get the declared byte size of the graphic
sal_uLong rByteSize = aGraphic.GetSizeBytes();
OUString rSwapFileURL = aGraphic.ImplGetImpGraphic()->getSwapFileURL();
CPPUNIT_ASSERT_EQUAL(true, rSwapFileURL.isEmpty());
// SWAP OUT
CPPUNIT_ASSERT_EQUAL(true, aGraphic.isAvailable());
CPPUNIT_ASSERT_EQUAL(true, aGraphic.ImplGetImpGraphic()->swapOut());
CPPUNIT_ASSERT_EQUAL(true, aGraphic.ImplGetImpGraphic()->isSwappedOut());
CPPUNIT_ASSERT_EQUAL(false, aGraphic.isAvailable());
// Byte size doesn't change when we swapped out
CPPUNIT_ASSERT_EQUAL(rByteSize, aGraphic.GetSizeBytes());
// Let's check the swap file
rSwapFileURL = aGraphic.ImplGetImpGraphic()->getSwapFileURL();
CPPUNIT_ASSERT_EQUAL(true, comphelper::DirectoryHelper::fileExists(rSwapFileURL));
{
// Check the swap file content
std::unique_ptr<SvStream> xStream = createStream(rSwapFileURL);
CPPUNIT_ASSERT_EQUAL(true, bool(xStream));
// Check size of the stream
CPPUNIT_ASSERT_EQUAL(sal_uInt64(15183), xStream->remainingSize());
std::vector<unsigned char> aHash = calculateHash(xStream);
CPPUNIT_ASSERT_EQUAL(std::string("deb13fdf0ffa0b58ce92fff0a6ca9e98c5d26ed9"),
toHexString(aHash));
}
// SWAP IN
CPPUNIT_ASSERT_EQUAL(false, aGraphic.isAvailable());
CPPUNIT_ASSERT_EQUAL(true, aGraphic.makeAvailable());
CPPUNIT_ASSERT_EQUAL(true, aGraphic.isAvailable());
CPPUNIT_ASSERT_EQUAL(false, aGraphic.ImplGetImpGraphic()->isSwappedOut());
// File shouldn't be available anymore
CPPUNIT_ASSERT_EQUAL(false, comphelper::DirectoryHelper::fileExists(rSwapFileURL));
// Check the bitmap
CPPUNIT_ASSERT_EQUAL(tools::Long(124), aGraphic.GetSizePixel().Width());
CPPUNIT_ASSERT_EQUAL(tools::Long(146), aGraphic.GetSizePixel().Height());
// Byte size is still the same
CPPUNIT_ASSERT_EQUAL(rByteSize, aGraphic.GetSizeBytes());
} }
} // namespace } // namespace

View File

@@ -160,11 +160,6 @@ namespace
{ {
#define NATIVE_FORMAT_50 COMPAT_FORMAT('N', 'A', 'T', '5') #define NATIVE_FORMAT_50 COMPAT_FORMAT('N', 'A', 'T', '5')
constexpr sal_uInt32 constSvgMagic = createMagic('s', 'v', 'g', '0');
constexpr sal_uInt32 constWmfMagic = createMagic('w', 'm', 'f', '0');
constexpr sal_uInt32 constEmfMagic = createMagic('e', 'm', 'f', '0');
constexpr sal_uInt32 constPdfMagic = createMagic('p', 'd', 'f', '0');
} // end anonymous namespace } // end anonymous namespace
void TypeSerializer::readGraphic(Graphic& rGraphic) void TypeSerializer::readGraphic(Graphic& rGraphic)

View File

@@ -50,23 +50,9 @@
#define GRAPHIC_MTFTOBMP_MAXEXT 2048 #define GRAPHIC_MTFTOBMP_MAXEXT 2048
#define GRAPHIC_STREAMBUFSIZE 8192UL #define GRAPHIC_STREAMBUFSIZE 8192UL
#define SYS_WINMETAFILE 0x00000003L #define SWAP_FORMAT_ID COMPAT_FORMAT( 'S', 'W', 'A', 'P' )
#define SYS_WNTMETAFILE 0x00000004L
#define SYS_OS2METAFILE 0x00000005L
#define SYS_MACMETAFILE 0x00000006L
#define GRAPHIC_FORMAT_50 COMPAT_FORMAT( 'G', 'R', 'F', '5' )
#define NATIVE_FORMAT_50 COMPAT_FORMAT( 'N', 'A', 'T', '5' ) #define NATIVE_FORMAT_50 COMPAT_FORMAT( 'N', 'A', 'T', '5' )
namespace {
constexpr sal_uInt32 constSvgMagic((sal_uInt32('s') << 24) | (sal_uInt32('v') << 16) | (sal_uInt32('g') << 8) | sal_uInt32('0'));
constexpr sal_uInt32 constWmfMagic((sal_uInt32('w') << 24) | (sal_uInt32('m') << 16) | (sal_uInt32('f') << 8) | sal_uInt32('0'));
constexpr sal_uInt32 constEmfMagic((sal_uInt32('e') << 24) | (sal_uInt32('m') << 16) | (sal_uInt32('f') << 8) | sal_uInt32('0'));
constexpr sal_uInt32 constPdfMagic((sal_uInt32('s') << 24) | (sal_uInt32('v') << 16) | (sal_uInt32('g') << 8) | sal_uInt32('0'));
}
using namespace com::sun::star; using namespace com::sun::star;
class ImpSwapFile : public vcl::SwapFile class ImpSwapFile : public vcl::SwapFile
@@ -348,6 +334,7 @@ void ImpGraphic::createSwapInfo()
if (isSwappedOut()) if (isSwappedOut())
return; return;
maSwapInfo.maSizePixel = ImplGetSizePixel();
maSwapInfo.maPrefMapMode = ImplGetPrefMapMode(); maSwapInfo.maPrefMapMode = ImplGetPrefMapMode();
maSwapInfo.maPrefSize = ImplGetPrefSize(); maSwapInfo.maPrefSize = ImplGetPrefSize();
maSwapInfo.mbIsAnimated = ImplIsAnimated(); maSwapInfo.mbIsAnimated = ImplIsAnimated();
@@ -363,7 +350,6 @@ void ImpGraphic::ImplClearGraphics()
maBitmapEx.Clear(); maBitmapEx.Clear();
maMetaFile.Clear(); maMetaFile.Clear();
mpAnimation.reset(); mpAnimation.reset();
mpGfxLink.reset();
maVectorGraphicData.reset(); maVectorGraphicData.reset();
} }
@@ -1121,107 +1107,32 @@ bool ImpGraphic::swapInContent(SvStream& rStream)
{ {
bool bRet = false; bool bRet = false;
ensureAvailable();
MapMode aMapMode;
Size aSize;
sal_uInt32 nId; sal_uInt32 nId;
sal_Int32 nType; sal_Int32 nType;
sal_Int32 nPageIndex = -1; sal_Int32 nLength;
const SvStreamEndian nOldFormat = rStream.GetEndian();
rStream.SetEndian(SvStreamEndian::LITTLE);
rStream.ReadUInt32(nId); rStream.ReadUInt32(nId);
// check version // check version
if (GRAPHIC_FORMAT_50 != nId) if (SWAP_FORMAT_ID != nId)
{
SAL_WARN("vcl", "Incompatible swap file!");
return false; return false;
}
// read new style header
VersionCompat aCompat(rStream, StreamMode::READ);
rStream.ReadInt32(nType); rStream.ReadInt32(nType);
sal_Int32 nLen; rStream.ReadInt32(nLength);
rStream.ReadInt32(nLen);
TypeSerializer aSerializer(rStream);
aSerializer.readSize(aSize);
ReadMapMode(rStream, aMapMode);
if (aCompat.GetVersion() >= 2)
{
rStream.ReadInt32(nPageIndex);
}
meType = static_cast<GraphicType>(nType); meType = static_cast<GraphicType>(nType);
if( meType != GraphicType::NONE ) if (meType == GraphicType::NONE || meType == GraphicType::Default)
{ {
if( meType == GraphicType::Bitmap ) return true;
{
if(maVectorGraphicData && maBitmapEx.IsEmpty())
{
// use maBitmapEx as local buffer for rendered svg
maBitmapEx = getVectorGraphicReplacement();
}
maBitmapEx.SetSizePixel(aSize);
if( aMapMode != MapMode() )
{
maBitmapEx.SetPrefMapMode( aMapMode );
maBitmapEx.SetPrefSize( aSize );
}
}
else
{
maMetaFile.SetPrefMapMode( aMapMode );
maMetaFile.SetPrefSize( aSize );
}
if( meType == GraphicType::Bitmap || meType == GraphicType::GdiMetafile )
{
swapInGraphic(rStream);
bRet = rStream.GetError() == ERRCODE_NONE;
}
else if( sal::static_int_cast<sal_uLong>(meType) >= SYS_WINMETAFILE
&& sal::static_int_cast<sal_uLong>(meType) <= SYS_MACMETAFILE )
{
Graphic aSysGraphic;
ConvertDataFormat nCvtType;
switch( sal::static_int_cast<sal_uLong>(meType) )
{
case SYS_WINMETAFILE:
case SYS_WNTMETAFILE: nCvtType = ConvertDataFormat::WMF; break;
case SYS_OS2METAFILE: nCvtType = ConvertDataFormat::MET; break;
case SYS_MACMETAFILE: nCvtType = ConvertDataFormat::PCT; break;
default:
nCvtType = ConvertDataFormat::Unknown;
break;
}
if( nType && GraphicConverter::Import(rStream, aSysGraphic, nCvtType) == ERRCODE_NONE )
{
*this = ImpGraphic( aSysGraphic.GetGDIMetaFile() );
bRet = rStream.GetError() == ERRCODE_NONE;
}
else
meType = GraphicType::Default;
}
if (bRet)
{
ImplSetPrefMapMode( aMapMode );
ImplSetPrefSize( aSize );
if (maVectorGraphicData)
maVectorGraphicData->setPageIndex(nPageIndex);
}
} }
else else
bRet = true; {
bRet = swapInGraphic(rStream);
rStream.SetEndian(nOldFormat); }
return bRet; return bRet;
} }
@@ -1239,149 +1150,111 @@ bool ImpGraphic::swapOutGraphic(SvStream& rStream)
return false; return false;
} }
if (rStream.GetVersion() >= SOFFICE_FILEFORMAT_50 && switch (meType)
rStream.GetCompressMode() & SvStreamCompressFlags::NATIVE &&
mpGfxLink && mpGfxLink->IsNative())
{ {
// native format case GraphicType::GdiMetafile:
rStream.WriteUInt32(NATIVE_FORMAT_50);
// write compat info, destructor writes stuff into the header
{ {
VersionCompat aCompat(rStream, StreamMode::WRITE, 1); WriteGDIMetaFile(rStream, maMetaFile);
} }
mpGfxLink->SetPrefMapMode(ImplGetPrefMapMode()); break;
mpGfxLink->SetPrefSize(ImplGetPrefSize());
TypeSerializer aSerializer(rStream);
aSerializer.writeGfxLink(*mpGfxLink);
}
else
{
switch (ImplGetType())
{
case GraphicType::NONE:
case GraphicType::Default:
break;
case GraphicType::Bitmap: case GraphicType::Bitmap:
{
if (maVectorGraphicData)
{ {
if (getVectorGraphicData()) rStream.WriteInt32(sal_Int32(GraphicContentType::Vector));
// stream out Vector Graphic defining data (length, byte array and evtl. path)
// this is used e.g. in swapping out graphic data and in transporting it over UNO API
// as sequence of bytes, but AFAIK not written anywhere to any kind of file, so it should be
// no problem to extend it; only used at runtime
switch (maVectorGraphicData->getVectorGraphicDataType())
{ {
// stream out Vector Graphic defining data (length, byte array and evtl. path) case VectorGraphicDataType::Wmf:
// this is used e.g. in swapping out graphic data and in transporting it over UNO API
// as sequence of bytes, but AFAIK not written anywhere to any kind of file, so it should be
// no problem to extend it; only used at runtime
switch (getVectorGraphicData()->getVectorGraphicDataType())
{ {
case VectorGraphicDataType::Wmf: rStream.WriteUInt32(constWmfMagic);
{ break;
rStream.WriteUInt32(constWmfMagic);
break;
}
case VectorGraphicDataType::Emf:
{
rStream.WriteUInt32(constEmfMagic);
break;
}
case VectorGraphicDataType::Svg:
{
rStream.WriteUInt32(constSvgMagic);
break;
}
case VectorGraphicDataType::Pdf:
{
rStream.WriteUInt32(constPdfMagic);
break;
}
} }
case VectorGraphicDataType::Emf:
{
rStream.WriteUInt32(constEmfMagic);
break;
}
case VectorGraphicDataType::Svg:
{
rStream.WriteUInt32(constSvgMagic);
break;
}
case VectorGraphicDataType::Pdf:
{
rStream.WriteUInt32(constPdfMagic);
break;
}
}
rStream.WriteUInt32(getVectorGraphicData()->getVectorGraphicDataArrayLength()); rStream.WriteUInt32(maVectorGraphicData->getVectorGraphicDataArrayLength());
rStream.WriteBytes(
getVectorGraphicData()->getVectorGraphicDataArray().getConstArray(), rStream.WriteBytes(
getVectorGraphicData()->getVectorGraphicDataArrayLength()); maVectorGraphicData->getVectorGraphicDataArray().getConstArray(),
rStream.WriteUniOrByteString(getVectorGraphicData()->getPath(), rStream.GetStreamCharSet()); maVectorGraphicData->getVectorGraphicDataArrayLength());
}
else if (ImplIsAnimated()) rStream.WriteUniOrByteString(maVectorGraphicData->getPath(), rStream.GetStreamCharSet());
{
WriteAnimation(rStream, *mpAnimation);
}
else
{
WriteDIBBitmapEx(maBitmapEx, rStream);
}
} }
break; else if (ImplIsAnimated())
default:
{ {
if (ImplIsSupportedGraphic()) rStream.WriteInt32(sal_Int32(GraphicContentType::Animation));
WriteGDIMetaFile(rStream, maMetaFile); WriteAnimation(rStream, *mpAnimation);
}
else
{
rStream.WriteInt32(sal_Int32(GraphicContentType::Bitmap));
WriteDIBBitmapEx(maBitmapEx, rStream);
} }
break;
} }
break;
case GraphicType::NONE:
case GraphicType::Default:
break;
} }
return true; return true;
} }
bool ImpGraphic::swapOutContent(SvStream& rOStm) bool ImpGraphic::swapOutContent(SvStream& rStream)
{ {
ensureAvailable(); ensureAvailable();
bool bRet = false;
if (meType == GraphicType::NONE || meType == GraphicType::Default || isSwappedOut()) if (meType == GraphicType::NONE || meType == GraphicType::Default || isSwappedOut())
return false; return false;
const SvStreamEndian nOldFormat = rOStm.GetEndian();
const MapMode aMapMode = ImplGetPrefMapMode();
const Size aSize = ImplGetPrefSize();
sal_uLong nDataFieldPos; sal_uLong nDataFieldPos;
rOStm.SetEndian(SvStreamEndian::LITTLE); // Write te SWAP ID
rStream.WriteUInt32(SWAP_FORMAT_ID);
// write ID for new format (5.0) rStream.WriteInt32(static_cast<sal_Int32>(meType));
rOStm.WriteUInt32(GRAPHIC_FORMAT_50);
// write new style header // data size is updated later
{ nDataFieldPos = rStream.Tell();
VersionCompat aCompat(rOStm, StreamMode::WRITE, 2); rStream.WriteInt32(0);
rOStm.WriteInt32(static_cast<sal_Int32>(meType));
// data size is updated later
nDataFieldPos = rOStm.Tell();
rOStm.WriteInt32(0);
TypeSerializer aSerializer(rOStm);
aSerializer.writeSize(aSize);
WriteMapMode(rOStm, aMapMode);
// Version 2
rOStm.WriteInt32(getPageNumber());
}
bool bRet = false;
// write data block // write data block
if (!rOStm.GetError()) const sal_uLong nDataStart = rStream.Tell();
swapOutGraphic(rStream);
if (!rStream.GetError())
{ {
const sal_uLong nDataStart = rOStm.Tell(); // Write the written length th the header
const sal_uLong nCurrentPosition = rStream.Tell();
if (ImplIsSupportedGraphic()) rStream.Seek(nDataFieldPos);
swapOutGraphic(rOStm); rStream.WriteInt32(nCurrentPosition - nDataStart);
rStream.Seek(nCurrentPosition);
if( !rOStm.GetError() ) bRet = true;
{
const sal_uLong nStmPos2 = rOStm.Tell();
rOStm.Seek( nDataFieldPos );
rOStm.WriteInt32( nStmPos2 - nDataStart );
rOStm.Seek( nStmPos2 );
bRet = true;
}
} }
rOStm.SetEndian(nOldFormat);
return bRet; return bRet;
} }
@@ -1390,46 +1263,67 @@ bool ImpGraphic::swapOut()
if (isSwappedOut()) if (isSwappedOut())
return false; return false;
// Create a temp filename for the swap file
utl::TempFile aTempFile;
const INetURLObject aTempFileURL(aTempFile.GetURL());
// Create a swap file
auto pSwapFile = o3tl::make_shared<ImpSwapFile>(aTempFileURL, getOriginURL());
bool bResult = false; bool bResult = false;
// Open a stream to write the swap file to // We have GfxLink so we have the source available
if (mpGfxLink && mpGfxLink->IsNative())
{ {
std::unique_ptr<SvStream> xOutputStream = pSwapFile->openOutputStream();
if (!xOutputStream)
return false;
// Write to stream
xOutputStream->SetVersion(SOFFICE_FILEFORMAT_50);
xOutputStream->SetCompressMode(SvStreamCompressFlags::NATIVE);
xOutputStream->SetBufferSize(GRAPHIC_STREAMBUFSIZE);
if (!xOutputStream->GetError() && swapOutContent(*xOutputStream))
{
xOutputStream->Flush();
bResult = !xOutputStream->GetError();
}
}
// Check if writing was successful
if (bResult)
{
// We have swapped out, so can clean memory and prepare swap info
createSwapInfo(); createSwapInfo();
ImplClearGraphics(); ImplClearGraphics();
mpSwapFile = std::move(pSwapFile); // reset the swap file
mpSwapFile.reset();
// mark as swapped out
mbSwapOut = true; mbSwapOut = true;
// Signal to manager that we have swapped out // Signal to manager that we have swapped out
vcl::graphic::Manager::get().swappedOut(this); vcl::graphic::Manager::get().swappedOut(this);
bResult = true;
}
else
{
// Create a temp filename for the swap file
utl::TempFile aTempFile;
const INetURLObject aTempFileURL(aTempFile.GetURL());
// Create a swap file
auto pSwapFile = o3tl::make_shared<ImpSwapFile>(aTempFileURL, getOriginURL());
// Open a stream to write the swap file to
{
std::unique_ptr<SvStream> xOutputStream = pSwapFile->openOutputStream();
if (!xOutputStream)
return false;
// Write to stream
xOutputStream->SetVersion(SOFFICE_FILEFORMAT_50);
xOutputStream->SetCompressMode(SvStreamCompressFlags::NATIVE);
xOutputStream->SetBufferSize(GRAPHIC_STREAMBUFSIZE);
if (!xOutputStream->GetError() && swapOutContent(*xOutputStream))
{
xOutputStream->Flush();
bResult = !xOutputStream->GetError();
}
}
// Check if writing was successful
if (bResult)
{
// We have swapped out, so can clean memory and prepare swap info
createSwapInfo();
ImplClearGraphics();
mpSwapFile = std::move(pSwapFile);
mbSwapOut = true;
// Signal to manager that we have swapped out
vcl::graphic::Manager::get().swappedOut(this);
}
} }
return bResult; return bResult;
@@ -1439,11 +1333,13 @@ bool ImpGraphic::ensureAvailable() const
{ {
auto pThis = const_cast<ImpGraphic*>(this); auto pThis = const_cast<ImpGraphic*>(this);
bool bResult = true;
if (isSwappedOut()) if (isSwappedOut())
return pThis->swapIn(); bResult = pThis->swapIn();
pThis->maLastUsed = std::chrono::high_resolution_clock::now(); pThis->maLastUsed = std::chrono::high_resolution_clock::now();
return true; return bResult;
} }
bool ImpGraphic::loadPrepared() bool ImpGraphic::loadPrepared()
@@ -1470,30 +1366,95 @@ void ImpGraphic::updateFromLoadedGraphic(ImpGraphic* graphic)
maGraphicExternalLink = aLink; maGraphicExternalLink = aLink;
} }
void ImpGraphic::restoreFromSwapInfo()
{
// Reset the parameters
if (!maBitmapEx.IsEmpty())
{
maBitmapEx.SetPrefMapMode(maSwapInfo.maPrefMapMode);
maBitmapEx.SetPrefSize(maSwapInfo.maPrefSize);
}
if (meType == GraphicType::GdiMetafile)
{
maMetaFile.SetPrefMapMode(maSwapInfo.maPrefMapMode);
maMetaFile.SetPrefSize(maSwapInfo.maPrefSize);
}
if (ImplIsAnimated())
{
auto & rAnimationBitmap = const_cast<BitmapEx&>(mpAnimation->GetBitmapEx());
rAnimationBitmap.SetPrefMapMode(maSwapInfo.maPrefMapMode);
rAnimationBitmap.SetPrefSize(maSwapInfo.maPrefSize);
}
if (maVectorGraphicData)
{
maExPrefSize = maSwapInfo.maPrefSize;
maVectorGraphicData->setPageIndex(maSwapInfo.mnPageIndex);
}
}
bool ImpGraphic::swapIn() bool ImpGraphic::swapIn()
{ {
bool bRet = false;
if (!isSwappedOut()) if (!isSwappedOut())
return bRet; return false;
bool bReturn = false;
if (mbPrepared) if (mbPrepared)
{ {
bRet = loadPrepared(); bReturn = loadPrepared();
}
else if (mpGfxLink && mpGfxLink->IsNative())
{
Graphic aGraphic;
if (!mpGfxLink->LoadNative(aGraphic))
return false;
auto & rImpGraphic = *aGraphic.ImplGetImpGraphic();
if (meType != rImpGraphic.meType)
return false;
// Move over only graphic content
mpAnimation.reset();
if (rImpGraphic.mpAnimation)
{
mpAnimation = std::make_unique<Animation>(*rImpGraphic.mpAnimation);
maBitmapEx = mpAnimation->GetBitmapEx();
}
else
{
maBitmapEx = rImpGraphic.maBitmapEx;
}
maMetaFile = rImpGraphic.maMetaFile;
maVectorGraphicData = rImpGraphic.maVectorGraphicData;
// Set to 0, to force recalculation
mnSizeBytes = 0;
mnChecksum = 0;
restoreFromSwapInfo();
maLastUsed = std::chrono::high_resolution_clock::now();
mbSwapOut = false;
bReturn = true;
} }
else else
{ {
OUString aSwapURL; OUString aSwapURL;
if( mpSwapFile ) if (mpSwapFile)
aSwapURL = mpSwapFile->getSwapURL().GetMainURL( INetURLObject::DecodeMechanism::NONE ); aSwapURL = mpSwapFile->getSwapURL().GetMainURL(INetURLObject::DecodeMechanism::NONE);
if( !aSwapURL.isEmpty() ) if (!aSwapURL.isEmpty())
{ {
std::unique_ptr<SvStream> xStream; std::unique_ptr<SvStream> xStream;
try try
{ {
xStream = ::utl::UcbStreamHelper::CreateStream( aSwapURL, StreamMode::READWRITE | StreamMode::SHARE_DENYWRITE ); xStream = ::utl::UcbStreamHelper::CreateStream(aSwapURL, StreamMode::READWRITE | StreamMode::SHARE_DENYWRITE);
} }
catch( const css::uno::Exception& ) catch( const css::uno::Exception& )
{ {
@@ -1501,51 +1462,43 @@ bool ImpGraphic::swapIn()
if (xStream) if (xStream)
{ {
xStream->SetVersion( SOFFICE_FILEFORMAT_50 ); xStream->SetVersion(SOFFICE_FILEFORMAT_50);
xStream->SetCompressMode( SvStreamCompressFlags::NATIVE ); xStream->SetCompressMode(SvStreamCompressFlags::NATIVE);
xStream->SetBufferSize(GRAPHIC_STREAMBUFSIZE);
bReturn = swapInFromStream(*xStream);
bRet = swapInFromStream(*xStream);
xStream.reset(); xStream.reset();
restoreFromSwapInfo();
if (mpSwapFile) if (mpSwapFile)
setOriginURL(mpSwapFile->getOriginURL()); setOriginURL(mpSwapFile->getOriginURL());
mpSwapFile.reset(); mpSwapFile.reset();
} }
} }
} }
if (bRet) if (bReturn)
vcl::graphic::Manager::get().swappedIn(this); vcl::graphic::Manager::get().swappedIn(this);
return bRet; return bReturn;
} }
bool ImpGraphic::swapInFromStream(SvStream& rStream) bool ImpGraphic::swapInFromStream(SvStream& rStream)
{ {
bool bRet = false; bool bRet = false;
rStream.SetBufferSize(GRAPHIC_STREAMBUFSIZE);
if (rStream.GetError()) if (rStream.GetError())
return false; return false;
//keep the swap file alive, because its quite possibly the backing storage ImplClearGraphics();
//for xIStm mnSizeBytes = 0;
std::shared_ptr<ImpSwapFile> xSwapFile(std::move(mpSwapFile)); mnChecksum = 0;
assert(!mpSwapFile);
std::shared_ptr<GraphicReader> xContext(std::move(mpContext));
assert(!mpContext);
bool bDummyContext = mbDummyContext;
mbDummyContext = false;
bRet = swapInContent(rStream); bRet = swapInContent(rStream);
//restore ownership of the swap file and context
mpSwapFile = std::move(xSwapFile);
mpContext = std::move(xContext);
mbDummyContext = bDummyContext;
if (!bRet) if (!bRet)
{ {
//throw away swapfile, etc. //throw away swapfile, etc.
@@ -1557,165 +1510,116 @@ bool ImpGraphic::swapInFromStream(SvStream& rStream)
return bRet; return bRet;
} }
void ImpGraphic::swapInGraphic(SvStream& rStream) bool ImpGraphic::swapInGraphic(SvStream& rStream)
{ {
bool bReturn = false;
if (rStream.GetError()) if (rStream.GetError())
return; return bReturn;
const sal_uLong nStmPos1 = rStream.Tell(); if (meType == GraphicType::Bitmap)
sal_uInt32 nID;
ImplClear();
// read Id
rStream.ReadUInt32(nID);
// if there is no more data, avoid further expensive
// reading which will create VDevs and other stuff, just to
// read nothing. CAUTION: Eof is only true AFTER reading another
// byte, a speciality of SvMemoryStream (!)
if (!rStream.good())
return;
if (NATIVE_FORMAT_50 == nID)
{ {
Graphic aGraphic; sal_Int32 nContentType = -1;
GfxLink aLink; rStream.ReadInt32(nContentType);
if (nContentType < 0)
return false;
// read compat info, destructor writes stuff into the header auto eContentType = static_cast<GraphicContentType>(nContentType);
switch (eContentType)
{ {
VersionCompat aCompat(rStream, StreamMode::READ); case GraphicContentType::Bitmap:
}
TypeSerializer aSerializer(rStream);
aSerializer.readGfxLink(aLink);
// set dummy link to avoid creation of additional link after filtering;
// we set a default link to avoid unnecessary swapping of native data
aGraphic.SetGfxLink(std::make_shared<GfxLink>());
if (!rStream.GetError() && aLink.LoadNative(aGraphic))
{
// set link only, if no other link was set
const bool bSetLink = !mpGfxLink;
// assign graphic
*this = *aGraphic.ImplGetImpGraphic();
if (aLink.IsPrefMapModeValid())
ImplSetPrefMapMode(aLink.GetPrefMapMode());
if (aLink.IsPrefSizeValid())
ImplSetPrefSize(aLink.GetPrefSize());
if (bSetLink)
ImplSetLink(std::make_shared<GfxLink>(aLink));
}
else
{
rStream.Seek(nStmPos1);
rStream.SetError(ERRCODE_IO_WRONGFORMAT);
}
return;
}
BitmapEx aBmpEx;
const SvStreamEndian nOldFormat = rStream.GetEndian();
rStream.SeekRel(-4);
rStream.SetEndian(SvStreamEndian::LITTLE);
ReadDIBBitmapEx(aBmpEx, rStream);
if (!rStream.GetError())
{
sal_uInt32 nMagic1(0);
sal_uInt32 nMagic2(0);
sal_uLong nActPos = rStream.Tell();
rStream.ReadUInt32(nMagic1);
rStream.ReadUInt32(nMagic2);
rStream.Seek(nActPos);
*this = ImpGraphic(aBmpEx);
if (!rStream.GetError() && (0x5344414e == nMagic1) && (0x494d4931 == nMagic2))
{
mpAnimation = std::make_unique<Animation>();
ReadAnimation(rStream, *mpAnimation);
// #108077# manually set loaded BmpEx to Animation
// (which skips loading its BmpEx if already done)
mpAnimation->SetBitmapEx(aBmpEx);
}
else
rStream.ResetError();
}
else
{
GDIMetaFile aMetaFile;
rStream.Seek(nStmPos1);
rStream.ResetError();
ReadGDIMetaFile(rStream, aMetaFile);
if (!rStream.GetError())
{
*this = aMetaFile;
}
else
{
ErrCode nOrigError = rStream.GetErrorCode();
// try to stream in Svg defining data (length, byte array and evtl. path)
// See below (operator<<) for more information
sal_uInt32 nMagic;
rStream.Seek(nStmPos1);
rStream.ResetError();
rStream.ReadUInt32( nMagic );
if (constSvgMagic == nMagic || constWmfMagic == nMagic || constEmfMagic == nMagic || constPdfMagic == nMagic)
{ {
sal_uInt32 nVectorGraphicDataArrayLength(0); BitmapEx aBitmapEx;
rStream.ReadUInt32(nVectorGraphicDataArrayLength); ReadDIBBitmapEx(aBitmapEx, rStream);
if (!rStream.GetError())
if (nVectorGraphicDataArrayLength)
{ {
VectorGraphicDataArray aNewData(nVectorGraphicDataArrayLength); maBitmapEx = aBitmapEx;
bReturn = true;
}
}
break;
rStream.ReadBytes(aNewData.getArray(), nVectorGraphicDataArrayLength); case GraphicContentType::Animation:
OUString aPath = rStream.ReadUniOrByteString(rStream.GetStreamCharSet()); {
auto pAnimation = std::make_unique<Animation>();
ReadAnimation(rStream, *pAnimation);
if (!rStream.GetError())
{
mpAnimation = std::move(pAnimation);
maBitmapEx = mpAnimation->GetBitmapEx();
bReturn = true;
}
}
break;
if (!rStream.GetError()) case GraphicContentType::Vector:
{
// try to stream in Svg defining data (length, byte array and evtl. path)
// See below (operator<<) for more information
sal_uInt32 nMagic;
rStream.ReadUInt32(nMagic);
if (constSvgMagic == nMagic || constWmfMagic == nMagic || constEmfMagic == nMagic || constPdfMagic == nMagic)
{
sal_uInt32 nVectorGraphicDataArrayLength(0);
rStream.ReadUInt32(nVectorGraphicDataArrayLength);
if (nVectorGraphicDataArrayLength)
{ {
VectorGraphicDataType aDataType(VectorGraphicDataType::Svg); VectorGraphicDataArray aNewData(nVectorGraphicDataArrayLength);
if (constWmfMagic == nMagic) rStream.ReadBytes(aNewData.getArray(), nVectorGraphicDataArrayLength);
OUString aPath = rStream.ReadUniOrByteString(rStream.GetStreamCharSet());
if (rStream.GetError())
return false;
VectorGraphicDataType aDataType;
switch (nMagic)
{ {
aDataType = VectorGraphicDataType::Wmf; case constSvgMagic:
} aDataType = VectorGraphicDataType::Svg;
else if (constEmfMagic == nMagic) break;
{ case constWmfMagic:
aDataType = VectorGraphicDataType::Emf; aDataType = VectorGraphicDataType::Wmf;
} break;
else if (constPdfMagic == nMagic) case constEmfMagic:
{ aDataType = VectorGraphicDataType::Emf;
aDataType = VectorGraphicDataType::Pdf; break;
case constPdfMagic:
aDataType = VectorGraphicDataType::Pdf;
break;
default:
return false;
} }
auto aVectorGraphicDataPtr = std::make_shared<VectorGraphicData>(aNewData, aPath, aDataType); auto aVectorGraphicDataPtr = std::make_shared<VectorGraphicData>(aNewData, aPath, aDataType);
*this = ImpGraphic(aVectorGraphicDataPtr);
if (!rStream.GetError())
{
maVectorGraphicData = aVectorGraphicDataPtr;
bReturn = true;
}
} }
} }
} }
else break;
{
rStream.SetError(nOrigError);
}
rStream.Seek(nStmPos1);
} }
} }
else if (meType == GraphicType::GdiMetafile)
rStream.SetEndian(nOldFormat); {
GDIMetaFile aMetaFile;
ReadGDIMetaFile(rStream, aMetaFile);
if (!rStream.GetError())
{
maMetaFile = aMetaFile;
bReturn = true;
}
}
return bReturn;
} }
void ImpGraphic::ImplSetLink(const std::shared_ptr<GfxLink>& rGfxLink) void ImpGraphic::ImplSetLink(const std::shared_ptr<GfxLink>& rGfxLink)