summaryrefslogtreecommitdiffstats
path: root/src/svg
diff options
context:
space:
mode:
authorJon A. Cruz <jon@joncruz.org>2008-07-05 05:11:28 +0000
committerjoncruz <joncruz@users.sourceforge.net>2008-07-05 05:11:28 +0000
commitb4f137620ee8de6501e07ecbc1a8a6214662b6e8 (patch)
tree2395c5abffb6b983fffb6029cfeaa5ce50f0745b /src/svg
parentWarning cleanup (diff)
downloadinkscape-b4f137620ee8de6501e07ecbc1a8a6214662b6e8.tar.gz
inkscape-b4f137620ee8de6501e07ecbc1a8a6214662b6e8.zip
EOL fixups
(bzr r6164)
Diffstat (limited to 'src/svg')
-rw-r--r--src/svg/svg-affine-test.h438
-rw-r--r--src/svg/svg-length-test.h92
-rw-r--r--src/svg/svg-path-test.h944
3 files changed, 737 insertions, 737 deletions
diff --git a/src/svg/svg-affine-test.h b/src/svg/svg-affine-test.h
index 4180fcf3d..658968bce 100644
--- a/src/svg/svg-affine-test.h
+++ b/src/svg/svg-affine-test.h
@@ -1,219 +1,219 @@
-#include <cxxtest/TestSuite.h>
-
-#include "svg/svg.h"
-#include <2geom/matrix.h>
-#include <algorithm>
-#include <glib.h>
-#include <iostream>
-#include <math.h>
-#include <utility>
-
-struct streq_free2 {
- bool operator()(char const *exp, char const *got) const
- {
- bool const ret = (strcmp(exp, got) == 0);
- g_free((void*)got);
- return ret;
- }
-};
-
-struct approx_equal {
- bool operator()(Geom::Matrix const &ref, Geom::Matrix const &cm) const
- {
- double maxabsdiff = 0;
- for(size_t i=0; i<6; i++) {
- maxabsdiff = std::max(std::abs(ref[i]-cm[i]), maxabsdiff);
- }
- return maxabsdiff < 1e-14;
- }
-};
-
-class SvgAffineTest : public CxxTest::TestSuite
-{
-private:
- struct test_t {
- char const * str;
- Geom::Matrix matrix;
- };
- static test_t const read_matrix_tests[3];
- static test_t const read_translate_tests[3];
- static test_t const read_scale_tests[3];
- static test_t const read_rotate_tests[4];
- static test_t const read_skew_tests[3];
- static test_t const write_matrix_tests[2];
- static test_t const write_translate_tests[3];
- static test_t const write_scale_tests[2];
- static test_t const write_rotate_tests[2];
- static test_t const write_skew_tests[3];
-public:
- SvgAffineTest() {
- }
-
- void testReadIdentity()
- {
- char const* strs[] = {
- //0,
- "",
- "matrix(1,0,0,1,0,0)",
- "translate(0,0)",
- "scale(1,1)",
- "rotate(0,0,0)",
- "skewX(0)",
- "skewY(0)"};
- size_t n = G_N_ELEMENTS(strs);
- for(size_t i=0; i<n; i++) {
- Geom::Matrix cm;
- TSM_ASSERT(strs[i] , sp_svg_transform_read(strs[i], &cm));
- TSM_ASSERT_EQUALS(strs[i] , Geom::identity() , cm);
- }
- }
-
- void testWriteIdentity()
- {
- TS_ASSERT_EQUALS(sp_svg_transform_write(Geom::identity()) , (void*)0)
- }
-
- void testReadMatrix()
- {
- for(size_t i=0; i<G_N_ELEMENTS(read_matrix_tests); i++) {
- Geom::Matrix cm;
- TSM_ASSERT(read_matrix_tests[i].str , sp_svg_transform_read(read_matrix_tests[i].str, &cm));
- TSM_ASSERT_RELATION(read_matrix_tests[i].str , approx_equal , read_matrix_tests[i].matrix , cm);
- }
- }
-
- void testReadTranslate()
- {
- for(size_t i=0; i<G_N_ELEMENTS(read_translate_tests); i++) {
- Geom::Matrix cm;
- TSM_ASSERT(read_translate_tests[i].str , sp_svg_transform_read(read_translate_tests[i].str, &cm));
- TSM_ASSERT_RELATION(read_translate_tests[i].str , approx_equal , read_translate_tests[i].matrix , cm);
- }
- }
-
- void testReadScale()
- {
- for(size_t i=0; i<G_N_ELEMENTS(read_scale_tests); i++) {
- Geom::Matrix cm;
- TSM_ASSERT(read_scale_tests[i].str , sp_svg_transform_read(read_scale_tests[i].str, &cm));
- TSM_ASSERT_RELATION(read_scale_tests[i].str , approx_equal , read_scale_tests[i].matrix , cm);
- }
- }
-
- void testReadRotate()
- {
- for(size_t i=0; i<G_N_ELEMENTS(read_rotate_tests); i++) {
- Geom::Matrix cm;
- TSM_ASSERT(read_rotate_tests[i].str , sp_svg_transform_read(read_rotate_tests[i].str, &cm));
- TSM_ASSERT_RELATION(read_rotate_tests[i].str , approx_equal , read_rotate_tests[i].matrix , cm);
- }
- }
-
- void testReadSkew()
- {
- for(size_t i=0; i<G_N_ELEMENTS(read_skew_tests); i++) {
- Geom::Matrix cm;
- TSM_ASSERT(read_skew_tests[i].str , sp_svg_transform_read(read_skew_tests[i].str, &cm));
- TSM_ASSERT_RELATION(read_skew_tests[i].str , approx_equal , read_skew_tests[i].matrix , cm);
- }
- }
-
- void testWriteMatrix()
- {
- for(size_t i=0; i<G_N_ELEMENTS(write_matrix_tests); i++) {
- TS_ASSERT_RELATION(streq_free2 , sp_svg_transform_write(write_matrix_tests[i].matrix) , write_matrix_tests[i].str);
- }
- }
-
- void testWriteTranslate()
- {
- for(size_t i=0; i<G_N_ELEMENTS(write_translate_tests); i++) {
- TS_ASSERT_RELATION(streq_free2 , sp_svg_transform_write(write_translate_tests[i].matrix) , write_translate_tests[i].str);
- }
- }
-
- void testWriteScale()
- {
- for(size_t i=0; i<G_N_ELEMENTS(write_scale_tests); i++) {
- TS_ASSERT_RELATION(streq_free2 , sp_svg_transform_write(write_scale_tests[i].matrix) , write_scale_tests[i].str);
- }
- }
-
- void testWriteRotate()
- {
- for(size_t i=0; i<G_N_ELEMENTS(write_rotate_tests); i++) {
- TS_ASSERT_RELATION(streq_free2 , sp_svg_transform_write(write_rotate_tests[i].matrix) , write_rotate_tests[i].str);
- }
- }
-
- void testWriteSkew()
- {
- for(size_t i=0; i<G_N_ELEMENTS(write_skew_tests); i++) {
- TS_ASSERT_RELATION(streq_free2 , sp_svg_transform_write(write_skew_tests[i].matrix) , write_skew_tests[i].str);
- }
- }
-
- void testReadConcatenation()
- {
- char const * str = "skewY(17)skewX(9)translate(7,13)scale(2)rotate(13)translate(3,5)";
- Geom::Matrix ref(2.0199976232558053, 1.0674773585906016, -0.14125199392774669, 1.9055550612095459, 14.412730624347654, 28.499820929377454); // Precomputed using Mathematica
- Geom::Matrix cm;
- TS_ASSERT(sp_svg_transform_read(str, &cm));
- TS_ASSERT_RELATION(approx_equal , ref , cm);
- }
-
- // TODO: Perhaps check faulty transforms (like "translate(1,2,3)", or "matrix(1,2,3,4,5)", or ...)
-};
-
-static double const DEGREE = M_PI/180.;
-
-SvgAffineTest::test_t const SvgAffineTest::read_matrix_tests[3] = {
- {"matrix(0,0,0,0,0,0)",Geom::Matrix(0,0,0,0,0,0)},
- {"matrix(1,2,3,4,5,6)",Geom::Matrix(1,2,3,4,5,6)},
- {"matrix(1 2 -3,-4,5e6,-6e-7)",Geom::Matrix(1,2,-3,-4,5e6,-6e-7)}};
-SvgAffineTest::test_t const SvgAffineTest::read_translate_tests[3] = {
- {"translate(1)",Geom::Matrix(1,0,0,1,1,0)},
- {"translate(1,1)",Geom::Matrix(1,0,0,1,1,1)},
- {"translate(-1e3 .123e2)",Geom::Matrix(1,0,0,1,-1e3,.123e2)}};
-SvgAffineTest::test_t const SvgAffineTest::read_scale_tests[3] = {
- {"scale(2)",Geom::Matrix(2,0,0,2,0,0)},
- {"scale(2,3)",Geom::Matrix(2,0,0,3,0,0)},
- {"scale(0.1e-2 -.475e0)",Geom::Matrix(0.1e-2,0,0,-.475e0,0,0)}};
-SvgAffineTest::test_t const SvgAffineTest::read_rotate_tests[4] = {
- {"rotate(13 )",Geom::Matrix(cos(13.*DEGREE),sin(13.*DEGREE),-sin(13.*DEGREE),cos(13.*DEGREE),0,0)},
- {"rotate(-13)",Geom::Matrix(cos(-13.*DEGREE),sin(-13.*DEGREE),-sin(-13.*DEGREE),cos(-13.*DEGREE),0,0)},
- {"rotate(373)",Geom::Matrix(cos(13.*DEGREE),sin(13.*DEGREE),-sin(13.*DEGREE),cos(13.*DEGREE),0,0)},
- {"rotate(13,7,11)",Geom::Matrix(cos(13.*DEGREE),sin(13.*DEGREE),-sin(13.*DEGREE),cos(13.*DEGREE),(1-cos(13.*DEGREE))*7+sin(13.*DEGREE)*11,(1-cos(13.*DEGREE))*11-sin(13.*DEGREE)*7)}};
-SvgAffineTest::test_t const SvgAffineTest::read_skew_tests[3] = {
- {"skewX( 30)",Geom::Matrix(1,0,tan(30.*DEGREE),1,0,0)},
- {"skewX(-30)",Geom::Matrix(1,0,tan(-30.*DEGREE),1,0,0)},
- {"skewY(390)",Geom::Matrix(1,tan(30.*DEGREE),0,1,0,0)}};
-
-SvgAffineTest::test_t const SvgAffineTest::write_matrix_tests[2] = {
- {"matrix(1,2,3,4,5,6)",Geom::Matrix(1,2,3,4,5,6)},
- {"matrix(-1,2123,3,0.4,1e-8,1e20)",Geom::Matrix(-1,2.123e3,3+1e-14,0.4,1e-8,1e20)}};
-SvgAffineTest::test_t const SvgAffineTest::write_translate_tests[3] = {
- {"translate(1,1)",Geom::Matrix(1,0,0,1,1,1)},
- {"translate(1)",Geom::Matrix(1,0,0,1,1,0)},
- {"translate(-1345,0.123)",Geom::Matrix(1,0,0,1,-1.345e3,.123)}};
-SvgAffineTest::test_t const SvgAffineTest::write_scale_tests[2] = {
- {"scale(0)",Geom::Matrix(0,0,0,0,0,0)},
- {"scale(2,3)",Geom::Matrix(2,0,0,3,0,0)}};
-SvgAffineTest::test_t const SvgAffineTest::write_rotate_tests[2] = {
- {"rotate(13)",Geom::Matrix(cos(13.*DEGREE),sin(13.*DEGREE),-sin(13.*DEGREE),cos(13.*DEGREE),0,0)},
- {"rotate(-13,7,11)",Geom::Matrix(cos(-13.*DEGREE),sin(-13.*DEGREE),-sin(-13.*DEGREE),cos(-13.*DEGREE),(1-cos(-13.*DEGREE))*7+sin(-13.*DEGREE)*11,(1-cos(-13.*DEGREE))*11-sin(-13.*DEGREE)*7)}};
-SvgAffineTest::test_t const SvgAffineTest::write_skew_tests[3] = {
- {"skewX(30)",Geom::Matrix(1,0,tan(30.*DEGREE),1,0,0)},
- {"skewX(-30)",Geom::Matrix(1,0,tan(-30.*DEGREE),1,0,0)},
- {"skewY(390)",Geom::Matrix(1,tan(30.*DEGREE),0,1,0,0)}};
-
-/*
- 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 :
+#include <cxxtest/TestSuite.h>
+
+#include "svg/svg.h"
+#include <2geom/matrix.h>
+#include <algorithm>
+#include <glib.h>
+#include <iostream>
+#include <math.h>
+#include <utility>
+
+struct streq_free2 {
+ bool operator()(char const *exp, char const *got) const
+ {
+ bool const ret = (strcmp(exp, got) == 0);
+ g_free((void*)got);
+ return ret;
+ }
+};
+
+struct approx_equal {
+ bool operator()(Geom::Matrix const &ref, Geom::Matrix const &cm) const
+ {
+ double maxabsdiff = 0;
+ for(size_t i=0; i<6; i++) {
+ maxabsdiff = std::max(std::abs(ref[i]-cm[i]), maxabsdiff);
+ }
+ return maxabsdiff < 1e-14;
+ }
+};
+
+class SvgAffineTest : public CxxTest::TestSuite
+{
+private:
+ struct test_t {
+ char const * str;
+ Geom::Matrix matrix;
+ };
+ static test_t const read_matrix_tests[3];
+ static test_t const read_translate_tests[3];
+ static test_t const read_scale_tests[3];
+ static test_t const read_rotate_tests[4];
+ static test_t const read_skew_tests[3];
+ static test_t const write_matrix_tests[2];
+ static test_t const write_translate_tests[3];
+ static test_t const write_scale_tests[2];
+ static test_t const write_rotate_tests[2];
+ static test_t const write_skew_tests[3];
+public:
+ SvgAffineTest() {
+ }
+
+ void testReadIdentity()
+ {
+ char const* strs[] = {
+ //0,
+ "",
+ "matrix(1,0,0,1,0,0)",
+ "translate(0,0)",
+ "scale(1,1)",
+ "rotate(0,0,0)",
+ "skewX(0)",
+ "skewY(0)"};
+ size_t n = G_N_ELEMENTS(strs);
+ for(size_t i=0; i<n; i++) {
+ Geom::Matrix cm;
+ TSM_ASSERT(strs[i] , sp_svg_transform_read(strs[i], &cm));
+ TSM_ASSERT_EQUALS(strs[i] , Geom::identity() , cm);
+ }
+ }
+
+ void testWriteIdentity()
+ {
+ TS_ASSERT_EQUALS(sp_svg_transform_write(Geom::identity()) , (void*)0)
+ }
+
+ void testReadMatrix()
+ {
+ for(size_t i=0; i<G_N_ELEMENTS(read_matrix_tests); i++) {
+ Geom::Matrix cm;
+ TSM_ASSERT(read_matrix_tests[i].str , sp_svg_transform_read(read_matrix_tests[i].str, &cm));
+ TSM_ASSERT_RELATION(read_matrix_tests[i].str , approx_equal , read_matrix_tests[i].matrix , cm);
+ }
+ }
+
+ void testReadTranslate()
+ {
+ for(size_t i=0; i<G_N_ELEMENTS(read_translate_tests); i++) {
+ Geom::Matrix cm;
+ TSM_ASSERT(read_translate_tests[i].str , sp_svg_transform_read(read_translate_tests[i].str, &cm));
+ TSM_ASSERT_RELATION(read_translate_tests[i].str , approx_equal , read_translate_tests[i].matrix , cm);
+ }
+ }
+
+ void testReadScale()
+ {
+ for(size_t i=0; i<G_N_ELEMENTS(read_scale_tests); i++) {
+ Geom::Matrix cm;
+ TSM_ASSERT(read_scale_tests[i].str , sp_svg_transform_read(read_scale_tests[i].str, &cm));
+ TSM_ASSERT_RELATION(read_scale_tests[i].str , approx_equal , read_scale_tests[i].matrix , cm);
+ }
+ }
+
+ void testReadRotate()
+ {
+ for(size_t i=0; i<G_N_ELEMENTS(read_rotate_tests); i++) {
+ Geom::Matrix cm;
+ TSM_ASSERT(read_rotate_tests[i].str , sp_svg_transform_read(read_rotate_tests[i].str, &cm));
+ TSM_ASSERT_RELATION(read_rotate_tests[i].str , approx_equal , read_rotate_tests[i].matrix , cm);
+ }
+ }
+
+ void testReadSkew()
+ {
+ for(size_t i=0; i<G_N_ELEMENTS(read_skew_tests); i++) {
+ Geom::Matrix cm;
+ TSM_ASSERT(read_skew_tests[i].str , sp_svg_transform_read(read_skew_tests[i].str, &cm));
+ TSM_ASSERT_RELATION(read_skew_tests[i].str , approx_equal , read_skew_tests[i].matrix , cm);
+ }
+ }
+
+ void testWriteMatrix()
+ {
+ for(size_t i=0; i<G_N_ELEMENTS(write_matrix_tests); i++) {
+ TS_ASSERT_RELATION(streq_free2 , sp_svg_transform_write(write_matrix_tests[i].matrix) , write_matrix_tests[i].str);
+ }
+ }
+
+ void testWriteTranslate()
+ {
+ for(size_t i=0; i<G_N_ELEMENTS(write_translate_tests); i++) {
+ TS_ASSERT_RELATION(streq_free2 , sp_svg_transform_write(write_translate_tests[i].matrix) , write_translate_tests[i].str);
+ }
+ }
+
+ void testWriteScale()
+ {
+ for(size_t i=0; i<G_N_ELEMENTS(write_scale_tests); i++) {
+ TS_ASSERT_RELATION(streq_free2 , sp_svg_transform_write(write_scale_tests[i].matrix) , write_scale_tests[i].str);
+ }
+ }
+
+ void testWriteRotate()
+ {
+ for(size_t i=0; i<G_N_ELEMENTS(write_rotate_tests); i++) {
+ TS_ASSERT_RELATION(streq_free2 , sp_svg_transform_write(write_rotate_tests[i].matrix) , write_rotate_tests[i].str);
+ }
+ }
+
+ void testWriteSkew()
+ {
+ for(size_t i=0; i<G_N_ELEMENTS(write_skew_tests); i++) {
+ TS_ASSERT_RELATION(streq_free2 , sp_svg_transform_write(write_skew_tests[i].matrix) , write_skew_tests[i].str);
+ }
+ }
+
+ void testReadConcatenation()
+ {
+ char const * str = "skewY(17)skewX(9)translate(7,13)scale(2)rotate(13)translate(3,5)";
+ Geom::Matrix ref(2.0199976232558053, 1.0674773585906016, -0.14125199392774669, 1.9055550612095459, 14.412730624347654, 28.499820929377454); // Precomputed using Mathematica
+ Geom::Matrix cm;
+ TS_ASSERT(sp_svg_transform_read(str, &cm));
+ TS_ASSERT_RELATION(approx_equal , ref , cm);
+ }
+
+ // TODO: Perhaps check faulty transforms (like "translate(1,2,3)", or "matrix(1,2,3,4,5)", or ...)
+};
+
+static double const DEGREE = M_PI/180.;
+
+SvgAffineTest::test_t const SvgAffineTest::read_matrix_tests[3] = {
+ {"matrix(0,0,0,0,0,0)",Geom::Matrix(0,0,0,0,0,0)},
+ {"matrix(1,2,3,4,5,6)",Geom::Matrix(1,2,3,4,5,6)},
+ {"matrix(1 2 -3,-4,5e6,-6e-7)",Geom::Matrix(1,2,-3,-4,5e6,-6e-7)}};
+SvgAffineTest::test_t const SvgAffineTest::read_translate_tests[3] = {
+ {"translate(1)",Geom::Matrix(1,0,0,1,1,0)},
+ {"translate(1,1)",Geom::Matrix(1,0,0,1,1,1)},
+ {"translate(-1e3 .123e2)",Geom::Matrix(1,0,0,1,-1e3,.123e2)}};
+SvgAffineTest::test_t const SvgAffineTest::read_scale_tests[3] = {
+ {"scale(2)",Geom::Matrix(2,0,0,2,0,0)},
+ {"scale(2,3)",Geom::Matrix(2,0,0,3,0,0)},
+ {"scale(0.1e-2 -.475e0)",Geom::Matrix(0.1e-2,0,0,-.475e0,0,0)}};
+SvgAffineTest::test_t const SvgAffineTest::read_rotate_tests[4] = {
+ {"rotate(13 )",Geom::Matrix(cos(13.*DEGREE),sin(13.*DEGREE),-sin(13.*DEGREE),cos(13.*DEGREE),0,0)},
+ {"rotate(-13)",Geom::Matrix(cos(-13.*DEGREE),sin(-13.*DEGREE),-sin(-13.*DEGREE),cos(-13.*DEGREE),0,0)},
+ {"rotate(373)",Geom::Matrix(cos(13.*DEGREE),sin(13.*DEGREE),-sin(13.*DEGREE),cos(13.*DEGREE),0,0)},
+ {"rotate(13,7,11)",Geom::Matrix(cos(13.*DEGREE),sin(13.*DEGREE),-sin(13.*DEGREE),cos(13.*DEGREE),(1-cos(13.*DEGREE))*7+sin(13.*DEGREE)*11,(1-cos(13.*DEGREE))*11-sin(13.*DEGREE)*7)}};
+SvgAffineTest::test_t const SvgAffineTest::read_skew_tests[3] = {
+ {"skewX( 30)",Geom::Matrix(1,0,tan(30.*DEGREE),1,0,0)},
+ {"skewX(-30)",Geom::Matrix(1,0,tan(-30.*DEGREE),1,0,0)},
+ {"skewY(390)",Geom::Matrix(1,tan(30.*DEGREE),0,1,0,0)}};
+
+SvgAffineTest::test_t const SvgAffineTest::write_matrix_tests[2] = {
+ {"matrix(1,2,3,4,5,6)",Geom::Matrix(1,2,3,4,5,6)},
+ {"matrix(-1,2123,3,0.4,1e-8,1e20)",Geom::Matrix(-1,2.123e3,3+1e-14,0.4,1e-8,1e20)}};
+SvgAffineTest::test_t const SvgAffineTest::write_translate_tests[3] = {
+ {"translate(1,1)",Geom::Matrix(1,0,0,1,1,1)},
+ {"translate(1)",Geom::Matrix(1,0,0,1,1,0)},
+ {"translate(-1345,0.123)",Geom::Matrix(1,0,0,1,-1.345e3,.123)}};
+SvgAffineTest::test_t const SvgAffineTest::write_scale_tests[2] = {
+ {"scale(0)",Geom::Matrix(0,0,0,0,0,0)},
+ {"scale(2,3)",Geom::Matrix(2,0,0,3,0,0)}};
+SvgAffineTest::test_t const SvgAffineTest::write_rotate_tests[2] = {
+ {"rotate(13)",Geom::Matrix(cos(13.*DEGREE),sin(13.*DEGREE),-sin(13.*DEGREE),cos(13.*DEGREE),0,0)},
+ {"rotate(-13,7,11)",Geom::Matrix(cos(-13.*DEGREE),sin(-13.*DEGREE),-sin(-13.*DEGREE),cos(-13.*DEGREE),(1-cos(-13.*DEGREE))*7+sin(-13.*DEGREE)*11,(1-cos(-13.*DEGREE))*11-sin(-13.*DEGREE)*7)}};
+SvgAffineTest::test_t const SvgAffineTest::write_skew_tests[3] = {
+ {"skewX(30)",Geom::Matrix(1,0,tan(30.*DEGREE),1,0,0)},
+ {"skewX(-30)",Geom::Matrix(1,0,tan(-30.*DEGREE),1,0,0)},
+ {"skewY(390)",Geom::Matrix(1,tan(30.*DEGREE),0,1,0,0)}};
+
+/*
+ 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-length-test.h b/src/svg/svg-length-test.h
index 0f628ae7c..f6c6cb4f6 100644
--- a/src/svg/svg-length-test.h
+++ b/src/svg/svg-length-test.h
@@ -1,46 +1,46 @@
-#include <cxxtest/TestSuite.h>
-
-#include "svg/svg-length.h"
-#include <glib.h>
-#include <utility>
-
-class SvgLengthTest : public CxxTest::TestSuite
-{
-private:
-public:
- SvgLengthTest() {
- }
-
- void testRead()
- {
- struct test_t {
- char const* str; float computed;
- test_t(char const* str, float computed) : str(str), computed(computed) {}
- };
- test_t tests[] = {
- test_t("0",0),
- test_t("1",1),
- test_t("1.00001",1.00001),
- test_t("1px",1),
- test_t(".1px",0.1)};
- size_t n = G_N_ELEMENTS(tests);
- for(size_t i=0; i<n; i++) {
- SVGLength l;
- TSM_ASSERT(tests[i].str , l.read(tests[i].str));
- TSM_ASSERT_EQUALS(tests[i].str , l.computed , tests[i].computed);
- }
- }
-
- // TODO: More tests
-};
-
-/*
- 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 :
+#include <cxxtest/TestSuite.h>
+
+#include "svg/svg-length.h"
+#include <glib.h>
+#include <utility>
+
+class SvgLengthTest : public CxxTest::TestSuite
+{
+private:
+public:
+ SvgLengthTest() {
+ }
+
+ void testRead()
+ {
+ struct test_t {
+ char const* str; float computed;
+ test_t(char const* str, float computed) : str(str), computed(computed) {}
+ };
+ test_t tests[] = {
+ test_t("0",0),
+ test_t("1",1),
+ test_t("1.00001",1.00001),
+ test_t("1px",1),
+ test_t(".1px",0.1)};
+ size_t n = G_N_ELEMENTS(tests);
+ for(size_t i=0; i<n; i++) {
+ SVGLength l;
+ TSM_ASSERT(tests[i].str , l.read(tests[i].str));
+ TSM_ASSERT_EQUALS(tests[i].str , l.computed , tests[i].computed);
+ }
+ }
+
+ // TODO: More tests
+};
+
+/*
+ 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
index 5247ace83..5e2038527 100644
--- a/src/svg/svg-path-test.h
+++ b/src/svg/svg-path-test.h
@@ -1,472 +1,472 @@
-#include <cxxtest/TestSuite.h>
-#include "libnr/n-art-bpath.h"
-#include "svg/svg.h"
-#include "2geom/coord.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
- }
-
- 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);
- }
-
-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 :
+#include <cxxtest/TestSuite.h>
+#include "libnr/n-art-bpath.h"
+#include "svg/svg.h"
+#include "2geom/coord.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
+ }
+
+ 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);
+ }
+
+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 :