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 /* "This product is not manufactured, approved, or supported by
24  * Corel Corporation or Corel Corporation Limited."
25  */
26 
27 #include "TextRunStyle.hxx"
28 
29 #include "FilterInternal.hxx"
30 #include "DocumentElement.hxx"
31 
32 #ifdef _MSC_VER
33 #include <minmax.h>
34 #endif
35 
36 #include <string.h>
37 
ParagraphStyle(const librevenge::RVNGPropertyList & pPropList,const librevenge::RVNGString & sName,Style::Zone zone)38 ParagraphStyle::ParagraphStyle(const librevenge::RVNGPropertyList &pPropList, const librevenge::RVNGString &sName, Style::Zone zone) : Style(sName, zone),
39 	mpPropList(pPropList)
40 {
41 }
42 
~ParagraphStyle()43 ParagraphStyle::~ParagraphStyle()
44 {
45 }
46 
write(OdfDocumentHandler * pHandler) const47 void ParagraphStyle::write(OdfDocumentHandler *pHandler) const
48 {
49 	ODFGEN_DEBUG_MSG(("ParagraphStyle: Writing a paragraph style..\n"));
50 
51 	librevenge::RVNGPropertyList propList;
52 	propList.insert("style:name", getName());
53 	propList.insert("style:family", "paragraph");
54 	if (mpPropList["style:display-name"])
55 		propList.insert("style:display-name", mpPropList["style:display-name"]->clone());
56 	if (mpPropList["style:parent-style-name"])
57 		propList.insert("style:parent-style-name", mpPropList["style:parent-style-name"]->clone());
58 	if (mpPropList["style:master-page-name"])
59 		propList.insert("style:master-page-name", mpPropList["style:master-page-name"]->clone());
60 	if (mpPropList["style:default-outline-level"] && mpPropList["style:default-outline-level"]->getInt()>0)
61 		propList.insert("style:default-outline-level", mpPropList["style:default-outline-level"]->clone());
62 	pHandler->startElement("style:style", propList);
63 
64 	propList.clear();
65 	librevenge::RVNGPropertyList::Iter i(mpPropList);
66 	for (i.rewind(); i.next();)
67 	{
68 		if (i.child() ||
69 		        !strcmp(i.key(), "style:display-name") ||
70 		        !strcmp(i.key(), "style:parent-style-name") ||
71 		        !strcmp(i.key(), "style:master-page-name") ||
72 		        !strcmp(i.key(), "style:default-outline-level") ||
73 		        !strncmp(i.key(), "librevenge:",11))
74 			continue;
75 		else if (!strncmp(i.key(), "fo:margin-",10))
76 		{
77 			if (!strcmp(i.key(), "fo:margin-left") ||
78 			        !strcmp(i.key(), "fo:margin-right") ||
79 			        !strcmp(i.key(), "fo:margin-top"))
80 				propList.insert(i.key(), i()->clone());
81 			else if (!strcmp(i.key(), "fo:margin-bottom"))
82 			{
83 				if (i()->getDouble() > 0.0)
84 					propList.insert("fo:margin-bottom", i()->clone());
85 				else
86 					propList.insert("fo:margin-bottom", 0.0);
87 			}
88 		}
89 		else if (!strncmp(i.key(), "style:border-line-width", 23))
90 		{
91 			if (!strcmp(i.key(), "style:border-line-width") ||
92 			        !strcmp(i.key(), "style:border-line-width-left") ||
93 			        !strcmp(i.key(), "style:border-line-width-right") ||
94 			        !strcmp(i.key(), "style:border-line-width-top") ||
95 			        !strcmp(i.key(), "style:border-line-width-bottom"))
96 				propList.insert(i.key(), i()->clone());
97 		}
98 		else if (!strncmp(i.key(), "fo:border", 9))
99 		{
100 			if (!strcmp(i.key(), "fo:border") ||
101 			        !strcmp(i.key(), "fo:border-left") ||
102 			        !strcmp(i.key(), "fo:border-right") ||
103 			        !strcmp(i.key(), "fo:border-top") ||
104 			        !strcmp(i.key(), "fo:border-bottom"))
105 				propList.insert(i.key(), i()->clone());
106 		}
107 		else if (!strcmp(i.key(), "text:outline-level"))
108 			continue;
109 		else
110 			propList.insert(i.key(), i()->clone());
111 	}
112 
113 	propList.insert("style:justify-single-word", "false");
114 	pHandler->startElement("style:paragraph-properties", propList);
115 
116 	const librevenge::RVNGPropertyListVector *pTabStops = mpPropList.child("style:tab-stops");
117 	if (pTabStops && pTabStops->count())
118 	{
119 		TagOpenElement tabListOpen("style:tab-stops");
120 		tabListOpen.write(pHandler);
121 		librevenge::RVNGPropertyListVector::Iter k(*pTabStops);
122 		for (k.rewind(); k.next();)
123 		{
124 			if (k()["style:position"] && k()["style:position"]->getDouble() < 0.0)
125 				continue;
126 			TagOpenElement tabStopOpen("style:tab-stop");
127 
128 			librevenge::RVNGPropertyList::Iter j(k());
129 			for (j.rewind(); j.next();)
130 			{
131 				tabStopOpen.addAttribute(j.key(), j()->getStr().cstr());
132 			}
133 			tabStopOpen.write(pHandler);
134 			pHandler->endElement("style:tab-stop");
135 		}
136 		pHandler->endElement("style:tab-stops");
137 	}
138 	const librevenge::RVNGPropertyListVector *pDropCap = mpPropList.child("style:drop-cap");
139 	if (pDropCap && pDropCap->count())
140 	{
141 		TagOpenElement dropCapOpen("style:drop-cap");
142 		librevenge::RVNGPropertyList::Iter j((*pDropCap)[0]);
143 		for (j.rewind(); j.next();)
144 		{
145 			dropCapOpen.addAttribute(j.key(), j()->getStr().cstr());
146 		}
147 		dropCapOpen.write(pHandler);
148 		pHandler->endElement("style:drop-cap");
149 	}
150 	pHandler->endElement("style:paragraph-properties");
151 	pHandler->endElement("style:style");
152 }
153 
SpanStyle(const char * psName,const librevenge::RVNGPropertyList & xPropList,Style::Zone zone)154 SpanStyle::SpanStyle(const char *psName, const librevenge::RVNGPropertyList &xPropList, Style::Zone zone) :
155 	Style(psName, zone),
156 	mPropList(xPropList)
157 {
158 }
159 
write(OdfDocumentHandler * pHandler) const160 void SpanStyle::write(OdfDocumentHandler *pHandler) const
161 {
162 	ODFGEN_DEBUG_MSG(("SpanStyle: Writing a span style..\n"));
163 	librevenge::RVNGPropertyList styleOpenList;
164 	styleOpenList.insert("style:name", getName());
165 	if (mPropList["style:display-name"])
166 		styleOpenList.insert("style:display-name", mPropList["style:display-name"]->clone());
167 	styleOpenList.insert("style:family", "text");
168 	pHandler->startElement("style:style", styleOpenList);
169 
170 	librevenge::RVNGPropertyList style;
171 	SpanStyleManager::addSpanProperties(mPropList, style);
172 	pHandler->startElement("style:text-properties", style);
173 	pHandler->endElement("style:text-properties");
174 	pHandler->endElement("style:style");
175 }
176 
clean()177 void ParagraphStyleManager::clean()
178 {
179 	mHashNameMap.clear();
180 	mStyleHash.clear();
181 	mDisplayNameMap.clear();
182 }
183 
write(OdfDocumentHandler * pHandler,Style::Zone zone) const184 void ParagraphStyleManager::write(OdfDocumentHandler *pHandler, Style::Zone zone) const
185 {
186 	for (const auto &iter : mStyleHash)
187 	{
188 		if (iter.second && iter.second->getZone()==zone)
189 			(iter.second)->write(pHandler);
190 	}
191 }
192 
findOrAdd(const librevenge::RVNGPropertyList & propList,Style::Zone zone)193 librevenge::RVNGString ParagraphStyleManager::findOrAdd(const librevenge::RVNGPropertyList &propList, Style::Zone zone)
194 {
195 	librevenge::RVNGPropertyList pList(propList);
196 
197 	// first check if we need to store the style as style or as automatic style
198 	bool deferMasterPageNameInsertion=false;
199 	Style::Zone currentZone=zone;
200 	if (propList["style:display-name"])
201 	{
202 		if (propList["style:master-page-name"])
203 		{
204 			deferMasterPageNameInsertion=true;
205 			pList.remove("style:master-page-name");
206 		}
207 		currentZone=Style::Z_Style;
208 	}
209 	else if (currentZone==Style::Z_Unknown)
210 		currentZone=Style::Z_ContentAutomatic;
211 	pList.insert("librevenge:zone-style", int(currentZone));
212 
213 	// look if we have already create this style
214 	librevenge::RVNGString hashKey = pList.getPropString();
215 	std::map<librevenge::RVNGString, librevenge::RVNGString>::const_iterator iter =
216 	    mHashNameMap.find(hashKey);
217 	librevenge::RVNGString sName("");
218 	if (iter!=mHashNameMap.end())
219 	{
220 		if (!deferMasterPageNameInsertion)
221 			return iter->second;
222 		sName=iter->second;
223 	}
224 	else
225 	{
226 		ODFGEN_DEBUG_MSG(("ParagraphStyleManager::findOrAdd: Paragraph Hash Key: %s\n", hashKey.cstr()));
227 
228 		// ok create a new style
229 		if (currentZone==Style::Z_Style)
230 			sName.sprintf("S_N%i", (int)mStyleHash.size());
231 		else if (currentZone==Style::Z_StyleAutomatic)
232 			sName.sprintf("S_M%i", (int)mStyleHash.size());
233 		else
234 			sName.sprintf("S%i", (int)mStyleHash.size());
235 		if (propList["style:display-name"])
236 		{
237 			librevenge::RVNGString name(propList["style:display-name"]->getStr());
238 			if (mDisplayNameMap.find(name) != mDisplayNameMap.end())
239 			{
240 				ODFGEN_DEBUG_MSG(("ParagraphStyleManager::findOrAdd: a paragraph with name %s already exists\n", name.cstr()));
241 				pList.remove("style:display-name");
242 			}
243 			else
244 				mDisplayNameMap[name]=sName;
245 		}
246 		std::shared_ptr<ParagraphStyle> parag(new ParagraphStyle(pList, sName, currentZone));
247 		mStyleHash[sName] =parag;
248 		mHashNameMap[hashKey] = sName;
249 		if (!deferMasterPageNameInsertion)
250 			return sName;
251 	}
252 
253 	//
254 	// we must now create the style with master-page-name attribute : let inherete from the named style
255 	//
256 	pList=propList;
257 	pList.remove("style:display-name");
258 	pList.insert("style:parent-style-name", sName);
259 	return findOrAdd(pList, zone);
260 }
261 
get(const librevenge::RVNGString & name) const262 std::shared_ptr<ParagraphStyle> const ParagraphStyleManager::get(const librevenge::RVNGString &name) const
263 {
264 	auto iter = mStyleHash.find(name);
265 	if (iter == mStyleHash.end()) return std::shared_ptr<ParagraphStyle>();
266 	return iter->second;
267 }
268 
269 ////////////////////////////////////////////////////////////
270 // span manager
271 ////////////////////////////////////////////////////////////
clean()272 void SpanStyleManager::clean()
273 {
274 	mHashNameMap.clear();
275 	mStyleHash.clear();
276 	mDisplayNameMap.clear();
277 }
278 
addSpanProperties(librevenge::RVNGPropertyList const & style,librevenge::RVNGPropertyList & element)279 void SpanStyleManager::addSpanProperties(librevenge::RVNGPropertyList const &style, librevenge::RVNGPropertyList &element)
280 {
281 	librevenge::RVNGPropertyList::Iter i(style);
282 	for (i.rewind(); i.next();)
283 	{
284 		if (i.child()) continue;
285 		switch (i.key()[0])
286 		{
287 		case 'f':
288 			if (strncmp(i.key(), "fo:", 3)!=0)
289 				break;
290 			if (!strcmp(i.key(), "fo:font-size"))
291 			{
292 				if (style["fo:font-size"]->getDouble() > 0.0)
293 				{
294 					element.insert("fo:font-size", style["fo:font-size"]->clone());
295 					if (!element["style:font-size-asian"])
296 						element.insert("style:font-size-asian", style["fo:font-size"]->clone());
297 					if (!element["style:font-size-complex"])
298 						element.insert("style:font-size-complex", style["fo:font-size"]->clone());
299 				}
300 				break;
301 			}
302 			if (!strcmp(i.key(), "fo:font-weight"))
303 			{
304 				element.insert("fo:font-weight", style["fo:font-weight"]->clone());
305 				if (!element["style:font-weight-asian"])
306 					element.insert("style:font-weight-asian", style["fo:font-weight"]->clone());
307 				if (!element["style:font-weight-complex"])
308 					element.insert("style:font-weight-complex", style["fo:font-weight"]->clone());
309 				break;
310 			}
311 			if (!strcmp(i.key(), "fo:font-style"))
312 			{
313 				element.insert("fo:font-style", style["fo:font-style"]->clone());
314 				if (!element["style:font-style-asian"])
315 					element.insert("style:font-style-asian", style["fo:font-style"]->clone());
316 				if (!element["style:font-style-complex"])
317 					element.insert("style:font-style-complex", style["fo:font-style"]->clone());
318 				break;
319 			}
320 			if (!strcmp(i.key(), "fo:background-color") || !strcmp(i.key(), "fo:color") ||
321 			        !strcmp(i.key(), "fo:country") || !strncmp(i.key(), "fo:font", 7) ||
322 			        !strncmp(i.key(), "fo:hyphen", 9) || !strcmp(i.key(), "fo:language") ||
323 			        !strcmp(i.key(), "fo:letter-spacing") || !strcmp(i.key(), "fo:script")  ||
324 			        !strncmp(i.key(), "fo:text", 7))
325 				element.insert(i.key(),i()->clone());
326 			break;
327 		case 's':
328 			if (strncmp(i.key(), "style:", 6)!=0)
329 				break;
330 			if (!strcmp(i.key(), "style:font-name"))
331 			{
332 				element.insert("style:font-name", style["style:font-name"]->clone());
333 				if (!element["style:font-name-asian"])
334 					element.insert("style:font-name-asian", style["style:font-name"]->clone());
335 				if (!element["style:font-name-complex"])
336 					element.insert("style:font-name-complex", style["style:font-name"]->clone());
337 				break;
338 			}
339 			if (!strncmp(i.key(), "style:country", 13) || !strncmp(i.key(), "style:font", 10) ||
340 			        !strncmp(i.key(), "style:language", 14) || !strncmp(i.key(), "style:letter", 12) ||
341 			        !strncmp(i.key(), "style:rfc-", 10) || !strncmp(i.key(), "style:script", 12) ||
342 			        !strncmp(i.key(), "style:text", 10) || !strcmp(i.key(), "style:use-window-font-color"))
343 				element.insert(i.key(),i()->clone());
344 			// style:writing-mode is ignored
345 			break;
346 		case 't':
347 			if (!strcmp(i.key(), "text:condition") || !strcmp(i.key(), "text:display"))
348 				element.insert(i.key(),i()->clone());
349 			break;
350 		default:
351 			break;
352 		}
353 	}
354 }
355 
write(OdfDocumentHandler * pHandler,Style::Zone zone) const356 void SpanStyleManager::write(OdfDocumentHandler *pHandler, Style::Zone zone) const
357 {
358 	for (const auto &iter : mStyleHash)
359 	{
360 		if (iter.second && iter.second->getZone()==zone)
361 			(iter.second)->write(pHandler);
362 	}
363 }
364 
findOrAdd(const librevenge::RVNGPropertyList & propList,Style::Zone zone)365 librevenge::RVNGString SpanStyleManager::findOrAdd(const librevenge::RVNGPropertyList &propList, Style::Zone zone)
366 {
367 	librevenge::RVNGPropertyList pList(propList);
368 
369 	// first check if we need to store the style as style or as automatic style
370 	if (propList["style:display-name"] && !propList["style:master-page-name"])
371 		zone=Style::Z_Style;
372 	else if (zone==Style::Z_Unknown)
373 		zone=Style::Z_ContentAutomatic;
374 	pList.insert("librevenge:zone-style", int(zone));
375 
376 	librevenge::RVNGString hashKey = pList.getPropString();
377 	std::map<librevenge::RVNGString, librevenge::RVNGString>::const_iterator iter =
378 	    mHashNameMap.find(hashKey);
379 	if (iter!=mHashNameMap.end()) return iter->second;
380 
381 	// ok create a new list
382 	ODFGEN_DEBUG_MSG(("SpanStyleManager::findOrAdd: Span Hash Key: %s\n", hashKey.cstr()));
383 
384 	librevenge::RVNGString sName("");
385 	if (zone==Style::Z_Style)
386 		sName.sprintf("Span_N%i", (int)mStyleHash.size());
387 	else if (zone==Style::Z_StyleAutomatic)
388 		sName.sprintf("Span_M%i", (int)mStyleHash.size());
389 	else
390 		sName.sprintf("Span%i", (int)mStyleHash.size());
391 	std::shared_ptr<SpanStyle> span(new SpanStyle(sName.cstr(), propList, zone));
392 	mStyleHash[sName] = span;
393 	mHashNameMap[hashKey] = sName;
394 	if (propList["style:display-name"] && !propList["style:display-name"]->getStr().empty())
395 		mDisplayNameMap[propList["style:display-name"]->getStr()]=sName;
396 	return sName;
397 }
398 
getFinalDisplayName(const librevenge::RVNGString & displayName)399 librevenge::RVNGString SpanStyleManager::getFinalDisplayName(const librevenge::RVNGString &displayName)
400 {
401 	if (mDisplayNameMap.find(displayName) != mDisplayNameMap.end())
402 		return mDisplayNameMap.find(displayName)->second;
403 	ODFGEN_DEBUG_MSG(("SpanStyleManager::getName: can not find style with display name: %s\n", displayName.cstr()));
404 	return librevenge::RVNGString("");
405 }
406 
get(const librevenge::RVNGString & name) const407 std::shared_ptr<SpanStyle> const SpanStyleManager::get(const librevenge::RVNGString &name) const
408 {
409 	auto iter = mStyleHash.find(name);
410 	if (iter == mStyleHash.end()) return std::shared_ptr<SpanStyle>();
411 	return iter->second;
412 }
413 
414 /* vim:set shiftwidth=4 softtabstop=4 noexpandtab: */
415