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