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 #ifdef HAVE_CONFIG_H
28 #include "config.h"
29 #endif
30 
31 #include "OdfGenerator.hxx"
32 
33 #include <math.h>
34 
35 #include <cctype>
36 #include <limits>
37 #include <memory>
38 #include <string>
39 #include <stack>
40 
41 #include <librevenge/librevenge.h>
42 
43 #include <libxml/parser.h>
44 
45 #include "DocumentElement.hxx"
46 #include "GraphicFunctions.hxx"
47 #include "InternalHandler.hxx"
48 #include "ListStyle.hxx"
49 #include "TableStyle.hxx"
50 
51 using namespace libodfgen;
52 
53 namespace
54 {
55 
appendUnicodeTo(unsigned long val,librevenge::RVNGString & buffer)56 static void appendUnicodeTo(unsigned long val, librevenge::RVNGString &buffer)
57 {
58 	uint8_t first;
59 	int len;
60 	if (val < 0x80)
61 	{
62 		first = 0;
63 		len = 1;
64 	}
65 	else if (val < 0x800)
66 	{
67 		first = 0xc0;
68 		len = 2;
69 	}
70 	else if (val < 0x10000)
71 	{
72 		first = 0xe0;
73 		len = 3;
74 	}
75 	else if (val < 0x200000)
76 	{
77 		first = 0xf0;
78 		len = 4;
79 	}
80 	else if (val < 0x4000000)
81 	{
82 		first = 0xf8;
83 		len = 5;
84 	}
85 	else
86 		throw "unicode value is too big";
87 
88 	char outbuf[7];
89 	int i;
90 	for (i = len - 1; i > 0; --i)
91 	{
92 		outbuf[i] = char((val & 0x3f) | 0x80);
93 		val >>= 6;
94 	}
95 	outbuf[0] = char(val | first);
96 	outbuf[len] = 0;
97 	buffer.append(outbuf);
98 }
99 
100 struct XMLException {};
101 
xmlCast(const xmlChar * const str)102 const char *xmlCast(const xmlChar *const str)
103 {
104 	return reinterpret_cast<const char *>(str);
105 }
106 
addAttributes(xmlAttributePtr attr,std::shared_ptr<TagOpenElement> & element)107 static void addAttributes(xmlAttributePtr attr, std::shared_ptr<TagOpenElement> &element)
108 {
109 	for (; attr; attr = xmlAttributePtr(attr->next))
110 	{
111 		std::string value;
112 
113 		for (xmlNodePtr node = attr->children; node; node = node->next)
114 			if (node->type == XML_TEXT_NODE)
115 				value += xmlCast(node->content);
116 		element->addAttribute(xmlCast(attr->name), value.c_str());
117 	}
118 }
119 
appendNameSpace(xmlNsPtr nSpace,std::shared_ptr<TagOpenElement> & element)120 static void appendNameSpace(xmlNsPtr nSpace, std::shared_ptr<TagOpenElement> &element)
121 {
122 	if (nSpace->type==XML_NAMESPACE_DECL && nSpace->href)
123 	{
124 		std::string tag("xmlns");
125 		if (nSpace->prefix) tag+=std::string(":")+xmlCast(nSpace->prefix);
126 		std::string value(xmlCast(nSpace->href));
127 		element->addAttribute(tag.c_str(), value.c_str());
128 		// FIXME: look also next to check if there is other namespace
129 	}
130 }
131 
appendElements(xmlNodePtr node,DocumentElementVector & out,bool rootNode=false)132 static void appendElements(xmlNodePtr node, DocumentElementVector &out, bool rootNode=false)
133 {
134 	for (; node; node = node->next)
135 	{
136 		switch (node->type)
137 		{
138 		case XML_ELEMENT_NODE:
139 		{
140 			auto element = xmlElementPtr(node);
141 			auto tag = std::make_shared<TagOpenElement>(xmlCast(element->name));
142 			out.push_back(tag);
143 			if (rootNode && node->nsDef) appendNameSpace(node->nsDef, tag);
144 			addAttributes(element->attributes, tag);
145 			appendElements(element->children, out);
146 			out.push_back(std::make_shared<TagCloseElement>(xmlCast(element->name)));
147 			break;
148 		}
149 		case XML_TEXT_NODE:
150 			out.push_back(std::make_shared<CharDataElement>(xmlCast(node->content)));
151 			break;
152 		default:
153 			break;
154 		}
155 	}
156 }
157 
appendXML(const std::string & data,DocumentElementVector & out)158 void appendXML(const std::string &data, DocumentElementVector &out)
159 {
160 	std::unique_ptr<xmlDoc, void(*)(xmlDocPtr)> doc
161 	{
162 		// FIXME: coverity warns that using XML_PARSE_RECOVER is unsafe,
163 		// but libstaroffice generates &alpha; , so we must first fix libstaroffice before removing XML_PARSE_RECOVER
164 		xmlReadDoc(reinterpret_cast<const xmlChar *>(data.c_str()), "", nullptr,
165 		           XML_PARSE_RECOVER | XML_PARSE_NOERROR | XML_PARSE_NOWARNING | XML_PARSE_NONET | XML_PARSE_NOCDATA | XML_PARSE_NSCLEAN),
166 		xmlFreeDoc};
167 	if (!doc)
168 		throw XMLException();
169 
170 	appendElements(doc->children, out, true);
171 }
172 
173 }
174 
OdfGenerator()175 OdfGenerator::OdfGenerator()
176 	: mpCurrentStorage()
177 	, mStorageStack()
178 	, mMetaDataStorage()
179 	, mpBodyStorage(new libodfgen::DocumentElementVector())
180 	, mPageSpanManager()
181 	, mFontManager()
182 	, mFillManager()
183 	, mGraphicManager(mFillManager)
184 	, mSpanManager()
185 	, mParagraphManager()
186 	, mNumberingManager()
187 	, mListManager()
188 	, mTableManager()
189 	, mbInHeaderFooter(false)
190 	, mbInMasterPage(false)
191 	, mIdSpanMap()
192 	, mIdSpanNameMap()
193 	, mLastSpanName("")
194 	, mIdParagraphMap()
195 	, mIdParagraphNameMap()
196 	, mLastParagraphName("")
197 	, miFrameNumber(0)
198 	, mFrameNameIdMap()
199 	, mLayerNameStack()
200 	, mLayerNameSet()
201 	, mLayerNameMap()
202 	, mGraphicStyle()
203 	, mIdChartMap()
204 	, mIdChartNameMap()
205 	, mDocumentStreamHandlers()
206 	, miObjectNumber(1)
207 	, mNameObjectMap()
208 	, mImageHandlers()
209 	, mObjectHandlers()
210 	, mParagraphHeadingStack()
211 	, mXmlToUnicodeMap()
212 {
213 	mpCurrentStorage = mpBodyStorage;
214 }
215 
~OdfGenerator()216 OdfGenerator::~OdfGenerator()
217 {
218 	mNumberingManager.clean();
219 	mParagraphManager.clean();
220 	mSpanManager.clean();
221 	mFontManager.clean();
222 	mFillManager.clean();
223 	mGraphicManager.clean();
224 	mTableManager.clean();
225 }
226 
getDocumentType(OdfStreamType streamType)227 std::string OdfGenerator::getDocumentType(OdfStreamType streamType)
228 {
229 	switch (streamType)
230 	{
231 	case ODF_FLAT_XML:
232 		return "office:document";
233 	case ODF_CONTENT_XML:
234 		return "office:document-content";
235 	case ODF_STYLES_XML:
236 		return "office:document-styles";
237 	case ODF_SETTINGS_XML:
238 		return "office:document-settings";
239 	case ODF_META_XML:
240 		return "office:document-meta";
241 	case ODF_MANIFEST_XML:
242 	default:
243 		return "office:document";
244 	}
245 }
246 
setDocumentMetaData(const librevenge::RVNGPropertyList & propList)247 void OdfGenerator::setDocumentMetaData(const librevenge::RVNGPropertyList &propList)
248 {
249 	std::string generator;
250 
251 	librevenge::RVNGPropertyList::Iter i(propList);
252 	for (i.rewind(); i.next();)
253 	{
254 		if (i()->getStr().empty()) continue;
255 		// filter out librevenge elements
256 		if (strncmp(i.key(), "librevenge:", 11) && strncmp(i.key(), "dcterms:", 8))
257 		{
258 			if (strncmp(i.key(), "meta:generator", 14))
259 			{
260 				mMetaDataStorage.push_back(std::make_shared<TagOpenElement>(i.key()));
261 				mMetaDataStorage.push_back(std::make_shared<CharDataElement>(i()->getStr().cstr()));
262 				mMetaDataStorage.push_back(std::make_shared<TagCloseElement>(i.key()));
263 			}
264 			else
265 			{
266 				generator = i()->getStr().cstr();
267 			}
268 		}
269 		else if (strncmp(i.key(), "librevenge:template", 19) == 0)
270 		{
271 			librevenge::RVNGString elementName = "meta:template";
272 			auto element = std::make_shared<TagOpenElement>(elementName);
273 			element->addAttribute("xlink:type", "simple");
274 			element->addAttribute("xlink:actuate", "onRequest");
275 			element->addAttribute("xlink:title", i()->getStr().cstr());
276 			element->addAttribute("xlink:href", "");
277 			mMetaDataStorage.push_back(element);
278 			mMetaDataStorage.push_back(std::make_shared<TagCloseElement>(elementName));
279 		}
280 		else if (strncmp(i.key(), "librevenge:", 11) == 0)
281 		{
282 			// convert to <meta:user-defined meta:name="some_metadata">
283 			librevenge::RVNGString elementName = "meta:user-defined";
284 			auto element = std::make_shared<TagOpenElement>(elementName);
285 			std::string user_defined(i.key());
286 			size_t found = user_defined.find_last_of(":");
287 			if (found != std::string::npos)
288 				user_defined = user_defined.substr(found+1);
289 			element->addAttribute("meta:name", user_defined.c_str());
290 			mMetaDataStorage.push_back(element);
291 			mMetaDataStorage.push_back(std::make_shared<CharDataElement>(i()->getStr().cstr()));
292 			mMetaDataStorage.push_back(std::make_shared<TagCloseElement>(elementName));
293 		}
294 	}
295 
296 #ifdef VERSION
297 	const std::string version(VERSION);
298 #else
299 	const std::string version("unknown");
300 #endif
301 	if (generator.empty())
302 	{
303 		generator = "libodfgen/" + version;
304 	}
305 	else
306 	{
307 		generator += " (";
308 		generator += "libodfgen/" + version;
309 		generator += ")";
310 	}
311 
312 	mMetaDataStorage.push_back(std::make_shared<TagOpenElement>("meta:generator"));
313 	mMetaDataStorage.push_back(std::make_shared<CharDataElement>(generator.c_str()));
314 	mMetaDataStorage.push_back(std::make_shared<TagCloseElement>("meta:generator"));
315 }
316 
writeDocumentMetaData(OdfDocumentHandler * pHandler)317 void OdfGenerator::writeDocumentMetaData(OdfDocumentHandler *pHandler)
318 {
319 	if (mMetaDataStorage.empty()) return;
320 	TagOpenElement("office:meta").write(pHandler);
321 	sendStorage(&mMetaDataStorage, pHandler);
322 	pHandler->endElement("office:meta");
323 }
324 
appendFilesInManifest(OdfDocumentHandler * pHandler)325 void OdfGenerator::appendFilesInManifest(OdfDocumentHandler *pHandler)
326 {
327 	std::map<OdfStreamType, OdfDocumentHandler *>::const_iterator iter = mDocumentStreamHandlers.begin();
328 	for (; iter != mDocumentStreamHandlers.end(); ++iter)
329 	{
330 		std::string name("");
331 		switch (iter->first)
332 		{
333 		case ODF_CONTENT_XML:
334 			name="content.xml";
335 			break;
336 		case ODF_META_XML:
337 			name="meta.xml";
338 			break;
339 		case ODF_STYLES_XML:
340 			name="styles.xml";
341 			break;
342 		case ODF_SETTINGS_XML:
343 			name="settings.xml";
344 			break;
345 		case ODF_FLAT_XML:
346 		case ODF_MANIFEST_XML:
347 		default:
348 			break;
349 		}
350 		if (name.empty())
351 			continue;
352 
353 		TagOpenElement file("manifest:file-entry");
354 		file.addAttribute("manifest:media-type","text/xml");
355 		file.addAttribute("manifest:full-path", name.c_str());
356 		file.write(pHandler);
357 		TagCloseElement("manifest:file-entry").write(pHandler);
358 	}
359 	for (auto &oIt : mNameObjectMap)
360 	{
361 		if (!oIt.second) continue;
362 
363 		TagOpenElement file("manifest:file-entry");
364 		file.addAttribute("manifest:media-type",oIt.second->mType);
365 		file.addAttribute("manifest:full-path", oIt.first);
366 		file.write(pHandler);
367 		TagCloseElement("manifest:file-entry").write(pHandler);
368 	}
369 
370 }
371 
initStateWith(OdfGenerator const & orig)372 void OdfGenerator::initStateWith(OdfGenerator const &orig)
373 {
374 	mImageHandlers=orig.mImageHandlers;
375 	mObjectHandlers=orig.mObjectHandlers;
376 	mIdSpanMap=orig.mIdSpanMap;
377 	mIdParagraphMap=orig.mIdParagraphMap;
378 	mIdChartMap=orig.mIdChartMap;
379 }
380 
appendBodySettings(const librevenge::RVNGPropertyList & propList)381 void OdfGenerator::appendBodySettings(const librevenge::RVNGPropertyList &propList)
382 {
383 	const librevenge::RVNGPropertyListVector *childs=propList.child("librevenge:childs");
384 	if (!childs) return;
385 	for (unsigned long c=0; c< childs->count(); ++c)
386 	{
387 		auto const &child=(*childs)[c];
388 		if (!child["librevenge:type"])
389 		{
390 			ODFGEN_DEBUG_MSG(("OdfGenerator::appendBodySettings: find a child without any type\n"));
391 			continue;
392 		}
393 		if (child["librevenge:type"]->getStr()=="table:calculation-settings")
394 		{
395 			auto calcSetting=std::make_shared<TagOpenElement>("table:calculation-settings");
396 			char const *wh[]= { "table:automatic-find-labels", "table:case-sensitive", "table:null-year",
397 			                    "table:precision-as-shown", "table:search-criteria-must-apply-to-whole-cell",
398 			                    "table:use-regular-expressions", "table:use-wildcards"
399 			                  };
400 			// checkme: had also childs elements: table:iteration, table:null-date
401 			for (auto &i : wh)
402 			{
403 				if (child[i])
404 					calcSetting->addAttribute(i, child[i]->getStr());
405 			}
406 			mpBodyStorage->push_back(calcSetting);
407 			mpBodyStorage->push_back(std::make_shared<TagCloseElement>("table:calculation-settings"));
408 		}
409 		else
410 		{
411 			if (!child["librevenge:type"]->getStr().empty())
412 			{
413 				ODFGEN_DEBUG_MSG(("OdfGenerator::appendBodySettings: find a child with unknown \"%s\" type\n",
414 				                  child["librevenge:type"]->getStr().cstr()));
415 			}
416 			else
417 			{
418 				ODFGEN_DEBUG_MSG(("OdfGenerator::appendBodySettings: find a child with an empty type\n"));
419 			}
420 		}
421 	}
422 
423 }
424 ////////////////////////////////////////////////////////////
425 // object
426 ////////////////////////////////////////////////////////////
~ObjectContainer()427 OdfGenerator::ObjectContainer::~ObjectContainer()
428 {
429 }
430 
createObjectFile(librevenge::RVNGString const & objectName,librevenge::RVNGString const & objectType,bool isDir)431 OdfGenerator::ObjectContainer &OdfGenerator::createObjectFile
432 (librevenge::RVNGString const &objectName, librevenge::RVNGString const &objectType, bool isDir)
433 {
434 	std::unique_ptr<ObjectContainer> res{new ObjectContainer(objectType, isDir)};
435 	auto ret = mNameObjectMap.insert(std::make_pair(objectName, std::move(res)));
436 	return *ret.first->second;
437 }
438 
getObjectNames() const439 librevenge::RVNGStringVector OdfGenerator::getObjectNames() const
440 {
441 	librevenge::RVNGStringVector res;
442 	for (const auto &it : mNameObjectMap)
443 	{
444 		if (!it.second || it.second->mIsDir) continue;
445 		res.append(it.first);
446 	}
447 	return res;
448 }
449 
getObjectContent(librevenge::RVNGString const & objectName,OdfDocumentHandler * pHandler)450 bool OdfGenerator::getObjectContent(librevenge::RVNGString const &objectName, OdfDocumentHandler *pHandler)
451 {
452 	if (!pHandler) return false;
453 	auto it=mNameObjectMap.find(objectName);
454 	if (it==mNameObjectMap.end() || !it->second)
455 	{
456 		ODFGEN_DEBUG_MSG(("OdfGenerator::getObjectContent: can not find object %s\n", objectName.cstr()));
457 		return false;
458 	}
459 	pHandler->startDocument();
460 	ObjectContainer &object=*(it->second);
461 	for (auto &i : object.mStorage)
462 	{
463 		if (!i) continue;
464 		i->write(pHandler);
465 	}
466 	pHandler->endDocument();
467 	return true;
468 }
469 
470 ////////////////////////////////////////////////////////////
471 // storage
472 ////////////////////////////////////////////////////////////
sendStorage(libodfgen::DocumentElementVector const * storage,OdfDocumentHandler * pHandler)473 void OdfGenerator::sendStorage(libodfgen::DocumentElementVector const *storage, OdfDocumentHandler *pHandler)
474 {
475 	if (!storage)
476 	{
477 		ODFGEN_DEBUG_MSG(("OdfGenerator::sendStorage: called without storage\n"));
478 		return;
479 	}
480 	for (const auto &i : *storage)
481 	{
482 		if (i)i->write(pHandler);
483 	}
484 }
485 
pushStorage(const std::shared_ptr<libodfgen::DocumentElementVector> & newStorage)486 void OdfGenerator::pushStorage(const std::shared_ptr<libodfgen::DocumentElementVector> &newStorage)
487 {
488 	if (!newStorage)
489 	{
490 		ODFGEN_DEBUG_MSG(("OdfGenerator::pushStorage: called without storage\n"));
491 		return;
492 	}
493 	mStorageStack.push(mpCurrentStorage);
494 	mpCurrentStorage=newStorage;
495 }
496 
popStorage()497 bool OdfGenerator::popStorage()
498 {
499 	if (mStorageStack.empty())
500 	{
501 		ODFGEN_DEBUG_MSG(("OdfGenerator::popStorage: the stack is empty\n"));
502 		return false;
503 	}
504 	mpCurrentStorage=mStorageStack.top();
505 	mStorageStack.pop();
506 	return false;
507 }
508 
509 ////////////////////////////////////////////////////////////
510 // document handler
511 ////////////////////////////////////////////////////////////
addDocumentHandler(OdfDocumentHandler * pHandler,const OdfStreamType streamType)512 void OdfGenerator::addDocumentHandler(OdfDocumentHandler *pHandler, const OdfStreamType streamType)
513 {
514 	if (!pHandler)
515 	{
516 		ODFGEN_DEBUG_MSG(("OdfGenerator::addDocumentHandler: called without handler\n"));
517 		return;
518 	}
519 	mDocumentStreamHandlers[streamType] = pHandler;
520 }
521 
writeTargetDocuments()522 void  OdfGenerator::writeTargetDocuments()
523 {
524 	std::map<OdfStreamType, OdfDocumentHandler *>::const_iterator iter = mDocumentStreamHandlers.begin();
525 	for (; iter != mDocumentStreamHandlers.end(); ++iter)
526 		writeTargetDocument(iter->second, iter->first);
527 }
528 
529 ////////////////////////////////////////////////////////////
530 // embedded
531 ////////////////////////////////////////////////////////////
findEmbeddedObjectHandler(const librevenge::RVNGString & mimeType) const532 OdfEmbeddedObject OdfGenerator::findEmbeddedObjectHandler(const librevenge::RVNGString &mimeType) const
533 {
534 	auto i = mObjectHandlers.find(mimeType);
535 	if (i != mObjectHandlers.end())
536 		return i->second;
537 
538 	return nullptr;
539 }
540 
findEmbeddedImageHandler(const librevenge::RVNGString & mimeType) const541 OdfEmbeddedImage OdfGenerator::findEmbeddedImageHandler(const librevenge::RVNGString &mimeType) const
542 {
543 	auto i = mImageHandlers.find(mimeType);
544 	if (i != mImageHandlers.end())
545 		return i->second;
546 
547 	return nullptr;
548 }
549 
registerEmbeddedObjectHandler(const librevenge::RVNGString & mimeType,OdfEmbeddedObject objectHandler)550 void OdfGenerator::registerEmbeddedObjectHandler(const librevenge::RVNGString &mimeType, OdfEmbeddedObject objectHandler)
551 {
552 	mObjectHandlers[mimeType]=objectHandler;
553 }
554 
registerEmbeddedImageHandler(const librevenge::RVNGString & mimeType,OdfEmbeddedImage imageHandler)555 void OdfGenerator::registerEmbeddedImageHandler(const librevenge::RVNGString &mimeType, OdfEmbeddedImage imageHandler)
556 {
557 	mImageHandlers[mimeType]=imageHandler;
558 }
559 
560 ////////////////////////////////////////////////////////////
561 // page function
562 ////////////////////////////////////////////////////////////
startHeaderFooter(bool,const librevenge::RVNGPropertyList &)563 void OdfGenerator::startHeaderFooter(bool, const librevenge::RVNGPropertyList &)
564 {
565 	if (mbInHeaderFooter)
566 	{
567 		ODFGEN_DEBUG_MSG(("OdfGenerator::startHeaderFooter: a master page is already open\n"));
568 		return;
569 	}
570 	mbInHeaderFooter=true;
571 }
572 
endHeaderFooter()573 void OdfGenerator::endHeaderFooter()
574 {
575 	if (!mbInHeaderFooter)
576 	{
577 		ODFGEN_DEBUG_MSG(("OdfGenerator::endHeaderFooter: can not find any open master page\n"));
578 		return;
579 	}
580 	mbInHeaderFooter=false;
581 }
582 
startMasterPage(const librevenge::RVNGPropertyList &)583 void OdfGenerator::startMasterPage(const librevenge::RVNGPropertyList &)
584 {
585 	if (mbInMasterPage)
586 	{
587 		ODFGEN_DEBUG_MSG(("OdfGenerator::startMasterPage: a master page is already open\n"));
588 		return;
589 	}
590 	mbInMasterPage=true;
591 }
592 
endMasterPage()593 void OdfGenerator::endMasterPage()
594 {
595 	if (!mbInMasterPage)
596 	{
597 		ODFGEN_DEBUG_MSG(("OdfGenerator::endMasterPage: can not find any open master page\n"));
598 		return;
599 	}
600 	mbInMasterPage=false;
601 }
602 
603 ////////////////////////////////////////////////////////////
604 // frame/group
605 ////////////////////////////////////////////////////////////
openFrame(const librevenge::RVNGPropertyList & propList)606 void OdfGenerator::openFrame(const librevenge::RVNGPropertyList &propList)
607 {
608 	// First, let's create a basic Style for this box
609 	librevenge::RVNGPropertyList style;
610 	if (propList["style:horizontal-pos"])
611 		style.insert("style:horizontal-pos", propList["style:horizontal-pos"]->getStr());
612 	else
613 		style.insert("style:horizontal-rel", "left");
614 	if (propList["style:horizontal-rel"])
615 		style.insert("style:horizontal-rel", propList["style:horizontal-rel"]->getStr());
616 	else
617 		style.insert("style:horizontal-rel", "paragraph");
618 	if (propList["style:vertical-pos"])
619 		style.insert("style:vertical-pos", propList["style:vertical-pos"]->getStr());
620 	else
621 		style.insert("style:vertical-rel", "top");
622 	if (propList["style:vertical-rel"])
623 		style.insert("style:vertical-rel", propList["style:vertical-rel"]->getStr());
624 	else
625 		style.insert("style:vertical-rel", "page-content");
626 	librevenge::RVNGString frameStyleName=mGraphicManager.findOrAdd(style, Style::Z_Style);
627 
628 	librevenge::RVNGPropertyList graphic;
629 	mGraphicManager.addGraphicProperties(propList, graphic);
630 	// we do not want to add the default solid stroke if there are some double/... borders
631 	if (!propList["draw:stroke"])
632 		graphic.remove("draw:stroke");
633 	GraphicStyleManager::addFrameProperties(propList, graphic);
634 	graphic.insert("style:parent-style-name", frameStyleName);
635 	graphic.insert("draw:ole-draw-aspect", "1");
636 	librevenge::RVNGString frameAutomaticStyleName=
637 	    mGraphicManager.findOrAdd(graphic, useStyleAutomaticZone() ? Style::Z_StyleAutomatic : Style::Z_ContentAutomatic);
638 
639 	// And write the frame itself
640 	unsigned objectId = 0;
641 	if (propList["librevenge:frame-name"])
642 		objectId= getFrameId(propList["librevenge:frame-name"]->getStr());
643 	else
644 		objectId= getFrameId("");
645 	auto drawFrameOpenElement = std::make_shared<TagOpenElement>("draw:frame");
646 	drawFrameOpenElement->addAttribute("draw:style-name", frameAutomaticStyleName);
647 	librevenge::RVNGString objectName;
648 	objectName.sprintf("Object%i", objectId);
649 	drawFrameOpenElement->addAttribute("draw:name", objectName);
650 	if (propList["svg:x"])
651 		drawFrameOpenElement->addAttribute("svg:x", propList["svg:x"]->getStr());
652 	if (propList["svg:y"])
653 		drawFrameOpenElement->addAttribute("svg:y", propList["svg:y"]->getStr());
654 	addFrameProperties(propList, *drawFrameOpenElement);
655 	mpCurrentStorage->push_back(drawFrameOpenElement);
656 }
657 
closeFrame()658 void OdfGenerator::closeFrame()
659 {
660 	mpCurrentStorage->push_back(std::make_shared<TagCloseElement>("draw:frame"));
661 }
662 
addFrameProperties(const librevenge::RVNGPropertyList & propList,TagOpenElement & element) const663 void OdfGenerator::addFrameProperties(const librevenge::RVNGPropertyList &propList, TagOpenElement &element) const
664 {
665 	static char const *frameAttrib[] =
666 	{
667 		"draw:z-index",
668 		"fo:max-width", "fo:max-height",
669 		"style:rel-width", "style:rel-height", // checkme
670 		"text:anchor-page-number", "text:anchor-type",
671 		"table:end-cell-address",
672 		"table:table-background"
673 	};
674 	for (auto &i : frameAttrib)
675 	{
676 		if (propList[i])
677 			element.addAttribute(i, propList[i]->getStr());
678 	}
679 
680 	if (propList["svg:width"])
681 		element.addAttribute("svg:width", propList["svg:width"]->getStr());
682 	else if (propList["fo:min-width"]) // fixme: must be an attribute of draw:text-box
683 		element.addAttribute("fo:min-width", propList["fo:min-width"]->getStr());
684 	if (propList["svg:height"])
685 		element.addAttribute("svg:height", propList["svg:height"]->getStr());
686 	else if (propList["fo:min-height"]) // fixme: must be an attribute of draw:text-box
687 		element.addAttribute("fo:min-height", propList["fo:min-height"]->getStr());
688 	element.addAttribute("draw:layer", getLayerName(propList));
689 }
690 
getFrameId(librevenge::RVNGString val)691 unsigned OdfGenerator::getFrameId(librevenge::RVNGString val)
692 {
693 	bool hasLabel = val.cstr() && val.len();
694 	if (hasLabel && mFrameNameIdMap.find(val) != mFrameNameIdMap.end())
695 		return mFrameNameIdMap.find(val)->second;
696 	unsigned id=miFrameNumber++;
697 	if (hasLabel)
698 		mFrameNameIdMap[val]=id;
699 	return id;
700 }
701 
openGroup(const librevenge::RVNGPropertyList & propList)702 void OdfGenerator::openGroup(const librevenge::RVNGPropertyList &propList)
703 {
704 	auto groupElement=std::make_shared<TagOpenElement>("draw:g");
705 	addFrameProperties(propList, *groupElement);
706 	mpCurrentStorage->push_back(groupElement);
707 }
708 
closeGroup()709 void OdfGenerator::closeGroup()
710 {
711 	mpCurrentStorage->push_back(std::make_shared<TagCloseElement>("draw:g"));
712 }
713 
getLayerName(const librevenge::RVNGPropertyList & propList) const714 librevenge::RVNGString OdfGenerator::getLayerName(const librevenge::RVNGPropertyList &propList) const
715 {
716 	// layer does not seems to works in masterpage
717 	if (inMasterPage())
718 		return "layout";
719 
720 	if (propList["draw:layer"] && !propList["draw:layer"]->getStr().empty())
721 	{
722 		librevenge::RVNGString layer;
723 		layer.appendEscapedXML(propList["draw:layer"]->getStr());
724 		if (mLayerNameMap.find(layer)!=mLayerNameMap.end())
725 			return mLayerNameMap.find(layer)->second;
726 		ODFGEN_DEBUG_MSG(("OdfGenerator::getLayerName: called with not existing layer, returns the current layer name\n"));
727 	}
728 	if (mLayerNameStack.empty())
729 		return "layout";
730 	return mLayerNameStack.top();
731 }
732 
openLayer(const librevenge::RVNGPropertyList & propList)733 void OdfGenerator::openLayer(const librevenge::RVNGPropertyList &propList)
734 {
735 	if (inMasterPage())
736 	{
737 		ODFGEN_DEBUG_MSG(("OdfGenerator::openLayer: can not create layer in master page\n"));
738 		mLayerNameStack.push("layout");
739 		return;
740 	}
741 	librevenge::RVNGString layerName("");
742 	if (propList["draw:layer"])
743 		layerName=propList["draw:layer"]->getStr();
744 	else if (propList["svg:id"])
745 		layerName=propList["svg:id"]->getStr();
746 	if (layerName.empty())
747 	{
748 		ODFGEN_DEBUG_MSG(("OdfGenerator::openLayer: can not find the layer name\n"));
749 		mLayerNameStack.push("layout");
750 		return;
751 	}
752 	librevenge::RVNGString layer;
753 	layer.appendEscapedXML(layerName);
754 	if (mLayerNameSet.find(layer)!=mLayerNameSet.end())
755 	{
756 		// try to find a new name
757 		ODFGEN_DEBUG_MSG(("OdfGenerator::openLayer: called with an existing name, try to find a new name\n"));
758 		bool ok=false;
759 		for (int i=0; i<100; ++i)
760 		{
761 			librevenge::RVNGString suffix;
762 			suffix.sprintf("#%d", i);
763 			librevenge::RVNGString newName(layer);
764 			newName.append(suffix);
765 			if (mLayerNameSet.find(newName)!=mLayerNameSet.end())
766 				continue;
767 			mLayerNameMap[layer]=newName;
768 			layer=newName;
769 			ok=true;
770 			break;
771 		}
772 		if (!ok)
773 		{
774 			ODFGEN_DEBUG_MSG(("OdfGenerator::openLayer: can not find a new name, used old\n"));
775 		}
776 	}
777 	else
778 		mLayerNameMap[layer]=layer;
779 	mLayerNameSet.insert(layer);
780 	mLayerNameStack.push(layer);
781 }
782 
closeLayer()783 void OdfGenerator::closeLayer()
784 {
785 	if (mLayerNameStack.empty())
786 	{
787 		ODFGEN_DEBUG_MSG(("OdfGenerator::closeLayer: open layer is not called\n"));
788 		return;
789 	}
790 	mLayerNameStack.pop();
791 }
792 
appendLayersMasterStyles(OdfDocumentHandler * pHandler)793 void OdfGenerator::appendLayersMasterStyles(OdfDocumentHandler *pHandler)
794 {
795 	if (mLayerNameSet.empty()) return;
796 
797 	TagOpenElement("draw:layer-set").write(pHandler);
798 
799 	TagOpenElement layer("draw:layer");
800 
801 	// add the default layers
802 	for (int i=0; i<5; ++i)
803 	{
804 		static char const *defaults[] = {"layout", "background", "backgroundobjects", "controls", "measurelines"};
805 		if (mLayerNameSet.find(defaults[i])!=mLayerNameSet.end())
806 			continue;
807 		layer.addAttribute("draw:name", defaults[i]);
808 		layer.write(pHandler);
809 		TagCloseElement("draw:layer").write(pHandler);
810 	}
811 	for (const auto &layerName : mLayerNameSet)
812 	{
813 		layer.addAttribute("draw:name", layerName);
814 		layer.write(pHandler);
815 		TagCloseElement("draw:layer").write(pHandler);
816 	}
817 	TagCloseElement("draw:layer-set").write(pHandler);
818 }
819 
820 ////////////////////////////////////////////////////////////
821 // span/paragraph
822 ////////////////////////////////////////////////////////////
insertTab()823 void OdfGenerator::insertTab()
824 {
825 	mpCurrentStorage->push_back(std::make_shared<TagOpenElement>("text:tab"));
826 	mpCurrentStorage->push_back(std::make_shared<TagCloseElement>("text:tab"));
827 }
828 
insertSpace()829 void OdfGenerator::insertSpace()
830 {
831 	mpCurrentStorage->push_back(std::make_shared<TagOpenElement>("text:s"));
832 	mpCurrentStorage->push_back(std::make_shared<TagCloseElement>("text:s"));
833 }
834 
insertLineBreak(bool forceParaClose)835 void OdfGenerator::insertLineBreak(bool forceParaClose)
836 {
837 	if (!forceParaClose)
838 	{
839 		mpCurrentStorage->push_back(std::make_shared<TagOpenElement>("text:line-break"));
840 		mpCurrentStorage->push_back(std::make_shared<TagCloseElement>("text:line-break"));
841 		return;
842 	}
843 	closeSpan();
844 	closeParagraph();
845 
846 	auto pParagraphOpenElement = std::make_shared<TagOpenElement>("text:p");
847 	if (!mLastParagraphName.empty())
848 		pParagraphOpenElement->addAttribute("text:style-name", mLastParagraphName.cstr());
849 	mpCurrentStorage->push_back(pParagraphOpenElement);
850 	mParagraphHeadingStack.push(false);
851 
852 	auto pSpanOpenElement = std::make_shared<TagOpenElement>("text:span");
853 	if (!mLastSpanName.empty())
854 		pSpanOpenElement->addAttribute("text:style-name", mLastSpanName.cstr());
855 	mpCurrentStorage->push_back(pSpanOpenElement);
856 
857 }
858 
insertField(const librevenge::RVNGPropertyList & propList)859 void OdfGenerator::insertField(const librevenge::RVNGPropertyList &propList)
860 {
861 	if (!propList["librevenge:field-type"] || propList["librevenge:field-type"]->getStr().empty())
862 		return;
863 
864 	const librevenge::RVNGString &type = propList["librevenge:field-type"]->getStr();
865 
866 	auto openElement = std::make_shared<TagOpenElement>(type);
867 	if (type == "text:page-number")
868 		openElement->addAttribute("text:select-page", propList["text:select-page"] ? propList["text:select-page"]->getStr() : "current");
869 	else if (type == "text:chapter")
870 	{
871 		for (int i=0; i<2; ++i)
872 		{
873 			char const *wh[]= {"text:display", "text:outline-level"};
874 			if (propList[wh[i]])
875 				openElement->addAttribute(wh[i], propList[wh[i]]->getStr());
876 		}
877 	}
878 	else if (type == "text:conditional-text")
879 	{
880 		for (int i=0; i<4; ++i)
881 		{
882 			char const *wh[]= {"text:condition", "text:current-value", "text:string-value-if-false", "text:string-value-if-true"};
883 			if (propList[wh[i]])
884 				openElement->addAttribute(wh[i], librevenge::RVNGString::escapeXML(propList[wh[i]]->getStr()));
885 		}
886 	}
887 	else if (type == "text:date" || type == "text:time")
888 	{
889 		// TODO: create a library function to avoid dupplicated code with OdsGenerator::openSheetCell
890 		if (type == "text:date" && propList["librevenge:day"] && propList["librevenge:month"] && propList["librevenge:year"])
891 		{
892 			librevenge::RVNGString date;
893 			if (propList["librevenge:hours"])
894 			{
895 				int minute=propList["librevenge:minutes"] ? propList["librevenge:minutes"]->getInt() : 0;
896 				int second=propList["librevenge:seconds"] ? propList["librevenge:seconds"]->getInt() : 0;
897 				date.sprintf("%04d-%02d-%02dT%02d:%02d:%02d", propList["librevenge:year"]->getInt(),
898 				             propList["librevenge:month"]->getInt(),  propList["librevenge:day"]->getInt(),
899 				             propList["librevenge:hours"]->getInt(), minute, second);
900 			}
901 			else
902 				date.sprintf("%04d-%02d-%02d", propList["librevenge:year"]->getInt(),
903 				             propList["librevenge:month"]->getInt(),  propList["librevenge:day"]->getInt());
904 			openElement->addAttribute("text:date-value", date);
905 		}
906 		else if (type == "text:time" && propList["librevenge:hours"])
907 		{
908 			int minute=propList["librevenge:minutes"] ? propList["librevenge:minutes"]->getInt() : 0;
909 			int second=propList["librevenge:seconds"] ? propList["librevenge:seconds"]->getInt() : 0;
910 			librevenge::RVNGString time;
911 			time.sprintf("PT%02dH%02dM%02dS", propList["librevenge:hours"]->getInt(), minute, second);
912 			openElement->addAttribute("office:time-value", time);
913 		}
914 
915 		if (propList["librevenge:value-type"])
916 		{
917 			librevenge::RVNGString numberingName=mNumberingManager.findOrAdd(propList);
918 			if (!numberingName.empty())
919 				openElement->addAttribute("style:data-style-name", numberingName.cstr());
920 		}
921 		if (propList["text:fixed"])
922 			openElement->addAttribute("text:fixed", propList["text:fixed"]->getStr());
923 	}
924 	else if (type == "text:file-name" || type=="text:template-name")
925 	{
926 		if (propList["text:display"])
927 			openElement->addAttribute("text:display", propList["text:display"]->getStr());
928 	}
929 	else if (type == "text:hidden-paragraph")
930 	{
931 		for (int i=0; i<2; ++i)
932 		{
933 			char const *wh[]= {"text:condition", "text:is-hidden"};
934 			if (propList[wh[i]])
935 				openElement->addAttribute(wh[i], librevenge::RVNGString::escapeXML(propList[wh[i]]->getStr()));
936 		}
937 	}
938 	else if (type == "text:placeholder")
939 	{
940 		for (int i=0; i<2; ++i)
941 		{
942 			char const *wh[]= {"text:description", "text:placeholder-type"};
943 			if (propList[wh[i]])
944 				openElement->addAttribute(wh[i], librevenge::RVNGString::escapeXML(propList[wh[i]]->getStr()));
945 		}
946 	}
947 	else if (type == "text:reference-mark" || type == "text:reference-mark-start" || type == "text:reference-mark-end" ||
948 	         type == "text:bookmark-start" || type == "text:bookmark-end")
949 	{
950 		for (int i=0; i<1; ++i)
951 		{
952 			char const *wh[]= {"text:name"};
953 			if (propList[wh[i]])
954 				openElement->addAttribute(wh[i], librevenge::RVNGString::escapeXML(propList[wh[i]]->getStr()));
955 		}
956 	}
957 	else if (type == "text:reference-ref")
958 	{
959 		for (int i=0; i<2; ++i)
960 		{
961 			char const *wh[]= {"text:reference-format", "text:ref-name"};
962 			if (propList[wh[i]])
963 				openElement->addAttribute(wh[i], librevenge::RVNGString::escapeXML(propList[wh[i]]->getStr()));
964 		}
965 	}
966 	else if (type == "text:sequence")
967 	{
968 		for (int i=0; i<3; ++i)
969 		{
970 			char const *wh[]= {"text:formula", "text:name", "text:ref-name"};
971 			if (propList[wh[i]])
972 				openElement->addAttribute(wh[i], librevenge::RVNGString::escapeXML(propList[wh[i]]->getStr()));
973 		}
974 	}
975 	else if (type == "text:text-input")
976 	{
977 		for (int i=0; i<1; ++i)
978 		{
979 			char const *wh[]= {"text:description"};
980 			if (propList[wh[i]])
981 				openElement->addAttribute(wh[i], librevenge::RVNGString::escapeXML(propList[wh[i]]->getStr()));
982 		}
983 	}
984 	else if (type == "text:user-defined")
985 	{
986 		for (int i=0; i<3; ++i)
987 		{
988 			char const *wh[]= {"office:string-value", "office:value", "text:name"};
989 			if (propList[wh[i]])
990 				openElement->addAttribute(wh[i], librevenge::RVNGString::escapeXML(propList[wh[i]]->getStr()));
991 		}
992 	}
993 	else if (type == "text:variable-get")
994 	{
995 		for (int i=0; i<2; ++i)
996 		{
997 			char const *wh[]= {"text:display", "text:name"};
998 			if (propList[wh[i]])
999 				openElement->addAttribute(wh[i], librevenge::RVNGString::escapeXML(propList[wh[i]]->getStr()));
1000 		}
1001 	}
1002 	else if (type == "text:variable-set" || type == "text:expression")
1003 	{
1004 		for (int i=0; i<5; ++i)
1005 		{
1006 			char const *wh[]= {"text:formula", "text:name", "office:string-value", "office:value", "office:value-type"};
1007 			if (propList[wh[i]])
1008 				openElement->addAttribute(wh[i], librevenge::RVNGString::escapeXML(propList[wh[i]]->getStr()));
1009 		}
1010 	}
1011 	// the database field
1012 	else if (type == "text:database-display")
1013 	{
1014 		for (int i=0; i<4; ++i)
1015 		{
1016 			char const *wh[]= {"text:column-name", "text:database-name", "text:table-name", "text:table-type"};
1017 			if (propList[wh[i]])
1018 				openElement->addAttribute(wh[i], librevenge::RVNGString::escapeXML(propList[wh[i]]->getStr()));
1019 		}
1020 	}
1021 	else if (type == "text:database-name")
1022 	{
1023 		for (int i=0; i<3; ++i)
1024 		{
1025 			char const *wh[]= {"text:database-name", "text:table-name", "text:table-type"};
1026 			if (propList[wh[i]])
1027 				openElement->addAttribute(wh[i], librevenge::RVNGString::escapeXML(propList[wh[i]]->getStr()));
1028 		}
1029 	}
1030 	else if (type == "text:database-row-select")
1031 	{
1032 		for (int i=0; i<5; ++i)
1033 		{
1034 			char const *wh[]= {"text:condition", "text:database-name", "text:row-number", "text:table-name", "text:table-type"};
1035 			if (propList[wh[i]])
1036 				openElement->addAttribute(wh[i], librevenge::RVNGString::escapeXML(propList[wh[i]]->getStr()));
1037 		}
1038 	}
1039 	if (propList["style:num-format"])
1040 		openElement->addAttribute("style:num-format", propList["style:num-format"]->getStr());
1041 
1042 	mpCurrentStorage->push_back(openElement);
1043 	if (propList["librevenge:field-content"])
1044 		mpCurrentStorage->push_back(std::make_shared<CharDataElement>(propList["librevenge:field-content"]->getStr()));
1045 	mpCurrentStorage->push_back(std::make_shared<TagCloseElement>(type));
1046 }
1047 
insertText(const librevenge::RVNGString & text)1048 void OdfGenerator::insertText(const librevenge::RVNGString &text)
1049 {
1050 	if (!text.empty())
1051 		mpCurrentStorage->push_back(std::make_shared<TextElement>(text));
1052 }
1053 
defineCharacterStyle(const librevenge::RVNGPropertyList & propList)1054 void OdfGenerator::defineCharacterStyle(const librevenge::RVNGPropertyList &propList)
1055 {
1056 	if (!propList["librevenge:span-id"])
1057 	{
1058 		ODFGEN_DEBUG_MSG(("OdfGenerator::defineCharacterStyle: called without id\n"));
1059 		return;
1060 	}
1061 	mIdSpanMap[propList["librevenge:span-id"]->getInt()]=propList;
1062 }
1063 
openSpan(const librevenge::RVNGPropertyList & propList)1064 void OdfGenerator::openSpan(const librevenge::RVNGPropertyList &propList)
1065 {
1066 	librevenge::RVNGString sName("");
1067 	librevenge::RVNGPropertyList pList(propList);
1068 	if (pList["librevenge:span-id"])
1069 	{
1070 		int id=pList["librevenge:span-id"]->getInt();
1071 		if (mIdSpanNameMap.find(id)!=mIdSpanNameMap.end())
1072 			sName=mIdSpanNameMap.find(id)->second;
1073 		else if (mIdSpanMap.find(id)!=mIdSpanMap.end())
1074 			pList=mIdSpanMap.find(id)->second;
1075 		else
1076 		{
1077 			ODFGEN_DEBUG_MSG(("OdfGenerator::openSpan: can not find the style %d\n", id));
1078 			pList.clear();
1079 		}
1080 	}
1081 
1082 	if (sName.empty())
1083 	{
1084 		if (pList["style:font-name"])
1085 			mFontManager.findOrAdd(pList["style:font-name"]->getStr().cstr());
1086 		sName = mSpanManager.findOrAdd(pList, useStyleAutomaticZone() ? Style::Z_StyleAutomatic : Style::Z_Unknown);
1087 		if (pList["librevenge:span-id"])
1088 			mIdSpanNameMap[pList["librevenge:span-id"]->getInt()]=sName;
1089 	}
1090 	auto pSpanOpenElement = std::make_shared<TagOpenElement>("text:span");
1091 	pSpanOpenElement->addAttribute("text:style-name", sName.cstr());
1092 	mpCurrentStorage->push_back(pSpanOpenElement);
1093 	mLastSpanName=sName;
1094 }
1095 
closeSpan()1096 void OdfGenerator::closeSpan()
1097 {
1098 	mpCurrentStorage->push_back(std::make_shared<TagCloseElement>("text:span"));
1099 }
1100 
openLink(const librevenge::RVNGPropertyList & propList)1101 void OdfGenerator::openLink(const librevenge::RVNGPropertyList &propList)
1102 {
1103 	if (!propList["librevenge:type"])
1104 	{
1105 		ODFGEN_DEBUG_MSG(("OdfGenerator::openLink: linked type is not defined, assume link\n"));
1106 	}
1107 	auto pLinkOpenElement = std::make_shared<TagOpenElement>("text:a");
1108 	librevenge::RVNGPropertyList::Iter i(propList);
1109 	for (i.rewind(); i.next();)
1110 	{
1111 		if (!i.child()) // write out simple properties only
1112 			// The string we get here might be url decoded, so
1113 			// sscape characters that might mess up the resulting xml
1114 			pLinkOpenElement->addAttribute(i.key(), librevenge::RVNGString::escapeXML(i()->getStr()));
1115 	}
1116 	mpCurrentStorage->push_back(pLinkOpenElement);
1117 }
1118 
closeLink()1119 void OdfGenerator::closeLink()
1120 {
1121 	mpCurrentStorage->push_back(std::make_shared<TagCloseElement>("text:a"));
1122 }
1123 
defineParagraphStyle(const librevenge::RVNGPropertyList & propList)1124 void OdfGenerator::defineParagraphStyle(const librevenge::RVNGPropertyList &propList)
1125 {
1126 	if (!propList["librevenge:paragraph-id"])
1127 	{
1128 		ODFGEN_DEBUG_MSG(("OdfGenerator::defineParagraphStyle: called without id\n"));
1129 		return;
1130 	}
1131 	mIdParagraphMap[propList["librevenge:paragraph-id"]->getInt()]=propList;
1132 }
1133 
openParagraph(const librevenge::RVNGPropertyList & propList)1134 void OdfGenerator::openParagraph(const librevenge::RVNGPropertyList &propList)
1135 {
1136 	librevenge::RVNGPropertyList pList(propList);
1137 	librevenge::RVNGString paragraphName("");
1138 	bool isMasterPage=(propList["style:master-page-name"]!=nullptr);
1139 
1140 	if (propList["text:outline-level"])
1141 		pList.insert("style:default-outline-level", propList["text:outline-level"]->clone());
1142 	if (pList["librevenge:paragraph-id"])
1143 	{
1144 		int id=pList["librevenge:paragraph-id"]->getInt();
1145 		if (mIdParagraphNameMap.find(id)!=mIdParagraphNameMap.end())
1146 			paragraphName=mIdParagraphNameMap.find(id)->second;
1147 		else if (mIdParagraphMap.find(id)!=mIdParagraphMap.end())
1148 			pList=mIdParagraphMap.find(id)->second;
1149 		else
1150 		{
1151 			ODFGEN_DEBUG_MSG(("OdfGenerator::openParagraph: can not find the style %d\n", id));
1152 			pList.clear();
1153 		}
1154 		if (isMasterPage)
1155 			pList.insert("style:master-page-name", propList["style:master-page-name"]->clone());
1156 	}
1157 
1158 	if (paragraphName.empty() || isMasterPage)
1159 	{
1160 		if (pList["style:font-name"])
1161 			mFontManager.findOrAdd(pList["style:font-name"]->getStr().cstr());
1162 		paragraphName = mParagraphManager.findOrAdd(pList, useStyleAutomaticZone() ? Style::Z_StyleAutomatic : Style::Z_Unknown);
1163 		if (pList["librevenge:paragraph-id"] && !isMasterPage)
1164 			mIdParagraphNameMap[pList["librevenge:paragraph-id"]->getInt()]=paragraphName;
1165 	}
1166 
1167 	// create a document element corresponding to the paragraph, and append it to our list of document elements
1168 	std::shared_ptr<TagOpenElement> pParagraphOpenElement;
1169 
1170 	if (propList["text:outline-level"])
1171 	{
1172 		mParagraphHeadingStack.push(true);
1173 		pParagraphOpenElement = std::make_shared<TagOpenElement>("text:h");
1174 		pParagraphOpenElement->addAttribute("text:outline-level", propList["text:outline-level"]->getStr());
1175 	}
1176 	else
1177 	{
1178 		mParagraphHeadingStack.push(false);
1179 		pParagraphOpenElement = std::make_shared<TagOpenElement>("text:p");
1180 	}
1181 	pParagraphOpenElement->addAttribute("text:style-name", paragraphName);
1182 	mpCurrentStorage->push_back(pParagraphOpenElement);
1183 	mLastParagraphName=paragraphName;
1184 }
1185 
closeParagraph()1186 void OdfGenerator::closeParagraph()
1187 {
1188 	if (mParagraphHeadingStack.empty())
1189 	{
1190 		ODFGEN_DEBUG_MSG(("OdfGenerator::closeParagraph: can not find any opened paragraph\n"));
1191 		return;
1192 	}
1193 	mpCurrentStorage->push_back(mParagraphHeadingStack.top() ? std::make_shared<TagCloseElement>("text:h") : std::make_shared<TagCloseElement>("text:p"));
1194 	mParagraphHeadingStack.pop();
1195 }
1196 
1197 ////////////////////////////////////////////////////////////
1198 // list
1199 ////////////////////////////////////////////////////////////
popListState()1200 void OdfGenerator::popListState()
1201 {
1202 	mListManager.popState();
1203 }
1204 
pushListState()1205 void OdfGenerator::pushListState()
1206 {
1207 	mListManager.pushState();
1208 }
1209 
openListLevel(const librevenge::RVNGPropertyList & propList,bool ordered)1210 void OdfGenerator::openListLevel(const librevenge::RVNGPropertyList &propList, bool ordered)
1211 {
1212 	ListManager::State &state=mListManager.getState();
1213 	if (state.mbListElementParagraphOpened)
1214 	{
1215 		closeParagraph();
1216 		state.mbListElementParagraphOpened = false;
1217 	}
1218 	librevenge::RVNGPropertyList pList(propList);
1219 	if (!pList["librevenge:level"])
1220 		pList.insert("librevenge:level", int(state.mbListElementOpened.size())+1);
1221 	if (pList["style:font-name"])
1222 		mFontManager.findOrAdd(pList["style:font-name"]->getStr().cstr());
1223 	mListManager.defineLevel(pList, ordered, useStyleAutomaticZone() ? Style::Z_StyleAutomatic : Style::Z_Unknown);
1224 
1225 	auto pListLevelOpenElement = std::make_shared<TagOpenElement>("text:list");
1226 	if (!state.mbListElementOpened.empty() && !state.mbListElementOpened.top())
1227 	{
1228 		mpCurrentStorage->push_back(std::make_shared<TagOpenElement>("text:list-item"));
1229 		state.mbListElementOpened.top() = true;
1230 	}
1231 
1232 	state.mbListElementOpened.push(false);
1233 	if (state.mbListElementOpened.size() == 1)
1234 	{
1235 		// add a sanity check ( to avoid a crash if mpCurrentListStyle is NULL)
1236 		if (state.mpCurrentListStyle)
1237 			pListLevelOpenElement->addAttribute("text:style-name", state.mpCurrentListStyle->getName());
1238 	}
1239 	if (ordered && state.mbListContinueNumbering)
1240 		pListLevelOpenElement->addAttribute("text:continue-numbering", "true");
1241 	mpCurrentStorage->push_back(pListLevelOpenElement);
1242 }
1243 
closeListLevel()1244 void OdfGenerator::closeListLevel()
1245 {
1246 	ListManager::State &state=mListManager.getState();
1247 	if (state.mbListElementOpened.empty())
1248 	{
1249 		// this implies that openListLevel was not called, so it is better to stop here
1250 		ODFGEN_DEBUG_MSG(("OdfGenerator: Attempting to close an unexisting level\n"));
1251 		return;
1252 	}
1253 	if (state.mbListElementOpened.top())
1254 	{
1255 		mpCurrentStorage->push_back(std::make_shared<TagCloseElement>("text:list-item"));
1256 		state.mbListElementOpened.top() = false;
1257 	}
1258 
1259 	mpCurrentStorage->push_back(std::make_shared<TagCloseElement>("text:list"));
1260 	state.mbListElementOpened.pop();
1261 }
1262 
openListElement(const librevenge::RVNGPropertyList & propList)1263 void OdfGenerator::openListElement(const librevenge::RVNGPropertyList &propList)
1264 {
1265 	ListManager::State &state=mListManager.getState();
1266 	state.miLastListLevel = state.miCurrentListLevel;
1267 	if (state.miCurrentListLevel == 1)
1268 		state.miLastListNumber++;
1269 
1270 	if (state.mbListElementOpened.top())
1271 	{
1272 		mpCurrentStorage->push_back(std::make_shared<TagCloseElement>("text:list-item"));
1273 		state.mbListElementOpened.top() = false;
1274 	}
1275 
1276 	librevenge::RVNGPropertyList finalPropList(propList);
1277 #if 0
1278 	// this property is ignored in TextRunStyle.c++
1279 	if (state.mpCurrentListStyle)
1280 		finalPropList.insert("style:list-style-name", state.mpCurrentListStyle->getName());
1281 #endif
1282 	finalPropList.insert("style:parent-style-name", "Standard");
1283 	librevenge::RVNGString paragName =mParagraphManager.findOrAdd(finalPropList, useStyleAutomaticZone() ? Style::Z_StyleAutomatic : Style::Z_Unknown);
1284 
1285 	auto pOpenListItem = std::make_shared<TagOpenElement>("text:list-item");
1286 	if (propList["text:start-value"] && propList["text:start-value"]->getInt() > 0)
1287 		pOpenListItem->addAttribute("text:start-value", propList["text:start-value"]->getStr());
1288 	mpCurrentStorage->push_back(pOpenListItem);
1289 
1290 	auto pOpenListElementParagraph = std::make_shared<TagOpenElement>("text:p");
1291 	pOpenListElementParagraph->addAttribute("text:style-name", paragName);
1292 	if (propList["style:master-page-name"])
1293 		pOpenListElementParagraph->addAttribute("style:master-page-name",propList["style:master-page-name"]->getStr());
1294 	mpCurrentStorage->push_back(pOpenListElementParagraph);
1295 	mParagraphHeadingStack.push(false);
1296 
1297 	state.mbListElementOpened.top() = true;
1298 	state.mbListElementParagraphOpened = true;
1299 	state.mbListContinueNumbering = false;
1300 }
1301 
closeListElement()1302 void OdfGenerator::closeListElement()
1303 {
1304 	// this code is kind of tricky, because we don't actually close the list element (because this list element
1305 	// could contain another list level in OOo's implementation of lists). that is done in the closeListLevel
1306 	// code (or when we open another list element)
1307 	if (mListManager.getState().mbListElementParagraphOpened)
1308 	{
1309 		closeParagraph();
1310 		mListManager.getState().mbListElementParagraphOpened = false;
1311 	}
1312 }
1313 
1314 
1315 ////////////////////////////////////////////////////////////
1316 // table
1317 ////////////////////////////////////////////////////////////
openTable(const librevenge::RVNGPropertyList & propList)1318 void OdfGenerator::openTable(const librevenge::RVNGPropertyList &propList)
1319 {
1320 	mTableManager.openTable(propList, useStyleAutomaticZone() ? Style::Z_StyleAutomatic : Style::Z_ContentAutomatic);
1321 
1322 	Table *table=mTableManager.getActualTable();
1323 	if (!table)
1324 	{
1325 		ODFGEN_DEBUG_MSG(("OdfGenerator::openTable: can not retrieve the table data\n"));
1326 		return;
1327 	}
1328 	librevenge::RVNGString tableName=table->getName();
1329 
1330 	auto pTableOpenElement = std::make_shared<TagOpenElement>("table:table");
1331 	pTableOpenElement->addAttribute("table:name", tableName.cstr());
1332 	pTableOpenElement->addAttribute("table:style-name", tableName.cstr());
1333 	mpCurrentStorage->push_back(pTableOpenElement);
1334 
1335 	for (int i=0; i<table->getNumColumns(); ++i)
1336 	{
1337 		auto pTableColumnOpenElement = std::make_shared<TagOpenElement>("table:table-column");
1338 		librevenge::RVNGString sColumnStyleName;
1339 		sColumnStyleName.sprintf("%s.Column%i", tableName.cstr(), (i+1));
1340 		pTableColumnOpenElement->addAttribute("table:style-name", sColumnStyleName.cstr());
1341 		mpCurrentStorage->push_back(pTableColumnOpenElement);
1342 
1343 		auto pTableColumnCloseElement = std::make_shared<TagCloseElement>("table:table-column");
1344 		mpCurrentStorage->push_back(pTableColumnCloseElement);
1345 	}
1346 }
1347 
closeTable()1348 void OdfGenerator::closeTable()
1349 {
1350 	if (!mTableManager.getActualTable())
1351 		return;
1352 	mTableManager.closeTable();
1353 	mpCurrentStorage->push_back(std::make_shared<TagCloseElement>("table:table"));
1354 }
1355 
openTableRow(const librevenge::RVNGPropertyList & propList,bool compatibleOdp)1356 bool OdfGenerator::openTableRow(const librevenge::RVNGPropertyList &propList, bool compatibleOdp)
1357 {
1358 	Table *table=mTableManager.getActualTable();
1359 	if (!table)
1360 	{
1361 		ODFGEN_DEBUG_MSG(("OdfGenerator::openTableRow called with no table\n"));
1362 		return false;
1363 	}
1364 	librevenge::RVNGString rowName=table->openRow(propList, compatibleOdp);
1365 	if (rowName.empty())
1366 		return false;
1367 	bool inHeader=false;
1368 	if (table->isRowOpened(inHeader) && inHeader)
1369 		mpCurrentStorage->push_back(std::make_shared<TagOpenElement>("table:table-header-rows"));
1370 
1371 	auto pTableRowOpenElement = std::make_shared<TagOpenElement>("table:table-row");
1372 	pTableRowOpenElement->addAttribute("table:style-name", rowName);
1373 	mpCurrentStorage->push_back(pTableRowOpenElement);
1374 	return true;
1375 }
1376 
closeTableRow()1377 void OdfGenerator::closeTableRow()
1378 {
1379 	Table *table=mTableManager.getActualTable();
1380 	if (!table) return;
1381 	bool inHeader=false;
1382 	if (!table->isRowOpened(inHeader) || !table->closeRow()) return;
1383 	mpCurrentStorage->push_back(std::make_shared<TagCloseElement>("table:table-row"));
1384 	if (inHeader)
1385 		mpCurrentStorage->push_back(std::make_shared<TagCloseElement>("table:table-header-rows"));
1386 }
1387 
isInTableRow(bool & inHeaderRow) const1388 bool OdfGenerator::isInTableRow(bool &inHeaderRow) const
1389 {
1390 	Table const *table=mTableManager.getActualTable();
1391 	if (!table) return false;
1392 	return table->isRowOpened(inHeaderRow);
1393 }
1394 
openTableCell(const librevenge::RVNGPropertyList & propList)1395 bool OdfGenerator::openTableCell(const librevenge::RVNGPropertyList &propList)
1396 {
1397 	Table *table=mTableManager.getActualTable();
1398 	if (!table)
1399 	{
1400 		ODFGEN_DEBUG_MSG(("OdfGenerator::openTableCell called with no table\n"));
1401 		return false;
1402 	}
1403 
1404 	librevenge::RVNGString cellName = table->openCell(propList);
1405 	if (cellName.empty())
1406 		return false;
1407 
1408 	auto pTableCellOpenElement = std::make_shared<TagOpenElement>("table:table-cell");
1409 	pTableCellOpenElement->addAttribute("table:style-name", cellName);
1410 	if (propList["table:number-columns-spanned"])
1411 		pTableCellOpenElement->addAttribute("table:number-columns-spanned",
1412 		                                    propList["table:number-columns-spanned"]->getStr().cstr());
1413 	if (propList["table:number-rows-spanned"])
1414 		pTableCellOpenElement->addAttribute("table:number-rows-spanned",
1415 		                                    propList["table:number-rows-spanned"]->getStr().cstr());
1416 	// pTableCellOpenElement->addAttribute("table:value-type", "string");
1417 	mpCurrentStorage->push_back(pTableCellOpenElement);
1418 	return true;
1419 }
1420 
closeTableCell()1421 void OdfGenerator::closeTableCell()
1422 {
1423 	if (!mTableManager.getActualTable() || !mTableManager.getActualTable()->closeCell())
1424 		return;
1425 
1426 	mpCurrentStorage->push_back(std::make_shared<TagCloseElement>("table:table-cell"));
1427 }
1428 
insertCoveredTableCell(const librevenge::RVNGPropertyList & propList)1429 void OdfGenerator::insertCoveredTableCell(const librevenge::RVNGPropertyList &propList)
1430 {
1431 	if (!mTableManager.getActualTable() ||
1432 	        !mTableManager.getActualTable()->insertCoveredCell(propList))
1433 		return;
1434 
1435 	mpCurrentStorage->push_back(std::make_shared<TagOpenElement>("table:covered-table-cell"));
1436 	mpCurrentStorage->push_back(std::make_shared<TagCloseElement>("table:covered-table-cell"));
1437 }
1438 
1439 ////////////////////////////////////////////////////////////
1440 // image/embedded
1441 ////////////////////////////////////////////////////////////
insertEquation(const librevenge::RVNGPropertyList & propList)1442 void OdfGenerator::insertEquation(const librevenge::RVNGPropertyList &propList)
1443 {
1444 	if ((!propList["librevenge:data"] && !propList["office:binary-data"]) || !propList["librevenge:mime-type"])
1445 	{
1446 		ODFGEN_DEBUG_MSG(("OdfGenerator::insertEquation: imcomplete property list\n"));
1447 		return;
1448 	}
1449 	if (propList["librevenge:mime-type"]->getStr()!="application/mathml-presentation+xml" &&
1450 	        propList["librevenge:mime-type"]->getStr()!="application/mathml+xml")
1451 	{
1452 		ODFGEN_DEBUG_MSG(("OdfGenerator::insertEquation: unexpected mime type \n"));
1453 		return;
1454 	}
1455 
1456 	std::string data;
1457 	if (propList["librevenge:data"])
1458 		data=propList["librevenge:data"]->getStr().cstr();
1459 	else
1460 	{
1461 		librevenge::RVNGBinaryData binaryData(propList["office:binary-data"]->getStr());
1462 		if (!binaryData.empty())
1463 			data.append((char const *) binaryData.getDataBuffer(), size_t(binaryData.size()));
1464 	}
1465 	if (data.empty())
1466 	{
1467 		ODFGEN_DEBUG_MSG(("OdfGenerator::insertEquation: can not find the data\n"));
1468 		return;
1469 	}
1470 
1471 	try
1472 	{
1473 		std::vector<std::shared_ptr<DocumentElement>> elementStack;
1474 		elementStack.push_back(std::make_shared<TagOpenElement>("draw:object"));
1475 		appendXML(data, elementStack);
1476 		elementStack.push_back(std::make_shared<TagCloseElement>("draw:object"));
1477 		mpCurrentStorage->insert(mpCurrentStorage->end(), elementStack.begin(), elementStack.end());
1478 	}
1479 	catch (...)
1480 	{
1481 		ODFGEN_DEBUG_MSG(("OdfGenerator::insertEquation: ARGHH, catch an exception when decoding picture!!!\n"));
1482 	}
1483 }
1484 
insertBinaryObject(const librevenge::RVNGPropertyList & propList)1485 void OdfGenerator::insertBinaryObject(const librevenge::RVNGPropertyList &propList)
1486 {
1487 	if (!propList["office:binary-data"] || !propList["librevenge:mime-type"])
1488 	{
1489 		// check for xlink
1490 		if (propList["librevenge:xlink"])
1491 		{
1492 			auto openElement = std::make_shared<TagOpenElement>("draw:image");
1493 
1494 			openElement->addAttribute("xlink:href", propList["librevenge:xlink"]->getStr());
1495 			openElement->addAttribute("xlink:type", "simple");
1496 			openElement->addAttribute("xlink:show", "embed");
1497 
1498 			mpCurrentStorage->push_back(openElement);
1499 			mpCurrentStorage->push_back(std::make_shared<TagCloseElement>("draw:image"));
1500 
1501 			librevenge::RVNGPropertyListVector const *replacements=propList.child("librevenge:replacement-objects");
1502 			if (!replacements)
1503 				return;
1504 			for (unsigned long c=0; c < replacements->count(); ++c)
1505 				insertBinaryObject((*replacements)[c]);
1506 			return;
1507 		}
1508 		ODFGEN_DEBUG_MSG(("OdfGenerator::addDocumentHandler: can not find data or mimetype\n"));
1509 		return;
1510 	}
1511 	if (propList["librevenge:mime-type"]->getStr()=="application/mathml-presentation+xml" ||
1512 	        propList["librevenge:mime-type"]->getStr()=="application/mathml+xml")
1513 	{
1514 		insertEquation(propList);
1515 		return;
1516 	}
1517 	OdfEmbeddedObject tmpObjectHandler = findEmbeddedObjectHandler(propList["librevenge:mime-type"]->getStr());
1518 	OdfEmbeddedImage tmpImageHandler = findEmbeddedImageHandler(propList["librevenge:mime-type"]->getStr());
1519 
1520 	if (tmpObjectHandler || tmpImageHandler)
1521 	{
1522 		try
1523 		{
1524 			librevenge::RVNGBinaryData data(propList["office:binary-data"]->getStr());
1525 			if (tmpObjectHandler)
1526 			{
1527 				DocumentElementVector tmpContentElements;
1528 				InternalHandler tmpHandler(&tmpContentElements);
1529 
1530 
1531 				if (tmpObjectHandler(data, &tmpHandler, ODF_FLAT_XML) && !tmpContentElements.empty())
1532 				{
1533 					mpCurrentStorage->push_back(std::make_shared<TagOpenElement>("draw:object"));
1534 					mpCurrentStorage->insert(mpCurrentStorage->end(), tmpContentElements.begin(), tmpContentElements.end());
1535 					mpCurrentStorage->push_back(std::make_shared<TagCloseElement>("draw:object"));
1536 				}
1537 			}
1538 			if (tmpImageHandler)
1539 			{
1540 				librevenge::RVNGBinaryData output;
1541 				if (tmpImageHandler(data, output))
1542 				{
1543 					mpCurrentStorage->push_back(std::make_shared<TagOpenElement>("draw:image"));
1544 
1545 					mpCurrentStorage->push_back(std::make_shared<TagOpenElement>("office:binary-data"));
1546 					librevenge::RVNGString binaryBase64Data = output.getBase64Data();
1547 
1548 					mpCurrentStorage->push_back(std::make_shared<CharDataElement>(binaryBase64Data.cstr()));
1549 
1550 					mpCurrentStorage->push_back(std::make_shared<TagCloseElement>("office:binary-data"));
1551 
1552 					mpCurrentStorage->push_back(std::make_shared<TagCloseElement>("draw:image"));
1553 				}
1554 			}
1555 		}
1556 		catch (...)
1557 		{
1558 			ODFGEN_DEBUG_MSG(("OdfGenerator::insertBinaryObject: ARGHH, catch an exception when decoding data!!!\n"));
1559 		}
1560 	}
1561 	else
1562 		// assuming we have a binary image or a object_ole that we can just insert as it is
1563 	{
1564 		if (propList["librevenge:mime-type"]->getStr() == "object/ole")
1565 			mpCurrentStorage->push_back(std::make_shared<TagOpenElement>("draw:object-ole"));
1566 		else
1567 			mpCurrentStorage->push_back(std::make_shared<TagOpenElement>("draw:image"));
1568 
1569 		mpCurrentStorage->push_back(std::make_shared<TagOpenElement>("office:binary-data"));
1570 		try
1571 		{
1572 			mpCurrentStorage->push_back(std::make_shared<CharDataElement>(propList["office:binary-data"]->getStr().cstr()));
1573 		}
1574 		catch (...)
1575 		{
1576 			ODFGEN_DEBUG_MSG(("OdfGenerator::insertBinaryObject: ARGHH, catch an exception when decoding picture!!!\n"));
1577 		}
1578 		mpCurrentStorage->push_back(std::make_shared<TagCloseElement>("office:binary-data"));
1579 
1580 		if (propList["librevenge:mime-type"]->getStr() == "object/ole")
1581 			mpCurrentStorage->push_back(std::make_shared<TagCloseElement>("draw:object-ole"));
1582 		else
1583 			mpCurrentStorage->push_back(std::make_shared<TagCloseElement>("draw:image"));
1584 	}
1585 
1586 	librevenge::RVNGPropertyListVector const *replacements=propList.child("librevenge:replacement-objects");
1587 	if (!replacements)
1588 		return;
1589 	for (unsigned long c=0; c < replacements->count(); ++c)
1590 		insertBinaryObject((*replacements)[c]);
1591 }
1592 
1593 ////////////////////////////////////////////////////////////
1594 // graphic
1595 ////////////////////////////////////////////////////////////
defineGraphicStyle(const librevenge::RVNGPropertyList & propList)1596 void OdfGenerator::defineGraphicStyle(const librevenge::RVNGPropertyList &propList)
1597 {
1598 	mGraphicStyle=propList;
1599 	if (propList["style:display-name"]) // force the style to be defined
1600 		getCurrentGraphicStyleName();
1601 }
1602 
getCurrentGraphicStyleName()1603 librevenge::RVNGString OdfGenerator::getCurrentGraphicStyleName()
1604 {
1605 	librevenge::RVNGPropertyList styleList;
1606 	mGraphicManager.addGraphicProperties(mGraphicStyle,styleList);
1607 	if (mGraphicStyle["style:display-name"])
1608 		GraphicStyleManager::addFrameProperties(mGraphicStyle,styleList);
1609 	return mGraphicManager.findOrAdd(styleList, useStyleAutomaticZone() ? Style::Z_StyleAutomatic : Style::Z_ContentAutomatic);
1610 }
1611 
getCurrentGraphicStyleName(const librevenge::RVNGPropertyList & shapeList)1612 librevenge::RVNGString OdfGenerator::getCurrentGraphicStyleName(const librevenge::RVNGPropertyList &shapeList)
1613 {
1614 	librevenge::RVNGPropertyList styleList;
1615 	mGraphicManager.addGraphicProperties(shapeList,styleList);
1616 	// check that no default are set, there will can be set with mGraphicStyle, if needed
1617 	for (int i=0; i<3; ++i)
1618 	{
1619 		char const *wh[]= {"draw:fill","draw:shadow","draw:stroke"};
1620 		if (!shapeList[wh[i]] && styleList[wh[i]])
1621 			styleList.remove(wh[i]);
1622 	}
1623 	mGraphicManager.addGraphicProperties(mGraphicStyle,styleList);
1624 	return mGraphicManager.findOrAdd(styleList, useStyleAutomaticZone() ? Style::Z_StyleAutomatic : Style::Z_ContentAutomatic);
1625 }
1626 
drawEllipse(const librevenge::RVNGPropertyList & propList)1627 void OdfGenerator::drawEllipse(const librevenge::RVNGPropertyList &propList)
1628 {
1629 	if (!propList["svg:rx"] || !propList["svg:ry"] || !propList["svg:cx"] || !propList["svg:cy"])
1630 	{
1631 		ODFGEN_DEBUG_MSG(("OdfGenerator::drawEllipse: position undefined\n"));
1632 		return;
1633 	}
1634 	double rx=0, ry=0, cx=0, cy=0;
1635 	if (!getInchValue(*propList["svg:rx"], rx) || !getInchValue(*propList["svg:ry"], ry) ||
1636 	        !getInchValue(*propList["svg:cx"], cx) || !getInchValue(*propList["svg:cy"], cy))
1637 	{
1638 		ODFGEN_DEBUG_MSG(("OdfGenerator::drawEllipse: can not read position\n"));
1639 		return;
1640 	}
1641 	librevenge::RVNGString sValue=getCurrentGraphicStyleName(propList);
1642 	auto pDrawEllipseElement = std::make_shared<TagOpenElement>("draw:ellipse");
1643 	pDrawEllipseElement->addAttribute("draw:style-name", sValue);
1644 	addFrameProperties(propList, *pDrawEllipseElement);
1645 	sValue = doubleToString(2 * rx);
1646 	sValue.append("in");
1647 	pDrawEllipseElement->addAttribute("svg:width", sValue);
1648 	sValue = doubleToString(2 * ry);
1649 	sValue.append("in");
1650 	pDrawEllipseElement->addAttribute("svg:height", sValue);
1651 	for (int i=0; i<4; ++i)
1652 	{
1653 		char const *wh[]= {"draw:kind", "draw:start-angle", "draw:end-angle", "draw:transform"};
1654 		if (propList[wh[i]])
1655 			pDrawEllipseElement->addAttribute(wh[i], propList[wh[i]]->getStr());
1656 	}
1657 	if (propList["librevenge:rotate"] &&
1658 	        (propList["librevenge:rotate"]->getDouble() < 0 || propList["librevenge:rotate"]->getDouble() > 0))
1659 	{
1660 		double rotation = propList["librevenge:rotate"]->getDouble();
1661 		while (rotation < -180)
1662 			rotation += 360;
1663 		while (rotation > 180)
1664 			rotation -= 360;
1665 		double radrotation = rotation*M_PI/180.0;
1666 		double deltax = sqrt(pow(rx, 2.0) + pow(ry, 2.0))*cos(atan(ry/rx)-radrotation) - rx;
1667 		double deltay = sqrt(pow(rx, 2.0) + pow(ry, 2.0))*sin(atan(ry/rx)- radrotation) - ry;
1668 		sValue = "rotate(";
1669 		sValue.append(doubleToString(radrotation));
1670 		sValue.append(") ");
1671 		sValue.append("translate(");
1672 		sValue.append(doubleToString(cx - rx - deltax));
1673 		sValue.append("in, ");
1674 		sValue.append(doubleToString(cy - ry - deltay));
1675 		sValue.append("in)");
1676 		pDrawEllipseElement->addAttribute("draw:transform", sValue);
1677 	}
1678 	else
1679 	{
1680 		sValue = doubleToString(cx-rx);
1681 		sValue.append("in");
1682 		pDrawEllipseElement->addAttribute("svg:x", sValue);
1683 		sValue = doubleToString(cy-ry);
1684 		sValue.append("in");
1685 		pDrawEllipseElement->addAttribute("svg:y", sValue);
1686 	}
1687 
1688 	if (propList["draw:display"])
1689 		pDrawEllipseElement->addAttribute("draw:display", propList["draw:display"]->getStr());
1690 
1691 	mpCurrentStorage->push_back(pDrawEllipseElement);
1692 	mpCurrentStorage->push_back(std::make_shared<TagCloseElement>("draw:ellipse"));
1693 }
1694 
drawPath(const librevenge::RVNGPropertyList & propList)1695 void OdfGenerator::drawPath(const librevenge::RVNGPropertyList &propList)
1696 {
1697 	const librevenge::RVNGPropertyListVector *path = propList.child("svg:d");
1698 	if (!path) return;
1699 	drawPath(*path, propList);
1700 }
1701 
drawPath(const librevenge::RVNGPropertyListVector & path,const librevenge::RVNGPropertyList & propList)1702 void OdfGenerator::drawPath(const librevenge::RVNGPropertyListVector &path, const librevenge::RVNGPropertyList &propList)
1703 {
1704 	if (!path.count())
1705 		return;
1706 
1707 	double px = 0.0, py = 0.0, qx = 0.0, qy = 0.0;
1708 	if (!libodfgen::getPathBBox(path, px, py, qx, qy))
1709 		return;
1710 
1711 	librevenge::RVNGString sValue=getCurrentGraphicStyleName(propList);
1712 	auto pDrawPathElement = std::make_shared<TagOpenElement>("draw:path");
1713 	pDrawPathElement->addAttribute("draw:style-name", sValue);
1714 	addFrameProperties(propList, *pDrawPathElement);
1715 	sValue = doubleToString(px);
1716 	sValue.append("in");
1717 	pDrawPathElement->addAttribute("svg:x", sValue);
1718 	sValue = doubleToString(py);
1719 	sValue.append("in");
1720 	pDrawPathElement->addAttribute("svg:y", sValue);
1721 	sValue = doubleToString((qx - px));
1722 	sValue.append("in");
1723 	pDrawPathElement->addAttribute("svg:width", sValue);
1724 	sValue = doubleToString((qy - py));
1725 	sValue.append("in");
1726 	pDrawPathElement->addAttribute("svg:height", sValue);
1727 	sValue.sprintf("%i %i %i %i", 0, 0, (unsigned)(2540*(qx - px)), (unsigned)(2540*(qy - py)));
1728 	pDrawPathElement->addAttribute("svg:viewBox", sValue);
1729 
1730 	if (propList["draw:display"])
1731 		pDrawPathElement->addAttribute("draw:display", propList["draw:display"]->getStr());
1732 
1733 	pDrawPathElement->addAttribute("svg:d", libodfgen::convertPath(path, px, py));
1734 	mpCurrentStorage->push_back(pDrawPathElement);
1735 	mpCurrentStorage->push_back(std::make_shared<TagCloseElement>("draw:path"));
1736 }
1737 
drawPolySomething(const librevenge::RVNGPropertyList & propList,bool isClosed)1738 void OdfGenerator::drawPolySomething(const librevenge::RVNGPropertyList &propList, bool isClosed)
1739 {
1740 	const librevenge::RVNGPropertyListVector *vertices = propList.child("svg:points");
1741 	if (!vertices || vertices->count() < 2)
1742 		return;
1743 
1744 	if (vertices->count() == 2)
1745 	{
1746 		if (!(*vertices)[0]["svg:x"]||!(*vertices)[0]["svg:y"]||!(*vertices)[1]["svg:x"]||!(*vertices)[1]["svg:y"])
1747 		{
1748 			ODFGEN_DEBUG_MSG(("OdfGenerator::drawPolySomething: some vertices are not defined\n"));
1749 			return;
1750 		}
1751 		librevenge::RVNGString sValue=getCurrentGraphicStyleName(propList);
1752 		bool isMeasure=(propList["draw:show-unit"] && propList["draw:show-unit"]->getStr()=="true");
1753 		librevenge::RVNGString what= isMeasure ? "draw:measure" : "draw:line";
1754 
1755 		auto pDrawLineElement = std::make_shared<TagOpenElement>(what);
1756 		addFrameProperties(propList, *pDrawLineElement);
1757 		pDrawLineElement->addAttribute("draw:style-name", sValue);
1758 		pDrawLineElement->addAttribute("svg:x1", (*vertices)[0]["svg:x"]->getStr());
1759 		pDrawLineElement->addAttribute("svg:y1", (*vertices)[0]["svg:y"]->getStr());
1760 		pDrawLineElement->addAttribute("svg:x2", (*vertices)[1]["svg:x"]->getStr());
1761 		pDrawLineElement->addAttribute("svg:y2", (*vertices)[1]["svg:y"]->getStr());
1762 		if (propList["draw:display"])
1763 			pDrawLineElement->addAttribute("draw:display", propList["draw:display"]->getStr());
1764 		mpCurrentStorage->push_back(pDrawLineElement);
1765 		mpCurrentStorage->push_back(std::make_shared<TagCloseElement>(what));
1766 	}
1767 	else
1768 	{
1769 		librevenge::RVNGPropertyListVector path;
1770 		librevenge::RVNGPropertyList element;
1771 
1772 		for (unsigned long ii = 0; ii < vertices->count(); ++ii)
1773 		{
1774 			element = (*vertices)[ii];
1775 			if (ii == 0)
1776 				element.insert("librevenge:path-action", "M");
1777 			else
1778 				element.insert("librevenge:path-action", "L");
1779 			path.append(element);
1780 			element.clear();
1781 		}
1782 		if (isClosed)
1783 		{
1784 			element.insert("librevenge:path-action", "Z");
1785 			path.append(element);
1786 		}
1787 		drawPath(path,propList);
1788 	}
1789 }
1790 
drawRectangle(const librevenge::RVNGPropertyList & propList)1791 void OdfGenerator::drawRectangle(const librevenge::RVNGPropertyList &propList)
1792 {
1793 	if (!propList["svg:x"] || !propList["svg:y"] ||
1794 	        !propList["svg:width"] || !propList["svg:height"])
1795 	{
1796 		ODFGEN_DEBUG_MSG(("OdfGenerator::drawRectangle: position undefined\n"));
1797 		return;
1798 	}
1799 	librevenge::RVNGString sValue=getCurrentGraphicStyleName(propList);
1800 	librevenge::RVNGPropertyList frame(propList);
1801 	frame.remove("svg:height");
1802 	frame.remove("svg:width");
1803 	auto pDrawRectElement = std::make_shared<TagOpenElement>("draw:rect");
1804 	addFrameProperties(frame, *pDrawRectElement);
1805 	pDrawRectElement->addAttribute("draw:style-name", sValue);
1806 	pDrawRectElement->addAttribute("svg:x", propList["svg:x"]->getStr());
1807 	pDrawRectElement->addAttribute("svg:y", propList["svg:y"]->getStr());
1808 	pDrawRectElement->addAttribute("svg:width", propList["svg:width"]->getStr());
1809 	pDrawRectElement->addAttribute("svg:height", propList["svg:height"]->getStr());
1810 	// FIXME: what to do when rx != ry ?
1811 	if (propList["svg:rx"])
1812 		pDrawRectElement->addAttribute("draw:corner-radius", propList["svg:rx"]->getStr());
1813 	else
1814 		pDrawRectElement->addAttribute("draw:corner-radius", "0.0000in");
1815 	if (propList["draw:transform"])
1816 		pDrawRectElement->addAttribute("draw:transform", propList["draw:transform"]->getStr());
1817 	if (propList["draw:display"])
1818 		pDrawRectElement->addAttribute("draw:display", propList["draw:display"]->getStr());
1819 
1820 	mpCurrentStorage->push_back(pDrawRectElement);
1821 	mpCurrentStorage->push_back(std::make_shared<TagCloseElement>("draw:rect"));
1822 }
1823 
drawConnector(const librevenge::RVNGPropertyList & propList)1824 void OdfGenerator::drawConnector(const librevenge::RVNGPropertyList &propList)
1825 {
1826 	const librevenge::RVNGPropertyListVector *path = propList.child("svg:d");
1827 	if (!path) return;
1828 	drawPath(*path, propList);
1829 }
1830 
1831 ////////////////////////////////////////////////////////////
1832 // chart
1833 ////////////////////////////////////////////////////////////
defineChartStyle(const librevenge::RVNGPropertyList & propList)1834 void OdfGenerator::defineChartStyle(const librevenge::RVNGPropertyList &propList)
1835 {
1836 	int chartId=-1;
1837 	if (propList["librevenge:chart-id"])
1838 		chartId=propList["librevenge:chart-id"]->getInt();
1839 	else
1840 	{
1841 		ODFGEN_DEBUG_MSG(("OdfGenerator::defineChartStyle: called without id\n"));
1842 	}
1843 	mIdChartMap[chartId]=propList;
1844 	mIdChartNameMap.erase(chartId);
1845 }
1846 
1847 ////////////////////////////////////////////////////////////
1848 // font
1849 ////////////////////////////////////////////////////////////
1850 
defineEmbeddedFont(const librevenge::RVNGPropertyList & propList)1851 void OdfGenerator::defineEmbeddedFont(const librevenge::RVNGPropertyList &propList)
1852 {
1853 	// AbiWord rejects such ODF files (rightfully, as office:binary-data is not allowed in
1854 	// svg:font-face-uri). So only do this for Flat ODF, until we have a way to insert binary
1855 	// files into the output archive (which is not possible without an API change).
1856 	if (!((mDocumentStreamHandlers.size() == 1) && (mDocumentStreamHandlers.begin()->first == ODF_FLAT_XML)))
1857 	{
1858 		ODFGEN_DEBUG_MSG(("OdfGenerator::defineEmbeddedFont: embedded fonts are only handled for Flat ODF currently\n"));
1859 		return;
1860 	}
1861 	if (!propList["office:binary-data"] || !propList["librevenge:mime-type"] || !propList["librevenge:name"])
1862 	{
1863 		ODFGEN_DEBUG_MSG(("OdfGenerator::defineEmbeddedFont: can not find data, mimetype, or name\n"));
1864 		return;
1865 	}
1866 
1867 	try
1868 	{
1869 		const librevenge::RVNGString name(propList["librevenge:name"]->getStr());
1870 		const librevenge::RVNGString mimeType(propList["librevenge:mime-type"]->getStr());
1871 		const librevenge::RVNGBinaryData data(propList["office:binary-data"]->getStr());
1872 
1873 		OdfEmbeddedImage imageHandler = findEmbeddedImageHandler(mimeType);
1874 		if (imageHandler)
1875 		{
1876 			librevenge::RVNGBinaryData output;
1877 			if (imageHandler(data, output))
1878 				mFontManager.setEmbedded(name, "application/x-font-ttf", output);
1879 		}
1880 		else
1881 		{
1882 			mFontManager.setEmbedded(name, mimeType, data);
1883 		}
1884 	}
1885 	catch (...)
1886 	{
1887 		ODFGEN_DEBUG_MSG(("OdfGenerator::defineEmbeddedFont: ARGHH, catch an exception when decoding font!!!\n"));
1888 	}
1889 }
1890 
1891 static const unsigned char librvng_utf8_skip_data[256] =
1892 {
1893 	1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1894 	1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1895 	1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1896 	1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1897 	1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1898 	1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1899 	2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
1900 	3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,6,6,1,1
1901 };
1902 
1903 #define librvng_utf8_next_char(p) ((p) + librvng_utf8_skip_data[*reinterpret_cast<unsigned char const *>(p)])
1904 
unescapeXML(const char * s,const unsigned long sz,librevenge::RVNGString & res)1905 void OdfGenerator::unescapeXML(const char *s, const unsigned long sz, librevenge::RVNGString &res)
1906 {
1907 	if (mXmlToUnicodeMap.empty())
1908 	{
1909 		mXmlToUnicodeMap=
1910 		{
1911 			/*{"Tab",0x00009}, {"NewLine",0x0000A},*/ {"excl",0x00021}, {"quot",0x00022}, {"QUOT",0x00022},
1912 			{"num",0x00023}, {"dollar",0x00024}, {"percnt",0x00025}, {"amp",0x00026}, {"AMP",0x00026},
1913 			{"apos",0x00027}, {"lpar",0x00028}, {"rpar",0x00029}, {"ast",0x0002A}, {"midast",0x0002A},
1914 			{"plus",0x0002B}, {"comma",0x0002C}, {"period",0x0002E}, {"sol",0x0002F}, {"colon",0x0003A},
1915 			{"semi",0x0003B}, {"lt",0x0003C}, {"LT",0x0003C}, {"equals",0x0003D}, {"gt",0x0003E},
1916 			{"GT",0x0003E}, {"quest",0x0003F}, {"commat",0x00040}, {"lsqb",0x0005B}, {"lbrack",0x0005B},
1917 			{"bsol",0x0005C}, {"rsqb",0x0005D}, {"rbrack",0x0005D}, {"Hat",0x0005E}, {"lowbar",0x0005F},
1918 			{"grave",0x00060}, {"DiacriticalGrave",0x00060}, {"lcub",0x0007B}, {"lbrace",0x0007B}, {"verbar",0x0007C},
1919 			{"vert",0x0007C}, {"VerticalLine",0x0007C}, {"rcub",0x0007D}, {"rbrace",0x0007D}, {"nbsp",0x000A0},
1920 			{"NonBreakingSpace",0x000A0}, {"iexcl",0x000A1}, {"cent",0x000A2}, {"pound",0x000A3}, {"curren",0x000A4},
1921 			{"yen",0x000A5}, {"brvbar",0x000A6}, {"sect",0x000A7}, {"Dot",0x000A8}, {"die",0x000A8},
1922 			{"DoubleDot",0x000A8}, {"uml",0x000A8}, {"copy",0x000A9}, {"COPY",0x000A9}, {"ordf",0x000AA},
1923 			{"laquo",0x000AB}, {"not",0x000AC}, {"shy",0x000AD}, {"reg",0x000AE}, {"circledR",0x000AE},
1924 			{"REG",0x000AE}, {"macr",0x000AF}, {"OverBar",0x000AF}, {"strns",0x000AF}, {"deg",0x000B0},
1925 			{"plusmn",0x000B1}, {"pm",0x000B1}, {"PlusMinus",0x000B1}, {"sup2",0x000B2}, {"sup3",0x000B3},
1926 			{"acute",0x000B4}, {"DiacriticalAcute",0x000B4}, {"micro",0x000B5}, {"para",0x000B6}, {"middot",0x000B7},
1927 			{"centerdot",0x000B7}, {"CenterDot",0x000B7}, {"cedil",0x000B8}, {"Cedilla",0x000B8}, {"sup1",0x000B9},
1928 			{"ordm",0x000BA}, {"raquo",0x000BB}, {"frac14",0x000BC}, {"frac12",0x000BD}, {"half",0x000BD},
1929 			{"frac34",0x000BE}, {"iquest",0x000BF}, {"Agrave",0x000C0}, {"Aacute",0x000C1}, {"Acirc",0x000C2},
1930 			{"Atilde",0x000C3}, {"Auml",0x000C4}, {"Aring",0x000C5}, {"AElig",0x000C6}, {"Ccedil",0x000C7},
1931 			{"Egrave",0x000C8}, {"Eacute",0x000C9}, {"Ecirc",0x000CA}, {"Euml",0x000CB}, {"Igrave",0x000CC},
1932 			{"Iacute",0x000CD}, {"Icirc",0x000CE}, {"Iuml",0x000CF}, {"ETH",0x000D0}, {"Ntilde",0x000D1},
1933 			{"Ograve",0x000D2}, {"Oacute",0x000D3}, {"Ocirc",0x000D4}, {"Otilde",0x000D5}, {"Ouml",0x000D6},
1934 			{"times",0x000D7}, {"Oslash",0x000D8}, {"Ugrave",0x000D9}, {"Uacute",0x000DA}, {"Ucirc",0x000DB},
1935 			{"Uuml",0x000DC}, {"Yacute",0x000DD}, {"THORN",0x000DE}, {"szlig",0x000DF}, {"agrave",0x000E0},
1936 			{"aacute",0x000E1}, {"acirc",0x000E2}, {"atilde",0x000E3}, {"auml",0x000E4}, {"aring",0x000E5},
1937 			{"aelig",0x000E6}, {"ccedil",0x000E7}, {"egrave",0x000E8}, {"eacute",0x000E9}, {"ecirc",0x000EA},
1938 			{"euml",0x000EB}, {"igrave",0x000EC}, {"iacute",0x000ED}, {"icirc",0x000EE}, {"iuml",0x000EF},
1939 			{"eth",0x000F0}, {"ntilde",0x000F1}, {"ograve",0x000F2}, {"oacute",0x000F3}, {"ocirc",0x000F4},
1940 			{"otilde",0x000F5}, {"ouml",0x000F6}, {"divide",0x000F7}, {"div",0x000F7}, {"oslash",0x000F8},
1941 			{"ugrave",0x000F9}, {"uacute",0x000FA}, {"ucirc",0x000FB}, {"uuml",0x000FC}, {"yacute",0x000FD},
1942 			{"thorn",0x000FE}, {"yuml",0x000FF}, {"Amacr",0x00100}, {"amacr",0x00101}, {"Abreve",0x00102},
1943 			{"abreve",0x00103}, {"Aogon",0x00104}, {"aogon",0x00105}, {"Cacute",0x00106}, {"cacute",0x00107},
1944 			{"Ccirc",0x00108}, {"ccirc",0x00109}, {"Cdot",0x0010A}, {"cdot",0x0010B}, {"Ccaron",0x0010C},
1945 			{"ccaron",0x0010D}, {"Dcaron",0x0010E}, {"dcaron",0x0010F}, {"Dstrok",0x00110}, {"dstrok",0x00111},
1946 			{"Emacr",0x00112}, {"emacr",0x00113}, {"Edot",0x00116}, {"edot",0x00117}, {"Eogon",0x00118},
1947 			{"eogon",0x00119}, {"Ecaron",0x0011A}, {"ecaron",0x0011B}, {"Gcirc",0x0011C}, {"gcirc",0x0011D},
1948 			{"Gbreve",0x0011E}, {"gbreve",0x0011F}, {"Gdot",0x00120}, {"gdot",0x00121}, {"Gcedil",0x00122},
1949 			{"Hcirc",0x00124}, {"hcirc",0x00125}, {"Hstrok",0x00126}, {"hstrok",0x00127}, {"Itilde",0x00128},
1950 			{"itilde",0x00129}, {"Imacr",0x0012A}, {"imacr",0x0012B}, {"Iogon",0x0012E}, {"iogon",0x0012F},
1951 			{"Idot",0x00130}, {"imath",0x00131}, {"inodot",0x00131}, {"IJlig",0x00132}, {"ijlig",0x00133},
1952 			{"Jcirc",0x00134}, {"jcirc",0x00135}, {"Kcedil",0x00136}, {"kcedil",0x00137}, {"kgreen",0x00138},
1953 			{"Lacute",0x00139}, {"lacute",0x0013A}, {"Lcedil",0x0013B}, {"lcedil",0x0013C}, {"Lcaron",0x0013D},
1954 			{"lcaron",0x0013E}, {"Lmidot",0x0013F}, {"lmidot",0x00140}, {"Lstrok",0x00141}, {"lstrok",0x00142},
1955 			{"Nacute",0x00143}, {"nacute",0x00144}, {"Ncedil",0x00145}, {"ncedil",0x00146}, {"Ncaron",0x00147},
1956 			{"ncaron",0x00148}, {"napos",0x00149}, {"ENG",0x0014A}, {"eng",0x0014B}, {"Omacr",0x0014C},
1957 			{"omacr",0x0014D}, {"Odblac",0x00150}, {"odblac",0x00151}, {"OElig",0x00152}, {"oelig",0x00153},
1958 			{"Racute",0x00154}, {"racute",0x00155}, {"Rcedil",0x00156}, {"rcedil",0x00157}, {"Rcaron",0x00158},
1959 			{"rcaron",0x00159}, {"Sacute",0x0015A}, {"sacute",0x0015B}, {"Scirc",0x0015C}, {"scirc",0x0015D},
1960 			{"Scedil",0x0015E}, {"scedil",0x0015F}, {"Scaron",0x00160}, {"scaron",0x00161}, {"Tcedil",0x00162},
1961 			{"tcedil",0x00163}, {"Tcaron",0x00164}, {"tcaron",0x00165}, {"Tstrok",0x00166}, {"tstrok",0x00167},
1962 			{"Utilde",0x00168}, {"utilde",0x00169}, {"Umacr",0x0016A}, {"umacr",0x0016B}, {"Ubreve",0x0016C},
1963 			{"ubreve",0x0016D}, {"Uring",0x0016E}, {"uring",0x0016F}, {"Udblac",0x00170}, {"udblac",0x00171},
1964 			{"Uogon",0x00172}, {"uogon",0x00173}, {"Wcirc",0x00174}, {"wcirc",0x00175}, {"Ycirc",0x00176},
1965 			{"ycirc",0x00177}, {"Yuml",0x00178}, {"Zacute",0x00179}, {"zacute",0x0017A}, {"Zdot",0x0017B},
1966 			{"zdot",0x0017C}, {"Zcaron",0x0017D}, {"zcaron",0x0017E}, {"fnof",0x00192}, {"imped",0x001B5},
1967 			{"gacute",0x001F5}, {"jmath",0x00237}, {"circ",0x002C6}, {"caron",0x002C7}, {"Hacek",0x002C7},
1968 			{"breve",0x002D8}, {"Breve",0x002D8}, {"dot",0x002D9}, {"DiacriticalDot",0x002D9}, {"ring",0x002DA},
1969 			{"ogon",0x002DB}, {"tilde",0x002DC}, {"DiacriticalTilde",0x002DC}, {"dblac",0x002DD}, {"DiacriticalDoubleAcute",0x002DD},
1970 			{"DownBreve",0x00311}, {"UnderBar",0x00332}, {"Alpha",0x00391}, {"Beta",0x00392}, {"Gamma",0x00393},
1971 			{"Delta",0x00394}, {"Epsilon",0x00395}, {"Zeta",0x00396}, {"Eta",0x00397}, {"Theta",0x00398},
1972 			{"Iota",0x00399}, {"Kappa",0x0039A}, {"Lambda",0x0039B}, {"Mu",0x0039C}, {"Nu",0x0039D},
1973 			{"Xi",0x0039E}, {"Omicron",0x0039F}, {"Pi",0x003A0}, {"Rho",0x003A1}, {"Sigma",0x003A3},
1974 			{"Tau",0x003A4}, {"Upsilon",0x003A5}, {"Phi",0x003A6}, {"Chi",0x003A7}, {"Psi",0x003A8},
1975 			{"Omega",0x003A9}, {"alpha",0x003B1}, {"beta",0x003B2}, {"gamma",0x003B3}, {"delta",0x003B4},
1976 			{"epsiv",0x003B5}, {"varepsilon",0x003B5}, {"epsilon",0x003B5}, {"zeta",0x003B6}, {"eta",0x003B7},
1977 			{"theta",0x003B8}, {"iota",0x003B9}, {"kappa",0x003BA}, {"lambda",0x003BB}, {"mu",0x003BC},
1978 			{"nu",0x003BD}, {"xi",0x003BE}, {"omicron",0x003BF}, {"pi",0x003C0}, {"rho",0x003C1},
1979 			{"sigmav",0x003C2}, {"varsigma",0x003C2}, {"sigmaf",0x003C2}, {"sigma",0x003C3}, {"tau",0x003C4},
1980 			{"upsi",0x003C5}, {"upsilon",0x003C5}, {"phi",0x003C6}, {"phiv",0x003C6}, {"varphi",0x003C6},
1981 			{"chi",0x003C7}, {"psi",0x003C8}, {"omega",0x003C9}, {"thetav",0x003D1}, {"vartheta",0x003D1},
1982 			{"thetasym",0x003D1}, {"Upsi",0x003D2}, {"upsih",0x003D2}, {"straightphi",0x003D5}, {"piv",0x003D6},
1983 			{"varpi",0x003D6}, {"Gammad",0x003DC}, {"gammad",0x003DD}, {"digamma",0x003DD}, {"kappav",0x003F0},
1984 			{"varkappa",0x003F0}, {"rhov",0x003F1}, {"varrho",0x003F1}, {"epsi",0x003F5}, {"straightepsilon",0x003F5},
1985 			{"bepsi",0x003F6}, {"backepsilon",0x003F6}, {"IOcy",0x00401}, {"DJcy",0x00402}, {"GJcy",0x00403},
1986 			{"Jukcy",0x00404}, {"DScy",0x00405}, {"Iukcy",0x00406}, {"YIcy",0x00407}, {"Jsercy",0x00408},
1987 			{"LJcy",0x00409}, {"NJcy",0x0040A}, {"TSHcy",0x0040B}, {"KJcy",0x0040C}, {"Ubrcy",0x0040E},
1988 			{"DZcy",0x0040F}, {"Acy",0x00410}, {"Bcy",0x00411}, {"Vcy",0x00412}, {"Gcy",0x00413},
1989 			{"Dcy",0x00414}, {"IEcy",0x00415}, {"ZHcy",0x00416}, {"Zcy",0x00417}, {"Icy",0x00418},
1990 			{"Jcy",0x00419}, {"Kcy",0x0041A}, {"Lcy",0x0041B}, {"Mcy",0x0041C}, {"Ncy",0x0041D},
1991 			{"Ocy",0x0041E}, {"Pcy",0x0041F}, {"Rcy",0x00420}, {"Scy",0x00421}, {"Tcy",0x00422},
1992 			{"Ucy",0x00423}, {"Fcy",0x00424}, {"KHcy",0x00425}, {"TScy",0x00426}, {"CHcy",0x00427},
1993 			{"SHcy",0x00428}, {"SHCHcy",0x00429}, {"HARDcy",0x0042A}, {"Ycy",0x0042B}, {"SOFTcy",0x0042C},
1994 			{"Ecy",0x0042D}, {"YUcy",0x0042E}, {"YAcy",0x0042F}, {"acy",0x00430}, {"bcy",0x00431},
1995 			{"vcy",0x00432}, {"gcy",0x00433}, {"dcy",0x00434}, {"iecy",0x00435}, {"zhcy",0x00436},
1996 			{"zcy",0x00437}, {"icy",0x00438}, {"jcy",0x00439}, {"kcy",0x0043A}, {"lcy",0x0043B},
1997 			{"mcy",0x0043C}, {"ncy",0x0043D}, {"ocy",0x0043E}, {"pcy",0x0043F}, {"rcy",0x00440},
1998 			{"scy",0x00441}, {"tcy",0x00442}, {"ucy",0x00443}, {"fcy",0x00444}, {"khcy",0x00445},
1999 			{"tscy",0x00446}, {"chcy",0x00447}, {"shcy",0x00448}, {"shchcy",0x00449}, {"hardcy",0x0044A},
2000 			{"ycy",0x0044B}, {"softcy",0x0044C}, {"ecy",0x0044D}, {"yucy",0x0044E}, {"yacy",0x0044F},
2001 			{"iocy",0x00451}, {"djcy",0x00452}, {"gjcy",0x00453}, {"jukcy",0x00454}, {"dscy",0x00455},
2002 			{"iukcy",0x00456}, {"yicy",0x00457}, {"jsercy",0x00458}, {"ljcy",0x00459}, {"njcy",0x0045A},
2003 			{"tshcy",0x0045B}, {"kjcy",0x0045C}, {"ubrcy",0x0045E}, {"dzcy",0x0045F}, {"ensp",0x02002},
2004 			{"emsp",0x02003}, {"emsp13",0x02004}, {"emsp14",0x02005}, {"numsp",0x02007}, {"puncsp",0x02008},
2005 			{"thinsp",0x02009}, {"ThinSpace",0x02009}, {"hairsp",0x0200A}, {"VeryThinSpace",0x0200A}, {"ZeroWidthSpace",0x0200B},
2006 			{"NegativeVeryThinSpace",0x0200B}, {"NegativeThinSpace",0x0200B}, {"NegativeMediumSpace",0x0200B}, {"NegativeThickSpace",0x0200B}, {"zwnj",0x0200C},
2007 			{"zwj",0x0200D}, {"lrm",0x0200E}, {"rlm",0x0200F}, {"hyphen",0x02010}, {"dash",0x02010},
2008 			{"ndash",0x02013}, {"mdash",0x02014}, {"horbar",0x02015}, {"Verbar",0x02016}, {"Vert",0x02016},
2009 			{"lsquo",0x02018}, {"OpenCurlyQuote",0x02018}, {"rsquo",0x02019}, {"rsquor",0x02019}, {"CloseCurlyQuote",0x02019},
2010 			{"lsquor",0x0201A}, {"sbquo",0x0201A}, {"ldquo",0x0201C}, {"OpenCurlyDoubleQuote",0x0201C}, {"rdquo",0x0201D},
2011 			{"rdquor",0x0201D}, {"CloseCurlyDoubleQuote",0x0201D}, {"ldquor",0x0201E}, {"bdquo",0x0201E}, {"dagger",0x02020},
2012 			{"Dagger",0x02021}, {"ddagger",0x02021}, {"bull",0x02022}, {"bullet",0x02022}, {"nldr",0x02025},
2013 			{"hellip",0x02026}, {"mldr",0x02026}, {"permil",0x02030}, {"pertenk",0x02031}, {"prime",0x02032},
2014 			{"Prime",0x02033}, {"tprime",0x02034}, {"bprime",0x02035}, {"backprime",0x02035}, {"lsaquo",0x02039},
2015 			{"rsaquo",0x0203A}, {"oline",0x0203E}, {"caret",0x02041}, {"hybull",0x02043}, {"frasl",0x02044},
2016 			{"bsemi",0x0204F}, {"qprime",0x02057}, {"MediumSpace",0x0205F}, {"NoBreak",0x02060}, {"ApplyFunction",0x02061},
2017 			{"af",0x02061}, {"InvisibleTimes",0x02062}, {"it",0x02062}, {"InvisibleComma",0x02063}, {"ic",0x02063},
2018 			{"euro",0x020AC}, {"tdot",0x020DB}, {"TripleDot",0x020DB}, {"DotDot",0x020DC}, {"Copf",0x02102},
2019 			{"complexes",0x02102}, {"incare",0x02105}, {"gscr",0x0210A}, {"hamilt",0x0210B}, {"HilbertSpace",0x0210B},
2020 			{"Hscr",0x0210B}, {"Hfr",0x0210C}, {"Poincareplane",0x0210C}, {"quaternions",0x0210D}, {"Hopf",0x0210D},
2021 			{"planckh",0x0210E}, {"planck",0x0210F}, {"hbar",0x0210F}, {"plankv",0x0210F}, {"hslash",0x0210F},
2022 			{"Iscr",0x02110}, {"imagline",0x02110}, {"image",0x02111}, {"Im",0x02111}, {"imagpart",0x02111},
2023 			{"Ifr",0x02111}, {"Lscr",0x02112}, {"lagran",0x02112}, {"Laplacetrf",0x02112}, {"ell",0x02113},
2024 			{"Nopf",0x02115}, {"naturals",0x02115}, {"numero",0x02116}, {"copysr",0x02117}, {"weierp",0x02118},
2025 			{"wp",0x02118}, {"Popf",0x02119}, {"primes",0x02119}, {"rationals",0x0211A}, {"Qopf",0x0211A},
2026 			{"Rscr",0x0211B}, {"realine",0x0211B}, {"real",0x0211C}, {"Re",0x0211C}, {"realpart",0x0211C},
2027 			{"Rfr",0x0211C}, {"reals",0x0211D}, {"Ropf",0x0211D}, {"rx",0x0211E}, {"trade",0x02122},
2028 			{"TRADE",0x02122}, {"integers",0x02124}, {"Zopf",0x02124}, {"ohm",0x02126}, {"mho",0x02127},
2029 			{"Zfr",0x02128}, {"zeetrf",0x02128}, {"iiota",0x02129}, {"angst",0x0212B}, {"bernou",0x0212C},
2030 			{"Bernoullis",0x0212C}, {"Bscr",0x0212C}, {"Cfr",0x0212D}, {"Cayleys",0x0212D}, {"escr",0x0212F},
2031 			{"Escr",0x02130}, {"expectation",0x02130}, {"Fscr",0x02131}, {"Fouriertrf",0x02131}, {"phmmat",0x02133},
2032 			{"Mellintrf",0x02133}, {"Mscr",0x02133}, {"order",0x02134}, {"orderof",0x02134}, {"oscr",0x02134},
2033 			{"alefsym",0x02135}, {"aleph",0x02135}, {"beth",0x02136}, {"gimel",0x02137}, {"daleth",0x02138},
2034 			{"CapitalDifferentialD",0x02145}, {"DD",0x02145}, {"DifferentialD",0x02146}, {"dd",0x02146}, {"ExponentialE",0x02147},
2035 			{"exponentiale",0x02147}, {"ee",0x02147}, {"ImaginaryI",0x02148}, {"ii",0x02148}, {"frac13",0x02153},
2036 			{"frac23",0x02154}, {"frac15",0x02155}, {"frac25",0x02156}, {"frac35",0x02157}, {"frac45",0x02158},
2037 			{"frac16",0x02159}, {"frac56",0x0215A}, {"frac18",0x0215B}, {"frac38",0x0215C}, {"frac58",0x0215D},
2038 			{"frac78",0x0215E}, {"larr",0x02190}, {"leftarrow",0x02190}, {"LeftArrow",0x02190}, {"slarr",0x02190},
2039 			{"ShortLeftArrow",0x02190}, {"uarr",0x02191}, {"uparrow",0x02191}, {"UpArrow",0x02191}, {"ShortUpArrow",0x02191},
2040 			{"rarr",0x02192}, {"rightarrow",0x02192}, {"RightArrow",0x02192}, {"srarr",0x02192}, {"ShortRightArrow",0x02192},
2041 			{"darr",0x02193}, {"downarrow",0x02193}, {"DownArrow",0x02193}, {"ShortDownArrow",0x02193}, {"harr",0x02194},
2042 			{"leftrightarrow",0x02194}, {"LeftRightArrow",0x02194}, {"varr",0x02195}, {"updownarrow",0x02195}, {"UpDownArrow",0x02195},
2043 			{"nwarr",0x02196}, {"UpperLeftArrow",0x02196}, {"nwarrow",0x02196}, {"nearr",0x02197}, {"UpperRightArrow",0x02197},
2044 			{"nearrow",0x02197}, {"searr",0x02198}, {"searrow",0x02198}, {"LowerRightArrow",0x02198}, {"swarr",0x02199},
2045 			{"swarrow",0x02199}, {"LowerLeftArrow",0x02199}, {"nlarr",0x0219A}, {"nleftarrow",0x0219A}, {"nrarr",0x0219B},
2046 			{"nrightarrow",0x0219B}, {"rarrw",0x0219D}, {"rightsquigarrow",0x0219D}, {"Larr",0x0219E}, {"twoheadleftarrow",0x0219E},
2047 			{"Uarr",0x0219F}, {"Rarr",0x021A0}, {"twoheadrightarrow",0x021A0}, {"Darr",0x021A1}, {"larrtl",0x021A2},
2048 			{"leftarrowtail",0x021A2}, {"rarrtl",0x021A3}, {"rightarrowtail",0x021A3}, {"LeftTeeArrow",0x021A4}, {"mapstoleft",0x021A4},
2049 			{"UpTeeArrow",0x021A5}, {"mapstoup",0x021A5}, {"map",0x021A6}, {"RightTeeArrow",0x021A6}, {"mapsto",0x021A6},
2050 			{"DownTeeArrow",0x021A7}, {"mapstodown",0x021A7}, {"larrhk",0x021A9}, {"hookleftarrow",0x021A9}, {"rarrhk",0x021AA},
2051 			{"hookrightarrow",0x021AA}, {"larrlp",0x021AB}, {"looparrowleft",0x021AB}, {"rarrlp",0x021AC}, {"looparrowright",0x021AC},
2052 			{"harrw",0x021AD}, {"leftrightsquigarrow",0x021AD}, {"nharr",0x021AE}, {"nleftrightarrow",0x021AE}, {"lsh",0x021B0},
2053 			{"Lsh",0x021B0}, {"rsh",0x021B1}, {"Rsh",0x021B1}, {"ldsh",0x021B2}, {"rdsh",0x021B3},
2054 			{"crarr",0x021B5}, {"cularr",0x021B6}, {"curvearrowleft",0x021B6}, {"curarr",0x021B7}, {"curvearrowright",0x021B7},
2055 			{"olarr",0x021BA}, {"circlearrowleft",0x021BA}, {"orarr",0x021BB}, {"circlearrowright",0x021BB}, {"lharu",0x021BC},
2056 			{"LeftVector",0x021BC}, {"leftharpoonup",0x021BC}, {"lhard",0x021BD}, {"leftharpoondown",0x021BD}, {"DownLeftVector",0x021BD},
2057 			{"uharr",0x021BE}, {"upharpoonright",0x021BE}, {"RightUpVector",0x021BE}, {"uharl",0x021BF}, {"upharpoonleft",0x021BF},
2058 			{"LeftUpVector",0x021BF}, {"rharu",0x021C0}, {"RightVector",0x021C0}, {"rightharpoonup",0x021C0}, {"rhard",0x021C1},
2059 			{"rightharpoondown",0x021C1}, {"DownRightVector",0x021C1}, {"dharr",0x021C2}, {"RightDownVector",0x021C2}, {"downharpoonright",0x021C2},
2060 			{"dharl",0x021C3}, {"LeftDownVector",0x021C3}, {"downharpoonleft",0x021C3}, {"rlarr",0x021C4}, {"rightleftarrows",0x021C4},
2061 			{"RightArrowLeftArrow",0x021C4}, {"udarr",0x021C5}, {"UpArrowDownArrow",0x021C5}, {"lrarr",0x021C6}, {"leftrightarrows",0x021C6},
2062 			{"LeftArrowRightArrow",0x021C6}, {"llarr",0x021C7}, {"leftleftarrows",0x021C7}, {"uuarr",0x021C8}, {"upuparrows",0x021C8},
2063 			{"rrarr",0x021C9}, {"rightrightarrows",0x021C9}, {"ddarr",0x021CA}, {"downdownarrows",0x021CA}, {"lrhar",0x021CB},
2064 			{"ReverseEquilibrium",0x021CB}, {"leftrightharpoons",0x021CB}, {"rlhar",0x021CC}, {"rightleftharpoons",0x021CC}, {"Equilibrium",0x021CC},
2065 			{"nlArr",0x021CD}, {"nLeftarrow",0x021CD}, {"nhArr",0x021CE}, {"nLeftrightarrow",0x021CE}, {"nrArr",0x021CF},
2066 			{"nRightarrow",0x021CF}, {"lArr",0x021D0}, {"Leftarrow",0x021D0}, {"DoubleLeftArrow",0x021D0}, {"uArr",0x021D1},
2067 			{"Uparrow",0x021D1}, {"DoubleUpArrow",0x021D1}, {"rArr",0x021D2}, {"Rightarrow",0x021D2}, {"Implies",0x021D2},
2068 			{"DoubleRightArrow",0x021D2}, {"dArr",0x021D3}, {"Downarrow",0x021D3}, {"DoubleDownArrow",0x021D3}, {"hArr",0x021D4},
2069 			{"Leftrightarrow",0x021D4}, {"DoubleLeftRightArrow",0x021D4}, {"iff",0x021D4}, {"vArr",0x021D5}, {"Updownarrow",0x021D5},
2070 			{"DoubleUpDownArrow",0x021D5}, {"nwArr",0x021D6}, {"neArr",0x021D7}, {"seArr",0x021D8}, {"swArr",0x021D9},
2071 			{"lAarr",0x021DA}, {"Lleftarrow",0x021DA}, {"rAarr",0x021DB}, {"Rrightarrow",0x021DB}, {"zigrarr",0x021DD},
2072 			{"larrb",0x021E4}, {"LeftArrowBar",0x021E4}, {"rarrb",0x021E5}, {"RightArrowBar",0x021E5}, {"duarr",0x021F5},
2073 			{"DownArrowUpArrow",0x021F5}, {"loarr",0x021FD}, {"roarr",0x021FE}, {"hoarr",0x021FF}, {"forall",0x02200},
2074 			{"ForAll",0x02200}, {"comp",0x02201}, {"complement",0x02201}, {"part",0x02202}, {"PartialD",0x02202},
2075 			{"exist",0x02203}, {"Exists",0x02203}, {"nexist",0x02204}, {"NotExists",0x02204}, {"nexists",0x02204},
2076 			{"empty",0x02205}, {"emptyset",0x02205}, {"emptyv",0x02205}, {"varnothing",0x02205}, {"nabla",0x02207},
2077 			{"Del",0x02207}, {"isin",0x02208}, {"isinv",0x02208}, {"Element",0x02208}, {"in",0x02208},
2078 			{"notin",0x02209}, {"NotElement",0x02209}, {"notinva",0x02209}, {"niv",0x0220B}, {"ReverseElement",0x0220B},
2079 			{"ni",0x0220B}, {"SuchThat",0x0220B}, {"notni",0x0220C}, {"notniva",0x0220C}, {"NotReverseElement",0x0220C},
2080 			{"prod",0x0220F}, {"Product",0x0220F}, {"coprod",0x02210}, {"Coproduct",0x02210}, {"sum",0x02211},
2081 			{"Sum",0x02211}, {"minus",0x02212}, {"mnplus",0x02213}, {"mp",0x02213}, {"MinusPlus",0x02213},
2082 			{"plusdo",0x02214}, {"dotplus",0x02214}, {"setmn",0x02216}, {"setminus",0x02216}, {"Backslash",0x02216},
2083 			{"ssetmn",0x02216}, {"smallsetminus",0x02216}, {"lowast",0x02217}, {"compfn",0x02218}, {"SmallCircle",0x02218},
2084 			{"radic",0x0221A}, {"Sqrt",0x0221A}, {"prop",0x0221D}, {"propto",0x0221D}, {"Proportional",0x0221D},
2085 			{"vprop",0x0221D}, {"varpropto",0x0221D}, {"infin",0x0221E}, {"angrt",0x0221F}, {"ang",0x02220},
2086 			{"angle",0x02220}, {"angmsd",0x02221}, {"measuredangle",0x02221}, {"angsph",0x02222}, {"mid",0x02223},
2087 			{"VerticalBar",0x02223}, {"smid",0x02223}, {"shortmid",0x02223}, {"nmid",0x02224}, {"NotVerticalBar",0x02224},
2088 			{"nsmid",0x02224}, {"nshortmid",0x02224}, {"par",0x02225}, {"parallel",0x02225}, {"DoubleVerticalBar",0x02225},
2089 			{"spar",0x02225}, {"shortparallel",0x02225}, {"npar",0x02226}, {"nparallel",0x02226}, {"NotDoubleVerticalBar",0x02226},
2090 			{"nspar",0x02226}, {"nshortparallel",0x02226}, {"and",0x02227}, {"wedge",0x02227}, {"or",0x02228},
2091 			{"vee",0x02228}, {"cap",0x02229}, {"cup",0x0222A}, {"int",0x0222B}, {"Integral",0x0222B},
2092 			{"Int",0x0222C}, {"tint",0x0222D}, {"iiint",0x0222D}, {"conint",0x0222E}, {"oint",0x0222E},
2093 			{"ContourIntegral",0x0222E}, {"Conint",0x0222F}, {"DoubleContourIntegral",0x0222F}, {"Cconint",0x02230}, {"cwint",0x02231},
2094 			{"cwconint",0x02232}, {"ClockwiseContourIntegral",0x02232}, {"awconint",0x02233}, {"CounterClockwiseContourIntegral",0x02233}, {"there4",0x02234},
2095 			{"therefore",0x02234}, {"Therefore",0x02234}, {"becaus",0x02235}, {"because",0x02235}, {"Because",0x02235},
2096 			{"ratio",0x02236}, {"Colon",0x02237}, {"Proportion",0x02237}, {"minusd",0x02238}, {"dotminus",0x02238},
2097 			{"mDDot",0x0223A}, {"homtht",0x0223B}, {"sim",0x0223C}, {"Tilde",0x0223C}, {"thksim",0x0223C},
2098 			{"thicksim",0x0223C}, {"bsim",0x0223D}, {"backsim",0x0223D}, {"ac",0x0223E}, {"mstpos",0x0223E},
2099 			{"acd",0x0223F}, {"wreath",0x02240}, {"VerticalTilde",0x02240}, {"wr",0x02240}, {"nsim",0x02241},
2100 			{"NotTilde",0x02241}, {"esim",0x02242}, {"EqualTilde",0x02242}, {"eqsim",0x02242}, {"sime",0x02243},
2101 			{"TildeEqual",0x02243}, {"simeq",0x02243}, {"nsime",0x02244}, {"nsimeq",0x02244}, {"NotTildeEqual",0x02244},
2102 			{"cong",0x02245}, {"TildeFullEqual",0x02245}, {"simne",0x02246}, {"ncong",0x02247}, {"NotTildeFullEqual",0x02247},
2103 			{"asymp",0x02248}, {"ap",0x02248}, {"TildeTilde",0x02248}, {"approx",0x02248}, {"thkap",0x02248},
2104 			{"thickapprox",0x02248}, {"nap",0x02249}, {"NotTildeTilde",0x02249}, {"napprox",0x02249}, {"ape",0x0224A},
2105 			{"approxeq",0x0224A}, {"apid",0x0224B}, {"bcong",0x0224C}, {"backcong",0x0224C}, {"asympeq",0x0224D},
2106 			{"CupCap",0x0224D}, {"bump",0x0224E}, {"HumpDownHump",0x0224E}, {"Bumpeq",0x0224E}, {"bumpe",0x0224F},
2107 			{"HumpEqual",0x0224F}, {"bumpeq",0x0224F}, {"esdot",0x02250}, {"DotEqual",0x02250}, {"doteq",0x02250},
2108 			{"eDot",0x02251}, {"doteqdot",0x02251}, {"efDot",0x02252}, {"fallingdotseq",0x02252}, {"erDot",0x02253},
2109 			{"risingdotseq",0x02253}, {"colone",0x02254}, {"coloneq",0x02254}, {"Assign",0x02254}, {"ecolon",0x02255},
2110 			{"eqcolon",0x02255}, {"ecir",0x02256}, {"eqcirc",0x02256}, {"cire",0x02257}, {"circeq",0x02257},
2111 			{"wedgeq",0x02259}, {"veeeq",0x0225A}, {"trie",0x0225C}, {"triangleq",0x0225C}, {"equest",0x0225F},
2112 			{"questeq",0x0225F}, {"ne",0x02260}, {"NotEqual",0x02260}, {"equiv",0x02261}, {"Congruent",0x02261},
2113 			{"nequiv",0x02262}, {"NotCongruent",0x02262}, {"le",0x02264}, {"leq",0x02264}, {"ge",0x02265},
2114 			{"GreaterEqual",0x02265}, {"geq",0x02265}, {"lE",0x02266}, {"LessFullEqual",0x02266}, {"leqq",0x02266},
2115 			{"gE",0x02267}, {"GreaterFullEqual",0x02267}, {"geqq",0x02267}, {"lnE",0x02268}, {"lneqq",0x02268},
2116 			{"gnE",0x02269}, {"gneqq",0x02269}, {"Lt",0x0226A}, {"NestedLessLess",0x0226A}, {"ll",0x0226A},
2117 			{"Gt",0x0226B}, {"NestedGreaterGreater",0x0226B}, {"gg",0x0226B}, {"twixt",0x0226C}, {"between",0x0226C},
2118 			{"NotCupCap",0x0226D}, {"nlt",0x0226E}, {"NotLess",0x0226E}, {"nless",0x0226E}, {"ngt",0x0226F},
2119 			{"NotGreater",0x0226F}, {"ngtr",0x0226F}, {"nle",0x02270}, {"NotLessEqual",0x02270}, {"nleq",0x02270},
2120 			{"nge",0x02271}, {"NotGreaterEqual",0x02271}, {"ngeq",0x02271}, {"lsim",0x02272}, {"LessTilde",0x02272},
2121 			{"lesssim",0x02272}, {"gsim",0x02273}, {"gtrsim",0x02273}, {"GreaterTilde",0x02273}, {"nlsim",0x02274},
2122 			{"NotLessTilde",0x02274}, {"ngsim",0x02275}, {"NotGreaterTilde",0x02275}, {"lg",0x02276}, {"lessgtr",0x02276},
2123 			{"LessGreater",0x02276}, {"gl",0x02277}, {"gtrless",0x02277}, {"GreaterLess",0x02277}, {"ntlg",0x02278},
2124 			{"NotLessGreater",0x02278}, {"ntgl",0x02279}, {"NotGreaterLess",0x02279}, {"pr",0x0227A}, {"Precedes",0x0227A},
2125 			{"prec",0x0227A}, {"sc",0x0227B}, {"Succeeds",0x0227B}, {"succ",0x0227B}, {"prcue",0x0227C},
2126 			{"PrecedesSlantEqual",0x0227C}, {"preccurlyeq",0x0227C}, {"sccue",0x0227D}, {"SucceedsSlantEqual",0x0227D}, {"succcurlyeq",0x0227D},
2127 			{"prsim",0x0227E}, {"precsim",0x0227E}, {"PrecedesTilde",0x0227E}, {"scsim",0x0227F}, {"succsim",0x0227F},
2128 			{"SucceedsTilde",0x0227F}, {"npr",0x02280}, {"nprec",0x02280}, {"NotPrecedes",0x02280}, {"nsc",0x02281},
2129 			{"nsucc",0x02281}, {"NotSucceeds",0x02281}, {"sub",0x02282}, {"subset",0x02282}, {"sup",0x02283},
2130 			{"supset",0x02283}, {"Superset",0x02283}, {"nsub",0x02284}, {"nsup",0x02285}, {"sube",0x02286},
2131 			{"SubsetEqual",0x02286}, {"subseteq",0x02286}, {"supe",0x02287}, {"supseteq",0x02287}, {"SupersetEqual",0x02287},
2132 			{"nsube",0x02288}, {"nsubseteq",0x02288}, {"NotSubsetEqual",0x02288}, {"nsupe",0x02289}, {"nsupseteq",0x02289},
2133 			{"NotSupersetEqual",0x02289}, {"subne",0x0228A}, {"subsetneq",0x0228A}, {"supne",0x0228B}, {"supsetneq",0x0228B},
2134 			{"cupdot",0x0228D}, {"uplus",0x0228E}, {"UnionPlus",0x0228E}, {"sqsub",0x0228F}, {"SquareSubset",0x0228F},
2135 			{"sqsubset",0x0228F}, {"sqsup",0x02290}, {"SquareSuperset",0x02290}, {"sqsupset",0x02290}, {"sqsube",0x02291},
2136 			{"SquareSubsetEqual",0x02291}, {"sqsubseteq",0x02291}, {"sqsupe",0x02292}, {"SquareSupersetEqual",0x02292}, {"sqsupseteq",0x02292},
2137 			{"sqcap",0x02293}, {"SquareIntersection",0x02293}, {"sqcup",0x02294}, {"SquareUnion",0x02294}, {"oplus",0x02295},
2138 			{"CirclePlus",0x02295}, {"ominus",0x02296}, {"CircleMinus",0x02296}, {"otimes",0x02297}, {"CircleTimes",0x02297},
2139 			{"osol",0x02298}, {"odot",0x02299}, {"CircleDot",0x02299}, {"ocir",0x0229A}, {"circledcirc",0x0229A},
2140 			{"oast",0x0229B}, {"circledast",0x0229B}, {"odash",0x0229D}, {"circleddash",0x0229D}, {"plusb",0x0229E},
2141 			{"boxplus",0x0229E}, {"minusb",0x0229F}, {"boxminus",0x0229F}, {"timesb",0x022A0}, {"boxtimes",0x022A0},
2142 			{"sdotb",0x022A1}, {"dotsquare",0x022A1}, {"vdash",0x022A2}, {"RightTee",0x022A2}, {"dashv",0x022A3},
2143 			{"LeftTee",0x022A3}, {"top",0x022A4}, {"DownTee",0x022A4}, {"bottom",0x022A5}, {"bot",0x022A5},
2144 			{"perp",0x022A5}, {"UpTee",0x022A5}, {"models",0x022A7}, {"vDash",0x022A8}, {"DoubleRightTee",0x022A8},
2145 			{"Vdash",0x022A9}, {"Vvdash",0x022AA}, {"VDash",0x022AB}, {"nvdash",0x022AC}, {"nvDash",0x022AD},
2146 			{"nVdash",0x022AE}, {"nVDash",0x022AF}, {"prurel",0x022B0}, {"vltri",0x022B2}, {"vartriangleleft",0x022B2},
2147 			{"LeftTriangle",0x022B2}, {"vrtri",0x022B3}, {"vartriangleright",0x022B3}, {"RightTriangle",0x022B3}, {"ltrie",0x022B4},
2148 			{"trianglelefteq",0x022B4}, {"LeftTriangleEqual",0x022B4}, {"rtrie",0x022B5}, {"trianglerighteq",0x022B5}, {"RightTriangleEqual",0x022B5},
2149 			{"origof",0x022B6}, {"imof",0x022B7}, {"mumap",0x022B8}, {"multimap",0x022B8}, {"hercon",0x022B9},
2150 			{"intcal",0x022BA}, {"intercal",0x022BA}, {"veebar",0x022BB}, {"barvee",0x022BD}, {"angrtvb",0x022BE},
2151 			{"lrtri",0x022BF}, {"xwedge",0x022C0}, {"Wedge",0x022C0}, {"bigwedge",0x022C0}, {"xvee",0x022C1},
2152 			{"Vee",0x022C1}, {"bigvee",0x022C1}, {"xcap",0x022C2}, {"Intersection",0x022C2}, {"bigcap",0x022C2},
2153 			{"xcup",0x022C3}, {"Union",0x022C3}, {"bigcup",0x022C3}, {"diam",0x022C4}, {"diamond",0x022C4},
2154 			{"Diamond",0x022C4}, {"sdot",0x022C5}, {"sstarf",0x022C6}, {"Star",0x022C6}, {"divonx",0x022C7},
2155 			{"divideontimes",0x022C7}, {"bowtie",0x022C8}, {"ltimes",0x022C9}, {"rtimes",0x022CA}, {"lthree",0x022CB},
2156 			{"leftthreetimes",0x022CB}, {"rthree",0x022CC}, {"rightthreetimes",0x022CC}, {"bsime",0x022CD}, {"backsimeq",0x022CD},
2157 			{"cuvee",0x022CE}, {"curlyvee",0x022CE}, {"cuwed",0x022CF}, {"curlywedge",0x022CF}, {"Sub",0x022D0},
2158 			{"Subset",0x022D0}, {"Sup",0x022D1}, {"Supset",0x022D1}, {"Cap",0x022D2}, {"Cup",0x022D3},
2159 			{"fork",0x022D4}, {"pitchfork",0x022D4}, {"epar",0x022D5}, {"ltdot",0x022D6}, {"lessdot",0x022D6},
2160 			{"gtdot",0x022D7}, {"gtrdot",0x022D7}, {"Ll",0x022D8}, {"Gg",0x022D9}, {"ggg",0x022D9},
2161 			{"leg",0x022DA}, {"LessEqualGreater",0x022DA}, {"lesseqgtr",0x022DA}, {"gel",0x022DB}, {"gtreqless",0x022DB},
2162 			{"GreaterEqualLess",0x022DB}, {"cuepr",0x022DE}, {"curlyeqprec",0x022DE}, {"cuesc",0x022DF}, {"curlyeqsucc",0x022DF},
2163 			{"nprcue",0x022E0}, {"NotPrecedesSlantEqual",0x022E0}, {"nsccue",0x022E1}, {"NotSucceedsSlantEqual",0x022E1}, {"nsqsube",0x022E2},
2164 			{"NotSquareSubsetEqual",0x022E2}, {"nsqsupe",0x022E3}, {"NotSquareSupersetEqual",0x022E3}, {"lnsim",0x022E6}, {"gnsim",0x022E7},
2165 			{"prnsim",0x022E8}, {"precnsim",0x022E8}, {"scnsim",0x022E9}, {"succnsim",0x022E9}, {"nltri",0x022EA},
2166 			{"ntriangleleft",0x022EA}, {"NotLeftTriangle",0x022EA}, {"nrtri",0x022EB}, {"ntriangleright",0x022EB}, {"NotRightTriangle",0x022EB},
2167 			{"nltrie",0x022EC}, {"ntrianglelefteq",0x022EC}, {"NotLeftTriangleEqual",0x022EC}, {"nrtrie",0x022ED}, {"ntrianglerighteq",0x022ED},
2168 			{"NotRightTriangleEqual",0x022ED}, {"vellip",0x022EE}, {"ctdot",0x022EF}, {"utdot",0x022F0}, {"dtdot",0x022F1},
2169 			{"disin",0x022F2}, {"isinsv",0x022F3}, {"isins",0x022F4}, {"isindot",0x022F5}, {"notinvc",0x022F6},
2170 			{"notinvb",0x022F7}, {"isinE",0x022F9}, {"nisd",0x022FA}, {"xnis",0x022FB}, {"nis",0x022FC},
2171 			{"notnivc",0x022FD}, {"notnivb",0x022FE}, {"barwed",0x02305}, {"barwedge",0x02305}, {"Barwed",0x02306},
2172 			{"doublebarwedge",0x02306}, {"lceil",0x02308}, {"LeftCeiling",0x02308}, {"rceil",0x02309}, {"RightCeiling",0x02309},
2173 			{"lfloor",0x0230A}, {"LeftFloor",0x0230A}, {"rfloor",0x0230B}, {"RightFloor",0x0230B}, {"drcrop",0x0230C},
2174 			{"dlcrop",0x0230D}, {"urcrop",0x0230E}, {"ulcrop",0x0230F}, {"bnot",0x02310}, {"profline",0x02312},
2175 			{"profsurf",0x02313}, {"telrec",0x02315}, {"target",0x02316}, {"ulcorn",0x0231C}, {"ulcorner",0x0231C},
2176 			{"urcorn",0x0231D}, {"urcorner",0x0231D}, {"dlcorn",0x0231E}, {"llcorner",0x0231E}, {"drcorn",0x0231F},
2177 			{"lrcorner",0x0231F}, {"frown",0x02322}, {"sfrown",0x02322}, {"smile",0x02323}, {"ssmile",0x02323},
2178 			{"cylcty",0x0232D}, {"profalar",0x0232E}, {"topbot",0x02336}, {"ovbar",0x0233D}, {"solbar",0x0233F},
2179 			{"angzarr",0x0237C}, {"lmoust",0x023B0}, {"lmoustache",0x023B0}, {"rmoust",0x023B1}, {"rmoustache",0x023B1},
2180 			{"tbrk",0x023B4}, {"OverBracket",0x023B4}, {"bbrk",0x023B5}, {"UnderBracket",0x023B5}, {"bbrktbrk",0x023B6},
2181 			{"OverParenthesis",0x023DC}, {"UnderParenthesis",0x023DD}, {"OverBrace",0x023DE}, {"UnderBrace",0x023DF}, {"trpezium",0x023E2},
2182 			{"elinters",0x023E7}, {"blank",0x02423}, {"oS",0x024C8}, {"circledS",0x024C8}, {"boxh",0x02500},
2183 			{"HorizontalLine",0x02500}, {"boxv",0x02502}, {"boxdr",0x0250C}, {"boxdl",0x02510}, {"boxur",0x02514},
2184 			{"boxul",0x02518}, {"boxvr",0x0251C}, {"boxvl",0x02524}, {"boxhd",0x0252C}, {"boxhu",0x02534},
2185 			{"boxvh",0x0253C}, {"boxH",0x02550}, {"boxV",0x02551}, {"boxdR",0x02552}, {"boxDr",0x02553},
2186 			{"boxDR",0x02554}, {"boxdL",0x02555}, {"boxDl",0x02556}, {"boxDL",0x02557}, {"boxuR",0x02558},
2187 			{"boxUr",0x02559}, {"boxUR",0x0255A}, {"boxuL",0x0255B}, {"boxUl",0x0255C}, {"boxUL",0x0255D},
2188 			{"boxvR",0x0255E}, {"boxVr",0x0255F}, {"boxVR",0x02560}, {"boxvL",0x02561}, {"boxVl",0x02562},
2189 			{"boxVL",0x02563}, {"boxHd",0x02564}, {"boxhD",0x02565}, {"boxHD",0x02566}, {"boxHu",0x02567},
2190 			{"boxhU",0x02568}, {"boxHU",0x02569}, {"boxvH",0x0256A}, {"boxVh",0x0256B}, {"boxVH",0x0256C},
2191 			{"uhblk",0x02580}, {"lhblk",0x02584}, {"block",0x02588}, {"blk14",0x02591}, {"blk12",0x02592},
2192 			{"blk34",0x02593}, {"squ",0x025A1}, {"square",0x025A1}, {"Square",0x025A1}, {"squf",0x025AA},
2193 			{"squarf",0x025AA}, {"blacksquare",0x025AA}, {"FilledVerySmallSquare",0x025AA}, {"EmptyVerySmallSquare",0x025AB}, {"rect",0x025AD},
2194 			{"marker",0x025AE}, {"fltns",0x025B1}, {"xutri",0x025B3}, {"bigtriangleup",0x025B3}, {"utrif",0x025B4},
2195 			{"blacktriangle",0x025B4}, {"utri",0x025B5}, {"triangle",0x025B5}, {"rtrif",0x025B8}, {"blacktriangleright",0x025B8},
2196 			{"rtri",0x025B9}, {"triangleright",0x025B9}, {"xdtri",0x025BD}, {"bigtriangledown",0x025BD}, {"dtrif",0x025BE},
2197 			{"blacktriangledown",0x025BE}, {"dtri",0x025BF}, {"triangledown",0x025BF}, {"ltrif",0x025C2}, {"blacktriangleleft",0x025C2},
2198 			{"ltri",0x025C3}, {"triangleleft",0x025C3}, {"loz",0x025CA}, {"lozenge",0x025CA}, {"cir",0x025CB},
2199 			{"tridot",0x025EC}, {"xcirc",0x025EF}, {"bigcirc",0x025EF}, {"ultri",0x025F8}, {"urtri",0x025F9},
2200 			{"lltri",0x025FA}, {"EmptySmallSquare",0x025FB}, {"FilledSmallSquare",0x025FC}, {"starf",0x02605}, {"bigstar",0x02605},
2201 			{"star",0x02606}, {"phone",0x0260E}, {"female",0x02640}, {"male",0x02642}, {"spades",0x02660},
2202 			{"spadesuit",0x02660}, {"clubs",0x02663}, {"clubsuit",0x02663}, {"hearts",0x02665}, {"heartsuit",0x02665},
2203 			{"diams",0x02666}, {"diamondsuit",0x02666}, {"sung",0x0266A}, {"flat",0x0266D}, {"natur",0x0266E},
2204 			{"natural",0x0266E}, {"sharp",0x0266F}, {"check",0x02713}, {"checkmark",0x02713}, {"cross",0x02717},
2205 			{"malt",0x02720}, {"maltese",0x02720}, {"sext",0x02736}, {"VerticalSeparator",0x02758}, {"lbbrk",0x02772},
2206 			{"rbbrk",0x02773}, {"lobrk",0x027E6}, {"LeftDoubleBracket",0x027E6}, {"robrk",0x027E7}, {"RightDoubleBracket",0x027E7},
2207 			{"lang",0x027E8}, {"LeftAngleBracket",0x027E8}, {"langle",0x027E8}, {"rang",0x027E9}, {"RightAngleBracket",0x027E9},
2208 			{"rangle",0x027E9}, {"Lang",0x027EA}, {"Rang",0x027EB}, {"loang",0x027EC}, {"roang",0x027ED},
2209 			{"xlarr",0x027F5}, {"longleftarrow",0x027F5}, {"LongLeftArrow",0x027F5}, {"xrarr",0x027F6}, {"longrightarrow",0x027F6},
2210 			{"LongRightArrow",0x027F6}, {"xharr",0x027F7}, {"longleftrightarrow",0x027F7}, {"LongLeftRightArrow",0x027F7}, {"xlArr",0x027F8},
2211 			{"Longleftarrow",0x027F8}, {"DoubleLongLeftArrow",0x027F8}, {"xrArr",0x027F9}, {"Longrightarrow",0x027F9}, {"DoubleLongRightArrow",0x027F9},
2212 			{"xhArr",0x027FA}, {"Longleftrightarrow",0x027FA}, {"DoubleLongLeftRightArrow",0x027FA}, {"xmap",0x027FC}, {"longmapsto",0x027FC},
2213 			{"dzigrarr",0x027FF}, {"nvlArr",0x02902}, {"nvrArr",0x02903}, {"nvHarr",0x02904}, {"Map",0x02905},
2214 			{"lbarr",0x0290C}, {"rbarr",0x0290D}, {"bkarow",0x0290D}, {"lBarr",0x0290E}, {"rBarr",0x0290F},
2215 			{"dbkarow",0x0290F}, {"RBarr",0x02910}, {"drbkarow",0x02910}, {"DDotrahd",0x02911}, {"UpArrowBar",0x02912},
2216 			{"DownArrowBar",0x02913}, {"Rarrtl",0x02916}, {"latail",0x02919}, {"ratail",0x0291A}, {"lAtail",0x0291B},
2217 			{"rAtail",0x0291C}, {"larrfs",0x0291D}, {"rarrfs",0x0291E}, {"larrbfs",0x0291F}, {"rarrbfs",0x02920},
2218 			{"nwarhk",0x02923}, {"nearhk",0x02924}, {"searhk",0x02925}, {"hksearow",0x02925}, {"swarhk",0x02926},
2219 			{"hkswarow",0x02926}, {"nwnear",0x02927}, {"nesear",0x02928}, {"toea",0x02928}, {"seswar",0x02929},
2220 			{"tosa",0x02929}, {"swnwar",0x0292A}, {"rarrc",0x02933}, {"cudarrr",0x02935}, {"ldca",0x02936},
2221 			{"rdca",0x02937}, {"cudarrl",0x02938}, {"larrpl",0x02939}, {"curarrm",0x0293C}, {"cularrp",0x0293D},
2222 			{"rarrpl",0x02945}, {"harrcir",0x02948}, {"Uarrocir",0x02949}, {"lurdshar",0x0294A}, {"ldrushar",0x0294B},
2223 			{"LeftRightVector",0x0294E}, {"RightUpDownVector",0x0294F}, {"DownLeftRightVector",0x02950}, {"LeftUpDownVector",0x02951}, {"LeftVectorBar",0x02952},
2224 			{"RightVectorBar",0x02953}, {"RightUpVectorBar",0x02954}, {"RightDownVectorBar",0x02955}, {"DownLeftVectorBar",0x02956}, {"DownRightVectorBar",0x02957},
2225 			{"LeftUpVectorBar",0x02958}, {"LeftDownVectorBar",0x02959}, {"LeftTeeVector",0x0295A}, {"RightTeeVector",0x0295B}, {"RightUpTeeVector",0x0295C},
2226 			{"RightDownTeeVector",0x0295D}, {"DownLeftTeeVector",0x0295E}, {"DownRightTeeVector",0x0295F}, {"LeftUpTeeVector",0x02960}, {"LeftDownTeeVector",0x02961},
2227 			{"lHar",0x02962}, {"uHar",0x02963}, {"rHar",0x02964}, {"dHar",0x02965}, {"luruhar",0x02966},
2228 			{"ldrdhar",0x02967}, {"ruluhar",0x02968}, {"rdldhar",0x02969}, {"lharul",0x0296A}, {"llhard",0x0296B},
2229 			{"rharul",0x0296C}, {"lrhard",0x0296D}, {"udhar",0x0296E}, {"UpEquilibrium",0x0296E}, {"duhar",0x0296F},
2230 			{"ReverseUpEquilibrium",0x0296F}, {"RoundImplies",0x02970}, {"erarr",0x02971}, {"simrarr",0x02972}, {"larrsim",0x02973},
2231 			{"rarrsim",0x02974}, {"rarrap",0x02975}, {"ltlarr",0x02976}, {"gtrarr",0x02978}, {"subrarr",0x02979},
2232 			{"suplarr",0x0297B}, {"lfisht",0x0297C}, {"rfisht",0x0297D}, {"ufisht",0x0297E}, {"dfisht",0x0297F},
2233 			{"lopar",0x02985}, {"ropar",0x02986}, {"lbrke",0x0298B}, {"rbrke",0x0298C}, {"lbrkslu",0x0298D},
2234 			{"rbrksld",0x0298E}, {"lbrksld",0x0298F}, {"rbrkslu",0x02990}, {"langd",0x02991}, {"rangd",0x02992},
2235 			{"lparlt",0x02993}, {"rpargt",0x02994}, {"gtlPar",0x02995}, {"ltrPar",0x02996}, {"vzigzag",0x0299A},
2236 			{"vangrt",0x0299C}, {"angrtvbd",0x0299D}, {"ange",0x029A4}, {"range",0x029A5}, {"dwangle",0x029A6},
2237 			{"uwangle",0x029A7}, {"angmsdaa",0x029A8}, {"angmsdab",0x029A9}, {"angmsdac",0x029AA}, {"angmsdad",0x029AB},
2238 			{"angmsdae",0x029AC}, {"angmsdaf",0x029AD}, {"angmsdag",0x029AE}, {"angmsdah",0x029AF}, {"bemptyv",0x029B0},
2239 			{"demptyv",0x029B1}, {"cemptyv",0x029B2}, {"raemptyv",0x029B3}, {"laemptyv",0x029B4}, {"ohbar",0x029B5},
2240 			{"omid",0x029B6}, {"opar",0x029B7}, {"operp",0x029B9}, {"olcross",0x029BB}, {"odsold",0x029BC},
2241 			{"olcir",0x029BE}, {"ofcir",0x029BF}, {"olt",0x029C0}, {"ogt",0x029C1}, {"cirscir",0x029C2},
2242 			{"cirE",0x029C3}, {"solb",0x029C4}, {"bsolb",0x029C5}, {"boxbox",0x029C9}, {"trisb",0x029CD},
2243 			{"rtriltri",0x029CE}, {"LeftTriangleBar",0x029CF}, {"RightTriangleBar",0x029D0}, {"race",0x029DA}, {"iinfin",0x029DC},
2244 			{"infintie",0x029DD}, {"nvinfin",0x029DE}, {"eparsl",0x029E3}, {"smeparsl",0x029E4}, {"eqvparsl",0x029E5},
2245 			{"lozf",0x029EB}, {"blacklozenge",0x029EB}, {"RuleDelayed",0x029F4}, {"dsol",0x029F6}, {"xodot",0x02A00},
2246 			{"bigodot",0x02A00}, {"xoplus",0x02A01}, {"bigoplus",0x02A01}, {"xotime",0x02A02}, {"bigotimes",0x02A02},
2247 			{"xuplus",0x02A04}, {"biguplus",0x02A04}, {"xsqcup",0x02A06}, {"bigsqcup",0x02A06}, {"qint",0x02A0C},
2248 			{"iiiint",0x02A0C}, {"fpartint",0x02A0D}, {"cirfnint",0x02A10}, {"awint",0x02A11}, {"rppolint",0x02A12},
2249 			{"scpolint",0x02A13}, {"npolint",0x02A14}, {"pointint",0x02A15}, {"quatint",0x02A16}, {"intlarhk",0x02A17},
2250 			{"pluscir",0x02A22}, {"plusacir",0x02A23}, {"simplus",0x02A24}, {"plusdu",0x02A25}, {"plussim",0x02A26},
2251 			{"plustwo",0x02A27}, {"mcomma",0x02A29}, {"minusdu",0x02A2A}, {"loplus",0x02A2D}, {"roplus",0x02A2E},
2252 			{"Cross",0x02A2F}, {"timesd",0x02A30}, {"timesbar",0x02A31}, {"smashp",0x02A33}, {"lotimes",0x02A34},
2253 			{"rotimes",0x02A35}, {"otimesas",0x02A36}, {"Otimes",0x02A37}, {"odiv",0x02A38}, {"triplus",0x02A39},
2254 			{"triminus",0x02A3A}, {"tritime",0x02A3B}, {"iprod",0x02A3C}, {"intprod",0x02A3C}, {"amalg",0x02A3F},
2255 			{"capdot",0x02A40}, {"ncup",0x02A42}, {"ncap",0x02A43}, {"capand",0x02A44}, {"cupor",0x02A45},
2256 			{"cupcap",0x02A46}, {"capcup",0x02A47}, {"cupbrcap",0x02A48}, {"capbrcup",0x02A49}, {"cupcup",0x02A4A},
2257 			{"capcap",0x02A4B}, {"ccups",0x02A4C}, {"ccaps",0x02A4D}, {"ccupssm",0x02A50}, {"And",0x02A53},
2258 			{"Or",0x02A54}, {"andand",0x02A55}, {"oror",0x02A56}, {"orslope",0x02A57}, {"andslope",0x02A58},
2259 			{"andv",0x02A5A}, {"orv",0x02A5B}, {"andd",0x02A5C}, {"ord",0x02A5D}, {"wedbar",0x02A5F},
2260 			{"sdote",0x02A66}, {"simdot",0x02A6A}, {"congdot",0x02A6D}, {"easter",0x02A6E}, {"apacir",0x02A6F},
2261 			{"apE",0x02A70}, {"eplus",0x02A71}, {"pluse",0x02A72}, {"Esim",0x02A73}, {"Colone",0x02A74},
2262 			{"Equal",0x02A75}, {"eDDot",0x02A77}, {"ddotseq",0x02A77}, {"equivDD",0x02A78}, {"ltcir",0x02A79},
2263 			{"gtcir",0x02A7A}, {"ltquest",0x02A7B}, {"gtquest",0x02A7C}, {"les",0x02A7D}, {"LessSlantEqual",0x02A7D},
2264 			{"leqslant",0x02A7D}, {"ges",0x02A7E}, {"GreaterSlantEqual",0x02A7E}, {"geqslant",0x02A7E}, {"lesdot",0x02A7F},
2265 			{"gesdot",0x02A80}, {"lesdoto",0x02A81}, {"gesdoto",0x02A82}, {"lesdotor",0x02A83}, {"gesdotol",0x02A84},
2266 			{"lap",0x02A85}, {"lessapprox",0x02A85}, {"gap",0x02A86}, {"gtrapprox",0x02A86}, {"lne",0x02A87},
2267 			{"lneq",0x02A87}, {"gne",0x02A88}, {"gneq",0x02A88}, {"lnap",0x02A89}, {"lnapprox",0x02A89},
2268 			{"gnap",0x02A8A}, {"gnapprox",0x02A8A}, {"lEg",0x02A8B}, {"lesseqqgtr",0x02A8B}, {"gEl",0x02A8C},
2269 			{"gtreqqless",0x02A8C}, {"lsime",0x02A8D}, {"gsime",0x02A8E}, {"lsimg",0x02A8F}, {"gsiml",0x02A90},
2270 			{"lgE",0x02A91}, {"glE",0x02A92}, {"lesges",0x02A93}, {"gesles",0x02A94}, {"els",0x02A95},
2271 			{"eqslantless",0x02A95}, {"egs",0x02A96}, {"eqslantgtr",0x02A96}, {"elsdot",0x02A97}, {"egsdot",0x02A98},
2272 			{"el",0x02A99}, {"eg",0x02A9A}, {"siml",0x02A9D}, {"simg",0x02A9E}, {"simlE",0x02A9F},
2273 			{"simgE",0x02AA0}, {"LessLess",0x02AA1}, {"GreaterGreater",0x02AA2}, {"glj",0x02AA4}, {"gla",0x02AA5},
2274 			{"ltcc",0x02AA6}, {"gtcc",0x02AA7}, {"lescc",0x02AA8}, {"gescc",0x02AA9}, {"smt",0x02AAA},
2275 			{"lat",0x02AAB}, {"smte",0x02AAC}, {"late",0x02AAD}, {"bumpE",0x02AAE}, {"pre",0x02AAF},
2276 			{"preceq",0x02AAF}, {"PrecedesEqual",0x02AAF}, {"sce",0x02AB0}, {"succeq",0x02AB0}, {"SucceedsEqual",0x02AB0},
2277 			{"prE",0x02AB3}, {"scE",0x02AB4}, {"prnE",0x02AB5}, {"precneqq",0x02AB5}, {"scnE",0x02AB6},
2278 			{"succneqq",0x02AB6}, {"prap",0x02AB7}, {"precapprox",0x02AB7}, {"scap",0x02AB8}, {"succapprox",0x02AB8},
2279 			{"prnap",0x02AB9}, {"precnapprox",0x02AB9}, {"scnap",0x02ABA}, {"succnapprox",0x02ABA}, {"Pr",0x02ABB},
2280 			{"Sc",0x02ABC}, {"subdot",0x02ABD}, {"supdot",0x02ABE}, {"subplus",0x02ABF}, {"supplus",0x02AC0},
2281 			{"submult",0x02AC1}, {"supmult",0x02AC2}, {"subedot",0x02AC3}, {"supedot",0x02AC4}, {"subE",0x02AC5},
2282 			{"subseteqq",0x02AC5}, {"supE",0x02AC6}, {"supseteqq",0x02AC6}, {"subsim",0x02AC7}, {"supsim",0x02AC8},
2283 			{"subnE",0x02ACB}, {"subsetneqq",0x02ACB}, {"supnE",0x02ACC}, {"supsetneqq",0x02ACC}, {"csub",0x02ACF},
2284 			{"csup",0x02AD0}, {"csube",0x02AD1}, {"csupe",0x02AD2}, {"subsup",0x02AD3}, {"supsub",0x02AD4},
2285 			{"subsub",0x02AD5}, {"supsup",0x02AD6}, {"suphsub",0x02AD7}, {"supdsub",0x02AD8}, {"forkv",0x02AD9},
2286 			{"topfork",0x02ADA}, {"mlcp",0x02ADB}, {"Dashv",0x02AE4}, {"DoubleLeftTee",0x02AE4}, {"Vdashl",0x02AE6},
2287 			{"Barv",0x02AE7}, {"vBar",0x02AE8}, {"vBarv",0x02AE9}, {"Vbar",0x02AEB}, {"Not",0x02AEC},
2288 			{"bNot",0x02AED}, {"rnmid",0x02AEE}, {"cirmid",0x02AEF}, {"midcir",0x02AF0}, {"topcir",0x02AF1},
2289 			{"nhpar",0x02AF2}, {"parsim",0x02AF3}, {"parsl",0x02AFD}, {"fflig",0x0FB00}, {"filig",0x0FB01},
2290 			{"fllig",0x0FB02}, {"ffilig",0x0FB03}, {"ffllig",0x0FB04}, {"Ascr",0x1D49C}, {"Cscr",0x1D49E},
2291 			{"Dscr",0x1D49F}, {"Gscr",0x1D4A2}, {"Jscr",0x1D4A5}, {"Kscr",0x1D4A6}, {"Nscr",0x1D4A9},
2292 			{"Oscr",0x1D4AA}, {"Pscr",0x1D4AB}, {"Qscr",0x1D4AC}, {"Sscr",0x1D4AE}, {"Tscr",0x1D4AF},
2293 			{"Uscr",0x1D4B0}, {"Vscr",0x1D4B1}, {"Wscr",0x1D4B2}, {"Xscr",0x1D4B3}, {"Yscr",0x1D4B4},
2294 			{"Zscr",0x1D4B5}, {"ascr",0x1D4B6}, {"bscr",0x1D4B7}, {"cscr",0x1D4B8}, {"dscr",0x1D4B9},
2295 			{"fscr",0x1D4BB}, {"hscr",0x1D4BD}, {"iscr",0x1D4BE}, {"jscr",0x1D4BF}, {"kscr",0x1D4C0},
2296 			{"lscr",0x1D4C1}, {"mscr",0x1D4C2}, {"nscr",0x1D4C3}, {"pscr",0x1D4C5}, {"qscr",0x1D4C6},
2297 			{"rscr",0x1D4C7}, {"sscr",0x1D4C8}, {"tscr",0x1D4C9}, {"uscr",0x1D4CA}, {"vscr",0x1D4CB},
2298 			{"wscr",0x1D4CC}, {"xscr",0x1D4CD}, {"yscr",0x1D4CE}, {"zscr",0x1D4CF}, {"Afr",0x1D504},
2299 			{"Bfr",0x1D505}, {"Dfr",0x1D507}, {"Efr",0x1D508}, {"Ffr",0x1D509}, {"Gfr",0x1D50A},
2300 			{"Jfr",0x1D50D}, {"Kfr",0x1D50E}, {"Lfr",0x1D50F}, {"Mfr",0x1D510}, {"Nfr",0x1D511},
2301 			{"Ofr",0x1D512}, {"Pfr",0x1D513}, {"Qfr",0x1D514}, {"Sfr",0x1D516}, {"Tfr",0x1D517},
2302 			{"Ufr",0x1D518}, {"Vfr",0x1D519}, {"Wfr",0x1D51A}, {"Xfr",0x1D51B}, {"Yfr",0x1D51C},
2303 			{"afr",0x1D51E}, {"bfr",0x1D51F}, {"cfr",0x1D520}, {"dfr",0x1D521}, {"efr",0x1D522},
2304 			{"ffr",0x1D523}, {"gfr",0x1D524}, {"hfr",0x1D525}, {"ifr",0x1D526}, {"jfr",0x1D527},
2305 			{"kfr",0x1D528}, {"lfr",0x1D529}, {"mfr",0x1D52A}, {"nfr",0x1D52B}, {"ofr",0x1D52C},
2306 			{"pfr",0x1D52D}, {"qfr",0x1D52E}, {"rfr",0x1D52F}, {"sfr",0x1D530}, {"tfr",0x1D531},
2307 			{"ufr",0x1D532}, {"vfr",0x1D533}, {"wfr",0x1D534}, {"xfr",0x1D535}, {"yfr",0x1D536},
2308 			{"zfr",0x1D537}, {"Aopf",0x1D538}, {"Bopf",0x1D539}, {"Dopf",0x1D53B}, {"Eopf",0x1D53C},
2309 			{"Fopf",0x1D53D}, {"Gopf",0x1D53E}, {"Iopf",0x1D540}, {"Jopf",0x1D541}, {"Kopf",0x1D542},
2310 			{"Lopf",0x1D543}, {"Mopf",0x1D544}, {"Oopf",0x1D546}, {"Sopf",0x1D54A}, {"Topf",0x1D54B},
2311 			{"Uopf",0x1D54C}, {"Vopf",0x1D54D}, {"Wopf",0x1D54E}, {"Xopf",0x1D54F}, {"Yopf",0x1D550},
2312 			{"aopf",0x1D552}, {"bopf",0x1D553}, {"copf",0x1D554}, {"dopf",0x1D555}, {"eopf",0x1D556},
2313 			{"fopf",0x1D557}, {"gopf",0x1D558}, {"hopf",0x1D559}, {"iopf",0x1D55A}, {"jopf",0x1D55B},
2314 			{"kopf",0x1D55C}, {"lopf",0x1D55D}, {"mopf",0x1D55E}, {"nopf",0x1D55F}, {"oopf",0x1D560},
2315 			{"popf",0x1D561}, {"qopf",0x1D562}, {"ropf",0x1D563}, {"sopf",0x1D564}, {"topf",0x1D565},
2316 			{"uopf",0x1D566}, {"vopf",0x1D567}, {"wopf",0x1D568}, {"xopf",0x1D569}, {"yopf",0x1D56A},
2317 			{"zopf",0x1D56B}
2318 		};
2319 	}
2320 
2321 	const char *p = s;
2322 	const char *const end = p + sz;
2323 	while (p != end)
2324 	{
2325 		const char *const next = librvng_utf8_next_char(p);
2326 		if (next > end)
2327 		{
2328 			// oops, the string is invalid
2329 			break;
2330 		}
2331 		if (p+4 <= end && p+1==next && *p=='&')
2332 		{
2333 			const char *q = p+1;
2334 			if (q[0]=='#')
2335 			{
2336 				++q;
2337 				unsigned long unicode=0;
2338 				if (std::isdigit(*q))
2339 				{
2340 					for (int i=0; i<6; ++i)
2341 					{
2342 						if (q>=end || !std::isdigit(*q))
2343 							break;
2344 						unicode=10*unicode+(unsigned long)(*(q++)-'0');
2345 					}
2346 				}
2347 				else if (*q=='x' && std::isalnum(q[1]))
2348 				{
2349 					++q;
2350 					for (int i=0; i<6; ++i)
2351 					{
2352 						if (q>=end)
2353 							break;
2354 						if (std::isdigit(*q))
2355 							unicode=16*unicode+(unsigned long)(*(q++)-'0');
2356 						else if (*q>='a' && *q<='f')
2357 							unicode=16*unicode+10+(unsigned long)(*(q++)-'a');
2358 						else if (*q>='A' && *q<='F')
2359 							unicode=16*unicode+10+(unsigned long)(*(q++)-'A');
2360 						else
2361 							break;
2362 					}
2363 
2364 				}
2365 				if (q<end && unicode && *q==';')
2366 				{
2367 					appendUnicodeTo(unicode, res);
2368 					p=q+1;
2369 					continue;
2370 				}
2371 			}
2372 			else if (std::isalpha(*q))
2373 			{
2374 				std::string str;
2375 				while (q<end && std::isalnum(*q))
2376 					str+=*q++;
2377 				if (q<end && *q==';' && mXmlToUnicodeMap.find(str)!=mXmlToUnicodeMap.end())
2378 				{
2379 					appendUnicodeTo(mXmlToUnicodeMap.find(str)->second, res);
2380 					p=q+1;
2381 					continue;
2382 				}
2383 			}
2384 		}
2385 
2386 		while (p != next)
2387 		{
2388 			res.append(*p);
2389 			++p;
2390 		}
2391 		p = next;
2392 	}
2393 }
2394 /* vim:set shiftwidth=4 softtabstop=4 noexpandtab: */
2395