introduce graphic format detector (and basic metadata)
Currently we detect a graphic format in a ImpPeekGraphicFormat
method, which is called when reading a graphic from a stream.
The code is quite convoluted and doesn't help with readability
of graphicfilter.cxx
Additionally there exists an detection code written for the UNO
class GraphicDescriptor, which duplicates the detection, but can
in addition also extract other metadata at the same time.
This introduces the initial implementation of GraphicFormatDetector
class. It will first replace the code in ImpPeekGraphicFormat
to detect the graphic format and then later also be extended to
do what the GraphicDescriptor is doing. But currently it only
duplicates the implementation of ImpPeekGraphicFormat.
The problem with both of the current solution is that there are
not any tests written. For the GraphicFormatDetector however the
approach is to add test cases to check the current implementation
and only then refactor the code.
Change-Id: Idb9d3859b4380e3f15237d97aff969ce81e631dd
Reviewed-on: https://gerrit.libreoffice.org/72086
Tested-by: Jenkins
Reviewed-by: Tomaž Vajngerl <quikee@gmail.com>
diff --git a/vcl/CppunitTest_vcl_graphic_test.mk b/vcl/CppunitTest_vcl_graphic_test.mk
index 7494826..d339cd2 100644
--- a/vcl/CppunitTest_vcl_graphic_test.mk
+++ b/vcl/CppunitTest_vcl_graphic_test.mk
@@ -12,6 +12,7 @@ $(eval $(call gb_CppunitTest_CppunitTest,vcl_graphic_test))
$(eval $(call gb_CppunitTest_add_exception_objects,vcl_graphic_test, \
vcl/qa/cppunit/GraphicTest \
vcl/qa/cppunit/GraphicDescriptorTest \
vcl/qa/cppunit/GraphicFormatDetectorTest \
))
$(eval $(call gb_CppunitTest_use_externals,vcl_graphic_test,\
diff --git a/vcl/Library_vcl.mk b/vcl/Library_vcl.mk
index 9621718..b7f85fe 100644
--- a/vcl/Library_vcl.mk
+++ b/vcl/Library_vcl.mk
@@ -395,6 +395,7 @@ $(eval $(call gb_Library_add_exception_objects,vcl,\
vcl/source/filter/graphicfilter2 \
vcl/source/filter/GraphicNativeTransform \
vcl/source/filter/GraphicNativeMetadata \
vcl/source/filter/GraphicFormatDetector \
vcl/source/filter/igif/decode \
vcl/source/filter/igif/gifread \
vcl/source/filter/ipdf/pdfread \
diff --git a/vcl/inc/graphic/GraphicFormatDetector.hxx b/vcl/inc/graphic/GraphicFormatDetector.hxx
new file mode 100644
index 0000000..e914ee6
--- /dev/null
+++ b/vcl/inc/graphic/GraphicFormatDetector.hxx
@@ -0,0 +1,76 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
#ifndef INCLUDED_VCL_INC_GRAPHICFORMATDETECTOR_HXX
#define INCLUDED_VCL_INC_GRAPHICFORMATDETECTOR_HXX
#include <tools/stream.hxx>
#include <vector>
VCL_DLLPUBLIC bool ImpPeekGraphicFormat(SvStream& rStream, OUString& rFormatExtension, bool bTest);
namespace vcl
{
class VCL_DLLPUBLIC GraphicFormatDetector
{
public:
SvStream& mrStream;
OUString maExtension;
std::vector<sal_uInt8> maFirstBytes;
sal_uInt32 mnFirstLong;
sal_uInt32 mnSecondLong;
sal_uInt64 mnStreamPosition;
sal_uInt64 mnStreamLength;
OUString msDetectedFormat;
GraphicFormatDetector(SvStream& rStream, OUString const& rFormatExtension);
bool detect();
bool checkMET();
bool checkBMP();
bool checkWMForEMF();
bool checkPCX();
bool checkTIF();
bool checkGIF();
bool checkPNG();
bool checkJPG();
bool checkSVM();
bool checkPCD();
bool checkPSD();
bool checkEPS();
bool checkDXF();
bool checkPCT();
bool checkPBMorPGMorPPM();
bool checkRAS();
bool checkXPM();
bool checkXBM();
bool checkSVG();
bool checkTGA();
bool checkMOV();
bool checkPDF();
};
}
#endif // INCLUDED_VCL_INC_GRAPHICFORMATDETECTOR_HXX
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/qa/cppunit/GraphicFormatDetectorTest.cxx b/vcl/qa/cppunit/GraphicFormatDetectorTest.cxx
new file mode 100644
index 0000000..691b341
--- /dev/null
+++ b/vcl/qa/cppunit/GraphicFormatDetectorTest.cxx
@@ -0,0 +1,300 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#include <cppunit/TestAssert.h>
#include <cppunit/TestFixture.h>
#include <cppunit/extensions/HelperMacros.h>
#include <cppunit/plugin/TestPlugIn.h>
#include <unotest/bootstrapfixturebase.hxx>
#include <com/sun/star/uno/Sequence.hxx>
#include <com/sun/star/beans/PropertyValue.hpp>
#include <vcl/graph.hxx>
#include <vcl/graphicfilter.hxx>
#include <graphic/GraphicFormatDetector.hxx>
#include <tools/stream.hxx>
using namespace css;
namespace
{
class GraphicFormatDetectorTest : public test::BootstrapFixtureBase
{
OUString getFullUrl(const OUString& sFileName)
{
return m_directories.getURLFromSrc("/vcl/qa/cppunit/data/") + sFileName;
}
void testDetectMET();
void testDetectBMP();
void testDetectWMF();
void testDetectPCX();
void testDetectJPG();
void testDetectPNG();
void testDetectGIF();
void testDetectPSD();
void testDetectTGA();
void testDetectTIF();
void testDetectXBM();
void testDetectXPM();
void testDetectSVG();
void testDetectSVGZ();
void testDetectPDF();
CPPUNIT_TEST_SUITE(GraphicFormatDetectorTest);
CPPUNIT_TEST(testDetectMET);
CPPUNIT_TEST(testDetectBMP);
CPPUNIT_TEST(testDetectWMF);
CPPUNIT_TEST(testDetectPCX);
CPPUNIT_TEST(testDetectJPG);
CPPUNIT_TEST(testDetectPNG);
CPPUNIT_TEST(testDetectGIF);
CPPUNIT_TEST(testDetectPSD);
CPPUNIT_TEST(testDetectTGA);
CPPUNIT_TEST(testDetectTIF);
CPPUNIT_TEST(testDetectXBM);
CPPUNIT_TEST(testDetectXPM);
CPPUNIT_TEST(testDetectSVG);
CPPUNIT_TEST(testDetectSVGZ);
CPPUNIT_TEST(testDetectPDF);
CPPUNIT_TEST_SUITE_END();
};
void GraphicFormatDetectorTest::testDetectMET()
{
SvFileStream aFileStream(getFullUrl("TypeDetectionExample.met"), StreamMode::READ);
vcl::GraphicFormatDetector aDetector(aFileStream, "MET");
CPPUNIT_ASSERT(aDetector.detect());
CPPUNIT_ASSERT(aDetector.checkMET());
aFileStream.Seek(aDetector.mnStreamPosition);
OUString rFormatExtension;
CPPUNIT_ASSERT(ImpPeekGraphicFormat(aFileStream, rFormatExtension, false));
CPPUNIT_ASSERT_EQUAL(OUString("MET"), rFormatExtension);
}
void GraphicFormatDetectorTest::testDetectBMP()
{
SvFileStream aFileStream(getFullUrl("TypeDetectionExample.bmp"), StreamMode::READ);
vcl::GraphicFormatDetector aDetector(aFileStream, "BMP");
CPPUNIT_ASSERT(aDetector.detect());
CPPUNIT_ASSERT(aDetector.checkBMP());
aFileStream.Seek(aDetector.mnStreamPosition);
OUString rFormatExtension;
CPPUNIT_ASSERT(ImpPeekGraphicFormat(aFileStream, rFormatExtension, false));
CPPUNIT_ASSERT_EQUAL(OUString("BMP"), rFormatExtension);
}
void GraphicFormatDetectorTest::testDetectWMF()
{
SvFileStream aFileStream(getFullUrl("TypeDetectionExample.wmf"), StreamMode::READ);
vcl::GraphicFormatDetector aDetector(aFileStream, "WMF");
CPPUNIT_ASSERT(aDetector.detect());
CPPUNIT_ASSERT(aDetector.checkWMForEMF());
aFileStream.Seek(aDetector.mnStreamPosition);
OUString rFormatExtension;
CPPUNIT_ASSERT(ImpPeekGraphicFormat(aFileStream, rFormatExtension, false));
CPPUNIT_ASSERT_EQUAL(OUString("WMF"), rFormatExtension);
}
void GraphicFormatDetectorTest::testDetectPCX()
{
SvFileStream aFileStream(getFullUrl("TypeDetectionExample.pcx"), StreamMode::READ);
vcl::GraphicFormatDetector aDetector(aFileStream, "PCX");
CPPUNIT_ASSERT(aDetector.detect());
CPPUNIT_ASSERT(aDetector.checkPCX());
aFileStream.Seek(aDetector.mnStreamPosition);
OUString rFormatExtension;
CPPUNIT_ASSERT(ImpPeekGraphicFormat(aFileStream, rFormatExtension, false));
CPPUNIT_ASSERT_EQUAL(OUString("PCX"), rFormatExtension);
}
void GraphicFormatDetectorTest::testDetectJPG()
{
SvFileStream aFileStream(getFullUrl("TypeDetectionExample.jpg"), StreamMode::READ);
vcl::GraphicFormatDetector aDetector(aFileStream, "JPG");
CPPUNIT_ASSERT(aDetector.detect());
CPPUNIT_ASSERT(aDetector.checkJPG());
aFileStream.Seek(aDetector.mnStreamPosition);
OUString rFormatExtension;
CPPUNIT_ASSERT(ImpPeekGraphicFormat(aFileStream, rFormatExtension, false));
CPPUNIT_ASSERT_EQUAL(OUString("JPG"), rFormatExtension);
}
void GraphicFormatDetectorTest::testDetectPNG()
{
SvFileStream aFileStream(getFullUrl("TypeDetectionExample.png"), StreamMode::READ);
vcl::GraphicFormatDetector aDetector(aFileStream, "PNG");
CPPUNIT_ASSERT(aDetector.detect());
CPPUNIT_ASSERT(aDetector.checkPNG());
aFileStream.Seek(aDetector.mnStreamPosition);
OUString rFormatExtension;
CPPUNIT_ASSERT(ImpPeekGraphicFormat(aFileStream, rFormatExtension, false));
CPPUNIT_ASSERT_EQUAL(OUString("PNG"), rFormatExtension);
}
void GraphicFormatDetectorTest::testDetectGIF()
{
SvFileStream aFileStream(getFullUrl("TypeDetectionExample.gif"), StreamMode::READ);
vcl::GraphicFormatDetector aDetector(aFileStream, "GIF");
CPPUNIT_ASSERT(aDetector.detect());
CPPUNIT_ASSERT(aDetector.checkGIF());
aFileStream.Seek(aDetector.mnStreamPosition);
OUString rFormatExtension;
CPPUNIT_ASSERT(ImpPeekGraphicFormat(aFileStream, rFormatExtension, false));
CPPUNIT_ASSERT_EQUAL(OUString("GIF"), rFormatExtension);
}
void GraphicFormatDetectorTest::testDetectPSD()
{
SvFileStream aFileStream(getFullUrl("TypeDetectionExample.psd"), StreamMode::READ);
vcl::GraphicFormatDetector aDetector(aFileStream, "PSD");
CPPUNIT_ASSERT(aDetector.detect());
CPPUNIT_ASSERT(aDetector.checkPSD());
aFileStream.Seek(aDetector.mnStreamPosition);
OUString rFormatExtension;
CPPUNIT_ASSERT(ImpPeekGraphicFormat(aFileStream, rFormatExtension, false));
CPPUNIT_ASSERT_EQUAL(OUString("PSD"), rFormatExtension);
}
void GraphicFormatDetectorTest::testDetectTGA()
{
SvFileStream aFileStream(getFullUrl("TypeDetectionExample.tga"), StreamMode::READ);
vcl::GraphicFormatDetector aDetector(aFileStream, "TGA");
CPPUNIT_ASSERT(aDetector.detect());
CPPUNIT_ASSERT(aDetector.checkTGA());
aFileStream.Seek(aDetector.mnStreamPosition);
OUString rFormatExtension("TGA"); // detection is based on extension only
CPPUNIT_ASSERT(ImpPeekGraphicFormat(aFileStream, rFormatExtension, false));
CPPUNIT_ASSERT_EQUAL(OUString("TGA"), rFormatExtension);
}
void GraphicFormatDetectorTest::testDetectTIF()
{
SvFileStream aFileStream(getFullUrl("TypeDetectionExample.tif"), StreamMode::READ);
vcl::GraphicFormatDetector aDetector(aFileStream, "TIF");
CPPUNIT_ASSERT(aDetector.detect());
CPPUNIT_ASSERT(aDetector.checkTIF());
aFileStream.Seek(aDetector.mnStreamPosition);
OUString rFormatExtension;
CPPUNIT_ASSERT(ImpPeekGraphicFormat(aFileStream, rFormatExtension, false));
CPPUNIT_ASSERT_EQUAL(OUString("TIF"), rFormatExtension);
}
void GraphicFormatDetectorTest::testDetectXBM()
{
SvFileStream aFileStream(getFullUrl("TypeDetectionExample.xbm"), StreamMode::READ);
vcl::GraphicFormatDetector aDetector(aFileStream, "XBM");
CPPUNIT_ASSERT(aDetector.detect());
CPPUNIT_ASSERT(aDetector.checkXBM());
aFileStream.Seek(aDetector.mnStreamPosition);
OUString rFormatExtension;
CPPUNIT_ASSERT(ImpPeekGraphicFormat(aFileStream, rFormatExtension, false));
CPPUNIT_ASSERT_EQUAL(OUString("XBM"), rFormatExtension);
}
void GraphicFormatDetectorTest::testDetectXPM()
{
SvFileStream aFileStream(getFullUrl("TypeDetectionExample.xpm"), StreamMode::READ);
vcl::GraphicFormatDetector aDetector(aFileStream, "XPM");
CPPUNIT_ASSERT(aDetector.detect());
CPPUNIT_ASSERT(aDetector.checkXPM());
aFileStream.Seek(aDetector.mnStreamPosition);
OUString rFormatExtension;
CPPUNIT_ASSERT(ImpPeekGraphicFormat(aFileStream, rFormatExtension, false));
CPPUNIT_ASSERT_EQUAL(OUString("XPM"), rFormatExtension);
}
void GraphicFormatDetectorTest::testDetectSVG()
{
SvFileStream aFileStream(getFullUrl("TypeDetectionExample.svg"), StreamMode::READ);
vcl::GraphicFormatDetector aDetector(aFileStream, "SVG");
CPPUNIT_ASSERT(aDetector.detect());
CPPUNIT_ASSERT(aDetector.checkSVG());
aFileStream.Seek(aDetector.mnStreamPosition);
OUString rFormatExtension;
CPPUNIT_ASSERT(ImpPeekGraphicFormat(aFileStream, rFormatExtension, false));
CPPUNIT_ASSERT_EQUAL(OUString("SVG"), rFormatExtension);
}
void GraphicFormatDetectorTest::testDetectSVGZ()
{
SvFileStream aFileStream(getFullUrl("TypeDetectionExample.svgz"), StreamMode::READ);
vcl::GraphicFormatDetector aDetector(aFileStream, "SVG");
CPPUNIT_ASSERT(aDetector.detect());
CPPUNIT_ASSERT(aDetector.checkSVG());
aFileStream.Seek(aDetector.mnStreamPosition);
OUString rFormatExtension;
CPPUNIT_ASSERT(ImpPeekGraphicFormat(aFileStream, rFormatExtension, false));
CPPUNIT_ASSERT_EQUAL(OUString("SVG"), rFormatExtension);
}
void GraphicFormatDetectorTest::testDetectPDF()
{
SvFileStream aFileStream(getFullUrl("TypeDetectionExample.pdf"), StreamMode::READ);
vcl::GraphicFormatDetector aDetector(aFileStream, "PDF");
CPPUNIT_ASSERT(aDetector.detect());
CPPUNIT_ASSERT(aDetector.checkPDF());
aFileStream.Seek(aDetector.mnStreamPosition);
OUString rFormatExtension;
CPPUNIT_ASSERT(ImpPeekGraphicFormat(aFileStream, rFormatExtension, false));
CPPUNIT_ASSERT_EQUAL(OUString("PDF"), rFormatExtension);
}
} // namespace
CPPUNIT_TEST_SUITE_REGISTRATION(GraphicFormatDetectorTest);
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/qa/cppunit/data/TypeDetectionExample.bmp b/vcl/qa/cppunit/data/TypeDetectionExample.bmp
new file mode 100644
index 0000000..5197e42
--- /dev/null
+++ b/vcl/qa/cppunit/data/TypeDetectionExample.bmp
Binary files differ
diff --git a/vcl/qa/cppunit/data/TypeDetectionExample.gif b/vcl/qa/cppunit/data/TypeDetectionExample.gif
new file mode 100644
index 0000000..b33eb4f
--- /dev/null
+++ b/vcl/qa/cppunit/data/TypeDetectionExample.gif
Binary files differ
diff --git a/vcl/qa/cppunit/data/TypeDetectionExample.jpg b/vcl/qa/cppunit/data/TypeDetectionExample.jpg
new file mode 100644
index 0000000..b8436ea
--- /dev/null
+++ b/vcl/qa/cppunit/data/TypeDetectionExample.jpg
Binary files differ
diff --git a/vcl/qa/cppunit/data/TypeDetectionExample.met b/vcl/qa/cppunit/data/TypeDetectionExample.met
new file mode 100644
index 0000000..7635e84
--- /dev/null
+++ b/vcl/qa/cppunit/data/TypeDetectionExample.met
Binary files differ
diff --git a/vcl/qa/cppunit/data/TypeDetectionExample.pcx b/vcl/qa/cppunit/data/TypeDetectionExample.pcx
new file mode 100644
index 0000000..6393234
--- /dev/null
+++ b/vcl/qa/cppunit/data/TypeDetectionExample.pcx
Binary files differ
diff --git a/vcl/qa/cppunit/data/TypeDetectionExample.pdf b/vcl/qa/cppunit/data/TypeDetectionExample.pdf
new file mode 100644
index 0000000..b68bff5
--- /dev/null
+++ b/vcl/qa/cppunit/data/TypeDetectionExample.pdf
Binary files differ
diff --git a/vcl/qa/cppunit/data/TypeDetectionExample.png b/vcl/qa/cppunit/data/TypeDetectionExample.png
new file mode 100644
index 0000000..f73f5fd
--- /dev/null
+++ b/vcl/qa/cppunit/data/TypeDetectionExample.png
Binary files differ
diff --git a/vcl/qa/cppunit/data/TypeDetectionExample.psd b/vcl/qa/cppunit/data/TypeDetectionExample.psd
new file mode 100644
index 0000000..8282b14
--- /dev/null
+++ b/vcl/qa/cppunit/data/TypeDetectionExample.psd
Binary files differ
diff --git a/vcl/qa/cppunit/data/TypeDetectionExample.svg b/vcl/qa/cppunit/data/TypeDetectionExample.svg
new file mode 100644
index 0000000..e23e44c
--- /dev/null
+++ b/vcl/qa/cppunit/data/TypeDetectionExample.svg
@@ -0,0 +1,4 @@
<svg width="10" height="10" version="1.1" viewBox="0 0 10 10" xmlns="http://www.w3.org/2000/svg">
<rect x="0" y="0" width="10" height="10" fill="#ffffff"/>
<rect x="1" y="1" width="8" height="8" fill="#72d1c8"/>
</svg>
diff --git a/vcl/qa/cppunit/data/TypeDetectionExample.svgz b/vcl/qa/cppunit/data/TypeDetectionExample.svgz
new file mode 100644
index 0000000..17c1bcc
--- /dev/null
+++ b/vcl/qa/cppunit/data/TypeDetectionExample.svgz
Binary files differ
diff --git a/vcl/qa/cppunit/data/TypeDetectionExample.tga b/vcl/qa/cppunit/data/TypeDetectionExample.tga
new file mode 100644
index 0000000..870c88b
--- /dev/null
+++ b/vcl/qa/cppunit/data/TypeDetectionExample.tga
Binary files differ
diff --git a/vcl/qa/cppunit/data/TypeDetectionExample.tif b/vcl/qa/cppunit/data/TypeDetectionExample.tif
new file mode 100644
index 0000000..dc74dc9
--- /dev/null
+++ b/vcl/qa/cppunit/data/TypeDetectionExample.tif
Binary files differ
diff --git a/vcl/qa/cppunit/data/TypeDetectionExample.wmf b/vcl/qa/cppunit/data/TypeDetectionExample.wmf
new file mode 100644
index 0000000..7ed7069
--- /dev/null
+++ b/vcl/qa/cppunit/data/TypeDetectionExample.wmf
Binary files differ
diff --git a/vcl/qa/cppunit/data/TypeDetectionExample.xbm b/vcl/qa/cppunit/data/TypeDetectionExample.xbm
new file mode 100644
index 0000000..b40d1a4
--- /dev/null
+++ b/vcl/qa/cppunit/data/TypeDetectionExample.xbm
@@ -0,0 +1,5 @@
#define sample_width 10
#define sample_height 10
static unsigned char sample_bits[] = {
0x00, 0x00, 0xfe, 0x01, 0xfe, 0x01, 0xfe, 0x01, 0xfe, 0x01, 0xfe, 0x01,
0xfe, 0x01, 0xfe, 0x01, 0xfe, 0x01, 0x00, 0x00 };
diff --git a/vcl/qa/cppunit/data/TypeDetectionExample.xpm b/vcl/qa/cppunit/data/TypeDetectionExample.xpm
new file mode 100644
index 0000000..7b9b943
--- /dev/null
+++ b/vcl/qa/cppunit/data/TypeDetectionExample.xpm
@@ -0,0 +1,15 @@
/* XPM */
static char * sample_xpm[] = {
"10 10 2 1",
" c #FFFFFF",
". c #72D1C8",
" ",
" ........ ",
" ........ ",
" ........ ",
" ........ ",
" ........ ",
" ........ ",
" ........ ",
" ........ ",
" "};
diff --git a/vcl/source/filter/GraphicFormatDetector.cxx b/vcl/source/filter/GraphicFormatDetector.cxx
new file mode 100644
index 0000000..aba1099
--- /dev/null
+++ b/vcl/source/filter/GraphicFormatDetector.cxx
@@ -0,0 +1,565 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
#include <graphic/GraphicFormatDetector.hxx>
#include <tools/solar.h>
#include <tools/zcodec.hxx>
namespace vcl
{
namespace
{
bool isPCT(SvStream& rStream, sal_uLong nStreamPos, sal_uLong nStreamLen)
{
sal_uInt8 sBuf[3];
// store number format
SvStreamEndian oldNumberFormat = rStream.GetEndian();
sal_uInt32 nOffset; // in MS documents the pict format is used without the first 512 bytes
for (nOffset = 0; (nOffset <= 512) && ((nStreamPos + nOffset + 14) <= nStreamLen);
nOffset += 512)
{
short y1, x1, y2, x2;
bool bdBoxOk = true;
rStream.Seek(nStreamPos + nOffset);
// size of the pict in version 1 pict ( 2bytes) : ignored
rStream.SeekRel(2);
// bounding box (bytes 2 -> 9)
rStream.SetEndian(SvStreamEndian::BIG);
rStream.ReadInt16(y1).ReadInt16(x1).ReadInt16(y2).ReadInt16(x2);
rStream.SetEndian(oldNumberFormat); // reset format
if (x1 > x2 || y1 > y2 || // bad bdbox
(x1 == x2 && y1 == y2) || // 1 pixel picture
x2 - x1 > 2048 || y2 - y1 > 2048) // picture abnormally big
bdBoxOk = false;
// read version op
rStream.ReadBytes(sBuf, 3);
// see http://developer.apple.com/legacy/mac/library/documentation/mac/pdf/Imaging_With_QuickDraw/Appendix_A.pdf
// normal version 2 - page A23 and A24
if (sBuf[0] == 0x00 && sBuf[1] == 0x11 && sBuf[2] == 0x02)
return true;
// normal version 1 - page A25
else if (sBuf[0] == 0x11 && sBuf[1] == 0x01 && bdBoxOk)
return true;
}
return false;
}
sal_uInt8* ImplSearchEntry(sal_uInt8* pSource, sal_uInt8 const* pDest, sal_uLong nComp,
sal_uLong nSize)
{
while (nComp-- >= nSize)
{
sal_uLong i;
for (i = 0; i < nSize; i++)
{
if ((pSource[i] & ~0x20) != (pDest[i] & ~0x20))
break;
}
if (i == nSize)
return pSource;
pSource++;
}
return nullptr;
}
} // end anonymous namespace
GraphicFormatDetector::GraphicFormatDetector(SvStream& rStream, OUString const& rFormatExtension)
: mrStream(rStream)
, maExtension(rFormatExtension)
{
}
bool GraphicFormatDetector::detect()
{
maFirstBytes.clear();
maFirstBytes.resize(256, 0);
mnFirstLong = 0;
mnSecondLong = 0;
mnStreamPosition = mrStream.Tell();
mnStreamLength = mrStream.remainingSize();
if (!mnStreamLength)
{
SvLockBytes* pLockBytes = mrStream.GetLockBytes();
if (pLockBytes)
pLockBytes->SetSynchronMode();
mnStreamLength = mrStream.remainingSize();
}
if (mnStreamLength == 0)
{
return false; // this prevents at least a STL assertion
}
else if (mnStreamLength >= maFirstBytes.size())
{
// load first 256 bytes into a buffer
sal_uInt64 nRead = mrStream.ReadBytes(maFirstBytes.data(), maFirstBytes.size());
if (nRead < maFirstBytes.size())
mnStreamLength = nRead;
}
else
{
mnStreamLength = mrStream.ReadBytes(maFirstBytes.data(), mnStreamLength);
}
if (mrStream.GetError())
return false;
for (int i = 0; i < 4; ++i)
{
mnFirstLong = (mnFirstLong << 8) | sal_uInt32(maFirstBytes[i]);
mnSecondLong = (mnSecondLong << 8) | sal_uInt32(maFirstBytes[i + 4]);
}
return true;
}
bool GraphicFormatDetector::checkMET()
{
if (maFirstBytes[2] != 0xd3)
return false;
mrStream.SetEndian(SvStreamEndian::BIG);
mrStream.Seek(mnStreamPosition);
sal_uInt16 nFieldSize;
sal_uInt8 nMagic;
mrStream.ReadUInt16(nFieldSize).ReadUChar(nMagic);
for (int i = 0; i < 3; i++)
{
if (nFieldSize < 6)
return false;
if (mnStreamLength < mrStream.Tell() + nFieldSize)
return false;
mrStream.SeekRel(nFieldSize - 3);
mrStream.ReadUInt16(nFieldSize).ReadUChar(nMagic);
if (nMagic != 0xd3)
return false;
}
mrStream.SetEndian(SvStreamEndian::LITTLE);
if (mrStream.GetError())
return false;
msDetectedFormat = "MET";
return true;
}
bool GraphicFormatDetector::checkBMP()
{
sal_uInt8 nOffset;
// We're possibly also able to read an OS/2 bitmap array
// ('BA'), therefore we must adjust the offset to discover the
// first bitmap in the array
if (maFirstBytes[0] == 0x42 && maFirstBytes[1] == 0x41)
nOffset = 14;
else
nOffset = 0;
// Now we initially test on 'BM'
if (maFirstBytes[0 + nOffset] == 0x42 && maFirstBytes[1 + nOffset] == 0x4d)
{
// OS/2 can set the Reserved flags to a value other than 0
// (which they really should not do...);
// In this case we test the size of the BmpInfoHeaders
if ((maFirstBytes[6 + nOffset] == 0x00 && maFirstBytes[7 + nOffset] == 0x00
&& maFirstBytes[8 + nOffset] == 0x00 && maFirstBytes[9 + nOffset] == 0x00)
|| maFirstBytes[14 + nOffset] == 0x28 || maFirstBytes[14 + nOffset] == 0x0c)
{
msDetectedFormat = "BMP";
return true;
}
}
return false;
}
bool GraphicFormatDetector::checkWMForEMF()
{
if (mnFirstLong == 0xd7cdc69a || mnFirstLong == 0x01000900)
{
msDetectedFormat = "WMF";
return true;
}
else if (mnFirstLong == 0x01000000 && maFirstBytes[40] == 0x20 && maFirstBytes[41] == 0x45
&& maFirstBytes[42] == 0x4d && maFirstBytes[43] == 0x46)
{
msDetectedFormat = "EMF";
return true;
}
return false;
}
bool GraphicFormatDetector::checkPCX()
{
if (maFirstBytes[0] != 0x0a)
return false;
sal_uInt8 nVersion = maFirstBytes[1];
sal_uInt8 nEncoding = maFirstBytes[2];
if ((nVersion == 0 || nVersion == 2 || nVersion == 3 || nVersion == 5) && nEncoding <= 1)
{
msDetectedFormat = "PCX";
return true;
}
return false;
}
bool GraphicFormatDetector::checkTIF()
{
if (mnFirstLong == 0x49492a00 || mnFirstLong == 0x4d4d002a)
{
msDetectedFormat = "TIF";
return true;
}
return false;
}
bool GraphicFormatDetector::checkGIF()
{
if (mnFirstLong == 0x47494638 && (maFirstBytes[4] == 0x37 || maFirstBytes[4] == 0x39)
&& maFirstBytes[5] == 0x61)
{
msDetectedFormat = "GIF";
return true;
}
return false;
}
bool GraphicFormatDetector::checkPNG()
{
if (mnFirstLong == 0x89504e47 && mnSecondLong == 0x0d0a1a0a)
{
msDetectedFormat = "PNG";
return true;
}
return false;
}
bool GraphicFormatDetector::checkJPG()
{
if ((mnFirstLong == 0xffd8ffe0 && maFirstBytes[6] == 0x4a && maFirstBytes[7] == 0x46
&& maFirstBytes[8] == 0x49 && maFirstBytes[9] == 0x46)
|| (mnFirstLong == 0xffd8fffe) || (0xffd8ff00 == (mnFirstLong & 0xffffff00)))
{
msDetectedFormat = "JPG";
return true;
}
return false;
}
bool GraphicFormatDetector::checkSVM()
{
if (mnFirstLong == 0x53564744 && maFirstBytes[4] == 0x49)
{
msDetectedFormat = "SVM";
return true;
}
else if (maFirstBytes[0] == 0x56 && maFirstBytes[1] == 0x43 && maFirstBytes[2] == 0x4C
&& maFirstBytes[3] == 0x4D && maFirstBytes[4] == 0x54 && maFirstBytes[5] == 0x46)
{
msDetectedFormat = "SVM";
return true;
}
return false;
}
bool GraphicFormatDetector::checkPCD()
{
if (mnStreamLength < 2055)
return false;
char sBuffer[8];
mrStream.Seek(mnStreamPosition + 2048);
mrStream.ReadBytes(sBuffer, 7);
if (strncmp(sBuffer, "PCD_IPI", 7) == 0)
{
msDetectedFormat = "PCD";
return true;
}
return false;
}
bool GraphicFormatDetector::checkPSD()
{
if ((mnFirstLong == 0x38425053) && ((mnSecondLong >> 16) == 1))
{
msDetectedFormat = "PSD";
return true;
}
return false;
}
bool GraphicFormatDetector::checkEPS()
{
if ((mnFirstLong == 0xC5D0D3C6)
|| (ImplSearchEntry(maFirstBytes.data(), reinterpret_cast<sal_uInt8 const*>("%!PS-Adobe"),
10, 10)
&& ImplSearchEntry(&maFirstBytes[15], reinterpret_cast<sal_uInt8 const*>("EPS"), 3, 3)))
{
msDetectedFormat = "EPS";
return true;
}
return false;
}
bool GraphicFormatDetector::checkDXF()
{
if (strncmp(reinterpret_cast<char*>(maFirstBytes.data()), "AutoCAD Binary DXF", 18) == 0)
{
msDetectedFormat = "DXF";
return true;
}
// ASCII DXF File Format
int i = 0;
while (i < 256 && maFirstBytes[i] <= 32)
{
++i;
}
if (i < 256 && maFirstBytes[i] == '0')
{
++i;
// only now do we have sufficient data to make a judgement
// based on a '0' + 'SECTION' == DXF argument
while (i < 256 && maFirstBytes[i] <= 32)
{
++i;
}
if (i + 7 < 256
&& (strncmp(reinterpret_cast<char*>(maFirstBytes.data() + i), "SECTION", 7) == 0))
{
msDetectedFormat = "DXF";
return true;
}
}
return false;
}
bool GraphicFormatDetector::checkPCT()
{
if (isPCT(mrStream, mnStreamPosition, mnStreamLength))
{
msDetectedFormat = "PCT";
return true;
}
return false;
}
bool GraphicFormatDetector::checkPBMorPGMorPPM()
{
if (maFirstBytes[0] == 'P')
{
switch (maFirstBytes[1])
{
case '1':
case '4':
msDetectedFormat = "PBM";
return true;
case '2':
case '5':
msDetectedFormat = "PGM";
return true;
case '3':
case '6':
msDetectedFormat = "PPM";
return true;
}
}
return false;
}
bool GraphicFormatDetector::checkRAS()
{
if (mnFirstLong == 0x59a66a95)
{
msDetectedFormat = "RAS";
return true;
}
return false;
}
bool GraphicFormatDetector::checkXPM()
{
if (ImplSearchEntry(maFirstBytes.data(), reinterpret_cast<sal_uInt8 const*>("/* XPM */"), 256,
9))
{
msDetectedFormat = "XPM";
return true;
}
return false;
}
bool GraphicFormatDetector::checkXBM()
{
sal_uInt64 nSize = std::min<sal_uInt64>(mnStreamLength, 2048);
std::unique_ptr<sal_uInt8[]> pBuffer(new sal_uInt8[nSize]);
mrStream.Seek(mnStreamPosition);
mrStream.ReadBytes(pBuffer.get(), nSize);
sal_uInt8* pPtr
= ImplSearchEntry(pBuffer.get(), reinterpret_cast<sal_uInt8 const*>("#define"), nSize, 7);
if (pPtr)
{
if (ImplSearchEntry(pPtr, reinterpret_cast<sal_uInt8 const*>("_width"),
pBuffer.get() + nSize - pPtr, 6))
{
msDetectedFormat = "XBM";
return true;
}
}
return false;
}
bool GraphicFormatDetector::checkSVG()
{
sal_uInt8* pCheckArray = maFirstBytes.data();
sal_uInt64 nCheckSize = std::min<sal_uInt64>(mnStreamLength, 256);
sal_uInt8 sExtendedOrDecompressedFirstBytes[2048];
sal_uInt64 nDecompressedSize = nCheckSize;
bool bIsGZip(false);
// check if it is gzipped -> svgz
if (maFirstBytes[0] == 0x1F && maFirstBytes[1] == 0x8B)
{
ZCodec aCodec;
mrStream.Seek(mnStreamPosition);
aCodec.BeginCompression(ZCODEC_DEFAULT_COMPRESSION, false, true);
nDecompressedSize = aCodec.Read(mrStream, sExtendedOrDecompressedFirstBytes, 2048);
nCheckSize = std::min<sal_uInt64>(nDecompressedSize, 256);
aCodec.EndCompression();
pCheckArray = sExtendedOrDecompressedFirstBytes;
bIsGZip = true;
}
bool bIsSvg(false);
// check for Xml
// #119176# SVG files which have no xml header at all have shown up this is optional
if (ImplSearchEntry(pCheckArray, reinterpret_cast<sal_uInt8 const*>("<?xml"), nCheckSize,
5) // is it xml
&& ImplSearchEntry(pCheckArray, reinterpret_cast<sal_uInt8 const*>("version"), nCheckSize,
7)) // does it have a version (required for xml)
{
// check for DOCTYPE svg combination
if (ImplSearchEntry(pCheckArray, reinterpret_cast<sal_uInt8 const*>("DOCTYPE"), nCheckSize,
7) // 'DOCTYPE' is there
&& ImplSearchEntry(pCheckArray, reinterpret_cast<sal_uInt8 const*>("svg"), nCheckSize,
3)) // 'svg' is there
{
bIsSvg = true;
}
}
// check for svg element in 1st 256 bytes
if (!bIsSvg
&& ImplSearchEntry(pCheckArray, reinterpret_cast<sal_uInt8 const*>("<svg"), nCheckSize,
4)) // '<svg'
{
bIsSvg = true;
}
// extended search for svg element
if (!bIsSvg)
{
// it's a xml, look for '<svg' in full file. Should not happen too
// often since the tests above will handle most cases, but can happen
// with Svg files containing big comment headers or Svg as the host
// language
pCheckArray = sExtendedOrDecompressedFirstBytes;
if (bIsGZip)
{
nCheckSize = std::min<sal_uInt64>(nDecompressedSize, 2048);
}
else
{
nCheckSize = std::min<sal_uInt64>(mnStreamLength, 2048);
mrStream.Seek(mnStreamPosition);
nCheckSize = mrStream.ReadBytes(sExtendedOrDecompressedFirstBytes, nCheckSize);
}
if (ImplSearchEntry(pCheckArray, reinterpret_cast<sal_uInt8 const*>("<svg"), nCheckSize,
4)) // '<svg'
{
bIsSvg = true;
}
}
if (bIsSvg)
{
msDetectedFormat = "SVG";
return true;
}
return false;
}
bool GraphicFormatDetector::checkTGA()
{
if (maExtension.startsWith("TGA"))
{
msDetectedFormat = "TGA";
return true;
}
return false;
}
bool GraphicFormatDetector::checkMOV()
{
if ((maFirstBytes[4] == 'f' && maFirstBytes[5] == 't' && maFirstBytes[6] == 'y'
&& maFirstBytes[7] == 'p' && maFirstBytes[8] == 'q' && maFirstBytes[9] == 't')
|| (maFirstBytes[4] == 'm' && maFirstBytes[5] == 'o' && maFirstBytes[6] == 'o'
&& maFirstBytes[7] == 'v' && maFirstBytes[11] == 'l' && maFirstBytes[12] == 'm'))
{
msDetectedFormat = "MOV";
return true;
}
return false;
}
bool GraphicFormatDetector::checkPDF()
{
if (maFirstBytes[0] == '%' && maFirstBytes[1] == 'P' && maFirstBytes[2] == 'D'
&& maFirstBytes[3] == 'F' && maFirstBytes[4] == '-')
{
msDetectedFormat = "PDF";
return true;
}
return false;
}
} // vcl namespace
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/graphicfilter.cxx b/vcl/source/filter/graphicfilter.cxx
index 7a0cb0f6..ecc9001 100644
--- a/vcl/source/filter/graphicfilter.cxx
+++ b/vcl/source/filter/graphicfilter.cxx
@@ -73,6 +73,8 @@
#include "FilterConfigCache.hxx"
#include "graphicfilter_internal.hxx"
#include <graphic/GraphicFormatDetector.hxx>
#define PMGCHUNG_msOG 0x6d734f47 // Microsoft Office Animated GIF
typedef ::std::vector< GraphicFilter* > FilterList_impl;
@@ -238,52 +240,18 @@ bool isPCT(SvStream& rStream, sal_uLong nStreamPos, sal_uLong nStreamLen)
*
*************************************************************************/
static bool ImpPeekGraphicFormat( SvStream& rStream, OUString& rFormatExtension, bool bTest )
bool ImpPeekGraphicFormat( SvStream& rStream, OUString& rFormatExtension, bool bTest )
{
sal_uInt8 sFirstBytes[ 256 ];
sal_uLong nFirstLong(0), nSecondLong(0);
sal_uLong nStreamPos = rStream.Tell();
sal_uLong nStreamLen = rStream.remainingSize();
if ( !nStreamLen )
{
SvLockBytes* pLockBytes = rStream.GetLockBytes();
if ( pLockBytes )
pLockBytes->SetSynchronMode();
nStreamLen = rStream.remainingSize();
}
if (!nStreamLen)
{
return false; // this prevents at least a STL assertion
}
else if (nStreamLen >= 256)
{
// load first 256 bytes into a buffer
sal_uLong nRead = rStream.ReadBytes(sFirstBytes, 256);
if (nRead < 256)
nStreamLen = nRead;
}
else
{
nStreamLen = rStream.ReadBytes(sFirstBytes, nStreamLen);
}
if (rStream.GetError())
vcl::GraphicFormatDetector aDetector(rStream, rFormatExtension);
if (!aDetector.detect())
return false;
for (sal_uLong i = nStreamLen; i < 256; ++i)
sFirstBytes[i] = 0;
sal_uInt8* sFirstBytes = aDetector.maFirstBytes.data();
sal_uLong nFirstLong = aDetector.mnFirstLong;
sal_uLong nSecondLong = aDetector.mnSecondLong;
// Accommodate the first 8 bytes in nFirstLong, nSecondLong
// Big-Endian:
for (int i = 0; i < 4; ++i)
{
nFirstLong=(nFirstLong<<8)|static_cast<sal_uLong>(sFirstBytes[i]);
nSecondLong=(nSecondLong<<8)|static_cast<sal_uLong>(sFirstBytes[i+4]);
}
sal_uLong nStreamPos = aDetector.mnStreamPosition;
sal_uLong nStreamLen = aDetector.mnStreamLength;
// The following variable is used when bTest == true. It remains false
// if the format (rFormatExtension) has not yet been set.