1 #include <cxxtest/TestSuite.h>
3 #include "svg/svg.h"
4 #include "streq.h"
5 #include <2geom/matrix.h>
6 #include <algorithm>
7 #include <glib.h>
8 #include <iostream>
9 #include <math.h>
10 #include <utility>
12 struct approx_equal {
13 bool operator()(Geom::Matrix const &ref, Geom::Matrix const &cm) const
14 {
15 double maxabsdiff = 0;
16 for(size_t i=0; i<6; i++) {
17 maxabsdiff = std::max(std::abs(ref[i]-cm[i]), maxabsdiff);
18 }
19 return maxabsdiff < 1e-14;
20 }
21 };
23 class SvgAffineTest : public CxxTest::TestSuite
24 {
25 private:
26 struct test_t {
27 char const * str;
28 Geom::Matrix matrix;
29 };
30 static test_t const read_matrix_tests[3];
31 static test_t const read_translate_tests[3];
32 static test_t const read_scale_tests[3];
33 static test_t const read_rotate_tests[4];
34 static test_t const read_skew_tests[3];
35 static char const * const read_fail_tests[25];
36 static test_t const write_matrix_tests[2];
37 static test_t const write_translate_tests[3];
38 static test_t const write_scale_tests[2];
39 static test_t const write_rotate_tests[2];
40 static test_t const write_skew_tests[3];
41 public:
42 SvgAffineTest() {
43 }
45 // createSuite and destroySuite get us per-suite setup and teardown
46 // without us having to worry about static initialization order, etc.
47 static SvgAffineTest *createSuite() { return new SvgAffineTest(); }
48 static void destroySuite( SvgAffineTest *suite ) { delete suite; }
50 void testReadIdentity()
51 {
52 char const* strs[] = {
53 //0,
54 "",
55 "matrix(1,0,0,1,0,0)",
56 "translate(0,0)",
57 "scale(1,1)",
58 "rotate(0,0,0)",
59 "skewX(0)",
60 "skewY(0)"};
61 size_t n = G_N_ELEMENTS(strs);
62 for(size_t i=0; i<n; i++) {
63 Geom::Matrix cm;
64 TSM_ASSERT(strs[i] , sp_svg_transform_read(strs[i], &cm));
65 TSM_ASSERT_EQUALS(strs[i] , Geom::identity() , cm);
66 }
67 }
69 void testWriteIdentity()
70 {
71 TS_ASSERT_EQUALS(sp_svg_transform_write(Geom::identity()) , (void*)0)
72 }
74 void testReadMatrix()
75 {
76 for(size_t i=0; i<G_N_ELEMENTS(read_matrix_tests); i++) {
77 Geom::Matrix cm;
78 TSM_ASSERT(read_matrix_tests[i].str , sp_svg_transform_read(read_matrix_tests[i].str, &cm));
79 TSM_ASSERT_RELATION(read_matrix_tests[i].str , approx_equal , read_matrix_tests[i].matrix , cm);
80 }
81 }
83 void testReadTranslate()
84 {
85 for(size_t i=0; i<G_N_ELEMENTS(read_translate_tests); i++) {
86 Geom::Matrix cm;
87 TSM_ASSERT(read_translate_tests[i].str , sp_svg_transform_read(read_translate_tests[i].str, &cm));
88 TSM_ASSERT_RELATION(read_translate_tests[i].str , approx_equal , read_translate_tests[i].matrix , cm);
89 }
90 }
92 void testReadScale()
93 {
94 for(size_t i=0; i<G_N_ELEMENTS(read_scale_tests); i++) {
95 Geom::Matrix cm;
96 TSM_ASSERT(read_scale_tests[i].str , sp_svg_transform_read(read_scale_tests[i].str, &cm));
97 TSM_ASSERT_RELATION(read_scale_tests[i].str , approx_equal , read_scale_tests[i].matrix , cm);
98 }
99 }
101 void testReadRotate()
102 {
103 for(size_t i=0; i<G_N_ELEMENTS(read_rotate_tests); i++) {
104 Geom::Matrix cm;
105 TSM_ASSERT(read_rotate_tests[i].str , sp_svg_transform_read(read_rotate_tests[i].str, &cm));
106 TSM_ASSERT_RELATION(read_rotate_tests[i].str , approx_equal , read_rotate_tests[i].matrix , cm);
107 }
108 }
110 void testReadSkew()
111 {
112 for(size_t i=0; i<G_N_ELEMENTS(read_skew_tests); i++) {
113 Geom::Matrix cm;
114 TSM_ASSERT(read_skew_tests[i].str , sp_svg_transform_read(read_skew_tests[i].str, &cm));
115 TSM_ASSERT_RELATION(read_skew_tests[i].str , approx_equal , read_skew_tests[i].matrix , cm);
116 }
117 }
119 void testWriteMatrix()
120 {
121 for(size_t i=0; i<G_N_ELEMENTS(write_matrix_tests); i++) {
122 char * str = sp_svg_transform_write(write_matrix_tests[i].matrix);
123 TS_ASSERT_RELATION(streq_rel , str , write_matrix_tests[i].str);
124 g_free(str);
125 }
126 }
128 void testWriteTranslate()
129 {
130 for(size_t i=0; i<G_N_ELEMENTS(write_translate_tests); i++) {
131 char * str = sp_svg_transform_write(write_translate_tests[i].matrix);
132 TS_ASSERT_RELATION(streq_rel , str , write_translate_tests[i].str);
133 g_free(str);
134 }
135 }
137 void testWriteScale()
138 {
139 for(size_t i=0; i<G_N_ELEMENTS(write_scale_tests); i++) {
140 char * str = sp_svg_transform_write(write_scale_tests[i].matrix);
141 TS_ASSERT_RELATION(streq_rel , str , write_scale_tests[i].str);
142 g_free(str);
143 }
144 }
146 void testWriteRotate()
147 {
148 for(size_t i=0; i<G_N_ELEMENTS(write_rotate_tests); i++) {
149 char * str = sp_svg_transform_write(write_rotate_tests[i].matrix);
150 TS_ASSERT_RELATION(streq_rel , str , write_rotate_tests[i].str);
151 g_free(str);
152 }
153 }
155 void testWriteSkew()
156 {
157 for(size_t i=0; i<G_N_ELEMENTS(write_skew_tests); i++) {
158 char * str = sp_svg_transform_write(write_skew_tests[i].matrix);
159 TS_ASSERT_RELATION(streq_rel , str , write_skew_tests[i].str);
160 g_free(str);
161 }
162 }
164 void testReadConcatenation()
165 {
166 // NOTE: According to the SVG specification (see the syntax at http://www.w3.org/TR/SVG/coords.html#TransformAttribute
167 // there should be 1 or more comma-wsp sequences between transforms... This doesn't make sense and it seems
168 // likely that instead of a + they meant a ? (zero or one comma-wsp sequences).
169 char const * str = "skewY(17)skewX(9)translate(7,13)scale(2)rotate(13)translate(3,5)";
170 Geom::Matrix ref(2.0199976232558053, 1.0674773585906016, -0.14125199392774669, 1.9055550612095459, 14.412730624347654, 28.499820929377454); // Precomputed using Mathematica
171 Geom::Matrix cm;
172 TS_ASSERT(sp_svg_transform_read(str, &cm));
173 TS_ASSERT_RELATION(approx_equal , ref , cm);
174 }
176 void testReadFailures()
177 {
178 for(size_t i=0; i<G_N_ELEMENTS(read_fail_tests); i++) {
179 Geom::Matrix cm;
180 TSM_ASSERT(read_fail_tests[i] , !sp_svg_transform_read(read_fail_tests[i], &cm));
181 }
182 }
183 };
185 static double const DEGREE = M_PI/180.;
187 SvgAffineTest::test_t const SvgAffineTest::read_matrix_tests[3] = {
188 {"matrix(0,0,0,0,0,0)",Geom::Matrix(0,0,0,0,0,0)},
189 {" matrix(1,2,3,4,5,6)",Geom::Matrix(1,2,3,4,5,6)},
190 {"matrix (1 2 -3,-4,5e6,-6e-7)",Geom::Matrix(1,2,-3,-4,5e6,-6e-7)}};
191 SvgAffineTest::test_t const SvgAffineTest::read_translate_tests[3] = {
192 {"translate(1)",Geom::Matrix(1,0,0,1,1,0)},
193 {"translate(1,1)",Geom::Matrix(1,0,0,1,1,1)},
194 {"translate(-1e3 .123e2)",Geom::Matrix(1,0,0,1,-1e3,.123e2)}};
195 SvgAffineTest::test_t const SvgAffineTest::read_scale_tests[3] = {
196 {"scale(2)",Geom::Matrix(2,0,0,2,0,0)},
197 {"scale(2,3)",Geom::Matrix(2,0,0,3,0,0)},
198 {"scale(0.1e-2 -.475e0)",Geom::Matrix(0.1e-2,0,0,-.475e0,0,0)}};
199 SvgAffineTest::test_t const SvgAffineTest::read_rotate_tests[4] = {
200 {"rotate(13 )",Geom::Matrix(cos(13.*DEGREE),sin(13.*DEGREE),-sin(13.*DEGREE),cos(13.*DEGREE),0,0)},
201 {"rotate(-13)",Geom::Matrix(cos(-13.*DEGREE),sin(-13.*DEGREE),-sin(-13.*DEGREE),cos(-13.*DEGREE),0,0)},
202 {"rotate(373)",Geom::Matrix(cos(13.*DEGREE),sin(13.*DEGREE),-sin(13.*DEGREE),cos(13.*DEGREE),0,0)},
203 {"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)}};
204 SvgAffineTest::test_t const SvgAffineTest::read_skew_tests[3] = {
205 {"skewX( 30)",Geom::Matrix(1,0,tan(30.*DEGREE),1,0,0)},
206 {"skewX(-30)",Geom::Matrix(1,0,tan(-30.*DEGREE),1,0,0)},
207 {"skewY(390)",Geom::Matrix(1,tan(30.*DEGREE),0,1,0,0)}};
208 char const * const SvgAffineTest::read_fail_tests[25] = {
209 "matrix((1,2,3,4,5,6)",
210 "matrix((1,2,3,4,5,6))",
211 "matrix(1,2,3,4,5,6))",
212 "matrix(,1,2,3,4,5,6)",
213 "matrix(1,2,3,4,5,6,)",
214 "matrix(1,2,3,4,5,)",
215 "matrix(1,2,3,4,5)",
216 "matrix(1,2,3,4,5e6-3)", // Here numbers HAVE to be separated by a comma-wsp sequence
217 "matrix(1,2,3,4,5e6.3)", // Here numbers HAVE to be separated by a comma-wsp sequence
218 "translate()",
219 "translate(,)",
220 "translate(1,)",
221 "translate(1,6,)",
222 "translate(1,6,0)",
223 "scale()",
224 "scale(1,6,2)",
225 "rotate()",
226 "rotate(1,6)",
227 "rotate(1,6,)",
228 "rotate(1,6,3,4)",
229 "skewX()",
230 "skewX(-)",
231 "skewX(.)",
232 "skewY(,)",
233 "skewY(1,2)"};
235 SvgAffineTest::test_t const SvgAffineTest::write_matrix_tests[2] = {
236 {"matrix(1,2,3,4,5,6)",Geom::Matrix(1,2,3,4,5,6)},
237 {"matrix(-1,2123,3,0.4,1e-8,1e20)",Geom::Matrix(-1,2.123e3,3+1e-14,0.4,1e-8,1e20)}};
238 SvgAffineTest::test_t const SvgAffineTest::write_translate_tests[3] = {
239 {"translate(1,1)",Geom::Matrix(1,0,0,1,1,1)},
240 {"translate(1)",Geom::Matrix(1,0,0,1,1,0)},
241 {"translate(-1345,0.123)",Geom::Matrix(1,0,0,1,-1.345e3,.123)}};
242 SvgAffineTest::test_t const SvgAffineTest::write_scale_tests[2] = {
243 {"scale(0)",Geom::Matrix(0,0,0,0,0,0)},
244 {"scale(2,3)",Geom::Matrix(2,0,0,3,0,0)}};
245 SvgAffineTest::test_t const SvgAffineTest::write_rotate_tests[2] = {
246 {"rotate(13)",Geom::Matrix(cos(13.*DEGREE),sin(13.*DEGREE),-sin(13.*DEGREE),cos(13.*DEGREE),0,0)},
247 {"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)}};
248 SvgAffineTest::test_t const SvgAffineTest::write_skew_tests[3] = {
249 {"skewX(30)",Geom::Matrix(1,0,tan(30.*DEGREE),1,0,0)},
250 {"skewX(-30)",Geom::Matrix(1,0,tan(-30.*DEGREE),1,0,0)},
251 {"skewY(390)",Geom::Matrix(1,tan(30.*DEGREE),0,1,0,0)}};
253 /*
254 Local Variables:
255 mode:c++
256 c-file-style:"stroustrup"
257 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
258 indent-tabs-mode:nil
259 fill-column:99
260 End:
261 */
262 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :