summary | shortlog | log | commit | commitdiff | tree
raw | patch | inline | side by side (parent: 5f42271)
raw | patch | inline | side by side (parent: 5f42271)
author | jaspervdg <jaspervdg@users.sourceforge.net> | |
Wed, 6 Aug 2008 14:31:46 +0000 (14:31 +0000) | ||
committer | jaspervdg <jaspervdg@users.sourceforge.net> | |
Wed, 6 Aug 2008 14:31:46 +0000 (14:31 +0000) |
build.xml | patch | blob | history | |
src/svg/Makefile_insert | patch | blob | history | |
src/svg/svg-length-test.h | patch | blob | history | |
src/svg/svg-path-geom-test.h | [new file with mode: 0644] | patch | blob |
src/svg/svg-path-nr-test.h | [new file with mode: 0644] | patch | blob |
src/svg/svg-path-test.h | [deleted file] | patch | blob | history |
diff --git a/build.xml b/build.xml
index c8a65cab3625a2f1219c56a374e026754187cf41..4a8bb093363df78a19646f20f7bc26c182533b99 100644 (file)
--- a/build.xml
+++ b/build.xml
<!-- -->
<property name="arch" value="mingw32-"/>
<property name="archutil" value=""/>
- <property name="devlibs" location="c:/devlibs"/>
+ <property name="devlibs" location="${env.DEVLIBS_PATH}"/>
<property name="cxxtest" location="cxxtest"/>
<!-- -->
<include name="svg-affine-test.h"/>
<include name="svg-color-test.h"/>
<include name="svg-length-test.h"/>
- <include name="svg-path-test.h"/>
+ <include name="svg-path-geom-test.h"/>
+ <include name="svg-path-nr-test.h"/>
</fileset>
</cxxtestpart>
<cxxtestpart command="python ${cxxtest}/cxxtestgen.py --have-eh"
index e8f31b3c82d96056ff0375c8d3ce7f8287f59314..f7d2fcc5e5729240bb167572ab53915c1ad82ca4 100644 (file)
--- a/src/svg/Makefile_insert
+++ b/src/svg/Makefile_insert
$(srcdir)/svg/svg-affine-test.h \
$(srcdir)/svg/svg-color-test.h \
$(srcdir)/svg/svg-length-test.h \
- $(srcdir)/svg/svg-path-test.h
+ $(srcdir)/svg/svg-path-geom-test.h \
+ $(srcdir)/svg/svg-path-nr-test.h
svg_libtest_svg_a_SOURCES = \
svg/test-svg.cpp \
index 0f1ca5c959908545b54bf27080347450330c8bf4..ce709b51d29b07199d63d02c46fe96b0ac1b79a9 100644 (file)
validStrings.erase(iter);
}
}
- TSM_ASSERT_EQUALS(validStrings, validStrings.size(), 0);
+ TSM_ASSERT_EQUALS(validStrings, validStrings.size(), 0u);
}
// TODO: More tests
diff --git a/src/svg/svg-path-geom-test.h b/src/svg/svg-path-geom-test.h
--- /dev/null
@@ -0,0 +1,563 @@
+#include <cxxtest/TestSuite.h>\r
+#include "2geom/coord.h"\r
+#include "2geom/curves.h"\r
+#include "2geom/pathvector.h"\r
+#include "svg/svg.h"\r
+#include "prefs-utils.h"\r
+#include "streq.h"\r
+#include <stdio.h>\r
+#include <string>\r
+#include <vector>\r
+#include <glib/gmem.h>\r
+\r
+class SvgPathGeomTest : public CxxTest::TestSuite\r
+{\r
+private:\r
+ std::vector<std::string> rectanglesAbsoluteClosed;\r
+ std::vector<std::string> rectanglesRelativeClosed;\r
+ std::vector<std::string> rectanglesAbsoluteOpen;\r
+ std::vector<std::string> rectanglesRelativeOpen;\r
+ Geom::PathVector rectanglepv;\r
+public:\r
+ SvgPathGeomTest() {\r
+ // Lots of ways to define the same rectangle\r
+ rectanglesAbsoluteClosed.push_back("M 1,2 L 4,2 L 4,8 L 1,8 L 1,2 Z");\r
+ rectanglesAbsoluteClosed.push_back("M 1,2 L 4,2 L 4,8 L 1,8 z");\r
+ rectanglesAbsoluteClosed.push_back("M 1,2 4,2 4,8 1,8 z");\r
+ rectanglesAbsoluteClosed.push_back("M 1,2 H 4 V 8 H 1 z");\r
+ rectanglesRelativeClosed.push_back("m 1,2 l 3,0 l 0,6 l -3,0 z");\r
+ rectanglesRelativeClosed.push_back("m 1,2 3,0 0,6 -3,0 z");\r
+ rectanglesRelativeClosed.push_back("m 1,2 h 3 v 6 h -3 z");\r
+ rectanglesAbsoluteOpen.push_back("M 1,2 L 4,2 L 4,8 L 1,8 L 1,2");\r
+ rectanglesAbsoluteOpen.push_back("M 1,2 4,2 4,8 1,8 1,2");\r
+ rectanglesAbsoluteOpen.push_back("M 1,2 H 4 V 8 H 1 V 2");\r
+ rectanglesRelativeOpen.push_back("m 1,2 l 3,0 l 0,6 l -3,0 l 0,-6");\r
+ rectanglesRelativeOpen.push_back("m 1,2 3,0 0,6 -3,0 0,-6");\r
+ rectanglesRelativeOpen.push_back("m 1,2 h 3 v 6 h -3 v -6");\r
+ rectanglepv.push_back(Geom::Path(Geom::Point(1,2)));\r
+ rectanglepv.back().append(Geom::LineSegment(Geom::Point(1,2),Geom::Point(4,2)));\r
+ rectanglepv.back().append(Geom::LineSegment(Geom::Point(4,2),Geom::Point(4,8)));\r
+ rectanglepv.back().append(Geom::LineSegment(Geom::Point(4,8),Geom::Point(1,8)));\r
+ rectanglepv.back().append(Geom::LineSegment(Geom::Point(1,8),Geom::Point(1,2)));\r
+ // TODO: Also test some (smooth) cubic/quadratic beziers and elliptical arcs\r
+ }\r
+\r
+// createSuite and destroySuite get us per-suite setup and teardown\r
+// without us having to worry about static initialization order, etc.\r
+ static SvgPathGeomTest *createSuite() { return new SvgPathGeomTest(); }\r
+ static void destroySuite( SvgPathGeomTest *suite ) { delete suite; }\r
+\r
+ void testReadRectanglesAbsoluteClosed()\r
+ {\r
+ rectanglepv.back().close();\r
+ for(size_t i=0; i<rectanglesAbsoluteClosed.size(); i++) {\r
+ Geom::PathVector pv = sp_svg_read_pathv(rectanglesAbsoluteClosed[i].c_str());\r
+ TSM_ASSERT(rectanglesAbsoluteClosed[i].c_str(), bpathEqual(pv,rectanglepv));\r
+ }\r
+ }\r
+\r
+ void testReadRectanglesRelativeClosed()\r
+ {\r
+ rectanglepv.back().close();\r
+ for(size_t i=0; i<rectanglesRelativeClosed.size(); i++) {\r
+ Geom::PathVector pv = sp_svg_read_pathv(rectanglesRelativeClosed[i].c_str());\r
+ TSM_ASSERT(rectanglesRelativeClosed[i].c_str(), bpathEqual(pv,rectanglepv));\r
+ }\r
+ }\r
+\r
+ void testReadRectanglesAbsoluteOpen()\r
+ {\r
+ rectanglepv.back().close(false);\r
+ for(size_t i=0; i<rectanglesAbsoluteOpen.size(); i++) {\r
+ Geom::PathVector pv = sp_svg_read_pathv(rectanglesAbsoluteOpen[i].c_str());\r
+ TSM_ASSERT(rectanglesAbsoluteOpen[i].c_str(), bpathEqual(pv,rectanglepv));\r
+ }\r
+ }\r
+\r
+ void testReadRectanglesRelativeOpen()\r
+ {\r
+ rectanglepv.back().close(false);\r
+ for(size_t i=0; i<rectanglesRelativeOpen.size(); i++) {\r
+ Geom::PathVector pv = sp_svg_read_pathv(rectanglesRelativeOpen[i].c_str());\r
+ TSM_ASSERT(rectanglesRelativeOpen[i].c_str(), bpathEqual(pv,rectanglepv));\r
+ }\r
+ }\r
+\r
+ void testReadConcatenatedPaths()\r
+ {\r
+ Geom::PathVector pv_good;\r
+ pv_good.push_back(rectanglepv.back());\r
+ pv_good.push_back(rectanglepv.back()*Geom::Translate(pv_good[0].finalPoint()));\r
+ pv_good.push_back(rectanglepv.back()*Geom::Translate(pv_good[1].finalPoint()));\r
+ pv_good.push_back(rectanglepv.back());\r
+ pv_good[0].close();\r
+ pv_good[1].close(false);\r
+ pv_good[2].close();\r
+ pv_good[3].close(false);\r
+ std::string path_str = rectanglesAbsoluteClosed[0] + rectanglesRelativeOpen[0] + rectanglesRelativeClosed[0] + rectanglesAbsoluteOpen[0];\r
+ Geom::PathVector pv = sp_svg_read_pathv(path_str.c_str());\r
+ TS_ASSERT(bpathEqual(pv,pv_good));\r
+ }\r
+\r
+ void testReadZeroLengthSubpaths() {\r
+ // Per the SVG 1.1 specification (section F5) zero-length subpaths are relevant\r
+ Geom::PathVector pv_good;\r
+ pv_good.push_back(Geom::Path(Geom::Point(0,0)));\r
+ pv_good.push_back(Geom::Path(Geom::Point(1,1)));\r
+ pv_good.back().append(Geom::LineSegment(Geom::Point(1,1),Geom::Point(2,2)));\r
+ pv_good.push_back(Geom::Path(Geom::Point(3,3)));\r
+ pv_good.back().close();\r
+ pv_good.push_back(Geom::Path(Geom::Point(4,4)));\r
+ pv_good.back().append(Geom::LineSegment(Geom::Point(4,4),Geom::Point(5,5)));\r
+ pv_good.back().close();\r
+ pv_good.push_back(Geom::Path(Geom::Point(6,6)));\r
+ { // Test absolute version\r
+ char const * path_str = "M 0,0 M 1,1 L 2,2 M 3,3 z M 4,4 L 5,5 z M 6,6";\r
+ Geom::PathVector pv = sp_svg_read_pathv(path_str);\r
+ TSM_ASSERT(path_str, bpathEqual(pv,pv_good));\r
+ }\r
+ { // Test relative version\r
+ char const * path_str = "m 0,0 m 1,1 l 1,1 m 1,1 z m 1,1 l 1,1 z m 2,2";\r
+ Geom::PathVector pv = sp_svg_read_pathv(path_str);\r
+ TSM_ASSERT(path_str, bpathEqual(pv,pv_good));\r
+ }\r
+ }\r
+\r
+ void testReadImplicitMoveto() {\r
+ Geom::PathVector pv_good;\r
+ pv_good.push_back(Geom::Path(Geom::Point(1,1)));\r
+ pv_good.back().append(Geom::LineSegment(Geom::Point(1,1),Geom::Point(2,2)));\r
+ pv_good.back().close();\r
+ pv_good.push_back(Geom::Path(Geom::Point(1,1)));\r
+ pv_good.back().append(Geom::LineSegment(Geom::Point(1,1),Geom::Point(3,3)));\r
+ pv_good.back().close();\r
+ { // Test absolute version\r
+ char const * path_str = "M 1,1 L 2,2 z L 3,3 z";\r
+ Geom::PathVector pv = sp_svg_read_pathv(path_str);\r
+ TSM_ASSERT(path_str, bpathEqual(pv,pv_good));\r
+ }\r
+ { // Test relative version\r
+ char const * path_str = "M 1,1 L 2,2 z L 3,3 z";\r
+ Geom::PathVector pv = sp_svg_read_pathv(path_str);\r
+ TSM_ASSERT(path_str, bpathEqual(pv,pv_good));\r
+ }\r
+ }\r
+\r
+ void testReadFloatingPoint() {\r
+ Geom::PathVector pv_good;\r
+ pv_good.push_back(Geom::Path(Geom::Point(.01,.02)));\r
+ pv_good.back().append(Geom::LineSegment(Geom::Point(.01,.02),Geom::Point(.04,.02)));\r
+ pv_good.back().append(Geom::LineSegment(Geom::Point(.04,.02),Geom::Point(1.5,1.6)));\r
+ pv_good.back().append(Geom::LineSegment(Geom::Point(1.5,1.6),Geom::Point(.01,.08)));\r
+ pv_good.back().close();\r
+ { // Test decimals\r
+ char const * path_str = "M .01,.02 L.04.02 L1.5,1.6L0.01,0.08 .01.02 z";\r
+ Geom::PathVector pv = sp_svg_read_pathv(path_str);\r
+ TSM_ASSERT(path_str, bpathEqual(pv,pv_good));\r
+ }\r
+ { // Test exponent\r
+ char const * path_str = "M 1e-2,.2e-1 L 0.004e1,0.0002e+2 L0150E-2,1.6e0L1.0e-2,80e-3 z";\r
+ Geom::PathVector pv = sp_svg_read_pathv(path_str);\r
+ TSM_ASSERT(path_str, bpathEqual(pv,pv_good));\r
+ }\r
+ }\r
+\r
+ void testReadImplicitSeparation() {\r
+ // Coordinates need not be separated by whitespace if they can still be read unambiguously\r
+ Geom::PathVector pv_good;\r
+ pv_good.push_back(Geom::Path(Geom::Point(.1,.2)));\r
+ pv_good.back().append(Geom::LineSegment(Geom::Point(.1,.2),Geom::Point(.4,.2)));\r
+ pv_good.back().append(Geom::LineSegment(Geom::Point(.4,.2),Geom::Point(.4,.8)));\r
+ pv_good.back().append(Geom::LineSegment(Geom::Point(.4,.8),Geom::Point(.1,.8)));\r
+ pv_good.back().close();\r
+ { // Test absolute\r
+ char const * path_str = "M .1.2+0.4.2e0.4e0+8e-1.1.8 z";\r
+ Geom::PathVector pv = sp_svg_read_pathv(path_str);\r
+ TSM_ASSERT(path_str, bpathEqual(pv,pv_good));\r
+ }\r
+ { // Test relative\r
+ char const * path_str = "m .1.2+0.3.0e0.0e0+6e-1-.3.0 z";\r
+ Geom::PathVector pv = sp_svg_read_pathv(path_str);\r
+ TSM_ASSERT(path_str, bpathEqual(pv,pv_good));\r
+ }\r
+ }\r
+\r
+ void testReadErrorMisplacedCharacter() {\r
+ char const * path_str;\r
+ Geom::PathVector pv;\r
+ rectanglepv.back().close();\r
+ // Comma in the wrong place (commas may only appear between parameters)\r
+ path_str = "M 1,2 4,2 4,8 1,8 z , m 13,15";\r
+ pv = sp_svg_read_pathv(path_str);\r
+ TSM_ASSERT(path_str, bpathEqual(pv,rectanglepv));\r
+ // Comma in the wrong place (commas may only appear between parameters)\r
+ path_str = "M 1,2 4,2 4,8 1,8 z m,13,15";\r
+ pv = sp_svg_read_pathv(path_str);\r
+ TSM_ASSERT(path_str, bpathEqual(pv,rectanglepv));\r
+ // Period in the wrong place (no numbers after a 'z')\r
+ path_str = "M 1,2 4,2 4,8 1,8 z . m 13,15";\r
+ pv = sp_svg_read_pathv(path_str);\r
+ TSM_ASSERT(path_str, bpathEqual(pv,rectanglepv));\r
+ // Sign in the wrong place (no numbers after a 'z')\r
+ path_str = "M 1,2 4,2 4,8 1,8 z + - m 13,15";\r
+ pv = sp_svg_read_pathv(path_str);\r
+ TSM_ASSERT(path_str, bpathEqual(pv,rectanglepv));\r
+ // Digit in the wrong place (no numbers after a 'z')\r
+ path_str = "M 1,2 4,2 4,8 1,8 z 9809 m 13,15";\r
+ pv = sp_svg_read_pathv(path_str);\r
+ TSM_ASSERT(path_str, bpathEqual(pv,rectanglepv));\r
+ // Digit in the wrong place (no numbers after a 'z')\r
+ path_str = "M 1,2 4,2 4,8 1,8 z 9809 876 m 13,15";\r
+ pv = sp_svg_read_pathv(path_str);\r
+ TSM_ASSERT(path_str, bpathEqual(pv,rectanglepv));\r
+ }\r
+\r
+ void testReadErrorUnrecognizedCharacter() {\r
+ char const * path_str;\r
+ Geom::PathVector pv;\r
+ rectanglepv.back().close();\r
+ // Unrecognized character\r
+ path_str = "M 1,2 4,2 4,8 1,8 z&m 13,15";\r
+ pv = sp_svg_read_pathv(path_str);\r
+ TSM_ASSERT(path_str, bpathEqual(pv,rectanglepv));\r
+ // Unrecognized character\r
+ path_str = "M 1,2 4,2 4,8 1,8 z m &13,15";\r
+ pv = sp_svg_read_pathv(path_str);\r
+ TSM_ASSERT(path_str, bpathEqual(pv,rectanglepv));\r
+ }\r
+\r
+ void testReadErrorTypo() {\r
+ char const * path_str;\r
+ Geom::PathVector pv;\r
+ rectanglepv.back().close();\r
+ // Typo\r
+ path_str = "M 1,2 4,2 4,8 1,8 z j 13,15";\r
+ pv = sp_svg_read_pathv(path_str);\r
+ TSM_ASSERT(path_str, bpathEqual(pv,rectanglepv));\r
+\r
+ rectanglepv.back().close(false);\r
+ // Typo\r
+ path_str = "M 1,2 4,2 4,8 1,8 L 1,2 x m 13,15";\r
+ pv = sp_svg_read_pathv(path_str);\r
+ TSM_ASSERT(path_str, bpathEqual(pv,rectanglepv));\r
+ }\r
+\r
+ void testReadErrorIllformedNumbers() {\r
+ char const * path_str;\r
+ Geom::PathVector pv;\r
+ rectanglepv.back().close();\r
+ // Double exponent\r
+ path_str = "M 1,2 4,2 4,8 1,8 z m 13e4e5,15";\r
+ pv = sp_svg_read_pathv(path_str);\r
+ TSM_ASSERT(path_str, bpathEqual(pv,rectanglepv));\r
+ // Double sign\r
+ path_str = "M 1,2 4,2 4,8 1,8 z m +-13,15";\r
+ pv = sp_svg_read_pathv(path_str);\r
+ TSM_ASSERT(path_str, bpathEqual(pv,rectanglepv));\r
+ // Double sign\r
+ path_str = "M 1,2 4,2 4,8 1,8 z m 13e+-12,15";\r
+ pv = sp_svg_read_pathv(path_str);\r
+ TSM_ASSERT(path_str, bpathEqual(pv,rectanglepv));\r
+ // No digit\r
+ path_str = "M 1,2 4,2 4,8 1,8 z m .e12,15";\r
+ pv = sp_svg_read_pathv(path_str);\r
+ TSM_ASSERT(path_str, bpathEqual(pv,rectanglepv));\r
+ // No digit\r
+ path_str = "M 1,2 4,2 4,8 1,8 z m .,15";\r
+ pv = sp_svg_read_pathv(path_str);\r
+ TSM_ASSERT(path_str, bpathEqual(pv,rectanglepv));\r
+ // No digit\r
+ path_str = "M 1,2 4,2 4,8 1,8 z m +,15";\r
+ pv = sp_svg_read_pathv(path_str);\r
+ TSM_ASSERT(path_str, bpathEqual(pv,rectanglepv));\r
+ // No digit\r
+ path_str = "M 1,2 4,2 4,8 1,8 z m +.e+,15";\r
+ pv = sp_svg_read_pathv(path_str);\r
+ TSM_ASSERT(path_str, bpathEqual(pv,rectanglepv));\r
+ }\r
+\r
+ void testReadErrorJunk() {\r
+ char const * path_str;\r
+ Geom::PathVector pv;\r
+ rectanglepv.back().close();\r
+ // Junk\r
+ path_str = "M 1,2 4,2 4,8 1,8 z j 357 hkjh.,34e34 90ih6kj4 h5k6vlh4N.,6,45wikuyi3yere..3487 m 13,23";\r
+ pv = sp_svg_read_pathv(path_str);\r
+ TSM_ASSERT(path_str, bpathEqual(pv,rectanglepv));\r
+ }\r
+\r
+ void testReadErrorStopReading() {\r
+ char const * path_str;\r
+ Geom::PathVector pv;\r
+ rectanglepv.back().close();\r
+ // Unrecognized parameter\r
+ path_str = "M 1,2 4,2 4,8 1,8 z m #$%,23,34";\r
+ pv = sp_svg_read_pathv(path_str);\r
+ TSM_ASSERT(path_str, bpathEqual(pv,rectanglepv));\r
+ // Invalid parameter\r
+ path_str = "M 1,2 4,2 4,8 1,8 z m #$%,23,34";\r
+ pv = sp_svg_read_pathv(path_str);\r
+ TSM_ASSERT(path_str, bpathEqual(pv,rectanglepv));\r
+ // Illformed parameter\r
+ path_str = "M 1,2 4,2 4,8 1,8 z m +-12,23,34";\r
+ pv = sp_svg_read_pathv(path_str);\r
+ TSM_ASSERT(path_str, bpathEqual(pv,rectanglepv));\r
+\r
+ rectanglepv.back().close(false);\r
+ // "Third" parameter\r
+ path_str = "M 1,2 4,2 4,8 1,8 1,2,3 M 12,23";\r
+ pv = sp_svg_read_pathv(path_str);\r
+ TSM_ASSERT(path_str, bpathEqual(pv,rectanglepv));\r
+ }\r
+\r
+ void testRoundTrip() {\r
+ // This is the easiest way to (also) test writing path data, as a path can be written in more than one way.\r
+ Geom::PathVector pv;\r
+ Geom::PathVector new_pv;\r
+ std::string org_path_str;\r
+ char * path_str;\r
+ // Rectangle (closed)\r
+ org_path_str = rectanglesAbsoluteClosed[0];\r
+ pv = sp_svg_read_pathv(org_path_str.c_str());\r
+ path_str = sp_svg_write_path(pv);\r
+ new_pv = sp_svg_read_pathv(path_str);\r
+ TSM_ASSERT(org_path_str.c_str(), bpathEqual(pv,new_pv));\r
+ g_free(path_str);\r
+ // Rectangle (open)\r
+ org_path_str = rectanglesAbsoluteOpen[0];\r
+ pv = sp_svg_read_pathv(org_path_str.c_str());\r
+ path_str = sp_svg_write_path(pv);\r
+ new_pv = sp_svg_read_pathv(path_str);\r
+ TSM_ASSERT(org_path_str.c_str(), bpathEqual(pv,new_pv));\r
+ g_free(path_str);\r
+ // Concatenated rectangles\r
+ org_path_str = rectanglesAbsoluteClosed[0] + rectanglesRelativeOpen[0] + rectanglesRelativeClosed[0] + rectanglesAbsoluteOpen[0];\r
+ pv = sp_svg_read_pathv(org_path_str.c_str());\r
+ path_str = sp_svg_write_path(pv);\r
+ new_pv = sp_svg_read_pathv(path_str);\r
+ TSM_ASSERT(org_path_str.c_str(), bpathEqual(pv,new_pv));\r
+ g_free(path_str);\r
+ // Zero-length subpaths\r
+ org_path_str = "M 0,0 M 1,1 L 2,2 M 3,3 z M 4,4 L 5,5 z M 6,6";\r
+ pv = sp_svg_read_pathv(org_path_str.c_str());\r
+ path_str = sp_svg_write_path(pv);\r
+ new_pv = sp_svg_read_pathv(path_str);\r
+ TSM_ASSERT(org_path_str.c_str(), bpathEqual(pv,new_pv));\r
+ g_free(path_str);\r
+ // Floating-point\r
+ org_path_str = "M .01,.02 L 0.04,0.02 L.04,.08L0.01,0.08 z""M 1e-2,.2e-1 L 0.004e1,0.0002e+2 L04E-2,.08e0L1.0e-2,80e-3 z";\r
+ pv = sp_svg_read_pathv(org_path_str.c_str());\r
+ path_str = sp_svg_write_path(pv);\r
+ new_pv = sp_svg_read_pathv(path_str);\r
+ TSM_ASSERT(org_path_str.c_str(), bpathEqual(pv, new_pv, 1e-17));\r
+ g_free(path_str);\r
+ }\r
+\r
+ void testMinexpPrecision() {\r
+ Geom::PathVector pv;\r
+ char * path_str;\r
+ // Default values\r
+ prefs_set_int_attribute("options.svgoutput", "allowrelativecoordinates", 1);\r
+ prefs_set_int_attribute("options.svgoutput", "forcerepeatcommands", 0);\r
+ prefs_set_int_attribute("options.svgoutput", "numericprecision", 8);\r
+ prefs_set_int_attribute("options.svgoutput", "minimumexponent", -8);\r
+ 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");\r
+ path_str = sp_svg_write_path(pv);\r
+ 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 );\r
+ g_free(path_str);\r
+ }\r
+\r
+private:\r
+ bool bpathEqual(Geom::PathVector const &a, Geom::PathVector const &b, double eps = 1e-16) {\r
+ if (a.size() != b.size()) {\r
+ char temp[100];\r
+ sprintf(temp, "PathVectors not the same size. (%u != %u)", a.size(), b.size());\r
+ TS_FAIL(temp);\r
+ return false;\r
+ }\r
+ for(size_t i=0; i<a.size(); i++) {\r
+ Geom::Path const &pa = a[i];\r
+ Geom::Path const &pb = b[i];\r
+ if (pa.closed() != pb.closed()) {\r
+ TS_FAIL("One path is closed, the other open.");\r
+ return false;\r
+ }\r
+ if (pa.size() != pb.size()) {\r
+ char temp[100];\r
+ sprintf(temp, "Not the same number of curves in path. (%u != %u)", pa.size(), pb.size());\r
+ TS_FAIL(temp);\r
+ return false;\r
+ }\r
+ for(size_t j=0; j<pa.size(); j++) {\r
+ Geom::Curve const* ca = &pa[j];\r
+ Geom::Curve const* cb = &pb[j];\r
+ if (typeid(*ca) == typeid(*cb))\r
+ {\r
+ if(Geom::LineSegment const *la = dynamic_cast<Geom::LineSegment const*>(ca))\r
+ {\r
+ Geom::LineSegment const *lb = dynamic_cast<Geom::LineSegment const*>(cb);\r
+ if (!Geom::are_near((*la)[0],(*lb)[0], eps)) {\r
+ char temp[200];\r
+ sprintf(temp, "Different start of segment: (%g,%g) != (%g,%g)", (*la)[0][Geom::X], (*la)[0][Geom::Y], (*lb)[0][Geom::X], (*lb)[0][Geom::Y]);\r
+ TS_FAIL(temp);\r
+ return false;\r
+ }\r
+ if (!Geom::are_near((*la)[1],(*lb)[1], eps)) {\r
+ char temp[200];\r
+ sprintf(temp, "Different end of segment: (%g,%g) != (%g,%g)", (*la)[1][Geom::X], (*la)[1][Geom::Y], (*lb)[1][Geom::X], (*lb)[1][Geom::Y]);\r
+ TS_FAIL(temp);\r
+ return false;\r
+ }\r
+ }\r
+ else if(Geom::HLineSegment const *la = dynamic_cast<Geom::HLineSegment const*>(ca))\r
+ {\r
+ Geom::HLineSegment const *lb = dynamic_cast<Geom::HLineSegment const*>(cb);\r
+ if (!Geom::are_near((*la).initialPoint(),(*lb).initialPoint(), eps)) {\r
+ char temp[200];\r
+ sprintf(temp, "Different start of segment: (%g,%g) != (%g,%g)", (*la).initialPoint()[Geom::X], (*la).initialPoint()[Geom::Y], (*lb).initialPoint()[Geom::X], (*lb).initialPoint()[Geom::Y]);\r
+ TS_FAIL(temp);\r
+ return false;\r
+ }\r
+ if (!Geom::are_near((*la).finalPoint(),(*lb).finalPoint(), eps)) {\r
+ char temp[200];\r
+ sprintf(temp, "Different end of segment: (%g,%g) != (%g,%g)", (*la).finalPoint()[Geom::X], (*la).finalPoint()[Geom::Y], (*lb).finalPoint()[Geom::X], (*lb).finalPoint()[Geom::Y]);\r
+ TS_FAIL(temp);\r
+ return false;\r
+ }\r
+ }\r
+ else if(Geom::VLineSegment const *la = dynamic_cast<Geom::VLineSegment const*>(ca))\r
+ {\r
+ Geom::VLineSegment const *lb = dynamic_cast<Geom::VLineSegment const*>(cb);\r
+ if (!Geom::are_near((*la).initialPoint(),(*lb).initialPoint(), eps)) {\r
+ char temp[200];\r
+ sprintf(temp, "Different start of segment: (%g,%g) != (%g,%g)", (*la).initialPoint()[Geom::X], (*la).initialPoint()[Geom::Y], (*lb).initialPoint()[Geom::X], (*lb).initialPoint()[Geom::Y]);\r
+ TS_FAIL(temp);\r
+ return false;\r
+ }\r
+ if (!Geom::are_near((*la).finalPoint(),(*lb).finalPoint(), eps)) {\r
+ char temp[200];\r
+ sprintf(temp, "Different end of segment: (%g,%g) != (%g,%g)", (*la).finalPoint()[Geom::X], (*la).finalPoint()[Geom::Y], (*lb).finalPoint()[Geom::X], (*lb).finalPoint()[Geom::Y]);\r
+ TS_FAIL(temp);\r
+ return false;\r
+ }\r
+ }\r
+ else if(Geom::CubicBezier const *la = dynamic_cast<Geom::CubicBezier const*>(ca))\r
+ {\r
+ Geom::CubicBezier const *lb = dynamic_cast<Geom::CubicBezier const*>(cb);\r
+ if (!Geom::are_near((*la)[0],(*lb)[0], eps)) {\r
+ char temp[200];\r
+ sprintf(temp, "Different start of segment: (%g,%g) != (%g,%g)", (*la)[0][Geom::X], (*la)[0][Geom::Y], (*lb)[0][Geom::X], (*lb)[0][Geom::Y]);\r
+ TS_FAIL(temp);\r
+ return false;\r
+ }\r
+ if (!Geom::are_near((*la)[1],(*lb)[1], eps)) {\r
+ char temp[200];\r
+ sprintf(temp, "Different 1st control point: (%g,%g) != (%g,%g)", (*la)[1][Geom::X], (*la)[1][Geom::Y], (*lb)[1][Geom::X], (*lb)[1][Geom::Y]);\r
+ TS_FAIL(temp);\r
+ return false;\r
+ }\r
+ if (!Geom::are_near((*la)[2],(*lb)[2], eps)) {\r
+ char temp[200];\r
+ sprintf(temp, "Different 2nd control point: (%g,%g) != (%g,%g)", (*la)[2][Geom::X], (*la)[2][Geom::Y], (*lb)[2][Geom::X], (*lb)[2][Geom::Y]);\r
+ TS_FAIL(temp);\r
+ return false;\r
+ }\r
+ if (!Geom::are_near((*la)[3],(*lb)[3], eps)) {\r
+ char temp[200];\r
+ sprintf(temp, "Different end of segment: (%g,%g) != (%g,%g)", (*la)[3][Geom::X], (*la)[3][Geom::Y], (*lb)[3][Geom::X], (*lb)[3][Geom::Y]);\r
+ TS_FAIL(temp);\r
+ return false;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ TS_FAIL((std::string("Unknown curve type: ") + typeid(*ca).name()).c_str());\r
+ return false;\r
+ }\r
+ }\r
+ else // not same type\r
+ {\r
+ if(Geom::LineSegment const *la = dynamic_cast<Geom::LineSegment const*>(ca))\r
+ {\r
+ if (Geom::HLineSegment const *lb = dynamic_cast<Geom::HLineSegment const*>(cb)) {\r
+ if (!Geom::are_near((*la).initialPoint(),(*lb).initialPoint(), eps)) {\r
+ char temp[200];\r
+ sprintf(temp, "Different start of segment: (%g,%g) != (%g,%g)", (*la).initialPoint()[Geom::X], (*la).initialPoint()[Geom::Y], (*lb).initialPoint()[Geom::X], (*lb).initialPoint()[Geom::Y]);\r
+ TS_FAIL(temp);\r
+ return false;\r
+ }\r
+ if (!Geom::are_near((*la).finalPoint(),(*lb).finalPoint(), eps)) {\r
+ char temp[200];\r
+ sprintf(temp, "Different end of segment: (%g,%g) != (%g,%g)", (*la).finalPoint()[Geom::X], (*la).finalPoint()[Geom::Y], (*lb).finalPoint()[Geom::X], (*lb).finalPoint()[Geom::Y]);\r
+ TS_FAIL(temp);\r
+ return false;\r
+ }\r
+ } else if (Geom::VLineSegment const *lb = dynamic_cast<Geom::VLineSegment const*>(cb)) {\r
+ if (!Geom::are_near((*la).initialPoint(),(*lb).initialPoint(), eps)) {\r
+ char temp[200];\r
+ sprintf(temp, "Different start of segment: (%g,%g) != (%g,%g)", (*la).initialPoint()[Geom::X], (*la).initialPoint()[Geom::Y], (*lb).initialPoint()[Geom::X], (*lb).initialPoint()[Geom::Y]);\r
+ TS_FAIL(temp);\r
+ return false;\r
+ }\r
+ if (!Geom::are_near((*la).finalPoint(),(*lb).finalPoint(), eps)) {\r
+ char temp[200];\r
+ sprintf(temp, "Different end of segment: (%g,%g) != (%g,%g)", (*la).finalPoint()[Geom::X], (*la).finalPoint()[Geom::Y], (*lb).finalPoint()[Geom::X], (*lb).finalPoint()[Geom::Y]);\r
+ TS_FAIL(temp);\r
+ return false;\r
+ }\r
+ } else {\r
+ TS_FAIL((std::string("Different curve types: ") + typeid(*ca).name() + " != " + typeid(*cb).name()).c_str());\r
+ return false;\r
+ }\r
+ }\r
+ else if(Geom::LineSegment const *lb = dynamic_cast<Geom::LineSegment const*>(cb))\r
+ {\r
+ if (Geom::HLineSegment const *la = dynamic_cast<Geom::HLineSegment const*>(ca)) {\r
+ if (!Geom::are_near((*la).initialPoint(),(*lb).initialPoint(), eps)) {\r
+ char temp[200];\r
+ sprintf(temp, "Different start of segment: (%g,%g) != (%g,%g)", (*la).initialPoint()[Geom::X], (*la).initialPoint()[Geom::Y], (*lb).initialPoint()[Geom::X], (*lb).initialPoint()[Geom::Y]);\r
+ TS_FAIL(temp);\r
+ return false;\r
+ }\r
+ if (!Geom::are_near((*la).finalPoint(),(*lb).finalPoint(), eps)) {\r
+ char temp[200];\r
+ sprintf(temp, "Different end of segment: (%g,%g) != (%g,%g)", (*la).finalPoint()[Geom::X], (*la).finalPoint()[Geom::Y], (*lb).finalPoint()[Geom::X], (*lb).finalPoint()[Geom::Y]);\r
+ TS_FAIL(temp);\r
+ return false;\r
+ }\r
+ } else if (Geom::VLineSegment const *la = dynamic_cast<Geom::VLineSegment const*>(ca)) {\r
+ if (!Geom::are_near((*la).initialPoint(),(*lb).initialPoint(), eps)) {\r
+ char temp[200];\r
+ sprintf(temp, "Different start of segment: (%g,%g) != (%g,%g)", (*la).initialPoint()[Geom::X], (*la).initialPoint()[Geom::Y], (*lb).initialPoint()[Geom::X], (*lb).initialPoint()[Geom::Y]);\r
+ TS_FAIL(temp);\r
+ return false;\r
+ }\r
+ if (!Geom::are_near((*la).finalPoint(),(*lb).finalPoint(), eps)) {\r
+ char temp[200];\r
+ sprintf(temp, "Different end of segment: (%g,%g) != (%g,%g)", (*la).finalPoint()[Geom::X], (*la).finalPoint()[Geom::Y], (*lb).finalPoint()[Geom::X], (*lb).finalPoint()[Geom::Y]);\r
+ TS_FAIL(temp);\r
+ return false;\r
+ }\r
+ } else {\r
+ TS_FAIL((std::string("Different curve types: ") + typeid(*ca).name() + " != " + typeid(*cb).name()).c_str());\r
+ return false;\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+ return true;\r
+ }\r
+};\r
+\r
+\r
+/*\r
+ Local Variables:\r
+ mode:c++\r
+ c-file-style:"stroustrup"\r
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))\r
+ indent-tabs-mode:nil\r
+ fill-column:99\r
+ End:\r
+*/\r
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :\r
diff --git a/src/svg/svg-path-nr-test.h b/src/svg/svg-path-nr-test.h
--- /dev/null
@@ -0,0 +1,493 @@
+#include <cxxtest/TestSuite.h>
+#include "libnr/n-art-bpath.h"
+#include "svg/svg.h"
+#include "2geom/coord.h"
+#include "prefs-utils.h"
+#include "streq.h"
+#include <string>
+#include <vector>
+#include <glib/gmem.h>
+
+class SvgPathNRTest : public CxxTest::TestSuite
+{
+private:
+ std::vector<std::string> rectanglesAbsoluteClosed;
+ std::vector<std::string> rectanglesRelativeClosed;
+ std::vector<std::string> rectanglesAbsoluteOpen;
+ std::vector<std::string> rectanglesRelativeOpen;
+ NArtBpath rectangleBpath[5+1];
+public:
+ SvgPathNRTest() {
+ // Lots of ways to define the same rectangle
+ rectanglesAbsoluteClosed.push_back("M 1,2 L 4,2 L 4,8 L 1,8 L 1,2 Z");
+ 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");
+ rectangleBpath[0].code = NR_MOVETO;
+ rectangleBpath[0].x3 = 1;
+ rectangleBpath[0].y3 = 2;
+ rectangleBpath[1].code = NR_LINETO;
+ rectangleBpath[1].x3 = 4;
+ rectangleBpath[1].y3 = 2;
+ rectangleBpath[2].code = NR_LINETO;
+ rectangleBpath[2].x3 = 4;
+ rectangleBpath[2].y3 = 8;
+ rectangleBpath[3].code = NR_LINETO;
+ rectangleBpath[3].x3 = 1;
+ rectangleBpath[3].y3 = 8;
+ rectangleBpath[4].code = NR_LINETO;
+ rectangleBpath[4].x3 = 1;
+ rectangleBpath[4].y3 = 2;
+ rectangleBpath[5].code = NR_END;
+ // TODO: Also test some (smooth) cubic/quadratic beziers and elliptical arcs
+ }
+
+// createSuite and destroySuite get us per-suite setup and teardown
+// without us having to worry about static initialization order, etc.
+ static SvgPathNRTest *createSuite() { return new SvgPathNRTest(); }
+ static void destroySuite( SvgPathNRTest *suite ) { delete suite; }
+
+ void testReadRectanglesAbsoluteClosed()
+ {
+ rectangleBpath[0].code = NR_MOVETO;
+ for(size_t i=0; i<rectanglesAbsoluteClosed.size(); i++) {
+ NArtBpath * bpath = sp_svg_read_path(rectanglesAbsoluteClosed[i].c_str());
+ TS_ASSERT(bpathEqual(bpath,rectangleBpath));
+ g_free(bpath);
+ }
+ }
+
+ void testReadRectanglesRelativeClosed()
+ {
+ rectangleBpath[0].code = NR_MOVETO;
+ for(size_t i=0; i<rectanglesRelativeClosed.size(); i++) {
+ NArtBpath * bpath = sp_svg_read_path(rectanglesRelativeClosed[i].c_str());
+ TS_ASSERT(bpathEqual(bpath,rectangleBpath));
+ g_free(bpath);
+ }
+ }
+
+ void testReadRectanglesAbsoluteOpen()
+ {
+ rectangleBpath[0].code = NR_MOVETO_OPEN;
+ for(size_t i=0; i<rectanglesAbsoluteOpen.size(); i++) {
+ NArtBpath * bpath = sp_svg_read_path(rectanglesAbsoluteOpen[i].c_str());
+ TS_ASSERT(bpathEqual(bpath,rectangleBpath));
+ g_free(bpath);
+ }
+ }
+
+ void testReadRectanglesRelativeOpen()
+ {
+ rectangleBpath[0].code = NR_MOVETO_OPEN;
+ for(size_t i=0; i<rectanglesRelativeOpen.size(); i++) {
+ NArtBpath * bpath = sp_svg_read_path(rectanglesRelativeOpen[i].c_str());
+ TS_ASSERT(bpathEqual(bpath,rectangleBpath));
+ g_free(bpath);
+ }
+ }
+
+ void testReadConcatenatedPaths()
+ {
+ NArtBpath bpath_good[4*5+1];
+ for(size_t i=0; i<4; i++) {
+ memcpy(bpath_good+i*5,rectangleBpath,sizeof(rectangleBpath[0])*5);
+ }
+ bpath_good[0*5].code = NR_MOVETO;
+ bpath_good[1*5].code = NR_MOVETO_OPEN;
+ bpath_good[2*5].code = NR_MOVETO;
+ bpath_good[3*5].code = NR_MOVETO_OPEN;
+ bpath_good[4*5].code = NR_END;
+ for(size_t i=0; i<5; i++) {
+ bpath_good[1*5+i].x3 += bpath_good[0*5+4].x3;
+ bpath_good[1*5+i].y3 += bpath_good[0*5+4].y3;
+ }
+ for(size_t i=0; i<5; i++) {
+ bpath_good[2*5+i].x3 += bpath_good[1*5+4].x3;
+ bpath_good[2*5+i].y3 += bpath_good[1*5+4].y3;
+ }
+ std::string path_str = rectanglesAbsoluteClosed[0] + rectanglesRelativeOpen[0] + rectanglesRelativeClosed[0] + rectanglesAbsoluteOpen[0];
+ NArtBpath * bpath = sp_svg_read_path(path_str.c_str());
+ TS_ASSERT(bpathEqual(bpath,bpath_good));
+ g_free(bpath);
+ }
+
+ void testReadZeroLengthSubpaths() {
+ // Per the SVG 1.1 specification (section F5) zero-length subpaths are relevant
+ NArtBpath bpath_good[8+1];
+ bpath_good[0].code = NR_MOVETO_OPEN;
+ bpath_good[0].x3 = bpath_good[0].y3 = 0;
+ bpath_good[1].code = NR_MOVETO_OPEN;
+ bpath_good[1].x3 = bpath_good[1].y3 = 1;
+ bpath_good[2].code = NR_LINETO;
+ bpath_good[2].x3 = bpath_good[2].y3 = 2;
+ bpath_good[3].code = NR_MOVETO;
+ bpath_good[3].x3 = bpath_good[3].y3 = 3;
+ bpath_good[4].code = NR_MOVETO;
+ bpath_good[4].x3 = bpath_good[4].y3 = 4;
+ bpath_good[5].code = NR_LINETO;
+ bpath_good[5].x3 = bpath_good[5].y3 = 5;
+ bpath_good[6].code = NR_LINETO;
+ bpath_good[6].x3 = bpath_good[6].y3 = 4;
+ bpath_good[7].code = NR_MOVETO_OPEN;
+ bpath_good[7].x3 = bpath_good[7].y3 = 6;
+ bpath_good[8].code = NR_END;
+ { // Test absolute version
+ char const * path_str = "M 0,0 M 1,1 L 2,2 M 3,3 z M 4,4 L 5,5 z M 6,6";
+ NArtBpath * bpath = sp_svg_read_path(path_str);
+ TS_ASSERT(bpathEqual(bpath,bpath_good));
+ g_free(bpath);
+ }
+ { // Test relative version
+ char const * path_str = "m 0,0 m 1,1 l 1,1 m 1,1 z m 1,1 l 1,1 z m 2,2";
+ NArtBpath * bpath = sp_svg_read_path(path_str);
+ TS_ASSERT(bpathEqual(bpath,bpath_good));
+ g_free(bpath);
+ }
+ }
+
+ void testReadImplicitMoveto() {
+ NArtBpath bpath_good[6+1];
+ bpath_good[0].code = NR_MOVETO;
+ bpath_good[0].x3 = bpath_good[0].y3 = 1;
+ bpath_good[1].code = NR_LINETO;
+ bpath_good[1].x3 = bpath_good[1].y3 = 2;
+ bpath_good[2].code = NR_LINETO;
+ bpath_good[2].x3 = bpath_good[2].y3 = 1;
+ bpath_good[3].code = NR_MOVETO;
+ bpath_good[3].x3 = bpath_good[3].y3 = 1;
+ bpath_good[4].code = NR_LINETO;
+ bpath_good[4].x3 = bpath_good[4].y3 = 3;
+ bpath_good[5].code = NR_LINETO;
+ bpath_good[5].x3 = bpath_good[5].y3 = 1;
+ bpath_good[6].code = NR_END;
+ { // Test absolute version
+ char const * path_str = "M 1,1 L 2,2 z L 3,3 z";
+ NArtBpath * bpath = sp_svg_read_path(path_str);
+ TS_ASSERT(bpathEqual(bpath,bpath_good));
+ g_free(bpath);
+ }
+ { // Test relative version
+ char const * path_str = "M 1,1 L 2,2 z L 3,3 z";
+ NArtBpath * bpath = sp_svg_read_path(path_str);
+ TS_ASSERT(bpathEqual(bpath,bpath_good));
+ g_free(bpath);
+ }
+ }
+
+ void testReadFloatingPoint() {
+ NArtBpath bpath_good[5+1];
+ bpath_good[0].code = NR_MOVETO;
+ bpath_good[0].x3 = .01;
+ bpath_good[0].y3 = .02;
+ bpath_good[1].code = NR_LINETO;
+ bpath_good[1].x3 = .04;
+ bpath_good[1].y3 = .02;
+ bpath_good[2].code = NR_LINETO;
+ bpath_good[2].x3 = 1.5;
+ bpath_good[2].y3 = 1.6;
+ bpath_good[3].code = NR_LINETO;
+ bpath_good[3].x3 = .01;
+ bpath_good[3].y3 = .08;
+ bpath_good[4].code = NR_LINETO;
+ bpath_good[4].x3 = .01;
+ bpath_good[4].y3 = .02;
+ bpath_good[5].code = NR_END;
+ { // Test decimals
+ char const * path_str = "M .01,.02 L.04.02 L1.5,1.6L0.01,0.08 .01.02 z";
+ NArtBpath * bpath = sp_svg_read_path(path_str);
+ TS_ASSERT(bpathEqual(bpath,bpath_good));
+ g_free(bpath);
+ }
+ { // Test exponent
+ char const * path_str = "M 1e-2,.2e-1 L 0.004e1,0.0002e+2 L0150E-2,1.6e0L1.0e-2,80e-3 z";
+ NArtBpath * bpath = sp_svg_read_path(path_str);
+ TS_ASSERT(bpathEqual(bpath,bpath_good));
+ g_free(bpath);
+ }
+ }
+
+ void testReadImplicitSeparation() {
+ // Coordinates need not be separated by whitespace if they can still be read unambiguously
+ NArtBpath bpath_good[5+1];
+ bpath_good[0].code = NR_MOVETO;
+ bpath_good[0].x3 = .1;
+ bpath_good[0].y3 = .2;
+ bpath_good[1].code = NR_LINETO;
+ bpath_good[1].x3 = .4;
+ bpath_good[1].y3 = .2;
+ bpath_good[2].code = NR_LINETO;
+ bpath_good[2].x3 = .4;
+ bpath_good[2].y3 = .8;
+ bpath_good[3].code = NR_LINETO;
+ bpath_good[3].x3 = .1;
+ bpath_good[3].y3 = .8;
+ bpath_good[4].code = NR_LINETO;
+ bpath_good[4].x3 = .1;
+ bpath_good[4].y3 = .2;
+ bpath_good[5].code = NR_END;
+ { // Test absolute
+ char const * path_str = "M .1.2+0.4.2e0.4e0+8e-1.1.8 z";
+ NArtBpath * bpath = sp_svg_read_path(path_str);
+ TS_ASSERT(bpathEqual(bpath,bpath_good));
+ g_free(bpath);
+ }
+ { // Test relative
+ char const * path_str = "m .1.2+0.3.0e0.0e0+6e-1-.3.0 z";
+ NArtBpath * bpath = sp_svg_read_path(path_str);
+ TS_ASSERT(bpathEqual(bpath,bpath_good));
+ g_free(bpath);
+ }
+ }
+
+ void testReadErrorMisplacedCharacter() {
+ char const * path_str;
+ NArtBpath * bpath;
+ NArtBpath * bpath_good = rectangleBpath;
+ bpath_good[0].code = NR_MOVETO;
+ // Comma in the wrong place (commas may only appear between parameters)
+ path_str = "M 1,2 4,2 4,8 1,8 z , m 13,15";
+ bpath = sp_svg_read_path(path_str);
+ TS_ASSERT(bpathEqual(bpath,bpath_good));
+ g_free(bpath);
+ // Comma in the wrong place (commas may only appear between parameters)
+ path_str = "M 1,2 4,2 4,8 1,8 z m,13,15";
+ bpath = sp_svg_read_path(path_str);
+ TS_ASSERT(bpathEqual(bpath,bpath_good));
+ g_free(bpath);
+ // Period in the wrong place (no numbers after a 'z')
+ path_str = "M 1,2 4,2 4,8 1,8 z . m 13,15";
+ bpath = sp_svg_read_path(path_str);
+ TS_ASSERT(bpathEqual(bpath,bpath_good));
+ g_free(bpath);
+ // Sign in the wrong place (no numbers after a 'z')
+ path_str = "M 1,2 4,2 4,8 1,8 z + - m 13,15";
+ bpath = sp_svg_read_path(path_str);
+ TS_ASSERT(bpathEqual(bpath,bpath_good));
+ g_free(bpath);
+ // Digit in the wrong place (no numbers after a 'z')
+ path_str = "M 1,2 4,2 4,8 1,8 z 9809 m 13,15";
+ bpath = sp_svg_read_path(path_str);
+ TS_ASSERT(bpathEqual(bpath,bpath_good));
+ g_free(bpath);
+ // Digit in the wrong place (no numbers after a 'z')
+ path_str = "M 1,2 4,2 4,8 1,8 z 9809 876 m 13,15";
+ bpath = sp_svg_read_path(path_str);
+ TS_ASSERT(bpathEqual(bpath,bpath_good));
+ g_free(bpath);
+ }
+
+ void testReadErrorUnrecognizedCharacter() {
+ char const * path_str;
+ NArtBpath * bpath;
+ NArtBpath * bpath_good = rectangleBpath;
+ bpath_good[0].code = NR_MOVETO;
+ // Unrecognized character
+ path_str = "M 1,2 4,2 4,8 1,8 z&m 13,15";
+ bpath = sp_svg_read_path(path_str);
+ TS_ASSERT(bpathEqual(bpath,bpath_good));
+ g_free(bpath);
+ // Unrecognized character
+ path_str = "M 1,2 4,2 4,8 1,8 z m &13,15";
+ bpath = sp_svg_read_path(path_str);
+ TS_ASSERT(bpathEqual(bpath,bpath_good));
+ g_free(bpath);
+ }
+
+ void testReadErrorTypo() {
+ char const * path_str;
+ NArtBpath * bpath;
+ NArtBpath * bpath_good = rectangleBpath;
+ bpath_good[0].code = NR_MOVETO;
+ // Typo
+ path_str = "M 1,2 4,2 4,8 1,8 z j 13,15";
+ bpath = sp_svg_read_path(path_str);
+ TS_ASSERT(bpathEqual(bpath,bpath_good));
+ g_free(bpath);
+
+ bpath_good[0].code = NR_MOVETO_OPEN;
+ // Typo
+ path_str = "M 1,2 4,2 4,8 1,8 L 1,2 x m 13,15";
+ bpath = sp_svg_read_path(path_str);
+ TS_ASSERT(bpathEqual(bpath,bpath_good));
+ g_free(bpath);
+ }
+
+ void testReadErrorIllformedNumbers() {
+ char const * path_str;
+ NArtBpath * bpath;
+ NArtBpath * bpath_good = rectangleBpath;
+ bpath_good[0].code = NR_MOVETO;
+ // Double exponent
+ path_str = "M 1,2 4,2 4,8 1,8 z m 13e4e5,15";
+ bpath = sp_svg_read_path(path_str);
+ TS_ASSERT(bpathEqual(bpath,bpath_good));
+ g_free(bpath);
+ // Double sign
+ path_str = "M 1,2 4,2 4,8 1,8 z m +-13,15";
+ bpath = sp_svg_read_path(path_str);
+ TS_ASSERT(bpathEqual(bpath,bpath_good));
+ g_free(bpath);
+ // Double sign
+ path_str = "M 1,2 4,2 4,8 1,8 z m 13e+-12,15";
+ bpath = sp_svg_read_path(path_str);
+ TS_ASSERT(bpathEqual(bpath,bpath_good));
+ g_free(bpath);
+ // No digit
+ path_str = "M 1,2 4,2 4,8 1,8 z m .e12,15";
+ bpath = sp_svg_read_path(path_str);
+ TS_ASSERT(bpathEqual(bpath,bpath_good));
+ g_free(bpath);
+ // No digit
+ path_str = "M 1,2 4,2 4,8 1,8 z m .,15";
+ bpath = sp_svg_read_path(path_str);
+ TS_ASSERT(bpathEqual(bpath,bpath_good));
+ g_free(bpath);
+ // No digit
+ path_str = "M 1,2 4,2 4,8 1,8 z m +,15";
+ bpath = sp_svg_read_path(path_str);
+ TS_ASSERT(bpathEqual(bpath,bpath_good));
+ g_free(bpath);
+ // No digit
+ path_str = "M 1,2 4,2 4,8 1,8 z m +.e+,15";
+ bpath = sp_svg_read_path(path_str);
+ TS_ASSERT(bpathEqual(bpath,bpath_good));
+ g_free(bpath);
+ }
+
+ void testReadErrorJunk() {
+ char const * path_str;
+ NArtBpath * bpath;
+ NArtBpath * bpath_good = rectangleBpath;
+ bpath_good[0].code = NR_MOVETO;
+ // Junk
+ path_str = "M 1,2 4,2 4,8 1,8 z j 357 hkjh.,34e34 90ih6kj4 h5k6vlh4N.,6,45wikuyi3yere..3487 m 13,23";
+ bpath = sp_svg_read_path(path_str);
+ TS_ASSERT(bpathEqual(bpath,bpath_good));
+ g_free(bpath);
+ }
+
+ void testReadErrorStopReading() {
+ char const * path_str;
+ NArtBpath * bpath;
+ NArtBpath * bpath_good = rectangleBpath;
+ bpath_good[0].code = NR_MOVETO;
+ // Unrecognized parameter
+ path_str = "M 1,2 4,2 4,8 1,8 z m #$%,23,34";
+ bpath = sp_svg_read_path(path_str);
+ TS_ASSERT(bpathEqual(bpath,bpath_good));
+ g_free(bpath);
+ // Invalid parameter
+ path_str = "M 1,2 4,2 4,8 1,8 z m #$%,23,34";
+ bpath = sp_svg_read_path(path_str);
+ TS_ASSERT(bpathEqual(bpath,bpath_good));
+ g_free(bpath);
+ // Illformed parameter
+ path_str = "M 1,2 4,2 4,8 1,8 z m +-12,23,34";
+ bpath = sp_svg_read_path(path_str);
+ TS_ASSERT(bpathEqual(bpath,bpath_good));
+ g_free(bpath);
+
+ bpath_good[0].code = NR_MOVETO_OPEN;
+ // "Third" parameter
+ path_str = "M 1,2 4,2 4,8 1,8 1,2,3 M 12,23";
+ bpath = sp_svg_read_path(path_str);
+ TS_ASSERT(bpathEqual(bpath,bpath_good));
+ g_free(bpath);
+ }
+
+ void testRoundTrip() {
+ // This is the easiest way to (also) test writing path data, as a path can be written in more than one way.
+ NArtBpath * bpath;
+ NArtBpath * new_bpath;
+ char * path_str;
+ // Rectangle (closed)
+ bpath = sp_svg_read_path(rectanglesAbsoluteClosed[0].c_str());
+ path_str = sp_svg_write_path(bpath);
+ new_bpath = sp_svg_read_path(path_str);
+ TS_ASSERT(bpathEqual(bpath,new_bpath));
+ g_free(bpath); g_free(path_str); g_free(new_bpath);
+ // Rectangle (open)
+ bpath = sp_svg_read_path(rectanglesAbsoluteOpen[0].c_str());
+ path_str = sp_svg_write_path(bpath);
+ new_bpath = sp_svg_read_path(path_str);
+ TS_ASSERT(bpathEqual(bpath,new_bpath));
+ g_free(bpath); g_free(path_str); g_free(new_bpath);
+ // Concatenated rectangles
+ bpath = sp_svg_read_path((rectanglesAbsoluteClosed[0] + rectanglesRelativeOpen[0] + rectanglesRelativeClosed[0] + rectanglesAbsoluteOpen[0]).c_str());
+ path_str = sp_svg_write_path(bpath);
+ new_bpath = sp_svg_read_path(path_str);
+ TS_ASSERT(bpathEqual(bpath,new_bpath));
+ g_free(bpath); g_free(path_str); g_free(new_bpath);
+ // Zero-length subpaths
+ bpath = sp_svg_read_path("M 0,0 M 1,1 L 2,2 M 3,3 z M 4,4 L 5,5 z M 6,6");
+ path_str = sp_svg_write_path(bpath);
+ new_bpath = sp_svg_read_path(path_str);
+ TS_ASSERT(bpathEqual(bpath,new_bpath));
+ g_free(bpath); g_free(path_str); g_free(new_bpath);
+ // Floating-point
+ bpath = sp_svg_read_path("M .01,.02 L 0.04,0.02 L.04,.08L0.01,0.08 z""M 1e-2,.2e-1 L 0.004e1,0.0002e+2 L04E-2,.08e0L1.0e-2,80e-3 z");
+ path_str = sp_svg_write_path(bpath);
+ new_bpath = sp_svg_read_path(path_str);
+ TS_ASSERT(bpathEqual(bpath, new_bpath, 1e-17));
+ g_free(bpath); g_free(path_str); g_free(new_bpath);
+ }
+
+ void testMinexpPrecision() {
+ NArtBpath * bpath;
+ char * path_str;
+ // Default values
+ prefs_set_int_attribute("options.svgoutput", "allowrelativecoordinates", 1);
+ prefs_set_int_attribute("options.svgoutput", "forcerepeatcommands", 0);
+ prefs_set_int_attribute("options.svgoutput", "numericprecision", 8);
+ prefs_set_int_attribute("options.svgoutput", "minimumexponent", -8);
+ bpath = sp_svg_read_path("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(bpath);
+ 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(bpath); g_free(path_str);
+ }
+
+private:
+ bool bpathEqual(NArtBpath const * a, NArtBpath const * b, double eps = 1e-16) {
+ while(a->code != NR_END && b->code == a->code) {
+ switch(a->code) {
+ case NR_MOVETO:
+ case NR_MOVETO_OPEN:
+ case NR_LINETO:
+ if (!Geom::are_near(a->x3,b->x3, eps) || !Geom::are_near(a->y3,b->y3, eps)) return false;
+ break;
+ case NR_CURVETO:
+ if (!Geom::are_near(a->x1,b->x1, eps) || !Geom::are_near(a->y1,b->y1, eps)) return false;
+ if (!Geom::are_near(a->x2,b->x2, eps) || !Geom::are_near(a->y2,b->y2, eps)) return false;
+ if (!Geom::are_near(a->x3,b->x3, eps) || !Geom::are_near(a->y3,b->y3, eps)) return false;
+ break;
+ default:
+ TS_FAIL("Unknown path code!");
+ }
+ a++;
+ b++;
+ }
+ return a->code == b->code;
+ }
+};
+
+
+/*
+ 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:encoding=utf-8:textwidth=99 :
diff --git a/src/svg/svg-path-test.h b/src/svg/svg-path-test.h
--- a/src/svg/svg-path-test.h
+++ /dev/null
@@ -1,493 +0,0 @@
-#include <cxxtest/TestSuite.h>
-#include "libnr/n-art-bpath.h"
-#include "svg/svg.h"
-#include "2geom/coord.h"
-#include "prefs-utils.h"
-#include "streq.h"
-#include <string>
-#include <vector>
-#include <glib/gmem.h>
-
-class SvgPathTest : public CxxTest::TestSuite
-{
-private:
- std::vector<std::string> rectanglesAbsoluteClosed;
- std::vector<std::string> rectanglesRelativeClosed;
- std::vector<std::string> rectanglesAbsoluteOpen;
- std::vector<std::string> rectanglesRelativeOpen;
- NArtBpath rectangleBpath[5+1];
-public:
- SvgPathTest() {
- // Lots of ways to define the same rectangle
- rectanglesAbsoluteClosed.push_back("M 1,2 L 4,2 L 4,8 L 1,8 L 1,2 Z");
- 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");
- rectangleBpath[0].code = NR_MOVETO;
- rectangleBpath[0].x3 = 1;
- rectangleBpath[0].y3 = 2;
- rectangleBpath[1].code = NR_LINETO;
- rectangleBpath[1].x3 = 4;
- rectangleBpath[1].y3 = 2;
- rectangleBpath[2].code = NR_LINETO;
- rectangleBpath[2].x3 = 4;
- rectangleBpath[2].y3 = 8;
- rectangleBpath[3].code = NR_LINETO;
- rectangleBpath[3].x3 = 1;
- rectangleBpath[3].y3 = 8;
- rectangleBpath[4].code = NR_LINETO;
- rectangleBpath[4].x3 = 1;
- rectangleBpath[4].y3 = 2;
- rectangleBpath[5].code = NR_END;
- // TODO: Also test some (smooth) cubic/quadratic beziers and elliptical arcs
- }
-
-// createSuite and destroySuite get us per-suite setup and teardown
-// without us having to worry about static initialization order, etc.
- static SvgPathTest *createSuite() { return new SvgPathTest(); }
- static void destroySuite( SvgPathTest *suite ) { delete suite; }
-
- void testReadRectanglesAbsoluteClosed()
- {
- rectangleBpath[0].code = NR_MOVETO;
- for(size_t i=0; i<rectanglesAbsoluteClosed.size(); i++) {
- NArtBpath * bpath = sp_svg_read_path(rectanglesAbsoluteClosed[i].c_str());
- TS_ASSERT(bpathEqual(bpath,rectangleBpath));
- g_free(bpath);
- }
- }
-
- void testReadRectanglesRelativeClosed()
- {
- rectangleBpath[0].code = NR_MOVETO;
- for(size_t i=0; i<rectanglesRelativeClosed.size(); i++) {
- NArtBpath * bpath = sp_svg_read_path(rectanglesRelativeClosed[i].c_str());
- TS_ASSERT(bpathEqual(bpath,rectangleBpath));
- g_free(bpath);
- }
- }
-
- void testReadRectanglesAbsoluteOpen()
- {
- rectangleBpath[0].code = NR_MOVETO_OPEN;
- for(size_t i=0; i<rectanglesAbsoluteOpen.size(); i++) {
- NArtBpath * bpath = sp_svg_read_path(rectanglesAbsoluteOpen[i].c_str());
- TS_ASSERT(bpathEqual(bpath,rectangleBpath));
- g_free(bpath);
- }
- }
-
- void testReadRectanglesRelativeOpen()
- {
- rectangleBpath[0].code = NR_MOVETO_OPEN;
- for(size_t i=0; i<rectanglesRelativeOpen.size(); i++) {
- NArtBpath * bpath = sp_svg_read_path(rectanglesRelativeOpen[i].c_str());
- TS_ASSERT(bpathEqual(bpath,rectangleBpath));
- g_free(bpath);
- }
- }
-
- void testReadConcatenatedPaths()
- {
- NArtBpath bpath_good[4*5+1];
- for(size_t i=0; i<4; i++) {
- memcpy(bpath_good+i*5,rectangleBpath,sizeof(rectangleBpath[0])*5);
- }
- bpath_good[0*5].code = NR_MOVETO;
- bpath_good[1*5].code = NR_MOVETO_OPEN;
- bpath_good[2*5].code = NR_MOVETO;
- bpath_good[3*5].code = NR_MOVETO_OPEN;
- bpath_good[4*5].code = NR_END;
- for(size_t i=0; i<5; i++) {
- bpath_good[1*5+i].x3 += bpath_good[0*5+4].x3;
- bpath_good[1*5+i].y3 += bpath_good[0*5+4].y3;
- }
- for(size_t i=0; i<5; i++) {
- bpath_good[2*5+i].x3 += bpath_good[1*5+4].x3;
- bpath_good[2*5+i].y3 += bpath_good[1*5+4].y3;
- }
- std::string path_str = rectanglesAbsoluteClosed[0] + rectanglesRelativeOpen[0] + rectanglesRelativeClosed[0] + rectanglesAbsoluteOpen[0];
- NArtBpath * bpath = sp_svg_read_path(path_str.c_str());
- TS_ASSERT(bpathEqual(bpath,bpath_good));
- g_free(bpath);
- }
-
- void testReadZeroLengthSubpaths() {
- // Per the SVG 1.1 specification (section F5) zero-length subpaths are relevant
- NArtBpath bpath_good[8+1];
- bpath_good[0].code = NR_MOVETO_OPEN;
- bpath_good[0].x3 = bpath_good[0].y3 = 0;
- bpath_good[1].code = NR_MOVETO_OPEN;
- bpath_good[1].x3 = bpath_good[1].y3 = 1;
- bpath_good[2].code = NR_LINETO;
- bpath_good[2].x3 = bpath_good[2].y3 = 2;
- bpath_good[3].code = NR_MOVETO;
- bpath_good[3].x3 = bpath_good[3].y3 = 3;
- bpath_good[4].code = NR_MOVETO;
- bpath_good[4].x3 = bpath_good[4].y3 = 4;
- bpath_good[5].code = NR_LINETO;
- bpath_good[5].x3 = bpath_good[5].y3 = 5;
- bpath_good[6].code = NR_LINETO;
- bpath_good[6].x3 = bpath_good[6].y3 = 4;
- bpath_good[7].code = NR_MOVETO_OPEN;
- bpath_good[7].x3 = bpath_good[7].y3 = 6;
- bpath_good[8].code = NR_END;
- { // Test absolute version
- char const * path_str = "M 0,0 M 1,1 L 2,2 M 3,3 z M 4,4 L 5,5 z M 6,6";
- NArtBpath * bpath = sp_svg_read_path(path_str);
- TS_ASSERT(bpathEqual(bpath,bpath_good));
- g_free(bpath);
- }
- { // Test relative version
- char const * path_str = "m 0,0 m 1,1 l 1,1 m 1,1 z m 1,1 l 1,1 z m 2,2";
- NArtBpath * bpath = sp_svg_read_path(path_str);
- TS_ASSERT(bpathEqual(bpath,bpath_good));
- g_free(bpath);
- }
- }
-
- void testReadImplicitMoveto() {
- NArtBpath bpath_good[6+1];
- bpath_good[0].code = NR_MOVETO;
- bpath_good[0].x3 = bpath_good[0].y3 = 1;
- bpath_good[1].code = NR_LINETO;
- bpath_good[1].x3 = bpath_good[1].y3 = 2;
- bpath_good[2].code = NR_LINETO;
- bpath_good[2].x3 = bpath_good[2].y3 = 1;
- bpath_good[3].code = NR_MOVETO;
- bpath_good[3].x3 = bpath_good[3].y3 = 1;
- bpath_good[4].code = NR_LINETO;
- bpath_good[4].x3 = bpath_good[4].y3 = 3;
- bpath_good[5].code = NR_LINETO;
- bpath_good[5].x3 = bpath_good[5].y3 = 1;
- bpath_good[6].code = NR_END;
- { // Test absolute version
- char const * path_str = "M 1,1 L 2,2 z L 3,3 z";
- NArtBpath * bpath = sp_svg_read_path(path_str);
- TS_ASSERT(bpathEqual(bpath,bpath_good));
- g_free(bpath);
- }
- { // Test relative version
- char const * path_str = "M 1,1 L 2,2 z L 3,3 z";
- NArtBpath * bpath = sp_svg_read_path(path_str);
- TS_ASSERT(bpathEqual(bpath,bpath_good));
- g_free(bpath);
- }
- }
-
- void testReadFloatingPoint() {
- NArtBpath bpath_good[5+1];
- bpath_good[0].code = NR_MOVETO;
- bpath_good[0].x3 = .01;
- bpath_good[0].y3 = .02;
- bpath_good[1].code = NR_LINETO;
- bpath_good[1].x3 = .04;
- bpath_good[1].y3 = .02;
- bpath_good[2].code = NR_LINETO;
- bpath_good[2].x3 = 1.5;
- bpath_good[2].y3 = 1.6;
- bpath_good[3].code = NR_LINETO;
- bpath_good[3].x3 = .01;
- bpath_good[3].y3 = .08;
- bpath_good[4].code = NR_LINETO;
- bpath_good[4].x3 = .01;
- bpath_good[4].y3 = .02;
- bpath_good[5].code = NR_END;
- { // Test decimals
- char const * path_str = "M .01,.02 L.04.02 L1.5,1.6L0.01,0.08 .01.02 z";
- NArtBpath * bpath = sp_svg_read_path(path_str);
- TS_ASSERT(bpathEqual(bpath,bpath_good));
- g_free(bpath);
- }
- { // Test exponent
- char const * path_str = "M 1e-2,.2e-1 L 0.004e1,0.0002e+2 L0150E-2,1.6e0L1.0e-2,80e-3 z";
- NArtBpath * bpath = sp_svg_read_path(path_str);
- TS_ASSERT(bpathEqual(bpath,bpath_good));
- g_free(bpath);
- }
- }
-
- void testReadImplicitSeparation() {
- // Coordinates need not be separated by whitespace if they can still be read unambiguously
- NArtBpath bpath_good[5+1];
- bpath_good[0].code = NR_MOVETO;
- bpath_good[0].x3 = .1;
- bpath_good[0].y3 = .2;
- bpath_good[1].code = NR_LINETO;
- bpath_good[1].x3 = .4;
- bpath_good[1].y3 = .2;
- bpath_good[2].code = NR_LINETO;
- bpath_good[2].x3 = .4;
- bpath_good[2].y3 = .8;
- bpath_good[3].code = NR_LINETO;
- bpath_good[3].x3 = .1;
- bpath_good[3].y3 = .8;
- bpath_good[4].code = NR_LINETO;
- bpath_good[4].x3 = .1;
- bpath_good[4].y3 = .2;
- bpath_good[5].code = NR_END;
- { // Test absolute
- char const * path_str = "M .1.2+0.4.2e0.4e0+8e-1.1.8 z";
- NArtBpath * bpath = sp_svg_read_path(path_str);
- TS_ASSERT(bpathEqual(bpath,bpath_good));
- g_free(bpath);
- }
- { // Test relative
- char const * path_str = "m .1.2+0.3.0e0.0e0+6e-1-.3.0 z";
- NArtBpath * bpath = sp_svg_read_path(path_str);
- TS_ASSERT(bpathEqual(bpath,bpath_good));
- g_free(bpath);
- }
- }
-
- void testReadErrorMisplacedCharacter() {
- char const * path_str;
- NArtBpath * bpath;
- NArtBpath * bpath_good = rectangleBpath;
- bpath_good[0].code = NR_MOVETO;
- // Comma in the wrong place (commas may only appear between parameters)
- path_str = "M 1,2 4,2 4,8 1,8 z , m 13,15";
- bpath = sp_svg_read_path(path_str);
- TS_ASSERT(bpathEqual(bpath,bpath_good));
- g_free(bpath);
- // Comma in the wrong place (commas may only appear between parameters)
- path_str = "M 1,2 4,2 4,8 1,8 z m,13,15";
- bpath = sp_svg_read_path(path_str);
- TS_ASSERT(bpathEqual(bpath,bpath_good));
- g_free(bpath);
- // Period in the wrong place (no numbers after a 'z')
- path_str = "M 1,2 4,2 4,8 1,8 z . m 13,15";
- bpath = sp_svg_read_path(path_str);
- TS_ASSERT(bpathEqual(bpath,bpath_good));
- g_free(bpath);
- // Sign in the wrong place (no numbers after a 'z')
- path_str = "M 1,2 4,2 4,8 1,8 z + - m 13,15";
- bpath = sp_svg_read_path(path_str);
- TS_ASSERT(bpathEqual(bpath,bpath_good));
- g_free(bpath);
- // Digit in the wrong place (no numbers after a 'z')
- path_str = "M 1,2 4,2 4,8 1,8 z 9809 m 13,15";
- bpath = sp_svg_read_path(path_str);
- TS_ASSERT(bpathEqual(bpath,bpath_good));
- g_free(bpath);
- // Digit in the wrong place (no numbers after a 'z')
- path_str = "M 1,2 4,2 4,8 1,8 z 9809 876 m 13,15";
- bpath = sp_svg_read_path(path_str);
- TS_ASSERT(bpathEqual(bpath,bpath_good));
- g_free(bpath);
- }
-
- void testReadErrorUnrecognizedCharacter() {
- char const * path_str;
- NArtBpath * bpath;
- NArtBpath * bpath_good = rectangleBpath;
- bpath_good[0].code = NR_MOVETO;
- // Unrecognized character
- path_str = "M 1,2 4,2 4,8 1,8 z&m 13,15";
- bpath = sp_svg_read_path(path_str);
- TS_ASSERT(bpathEqual(bpath,bpath_good));
- g_free(bpath);
- // Unrecognized character
- path_str = "M 1,2 4,2 4,8 1,8 z m &13,15";
- bpath = sp_svg_read_path(path_str);
- TS_ASSERT(bpathEqual(bpath,bpath_good));
- g_free(bpath);
- }
-
- void testReadErrorTypo() {
- char const * path_str;
- NArtBpath * bpath;
- NArtBpath * bpath_good = rectangleBpath;
- bpath_good[0].code = NR_MOVETO;
- // Typo
- path_str = "M 1,2 4,2 4,8 1,8 z j 13,15";
- bpath = sp_svg_read_path(path_str);
- TS_ASSERT(bpathEqual(bpath,bpath_good));
- g_free(bpath);
-
- bpath_good[0].code = NR_MOVETO_OPEN;
- // Typo
- path_str = "M 1,2 4,2 4,8 1,8 L 1,2 x m 13,15";
- bpath = sp_svg_read_path(path_str);
- TS_ASSERT(bpathEqual(bpath,bpath_good));
- g_free(bpath);
- }
-
- void testReadErrorIllformedNumbers() {
- char const * path_str;
- NArtBpath * bpath;
- NArtBpath * bpath_good = rectangleBpath;
- bpath_good[0].code = NR_MOVETO;
- // Double exponent
- path_str = "M 1,2 4,2 4,8 1,8 z m 13e4e5,15";
- bpath = sp_svg_read_path(path_str);
- TS_ASSERT(bpathEqual(bpath,bpath_good));
- g_free(bpath);
- // Double sign
- path_str = "M 1,2 4,2 4,8 1,8 z m +-13,15";
- bpath = sp_svg_read_path(path_str);
- TS_ASSERT(bpathEqual(bpath,bpath_good));
- g_free(bpath);
- // Double sign
- path_str = "M 1,2 4,2 4,8 1,8 z m 13e+-12,15";
- bpath = sp_svg_read_path(path_str);
- TS_ASSERT(bpathEqual(bpath,bpath_good));
- g_free(bpath);
- // No digit
- path_str = "M 1,2 4,2 4,8 1,8 z m .e12,15";
- bpath = sp_svg_read_path(path_str);
- TS_ASSERT(bpathEqual(bpath,bpath_good));
- g_free(bpath);
- // No digit
- path_str = "M 1,2 4,2 4,8 1,8 z m .,15";
- bpath = sp_svg_read_path(path_str);
- TS_ASSERT(bpathEqual(bpath,bpath_good));
- g_free(bpath);
- // No digit
- path_str = "M 1,2 4,2 4,8 1,8 z m +,15";
- bpath = sp_svg_read_path(path_str);
- TS_ASSERT(bpathEqual(bpath,bpath_good));
- g_free(bpath);
- // No digit
- path_str = "M 1,2 4,2 4,8 1,8 z m +.e+,15";
- bpath = sp_svg_read_path(path_str);
- TS_ASSERT(bpathEqual(bpath,bpath_good));
- g_free(bpath);
- }
-
- void testReadErrorJunk() {
- char const * path_str;
- NArtBpath * bpath;
- NArtBpath * bpath_good = rectangleBpath;
- bpath_good[0].code = NR_MOVETO;
- // Junk
- path_str = "M 1,2 4,2 4,8 1,8 z j 357 hkjh.,34e34 90ih6kj4 h5k6vlh4N.,6,45wikuyi3yere..3487 m 13,23";
- bpath = sp_svg_read_path(path_str);
- TS_ASSERT(bpathEqual(bpath,bpath_good));
- g_free(bpath);
- }
-
- void testReadErrorStopReading() {
- char const * path_str;
- NArtBpath * bpath;
- NArtBpath * bpath_good = rectangleBpath;
- bpath_good[0].code = NR_MOVETO;
- // Unrecognized parameter
- path_str = "M 1,2 4,2 4,8 1,8 z m #$%,23,34";
- bpath = sp_svg_read_path(path_str);
- TS_ASSERT(bpathEqual(bpath,bpath_good));
- g_free(bpath);
- // Invalid parameter
- path_str = "M 1,2 4,2 4,8 1,8 z m #$%,23,34";
- bpath = sp_svg_read_path(path_str);
- TS_ASSERT(bpathEqual(bpath,bpath_good));
- g_free(bpath);
- // Illformed parameter
- path_str = "M 1,2 4,2 4,8 1,8 z m +-12,23,34";
- bpath = sp_svg_read_path(path_str);
- TS_ASSERT(bpathEqual(bpath,bpath_good));
- g_free(bpath);
-
- bpath_good[0].code = NR_MOVETO_OPEN;
- // "Third" parameter
- path_str = "M 1,2 4,2 4,8 1,8 1,2,3 M 12,23";
- bpath = sp_svg_read_path(path_str);
- TS_ASSERT(bpathEqual(bpath,bpath_good));
- g_free(bpath);
- }
-
- void testRoundTrip() {
- // This is the easiest way to (also) test writing path data, as a path can be written in more than one way.
- NArtBpath * bpath;
- NArtBpath * new_bpath;
- char * path_str;
- // Rectangle (closed)
- bpath = sp_svg_read_path(rectanglesAbsoluteClosed[0].c_str());
- path_str = sp_svg_write_path(bpath);
- new_bpath = sp_svg_read_path(path_str);
- TS_ASSERT(bpathEqual(bpath,new_bpath));
- g_free(bpath); g_free(path_str); g_free(new_bpath);
- // Rectangle (open)
- bpath = sp_svg_read_path(rectanglesAbsoluteOpen[0].c_str());
- path_str = sp_svg_write_path(bpath);
- new_bpath = sp_svg_read_path(path_str);
- TS_ASSERT(bpathEqual(bpath,new_bpath));
- g_free(bpath); g_free(path_str); g_free(new_bpath);
- // Concatenated rectangles
- bpath = sp_svg_read_path((rectanglesAbsoluteClosed[0] + rectanglesRelativeOpen[0] + rectanglesRelativeClosed[0] + rectanglesAbsoluteOpen[0]).c_str());
- path_str = sp_svg_write_path(bpath);
- new_bpath = sp_svg_read_path(path_str);
- TS_ASSERT(bpathEqual(bpath,new_bpath));
- g_free(bpath); g_free(path_str); g_free(new_bpath);
- // Zero-length subpaths
- bpath = sp_svg_read_path("M 0,0 M 1,1 L 2,2 M 3,3 z M 4,4 L 5,5 z M 6,6");
- path_str = sp_svg_write_path(bpath);
- new_bpath = sp_svg_read_path(path_str);
- TS_ASSERT(bpathEqual(bpath,new_bpath));
- g_free(bpath); g_free(path_str); g_free(new_bpath);
- // Floating-point
- bpath = sp_svg_read_path("M .01,.02 L 0.04,0.02 L.04,.08L0.01,0.08 z""M 1e-2,.2e-1 L 0.004e1,0.0002e+2 L04E-2,.08e0L1.0e-2,80e-3 z");
- path_str = sp_svg_write_path(bpath);
- new_bpath = sp_svg_read_path(path_str);
- TS_ASSERT(bpathEqual(bpath, new_bpath, 1e-17));
- g_free(bpath); g_free(path_str); g_free(new_bpath);
- }
-
- void testMinexpPrecision() {
- NArtBpath * bpath;
- char * path_str;
- // Default values
- prefs_set_int_attribute("options.svgoutput", "allowrelativecoordinates", 1);
- prefs_set_int_attribute("options.svgoutput", "forcerepeatcommands", 0);
- prefs_set_int_attribute("options.svgoutput", "numericprecision", 8);
- prefs_set_int_attribute("options.svgoutput", "minimumexponent", -8);
- bpath = sp_svg_read_path("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(bpath);
- 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(bpath); g_free(path_str);
- }
-
-private:
- bool bpathEqual(NArtBpath const * a, NArtBpath const * b, double eps = 1e-16) {
- while(a->code != NR_END && b->code == a->code) {
- switch(a->code) {
- case NR_MOVETO:
- case NR_MOVETO_OPEN:
- case NR_LINETO:
- if (!Geom::are_near(a->x3,b->x3, eps) || !Geom::are_near(a->y3,b->y3, eps)) return false;
- break;
- case NR_CURVETO:
- if (!Geom::are_near(a->x1,b->x1, eps) || !Geom::are_near(a->y1,b->y1, eps)) return false;
- if (!Geom::are_near(a->x2,b->x2, eps) || !Geom::are_near(a->y2,b->y2, eps)) return false;
- if (!Geom::are_near(a->x3,b->x3, eps) || !Geom::are_near(a->y3,b->y3, eps)) return false;
- break;
- default:
- TS_FAIL("Unknown path code!");
- }
- a++;
- b++;
- }
- return a->code == b->code;
- }
-};
-
-
-/*
- 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:encoding=utf-8:textwidth=99 :