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 α , 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