1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2 /* librevenge
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) 2006 Ariya Hidayat (ariya@kde.org)
11  * Copyright (C) 2005 Fridrich Strba (fridrich.strba@bluewin.ch)
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 
21 #include <stdio.h>
22 
23 #include <iostream>
24 #include <map>
25 #include <sstream>
26 #include <string>
27 #include <vector>
28 
29 #include <boost/shared_ptr.hpp>
30 
31 #include <librevenge-generators/librevenge-generators.h>
32 
33 #include "librevenge_internal.h"
34 
35 namespace librevenge
36 {
37 
38 namespace DrawingSVG
39 {
40 
getInchValue(librevenge::RVNGProperty const & prop)41 static double getInchValue(librevenge::RVNGProperty const &prop)
42 {
43 	double value=prop.getDouble();
44 	switch (prop.getUnit())
45 	{
46 	case librevenge::RVNG_GENERIC: // assume inch
47 	case librevenge::RVNG_INCH:
48 		return value;
49 	case librevenge::RVNG_POINT:
50 		value /= 72.;
51 		return value;
52 	case librevenge::RVNG_TWIP:
53 		value /= 1440.;
54 		return value;
55 	case librevenge::RVNG_PERCENT:
56 	case librevenge::RVNG_UNIT_ERROR:
57 	default:
58 	{
59 		static bool first=true;
60 		if (first)
61 		{
62 			RVNG_DEBUG_MSG(("librevenge::getInchValue: call with no double value\n"));
63 			first=false;
64 		}
65 		break;
66 	}
67 	}
68 	return value;
69 }
70 
doubleToString(const double value)71 static std::string doubleToString(const double value)
72 {
73 	RVNGProperty *prop = RVNGPropertyFactory::newDoubleProp(value);
74 	std::string retVal = prop->getStr().cstr();
75 	delete prop;
76 	return retVal;
77 }
78 
stringToColour(const RVNGString & s)79 static unsigned stringToColour(const RVNGString &s)
80 {
81 	std::string str(s.cstr());
82 	if (str[0] == '#')
83 	{
84 		if (str.length() != 7)
85 			return 0;
86 		else
87 			str.erase(str.begin());
88 	}
89 	else
90 		return 0;
91 
92 	std::istringstream istr(str);
93 	unsigned val = 0;
94 	istr >> std::hex >> val;
95 	return val;
96 }
97 
98 //! basic class used to store table information
99 struct Table
100 {
101 	//! constructor
Tablelibrevenge::DrawingSVG::Table102 	Table(const RVNGPropertyList &propList) : m_column(0), m_row(0), m_x(0), m_y(0), m_columnsDistanceFromOrigin(), m_rowsDistanceFromOrigin()
103 	{
104 		if (propList["svg:x"])
105 			m_x=getInchValue(*propList["svg:x"]);
106 		if (propList["svg:y"])
107 			m_y=getInchValue(*propList["svg:y"]);
108 		// we do not actually use height/width, so...
109 
110 		m_columnsDistanceFromOrigin.push_back(0);
111 		m_rowsDistanceFromOrigin.push_back(0);
112 
113 		const librevenge::RVNGPropertyListVector *columns = propList.child("librevenge:table-columns");
114 		if (columns)
115 		{
116 			double actDist=0;
117 			for (unsigned long i=0; i<columns->count(); ++i)
118 			{
119 				if ((*columns)[i]["style:column-width"])
120 					actDist+=getInchValue(*(*columns)[i]["style:column-width"]);
121 				m_columnsDistanceFromOrigin.push_back(actDist);
122 			}
123 		}
124 		else
125 		{
126 			RVNG_DEBUG_MSG(("librevenge::DrawingSVG::Table::Table: can not find any columns\n"));
127 		}
128 	}
129 	//! calls to open a row
openRowlibrevenge::DrawingSVG::Table130 	void openRow(const RVNGPropertyList &propList)
131 	{
132 		double height=0;
133 		if (propList["style:row-height"])
134 			height=getInchValue(*propList["style:row-height"]);
135 		// changeme
136 		else if (propList["style:min-row-height"])
137 			height=getInchValue(*propList["style:min-row-height"]);
138 		else
139 		{
140 			RVNG_DEBUG_MSG(("librevenge::DrawingSVG::Table::openRow: can not determine row height\n"));
141 		}
142 		m_rowsDistanceFromOrigin.push_back(m_rowsDistanceFromOrigin.back()+height);
143 	}
144 	//! call to close a row
closeRowlibrevenge::DrawingSVG::Table145 	void closeRow()
146 	{
147 		++m_row;
148 	}
149 	//! returns the position of a cellule
getPositionlibrevenge::DrawingSVG::Table150 	bool getPosition(int column, int row, double &x, double &y) const
151 	{
152 		bool ok=true;
153 		if (column>=0 && column <int(m_columnsDistanceFromOrigin.size()))
154 			x=m_x+m_columnsDistanceFromOrigin[size_t(column)];
155 		else
156 		{
157 			ok=false;
158 			RVNG_DEBUG_MSG(("librevenge::DrawingSVG::Table::getPosition: the column %d seems bad\n", column));
159 			x=(column<0 || m_columnsDistanceFromOrigin.empty()) ? m_x : m_x + m_columnsDistanceFromOrigin.back();
160 		}
161 		if (row>=0 && row <int(m_rowsDistanceFromOrigin.size()))
162 			y=m_y+m_rowsDistanceFromOrigin[size_t(row)];
163 		else
164 		{
165 			ok=false;
166 			RVNG_DEBUG_MSG(("librevenge::DrawingSVG::Table::getPosition: the row %d seems bad\n", row));
167 			y=(row<0 || m_rowsDistanceFromOrigin.empty()) ? m_y : m_y + m_rowsDistanceFromOrigin.back();
168 		}
169 		return ok;
170 	}
171 	//! the actual column
172 	int m_column;
173 	//! the actual row
174 	int m_row;
175 	//! the origin table position in inches
176 	double m_x, m_y;
177 	//! the distance of each begin column in inches from origin
178 	std::vector<double> m_columnsDistanceFromOrigin;
179 	//! the distance of each begin row in inches from origin
180 	std::vector<double> m_rowsDistanceFromOrigin;
181 };
182 
183 } // DrawingSVG namespace
184 
185 using namespace DrawingSVG;
186 
187 struct RVNGSVGDrawingGeneratorPrivate
188 {
189 	RVNGSVGDrawingGeneratorPrivate(RVNGStringVector &vec, const RVNGString &nmSpace);
190 
191 	void setStyle(const RVNGPropertyList &propList);
192 	void writeStyle(bool isClosed=true);
193 	void drawPolySomething(const RVNGPropertyListVector &vertices, bool isClosed);
194 
195 	//! return the namespace and the delimiter
getNamespaceAndDelimlibrevenge::RVNGSVGDrawingGeneratorPrivate196 	std::string const &getNamespaceAndDelim() const
197 	{
198 		return m_nmSpaceAndDelim;
199 	}
200 
201 	std::map<int, RVNGPropertyList> m_idSpanMap;
202 
203 	RVNGPropertyListVector m_gradient;
204 	RVNGPropertyList m_style;
205 	int m_gradientIndex, m_shadowIndex;
206 	//! index uses when fill=bitmap
207 	int m_patternIndex;
208 	int m_arrowStartIndex /** start arrow index*/, m_arrowEndIndex /** end arrow index */;
209 	//! groupId used if svg:id is not defined when calling openGroup
210 	int m_groupId;
211 	//! layerId used if svg:id is not defined when calling startLayer
212 	int m_layerId;
213 	//! a prefix used to define the svg namespace
214 	std::string m_nmSpace;
215 	//! a prefix used to define the svg namespace with delimiter
216 	std::string m_nmSpaceAndDelim;
217 	std::ostringstream m_outputSink;
218 	RVNGStringVector &m_vec;
219 	//! the actual master name
220 	RVNGString m_masterName;
221 	//! a map master name to master content
222 	std::map<RVNGString, std::string> m_masterNameToContentMap;
223 	//! the actual opened table
224 	boost::shared_ptr<Table> m_table;
225 };
226 
RVNGSVGDrawingGeneratorPrivate(RVNGStringVector & vec,const RVNGString & nmSpace)227 RVNGSVGDrawingGeneratorPrivate::RVNGSVGDrawingGeneratorPrivate(RVNGStringVector &vec, const RVNGString &nmSpace) :
228 	m_idSpanMap(),
229 	m_gradient(),
230 	m_style(),
231 	m_gradientIndex(1),
232 	m_shadowIndex(1),
233 	m_patternIndex(1),
234 	m_arrowStartIndex(1),
235 	m_arrowEndIndex(1),
236 	m_groupId(1000), m_layerId(1000),
237 	m_nmSpace(nmSpace.cstr()),
238 	m_nmSpaceAndDelim(""),
239 	m_outputSink(),
240 	m_vec(vec),
241 	m_masterName(), m_masterNameToContentMap(),
242 	m_table()
243 {
244 	if (!m_nmSpace.empty())
245 		m_nmSpaceAndDelim = m_nmSpace+":";
246 }
247 
drawPolySomething(const RVNGPropertyListVector & vertices,bool isClosed)248 void RVNGSVGDrawingGeneratorPrivate::drawPolySomething(const RVNGPropertyListVector &vertices, bool isClosed)
249 {
250 	if (vertices.count() < 2)
251 		return;
252 
253 	if (vertices.count() == 2)
254 	{
255 		if (!vertices[0]["svg:x"]||!vertices[0]["svg:y"]||!vertices[1]["svg:x"]||!vertices[1]["svg:y"])
256 			return;
257 		m_outputSink << "<" << getNamespaceAndDelim() << "line ";
258 		m_outputSink << "x1=\"" << doubleToString(72*getInchValue(*vertices[0]["svg:x"])) << "\"  y1=\"" << doubleToString(72*getInchValue(*vertices[0]["svg:y"])) << "\" ";
259 		m_outputSink << "x2=\"" << doubleToString(72*getInchValue(*vertices[1]["svg:x"])) << "\"  y2=\"" << doubleToString(72*getInchValue(*vertices[1]["svg:y"])) << "\"\n";
260 		writeStyle();
261 		m_outputSink << "/>\n";
262 	}
263 	else
264 	{
265 		if (isClosed)
266 			m_outputSink << "<" << getNamespaceAndDelim() << "polygon ";
267 		else
268 			m_outputSink << "<" << getNamespaceAndDelim() << "polyline ";
269 
270 		m_outputSink << "points=\"";
271 		for (unsigned i = 0; i < vertices.count(); i++)
272 		{
273 			if (!vertices[i]["svg:x"]||!vertices[i]["svg:y"])
274 				continue;
275 			m_outputSink << doubleToString(72*getInchValue(*vertices[i]["svg:x"])) << " " << doubleToString(72*getInchValue(*vertices[i]["svg:y"]));
276 			if (i < vertices.count()-1)
277 				m_outputSink << ", ";
278 		}
279 		m_outputSink << "\"\n";
280 		writeStyle(isClosed);
281 		m_outputSink << "/>\n";
282 	}
283 }
284 
setStyle(const RVNGPropertyList & propList)285 void RVNGSVGDrawingGeneratorPrivate::setStyle(const RVNGPropertyList &propList)
286 {
287 	m_style.clear();
288 	m_style = propList;
289 
290 	const librevenge::RVNGPropertyListVector *gradient = propList.child("svg:linearGradient");
291 	if (!gradient)
292 		gradient = propList.child("svg:radialGradient");
293 	m_gradient = gradient ? *gradient : librevenge::RVNGPropertyListVector();
294 	if (m_style["draw:shadow"] && m_style["draw:shadow"]->getStr() == "visible" && m_style["draw:shadow-opacity"])
295 	{
296 		double shadowRed = 0.0;
297 		double shadowGreen = 0.0;
298 		double shadowBlue = 0.0;
299 		if (m_style["draw:shadow-color"])
300 		{
301 			unsigned shadowColour = stringToColour(m_style["draw:shadow-color"]->getStr());
302 			shadowRed = (double)((shadowColour & 0x00ff0000) >> 16)/255.0;
303 			shadowGreen = (double)((shadowColour & 0x0000ff00) >> 8)/255.0;
304 			shadowBlue = (double)(shadowColour & 0x000000ff)/255.0;
305 		}
306 		m_outputSink << "<" << getNamespaceAndDelim() << "defs>\n";
307 		m_outputSink << "<" << getNamespaceAndDelim() << "filter filterUnits=\"userSpaceOnUse\" id=\"shadow" << m_shadowIndex++ << "\">";
308 		m_outputSink << "<" << getNamespaceAndDelim() << "feOffset in=\"SourceGraphic\" result=\"offset\" ";
309 		if (m_style["draw:shadow-offset-x"])
310 			m_outputSink << "dx=\"" << doubleToString(72*getInchValue(*m_style["draw:shadow-offset-x"])) << "\" ";
311 		if (m_style["draw:shadow-offset-y"])
312 			m_outputSink << "dy=\"" << doubleToString(72*getInchValue(*m_style["draw:shadow-offset-y"])) << "\" ";
313 		m_outputSink << "/>";
314 		m_outputSink << "<" << getNamespaceAndDelim() << "feColorMatrix in=\"offset\" result=\"offset-color\" type=\"matrix\" values=\"";
315 		m_outputSink << "0 0 0 0 " << doubleToString(shadowRed) ;
316 		m_outputSink << " 0 0 0 0 " << doubleToString(shadowGreen);
317 		m_outputSink << " 0 0 0 0 " << doubleToString(shadowBlue);
318 		if (m_style["draw:opacity"] && m_style["draw:opacity"]->getDouble() < 1)
319 			m_outputSink << " 0 0 0 "   << doubleToString(m_style["draw:shadow-opacity"]->getDouble()/m_style["draw:opacity"]->getDouble()) << " 0\"/>";
320 		else
321 			m_outputSink << " 0 0 0 "   << doubleToString(m_style["draw:shadow-opacity"]->getDouble()) << " 0\"/>";
322 
323 		m_outputSink << "<" << getNamespaceAndDelim() << "feMerge>";
324 		m_outputSink << "<" << getNamespaceAndDelim() << "feMergeNode in=\"offset-color\" />";
325 		m_outputSink << "<" << getNamespaceAndDelim() << "feMergeNode in=\"SourceGraphic\" />";
326 		m_outputSink << "</" << getNamespaceAndDelim() << "feMerge>";
327 		m_outputSink << "</" << getNamespaceAndDelim() << "filter>";
328 		m_outputSink << "</" << getNamespaceAndDelim() << "defs>";
329 	}
330 
331 	if (m_style["draw:fill"] && m_style["draw:fill"]->getStr() == "gradient")
332 	{
333 		double angle = (m_style["draw:angle"] ? m_style["draw:angle"]->getDouble() : 0.0);
334 		angle *= -1.0;
335 		while (angle < 0)
336 			angle += 360;
337 		while (angle > 360)
338 			angle -= 360;
339 
340 		if (m_style["draw:style"] && (m_style["draw:style"]->getStr() == "radial" || m_style["draw:style"]->getStr() == "rectangular" ||
341 		                              m_style["draw:style"]->getStr() == "square" || m_style["draw:style"]->getStr() == "ellipsoid"))
342 		{
343 			m_outputSink << "<" << getNamespaceAndDelim() << "defs>\n";
344 			m_outputSink << "  <" << getNamespaceAndDelim() << "radialGradient id=\"grad" << m_gradientIndex++ << "\"";
345 
346 			if (m_style["svg:cx"])
347 				m_outputSink << " cx=\"" << m_style["svg:cx"]->getStr().cstr() << "\"";
348 			else if (m_style["draw:cx"])
349 				m_outputSink << " cx=\"" << m_style["draw:cx"]->getStr().cstr() << "\"";
350 
351 			if (m_style["svg:cy"])
352 				m_outputSink << " cy=\"" << m_style["svg:cy"]->getStr().cstr() << "\"";
353 			else if (m_style["draw:cy"])
354 				m_outputSink << " cy=\"" << m_style["draw:cy"]->getStr().cstr() << "\"";
355 			if (m_style["svg:r"])
356 				m_outputSink << " r=\"" << m_style["svg:r"]->getStr().cstr() << "\"";
357 			else if (m_style["draw:border"])
358 				m_outputSink << " r=\"" << doubleToString((1 - m_style["draw:border"]->getDouble())*100.0) << "%\"";
359 			else
360 				m_outputSink << " r=\"100%\"";
361 			m_outputSink << " >\n";
362 			if (m_gradient.count())
363 			{
364 				for (unsigned c = 0; c < m_gradient.count(); c++)
365 				{
366 					RVNGPropertyList const &grad=m_gradient[c];
367 					m_outputSink << "    <" << getNamespaceAndDelim() << "stop";
368 					if (grad["svg:offset"])
369 						m_outputSink << " offset=\"" << grad["svg:offset"]->getStr().cstr() << "\"";
370 					if (grad["svg:stop-color"])
371 						m_outputSink << " stop-color=\"" << grad["svg:stop-color"]->getStr().cstr() << "\"";
372 					if (grad["svg:stop-opacity"])
373 						m_outputSink << " stop-opacity=\"" << doubleToString(grad["svg:stop-opacity"]->getDouble()) << "\"";
374 					m_outputSink << "/>" << std::endl;
375 				}
376 			}
377 			else if (m_style["draw:start-color"] && m_style["draw:end-color"])
378 			{
379 				m_outputSink << "    <" << getNamespaceAndDelim() << "stop offset=\"0%\"";
380 				m_outputSink << " stop-color=\"" << m_style["draw:end-color"]->getStr().cstr() << "\"";
381 				m_outputSink << " stop-opacity=\"" << doubleToString(m_style["librevenge:end-opacity"] ? m_style["librevenge:end-opacity"]->getDouble() : 1) << "\" />" << std::endl;
382 
383 				m_outputSink << "    <" << getNamespaceAndDelim() << "stop offset=\"100%\"";
384 				m_outputSink << " stop-color=\"" << m_style["draw:start-color"]->getStr().cstr() << "\"";
385 				m_outputSink << " stop-opacity=\"" << doubleToString(m_style["librevenge:start-opacity"] ? m_style["librevenge:start-opacity"]->getDouble() : 1) << "\" />" << std::endl;
386 			}
387 			m_outputSink << "  </" << getNamespaceAndDelim() << "radialGradient>\n";
388 			m_outputSink << "</" << getNamespaceAndDelim() << "defs>\n";
389 		}
390 		else if (!m_style["draw:style"] || m_style["draw:style"]->getStr() == "linear" || m_style["draw:style"]->getStr() == "axial")
391 		{
392 			m_outputSink << "<" << getNamespaceAndDelim() << "defs>\n";
393 			m_outputSink << "  <" << getNamespaceAndDelim() << "linearGradient id=\"grad" << m_gradientIndex++ << "\" >\n";
394 
395 			if (m_gradient.count())
396 			{
397 				bool canBuildAxial = false;
398 				if (m_style["draw:style"] && m_style["draw:style"]->getStr() == "axial")
399 				{
400 					// check if we can reconstruct the linear offset, ie. if each offset is a valid percent%
401 					canBuildAxial = true;
402 					for (unsigned c = 0; c < m_gradient.count(); ++c)
403 					{
404 						RVNGPropertyList const &grad=m_gradient[c];
405 						if (!grad["svg:offset"] || grad["svg:offset"]->getDouble()<0 || grad["svg:offset"]->getDouble() > 1)
406 						{
407 							canBuildAxial=false;
408 							break;
409 						}
410 						RVNGString str=grad["svg:offset"]->getStr();
411 						int len=str.len();
412 						if (len<1 || str.cstr()[len-1]!='%')
413 						{
414 							canBuildAxial=false;
415 							break;
416 						}
417 					}
418 				}
419 				if (canBuildAxial)
420 				{
421 					for (unsigned long c = m_gradient.count(); c>0 ;)
422 					{
423 						RVNGPropertyList const &grad=m_gradient[--c];
424 						m_outputSink << "    <" << getNamespaceAndDelim() << "stop ";
425 						if (grad["svg:offset"])
426 							m_outputSink << "offset=\"" << doubleToString(50.-50.*grad["svg:offset"]->getDouble()) << "%\"";
427 						if (grad["svg:stop-color"])
428 							m_outputSink << " stop-color=\"" << grad["svg:stop-color"]->getStr().cstr() << "\"";
429 						if (grad["svg:stop-opacity"])
430 							m_outputSink << " stop-opacity=\"" << doubleToString(grad["svg:stop-opacity"]->getDouble()) << "\"";
431 						m_outputSink << "/>" << std::endl;
432 					}
433 					for (unsigned long c = 0; c < m_gradient.count(); ++c)
434 					{
435 						RVNGPropertyList const &grad=m_gradient[c];
436 						if (c==0 && grad["svg:offset"] && grad["svg:offset"]->getDouble() <= 0)
437 							continue;
438 						m_outputSink << "    <" << getNamespaceAndDelim() << "stop ";
439 						if (grad["svg:offset"])
440 							m_outputSink << "offset=\"" << doubleToString(50.+50.*grad["svg:offset"]->getDouble()) << "%\"";
441 						if (grad["svg:stop-color"])
442 							m_outputSink << " stop-color=\"" << grad["svg:stop-color"]->getStr().cstr() << "\"";
443 						if (grad["svg:stop-opacity"])
444 							m_outputSink << " stop-opacity=\"" << doubleToString(grad["svg:stop-opacity"]->getDouble()) << "\"";
445 						m_outputSink << "/>" << std::endl;
446 					}
447 				}
448 				else
449 				{
450 					for (unsigned c = 0; c < m_gradient.count(); c++)
451 					{
452 						RVNGPropertyList const &grad=m_gradient[c];
453 						m_outputSink << "    <" << getNamespaceAndDelim() << "stop";
454 						if (grad["svg:offset"])
455 							m_outputSink << " offset=\"" << grad["svg:offset"]->getStr().cstr() << "\"";
456 						if (grad["svg:stop-color"])
457 							m_outputSink << " stop-color=\"" << grad["svg:stop-color"]->getStr().cstr() << "\"";
458 						if (grad["svg:stop-opacity"])
459 							m_outputSink << " stop-opacity=\"" << doubleToString(grad["svg:stop-opacity"]->getDouble()) << "\"";
460 						m_outputSink << "/>" << std::endl;
461 					}
462 				}
463 			}
464 			else if (m_style["draw:start-color"] && m_style["draw:end-color"])
465 			{
466 				if (!m_style["draw:style"] || m_style["draw:style"]->getStr() == "linear")
467 				{
468 					m_outputSink << "    <" << getNamespaceAndDelim() << "stop offset=\"0%\"";
469 					m_outputSink << " stop-color=\"" << m_style["draw:start-color"]->getStr().cstr() << "\"";
470 					m_outputSink << " stop-opacity=\"" << doubleToString(m_style["librevenge:start-opacity"] ? m_style["librevenge:start-opacity"]->getDouble() : 1) << "\" />" << std::endl;
471 
472 					m_outputSink << "    <" << getNamespaceAndDelim() << "stop offset=\"100%\"";
473 					m_outputSink << " stop-color=\"" << m_style["draw:end-color"]->getStr().cstr() << "\"";
474 					m_outputSink << " stop-opacity=\"" << doubleToString(m_style["librevenge:end-opacity"] ? m_style["librevenge:end-opacity"]->getDouble() : 1) << "\" />" << std::endl;
475 				}
476 				else
477 				{
478 					m_outputSink << "    <" << getNamespaceAndDelim() << "stop offset=\"0%\"";
479 					m_outputSink << " stop-color=\"" << m_style["draw:end-color"]->getStr().cstr() << "\"";
480 					m_outputSink << " stop-opacity=\"" << doubleToString(m_style["librevenge:end-opacity"] ? m_style["librevenge:end-opacity"]->getDouble() : 1) << "\" />" << std::endl;
481 
482 					m_outputSink << "    <" << getNamespaceAndDelim() << "stop offset=\"50%\"";
483 					m_outputSink << " stop-color=\"" << m_style["draw:start-color"]->getStr().cstr() << "\"";
484 					m_outputSink << " stop-opacity=\"" << doubleToString(m_style["librevenge:start-opacity"] ? m_style["librevenge:start-opacity"]->getDouble() : 1) << "\" />" << std::endl;
485 
486 					m_outputSink << "    <" << getNamespaceAndDelim() << "stop offset=\"100%\"";
487 					m_outputSink << " stop-color=\"" << m_style["draw:end-color"]->getStr().cstr() << "\"";
488 					m_outputSink << " stop-opacity=\"" << doubleToString(m_style["librevenge:end-opacity"] ? m_style["librevenge:end-opacity"]->getDouble() : 1) << "\" />" << std::endl;
489 				}
490 			}
491 			m_outputSink << "  </" << getNamespaceAndDelim() << "linearGradient>\n";
492 
493 			// not a simple horizontal gradient
494 			if (angle<270 || angle>270)
495 			{
496 				m_outputSink << "  <" << getNamespaceAndDelim() << "linearGradient xlink:href=\"#grad" << m_gradientIndex-1 << "\"";
497 				m_outputSink << " id=\"grad" << m_gradientIndex++ << "\" ";
498 				m_outputSink << "x1=\"0\" y1=\"0\" x2=\"0\" y2=\"1\" ";
499 				m_outputSink << "gradientTransform=\"rotate(" << angle << " .5 .5)\" ";
500 				m_outputSink << "gradientUnits=\"objectBoundingBox\" >\n";
501 				m_outputSink << "  </" << getNamespaceAndDelim() << "linearGradient>\n";
502 			}
503 
504 			m_outputSink << "</" << getNamespaceAndDelim() << "defs>\n";
505 		}
506 	}
507 	else if (m_style["draw:fill"] && m_style["draw:fill"]->getStr() == "bitmap" && m_style["draw:fill-image"] && m_style["librevenge:mime-type"])
508 	{
509 		m_outputSink << "<" << getNamespaceAndDelim() << "defs>\n";
510 		m_outputSink << "  <" << getNamespaceAndDelim() << "pattern id=\"img" << m_patternIndex++ << "\" patternUnits=\"userSpaceOnUse\" ";
511 		if (m_style["svg:width"])
512 			m_outputSink << "width=\"" << doubleToString(72*getInchValue(*m_style["svg:width"])) << "\" ";
513 		else
514 			m_outputSink << "width=\"100\" ";
515 
516 		if (m_style["svg:height"])
517 			m_outputSink << "height=\"" << doubleToString(72*getInchValue(*m_style["svg:height"])) << "\">" << std::endl;
518 		else
519 			m_outputSink << "height=\"100\">" << std::endl;
520 		m_outputSink << "<" << getNamespaceAndDelim() << "image ";
521 
522 		if (m_style["svg:x"])
523 			m_outputSink << "x=\"" << doubleToString(72*getInchValue(*m_style["svg:x"])) << "\" ";
524 		else
525 			m_outputSink << "x=\"0\" ";
526 
527 		if (m_style["svg:y"])
528 			m_outputSink << "y=\"" << doubleToString(72*getInchValue(*m_style["svg:y"])) << "\" ";
529 		else
530 			m_outputSink << "y=\"0\" ";
531 
532 		if (m_style["svg:width"])
533 			m_outputSink << "width=\"" << doubleToString(72*getInchValue(*m_style["svg:width"])) << "\" ";
534 		else
535 			m_outputSink << "width=\"100\" ";
536 
537 		if (m_style["svg:height"])
538 			m_outputSink << "height=\"" << doubleToString(72*getInchValue(*m_style["svg:height"])) << "\" ";
539 		else
540 			m_outputSink << "height=\"100\" ";
541 
542 		m_outputSink << "xlink:href=\"data:" << m_style["librevenge:mime-type"]->getStr().cstr() << ";base64,";
543 		m_outputSink << m_style["draw:fill-image"]->getStr().cstr();
544 		m_outputSink << "\" />\n";
545 		m_outputSink << "  </" << getNamespaceAndDelim() << "pattern>\n";
546 		m_outputSink << "</" << getNamespaceAndDelim() << "defs>\n";
547 	}
548 
549 	// check for arrow and if find some, define a basic arrow
550 	if (m_style["draw:marker-start-path"])
551 	{
552 		m_outputSink << "<" << getNamespaceAndDelim() << "defs>\n";
553 		m_outputSink << "<" << getNamespaceAndDelim() << "marker id=\"startMarker" << m_arrowStartIndex++ << "\" ";
554 		m_outputSink << " markerUnits=\"strokeWidth\" orient=\"auto\" markerWidth=\"8\" markerHeight=\"6\"\n";
555 		m_outputSink << " viewBox=\"0 0 10 10\" refX=\"9\" refY=\"5\">\n";
556 		m_outputSink << "<" << getNamespaceAndDelim() << "polyline points=\"10,0 0,5 10,10 9,5\" fill=\"solid\" />\n";
557 		m_outputSink << "</" << getNamespaceAndDelim() << "marker>\n";
558 		m_outputSink << "</" << getNamespaceAndDelim() << "defs>\n";
559 	}
560 	if (m_style["draw:marker-end-path"])
561 	{
562 		m_outputSink << "<" << getNamespaceAndDelim() << "defs>\n";
563 		m_outputSink << "<" << getNamespaceAndDelim() << "marker id=\"endMarker" << m_arrowEndIndex++ << "\" ";
564 		m_outputSink << " markerUnits=\"strokeWidth\" orient=\"auto\" markerWidth=\"8\" markerHeight=\"6\"\n";
565 		m_outputSink << " viewBox=\"0 0 10 10\" refX=\"1\" refY=\"5\">\n";
566 		m_outputSink << "<" << getNamespaceAndDelim() << "polyline points=\"0,0 10,5 0,10 1,5\" fill=\"solid\" />\n";
567 		m_outputSink << "</" << getNamespaceAndDelim() << "marker>\n";
568 		m_outputSink << "</" << getNamespaceAndDelim() << "defs>\n";
569 	}
570 }
571 
572 // create "style" attribute based on current pen and brush
writeStyle(bool)573 void RVNGSVGDrawingGeneratorPrivate::writeStyle(bool /* isClosed */)
574 {
575 	m_outputSink << "style=\"";
576 
577 	double width = 1.0 / 72.0;
578 	if (m_style["svg:stroke-width"])
579 	{
580 		width = getInchValue(*m_style["svg:stroke-width"]);
581 #if 0
582 		// add me in libmspub and libcdr
583 		if (width <= 0.0 && m_style["draw:stroke"] && m_style["draw:stroke"]->getStr() != "none")
584 			width = 0.2 / 72.0; // reasonable hairline
585 #endif
586 		m_outputSink << "stroke-width: " << doubleToString(72*width) << "; ";
587 	}
588 
589 	if (m_style["draw:stroke"] && m_style["draw:stroke"]->getStr() != "none")
590 	{
591 		if (m_style["svg:stroke-color"])
592 			m_outputSink << "stroke: " << m_style["svg:stroke-color"]->getStr().cstr()  << "; ";
593 		if (m_style["svg:stroke-opacity"] &&  m_style["svg:stroke-opacity"]->getInt()!= 1)
594 			m_outputSink << "stroke-opacity: " << doubleToString(m_style["svg:stroke-opacity"]->getDouble()) << "; ";
595 	}
596 
597 	if (m_style["draw:stroke"] && m_style["draw:stroke"]->getStr() == "solid")
598 		m_outputSink << "stroke-dasharray: none; ";
599 	else if (m_style["draw:stroke"] && m_style["draw:stroke"]->getStr() == "dash")
600 	{
601 		int dots1 = m_style["draw:dots1"] ? m_style["draw:dots1"]->getInt() : 0;
602 		int dots2 = m_style["draw:dots2"] ? m_style["draw:dots2"]->getInt() : 0;
603 		double dots1len = 72.*width, dots2len = 72.*width, gap = 72.*width;
604 		if (m_style["draw:dots1-length"])
605 		{
606 			if (m_style["draw:dots1-length"]->getUnit()==librevenge::RVNG_PERCENT)
607 				dots1len=72*m_style["draw:dots1-length"]->getDouble()*width;
608 			else
609 				dots1len=72*getInchValue(*m_style["draw:dots1-length"]);
610 		}
611 		if (m_style["draw:dots2-length"])
612 		{
613 			if (m_style["draw:dots2-length"]->getUnit()==librevenge::RVNG_PERCENT)
614 				dots2len=72*m_style["draw:dots2-length"]->getDouble()*width;
615 			else
616 				dots2len=72*getInchValue(*m_style["draw:dots2-length"]);
617 		}
618 		if (m_style["draw:distance"])
619 		{
620 			if (m_style["draw:distance"]->getUnit()==librevenge::RVNG_PERCENT)
621 				gap=72*m_style["draw:distance"]->getDouble()*width;
622 			else
623 				gap=72*getInchValue(*m_style["draw:distance"]);
624 		}
625 		m_outputSink << "stroke-dasharray: ";
626 		for (int i = 0; i < dots1; i++)
627 		{
628 			if (i)
629 				m_outputSink << ", ";
630 			m_outputSink << doubleToString(dots1len);
631 			m_outputSink << ", ";
632 			m_outputSink << doubleToString(gap);
633 		}
634 		for (int j = 0; j < dots2; j++)
635 		{
636 			m_outputSink << ", ";
637 			m_outputSink << doubleToString(dots2len);
638 			m_outputSink << ", ";
639 			m_outputSink << doubleToString(gap);
640 		}
641 		m_outputSink << "; ";
642 	}
643 
644 	if (m_style["svg:stroke-linecap"])
645 		m_outputSink << "stroke-linecap: " << m_style["svg:stroke-linecap"]->getStr().cstr() << "; ";
646 
647 	if (m_style["svg:stroke-linejoin"])
648 		m_outputSink << "stroke-linejoin: " << m_style["svg:stroke-linejoin"]->getStr().cstr() << "; ";
649 
650 	if (m_style["draw:fill"] && m_style["draw:fill"]->getStr() == "none")
651 		m_outputSink << "fill: none; ";
652 	else if (m_style["svg:fill-rule"])
653 		m_outputSink << "fill-rule: " << m_style["svg:fill-rule"]->getStr().cstr() << "; ";
654 
655 	if (m_style["draw:fill"] && m_style["draw:fill"]->getStr() == "gradient")
656 		m_outputSink << "fill: url(#grad" << m_gradientIndex-1 << "); ";
657 	else if (m_style["draw:fill"] && m_style["draw:fill"]->getStr() == "bitmap")
658 		m_outputSink << "fill: url(#img" << m_patternIndex-1 << "); ";
659 
660 	if (m_style["draw:shadow"] && m_style["draw:shadow"]->getStr() == "visible")
661 		m_outputSink << "filter:url(#shadow" << m_shadowIndex-1 << "); ";
662 
663 	if (m_style["draw:fill"] && m_style["draw:fill"]->getStr() == "solid")
664 		if (m_style["draw:fill-color"])
665 			m_outputSink << "fill: " << m_style["draw:fill-color"]->getStr().cstr() << "; ";
666 	if (m_style["draw:opacity"] && m_style["draw:opacity"]->getDouble() < 1)
667 		m_outputSink << "fill-opacity: " << doubleToString(m_style["draw:opacity"]->getDouble()) << "; ";
668 
669 	if (m_style["draw:marker-start-path"])
670 		m_outputSink << "marker-start: url(#startMarker" << m_arrowStartIndex-1 << "); ";
671 	if (m_style["draw:marker-end-path"])
672 		m_outputSink << "marker-end: url(#endMarker" << m_arrowEndIndex-1 << "); ";
673 
674 	m_outputSink << "\""; // style
675 }
676 
677 
RVNGSVGDrawingGenerator(RVNGStringVector & vec,const RVNGString & nmSpace)678 RVNGSVGDrawingGenerator::RVNGSVGDrawingGenerator(RVNGStringVector &vec, const RVNGString &nmSpace) :
679 	m_pImpl(new RVNGSVGDrawingGeneratorPrivate(vec, nmSpace))
680 {
681 }
682 
~RVNGSVGDrawingGenerator()683 RVNGSVGDrawingGenerator::~RVNGSVGDrawingGenerator()
684 {
685 	delete m_pImpl;
686 }
687 
startDocument(const RVNGPropertyList &)688 void RVNGSVGDrawingGenerator::startDocument(const RVNGPropertyList & /*propList*/) {}
endDocument()689 void RVNGSVGDrawingGenerator::endDocument() {}
setDocumentMetaData(const RVNGPropertyList &)690 void RVNGSVGDrawingGenerator::setDocumentMetaData(const RVNGPropertyList & /*propList*/) {}
defineEmbeddedFont(const RVNGPropertyList &)691 void RVNGSVGDrawingGenerator::defineEmbeddedFont(const RVNGPropertyList & /*propList*/) {}
692 
startPage(const RVNGPropertyList & propList)693 void RVNGSVGDrawingGenerator::startPage(const RVNGPropertyList &propList)
694 {
695 	if (propList["librevenge:master-page-name"])
696 	{
697 		if (m_pImpl->m_masterNameToContentMap.find(propList["librevenge:master-page-name"]->getStr())!=
698 		        m_pImpl->m_masterNameToContentMap.end())
699 		{
700 			m_pImpl->m_outputSink << m_pImpl->m_masterNameToContentMap.find(propList["librevenge:master-page-name"]->getStr())->second;
701 			return;
702 		}
703 		RVNG_DEBUG_MSG(("RVNGSVGDrawingGenerator::startPage: can not find page with given master name\n"));
704 	}
705 
706 	m_pImpl->m_outputSink << "<" << m_pImpl->getNamespaceAndDelim() << "svg version=\"1.1\" xmlns";
707 	m_pImpl->m_outputSink << (m_pImpl->m_nmSpace.empty() ? "" : ":") << m_pImpl->m_nmSpace << "=\"http://www.w3.org/2000/svg\" ";
708 	m_pImpl->m_outputSink << "xmlns:xlink=\"http://www.w3.org/1999/xlink\" ";
709 	if (propList["svg:width"])
710 		m_pImpl->m_outputSink << "width=\"" << doubleToString(72*getInchValue(*propList["svg:width"])) << "\" ";
711 	if (propList["svg:height"])
712 		m_pImpl->m_outputSink << "height=\"" << doubleToString(72*getInchValue(*propList["svg:height"])) << "\"";
713 	m_pImpl->m_outputSink << " >\n";
714 }
715 
endPage()716 void RVNGSVGDrawingGenerator::endPage()
717 {
718 	m_pImpl->m_outputSink << "</" << m_pImpl->getNamespaceAndDelim() << "svg>\n";
719 	m_pImpl->m_vec.append(m_pImpl->m_outputSink.str().c_str());
720 	m_pImpl->m_outputSink.str("");
721 }
722 
startMasterPage(const RVNGPropertyList & propList)723 void RVNGSVGDrawingGenerator::startMasterPage(const RVNGPropertyList &propList)
724 {
725 	if (!m_pImpl->m_masterName.empty())
726 	{
727 		RVNG_DEBUG_MSG(("RVNGSVGDrawingGenerator::startMasterPage: a master page is already opened\n"));
728 	}
729 	else if (propList["librevenge:master-page-name"])
730 	{
731 		m_pImpl->m_masterName=propList["librevenge:master-page-name"]->getStr();
732 		RVNGPropertyList pList(propList);
733 		pList.remove("librevenge:master-page-name");
734 		startPage(pList);
735 	}
736 	else
737 	{
738 		RVNG_DEBUG_MSG(("RVNGSVGDrawingGenerator::startMasterPage: can not find the master name\n"));
739 	}
740 }
741 
endMasterPage()742 void RVNGSVGDrawingGenerator::endMasterPage()
743 {
744 	if (m_pImpl->m_masterName.empty())
745 	{
746 		RVNG_DEBUG_MSG(("RVNGSVGDrawingGenerator::endMasterPage: no master pages are opened\n"));
747 	}
748 	else
749 	{
750 		if (m_pImpl->m_masterNameToContentMap.find(m_pImpl->m_masterName)!=m_pImpl->m_masterNameToContentMap.end())
751 		{
752 			RVNG_DEBUG_MSG(("RVNGSVGDrawingGenerator::endMasterPage: a master page with name %s already exists\n",
753 			                m_pImpl->m_masterName.cstr()));
754 		}
755 		// no need to close the page, this will be done when the master page will be used
756 		m_pImpl->m_masterNameToContentMap[m_pImpl->m_masterName]=m_pImpl->m_outputSink.str();
757 		m_pImpl->m_masterName.clear();
758 	}
759 	// reset the content
760 	m_pImpl->m_outputSink.str("");
761 }
762 
startLayer(const RVNGPropertyList & propList)763 void RVNGSVGDrawingGenerator::startLayer(const RVNGPropertyList &propList)
764 {
765 	m_pImpl->m_outputSink << "<" << m_pImpl->getNamespaceAndDelim() << "g";
766 	librevenge::RVNGString layer("Layer");
767 	if (propList["draw:layer"])
768 		layer.append(propList["draw:layer"]->getStr());
769 	else if (propList["svg:id"])
770 		layer.append(propList["svg:id"]->getStr());
771 	else
772 		layer.sprintf("Layer%d", m_pImpl->m_layerId++);
773 	librevenge::RVNGString finalName("");
774 	finalName.appendEscapedXML(layer);
775 	m_pImpl->m_outputSink << " id=\"" << finalName.cstr() << "\"";
776 	if (propList["svg:fill-rule"])
777 		m_pImpl->m_outputSink << " fill-rule=\"" << propList["svg:fill-rule"]->getStr().cstr() << "\"";
778 	m_pImpl->m_outputSink << " >\n";
779 }
780 
endLayer()781 void RVNGSVGDrawingGenerator::endLayer()
782 {
783 	m_pImpl->m_outputSink << "</" << m_pImpl->getNamespaceAndDelim() << "g>\n";
784 }
785 
startEmbeddedGraphics(const RVNGPropertyList &)786 void RVNGSVGDrawingGenerator::startEmbeddedGraphics(const RVNGPropertyList & /*propList*/) {}
endEmbeddedGraphics()787 void RVNGSVGDrawingGenerator::endEmbeddedGraphics() {}
788 
openGroup(const RVNGPropertyList &)789 void RVNGSVGDrawingGenerator::openGroup(const RVNGPropertyList & /*propList*/)
790 {
791 	m_pImpl->m_outputSink << "<" << m_pImpl->getNamespaceAndDelim() << "g";
792 	librevenge::RVNGString group;
793 	group.sprintf("Group%d", m_pImpl->m_groupId++);
794 	m_pImpl->m_outputSink << " id=\"" << group.cstr() << "\"";
795 	m_pImpl->m_outputSink << " >\n";
796 }
797 
closeGroup()798 void RVNGSVGDrawingGenerator::closeGroup()
799 {
800 	m_pImpl->m_outputSink << "</" << m_pImpl->getNamespaceAndDelim() << "g>\n";
801 }
802 
setStyle(const RVNGPropertyList & propList)803 void RVNGSVGDrawingGenerator::setStyle(const RVNGPropertyList &propList)
804 {
805 	m_pImpl->setStyle(propList);
806 }
807 
drawRectangle(const RVNGPropertyList & propList)808 void RVNGSVGDrawingGenerator::drawRectangle(const RVNGPropertyList &propList)
809 {
810 	if (!propList["svg:x"] || !propList["svg:y"] || !propList["svg:width"] || !propList["svg:height"])
811 		return;
812 	m_pImpl->m_outputSink << "<" << m_pImpl->getNamespaceAndDelim() << "rect ";
813 	m_pImpl->m_outputSink << "x=\"" << doubleToString(72*getInchValue(*propList["svg:x"])) << "\" y=\"" << doubleToString(72*getInchValue(*propList["svg:y"])) << "\" ";
814 	m_pImpl->m_outputSink << "width=\"" << doubleToString(72*getInchValue(*propList["svg:width"])) << "\" height=\"" << doubleToString(72*getInchValue(*propList["svg:height"])) << "\" ";
815 	if (propList["svg:rx"] && propList["svg:rx"]->getDouble() > 0 && propList["svg:ry"] && propList["svg:ry"]->getDouble()>0)
816 		m_pImpl->m_outputSink << "rx=\"" << doubleToString(72*getInchValue(*propList["svg:rx"])) << "\" ry=\"" << doubleToString(72*getInchValue(*propList["svg:ry"])) << "\" ";
817 	m_pImpl->writeStyle();
818 	m_pImpl->m_outputSink << "/>\n";
819 }
820 
drawEllipse(const RVNGPropertyList & propList)821 void RVNGSVGDrawingGenerator::drawEllipse(const RVNGPropertyList &propList)
822 {
823 	if (!propList["svg:cx"] || !propList["svg:cy"] || !propList["svg:rx"] || !propList["svg:ry"])
824 		return;
825 	m_pImpl->m_outputSink << "<" << m_pImpl->getNamespaceAndDelim() << "ellipse ";
826 	m_pImpl->m_outputSink << "cx=\"" << doubleToString(72*getInchValue(*propList["svg:cx"])) << "\" cy=\"" << doubleToString(72*getInchValue(*propList["svg:cy"])) << "\" ";
827 	m_pImpl->m_outputSink << "rx=\"" << doubleToString(72*getInchValue(*propList["svg:rx"])) << "\" ry=\"" << doubleToString(72*getInchValue(*propList["svg:ry"])) << "\" ";
828 	m_pImpl->writeStyle();
829 	if (propList["librevenge:rotate"] && (propList["librevenge:rotate"]->getDouble()<0||propList["librevenge:rotate"]->getDouble()>0))
830 		m_pImpl->m_outputSink << " transform=\" rotate(" << doubleToString(-propList["librevenge:rotate"]->getDouble())
831 		                      << ", " << doubleToString(72*getInchValue(*propList["svg:cx"]))
832 		                      << ", " << doubleToString(72*getInchValue(*propList["svg:cy"]))
833 		                      << ")\" ";
834 	m_pImpl->m_outputSink << "/>\n";
835 }
836 
drawPolyline(const RVNGPropertyList & propList)837 void RVNGSVGDrawingGenerator::drawPolyline(const RVNGPropertyList &propList)
838 {
839 	const RVNGPropertyListVector *vertices = propList.child("svg:points");
840 	if (vertices && vertices->count())
841 		m_pImpl->drawPolySomething(*vertices, false);
842 }
843 
drawPolygon(const RVNGPropertyList & propList)844 void RVNGSVGDrawingGenerator::drawPolygon(const RVNGPropertyList &propList)
845 {
846 	const RVNGPropertyListVector *vertices = propList.child("svg:points");
847 	if (vertices && vertices->count())
848 		m_pImpl->drawPolySomething(*vertices, true);
849 }
850 
drawPath(const RVNGPropertyList & propList)851 void RVNGSVGDrawingGenerator::drawPath(const RVNGPropertyList &propList)
852 {
853 	const RVNGPropertyListVector *path = propList.child("svg:d");
854 	if (!path)
855 		return;
856 	m_pImpl->m_outputSink << "<" << m_pImpl->getNamespaceAndDelim() << "path d=\" ";
857 	bool isClosed = false;
858 	unsigned i=0;
859 	for (i=0; i < path->count(); i++)
860 	{
861 		RVNGPropertyList pList((*path)[i]);
862 		if (!pList["librevenge:path-action"]) continue;
863 		std::string action=pList["librevenge:path-action"]->getStr().cstr();
864 		if (action.length()!=1) continue;
865 		bool coordOk=pList["svg:x"]&&pList["svg:y"];
866 		bool coord1Ok=coordOk && pList["svg:x1"]&&pList["svg:y1"];
867 		bool coord2Ok=coord1Ok && pList["svg:x2"]&&pList["svg:y2"];
868 		if (pList["svg:x"] && action[0] == 'H')
869 			m_pImpl->m_outputSink << "\nH" << doubleToString(72*getInchValue(*pList["svg:x"]));
870 		else if (pList["svg:y"] && action[0] == 'V')
871 			m_pImpl->m_outputSink << "\nV" << doubleToString(72*getInchValue(*pList["svg:y"]));
872 		else if (coordOk && (action[0] == 'M' || action[0] == 'L' || action[0] == 'T'))
873 		{
874 			m_pImpl->m_outputSink << "\n" << action;
875 			m_pImpl->m_outputSink << doubleToString(72*getInchValue(*pList["svg:x"])) << "," << doubleToString(72*getInchValue(*pList["svg:y"]));
876 		}
877 		else if (coord1Ok && (action[0] == 'Q' || action[0] == 'S'))
878 		{
879 			m_pImpl->m_outputSink << "\n" << action;
880 			m_pImpl->m_outputSink << doubleToString(72*getInchValue(*pList["svg:x1"])) << "," << doubleToString(72*getInchValue(*pList["svg:y1"])) << " ";
881 			m_pImpl->m_outputSink << doubleToString(72*getInchValue(*pList["svg:x"])) << "," << doubleToString(72*getInchValue(*pList["svg:y"]));
882 		}
883 		else if (coord2Ok && action[0] == 'C')
884 		{
885 			m_pImpl->m_outputSink << "\nC";
886 			m_pImpl->m_outputSink << doubleToString(72*getInchValue(*pList["svg:x1"])) << "," << doubleToString(72*getInchValue(*pList["svg:y1"])) << " ";
887 			m_pImpl->m_outputSink << doubleToString(72*getInchValue(*pList["svg:x2"])) << "," << doubleToString(72*getInchValue(*pList["svg:y2"])) << " ";
888 			m_pImpl->m_outputSink << doubleToString(72*getInchValue(*pList["svg:x"])) << "," << doubleToString(72*getInchValue(*pList["svg:y"]));
889 		}
890 		else if (coordOk && pList["svg:rx"] && pList["svg:ry"] && action[0] == 'A')
891 		{
892 			m_pImpl->m_outputSink << "\nA";
893 			m_pImpl->m_outputSink << doubleToString(72*getInchValue(*pList["svg:rx"])) << "," << doubleToString(72*getInchValue(*pList["svg:ry"])) << " ";
894 			m_pImpl->m_outputSink << doubleToString(pList["librevenge:rotate"] ? pList["librevenge:rotate"]->getDouble() : 0) << " ";
895 			m_pImpl->m_outputSink << (pList["librevenge:large-arc"] ? pList["librevenge:large-arc"]->getInt() : 1) << ",";
896 			m_pImpl->m_outputSink << (pList["librevenge:sweep"] ? pList["librevenge:sweep"]->getInt() : 1) << " ";
897 			m_pImpl->m_outputSink << doubleToString(72*getInchValue(*pList["svg:x"])) << "," << doubleToString(72*getInchValue(*pList["svg:y"]));
898 		}
899 		else if (action[0] == 'Z')
900 		{
901 			isClosed = true;
902 			m_pImpl->m_outputSink << "\nZ";
903 		}
904 	}
905 
906 	m_pImpl->m_outputSink << "\" \n";
907 	m_pImpl->writeStyle(isClosed);
908 	m_pImpl->m_outputSink << "/>\n";
909 }
910 
drawGraphicObject(const RVNGPropertyList & propList)911 void RVNGSVGDrawingGenerator::drawGraphicObject(const RVNGPropertyList &propList)
912 {
913 	if (!propList["librevenge:mime-type"] || propList["librevenge:mime-type"]->getStr().len() <= 0)
914 		return;
915 	if (!propList["office:binary-data"])
916 		return;
917 	m_pImpl->m_outputSink << "<" << m_pImpl->getNamespaceAndDelim() << "image ";
918 	if (propList["svg:x"] && propList["svg:y"] && propList["svg:width"] && propList["svg:height"])
919 	{
920 		double x=getInchValue(*propList["svg:x"]);
921 		double y=getInchValue(*propList["svg:y"]);
922 		double width=getInchValue(*propList["svg:width"]);
923 		double height=getInchValue(*propList["svg:height"]);
924 		bool flipX(propList["draw:mirror-horizontal"] && propList["draw:mirror-horizontal"]->getInt());
925 		bool flipY(propList["draw:mirror-vertical"] && propList["draw:mirror-vertical"]->getInt());
926 
927 		m_pImpl->m_outputSink << "x=\"" << doubleToString(72*x) << "\" y=\"" << doubleToString(72*y) << "\" ";
928 		m_pImpl->m_outputSink << "width=\"" << doubleToString(72*width) << "\" height=\"" << doubleToString(72*height) << "\" ";
929 		if (flipX || flipY || propList["librevenge:rotate"])
930 		{
931 			double xmiddle = x + width / 2.0;
932 			double ymiddle = y + height / 2.0;
933 			m_pImpl->m_outputSink << "transform=\"";
934 			m_pImpl->m_outputSink << " translate(" << doubleToString(72*xmiddle) << ", " << doubleToString(72*ymiddle) << ") ";
935 			m_pImpl->m_outputSink << " scale(" << (flipX ? "-1" : "1") << ", " << (flipY ? "-1" : "1") << ") ";
936 			// rotation is around the center of the object's bounding box
937 			if (propList["librevenge:rotate"])
938 			{
939 				double angle(propList["librevenge:rotate"]->getDouble());
940 				while (angle > 180.0)
941 					angle -= 360.0;
942 				while (angle < -180.0)
943 					angle += 360.0;
944 				m_pImpl->m_outputSink << " rotate(" << doubleToString(angle) << ") ";
945 			}
946 			m_pImpl->m_outputSink << " translate(" << doubleToString(-72*xmiddle) << ", " << doubleToString(-72*ymiddle) << ") ";
947 			m_pImpl->m_outputSink << "\" ";
948 		}
949 	}
950 	m_pImpl->m_outputSink << "xlink:href=\"data:" << propList["librevenge:mime-type"]->getStr().cstr() << ";base64,";
951 	m_pImpl->m_outputSink << propList["office:binary-data"]->getStr().cstr();
952 	m_pImpl->m_outputSink << "\" />\n";
953 }
954 
drawConnector(const RVNGPropertyList &)955 void RVNGSVGDrawingGenerator::drawConnector(const RVNGPropertyList &/*propList*/)
956 {
957 	// TODO: implement me
958 }
959 
startTextObject(const RVNGPropertyList & propList)960 void RVNGSVGDrawingGenerator::startTextObject(const RVNGPropertyList &propList)
961 {
962 	double x = 0.0;
963 	double y = 0.0;
964 	double height = 0.0;
965 	m_pImpl->m_outputSink << "<" << m_pImpl->getNamespaceAndDelim() << "text ";
966 	if (propList["svg:x"] && propList["svg:y"])
967 	{
968 		x = getInchValue(*propList["svg:x"]);
969 		y = getInchValue(*propList["svg:y"]);
970 	}
971 
972 	double xmiddle = x;
973 	double ymiddle = y;
974 
975 	if (propList["svg:width"])
976 	{
977 		double width = getInchValue(*propList["svg:width"]);
978 		xmiddle += width / 2.0;
979 	}
980 
981 	if (propList["svg:height"])
982 	{
983 		height = getInchValue(*propList["svg:height"]);
984 		ymiddle += height / 2.0;
985 	}
986 
987 	if (propList["draw:textarea-vertical-align"])
988 	{
989 		if (propList["draw:textarea-vertical-align"]->getStr() == "middle")
990 			y = ymiddle;
991 		if (propList["draw:textarea-vertical-align"]->getStr() == "bottom")
992 		{
993 			y += height;
994 			if (propList["fo:padding-bottom"])
995 				y -= propList["fo:padding-bottom"]->getDouble();
996 		}
997 	}
998 	else
999 		y += height;
1000 
1001 	if (propList["fo:padding-left"])
1002 		x += propList["fo:padding-left"]->getDouble();
1003 
1004 	m_pImpl->m_outputSink << "x=\"" << doubleToString(72*x) << "\" y=\"" << doubleToString(72*y) << "\"";
1005 
1006 	// rotation is around the center of the object's bounding box
1007 	if (propList["librevenge:rotate"] && (propList["librevenge:rotate"]->getDouble()<0||propList["librevenge:rotate"]->getDouble()>0))
1008 	{
1009 		double angle(propList["librevenge:rotate"]->getDouble());
1010 		while (angle > 180.0)
1011 			angle -= 360.0;
1012 		while (angle < -180.0)
1013 			angle += 360.0;
1014 		m_pImpl->m_outputSink << " transform=\"rotate(" << doubleToString(angle) << ", " << doubleToString(72*xmiddle) << ", " << doubleToString(72*ymiddle) << ")\" ";
1015 	}
1016 	m_pImpl->m_outputSink << ">\n";
1017 
1018 }
1019 
endTextObject()1020 void RVNGSVGDrawingGenerator::endTextObject()
1021 {
1022 	m_pImpl->m_outputSink << "</" << m_pImpl->getNamespaceAndDelim() << "text>\n";
1023 }
1024 
openOrderedListLevel(const RVNGPropertyList &)1025 void RVNGSVGDrawingGenerator::openOrderedListLevel(const RVNGPropertyList & /*propList*/) {}
closeOrderedListLevel()1026 void RVNGSVGDrawingGenerator::closeOrderedListLevel() {}
1027 
openUnorderedListLevel(const RVNGPropertyList &)1028 void RVNGSVGDrawingGenerator::openUnorderedListLevel(const RVNGPropertyList & /*propList*/) {}
closeUnorderedListLevel()1029 void RVNGSVGDrawingGenerator::closeUnorderedListLevel() {}
1030 
openListElement(const RVNGPropertyList &)1031 void RVNGSVGDrawingGenerator::openListElement(const RVNGPropertyList & /*propList*/) {}
closeListElement()1032 void RVNGSVGDrawingGenerator::closeListElement() {}
1033 
defineParagraphStyle(const RVNGPropertyList &)1034 void RVNGSVGDrawingGenerator::defineParagraphStyle(const RVNGPropertyList & /*propList*/) {}
openParagraph(const RVNGPropertyList &)1035 void RVNGSVGDrawingGenerator::openParagraph(const RVNGPropertyList & /*propList*/) {}
closeParagraph()1036 void RVNGSVGDrawingGenerator::closeParagraph() {}
1037 
defineCharacterStyle(const RVNGPropertyList & propList)1038 void RVNGSVGDrawingGenerator::defineCharacterStyle(const RVNGPropertyList &propList)
1039 {
1040 	if (!propList["librevenge:span-id"])
1041 	{
1042 		RVNG_DEBUG_MSG(("RVNGSVGDrawingGenerator::defineCharacterStyle: can not find the span-id\n"));
1043 		return;
1044 	}
1045 	m_pImpl->m_idSpanMap[propList["librevenge:span-id"]->getInt()]=propList;
1046 }
1047 
openSpan(const RVNGPropertyList & propList)1048 void RVNGSVGDrawingGenerator::openSpan(const RVNGPropertyList &propList)
1049 {
1050 	RVNGPropertyList pList(propList);
1051 	if (propList["librevenge:span-id"] &&
1052 	        m_pImpl->m_idSpanMap.find(propList["librevenge:span-id"]->getInt())!=m_pImpl->m_idSpanMap.end())
1053 		pList=m_pImpl->m_idSpanMap.find(propList["librevenge:span-id"]->getInt())->second;
1054 
1055 	m_pImpl->m_outputSink << "<" << m_pImpl->getNamespaceAndDelim() << "tspan ";
1056 	if (pList["style:font-name"])
1057 		m_pImpl->m_outputSink << "font-family=\"" << pList["style:font-name"]->getStr().cstr() << "\" ";
1058 	if (pList["fo:font-style"])
1059 		m_pImpl->m_outputSink << "font-style=\"" << pList["fo:font-style"]->getStr().cstr() << "\" ";
1060 	if (pList["fo:font-weight"])
1061 		m_pImpl->m_outputSink << "font-weight=\"" << pList["fo:font-weight"]->getStr().cstr() << "\" ";
1062 	if (pList["fo:font-variant"])
1063 		m_pImpl->m_outputSink << "font-variant=\"" << pList["fo:font-variant"]->getStr().cstr() << "\" ";
1064 	if (pList["fo:font-size"])
1065 		m_pImpl->m_outputSink << "font-size=\"" << doubleToString(pList["fo:font-size"]->getDouble()) << "\" ";
1066 	if (pList["fo:color"])
1067 		m_pImpl->m_outputSink << "fill=\"" << pList["fo:color"]->getStr().cstr() << "\" ";
1068 	if (pList["fo:text-transform"])
1069 		m_pImpl->m_outputSink << "text-transform=\"" << pList["fo:text-transform"]->getStr().cstr() << "\" ";
1070 	if (pList["svg:fill-opacity"])
1071 		m_pImpl->m_outputSink << "fill-opacity=\"" << doubleToString(pList["svg:fill-opacity"]->getDouble()) << "\" ";
1072 	if (pList["svg:stroke-opacity"])
1073 		m_pImpl->m_outputSink << "stroke-opacity=\"" << doubleToString(pList["svg:stroke-opacity"]->getDouble()) << "\" ";
1074 	m_pImpl->m_outputSink << ">\n";
1075 }
1076 
closeSpan()1077 void RVNGSVGDrawingGenerator::closeSpan()
1078 {
1079 	m_pImpl->m_outputSink << "</" << m_pImpl->getNamespaceAndDelim() << "tspan>\n";
1080 }
1081 
openLink(const RVNGPropertyList &)1082 void RVNGSVGDrawingGenerator::openLink(const RVNGPropertyList & /*propList*/) {}
closeLink()1083 void RVNGSVGDrawingGenerator::closeLink() {}
1084 
insertText(const RVNGString & str)1085 void RVNGSVGDrawingGenerator::insertText(const RVNGString &str)
1086 {
1087 	m_pImpl->m_outputSink << RVNGString::escapeXML(str).cstr();
1088 }
1089 
insertTab()1090 void RVNGSVGDrawingGenerator::insertTab()
1091 {
1092 	m_pImpl->m_outputSink << "\t";
1093 }
1094 
insertSpace()1095 void RVNGSVGDrawingGenerator::insertSpace()
1096 {
1097 	m_pImpl->m_outputSink << " ";
1098 }
1099 
insertLineBreak()1100 void RVNGSVGDrawingGenerator::insertLineBreak()
1101 {
1102 	m_pImpl->m_outputSink << "\n";
1103 }
1104 
insertField(const RVNGPropertyList &)1105 void RVNGSVGDrawingGenerator::insertField(const RVNGPropertyList & /*propList*/) {}
1106 
startTableObject(const RVNGPropertyList & propList)1107 void RVNGSVGDrawingGenerator::startTableObject(const RVNGPropertyList &propList)
1108 {
1109 	if (m_pImpl->m_table)
1110 	{
1111 		RVNG_DEBUG_MSG(("RVNGSVGDrawingGenerator::startTableObject: a table is already opened\n"));
1112 		return;
1113 	}
1114 	m_pImpl->m_table.reset(new Table(propList));
1115 }
1116 
openTableRow(const RVNGPropertyList & propList)1117 void RVNGSVGDrawingGenerator::openTableRow(const RVNGPropertyList &propList)
1118 {
1119 	if (!m_pImpl->m_table) return;
1120 	m_pImpl->m_table->openRow(propList);
1121 }
1122 
closeTableRow()1123 void RVNGSVGDrawingGenerator::closeTableRow()
1124 {
1125 	if (!m_pImpl->m_table) return;
1126 	m_pImpl->m_table->closeRow();
1127 }
1128 
openTableCell(const RVNGPropertyList & propList)1129 void RVNGSVGDrawingGenerator::openTableCell(const RVNGPropertyList &propList)
1130 {
1131 	if (!m_pImpl->m_table) return;
1132 
1133 	if (propList["librevenge:column"])
1134 		m_pImpl->m_table->m_column=propList["librevenge:column"]->getInt();
1135 	if (propList["librevenge:row"])
1136 		m_pImpl->m_table->m_row=propList["librevenge:row"]->getInt();
1137 
1138 	double x = 0, y=0;
1139 	m_pImpl->m_table->getPosition(m_pImpl->m_table->m_column, m_pImpl->m_table->m_row, x, y);
1140 	m_pImpl->m_outputSink << "<" << m_pImpl->getNamespaceAndDelim() << "text ";
1141 	m_pImpl->m_outputSink << "x=\"" << doubleToString(72*x) << "\" y=\"" << doubleToString(72*y) << "\"";
1142 	m_pImpl->m_outputSink << ">\n";
1143 
1144 	// time to update the next cell's column
1145 	if (propList["table:number-columns-spanned"])
1146 		m_pImpl->m_table->m_column += propList["librevenge:column"]->getInt();
1147 	else
1148 		++m_pImpl->m_table->m_column;
1149 }
1150 
closeTableCell()1151 void RVNGSVGDrawingGenerator::closeTableCell()
1152 {
1153 	if (!m_pImpl->m_table) return;
1154 	m_pImpl->m_outputSink << "</" << m_pImpl->getNamespaceAndDelim() << "text>\n";
1155 }
1156 
insertCoveredTableCell(const RVNGPropertyList &)1157 void RVNGSVGDrawingGenerator::insertCoveredTableCell(const RVNGPropertyList &/*propList*/)
1158 {
1159 	if (!m_pImpl->m_table) return;
1160 	// TODO: implement me
1161 }
1162 
endTableObject()1163 void RVNGSVGDrawingGenerator::endTableObject()
1164 {
1165 	if (!m_pImpl->m_table)
1166 	{
1167 		RVNG_DEBUG_MSG(("RVNGSVGDrawingGenerator::endTableObject: no table is already opened\n"));
1168 		return;
1169 	}
1170 	m_pImpl->m_table.reset();
1171 }
1172 
1173 }
1174 
1175 /* vim:set shiftwidth=4 softtabstop=4 noexpandtab: */
1176