1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2 /* libodfgen
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-2004 William Lachance (wrlach@gmail.com)
11  * Copyright (C) 2004 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  * For further information visit http://libwpd.sourceforge.net
21  */
22 
23 #include "FillManager.hxx"
24 
25 #include <librevenge/librevenge.h>
26 
27 #include "FilterInternal.hxx"
28 #include "DocumentElement.hxx"
29 
clean()30 void FillManager::clean()
31 {
32 	mBitmapStyles.clear();
33 	mGradientStyles.clear();
34 	mHatchStyles.clear();
35 	mOpacityStyles.clear();
36 
37 	mBitmapNameMap.clear();
38 	mGradientNameMap.clear();
39 	mDisplayGradientNameMap.clear();
40 	mHatchNameMap.clear();
41 	mDisplayHatchNameMap.clear();
42 	mOpacityNameMap.clear();
43 	mDisplayOpacityNameMap.clear();
44 }
45 
write(OdfDocumentHandler * pHandler) const46 void FillManager::write(OdfDocumentHandler *pHandler) const
47 {
48 	for (const auto &bitmapStyle : mBitmapStyles)
49 		bitmapStyle->write(pHandler);
50 	for (const auto &gradientStyle : mGradientStyles)
51 		gradientStyle->write(pHandler);
52 	for (const auto &hatchStyle : mHatchStyles)
53 		hatchStyle->write(pHandler);
54 	for (const auto &opacityStyle : mOpacityStyles)
55 		opacityStyle->write(pHandler);
56 }
57 
getStyleNameForBitmap(librevenge::RVNGString const & bitmap)58 librevenge::RVNGString FillManager::getStyleNameForBitmap(librevenge::RVNGString const &bitmap)
59 {
60 	if (bitmap.empty())
61 		return "";
62 	if (mBitmapNameMap.find(bitmap) != mBitmapNameMap.end())
63 		return mBitmapNameMap.find(bitmap)->second;
64 
65 	librevenge::RVNGString name;
66 	name.sprintf("Bitmap_%i", (int) mBitmapNameMap.size());
67 	mBitmapNameMap[bitmap]=name;
68 
69 	auto openElement = std::make_shared<TagOpenElement>("draw:fill-image");
70 	openElement->addAttribute("draw:name", name);
71 	mBitmapStyles.push_back(openElement);
72 	mBitmapStyles.push_back(std::make_shared<TagOpenElement>("office:binary-data"));
73 	mBitmapStyles.push_back(std::make_shared<CharDataElement>(bitmap));
74 	mBitmapStyles.push_back(std::make_shared<TagCloseElement>("office:binary-data"));
75 	mBitmapStyles.push_back(std::make_shared<TagCloseElement>("draw:fill-image"));
76 	return name;
77 }
78 
getStyleNameForGradient(librevenge::RVNGPropertyList const & style,bool & needCreateOpacityStyle)79 librevenge::RVNGString FillManager::getStyleNameForGradient(librevenge::RVNGPropertyList const &style,
80                                                             bool &needCreateOpacityStyle)
81 {
82 	needCreateOpacityStyle=false;
83 
84 	bool isStyle=style["style:display-name"]!=nullptr;
85 	bool hasParent=style["librevenge:parent-display-name"]!=nullptr;
86 	bool checkEmpty=isStyle || hasParent;
87 	librevenge::RVNGPropertyList pList;
88 	if (!checkEmpty)
89 	{
90 		// default value
91 		pList.insert("draw:style", "linear");
92 		pList.insert("draw:border", "0%");
93 		pList.insert("draw:start-intensity", "100%");
94 		pList.insert("draw:end-intensity", "100%");
95 	}
96 	// property rename
97 	if (style["svg:cx"])
98 		pList.insert("draw:cx", style["svg:cx"]->getStr());
99 	if (style["svg:cy"])
100 		pList.insert("draw:cy", style["svg:cy"]->getStr());
101 	// prepare angle: ODG angle unit is 0.1 degree
102 	librevenge::RVNGString angleValue;
103 	if (!checkEmpty || style["draw:angle"])
104 	{
105 		double angle = style["draw:angle"] ? style["draw:angle"]->getDouble() : 0.0;
106 		while (angle < 0)
107 			angle += 360;
108 		while (angle > 360)
109 			angle -= 360;
110 		angleValue.sprintf("%i", (unsigned)(angle*10));
111 		pList.insert("draw:angle", angleValue);
112 	}
113 	// gradient vector
114 	const librevenge::RVNGPropertyListVector *gradient = style.child("svg:linearGradient");
115 	if (!gradient)
116 		gradient = style.child("svg:radialGradient");
117 	if (gradient) pList.insert("svg:linearGradient", *gradient);
118 	char const *wh[] =
119 	{
120 		"draw:border", "draw:cx", "draw:cy", "draw:end-color", "draw:end-intensity",
121 		"draw:start-color", "draw:start-intensity", "draw:style"
122 	};
123 	for (auto &i : wh)
124 	{
125 		if (style[i])
126 			pList.insert(i, style[i]->getStr());
127 	}
128 	if (pList.empty())
129 	{
130 		if (isStyle && hasParent)
131 		{
132 			librevenge::RVNGString pName=style["librevenge:parent-display-name"]->getStr();
133 			if (mDisplayGradientNameMap.find(pName)!=mDisplayGradientNameMap.end())
134 				mDisplayGradientNameMap[style["style:display-name"]->getStr()]=mDisplayGradientNameMap.find(pName)->second;
135 		}
136 		return "";
137 	}
138 	librevenge::RVNGString hashKey = pList.getPropString();
139 	if (mGradientNameMap.find(hashKey) != mGradientNameMap.end())
140 		return mGradientNameMap.find(hashKey)->second;
141 
142 	librevenge::RVNGString name;
143 	name.sprintf("Gradient_%i", (int) mGradientNameMap.size());
144 	mGradientNameMap[hashKey]=name;
145 	if (isStyle) mDisplayGradientNameMap[style["style:display-name"]->getStr()]=name;
146 
147 	std::shared_ptr<TagOpenElement> openElement(std::make_shared<TagOpenElement>("draw:gradient"));
148 	openElement->addAttribute("draw:name", name);
149 	if (hasParent)
150 	{
151 		librevenge::RVNGString pName=style["librevenge:parent-display-name"]->getStr();
152 		if (mDisplayGradientNameMap.find(pName)!=mDisplayGradientNameMap.end())
153 			openElement->addAttribute("style:parent-style-name", mDisplayGradientNameMap.find(pName)->second);
154 	}
155 	if (pList["draw:style"])
156 		openElement->addAttribute("draw:style", pList["draw:style"]->getStr());
157 	if (!angleValue.empty())
158 		openElement->addAttribute("draw:angle", angleValue);
159 	if (pList["draw:cx"])
160 		openElement->addAttribute("draw:cx", pList["draw:cx"]->getStr());
161 	if (pList["draw:cy"])
162 		openElement->addAttribute("draw:cy", pList["draw:cy"]->getStr());
163 
164 	if (!gradient || !gradient->count())
165 	{
166 		char const *attrib[] =
167 		{
168 			"draw:start-color", "draw:end-color", "draw:border", "draw:start-intensity", "draw:end-intensity"
169 		};
170 		for (auto &i : attrib)
171 		{
172 			if (pList[i])
173 				openElement->addAttribute(i, pList[i]->getStr());
174 		}
175 
176 		// Work around a mess in LibreOffice where both opacities of 100% are interpreted as complete transparency
177 		// Nevertheless, when one is different, immediately, they are interpreted correctly
178 		if (style["librevenge:start-opacity"] && style["librevenge:end-opacity"]
179 		        && (style["librevenge:start-opacity"]->getDouble() < 1.0 || style["librevenge:end-opacity"]->getDouble() < 1.0))
180 			needCreateOpacityStyle=true;
181 	}
182 	else if (gradient->count() >= 2)
183 	{
184 		if ((*gradient)[1]["svg:stop-color"])
185 			openElement->addAttribute("draw:start-color", (*gradient)[1]["svg:stop-color"]->getStr());
186 		if ((*gradient)[0]["svg:stop-color"])
187 			openElement->addAttribute("draw:end-color", (*gradient)[0]["svg:stop-color"]->getStr());
188 		if ((*gradient)[0]["svg:stop-opacity"] || (*gradient)[1]["svg:stop-opacity"])
189 			needCreateOpacityStyle=true;
190 		openElement->addAttribute("draw:border", "0%");
191 	}
192 	else
193 	{
194 		return "";
195 	}
196 
197 	mGradientStyles.push_back(openElement);
198 	mGradientStyles.push_back(std::make_shared<TagCloseElement>("draw:gradient"));
199 	return name;
200 }
201 
getStyleNameForHatch(librevenge::RVNGPropertyList const & style)202 librevenge::RVNGString FillManager::getStyleNameForHatch(librevenge::RVNGPropertyList const &style)
203 {
204 	librevenge::RVNGPropertyList pList;
205 	bool isStyle=style["style:display-name"]!=nullptr;
206 	bool hasParent=style["librevenge:parent-display-name"]!=nullptr;
207 	// basic data
208 	char const *wh[] = { "draw:color", "draw:distance", "draw:rotation", "draw:style" };
209 	for (auto &i : wh)
210 	{
211 		if (style[i])
212 			pList.insert(i, style[i]->getStr());
213 	}
214 	if (pList.empty() && (isStyle || hasParent))
215 	{
216 		if (isStyle && hasParent)
217 		{
218 			librevenge::RVNGString pName=style["librevenge:parent-display-name"]->getStr();
219 			if (mDisplayHatchNameMap.find(pName)!=mDisplayHatchNameMap.end())
220 				mDisplayHatchNameMap[style["style:display-name"]->getStr()]=mDisplayHatchNameMap.find(pName)->second;
221 		}
222 		return "";
223 	}
224 	librevenge::RVNGString hashKey = pList.getPropString();
225 	if (mHatchNameMap.find(hashKey) != mHatchNameMap.end())
226 		return mHatchNameMap.find(hashKey)->second;
227 
228 	librevenge::RVNGString name;
229 	name.sprintf("Hatch_%i", (int) mHatchNameMap.size());
230 	mHatchNameMap[hashKey]=name;
231 	if (isStyle) mDisplayHatchNameMap[style["style:display-name"]->getStr()]=name;
232 
233 	auto openElement = std::make_shared<TagOpenElement>("draw:hatch");
234 	openElement->addAttribute("draw:name", name);
235 	if (hasParent)
236 	{
237 		librevenge::RVNGString pName=style["librevenge:parent-display-name"]->getStr();
238 		if (mDisplayHatchNameMap.find(pName)!=mDisplayHatchNameMap.end())
239 			openElement->addAttribute("style:parent-style-name", mDisplayHatchNameMap.find(pName)->second);
240 	}
241 	if (style["draw:color"])
242 		openElement->addAttribute("draw:color", style["draw:color"]->getStr());
243 	if (style["draw:distance"])
244 		openElement->addAttribute("draw:distance", style["draw:distance"]->getStr());
245 	if (style["draw:style"])
246 		openElement->addAttribute("draw:style", style["draw:style"]->getStr());
247 	// prepare angle: ODG angle unit is 0.1 degree
248 	double rotation = style["draw:rotation"] ? style["draw:rotation"]->getDouble() : 0.0;
249 	while (rotation < 0)
250 		rotation += 360;
251 	while (rotation > 360)
252 		rotation -= 360;
253 	librevenge::RVNGString sValue;
254 	sValue.sprintf("%i", (unsigned)(rotation*10));
255 	openElement->addAttribute("draw:rotation", sValue);
256 
257 	mHatchStyles.push_back(openElement);
258 	mHatchStyles.push_back(std::make_shared<TagCloseElement>("draw:hatch"));
259 	return name;
260 }
261 
getStyleNameForOpacity(librevenge::RVNGPropertyList const & style)262 librevenge::RVNGString FillManager::getStyleNameForOpacity(librevenge::RVNGPropertyList const &style)
263 {
264 	bool isStyle=style["style:display-name"]!=nullptr;
265 	bool hasParent=style["librevenge:parent-display-name"]!=nullptr;
266 	bool checkEmpty=isStyle || hasParent;
267 
268 	librevenge::RVNGPropertyList pList;
269 	// default value
270 	if (!checkEmpty)
271 	{
272 		pList.insert("draw:border", "0%");
273 		pList.insert("draw:start", "100%");
274 		pList.insert("draw:end", "100%");
275 	}
276 	// property rename
277 	if (style["svg:cx"])
278 		pList.insert("draw:cx", style["svg:cx"]->getStr());
279 	if (style["svg:cy"])
280 		pList.insert("draw:cy", style["svg:cy"]->getStr());
281 	if (style["draw:start-intensity"])
282 		pList.insert("draw:start", style["draw:start-intensity"]->getStr());
283 	if (style["draw:end-intensity"])
284 		pList.insert("draw:end", style["draw:end-intensity"]->getStr());
285 	// data in gradient vector
286 	const librevenge::RVNGPropertyListVector *gradient = style.child("svg:linearGradient");
287 	if (!gradient)
288 		gradient = style.child("svg:radialGradient");
289 	if (gradient && gradient->count() >= 2)
290 	{
291 		if ((*gradient)[1]["svg:stop-opacity"])
292 			pList.insert("draw:start", (*gradient)[1]["svg:stop-opacity"]->getStr());
293 		if ((*gradient)[0]["svg:stop-opacity"])
294 			pList.insert("draw:end", (*gradient)[0]["svg:stop-opacity"]->getStr());
295 	}
296 	// prepare angle: ODG angle unit is 0.1 degree
297 	librevenge::RVNGString angleValue;
298 	if (!checkEmpty || style["draw:angle"])
299 	{
300 		double angle = style["draw:angle"] ? style["draw:angle"]->getDouble() : 0.0;
301 		while (angle < 0)
302 			angle += 360;
303 		while (angle > 360)
304 			angle -= 360;
305 		angleValue.sprintf("%i", (unsigned)(angle*10));
306 		pList.insert("draw:angle", angleValue);
307 	}
308 	// basic data
309 	char const *wh[] = { "draw:border", "draw:cx", "draw:cy"	};
310 	for (auto &i : wh)
311 	{
312 		if (style[i])
313 			pList.insert(i, style[i]->getStr());
314 	}
315 	if (pList.empty())
316 	{
317 		if (isStyle && hasParent)
318 		{
319 			librevenge::RVNGString pName=style["librevenge:parent-display-name"]->getStr();
320 			if (mDisplayOpacityNameMap.find(pName)!=mDisplayOpacityNameMap.end())
321 				mDisplayOpacityNameMap[style["style:display-name"]->getStr()]=mDisplayOpacityNameMap.find(pName)->second;
322 		}
323 		return "";
324 	}
325 	librevenge::RVNGString hashKey = pList.getPropString();
326 	if (mOpacityNameMap.find(hashKey) != mOpacityNameMap.end())
327 		return mOpacityNameMap.find(hashKey)->second;
328 
329 	librevenge::RVNGString name;
330 	name.sprintf("Transparency_%i", (int) mOpacityNameMap.size());
331 	mOpacityNameMap[hashKey]=name;
332 	if (isStyle) mDisplayOpacityNameMap[style["style:display-name"]->getStr()]=name;
333 
334 	auto openElement = std::make_shared<TagOpenElement>("draw:opacity");
335 	openElement->addAttribute("draw:name", name);
336 	if (hasParent)
337 	{
338 		librevenge::RVNGString pName=style["librevenge:parent-display-name"]->getStr();
339 		if (mDisplayOpacityNameMap.find(pName)!=mDisplayOpacityNameMap.end())
340 			openElement->addAttribute("style:parent-style-name", mDisplayOpacityNameMap.find(pName)->second);
341 	}
342 	if (!angleValue.empty())
343 		openElement->addAttribute("draw:angle", angleValue);
344 	if (pList["draw:border"])
345 		openElement->addAttribute("draw:border", pList["draw:border"]->getStr());
346 	if (pList["draw:cx"])
347 		openElement->addAttribute("draw:cx", pList["draw:cx"]->getStr());
348 	if (pList["draw:cy"])
349 		openElement->addAttribute("draw:cy", pList["draw:cy"]->getStr());
350 	if (pList["draw:start"])
351 		openElement->addAttribute("draw:start", pList["draw:start"]->getStr());
352 	if (pList["draw:end"])
353 		openElement->addAttribute("draw:end", pList["draw:end"]->getStr());
354 
355 	mOpacityStyles.push_back(openElement);
356 	mOpacityStyles.push_back(std::make_shared<TagCloseElement>("draw:opacity"));
357 	return name;
358 }
359 
addProperties(librevenge::RVNGPropertyList const & style,librevenge::RVNGPropertyList & element)360 void FillManager::addProperties(librevenge::RVNGPropertyList const &style, librevenge::RVNGPropertyList &element)
361 {
362 	bool sendAll=false;
363 	if (style["style:display-name"])
364 		sendAll=true;
365 	else if (!style["draw:fill"])
366 	{
367 		if (!style["librevenge:parent-display-name"])
368 			return;
369 		else
370 			sendAll=true;
371 	}
372 	const librevenge::RVNGString &fill = style["draw:fill"] ?  style["draw:fill"]->getStr() : "";
373 	if (fill == "none")
374 		element.insert("draw:fill", "none");
375 	if (sendAll || (fill == "bitmap" && style["draw:fill-image"] && style["librevenge:mime-type"]))
376 	{
377 		librevenge::RVNGString name=style["draw:fill-image"] ? getStyleNameForBitmap(style["draw:fill-image"]->getStr()) : "";
378 		if (sendAll || !name.empty())
379 		{
380 			if (fill=="bitmap")
381 				element.insert("draw:fill", "bitmap");
382 			if (!name.empty())
383 				element.insert("draw:fill-image-name", name);
384 			if (style["draw:fill-image-width"])
385 				element.insert("draw:fill-image-width", style["draw:fill-image-width"]->getStr());
386 			else if (style["svg:width"])
387 				element.insert("draw:fill-image-width", style["svg:width"]->getStr());
388 			if (style["draw:fill-image-height"])
389 				element.insert("draw:fill-image-height", style["draw:fill-image-height"]->getStr());
390 			else if (style["svg:height"])
391 				element.insert("draw:fill-image-height", style["svg:height"]->getStr());
392 			if (style["style:repeat"])
393 				element.insert("style:repeat", style["style:repeat"]->getStr());
394 			if (style["draw:fill-image-ref-point"])
395 				element.insert("draw:fill-image-ref-point", style["draw:fill-image-ref-point"]->getStr());
396 			if (style["draw:fill-image-ref-point-x"])
397 				element.insert("draw:fill-image-ref-point-x", style["draw:fill-image-ref-point-x"]->getStr());
398 			if (style["draw:fill-image-ref-point-y"])
399 				element.insert("draw:fill-image-ref-point-y", style["draw:fill-image-ref-point-y"]->getStr());
400 			if (style["draw:opacity"])
401 				element.insert("draw:opacity", style["draw:opacity"]->getStr());
402 		}
403 		else
404 			element.insert("draw:fill", "none");
405 	}
406 	if (sendAll || fill == "gradient")
407 	{
408 		librevenge::RVNGString gradientName(""), opacityName("");
409 		bool bUseOpacityGradient = false;
410 		gradientName=getStyleNameForGradient(style, bUseOpacityGradient);
411 		if (!gradientName.empty())
412 		{
413 			if (fill=="gradient")
414 				element.insert("draw:fill", "gradient");
415 			element.insert("draw:fill-gradient-name", gradientName);
416 			if (bUseOpacityGradient)
417 			{
418 				opacityName=getStyleNameForOpacity(style);
419 				if (!opacityName.empty())
420 					element.insert("draw:opacity-name", opacityName);
421 			}
422 		}
423 		else if (!sendAll)
424 		{
425 			element.insert("draw:fill", "solid");
426 			// try to use the gradient to define the color
427 			const librevenge::RVNGPropertyListVector *gradient = style.child("svg:linearGradient");
428 			if (!gradient)
429 				gradient = style.child("svg:radialGradient");
430 			if (gradient && gradient->count() >= 1 && (*gradient)[0]["svg:stop-color"])
431 				element.insert("draw:fill-color", (*gradient)[0]["svg:stop-color"]->getStr());
432 		}
433 	}
434 	if (sendAll || fill == "hatch")
435 	{
436 		const librevenge::RVNGString hatchName(getStyleNameForHatch(style));
437 		if (sendAll || !hatchName.empty())
438 		{
439 			if (fill=="hatch")
440 				element.insert("draw:fill", "hatch");
441 			if (!hatchName.empty())
442 				element.insert("draw:fill-hatch-name", hatchName);
443 			if (style["draw:fill-color"])
444 				element.insert("draw:fill-color", style["draw:fill-color"]->getStr());
445 			if (style["draw:opacity"])
446 				element.insert("draw:opacity", style["draw:opacity"]->getStr());
447 			if (style["draw:fill-hatch-solid"])
448 				element.insert("draw:fill-hatch-solid", style["draw:fill-hatch-solid"]->getStr());
449 		}
450 		else
451 		{
452 			element.insert("draw:fill", "none");
453 		}
454 	}
455 	if (sendAll || fill == "solid")
456 	{
457 		if (fill=="solid")
458 			element.insert("draw:fill", "solid");
459 		if (style["draw:fill-color"])
460 			element.insert("draw:fill-color", style["draw:fill-color"]->getStr());
461 		if (style["draw:opacity"])
462 			element.insert("draw:opacity", style["draw:opacity"]->getStr());
463 	}
464 }
465 
466 /* vim:set shiftwidth=4 softtabstop=4 noexpandtab: */
467