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 "GraphicStyle.hxx"
24
25 #include <librevenge/librevenge.h>
26
27 #include "FilterInternal.hxx"
28 #include "FillManager.hxx"
29 #include "DocumentElement.hxx"
30
GraphicStyle(const librevenge::RVNGPropertyList & xPropList,const char * psName,Style::Zone zone)31 GraphicStyle::GraphicStyle(const librevenge::RVNGPropertyList &xPropList, const char *psName, Style::Zone zone) :
32 Style(psName, zone), mPropList(xPropList)
33 {
34 }
35
~GraphicStyle()36 GraphicStyle::~GraphicStyle()
37 {
38 }
39
write(OdfDocumentHandler * pHandler) const40 void GraphicStyle::write(OdfDocumentHandler *pHandler) const
41 {
42 librevenge::RVNGPropertyList openElement;
43 openElement.insert("style:name", getName());
44 openElement.insert("style:family", "graphic");
45 if (mPropList["style:parent-style-name"])
46 openElement.insert("style:parent-style-name", mPropList["style:parent-style-name"]->getStr());
47 else
48 openElement.insert("style:parent-style-name", "standard");
49 if (mPropList["style:display-name"])
50 openElement.insert("style:display-name", mPropList["style:display-name"]->getStr());
51 pHandler->startElement("style:style", openElement);
52
53 librevenge::RVNGPropertyList graphicElement;
54 librevenge::RVNGPropertyList::Iter i(mPropList);
55 for (i.rewind(); i.next();)
56 {
57 if (strcmp(i.key(), "style:display-name")==0 || strcmp(i.key(), "style:parent-style-name") == 0 ||
58 strncmp(i.key(), "librevenge:", 11)==0)
59 continue;
60 graphicElement.insert(i.key(),i()->getStr());
61 }
62 pHandler->startElement("style:graphic-properties", graphicElement);
63 pHandler->endElement("style:graphic-properties");
64
65 if (mPropList["draw:show-unit"] && mPropList["draw:show-unit"]->getStr()=="true")
66 {
67 librevenge::RVNGPropertyList textElement;
68 textElement.insert("fo:font-size", 12, librevenge::RVNG_POINT);
69 pHandler->startElement("style:text-properties", textElement);
70 pHandler->endElement("style:text-properties");
71 }
72 pHandler->endElement("style:style");
73 }
74
75 //
76 // manager
77 //
clean()78 void GraphicStyleManager::clean()
79 {
80 mStyles.resize(0);
81
82 mMarkerStyles.clear();
83 mStrokeDashStyles.clear();
84
85 mMarkerNameMap.clear();
86 mStrokeDashNameMap.clear();
87 mDisplayStrokeDashNameMap.clear();
88 mStyleNameMap.clear();
89 mDisplayNameMap.clear();
90 }
91
write(OdfDocumentHandler * pHandler,Style::Zone zone) const92 void GraphicStyleManager::write(OdfDocumentHandler *pHandler, Style::Zone zone) const
93 {
94 if (zone==Style::Z_Style)
95 {
96 for (const auto &markerStyle : mMarkerStyles)
97 markerStyle->write(pHandler);
98 for (const auto &strokeDashStyle : mStrokeDashStyles)
99 strokeDashStyle->write(pHandler);
100 }
101 for (const auto &style : mStyles)
102 {
103 if (style && style->getZone()==zone)
104 style->write(pHandler);
105 }
106 }
107
findOrAdd(librevenge::RVNGPropertyList const & propList,Style::Zone zone)108 librevenge::RVNGString GraphicStyleManager::findOrAdd(librevenge::RVNGPropertyList const &propList, Style::Zone zone)
109 {
110 librevenge::RVNGPropertyList pList(propList);
111 if (propList["style:display-name"])
112 zone=Style::Z_Style;
113 else if (zone==Style::Z_Unknown)
114 zone=Style::Z_ContentAutomatic;
115 pList.insert("librevenge:zone-style", int(zone));
116 librevenge::RVNGString hashKey = pList.getPropString();
117 if (mStyleNameMap.find(hashKey) != mStyleNameMap.end())
118 return mStyleNameMap.find(hashKey)->second;
119
120 librevenge::RVNGString name;
121 if (zone==Style::Z_StyleAutomatic)
122 name.sprintf("gr_M%i", (int) mStyleNameMap.size());
123 else if (zone==Style::Z_Style)
124 name.sprintf("GraphicStyle_%i", (int) mStyleNameMap.size());
125 else
126 name.sprintf("gr_%i", (int) mStyleNameMap.size());
127
128 mStyleNameMap[hashKey]=name;
129
130 pList.remove("librevenge:zone-style");
131 if (pList["librevenge:parent-display-name"])
132 {
133 // replace "librevenge:parent-display-name" by the final "style:parent-style-name"
134 librevenge::RVNGString pName(pList["librevenge:parent-display-name"]->getStr());
135 pList.remove("librevenge:parent-display-name");
136 if (mDisplayNameMap.find(pName) != mDisplayNameMap.end())
137 pList.insert("style:parent-style-name", mDisplayNameMap.find(pName)->second);
138 else
139 {
140 ODFGEN_DEBUG_MSG(("GraphicStyleManager::findOrAdd: can not find any style with name %s\n", pName.cstr()));
141 }
142 }
143 std::shared_ptr<GraphicStyle> style(new GraphicStyle(pList, name.cstr(), zone));
144 mStyles.push_back(style);
145
146 if (propList["style:display-name"] && !propList["style:display-name"]->getStr().empty())
147 mDisplayNameMap[propList["style:display-name"]->getStr()]=name;
148 return name;
149 }
150
getFinalDisplayName(const librevenge::RVNGString & displayName)151 librevenge::RVNGString GraphicStyleManager::getFinalDisplayName(const librevenge::RVNGString &displayName)
152 {
153 if (mDisplayNameMap.find(displayName) != mDisplayNameMap.end())
154 return mDisplayNameMap.find(displayName)->second;
155 ODFGEN_DEBUG_MSG(("GraphicStyleManager::getName: can not find style with display name: %s\n", displayName.cstr()));
156 return librevenge::RVNGString("");
157 }
158
159 ////////////////////////////////////////////////////////////
160 //
161 ////////////////////////////////////////////////////////////
getStyleNameForMarker(librevenge::RVNGPropertyList const & style,bool startMarker)162 librevenge::RVNGString GraphicStyleManager::getStyleNameForMarker(librevenge::RVNGPropertyList const &style, bool startMarker)
163 {
164 librevenge::RVNGPropertyList pList;
165 if (startMarker)
166 {
167 if (!style["draw:marker-start-path"])
168 return "";
169 pList.insert("svg:d", style["draw:marker-start-path"]->getStr());
170 if (style["draw:marker-start-viewbox"])
171 pList.insert("svg:viewBox", style["draw:marker-start-viewbox"]->getStr());
172 }
173 else
174 {
175 if (!style["draw:marker-end-path"])
176 return "";
177 pList.insert("svg:d", style["draw:marker-end-path"]->getStr());
178 if (style["draw:marker-end-viewbox"])
179 pList.insert("svg:viewBox", style["draw:marker-end-viewbox"]->getStr());
180 }
181 librevenge::RVNGString hashKey = pList.getPropString();
182 if (mMarkerNameMap.find(hashKey) != mMarkerNameMap.end())
183 return mMarkerNameMap.find(hashKey)->second;
184
185 librevenge::RVNGString name;
186 name.sprintf("Marker_%i", (int) mMarkerNameMap.size());
187 mMarkerNameMap[hashKey]=name;
188
189 auto openElement = std::make_shared<TagOpenElement>("draw:marker");
190 openElement->addAttribute("draw:name", name);
191 if (pList["svg:viewBox"])
192 openElement->addAttribute("svg:viewBox", pList["svg:viewBox"]->getStr());
193 openElement->addAttribute("svg:d", pList["svg:d"]->getStr());
194 mMarkerStyles.push_back(openElement);
195 mMarkerStyles.push_back(std::make_shared<TagCloseElement>("draw:marker"));
196 return name;
197 }
198
getStyleNameForStrokeDash(librevenge::RVNGPropertyList const & style)199 librevenge::RVNGString GraphicStyleManager::getStyleNameForStrokeDash(librevenge::RVNGPropertyList const &style)
200 {
201 bool isStyle=style["style:display-name"]!=nullptr;
202 bool hasParent=style["librevenge:parent-display-name"]!=nullptr;
203 bool checkEmpty=isStyle || hasParent;
204
205 librevenge::RVNGPropertyList pList;
206 if (style["svg:stroke-linecap"])
207 pList.insert("draw:style", style["svg:stroke-linecap"]->getStr());
208 else if (!checkEmpty)
209 pList.insert("draw:style", "rect");
210 if (style["draw:distance"])
211 pList.insert("draw:distance", style["draw:distance"]->getStr());
212 if (style["draw:dots1"])
213 pList.insert("draw:dots1", style["draw:dots1"]->getStr());
214 if (style["draw:dots1-length"])
215 pList.insert("draw:dots1-length", style["draw:dots1-length"]->getStr());
216 if (style["draw:dots2"])
217 pList.insert("draw:dots2", style["draw:dots2"]->getStr());
218 if (style["draw:dots2-length"])
219 pList.insert("draw:dots2-length", style["draw:dots2-length"]->getStr());
220 if (pList.empty())
221 {
222 if (isStyle && hasParent)
223 {
224 librevenge::RVNGString pName=style["librevenge:parent-display-name"]->getStr();
225 if (mDisplayStrokeDashNameMap.find(pName)!=mDisplayStrokeDashNameMap.end())
226 mDisplayStrokeDashNameMap[style["style:display-name"]->getStr()]=mDisplayStrokeDashNameMap.find(pName)->second;
227 }
228 return "";
229 }
230
231 librevenge::RVNGString hashKey = pList.getPropString();
232 if (mStrokeDashNameMap.find(hashKey) != mStrokeDashNameMap.end())
233 return mStrokeDashNameMap.find(hashKey)->second;
234
235 librevenge::RVNGString name;
236 name.sprintf("Dash_%i", (int) mStrokeDashNameMap.size());
237 mStrokeDashNameMap[hashKey]=name;
238 if (isStyle) mDisplayStrokeDashNameMap[style["style:display-name"]->getStr()]=name;
239
240 auto openElement = std::make_shared<TagOpenElement>("draw:stroke-dash");
241 openElement->addAttribute("draw:name", name);
242 if (hasParent)
243 {
244 librevenge::RVNGString pName=style["librevenge:parent-display-name"]->getStr();
245 if (mDisplayStrokeDashNameMap.find(pName)!=mDisplayStrokeDashNameMap.end())
246 openElement->addAttribute("style:parent-style-name", mDisplayStrokeDashNameMap.find(pName)->second);
247 }
248 if (pList["draw:style"])
249 openElement->addAttribute("draw:style", pList["draw:style"]->getStr());
250 if (pList["draw:distance"])
251 openElement->addAttribute("draw:distance", pList["draw:distance"]->getStr());
252 if (pList["draw:dots1"])
253 openElement->addAttribute("draw:dots1", pList["draw:dots1"]->getStr());
254 if (pList["draw:dots1-length"])
255 openElement->addAttribute("draw:dots1-length", pList["draw:dots1-length"]->getStr());
256 if (pList["draw:dots2"])
257 openElement->addAttribute("draw:dots2", pList["draw:dots2"]->getStr());
258 if (pList["draw:dots2-length"])
259 openElement->addAttribute("draw:dots2-length", pList["draw:dots2-length"]->getStr());
260 mStrokeDashStyles.push_back(openElement);
261 mStrokeDashStyles.push_back(std::make_shared<TagCloseElement>("draw:stroke-dash"));
262 return name;
263 }
264
addGraphicProperties(librevenge::RVNGPropertyList const & style,librevenge::RVNGPropertyList & element)265 void GraphicStyleManager::addGraphicProperties(librevenge::RVNGPropertyList const &style, librevenge::RVNGPropertyList &element)
266 {
267 bool isStyleNamed=false, hasParent=false;
268 if (style["style:display-name"])
269 {
270 element.insert("style:display-name", style["style:display-name"]->getStr());
271 isStyleNamed=true;
272 }
273 if (style["librevenge:parent-display-name"])
274 {
275 hasParent=true;
276 element.insert("librevenge:parent-display-name", style["librevenge:parent-display-name"]->getStr());
277 }
278 if (style["draw:stroke"] && style["draw:stroke"]->getStr() == "none")
279 element.insert("draw:stroke", "none");
280 else
281 {
282 librevenge::RVNGString name("");
283 if (style["draw:stroke"] && style["draw:stroke"]->getStr() == "dash")
284 name=getStyleNameForStrokeDash(style);
285 if (!name.empty())
286 {
287 element.insert("draw:stroke", "dash");
288 element.insert("draw:stroke-dash", name);
289 }
290 else if (style["draw:stroke"] || (!isStyleNamed && !hasParent))
291 element.insert("draw:stroke", "solid");
292 }
293 if (!style["draw:stroke"] || style["draw:stroke"]->getStr() != "none" || isStyleNamed)
294 {
295 if (style["svg:stroke-width"])
296 element.insert("svg:stroke-width", style["svg:stroke-width"]->getStr());
297 if (style["svg:stroke-color"])
298 element.insert("svg:stroke-color", style["svg:stroke-color"]->getStr());
299 if (style["svg:stroke-opacity"])
300 element.insert("svg:stroke-opacity", style["svg:stroke-opacity"]->getStr());
301 if (style["svg:stroke-linejoin"])
302 element.insert("draw:stroke-linejoin", style["svg:stroke-linejoin"]->getStr());
303 if (style["svg:stroke-linecap"])
304 element.insert("svg:stroke-linecap", style["svg:stroke-linecap"]->getStr());
305 }
306
307 if (style["draw:color-mode"] && style["draw:color-mode"]->getStr().len() > 0)
308 element.insert("draw:color-mode", style["draw:color-mode"]->getStr());
309 if (style["draw:luminance"] && style["draw:luminance"]->getStr().len() > 0)
310 element.insert("draw:luminance", style["draw:luminance"]->getStr());
311 if (style["draw:contrast"] && style["draw:contrast"]->getStr().len() > 0)
312 element.insert("draw:contrast", style["draw:contrast"]->getStr());
313 if (style["draw:gamma"] && style["draw:gamma"]->getStr().len() > 0)
314 element.insert("draw:gamma", style["draw:gamma"]->getStr());
315 if (style["draw:red"] && style["draw:red"]->getStr().len() > 0)
316 element.insert("draw:red", style["draw:red"]->getStr());
317 if (style["draw:green"] && style["draw:green"]->getStr().len() > 0)
318 element.insert("draw:green", style["draw:green"]->getStr());
319 if (style["draw:blue"] && style["draw:blue"]->getStr().len() > 0)
320 element.insert("draw:blue", style["draw:blue"]->getStr());
321
322 // only append shadow props if the shape is filled
323 if (!style["draw:fill"] || style["draw:fill"]->getStr() != "none" || isStyleNamed)
324 {
325 if (style["draw:shadow"])
326 element.insert("draw:shadow", style["draw:shadow"]->getStr());
327 else if (!isStyleNamed && !hasParent)
328 element.insert("draw:shadow", "hidden");
329 if (style["draw:shadow-offset-x"])
330 element.insert("draw:shadow-offset-x", style["draw:shadow-offset-x"]->getStr());
331 if (style["draw:shadow-offset-y"])
332 element.insert("draw:shadow-offset-y", style["draw:shadow-offset-y"]->getStr());
333 if (style["draw:shadow-color"])
334 element.insert("draw:shadow-color", style["draw:shadow-color"]->getStr());
335 if (style["draw:shadow-opacity"])
336 element.insert("draw:shadow-opacity", style["draw:shadow-opacity"]->getStr());
337 if (style["svg:fill-rule"])
338 element.insert("svg:fill-rule", style["svg:fill-rule"]->getStr());
339 }
340
341 mFillManager.addProperties(style, element);
342
343 // marker
344 if (style["draw:marker-start-path"])
345 {
346 librevenge::RVNGString marker=getStyleNameForMarker(style, true);
347 if (!marker.empty())
348 element.insert("draw:marker-start", marker);
349 }
350 if (style["draw:marker-start-path"] || isStyleNamed || hasParent)
351 {
352 if (style["draw:marker-start-center"])
353 element.insert("draw:marker-start-center", style["draw:marker-start-center"]->getStr());
354 if (style["draw:marker-start-width"])
355 element.insert("draw:marker-start-width", style["draw:marker-start-width"]->getStr());
356 else if (!isStyleNamed && !hasParent)
357 element.insert("draw:marker-start-width", "0.118in");
358 }
359 if (style["draw:marker-end-path"])
360 {
361 librevenge::RVNGString marker=getStyleNameForMarker(style, false);
362 if (!marker.empty())
363 element.insert("draw:marker-end", marker);
364 }
365 if (style["draw:marker-end-path"] || isStyleNamed || hasParent)
366 {
367 if (style["draw:marker-end-center"])
368 element.insert("draw:marker-end-center", style["draw:marker-end-center"]->getStr());
369 if (style["draw:marker-end-width"])
370 element.insert("draw:marker-end-width", style["draw:marker-end-width"]->getStr());
371 else if (!isStyleNamed && !hasParent)
372 element.insert("draw:marker-end-width", "0.118in");
373 }
374 // other
375 char const *others[] =
376 {
377 "draw:auto-grow-height", "draw:auto-grow-width",
378 "draw:fit-to-size",
379 "draw:ole-draw-aspect",
380 "draw:show-unit",
381 "fo:background-color",
382 "fo:border","fo:border-top","fo:border-left","fo:border-bottom","fo:border-right",
383 "fo:clip",
384 "style:background-transparency",
385 "style:border-line-width","style:border-line-width-top","style:border-line-width-left",
386 "style:border-line-width-bottom","style:border-line-width-right",
387 "style:shrink-to-fit",
388 "style:mirror", "style:parent-style-name", "style:print-content",
389 "style:run-through", "style:wrap"
390 };
391 for (auto &other : others)
392 {
393 if (style[other])
394 element.insert(other, style[other]->getStr());
395 }
396 }
397
addFrameProperties(librevenge::RVNGPropertyList const & propList,librevenge::RVNGPropertyList & element)398 void GraphicStyleManager::addFrameProperties(librevenge::RVNGPropertyList const &propList, librevenge::RVNGPropertyList &element)
399 {
400 element.insert("fo:min-width", "1in");
401 char const *attrib[]=
402 {
403 "fo:min-width", "fo:min-height", "fo:max-width", "fo:max-height", "fo:padding-top", "fo:padding-bottom",
404 "fo:padding-left", "fo:padding-right", "draw:textarea-vertical-align", "draw:fill", "draw:fill-color"
405 // checkme
406 // "draw:z-index",
407 // "svg:x", "svg:y", "svg:width", "svg:height", "style:wrap", "style:run-through",
408 // "text:anchor-type", "text:anchor-page-number"
409 };
410 for (auto &i : attrib)
411 {
412 if (propList[i])
413 element.insert(i, propList[i]->getStr());
414 }
415 }
416
417 /* vim:set shiftwidth=4 softtabstop=4 noexpandtab: */
418