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