// SPDX-License-Identifier: GPL-2.0-or-later /** @file * TODO: insert short description here *//* * Authors: see git history * * Copyright (C) 2015 Authors * Released under GNU GPL v2+, read the file 'COPYING' for more information. */ #include #include "2geom/coord.h" #include "2geom/curves.h" #include "2geom/pathvector.h" #include "svg/svg.h" #include "preferences.h" #include "streq.h" #include #include #include #include class SvgPathGeomTest : public CxxTest::TestSuite { private: std::vector rectanglesAbsoluteClosed; std::vector rectanglesRelativeClosed; std::vector rectanglesAbsoluteOpen; std::vector rectanglesRelativeOpen; std::vector rectanglesAbsoluteClosed2; std::vector rectanglesRelativeClosed2; Geom::PathVector rectanglepvopen; Geom::PathVector rectanglepvclosed; Geom::PathVector rectanglepvclosed2; public: SvgPathGeomTest() { // Lots of ways to define the same rectangle rectanglesAbsoluteClosed.push_back("M 1,2 L 4,2 L 4,8 L 1,8 z"); rectanglesAbsoluteClosed.push_back("M 1,2 4,2 4,8 1,8 z"); rectanglesAbsoluteClosed.push_back("M 1,2 H 4 V 8 H 1 z"); rectanglesRelativeClosed.push_back("m 1,2 l 3,0 l 0,6 l -3,0 z"); rectanglesRelativeClosed.push_back("m 1,2 3,0 0,6 -3,0 z"); rectanglesRelativeClosed.push_back("m 1,2 h 3 v 6 h -3 z"); rectanglesAbsoluteOpen.push_back("M 1,2 L 4,2 L 4,8 L 1,8 L 1,2"); rectanglesAbsoluteOpen.push_back("M 1,2 4,2 4,8 1,8 1,2"); rectanglesAbsoluteOpen.push_back("M 1,2 H 4 V 8 H 1 V 2"); rectanglesRelativeOpen.push_back("m 1,2 l 3,0 l 0,6 l -3,0 l 0,-6"); rectanglesRelativeOpen.push_back("m 1,2 3,0 0,6 -3,0 0,-6"); rectanglesRelativeOpen.push_back("m 1,2 h 3 v 6 h -3 v -6"); rectanglesAbsoluteClosed2.push_back("M 1,2 L 4,2 L 4,8 L 1,8 L 1,2 z"); rectanglesAbsoluteClosed2.push_back("M 1,2 4,2 4,8 1,8 1,2 z"); rectanglesAbsoluteClosed2.push_back("M 1,2 H 4 V 8 H 1 V 2 z"); rectanglesRelativeClosed2.push_back("m 1,2 l 3,0 l 0,6 l -3,0 l 0,-6 z"); rectanglesRelativeClosed2.push_back("m 1,2 3,0 0,6 -3,0 0,-6 z"); rectanglesRelativeClosed2.push_back("m 1,2 h 3 v 6 h -3 v -6 z"); rectanglepvopen.push_back(Geom::Path(Geom::Point(1,2))); rectanglepvopen.back().append(Geom::LineSegment(Geom::Point(1,2),Geom::Point(4,2))); rectanglepvopen.back().append(Geom::LineSegment(Geom::Point(4,2),Geom::Point(4,8))); rectanglepvopen.back().append(Geom::LineSegment(Geom::Point(4,8),Geom::Point(1,8))); rectanglepvopen.back().append(Geom::LineSegment(Geom::Point(1,8),Geom::Point(1,2))); rectanglepvclosed.push_back(Geom::Path(Geom::Point(1,2))); rectanglepvclosed.back().append(Geom::LineSegment(Geom::Point(1,2),Geom::Point(4,2))); rectanglepvclosed.back().append(Geom::LineSegment(Geom::Point(4,2),Geom::Point(4,8))); rectanglepvclosed.back().append(Geom::LineSegment(Geom::Point(4,8),Geom::Point(1,8))); rectanglepvclosed.back().close(); rectanglepvclosed2.push_back(Geom::Path(Geom::Point(1,2))); rectanglepvclosed2.back().append(Geom::LineSegment(Geom::Point(1,2),Geom::Point(4,2))); rectanglepvclosed2.back().append(Geom::LineSegment(Geom::Point(4,2),Geom::Point(4,8))); rectanglepvclosed2.back().append(Geom::LineSegment(Geom::Point(4,8),Geom::Point(1,8))); rectanglepvclosed2.back().append(Geom::LineSegment(Geom::Point(1,8),Geom::Point(1,2))); rectanglepvclosed2.back().close(); // TODO: Also test some (smooth) cubic/quadratic beziers and elliptical arcs // TODO: Should we make it mandatory that h/v in the path data results in a H/VLineSegment? // If so, the tests should be modified to reflect this. } // createSuite and destroySuite get us per-suite setup and teardown // without us having to worry about static initialization order, etc. static SvgPathGeomTest *createSuite() { return new SvgPathGeomTest(); } static void destroySuite( SvgPathGeomTest *suite ) { delete suite; } void testReadRectanglesAbsoluteClosed() { for(size_t i=0; isetBool("/options/svgoutput/allowrelativecoordinates", true); prefs->setBool("/options/svgoutput/forcerepeatcommands", false); prefs->setInt("/options/svgoutput/numericprecision", 8); prefs->setInt("/options/svgoutput/minimumexponent", -8); pv = sp_svg_read_pathv("M 123456781,1.23456781e-8 L 123456782,1.23456782e-8 L 123456785,1.23456785e-8 L 10123456400,1.23456785e-8 L 123456789,1.23456789e-8 L 123456789,101.234564e-8 L 123456789,1.23456789e-8"); path_str = sp_svg_write_path(pv); TS_ASSERT_RELATION( streq_rel , "m 123456780,1.2345678e-8 0,0 10,1e-15 9999999210,0 -9999999210,0 0,9.99999921e-7 0,-9.99999921e-7" , path_str ); g_free(path_str); } private: bool bpathEqual(Geom::PathVector const &a, Geom::PathVector const &b, double eps = 1e-16) { if (a.size() != b.size()) { char temp[100]; sprintf(temp, "PathVectors not the same size: %u != %u", static_cast(a.size()),static_cast( b.size())); TS_FAIL(temp); return false; } for(size_t i=0; i(i)); TS_FAIL(temp); return false; } if (!pa.closed() && pb.closed()) { char temp[100]; sprintf(temp, "Right subpath is closed, left subpath is open. Subpath: %u", static_cast(i)); TS_FAIL(temp); return false; } if (pa.size() != pb.size()) { char temp[100]; sprintf(temp, "Not the same number of segments: %u != %u, subpath: %u", static_cast(pa.size()), static_cast(pb.size()), static_cast(i)); TS_FAIL(temp); return false; } for(size_t j=0; j(ca)) { Geom::LineSegment const *lb = dynamic_cast(cb); if (!Geom::are_near((*la)[0],(*lb)[0], eps)) { char temp[200]; sprintf(temp, "Different start of segment: (%g,%g) != (%g,%g), subpath: %u, segment: %u", (*la)[0][Geom::X], (*la)[0][Geom::Y], (*lb)[0][Geom::X], (*lb)[0][Geom::Y], static_cast(i), static_cast(j)); TS_FAIL(temp); return false; } if (!Geom::are_near((*la)[1],(*lb)[1], eps)) { char temp[200]; sprintf(temp, "Different end of segment: (%g,%g) != (%g,%g), subpath: %u, segment: %u", (*la)[1][Geom::X], (*la)[1][Geom::Y], (*lb)[1][Geom::X], (*lb)[1][Geom::Y], static_cast(i), static_cast(j)); TS_FAIL(temp); return false; } } else if(Geom::CubicBezier const *la = dynamic_cast(ca)) { Geom::CubicBezier const *lb = dynamic_cast(cb); if (!Geom::are_near((*la)[0],(*lb)[0], eps)) { char temp[200]; sprintf(temp, "Different start of segment: (%g,%g) != (%g,%g), subpath: %u, segment: %u", (*la)[0][Geom::X], (*la)[0][Geom::Y], (*lb)[0][Geom::X], (*lb)[0][Geom::Y], static_cast(i), static_cast(j)); TS_FAIL(temp); return false; } if (!Geom::are_near((*la)[1],(*lb)[1], eps)) { char temp[200]; sprintf(temp, "Different 1st control point: (%g,%g) != (%g,%g), subpath: %u, segment: %u", (*la)[1][Geom::X], (*la)[1][Geom::Y], (*lb)[1][Geom::X], (*lb)[1][Geom::Y], static_cast(i), static_cast(j)); TS_FAIL(temp); return false; } if (!Geom::are_near((*la)[2],(*lb)[2], eps)) { char temp[200]; sprintf(temp, "Different 2nd control point: (%g,%g) != (%g,%g), subpath: %u, segment: %u", (*la)[2][Geom::X], (*la)[2][Geom::Y], (*lb)[2][Geom::X], (*lb)[2][Geom::Y], static_cast(i), static_cast(j)); TS_FAIL(temp); return false; } if (!Geom::are_near((*la)[3],(*lb)[3], eps)) { char temp[200]; sprintf(temp, "Different end of segment: (%g,%g) != (%g,%g), subpath: %u, segment: %u", (*la)[3][Geom::X], (*la)[3][Geom::Y], (*lb)[3][Geom::X], (*lb)[3][Geom::Y], static_cast(i), static_cast(j)); TS_FAIL(temp); return false; } } else { char temp[200]; sprintf(temp, "Unknown curve type: %s, subpath: %u, segment: %u", typeid(*ca).name(), static_cast(i), static_cast(j)); TS_FAIL(temp); } } else // not same type { char temp[200]; sprintf(temp, "Different curve types: %s != %s, subpath: %u, segment: %u", typeid(*ca).name(), typeid(*cb).name(), static_cast(i), static_cast(j)); TS_FAIL(temp); return false; } } } return true; } }; /* Local Variables: mode:c++ c-file-style:"stroustrup" c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) indent-tabs-mode:nil fill-column:99 End: */ // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :