1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2 /* libwps
3  * Version: MPL 2.0 / LGPLv2.1+
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * Major Contributor(s):
10  * Copyright (C) 2002, 2005 William Lachance (william.lachance@sympatico.ca)
11  * Copyright (C) 2002, 2004 Marc Maurer (uwog@uwog.net)
12  *
13  * For minor contributions see the git repository.
14  *
15  * Alternatively, the contents of this file may be used under the terms
16  * of the GNU Lesser General Public License Version 2.1 or later
17  * (LGPLv2.1+), in which case the provisions of the LGPLv2.1+ are
18  * applicable instead of those above.
19  *
20  * For further information visit http://libwps.sourceforge.net
21  */
22 
23 #include <string.h>
24 
25 #include <cmath>
26 #include <cstring>
27 #include <iomanip>
28 #include <iostream>
29 #include <sstream>
30 #include <string>
31 
32 #include <librevenge/librevenge.h>
33 
34 #include "libwps_internal.h"
35 
36 #include "WPSGraphicShape.h"
37 
38 ////////////////////////////////////////////////////////////
39 // WPSGraphicShape::PathData
40 ////////////////////////////////////////////////////////////
operator <<(std::ostream & o,WPSGraphicShape::PathData const & path)41 std::ostream &operator<<(std::ostream &o, WPSGraphicShape::PathData const &path)
42 {
43 	o << path.m_type;
44 	switch (path.m_type)
45 	{
46 	case 'H':
47 		o << ":" << path.m_x[0];
48 		break;
49 	case 'V':
50 		o << ":" << path.m_x[1];
51 		break;
52 	case 'M':
53 	case 'L':
54 	case 'T':
55 		o << ":" << path.m_x;
56 		break;
57 	case 'Q':
58 	case 'S':
59 		o << ":" << path.m_x << ":" << path.m_x1;
60 		break;
61 	case 'C':
62 		o << ":" << path.m_x << ":" << path.m_x1 << ":" << path.m_x2;
63 		break;
64 	case 'A':
65 		o << ":" << path.m_x << ":r=" << path.m_r;
66 		if (path.m_largeAngle) o << ":largeAngle";
67 		if (path.m_sweep) o << ":sweep";
68 		if (path.m_rotate<0 || path.m_rotate>0) o << ":rot=" << path.m_rotate;
69 		break;
70 	case 'Z':
71 		break;
72 	default:
73 		o << "###";
74 	}
75 	return o;
76 }
77 
translate(Vec2f const & decal)78 void WPSGraphicShape::PathData::translate(Vec2f const &decal)
79 {
80 	if (m_type=='Z')
81 		return;
82 	m_x += decal;
83 	if (m_type=='H' || m_type=='V' || m_type=='M' || m_type=='L' || m_type=='T' || m_type=='A')
84 		return;
85 	m_x1 += decal;
86 	if (m_type=='Q' || m_type=='S')
87 		return;
88 	m_x2 += decal;
89 }
90 
scale(Vec2f const & scaling)91 void WPSGraphicShape::PathData::scale(Vec2f const &scaling)
92 {
93 	if (m_type=='Z')
94 		return;
95 	m_x = Vec2f(m_x[0]*scaling[0], m_x[1]*scaling[1]);
96 	if (m_type=='H' || m_type=='V' || m_type=='M' || m_type=='L' || m_type=='T' || m_type=='A')
97 		return;
98 	m_x1 = Vec2f(m_x1[0]*scaling[0], m_x1[1]*scaling[1]);
99 	if (m_type=='Q' || m_type=='S')
100 		return;
101 	m_x2 = Vec2f(m_x2[0]*scaling[0], m_x2[1]*scaling[1]);
102 }
103 
rotate(float angle,Vec2f const & decal)104 void WPSGraphicShape::PathData::rotate(float angle, Vec2f const &decal)
105 {
106 	if (m_type=='Z')
107 		return;
108 	float angl=angle*float(M_PI/180.);
109 	m_x = Vec2f(std::cos(angl)*m_x[0]-std::sin(angl)*m_x[1],
110 	            std::sin(angl)*m_x[0]+std::cos(angl)*m_x[1])+decal;
111 	if (m_type=='A')
112 	{
113 		m_rotate += angle;
114 		return;
115 	}
116 	if (m_type=='H' || m_type=='V' || m_type=='M' || m_type=='L' || m_type=='T')
117 		return;
118 	m_x1 = Vec2f(std::cos(angl)*m_x1[0]-std::sin(angl)*m_x1[1],
119 	             std::sin(angl)*m_x1[0]+std::cos(angl)*m_x1[1])+decal;
120 	if (m_type=='Q' || m_type=='S')
121 		return;
122 	m_x2 = Vec2f(std::cos(angl)*m_x2[0]-std::sin(angl)*m_x2[1],
123 	             std::sin(angl)*m_x2[0]+std::cos(angl)*m_x2[1])+decal;
124 }
125 
transform(WPSTransformation const & matrix,float rotation)126 void WPSGraphicShape::PathData::transform(WPSTransformation const &matrix, float rotation)
127 {
128 	if (m_type=='Z')
129 		return;
130 	m_x = matrix*m_x;
131 	if (m_type=='A')
132 	{
133 		m_rotate += rotation;
134 		return;
135 	}
136 	if (m_type=='H' || m_type=='V' || m_type=='M' || m_type=='L' || m_type=='T')
137 		return;
138 	m_x1 = matrix*m_x1;
139 	if (m_type=='Q' || m_type=='S')
140 		return;
141 	m_x2 = matrix*m_x2;
142 }
143 
get(librevenge::RVNGPropertyList & list,Vec2f const & orig) const144 bool WPSGraphicShape::PathData::get(librevenge::RVNGPropertyList &list, Vec2f const &orig) const
145 {
146 	list.clear();
147 	std::string type("");
148 	type += m_type;
149 	list.insert("librevenge:path-action", type.c_str());
150 	if (m_type=='Z')
151 		return true;
152 	if (m_type=='H')
153 	{
154 		list.insert("svg:x",double(m_x[0]-orig[0]), librevenge::RVNG_POINT);
155 		return true;
156 	}
157 	if (m_type=='V')
158 	{
159 		list.insert("svg:y",double(m_x[1]-orig[1]), librevenge::RVNG_POINT);
160 		return true;
161 	}
162 	list.insert("svg:x",double(m_x[0]-orig[0]), librevenge::RVNG_POINT);
163 	list.insert("svg:y",double(m_x[1]-orig[1]), librevenge::RVNG_POINT);
164 	if (m_type=='M' || m_type=='L' || m_type=='T')
165 		return true;
166 	if (m_type=='A')
167 	{
168 		list.insert("svg:rx",double(m_r[0]), librevenge::RVNG_POINT);
169 		list.insert("svg:ry",double(m_r[1]), librevenge::RVNG_POINT);
170 		list.insert("librevenge:large-arc", m_largeAngle);
171 		list.insert("librevenge:sweep", m_sweep);
172 		list.insert("librevenge:rotate", double(m_rotate), librevenge::RVNG_GENERIC);
173 		return true;
174 	}
175 	list.insert("svg:x1",double(m_x1[0]-orig[0]), librevenge::RVNG_POINT);
176 	list.insert("svg:y1",double(m_x1[1]-orig[1]), librevenge::RVNG_POINT);
177 	if (m_type=='Q' || m_type=='S')
178 		return true;
179 	list.insert("svg:x2",double(m_x2[0]-orig[0]), librevenge::RVNG_POINT);
180 	list.insert("svg:y2",double(m_x2[1]-orig[1]), librevenge::RVNG_POINT);
181 	if (m_type=='C')
182 		return true;
183 	WPS_DEBUG_MSG(("WPSGraphicShape::PathData::get: unknown command %c\n", m_type));
184 	list.clear();
185 	return false;
186 }
187 
cmp(WPSGraphicShape::PathData const & a) const188 int WPSGraphicShape::PathData::cmp(WPSGraphicShape::PathData const &a) const
189 {
190 	if (m_type < a.m_type) return 1;
191 	if (m_type > a.m_type) return 1;
192 	int diff = m_x.cmp(a.m_x);
193 	if (diff) return diff;
194 	diff = m_x1.cmp(a.m_x1);
195 	if (diff) return diff;
196 	diff = m_x2.cmp(a.m_x2);
197 	if (diff) return diff;
198 	diff = m_r.cmp(a.m_r);
199 	if (diff) return diff;
200 	if (m_rotate < a.m_rotate) return 1;
201 	if (m_rotate > a.m_rotate) return -1;
202 	if (m_largeAngle != a.m_largeAngle)
203 		return m_largeAngle ? 1 : -1;
204 	if (m_sweep != a.m_sweep)
205 		return m_sweep ? 1 : -1;
206 	return 0;
207 }
208 
209 ////////////////////////////////////////////////////////////
210 // WPSGraphicShape
211 ////////////////////////////////////////////////////////////
line(Vec2f const & orig,Vec2f const & dest)212 WPSGraphicShape WPSGraphicShape::line(Vec2f const &orig, Vec2f const &dest)
213 {
214 	WPSGraphicShape res;
215 	res.m_type = WPSGraphicShape::Line;
216 	res.m_vertices.resize(2);
217 	res.m_vertices[0]=orig;
218 	res.m_vertices[1]=dest;
219 	Vec2f minPt(orig), maxPt(orig);
220 	for (int c=0; c<2; ++c)
221 	{
222 		if (orig[c] < dest[c])
223 			maxPt[c]=dest[c];
224 		else
225 			minPt[c]=dest[c];
226 	}
227 	res.m_bdBox=WPSBox2f(minPt,maxPt);
228 	return res;
229 }
230 
operator <<(std::ostream & o,WPSGraphicShape const & sh)231 std::ostream &operator<<(std::ostream &o, WPSGraphicShape const &sh)
232 {
233 	o << "box=" << sh.m_bdBox << ",";
234 	switch (sh.m_type)
235 	{
236 	case WPSGraphicShape::Line:
237 		o << "line,";
238 		if (sh.m_vertices.size()!=2)
239 			o << "###pts,";
240 		else
241 			o << "pts=" << sh.m_vertices[0] << "<->" << sh.m_vertices[1] << ",";
242 		break;
243 	case WPSGraphicShape::Rectangle:
244 		o << "rect,";
245 		if (sh.m_formBox!=sh.m_bdBox)
246 			o << "box[rect]=" << sh.m_formBox << ",";
247 		if (sh.m_cornerWidth!=Vec2f(0,0))
248 			o << "corners=" << sh.m_cornerWidth << ",";
249 		break;
250 	case WPSGraphicShape::Circle:
251 		o << "circle,";
252 		break;
253 	case WPSGraphicShape::Arc:
254 	case WPSGraphicShape::Pie:
255 		o << (sh.m_type == WPSGraphicShape::Arc ? "arc," : "pie,");
256 		o << "box[ellipse]=" << sh.m_formBox << ",";
257 		o << "angle=" << sh.m_arcAngles << ",";
258 		break;
259 	case WPSGraphicShape::Polygon:
260 		o << "polygons,pts=[";
261 		for (auto const &pt : sh.m_vertices)
262 			o << pt << ",";
263 		o << "],";
264 		break;
265 	case WPSGraphicShape::Polyline:
266 		o << "polyline,pts=[";
267 		for (auto const &pt : sh.m_vertices)
268 			o << pt << ",";
269 		o << "],";
270 		break;
271 	case WPSGraphicShape::Path:
272 		o << "path,pts=[";
273 		for (auto const &pt : sh.m_path)
274 			o << pt << ",";
275 		o << "],";
276 		break;
277 	case WPSGraphicShape::ShapeUnknown:
278 	default:
279 		o << "###unknwown[shape],";
280 		break;
281 	}
282 	o << sh.m_extra;
283 	return o;
284 }
285 
cmp(WPSGraphicShape const & a) const286 int WPSGraphicShape::cmp(WPSGraphicShape const &a) const
287 {
288 	if (m_type < a.m_type) return 1;
289 	if (m_type > a.m_type) return -1;
290 	int diff = m_bdBox.cmp(a.m_bdBox);
291 	if (diff) return diff;
292 	diff = m_formBox.cmp(a.m_formBox);
293 	if (diff) return diff;
294 	diff = m_cornerWidth.cmp(a.m_cornerWidth);
295 	if (diff) return diff;
296 	diff = m_arcAngles.cmp(a.m_arcAngles);
297 	if (diff) return diff;
298 	if (m_vertices.size()<a.m_vertices.size()) return -1;
299 	if (m_vertices.size()>a.m_vertices.size()) return -1;
300 	for (size_t pt=0; pt < m_vertices.size(); ++pt)
301 	{
302 		diff = m_vertices[pt].cmp(a.m_vertices[pt]);
303 		if (diff) return diff;
304 	}
305 	if (m_path.size()<a.m_path.size()) return -1;
306 	if (m_path.size()>a.m_path.size()) return -1;
307 	for (size_t pt=0; pt < m_path.size(); ++pt)
308 	{
309 		diff = m_path[pt].cmp(a.m_path[pt]);
310 		if (diff) return diff;
311 	}
312 	return 0;
313 }
314 
translate(Vec2f const & decal)315 void WPSGraphicShape::translate(Vec2f const &decal)
316 {
317 	if (decal==Vec2f(0,0))
318 		return;
319 	m_bdBox=WPSBox2f(m_bdBox.min()+decal, m_bdBox.max()+decal);
320 	m_formBox=WPSBox2f(m_formBox.min()+decal, m_formBox.max()+decal);
321 	for (auto &pt : m_vertices)
322 		pt+=decal;
323 	for (auto &pt : m_path)
324 		pt.translate(decal);
325 }
326 
scale(Vec2f const & scaling)327 void WPSGraphicShape::scale(Vec2f const &scaling)
328 {
329 	m_bdBox=WPSBox2f(Vec2f(scaling[0]*m_bdBox.min()[0],scaling[1]*m_bdBox.min()[1]),
330 	                 Vec2f(scaling[0]*m_bdBox.max()[0],scaling[1]*m_bdBox.max()[1]));
331 	m_formBox=WPSBox2f(Vec2f(scaling[0]*m_formBox.min()[0],scaling[1]*m_formBox.min()[1]),
332 	                   Vec2f(scaling[0]*m_formBox.max()[0],scaling[1]*m_formBox.max()[1]));
333 	for (auto &vertex : m_vertices)
334 		vertex=Vec2f(scaling[0]*vertex[0],
335 		             scaling[1]*vertex[1]);
336 	for (auto &pt : m_path)
337 		pt.scale(scaling);
338 }
339 
rotate(float angle,Vec2f const & center) const340 WPSGraphicShape WPSGraphicShape::rotate(float angle, Vec2f const &center) const
341 {
342 	while (angle >= 360) angle -= 360;
343 	while (angle <= -360) angle += 360;
344 	if (angle >= -1e-3f && angle <= 1e-3f) return *this;
345 	float angl=angle*float(M_PI/180.);
346 	Vec2f decal=center-Vec2f(std::cos(angl)*center[0]-std::sin(angl)*center[1],
347 	                         std::sin(angl)*center[0]+std::cos(angl)*center[1]);
348 	WPSBox2f fBox;
349 	for (int i=0; i < 4; ++i)
350 	{
351 		Vec2f pt=Vec2f(m_bdBox[i%2][0],m_bdBox[i/2][1]);
352 		pt = Vec2f(std::cos(angl)*pt[0]-std::sin(angl)*pt[1],
353 		           std::sin(angl)*pt[0]+std::cos(angl)*pt[1])+decal;
354 		if (i==0) fBox=WPSBox2f(pt,pt);
355 		else fBox=fBox.getUnion(WPSBox2f(pt,pt));
356 	}
357 	WPSGraphicShape res = path(fBox);
358 	res.m_path=getPath(false);
359 	for (auto &pt : res.m_path)
360 		pt.rotate(angle, decal);
361 	return res;
362 }
363 
transform(WPSTransformation const & matrix) const364 WPSGraphicShape WPSGraphicShape::transform(WPSTransformation const &matrix) const
365 {
366 	if (matrix.isIdentity()) return *this;
367 	if (matrix[0][1]<=0 && matrix[0][1]>=0 && matrix[1][0]<=0 && matrix[1][0]>=0)
368 	{
369 		WPSGraphicShape res=*this;
370 		if (matrix[0][0]<1 || matrix[0][0]>1 || matrix[1][1]<1 || matrix[1][1]>1)
371 			res.scale(Vec2f(matrix[0][0], matrix[1][1]));
372 		res.translate(Vec2f(matrix[0][2],matrix[1][2]));
373 		return res;
374 	}
375 
376 	WPSBox2f fBox;
377 	for (int i=0; i < 4; ++i)
378 	{
379 		Vec2f pt = matrix*Vec2f(m_bdBox[i%2][0],m_bdBox[i/2][1]);
380 		if (i==0) fBox=WPSBox2f(pt,pt);
381 		else fBox=fBox.getUnion(WPSBox2f(pt,pt));
382 	}
383 	WPSGraphicShape res = path(fBox);
384 	res.m_path=getPath(true);
385 
386 	WPSTransformation transf;
387 	float rotation=0;
388 	Vec2f shearing;
389 	if (!matrix.decompose(rotation,shearing,transf,fBox.center()))
390 		rotation=0;
391 	for (auto &p : res.m_path)
392 		p.transform(matrix, rotation);
393 	return res;
394 }
395 
addTo(Vec2f const & orig,bool asSurface,librevenge::RVNGPropertyList & propList) const396 WPSGraphicShape::Command WPSGraphicShape::addTo(Vec2f const &orig, bool asSurface, librevenge::RVNGPropertyList &propList) const
397 {
398 	Vec2f pt;
399 	librevenge::RVNGPropertyList list;
400 	librevenge::RVNGPropertyListVector vect;
401 	Vec2f decal=orig-m_bdBox[0];
402 	switch (m_type)
403 	{
404 	case Line:
405 		if (m_vertices.size()!=2) break;
406 		pt=m_vertices[0]+decal;
407 		list.insert("svg:x",double(pt.x()), librevenge::RVNG_POINT);
408 		list.insert("svg:y",double(pt.y()), librevenge::RVNG_POINT);
409 		vect.append(list);
410 		pt=m_vertices[1]+decal;
411 		list.insert("svg:x",double(pt.x()), librevenge::RVNG_POINT);
412 		list.insert("svg:y",double(pt.y()), librevenge::RVNG_POINT);
413 		vect.append(list);
414 		propList.insert("svg:points", vect);
415 		return C_Polyline;
416 	case Rectangle:
417 		if (m_cornerWidth[0] > 0 && m_cornerWidth[1] > 0)
418 		{
419 			propList.insert("svg:rx",double(m_cornerWidth[0]), librevenge::RVNG_POINT);
420 			propList.insert("svg:ry",double(m_cornerWidth[1]), librevenge::RVNG_POINT);
421 		}
422 		pt=m_formBox[0]+decal;
423 		propList.insert("svg:x",double(pt.x()), librevenge::RVNG_POINT);
424 		propList.insert("svg:y",double(pt.y()), librevenge::RVNG_POINT);
425 		pt=m_formBox.size();
426 		propList.insert("svg:width",double(pt.x()), librevenge::RVNG_POINT);
427 		propList.insert("svg:height",double(pt.y()), librevenge::RVNG_POINT);
428 		return C_Rectangle;
429 	case Circle:
430 		pt=0.5*(m_formBox[0]+m_formBox[1])+decal;
431 		propList.insert("svg:cx",double(pt.x()), librevenge::RVNG_POINT);
432 		propList.insert("svg:cy",double(pt.y()), librevenge::RVNG_POINT);
433 		pt=0.5*(m_formBox[1]-m_formBox[0]);
434 		propList.insert("svg:rx",double(pt.x()), librevenge::RVNG_POINT);
435 		propList.insert("svg:ry",double(pt.y()), librevenge::RVNG_POINT);
436 		return C_Ellipse;
437 	case Arc:
438 	case Pie:
439 	{
440 		Vec2f center=0.5*(m_formBox[0]+m_formBox[1])+decal;
441 		Vec2f rad=0.5*(m_formBox[1]-m_formBox[0]);
442 		float angl0=m_arcAngles[0];
443 		float angl1=m_arcAngles[1];
444 		if (rad[1]<0)
445 		{
446 			static bool first=true;
447 			if (first)
448 			{
449 				WPS_DEBUG_MSG(("WPSGraphicShape::addTo: oops radiusY for arc is negative, inverse it\n"));
450 				first=false;
451 			}
452 			rad[1]=-rad[1];
453 		}
454 		while (angl1<angl0)
455 			angl1+=360.f;
456 		while (angl1>angl0+360.f)
457 			angl1-=360.f;
458 		if (angl1-angl0>=180.f && angl1-angl0<=180.f)
459 			angl1+=0.01f;
460 		float angl=angl0*float(M_PI/180.);
461 		bool addCenter=m_type==Pie && asSurface;
462 		if (addCenter)
463 		{
464 			pt=center;
465 			list.insert("librevenge:path-action", "M");
466 			list.insert("svg:x",double(pt.x()), librevenge::RVNG_POINT);
467 			list.insert("svg:y",double(pt.y()), librevenge::RVNG_POINT);
468 			vect.append(list);
469 		}
470 		list.clear();
471 		pt=center+Vec2f(std::cos(angl)*rad[0],-std::sin(angl)*rad[1]);
472 		list.insert("librevenge:path-action", addCenter ? "L" : "M");
473 		list.insert("svg:x",double(pt.x()), librevenge::RVNG_POINT);
474 		list.insert("svg:y",double(pt.y()), librevenge::RVNG_POINT);
475 		vect.append(list);
476 
477 		list.clear();
478 		angl=angl1*float(M_PI/180.);
479 		pt=center+Vec2f(std::cos(angl)*rad[0],-std::sin(angl)*rad[1]);
480 		list.insert("librevenge:path-action", "A");
481 		list.insert("librevenge:large-arc", !(angl1-angl0<180.f));
482 		list.insert("librevenge:sweep", false);
483 		list.insert("svg:rx",double(rad.x()), librevenge::RVNG_POINT);
484 		list.insert("svg:ry",double(rad.y()), librevenge::RVNG_POINT);
485 		list.insert("svg:x",double(pt.x()), librevenge::RVNG_POINT);
486 		list.insert("svg:y",double(pt.y()), librevenge::RVNG_POINT);
487 		vect.append(list);
488 		if (asSurface)
489 		{
490 			list.clear();
491 			list.insert("librevenge:path-action", "Z");
492 			vect.append(list);
493 		}
494 
495 		propList.insert("svg:d", vect);
496 		return C_Path;
497 	}
498 	case Polyline:
499 	case Polygon:
500 	{
501 		size_t n=m_vertices.size();
502 		if (n<2) break;
503 		for (size_t i = 0; i < n; ++i)
504 		{
505 			list.clear();
506 			pt=m_vertices[i]+decal;
507 			list.insert("svg:x", double(pt.x()), librevenge::RVNG_POINT);
508 			list.insert("svg:y", double(pt.y()), librevenge::RVNG_POINT);
509 			vect.append(list);
510 		}
511 		propList.insert("svg:points", vect);
512 		return (m_type==Polygon && asSurface) ? C_Polygon : C_Polyline;
513 	}
514 	case Path:
515 	{
516 		size_t n=m_path.size();
517 		if (!n) break;
518 		for (size_t c=0; c < n; ++c)
519 		{
520 			list.clear();
521 			if (m_path[c].get(list, -1.0f*decal))
522 				vect.append(list);
523 		}
524 		if (asSurface && m_path[n-1].m_type != 'Z')
525 		{
526 			// odg need a closed path to draw surface, so ...
527 			list.clear();
528 			list.insert("librevenge:path-action", "Z");
529 			vect.append(list);
530 		}
531 		propList.insert("svg:d", vect);
532 		return C_Path;
533 	}
534 	case ShapeUnknown:
535 	default:
536 		break;
537 	}
538 	WPS_DEBUG_MSG(("WPSGraphicShape::addTo: can not send a shape with type=%d\n", int(m_type)));
539 	return C_Bad;
540 }
541 
getPath(bool forTransformation) const542 std::vector<WPSGraphicShape::PathData> WPSGraphicShape::getPath(bool forTransformation) const
543 {
544 	std::vector<WPSGraphicShape::PathData> res;
545 	float const delta=0.55228f;
546 	switch (m_type)
547 	{
548 	case Line:
549 	case Polyline:
550 	case Polygon:
551 	{
552 		size_t n=m_vertices.size();
553 		if (n<2) break;
554 		res.push_back(PathData('M',m_vertices[0]));
555 		for (size_t i = 1; i < n; ++i)
556 			res.push_back(PathData('L', m_vertices[i]));
557 		break;
558 	}
559 	case Rectangle:
560 		if (m_cornerWidth[0] > 0 && m_cornerWidth[1] > 0)
561 		{
562 			WPSBox2f box=m_formBox;
563 			Vec2f c=m_cornerWidth;
564 			if (2*c[0]>box.size()[0]) c[0]=0.5f*box.size()[0];
565 			if (2*c[1]>box.size()[1]) c[1]=0.5f*box.size()[1];
566 			if (forTransformation)
567 			{
568 				Vec2f pt0(box[1][0]-c[0],box[0][1]);
569 				res.push_back(PathData('M',pt0));
570 				Vec2f pt1(box[1][0],box[0][1]+c[1]);
571 				res.push_back(PathData('C',pt1,pt0+Vec2f(delta*c[0],0),pt1-Vec2f(0,delta*c[1])));
572 				pt0=Vec2f(box[1][0],box[1][1]-c[1]);
573 				res.push_back(PathData('L',pt0));
574 				pt1=Vec2f(box[1][0]-c[0],box[1][1]);
575 				res.push_back(PathData('C',pt1,pt0+Vec2f(0,delta*c[1]),pt1+Vec2f(delta*c[0],0)));
576 				pt0=Vec2f(box[0][0]+c[0],box[1][1]);
577 				res.push_back(PathData('L',pt0));
578 				pt1=Vec2f(box[0][0],box[1][1]-c[1]);
579 				res.push_back(PathData('C',pt1,pt0-Vec2f(delta*c[0],0),pt1+Vec2f(0,delta*c[1])));
580 				pt0=Vec2f(box[0][0],box[0][1]+c[1]);
581 				res.push_back(PathData('L',pt0));
582 				pt1=Vec2f(box[0][0]+c[0],box[0][1]);
583 				res.push_back(PathData('C',pt1,pt0-Vec2f(0,delta*c[1]),pt1-Vec2f(delta*c[0],0)));
584 			}
585 			else
586 			{
587 				res.push_back(PathData('M',Vec2f(box[1][0]-c[0],box[0][1])));
588 				PathData data('A',Vec2f(box[1][0],box[0][1]+c[1]));
589 				data.m_r=c;
590 				data.m_sweep=true;
591 				res.push_back(data);
592 				res.push_back(PathData('L',Vec2f(box[1][0],box[1][1]-c[1])));
593 				data.m_x=Vec2f(box[1][0]-c[0],box[1][1]);
594 				res.push_back(data);
595 				res.push_back(PathData('L',Vec2f(box[0][0]+c[0],box[1][1])));
596 				data.m_x=Vec2f(box[0][0],box[1][1]-c[1]);
597 				res.push_back(data);
598 				res.push_back(PathData('L',Vec2f(box[0][0],box[0][1]+c[1])));
599 				data.m_x=Vec2f(box[0][0]+c[0],box[0][1]);
600 				res.push_back(data);
601 			}
602 			res.push_back(PathData('Z'));
603 			break;
604 		}
605 		res.push_back(PathData('M',m_formBox[0]));
606 		res.push_back(PathData('L',Vec2f(m_formBox[0][0],m_formBox[1][1])));
607 		res.push_back(PathData('L',m_formBox[1]));
608 		res.push_back(PathData('L',Vec2f(m_formBox[1][0],m_formBox[0][1])));
609 		res.push_back(PathData('Z'));
610 		break;
611 	case Circle:
612 	{
613 		if (forTransformation)
614 		{
615 			Vec2f center=m_formBox.center();
616 			Vec2f dir=0.5f*delta*(m_formBox[1]-m_formBox[0]);
617 			Vec2f pt0(m_formBox[0][0],center[1]);
618 			res.push_back(PathData('M',pt0));
619 			Vec2f pt1(center[0],m_formBox[0][1]);
620 			res.push_back(PathData('C',pt1, pt0-Vec2f(0,dir[1]), pt1-Vec2f(dir[0],0)));
621 			pt0=Vec2f(m_formBox[1][0],center[1]);
622 			res.push_back(PathData('C',pt0, pt1+Vec2f(dir[0],0), pt0-Vec2f(0,dir[1])));
623 			pt1=Vec2f(center[0],m_formBox[1][1]);
624 			res.push_back(PathData('C',pt1, pt0+Vec2f(0,dir[1]), pt1+Vec2f(dir[0],0)));
625 			pt0=Vec2f(m_formBox[0][0],center[1]);
626 			res.push_back(PathData('C',pt0, pt1-Vec2f(dir[0],0), pt0+Vec2f(0,dir[1])));
627 			res.push_back(PathData('Z'));
628 		}
629 		else
630 		{
631 			Vec2f pt0 = Vec2f(m_formBox[0][0],0.5f*(m_formBox[0][1]+m_formBox[1][1]));
632 			Vec2f pt1 = Vec2f(m_formBox[1][0],pt0[1]);
633 			res.push_back(PathData('M',pt0));
634 			PathData data('A',pt1);
635 			data.m_r=0.5*(m_formBox[1]-m_formBox[0]);
636 			data.m_largeAngle=true;
637 			res.push_back(data);
638 			data.m_x=pt0;
639 			res.push_back(data);
640 			break;
641 		}
642 	}
643 	WPS_FALLTHROUGH;
644 	case Arc:
645 	case Pie:
646 	{
647 		Vec2f center=0.5*(m_formBox[0]+m_formBox[1]);
648 		Vec2f rad=0.5*(m_formBox[1]-m_formBox[0]);
649 		float angl0=m_arcAngles[0];
650 		float angl1=m_arcAngles[1];
651 		if (rad[1]<0)
652 		{
653 			static bool first=true;
654 			if (first)
655 			{
656 				WPS_DEBUG_MSG(("WPSGraphicShape::getPath: oops radiusY for arc is negative, inverse it\n"));
657 				first=false;
658 			}
659 			rad[1]=-rad[1];
660 		}
661 		while (angl1<angl0)
662 			angl1+=360.f;
663 		while (angl1>angl0+360.f)
664 			angl1-=360.f;
665 		if (angl1-angl0>=180.f && angl1-angl0<=180.f)
666 			angl1+=0.01f;
667 		float angl=angl0*float(M_PI/180.);
668 		bool addCenter=m_type==Pie;
669 		if (addCenter)
670 			res.push_back(PathData('M', center));
671 		Vec2f pt=center+Vec2f(std::cos(angl)*rad[0],-std::sin(angl)*rad[1]);
672 		res.push_back(PathData(addCenter ? 'L' : 'M', pt));
673 		if (!forTransformation)
674 		{
675 			angl=angl1*float(M_PI/180.);
676 			pt=center+Vec2f(std::cos(angl)*rad[0],-std::sin(angl)*rad[1]);
677 			PathData data('A',pt);
678 			data.m_largeAngle=(angl1-angl0>=180.f);
679 			data.m_r=rad;
680 			res.push_back(data);
681 		}
682 		else
683 		{
684 			int N=int(angl1-angl0)/90;
685 			float dAngle=float(angl1-angl0)/float(N+1);
686 			for (int i=0; i<=N; ++i)
687 			{
688 				float newAngl= i==N ? angl1 : angl0+float(i+1)*dAngle;
689 				newAngl*=float(M_PI/180.);
690 				Vec2f newPt=center+Vec2f(std::cos(angl1)*rad[0],-std::sin(angl1)*rad[1]);
691 				Vec2f dir(-std::sin(angl)*rad[0],-std::cos(angl)*rad[1]);
692 				Vec2f newDir(-std::sin(newAngl)*rad[0],-std::cos(newAngl)*rad[1]);
693 				float deltaDir=4/3.f*std::tan((newAngl-angl)/4);
694 				res.push_back(PathData('C',newPt,pt+deltaDir*dir,newPt-deltaDir*newDir));
695 				pt=newPt;
696 				angl=newAngl;
697 			}
698 			if (m_type==Pie) res.push_back(PathData('Z'));
699 		}
700 		break;
701 	}
702 	case Path:
703 		return m_path;
704 	case ShapeUnknown:
705 	default:
706 		WPS_DEBUG_MSG(("WPSGraphicShape::getPath: unexpected type\n"));
707 		break;
708 	}
709 	return res;
710 }
711 /* vim:set shiftwidth=4 softtabstop=4 noexpandtab: */
712