vcl/inc/graphic/DetectorTools.hxx | 63 +++++++++++++ vcl/qa/cppunit/GraphicFormatDetectorTest.cxx | 123 +++++++++++++++++++++++++++ vcl/qa/cppunit/data/TypeDetectionExample.eps | 82 ++++++++++++++++++ vcl/source/filter/GraphicFormatDetector.cxx | 52 +++-------- 4 files changed, 286 insertions(+), 34 deletions(-)
New commits: commit c4f2614a212f54e2edd903d6a73b3036a90be836 Author: Tomaž Vajngerl <tomaz.vajng...@collabora.co.uk> AuthorDate: Sat May 2 14:46:37 2020 +0200 Commit: Tomaž Vajngerl <tomaz.vajng...@collabora.co.uk> CommitDate: Sat May 2 14:46:37 2020 +0200 vcl: simplify graphic type detection for EPS files Change-Id: Ia48560274c33aaa8e81a5ee6eb00f6470e92e0fd diff --git a/vcl/source/filter/GraphicFormatDetector.cxx b/vcl/source/filter/GraphicFormatDetector.cxx index 5682d78f8e3c..56f7a9f2d006 100644 --- a/vcl/source/filter/GraphicFormatDetector.cxx +++ b/vcl/source/filter/GraphicFormatDetector.cxx @@ -311,8 +311,7 @@ bool GraphicFormatDetector::checkEPS() msDetectedFormat = "EPS"; return true; } - else if (matchArrayWithString(pFirstBytesAsCharArray, 10, "%!PS-Adobe") - && matchArrayWithString(pFirstBytesAsCharArray + 15, 3, "EPS")) + else if (checkArrayForMatchingStrings(pFirstBytesAsCharArray, 30, { "%!PS-Adobe", " EPS" })) { msDetectedFormat = "EPS"; return true; commit bcb2373d23e52d600809acb181c811dd08143830 Author: Tomaž Vajngerl <tomaz.vajng...@collabora.co.uk> AuthorDate: Sat May 2 14:42:58 2020 +0200 Commit: Tomaž Vajngerl <tomaz.vajng...@collabora.co.uk> CommitDate: Sat May 2 14:43:15 2020 +0200 vcl: add test case for EPS file detection Change-Id: Ia6a4cedf5c570e5d9544887ae66da0ec1e491647 diff --git a/vcl/qa/cppunit/GraphicFormatDetectorTest.cxx b/vcl/qa/cppunit/GraphicFormatDetectorTest.cxx index 155ff089811b..1ce516bf52b3 100644 --- a/vcl/qa/cppunit/GraphicFormatDetectorTest.cxx +++ b/vcl/qa/cppunit/GraphicFormatDetectorTest.cxx @@ -42,6 +42,7 @@ class GraphicFormatDetectorTest : public test::BootstrapFixtureBase void testDetectSVG(); void testDetectSVGZ(); void testDetectPDF(); + void testDetectEPS(); void testMatchArray(); void testCheckArrayForMatchingStrings(); @@ -61,6 +62,7 @@ class GraphicFormatDetectorTest : public test::BootstrapFixtureBase CPPUNIT_TEST(testDetectSVG); CPPUNIT_TEST(testDetectSVGZ); CPPUNIT_TEST(testDetectPDF); + CPPUNIT_TEST(testDetectEPS); CPPUNIT_TEST(testMatchArray); CPPUNIT_TEST(testCheckArrayForMatchingStrings); CPPUNIT_TEST_SUITE_END(); @@ -291,6 +293,21 @@ void GraphicFormatDetectorTest::testDetectPDF() CPPUNIT_ASSERT_EQUAL(OUString("PDF"), rFormatExtension); } +void GraphicFormatDetectorTest::testDetectEPS() +{ + SvFileStream aFileStream(getFullUrl("TypeDetectionExample.eps"), StreamMode::READ); + vcl::GraphicFormatDetector aDetector(aFileStream, "EPS"); + + CPPUNIT_ASSERT(aDetector.detect()); + CPPUNIT_ASSERT(aDetector.checkEPS()); + + aFileStream.Seek(aDetector.mnStreamPosition); + + OUString rFormatExtension; + CPPUNIT_ASSERT(ImpPeekGraphicFormat(aFileStream, rFormatExtension, false)); + CPPUNIT_ASSERT_EQUAL(OUString("EPS"), rFormatExtension); +} + void GraphicFormatDetectorTest::testMatchArray() { std::string aString("<?xml version=\"1.0\" standalone=\"no\"?>\n" diff --git a/vcl/qa/cppunit/data/TypeDetectionExample.eps b/vcl/qa/cppunit/data/TypeDetectionExample.eps new file mode 100644 index 000000000000..7f0db47bc80c --- /dev/null +++ b/vcl/qa/cppunit/data/TypeDetectionExample.eps @@ -0,0 +1,82 @@ +%!PS-Adobe-3.0 EPSF-3.0 +%%Creator: cairo 1.16.0 (https://cairographics.org) +%%CreationDate: Sat May 2 14:29:27 2020 +%%Pages: 1 +%%DocumentData: Clean7Bit +%%LanguageLevel: 2 +%%BoundingBox: 0 1 7 8 +%%EndComments +%%BeginProlog +50 dict begin +/q { gsave } bind def +/Q { grestore } bind def +/cm { 6 array astore concat } bind def +/w { setlinewidth } bind def +/J { setlinecap } bind def +/j { setlinejoin } bind def +/M { setmiterlimit } bind def +/d { setdash } bind def +/m { moveto } bind def +/l { lineto } bind def +/c { curveto } bind def +/h { closepath } bind def +/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto + 0 exch rlineto 0 rlineto closepath } bind def +/S { stroke } bind def +/f { fill } bind def +/f* { eofill } bind def +/n { newpath } bind def +/W { clip } bind def +/W* { eoclip } bind def +/BT { } bind def +/ET { } bind def +/BDC { mark 3 1 roll /BDC pdfmark } bind def +/EMC { mark /EMC pdfmark } bind def +/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def +/Tj { show currentpoint cairo_store_point } bind def +/TJ { + { + dup + type /stringtype eq + { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse + } forall + currentpoint cairo_store_point +} bind def +/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore + cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def +/Tf { pop /cairo_font exch def /cairo_font_matrix where + { pop cairo_selectfont } if } bind def +/Td { matrix translate cairo_font_matrix matrix concatmatrix dup + /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point + /cairo_font where { pop cairo_selectfont } if } bind def +/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def + cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def +/g { setgray } bind def +/rg { setrgbcolor } bind def +/d1 { setcachedevice } bind def +/cairo_data_source { + CairoDataIndex CairoData length lt + { CairoData CairoDataIndex get /CairoDataIndex CairoDataIndex 1 add def } + { () } ifelse +} def +/cairo_flush_ascii85_file { cairo_ascii85_file status { cairo_ascii85_file flushfile } if } def +/cairo_image { image cairo_flush_ascii85_file } def +/cairo_imagemask { imagemask cairo_flush_ascii85_file } def +%%EndProlog +%%BeginSetup +%%EndSetup +%%Page: 1 1 +%%BeginPageSetup +%%PageBoundingBox: 0 1 7 8 +%%EndPageSetup +q 0 1 7 7 rectclip +1 0 0 -1 0 8 cm q +0.956863 0.831373 0.266667 rg +3.75 0.75 m 5.41 0.75 6.75 2.09 6.75 3.75 c 6.75 5.41 5.41 6.75 3.75 6.75 + c 2.09 6.75 0.75 5.41 0.75 3.75 c 0.75 2.09 2.09 0.75 3.75 0.75 c h +3.75 0.75 m f +Q Q +showpage +%%Trailer +end +%%EOF commit 05f5c751bc1b4dceb8d6f788d963650c259697cd Author: Tomaž Vajngerl <tomaz.vajng...@collabora.co.uk> AuthorDate: Sat May 2 14:35:23 2020 +0200 Commit: Tomaž Vajngerl <tomaz.vajng...@collabora.co.uk> CommitDate: Sat May 2 14:35:23 2020 +0200 vcl: add DetectorTools + tests, refactor array string matching Add DetectorTools with byte array searching and matching to a input string (or another byte array). This refactors the existing function in GraphicFormatDetector. It needs to go into its own header so that the function(s) can be tested easily. Replace the previous searchEntry implementation with refactored one in the source code. Change-Id: I59d30b694e13f28d6366f1a99fe2ef2ea3c1a07d diff --git a/vcl/inc/graphic/DetectorTools.hxx b/vcl/inc/graphic/DetectorTools.hxx new file mode 100644 index 000000000000..b9163de135d9 --- /dev/null +++ b/vcl/inc/graphic/DetectorTools.hxx @@ -0,0 +1,63 @@ +/* -*- 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/. + * + */ + +#pragma once + +namespace vcl +{ +const char* matchArray(const char* pSource, sal_Int32 nSourceSize, const char* pSearch, + sal_Int32 nSearchSize) +{ + for (sal_Int32 increment = 0; increment <= (nSourceSize - nSearchSize); ++increment) + { + bool bMatch = true; + // search both arrays if they match + for (sal_Int32 index = 0; index < nSearchSize && bMatch; ++index) + { + if (pSource[index] != pSearch[index]) + bMatch = false; + } + // match has been found + if (bMatch) + return pSource; + pSource++; + } + return nullptr; +} + +const char* matchArrayWithString(const char* pSource, sal_Int32 nSourceSize, OString const& rString) +{ + return matchArray(pSource, nSourceSize, rString.getStr(), rString.getLength()); +} + +bool checkArrayForMatchingStrings(const char* pSource, sal_Int32 nSourceSize, + std::vector<OString> const& rStrings) +{ + if (rStrings.empty()) + return false; + if (rStrings.size() < 2) + return matchArrayWithString(pSource, nSourceSize, rStrings[0]) != nullptr; + + const char* pBegin = pSource; + const char* pCurrent = pSource; + for (OString const& rString : rStrings) + { + sal_Int32 nCurrentSize = nSourceSize - sal_Int32(pCurrent - pBegin); + printf("Current size %d -> %d\n", nCurrentSize, nSourceSize); + pCurrent = matchArray(pCurrent, nCurrentSize, rString.getStr(), rString.getLength()); + if (pCurrent == nullptr) + return false; + printf("%s\n", pCurrent); + } + return true; +} +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/qa/cppunit/GraphicFormatDetectorTest.cxx b/vcl/qa/cppunit/GraphicFormatDetectorTest.cxx index 90183279dbc9..155ff089811b 100644 --- a/vcl/qa/cppunit/GraphicFormatDetectorTest.cxx +++ b/vcl/qa/cppunit/GraphicFormatDetectorTest.cxx @@ -12,6 +12,7 @@ #include <unotest/bootstrapfixturebase.hxx> #include <graphic/GraphicFormatDetector.hxx> +#include <graphic/DetectorTools.hxx> #include <tools/stream.hxx> @@ -41,6 +42,8 @@ class GraphicFormatDetectorTest : public test::BootstrapFixtureBase void testDetectSVG(); void testDetectSVGZ(); void testDetectPDF(); + void testMatchArray(); + void testCheckArrayForMatchingStrings(); CPPUNIT_TEST_SUITE(GraphicFormatDetectorTest); CPPUNIT_TEST(testDetectMET); @@ -58,6 +61,8 @@ class GraphicFormatDetectorTest : public test::BootstrapFixtureBase CPPUNIT_TEST(testDetectSVG); CPPUNIT_TEST(testDetectSVGZ); CPPUNIT_TEST(testDetectPDF); + CPPUNIT_TEST(testMatchArray); + CPPUNIT_TEST(testCheckArrayForMatchingStrings); CPPUNIT_TEST_SUITE_END(); }; @@ -286,6 +291,107 @@ void GraphicFormatDetectorTest::testDetectPDF() CPPUNIT_ASSERT_EQUAL(OUString("PDF"), rFormatExtension); } +void GraphicFormatDetectorTest::testMatchArray() +{ + std::string aString("<?xml version=\"1.0\" standalone=\"no\"?>\n" + "<svg width=\"5cm\" height=\"4cm\" version=\"1.1\"\n" + "xmlns=\"http://www.w3.org/2000/svg\">\n" + "</svg>"); + + const char* pCompleteStringPointer = aString.c_str(); + const char* pMatchPointer; + int nCheckSize = aString.size(); + + // Check beginning of the input string + pMatchPointer = vcl::matchArrayWithString(pCompleteStringPointer, nCheckSize, "<?xml"); + CPPUNIT_ASSERT(pMatchPointer != nullptr); + CPPUNIT_ASSERT_EQUAL(0, int(pMatchPointer - pCompleteStringPointer)); + CPPUNIT_ASSERT_EQUAL(true, OString(pMatchPointer).startsWith("<?xml")); + + // Check middle of the input string + pMatchPointer = vcl::matchArrayWithString(aString.c_str(), nCheckSize, "version"); + CPPUNIT_ASSERT(pMatchPointer != nullptr); + CPPUNIT_ASSERT_EQUAL(6, int(pMatchPointer - pCompleteStringPointer)); + CPPUNIT_ASSERT_EQUAL(true, OString(pMatchPointer).startsWith("version")); + + pMatchPointer = vcl::matchArrayWithString(aString.c_str(), nCheckSize, "<svg"); + CPPUNIT_ASSERT(pMatchPointer != nullptr); + CPPUNIT_ASSERT_EQUAL(38, int(pMatchPointer - pCompleteStringPointer)); + CPPUNIT_ASSERT_EQUAL(true, OString(pMatchPointer).startsWith("<svg")); + + // Check end of the input string + pMatchPointer = vcl::matchArrayWithString(aString.c_str(), nCheckSize, "/svg>"); + CPPUNIT_ASSERT(pMatchPointer != nullptr); + CPPUNIT_ASSERT_EQUAL(119, int(pMatchPointer - pCompleteStringPointer)); + CPPUNIT_ASSERT_EQUAL(true, OString(pMatchPointer).startsWith("/svg>")); + + // Check that non-existing search string + pMatchPointer = vcl::matchArrayWithString(aString.c_str(), nCheckSize, "none"); + CPPUNIT_ASSERT(pMatchPointer == nullptr); +} + +void GraphicFormatDetectorTest::testCheckArrayForMatchingStrings() +{ + std::string aString("<?xml version=\"1.0\" standalone=\"no\"?>\n" + "<svg width=\"5cm\" height=\"4cm\" version=\"1.1\"\n" + "xmlns=\"http://www.w3.org/2000/svg\">\n" + "</svg>"); + const char* pCompleteStringPointer = aString.c_str(); + int nCheckSize = aString.size(); + bool bResult; + + // check beginning string + bResult = vcl::checkArrayForMatchingStrings(pCompleteStringPointer, nCheckSize, { "<?xml" }); + CPPUNIT_ASSERT_EQUAL(true, bResult); + + // check ending string + bResult = vcl::checkArrayForMatchingStrings(pCompleteStringPointer, nCheckSize, { "/svg>" }); + CPPUNIT_ASSERT_EQUAL(true, bResult); + + // check middle string + bResult = vcl::checkArrayForMatchingStrings(pCompleteStringPointer, nCheckSize, { "version" }); + CPPUNIT_ASSERT_EQUAL(true, bResult); + + // check beginning and then ending string + bResult = vcl::checkArrayForMatchingStrings(pCompleteStringPointer, nCheckSize, + { "<?xml", "/svg>" }); + CPPUNIT_ASSERT_EQUAL(true, bResult); + + // check ending and then beginning string + bResult = vcl::checkArrayForMatchingStrings(pCompleteStringPointer, nCheckSize, + { "/svg>", "<?xml" }); + CPPUNIT_ASSERT_EQUAL(false, bResult); + + // check middle strings + bResult = vcl::checkArrayForMatchingStrings(pCompleteStringPointer, nCheckSize, + { "version", "<svg" }); + CPPUNIT_ASSERT_EQUAL(true, bResult); + + // check beginning, middle and ending strings + bResult = vcl::checkArrayForMatchingStrings(pCompleteStringPointer, nCheckSize, + { "<?xml", "version", "<svg", "/svg>" }); + CPPUNIT_ASSERT_EQUAL(true, bResult); + + // check non-existing + bResult = vcl::checkArrayForMatchingStrings(pCompleteStringPointer, nCheckSize, { "none" }); + CPPUNIT_ASSERT_EQUAL(false, bResult); + + // check non-existing on the beginning + bResult = vcl::checkArrayForMatchingStrings(pCompleteStringPointer, nCheckSize, + { "none", "version", "<svg", "/svg>" }); + CPPUNIT_ASSERT_EQUAL(false, bResult); + + // check non-existing on the end + bResult = vcl::checkArrayForMatchingStrings(pCompleteStringPointer, nCheckSize, + { "<?xml", "version", "<svg", "none" }); + CPPUNIT_ASSERT_EQUAL(false, bResult); + + // check non-existing after the end + bResult = vcl::checkArrayForMatchingStrings(pCompleteStringPointer, nCheckSize, + { "<?xml", "/svg>", "none" }); + CPPUNIT_ASSERT_EQUAL(false, bResult); +} + } // namespace CPPUNIT_TEST_SUITE_REGISTRATION(GraphicFormatDetectorTest); diff --git a/vcl/source/filter/GraphicFormatDetector.cxx b/vcl/source/filter/GraphicFormatDetector.cxx index 56624074366e..5682d78f8e3c 100644 --- a/vcl/source/filter/GraphicFormatDetector.cxx +++ b/vcl/source/filter/GraphicFormatDetector.cxx @@ -22,6 +22,7 @@ #include <algorithm> #include <graphic/GraphicFormatDetector.hxx> +#include <graphic/DetectorTools.hxx> #include <tools/solar.h> #include <tools/zcodec.hxx> @@ -67,23 +68,6 @@ bool isPCT(SvStream& rStream, sal_uLong nStreamPos, sal_uLong nStreamLen) return false; } -sal_uInt8* searchEntry(sal_uInt8* pSource, const char* 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) @@ -320,13 +304,15 @@ bool GraphicFormatDetector::checkPSD() bool GraphicFormatDetector::checkEPS() { + const char* pFirstBytesAsCharArray = reinterpret_cast<char*>(maFirstBytes.data()); + if (mnFirstLong == 0xC5D0D3C6) { msDetectedFormat = "EPS"; return true; } - else if (searchEntry(maFirstBytes.data(), "%!PS-Adobe", 10, 10) - && searchEntry(&maFirstBytes[15], "EPS", 3, 3)) + else if (matchArrayWithString(pFirstBytesAsCharArray, 10, "%!PS-Adobe") + && matchArrayWithString(pFirstBytesAsCharArray + 15, 3, "EPS")) { msDetectedFormat = "EPS"; return true; @@ -419,7 +405,8 @@ bool GraphicFormatDetector::checkRAS() bool GraphicFormatDetector::checkXPM() { - if (searchEntry(maFirstBytes.data(), "/* XPM */", 256, 9)) + const char* pFirstBytesAsCharArray = reinterpret_cast<char*>(maFirstBytes.data()); + if (matchArrayWithString(pFirstBytesAsCharArray, 256, "/* XPM */")) { msDetectedFormat = "XPM"; return true; @@ -434,15 +421,13 @@ bool GraphicFormatDetector::checkXBM() mrStream.Seek(mnStreamPosition); mrStream.ReadBytes(pBuffer.get(), nSize); - sal_uInt8* pPtr = searchEntry(pBuffer.get(), "#define", nSize, 7); - if (pPtr) + const char* pBufferAsCharArray = reinterpret_cast<char*>(pBuffer.get()); + + if (checkArrayForMatchingStrings(pBufferAsCharArray, nSize, { "#define", "_width" })) { - if (searchEntry(pPtr, "_width", pBuffer.get() + nSize - pPtr, 6)) - { - msDetectedFormat = "XBM"; - return true; - } + msDetectedFormat = "XBM"; + return true; } return false; } @@ -473,20 +458,20 @@ bool GraphicFormatDetector::checkSVG() bool bIsSvg(false); + const char* pCheckArrayAsCharArray = reinterpret_cast<char*>(pCheckArray); + // check for XML // #119176# SVG files which have no xml header at all have shown up this is optional // check for "xml" then "version" then "DOCTYPE" and "svg" tags - if (searchEntry(pCheckArray, "<?xml", nCheckSize, 5) - && searchEntry(pCheckArray, "version", nCheckSize, 7) - && searchEntry(pCheckArray, "DOCTYPE", nCheckSize, 7) - && searchEntry(pCheckArray, "svg", nCheckSize, 3)) + if (checkArrayForMatchingStrings(pCheckArrayAsCharArray, nCheckSize, + { "<?xml", "version", "DOCTYPE", "svg" })) { bIsSvg = true; } // check for svg element in 1st 256 bytes // search for '<svg' - if (!bIsSvg && searchEntry(pCheckArray, "<svg", nCheckSize, 4)) + if (!bIsSvg && checkArrayForMatchingStrings(pCheckArrayAsCharArray, nCheckSize, { "<svg" })) { bIsSvg = true; } @@ -499,7 +484,7 @@ bool GraphicFormatDetector::checkSVG() // with Svg files containing big comment headers or Svg as the host // language - pCheckArray = sExtendedOrDecompressedFirstBytes; + pCheckArrayAsCharArray = reinterpret_cast<char*>(sExtendedOrDecompressedFirstBytes); if (bIsGZip) { @@ -513,7 +498,7 @@ bool GraphicFormatDetector::checkSVG() } // search for '<svg' - if (searchEntry(pCheckArray, "<svg", nCheckSize, 4)) + if (checkArrayForMatchingStrings(pCheckArrayAsCharArray, nCheckSize, { "<svg" })) { bIsSvg = true; } _______________________________________________ Libreoffice-commits mailing list libreoffice-comm...@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits