~ubuntu-branches/ubuntu/oneiric/inkscape/oneiric-updates

« back to all changes in this revision

Viewing changes to inkscape-0.47pre1/src/svg/svg-path-geom-test.h

  • Committer: Bazaar Package Importer
  • Author(s): Bryce Harrington
  • Date: 2009-07-02 17:09:45 UTC
  • mfrom: (1.1.9 upstream)
  • Revision ID: james.westby@ubuntu.com-20090702170945-nn6d6zswovbwju1t
Tags: 0.47~pre1-0ubuntu1
* New upstream release.
  - Don't constrain maximization on small resolution devices (pre0)
    (LP: #348842)
  - Fixes segfault on startup (pre0)
    (LP: #391149)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#include <cxxtest/TestSuite.h>
 
2
#include "2geom/coord.h"
 
3
#include "2geom/curves.h"
 
4
#include "2geom/pathvector.h"
 
5
#include "svg/svg.h"
 
6
#include "preferences.h"
 
7
#include "streq.h"
 
8
#include <stdio.h>
 
9
#include <string>
 
10
#include <vector>
 
11
#include <glib/gmem.h>
 
12
 
 
13
class SvgPathGeomTest : public CxxTest::TestSuite
 
14
{
 
15
private:
 
16
    std::vector<std::string> rectanglesAbsoluteClosed;
 
17
    std::vector<std::string> rectanglesRelativeClosed;
 
18
    std::vector<std::string> rectanglesAbsoluteOpen;
 
19
    std::vector<std::string> rectanglesRelativeOpen;
 
20
    std::vector<std::string> rectanglesAbsoluteClosed2;
 
21
    std::vector<std::string> rectanglesRelativeClosed2;
 
22
    Geom::PathVector rectanglepvopen;
 
23
    Geom::PathVector rectanglepvclosed;
 
24
    Geom::PathVector rectanglepvclosed2;
 
25
public:
 
26
    SvgPathGeomTest() {
 
27
        // Lots of ways to define the same rectangle
 
28
        rectanglesAbsoluteClosed.push_back("M 1,2 L 4,2 L 4,8 L 1,8 z");
 
29
        rectanglesAbsoluteClosed.push_back("M 1,2 4,2 4,8 1,8 z");
 
30
        rectanglesAbsoluteClosed.push_back("M 1,2 H 4 V 8 H 1 z");
 
31
        rectanglesRelativeClosed.push_back("m 1,2 l 3,0 l 0,6 l -3,0 z");
 
32
        rectanglesRelativeClosed.push_back("m 1,2 3,0 0,6 -3,0 z");
 
33
        rectanglesRelativeClosed.push_back("m 1,2 h 3 v 6 h -3 z");
 
34
        rectanglesAbsoluteOpen.push_back("M 1,2 L 4,2 L 4,8 L 1,8 L 1,2");
 
35
        rectanglesAbsoluteOpen.push_back("M 1,2 4,2 4,8 1,8 1,2");
 
36
        rectanglesAbsoluteOpen.push_back("M 1,2 H 4 V 8 H 1 V 2");
 
37
        rectanglesRelativeOpen.push_back("m 1,2 l 3,0 l 0,6 l -3,0 l 0,-6");
 
38
        rectanglesRelativeOpen.push_back("m 1,2 3,0 0,6 -3,0 0,-6");
 
39
        rectanglesRelativeOpen.push_back("m 1,2 h 3 v 6 h -3 v -6");
 
40
        rectanglesAbsoluteClosed2.push_back("M 1,2 L 4,2 L 4,8 L 1,8 L 1,2 z");
 
41
        rectanglesAbsoluteClosed2.push_back("M 1,2 4,2 4,8 1,8 1,2 z");
 
42
        rectanglesAbsoluteClosed2.push_back("M 1,2 H 4 V 8 H 1 V 2 z");
 
43
        rectanglesRelativeClosed2.push_back("m 1,2 l 3,0 l 0,6 l -3,0 l 0,-6 z");
 
44
        rectanglesRelativeClosed2.push_back("m 1,2 3,0 0,6 -3,0 0,-6 z");
 
45
        rectanglesRelativeClosed2.push_back("m 1,2 h 3 v 6 h -3 v -6 z");
 
46
        rectanglepvopen.push_back(Geom::Path(Geom::Point(1,2)));
 
47
        rectanglepvopen.back().append(Geom::LineSegment(Geom::Point(1,2),Geom::Point(4,2)));
 
48
        rectanglepvopen.back().append(Geom::LineSegment(Geom::Point(4,2),Geom::Point(4,8)));
 
49
        rectanglepvopen.back().append(Geom::LineSegment(Geom::Point(4,8),Geom::Point(1,8)));
 
50
        rectanglepvopen.back().append(Geom::LineSegment(Geom::Point(1,8),Geom::Point(1,2)));
 
51
        rectanglepvclosed.push_back(Geom::Path(Geom::Point(1,2)));
 
52
        rectanglepvclosed.back().append(Geom::LineSegment(Geom::Point(1,2),Geom::Point(4,2)));
 
53
        rectanglepvclosed.back().append(Geom::LineSegment(Geom::Point(4,2),Geom::Point(4,8)));
 
54
        rectanglepvclosed.back().append(Geom::LineSegment(Geom::Point(4,8),Geom::Point(1,8)));
 
55
        rectanglepvclosed.back().close();
 
56
        rectanglepvclosed2.push_back(Geom::Path(Geom::Point(1,2)));
 
57
        rectanglepvclosed2.back().append(Geom::LineSegment(Geom::Point(1,2),Geom::Point(4,2)));
 
58
        rectanglepvclosed2.back().append(Geom::LineSegment(Geom::Point(4,2),Geom::Point(4,8)));
 
59
        rectanglepvclosed2.back().append(Geom::LineSegment(Geom::Point(4,8),Geom::Point(1,8)));
 
60
        rectanglepvclosed2.back().append(Geom::LineSegment(Geom::Point(1,8),Geom::Point(1,2)));
 
61
        rectanglepvclosed2.back().close();
 
62
        // TODO: Also test some (smooth) cubic/quadratic beziers and elliptical arcs
 
63
        // TODO: Should we make it mandatory that h/v in the path data results in a H/VLineSegment?
 
64
        //       If so, the tests should be modified to reflect this.
 
65
    }
 
66
 
 
67
// createSuite and destroySuite get us per-suite setup and teardown
 
68
// without us having to worry about static initialization order, etc.
 
69
    static SvgPathGeomTest *createSuite() { return new SvgPathGeomTest(); }
 
70
    static void destroySuite( SvgPathGeomTest *suite ) { delete suite; }
 
71
 
 
72
    void testReadRectanglesAbsoluteClosed()
 
73
    {
 
74
        for(size_t i=0; i<rectanglesAbsoluteClosed.size(); i++) {
 
75
            Geom::PathVector pv = sp_svg_read_pathv(rectanglesAbsoluteClosed[i].c_str());
 
76
            TSM_ASSERT(rectanglesAbsoluteClosed[i].c_str(), bpathEqual(pv,rectanglepvclosed));
 
77
        }
 
78
    }
 
79
 
 
80
    void testReadRectanglesRelativeClosed()
 
81
    {
 
82
        for(size_t i=0; i<rectanglesRelativeClosed.size(); i++) {
 
83
            Geom::PathVector pv = sp_svg_read_pathv(rectanglesRelativeClosed[i].c_str());
 
84
            TSM_ASSERT(rectanglesRelativeClosed[i].c_str(), bpathEqual(pv,rectanglepvclosed));
 
85
        }
 
86
    }
 
87
 
 
88
    void testReadRectanglesAbsoluteOpen()
 
89
    {
 
90
        for(size_t i=0; i<rectanglesAbsoluteOpen.size(); i++) {
 
91
            Geom::PathVector pv = sp_svg_read_pathv(rectanglesAbsoluteOpen[i].c_str());
 
92
            TSM_ASSERT(rectanglesAbsoluteOpen[i].c_str(), bpathEqual(pv,rectanglepvopen));
 
93
        }
 
94
    }
 
95
 
 
96
    void testReadRectanglesRelativeOpen()
 
97
    {
 
98
        for(size_t i=0; i<rectanglesRelativeOpen.size(); i++) {
 
99
            Geom::PathVector pv = sp_svg_read_pathv(rectanglesRelativeOpen[i].c_str());
 
100
            TSM_ASSERT(rectanglesRelativeOpen[i].c_str(), bpathEqual(pv,rectanglepvopen));
 
101
        }
 
102
    }
 
103
 
 
104
    void testReadRectanglesAbsoluteClosed2()
 
105
    {
 
106
        for(size_t i=0; i<rectanglesAbsoluteClosed2.size(); i++) {
 
107
            Geom::PathVector pv = sp_svg_read_pathv(rectanglesAbsoluteClosed2[i].c_str());
 
108
            TSM_ASSERT(rectanglesAbsoluteClosed2[i].c_str(), bpathEqual(pv,rectanglepvclosed2));
 
109
        }
 
110
    }
 
111
 
 
112
    void testReadRectanglesRelativeClosed2()
 
113
    {
 
114
        for(size_t i=0; i<rectanglesRelativeClosed2.size(); i++) {
 
115
            Geom::PathVector pv = sp_svg_read_pathv(rectanglesRelativeClosed2[i].c_str());
 
116
            TSM_ASSERT(rectanglesRelativeClosed2[i].c_str(), bpathEqual(pv,rectanglepvclosed2));
 
117
        }
 
118
    }
 
119
 
 
120
    void testReadConcatenatedPaths()
 
121
    {
 
122
        // Note that finalPoint doesn't actually return the final point of the path, just the last given point... (but since this might be intentional and we're not testing lib2geom here, we just specify the final point explicitly
 
123
        Geom::PathVector pv_good;
 
124
        pv_good.push_back(rectanglepvclosed.back());
 
125
        pv_good.push_back(rectanglepvopen.back() * Geom::Translate(1,2)/* * Geom::Translate(pv_good[0].finalPoint())*/);
 
126
        pv_good.push_back(rectanglepvclosed.back() * Geom::Translate(2,4)/* *Geom::Translate(pv_good[1].finalPoint())*/);
 
127
        pv_good.push_back(rectanglepvopen.back());
 
128
        pv_good[0].close();
 
129
        pv_good[1].close(false);
 
130
        pv_good[2].close();
 
131
        pv_good[3].close(false);
 
132
        std::string path_str = rectanglesAbsoluteClosed[0] + rectanglesRelativeOpen[0] + rectanglesRelativeClosed[0] + rectanglesAbsoluteOpen[0];
 
133
        Geom::PathVector pv = sp_svg_read_pathv(path_str.c_str());
 
134
        TS_ASSERT(bpathEqual(pv,pv_good));
 
135
    }
 
136
 
 
137
    void testReadZeroLengthSubpaths() {
 
138
        // Per the SVG 1.1 specification (section F5) zero-length subpaths are relevant
 
139
        Geom::PathVector pv_good;
 
140
        pv_good.push_back(Geom::Path(Geom::Point(0,0)));
 
141
        pv_good.push_back(Geom::Path(Geom::Point(1,1)));
 
142
        pv_good.back().append(Geom::LineSegment(Geom::Point(1,1),Geom::Point(2,2)));
 
143
        pv_good.push_back(Geom::Path(Geom::Point(3,3)));
 
144
        pv_good.back().close();
 
145
        pv_good.push_back(Geom::Path(Geom::Point(4,4)));
 
146
        pv_good.back().append(Geom::LineSegment(Geom::Point(4,4),Geom::Point(5,5)));
 
147
        pv_good.back().close();
 
148
        pv_good.push_back(Geom::Path(Geom::Point(6,6)));
 
149
        {   // Test absolute version
 
150
            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";
 
151
            Geom::PathVector pv = sp_svg_read_pathv(path_str);
 
152
            TSM_ASSERT(path_str, bpathEqual(pv,pv_good));
 
153
        }
 
154
        {   // Test relative version
 
155
            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";
 
156
            Geom::PathVector pv = sp_svg_read_pathv(path_str);
 
157
            TSM_ASSERT(path_str, bpathEqual(pv,pv_good));
 
158
        }
 
159
    }
 
160
 
 
161
    void testReadImplicitMoveto() {
 
162
        TS_WARN("Currently lib2geom (/libnr) has no way of specifying the difference between '... z M 0,0 L 1,0' and '... z L 1,0', the SVG specification does state that these should be handled differently with respect to markers however, see the description of the 'orient' attribute of the 'marker' element.");
 
163
        Geom::PathVector pv_good;
 
164
        pv_good.push_back(Geom::Path(Geom::Point(1,1)));
 
165
        pv_good.back().append(Geom::LineSegment(Geom::Point(1,1),Geom::Point(2,2)));
 
166
        pv_good.back().close();
 
167
        pv_good.push_back(Geom::Path(Geom::Point(1,1)));
 
168
        pv_good.back().append(Geom::LineSegment(Geom::Point(1,1),Geom::Point(3,3)));
 
169
        pv_good.back().close();
 
170
        {   // Test absolute version
 
171
            char const * path_str = "M 1,1 L 2,2 z L 3,3 z";
 
172
            Geom::PathVector pv = sp_svg_read_pathv(path_str);
 
173
            TSM_ASSERT(path_str, bpathEqual(pv,pv_good));
 
174
        }
 
175
        {   // Test relative version
 
176
            char const * path_str = "M 1,1 L 2,2 z L 3,3 z";
 
177
            Geom::PathVector pv = sp_svg_read_pathv(path_str);
 
178
            TSM_ASSERT(path_str, bpathEqual(pv,pv_good));
 
179
        }
 
180
    }
 
181
 
 
182
    void testReadFloatingPoint() {
 
183
        Geom::PathVector pv_good1;
 
184
        pv_good1.push_back(Geom::Path(Geom::Point(.01,.02)));
 
185
        pv_good1.back().append(Geom::LineSegment(Geom::Point(.01,.02),Geom::Point(.04,.02)));
 
186
        pv_good1.back().append(Geom::LineSegment(Geom::Point(.04,.02),Geom::Point(1.5,1.6)));
 
187
        pv_good1.back().append(Geom::LineSegment(Geom::Point(1.5,1.6),Geom::Point(.01,.08)));
 
188
        pv_good1.back().append(Geom::LineSegment(Geom::Point(.01,.08),Geom::Point(.01,.02)));
 
189
        pv_good1.back().close();
 
190
        {   // Test decimals
 
191
            char const * path_str = "M .01,.02 L.04.02 L1.5,1.6L0.01,0.08 .01.02 z";
 
192
            Geom::PathVector pv = sp_svg_read_pathv(path_str);
 
193
            TSM_ASSERT(path_str, bpathEqual(pv,pv_good1));
 
194
        }
 
195
        Geom::PathVector pv_good2;
 
196
        pv_good2.push_back(Geom::Path(Geom::Point(.01,.02)));
 
197
        pv_good2.back().append(Geom::LineSegment(Geom::Point(.01,.02),Geom::Point(.04,.02)));
 
198
        pv_good2.back().append(Geom::LineSegment(Geom::Point(.04,.02),Geom::Point(1.5,1.6)));
 
199
        pv_good2.back().append(Geom::LineSegment(Geom::Point(1.5,1.6),Geom::Point(.01,.08)));
 
200
        pv_good2.back().close();
 
201
        {   // Test exponent
 
202
            char const * path_str = "M 1e-2,.2e-1 L 0.004e1,0.0002e+2 L0150E-2,1.6e0L1.0e-2,80e-3 z";
 
203
            Geom::PathVector pv = sp_svg_read_pathv(path_str);
 
204
            TSM_ASSERT(path_str, bpathEqual(pv,pv_good2));
 
205
        }
 
206
    }
 
207
 
 
208
    void testReadImplicitSeparation() {
 
209
        // Coordinates need not be separated by whitespace if they can still be read unambiguously
 
210
        Geom::PathVector pv_good;
 
211
        pv_good.push_back(Geom::Path(Geom::Point(.1,.2)));
 
212
        pv_good.back().append(Geom::LineSegment(Geom::Point(.1,.2),Geom::Point(.4,.2)));
 
213
        pv_good.back().append(Geom::LineSegment(Geom::Point(.4,.2),Geom::Point(.4,.8)));
 
214
        pv_good.back().append(Geom::LineSegment(Geom::Point(.4,.8),Geom::Point(.1,.8)));
 
215
        pv_good.back().close();
 
216
        {   // Test absolute
 
217
            char const * path_str = "M .1.2+0.4.2e0.4e0+8e-1.1.8 z";
 
218
            Geom::PathVector pv = sp_svg_read_pathv(path_str);
 
219
            TSM_ASSERT(path_str, bpathEqual(pv,pv_good));
 
220
        }
 
221
        {   // Test relative
 
222
            char const * path_str = "m .1.2+0.3.0e0.0e0+6e-1-.3.0 z";
 
223
            Geom::PathVector pv = sp_svg_read_pathv(path_str);
 
224
            TSM_ASSERT(path_str, bpathEqual(pv,pv_good));
 
225
        }
 
226
    }
 
227
 
 
228
    void testReadErrorMisplacedCharacter() {
 
229
        char const * path_str;
 
230
        Geom::PathVector pv;
 
231
        // Comma in the wrong place (commas may only appear between parameters)
 
232
        path_str = "M 1,2 4,2 4,8 1,8 z , m 13,15";
 
233
        pv = sp_svg_read_pathv(path_str);
 
234
        TSM_ASSERT(path_str, bpathEqual(pv,rectanglepvclosed));
 
235
        // Comma in the wrong place (commas may only appear between parameters)
 
236
        path_str = "M 1,2 4,2 4,8 1,8 z m,13,15";
 
237
        pv = sp_svg_read_pathv(path_str);
 
238
        TSM_ASSERT(path_str, bpathEqual(pv,rectanglepvclosed));
 
239
        // Period in the wrong place (no numbers after a 'z')
 
240
        path_str = "M 1,2 4,2 4,8 1,8 z . m 13,15";
 
241
        pv = sp_svg_read_pathv(path_str);
 
242
        TSM_ASSERT(path_str, bpathEqual(pv,rectanglepvclosed));
 
243
        // Sign in the wrong place (no numbers after a 'z')
 
244
        path_str = "M 1,2 4,2 4,8 1,8 z + - m 13,15";
 
245
        pv = sp_svg_read_pathv(path_str);
 
246
        TSM_ASSERT(path_str, bpathEqual(pv,rectanglepvclosed));
 
247
        // Digit in the wrong place (no numbers after a 'z')
 
248
        path_str = "M 1,2 4,2 4,8 1,8 z 9809 m 13,15";
 
249
        pv = sp_svg_read_pathv(path_str);
 
250
        TSM_ASSERT(path_str, bpathEqual(pv,rectanglepvclosed));
 
251
        // Digit in the wrong place (no numbers after a 'z')
 
252
        path_str = "M 1,2 4,2 4,8 1,8 z 9809 876 m 13,15";
 
253
        pv = sp_svg_read_pathv(path_str);
 
254
        TSM_ASSERT(path_str, bpathEqual(pv,rectanglepvclosed));
 
255
    }
 
256
 
 
257
    void testReadErrorUnrecognizedCharacter() {
 
258
        char const * path_str;
 
259
        Geom::PathVector pv;
 
260
        // Unrecognized character
 
261
        path_str = "M 1,2 4,2 4,8 1,8 z&m 13,15";
 
262
        pv = sp_svg_read_pathv(path_str);
 
263
        TSM_ASSERT(path_str, bpathEqual(pv,rectanglepvclosed));
 
264
        // Unrecognized character
 
265
        path_str = "M 1,2 4,2 4,8 1,8 z m &13,15";
 
266
        pv = sp_svg_read_pathv(path_str);
 
267
        TSM_ASSERT(path_str, bpathEqual(pv,rectanglepvclosed));
 
268
    }
 
269
 
 
270
    void testReadErrorTypo() {
 
271
        char const * path_str;
 
272
        Geom::PathVector pv;
 
273
        // Typo
 
274
        path_str = "M 1,2 4,2 4,8 1,8 z j 13,15";
 
275
        pv = sp_svg_read_pathv(path_str);
 
276
        TSM_ASSERT(path_str, bpathEqual(pv,rectanglepvclosed));
 
277
 
 
278
        // Typo
 
279
        path_str = "M 1,2 4,2 4,8 1,8 L 1,2 x m 13,15";
 
280
        pv = sp_svg_read_pathv(path_str);
 
281
        TSM_ASSERT(path_str, bpathEqual(pv,rectanglepvopen));
 
282
    }
 
283
 
 
284
    void testReadErrorIllformedNumbers() {
 
285
        char const * path_str;
 
286
        Geom::PathVector pv;
 
287
        // Double exponent
 
288
        path_str = "M 1,2 4,2 4,8 1,8 z m 13e4e5,15";
 
289
        pv = sp_svg_read_pathv(path_str);
 
290
        TSM_ASSERT(path_str, bpathEqual(pv,rectanglepvclosed));
 
291
        // Double sign
 
292
        path_str = "M 1,2 4,2 4,8 1,8 z m +-13,15";
 
293
        pv = sp_svg_read_pathv(path_str);
 
294
        TSM_ASSERT(path_str, bpathEqual(pv,rectanglepvclosed));
 
295
        // Double sign
 
296
        path_str = "M 1,2 4,2 4,8 1,8 z m 13e+-12,15";
 
297
        pv = sp_svg_read_pathv(path_str);
 
298
        TSM_ASSERT(path_str, bpathEqual(pv,rectanglepvclosed));
 
299
        // No digit
 
300
        path_str = "M 1,2 4,2 4,8 1,8 z m .e12,15";
 
301
        pv = sp_svg_read_pathv(path_str);
 
302
        TSM_ASSERT(path_str, bpathEqual(pv,rectanglepvclosed));
 
303
        // No digit
 
304
        path_str = "M 1,2 4,2 4,8 1,8 z m .,15";
 
305
        pv = sp_svg_read_pathv(path_str);
 
306
        TSM_ASSERT(path_str, bpathEqual(pv,rectanglepvclosed));
 
307
        // No digit
 
308
        path_str = "M 1,2 4,2 4,8 1,8 z m +,15";
 
309
        pv = sp_svg_read_pathv(path_str);
 
310
        TSM_ASSERT(path_str, bpathEqual(pv,rectanglepvclosed));
 
311
        // No digit
 
312
        path_str = "M 1,2 4,2 4,8 1,8 z m +.e+,15";
 
313
        pv = sp_svg_read_pathv(path_str);
 
314
        TSM_ASSERT(path_str, bpathEqual(pv,rectanglepvclosed));
 
315
    }
 
316
 
 
317
    void testReadErrorJunk() {
 
318
        char const * path_str;
 
319
        Geom::PathVector pv;
 
320
        // Junk
 
321
        path_str = "M 1,2 4,2 4,8 1,8 z j 357 hkjh.,34e34 90ih6kj4 h5k6vlh4N.,6,45wikuyi3yere..3487 m 13,23";
 
322
        pv = sp_svg_read_pathv(path_str);
 
323
        TSM_ASSERT(path_str, bpathEqual(pv,rectanglepvclosed));
 
324
    }
 
325
 
 
326
    void testReadErrorStopReading() {
 
327
        char const * path_str;
 
328
        Geom::PathVector pv;
 
329
        // Unrecognized parameter
 
330
        path_str = "M 1,2 4,2 4,8 1,8 z m #$%,23,34";
 
331
        pv = sp_svg_read_pathv(path_str);
 
332
        TSM_ASSERT(path_str, bpathEqual(pv,rectanglepvclosed));
 
333
        // Invalid parameter
 
334
        path_str = "M 1,2 4,2 4,8 1,8 z m #$%,23,34";
 
335
        pv = sp_svg_read_pathv(path_str);
 
336
        TSM_ASSERT(path_str, bpathEqual(pv,rectanglepvclosed));
 
337
        // Illformed parameter
 
338
        path_str = "M 1,2 4,2 4,8 1,8 z m +-12,23,34";
 
339
        pv = sp_svg_read_pathv(path_str);
 
340
        TSM_ASSERT(path_str, bpathEqual(pv,rectanglepvclosed));
 
341
 
 
342
        // "Third" parameter
 
343
        path_str = "M 1,2 4,2 4,8 1,8 1,2,3 M 12,23";
 
344
        pv = sp_svg_read_pathv(path_str);
 
345
        TSM_ASSERT(path_str, bpathEqual(pv,rectanglepvopen));
 
346
    }
 
347
 
 
348
    void testRoundTrip() {
 
349
        // This is the easiest way to (also) test writing path data, as a path can be written in more than one way.
 
350
        Geom::PathVector pv;
 
351
        Geom::PathVector new_pv;
 
352
        std::string org_path_str;
 
353
        char * path_str;
 
354
        // Rectangle (closed)
 
355
        org_path_str = rectanglesAbsoluteClosed[0];
 
356
        pv = sp_svg_read_pathv(org_path_str.c_str());
 
357
        path_str = sp_svg_write_path(pv);
 
358
        new_pv = sp_svg_read_pathv(path_str);
 
359
        TSM_ASSERT(org_path_str.c_str(), bpathEqual(pv,new_pv));
 
360
        g_free(path_str);
 
361
        // Rectangle (open)
 
362
        org_path_str = rectanglesAbsoluteOpen[0];
 
363
        pv = sp_svg_read_pathv(org_path_str.c_str());
 
364
        path_str = sp_svg_write_path(pv);
 
365
        new_pv = sp_svg_read_pathv(path_str);
 
366
        TSM_ASSERT(org_path_str.c_str(), bpathEqual(pv,new_pv));
 
367
        g_free(path_str);
 
368
        // Concatenated rectangles
 
369
        org_path_str = rectanglesAbsoluteClosed[0] + rectanglesRelativeOpen[0] + rectanglesRelativeClosed[0] + rectanglesAbsoluteOpen[0];
 
370
        pv = sp_svg_read_pathv(org_path_str.c_str());
 
371
        path_str = sp_svg_write_path(pv);
 
372
        new_pv = sp_svg_read_pathv(path_str);
 
373
        TSM_ASSERT(org_path_str.c_str(), bpathEqual(pv,new_pv));
 
374
        g_free(path_str);
 
375
        // Zero-length subpaths
 
376
        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";
 
377
        pv = sp_svg_read_pathv(org_path_str.c_str());
 
378
        path_str = sp_svg_write_path(pv);
 
379
        new_pv = sp_svg_read_pathv(path_str);
 
380
        TSM_ASSERT(org_path_str.c_str(), bpathEqual(pv,new_pv));
 
381
        g_free(path_str);
 
382
        // Floating-point
 
383
        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";
 
384
        pv = sp_svg_read_pathv(org_path_str.c_str());
 
385
        path_str = sp_svg_write_path(pv);
 
386
        new_pv = sp_svg_read_pathv(path_str);
 
387
        TSM_ASSERT(org_path_str.c_str(), bpathEqual(pv, new_pv, 1e-17));
 
388
        g_free(path_str);
 
389
    }
 
390
 
 
391
    void testMinexpPrecision() {
 
392
        Geom::PathVector pv;
 
393
        char * path_str;
 
394
        // Default values
 
395
        Inkscape::Preferences *prefs = Inkscape::Preferences::get();
 
396
        prefs->setBool("/options/svgoutput/allowrelativecoordinates", true);
 
397
        prefs->setBool("/options/svgoutput/forcerepeatcommands", false);
 
398
        prefs->setInt("/options/svgoutput/numericprecision", 8);
 
399
        prefs->setInt("/options/svgoutput/minimumexponent", -8);
 
400
        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");
 
401
        path_str = sp_svg_write_path(pv);
 
402
        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 );
 
403
        g_free(path_str);
 
404
    }
 
405
 
 
406
private:
 
407
    bool bpathEqual(Geom::PathVector const &a, Geom::PathVector const &b, double eps = 1e-16) {
 
408
        if (a.size() != b.size()) {
 
409
            char temp[100];
 
410
            sprintf(temp, "PathVectors not the same size: %u != %u", a.size(), b.size());
 
411
            TS_FAIL(temp);
 
412
            return false;
 
413
        }
 
414
        for(size_t i=0; i<a.size(); i++) {
 
415
            Geom::Path const &pa = a[i];
 
416
            Geom::Path const &pb = b[i];
 
417
            if (pa.closed() && !pb.closed()) {
 
418
                char temp[100];
 
419
                sprintf(temp, "Left subpath is closed, right subpath is open. Subpath: %u", i);
 
420
                TS_FAIL(temp);
 
421
                return false;
 
422
            }
 
423
            if (!pa.closed() && pb.closed()) {
 
424
                char temp[100];
 
425
                sprintf(temp, "Right subpath is closed, left subpath is open. Subpath: %u", i);
 
426
                TS_FAIL(temp);
 
427
                return false;
 
428
            }
 
429
            if (pa.size() != pb.size()) {
 
430
                char temp[100];
 
431
                sprintf(temp, "Not the same number of segments: %u != %u, subpath: %u", pa.size(), pb.size(), i);
 
432
                TS_FAIL(temp);
 
433
                return false;
 
434
            }
 
435
            for(size_t j=0; j<pa.size(); j++) {
 
436
                Geom::Curve const* ca = &pa[j];
 
437
                Geom::Curve const* cb = &pb[j];
 
438
                if (typeid(*ca) == typeid(*cb))
 
439
                {
 
440
                    if(Geom::LineSegment const *la = dynamic_cast<Geom::LineSegment const*>(ca))
 
441
                    {
 
442
                        Geom::LineSegment const *lb = dynamic_cast<Geom::LineSegment const*>(cb);
 
443
                        if (!Geom::are_near((*la)[0],(*lb)[0], eps)) {
 
444
                            char temp[200];
 
445
                            sprintf(temp, "Different start of segment: (%g,%g) != (%g,%g), subpath: %u, segment: %u", (*la)[0][Geom::X], (*la)[0][Geom::Y], (*lb)[0][Geom::X], (*lb)[0][Geom::Y], i, j);
 
446
                            TS_FAIL(temp);
 
447
                            return false;
 
448
                        }
 
449
                        if (!Geom::are_near((*la)[1],(*lb)[1], eps)) {
 
450
                            char temp[200];
 
451
                            sprintf(temp, "Different end of segment: (%g,%g) != (%g,%g), subpath: %u, segment: %u", (*la)[1][Geom::X], (*la)[1][Geom::Y], (*lb)[1][Geom::X], (*lb)[1][Geom::Y], i, j);
 
452
                            TS_FAIL(temp);
 
453
                            return false;
 
454
                        }
 
455
                    }
 
456
                    else if(Geom::HLineSegment const *la = dynamic_cast<Geom::HLineSegment const*>(ca))
 
457
                    {
 
458
                        Geom::HLineSegment const *lb = dynamic_cast<Geom::HLineSegment const*>(cb);
 
459
                        if (!Geom::are_near((*la).initialPoint(),(*lb).initialPoint(), eps)) {
 
460
                            char temp[200];
 
461
                            sprintf(temp, "Different start of segment: (%g,%g) != (%g,%g), subpath: %u, segment: %u", (*la).initialPoint()[Geom::X], (*la).initialPoint()[Geom::Y], (*lb).initialPoint()[Geom::X], (*lb).initialPoint()[Geom::Y], i, j);
 
462
                            TS_FAIL(temp);
 
463
                            return false;
 
464
                        }
 
465
                        if (!Geom::are_near((*la).finalPoint(),(*lb).finalPoint(), eps)) {
 
466
                            char temp[200];
 
467
                            sprintf(temp, "Different end of segment: (%g,%g) != (%g,%g), subpath: %u, segment: %u", (*la).finalPoint()[Geom::X], (*la).finalPoint()[Geom::Y], (*lb).finalPoint()[Geom::X], (*lb).finalPoint()[Geom::Y], i, j);
 
468
                            TS_FAIL(temp);
 
469
                            return false;
 
470
                        }
 
471
                    }
 
472
                    else if(Geom::VLineSegment const *la = dynamic_cast<Geom::VLineSegment const*>(ca))
 
473
                    {
 
474
                        Geom::VLineSegment const *lb = dynamic_cast<Geom::VLineSegment const*>(cb);
 
475
                        if (!Geom::are_near((*la).initialPoint(),(*lb).initialPoint(), eps)) {
 
476
                            char temp[200];
 
477
                            sprintf(temp, "Different start of segment: (%g,%g) != (%g,%g), subpath: %u, segment: %u", (*la).initialPoint()[Geom::X], (*la).initialPoint()[Geom::Y], (*lb).initialPoint()[Geom::X], (*lb).initialPoint()[Geom::Y], i, j);
 
478
                            TS_FAIL(temp);
 
479
                            return false;
 
480
                        }
 
481
                        if (!Geom::are_near((*la).finalPoint(),(*lb).finalPoint(), eps)) {
 
482
                            char temp[200];
 
483
                            sprintf(temp, "Different end of segment: (%g,%g) != (%g,%g), subpath: %u, segment: %u", (*la).finalPoint()[Geom::X], (*la).finalPoint()[Geom::Y], (*lb).finalPoint()[Geom::X], (*lb).finalPoint()[Geom::Y], i, j);
 
484
                            TS_FAIL(temp);
 
485
                            return false;
 
486
                        }
 
487
                    }
 
488
                    else if(Geom::CubicBezier const *la = dynamic_cast<Geom::CubicBezier const*>(ca))
 
489
                    {
 
490
                        Geom::CubicBezier const *lb = dynamic_cast<Geom::CubicBezier const*>(cb);
 
491
                        if (!Geom::are_near((*la)[0],(*lb)[0], eps)) {
 
492
                            char temp[200];
 
493
                            sprintf(temp, "Different start of segment: (%g,%g) != (%g,%g), subpath: %u, segment: %u", (*la)[0][Geom::X], (*la)[0][Geom::Y], (*lb)[0][Geom::X], (*lb)[0][Geom::Y], i, j);
 
494
                            TS_FAIL(temp);
 
495
                            return false;
 
496
                        }
 
497
                        if (!Geom::are_near((*la)[1],(*lb)[1], eps)) {
 
498
                            char temp[200];
 
499
                            sprintf(temp, "Different 1st control point: (%g,%g) != (%g,%g), subpath: %u, segment: %u", (*la)[1][Geom::X], (*la)[1][Geom::Y], (*lb)[1][Geom::X], (*lb)[1][Geom::Y], i, j);
 
500
                            TS_FAIL(temp);
 
501
                            return false;
 
502
                        }
 
503
                        if (!Geom::are_near((*la)[2],(*lb)[2], eps)) {
 
504
                            char temp[200];
 
505
                            sprintf(temp, "Different 2nd control point: (%g,%g) != (%g,%g), subpath: %u, segment: %u", (*la)[2][Geom::X], (*la)[2][Geom::Y], (*lb)[2][Geom::X], (*lb)[2][Geom::Y], i, j);
 
506
                            TS_FAIL(temp);
 
507
                            return false;
 
508
                        }
 
509
                        if (!Geom::are_near((*la)[3],(*lb)[3], eps)) {
 
510
                            char temp[200];
 
511
                            sprintf(temp, "Different end of segment: (%g,%g) != (%g,%g), subpath: %u, segment: %u", (*la)[3][Geom::X], (*la)[3][Geom::Y], (*lb)[3][Geom::X], (*lb)[3][Geom::Y], i, j);
 
512
                            TS_FAIL(temp);
 
513
                            return false;
 
514
                        }
 
515
                    }
 
516
                    else
 
517
                    {
 
518
                        char temp[200];
 
519
                        sprintf(temp, "Unknown curve type: %s, subpath: %u, segment: %u", typeid(*ca).name(), i, j);
 
520
                        TS_FAIL(temp);
 
521
                    }
 
522
                }
 
523
                else // not same type
 
524
                {
 
525
                    if(Geom::LineSegment const *la = dynamic_cast<Geom::LineSegment const*>(ca))
 
526
                    {
 
527
                        if (Geom::HLineSegment const *lb = dynamic_cast<Geom::HLineSegment const*>(cb)) {
 
528
                            if (!Geom::are_near((*la).initialPoint(),(*lb).initialPoint(), eps)) {
 
529
                                char temp[200];
 
530
                                sprintf(temp, "Different start of segment: (%g,%g) != (%g,%g), subpath: %u, segment: %u", (*la).initialPoint()[Geom::X], (*la).initialPoint()[Geom::Y], (*lb).initialPoint()[Geom::X], (*lb).initialPoint()[Geom::Y], i, j);
 
531
                                TS_FAIL(temp);
 
532
                                return false;
 
533
                            }
 
534
                            if (!Geom::are_near((*la).finalPoint(),(*lb).finalPoint(), eps)) {
 
535
                                char temp[200];
 
536
                                sprintf(temp, "Different end of segment: (%g,%g) != (%g,%g), subpath: %u, segment: %u", (*la).finalPoint()[Geom::X], (*la).finalPoint()[Geom::Y], (*lb).finalPoint()[Geom::X], (*lb).finalPoint()[Geom::Y], i, j);
 
537
                                TS_FAIL(temp);
 
538
                                return false;
 
539
                            }
 
540
                            char temp[200];
 
541
                            sprintf(temp, "A LineSegment and an HLineSegment have been considered equal. Subpath: %u, segment: %u", i, j);
 
542
                            TS_TRACE(temp);
 
543
                        } else if (Geom::VLineSegment const *lb = dynamic_cast<Geom::VLineSegment const*>(cb)) {
 
544
                            if (!Geom::are_near((*la).initialPoint(),(*lb).initialPoint(), eps)) {
 
545
                                char temp[200];
 
546
                                sprintf(temp, "Different start of segment: (%g,%g) != (%g,%g), subpath: %u, segment: %u", (*la).initialPoint()[Geom::X], (*la).initialPoint()[Geom::Y], (*lb).initialPoint()[Geom::X], (*lb).initialPoint()[Geom::Y], i, j);
 
547
                                TS_FAIL(temp);
 
548
                                return false;
 
549
                            }
 
550
                            if (!Geom::are_near((*la).finalPoint(),(*lb).finalPoint(), eps)) {
 
551
                                char temp[200];
 
552
                                sprintf(temp, "Different end of segment: (%g,%g) != (%g,%g), subpath: %u, segment: %u", (*la).finalPoint()[Geom::X], (*la).finalPoint()[Geom::Y], (*lb).finalPoint()[Geom::X], (*lb).finalPoint()[Geom::Y], i, j);
 
553
                                TS_FAIL(temp);
 
554
                                return false;
 
555
                            }
 
556
                            char temp[200];
 
557
                            sprintf(temp, "A LineSegment and a VLineSegment have been considered equal. Subpath: %u, segment: %u", i, j);
 
558
                            TS_TRACE(temp);
 
559
                        } else {
 
560
                            char temp[200];
 
561
                            sprintf(temp, "Different curve types: %s != %s, subpath: %u, segment: %u", typeid(*ca).name(), typeid(*cb).name(), i, j);
 
562
                            TS_FAIL(temp);
 
563
                        }
 
564
                    }
 
565
                    else if(Geom::LineSegment const *lb = dynamic_cast<Geom::LineSegment const*>(cb))
 
566
                    {
 
567
                        if (Geom::HLineSegment const *la = dynamic_cast<Geom::HLineSegment const*>(ca)) {
 
568
                            if (!Geom::are_near((*la).initialPoint(),(*lb).initialPoint(), eps)) {
 
569
                                char temp[200];
 
570
                                sprintf(temp, "Different start of segment: (%g,%g) != (%g,%g), subpath: %u, segment: %u", (*la).initialPoint()[Geom::X], (*la).initialPoint()[Geom::Y], (*lb).initialPoint()[Geom::X], (*lb).initialPoint()[Geom::Y], i, j);
 
571
                                TS_FAIL(temp);
 
572
                                return false;
 
573
                            }
 
574
                            if (!Geom::are_near((*la).finalPoint(),(*lb).finalPoint(), eps)) {
 
575
                                char temp[200];
 
576
                                sprintf(temp, "Different end of segment: (%g,%g) != (%g,%g), subpath: %u, segment: %u", (*la).finalPoint()[Geom::X], (*la).finalPoint()[Geom::Y], (*lb).finalPoint()[Geom::X], (*lb).finalPoint()[Geom::Y], i, j);
 
577
                                TS_FAIL(temp);
 
578
                                return false;
 
579
                            }
 
580
                            char temp[200];
 
581
                            sprintf(temp, "An HLineSegment and a LineSegment have been considered equal. Subpath: %u, segment: %u", i, j);
 
582
                            TS_TRACE(temp);
 
583
                        } else if (Geom::VLineSegment const *la = dynamic_cast<Geom::VLineSegment const*>(ca)) {
 
584
                            if (!Geom::are_near((*la).initialPoint(),(*lb).initialPoint(), eps)) {
 
585
                                char temp[200];
 
586
                                sprintf(temp, "Different start of segment: (%g,%g) != (%g,%g), subpath: %u, segment: %u", (*la).initialPoint()[Geom::X], (*la).initialPoint()[Geom::Y], (*lb).initialPoint()[Geom::X], (*lb).initialPoint()[Geom::Y], i, j);
 
587
                                TS_FAIL(temp);
 
588
                                return false;
 
589
                            }
 
590
                            if (!Geom::are_near((*la).finalPoint(),(*lb).finalPoint(), eps)) {
 
591
                                char temp[200];
 
592
                                sprintf(temp, "Different end of segment: (%g,%g) != (%g,%g), subpath: %u, segment: %u", (*la).finalPoint()[Geom::X], (*la).finalPoint()[Geom::Y], (*lb).finalPoint()[Geom::X], (*lb).finalPoint()[Geom::Y], i, j);
 
593
                                TS_FAIL(temp);
 
594
                                return false;
 
595
                            }
 
596
                            char temp[200];
 
597
                            sprintf(temp, "A VLineSegment and a LineSegment have been considered equal. Subpath: %u, segment: %u", i, j);
 
598
                            TS_TRACE(temp);
 
599
                        } else {
 
600
                            char temp[200];
 
601
                            sprintf(temp, "Different curve types: %s != %s, subpath: %u, segment: %u", typeid(*ca).name(), typeid(*cb).name(), i, j);
 
602
                            TS_FAIL(temp);
 
603
                            return false;
 
604
                        }
 
605
                    } else {
 
606
                        char temp[200];
 
607
                        sprintf(temp, "Different curve types: %s != %s, subpath: %u, segment: %u", typeid(*ca).name(), typeid(*cb).name(), i, j);
 
608
                        TS_FAIL(temp);
 
609
                    }
 
610
                }
 
611
            }
 
612
        }
 
613
        return true;
 
614
    }
 
615
};
 
616
 
 
617
 
 
618
/*
 
619
  Local Variables:
 
620
  mode:c++
 
621
  c-file-style:"stroustrup"
 
622
  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
 
623
  indent-tabs-mode:nil
 
624
  fill-column:99
 
625
  End:
 
626
*/
 
627
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :