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