Home | News | Projects | Releases
Bugs | RFE | Repositories | Help
odthelpbrowser: ODT Help Browser implemented
[xestiaab/.git] / source / tools / odthelpbrowser / odt.cpp
1 #include "odt.h"
2 #include "base64.h"
4 #include <algorithm>
5 #include <iostream>
6 #include <cstdlib>
7 #include <map>
9 #include <wx/fs_mem.h>
10 #include <wx/string.h>
11 #include <wx/mstream.h>
12 #include <wx/image.h>
14 #define X(param) (xmlChar*)param
16 namespace ODT
17 {
18         static int footnoteID = 0;
20         ODT::ODT()
21         {
22                 wxInitAllImageHandlers();
23                 xmlKeepBlanksDefault(0);
24         }
26         bool ODT::LoadDocument(std::string document)
27         {
28                 // Load the document into the memory.
29                 std::ifstream odtDocumentStream;
30                 odtDocumentStream.open(document, std::ios::in);
31         
32                 if (!odtDocumentStream.good())
33                         return false;
34         
35                 std::string odtDocument((std::istreambuf_iterator<char>(odtDocumentStream)),
36                                         (std::istreambuf_iterator<char>()));
37         
38                 // Process the XML document
39                 xmlDocPtr xmlODTDoc;
40                 xmlODTDoc = xmlReadMemory(odtDocument.c_str(), (int)odtDocument.size(), "noname.xml", NULL, 0);
41                 if (!ProcessDocument(xmlODTDoc))
42                         return false;
43         }
44         
45         bool ODT::ProcessDocument(xmlDocPtr document)
46         {
47                 std::vector<bool(ODT::ODT::*)(xmlDocPtr)> processFunctions = { &ODT::ODT::GenerateStyleList, 
48                         &ODT::ODT::GetTitle, &ODT::ODT::GenerateTOC, &ODT::ODT::ProcessLineBreaks, 
49                         &ODT::ODT::ProcessImages, &ODT::ODT::GenerateHelpTopics };
50                 
51                 for (auto processFunction : processFunctions)
52                         if (!(this->*processFunction)(document)) return false;
53                 
54                 return true;
55         }
57         bool ODT::GenerateStyleList(xmlDocPtr document)
58         {
59                 xmlXPathContextPtr context;
60                 xmlXPathObjectPtr result;
62                 context = xmlXPathNewContext(document);
63                 xmlXPathRegisterNs(context, X("office"), X("urn:oasis:names:tc:opendocument:xmlns:office:1.0"));
64                 xmlXPathRegisterNs(context, X("text"), X("urn:oasis:names:tc:opendocument:xmlns:text:1.0"));
65                 xmlXPathRegisterNs(context, X("xlink"), X("http://www.w3.org/1999/xlink"));
66                 xmlXPathRegisterNs(context, X("style"), X("urn:oasis:names:tc:opendocument:xmlns:style:1.0"));
67                 result = xmlXPathEvalExpression((xmlChar*)"//office:document//office:automatic-styles//style:style//*", context);
68                 if(xmlXPathNodeSetIsEmpty(result->nodesetval)){
69                         xmlXPathFreeObject(result);
70                         std::cout << "Failed getting style list!" << std::endl;
71                         return false;
72                 }
73         
74                 xmlNodeSetPtr nodeSet = result->nodesetval;
75                 xmlNodePtr meep = nodeSet->nodeTab[0]->children;
76                 
77                 for (int nodeSeek = 0; nodeSeek < nodeSet->nodeNr; nodeSeek++) {
78                         xmlNodePtr nodeData = nodeSet->nodeTab[nodeSeek]->parent;
79                         xmlChar *styleNameChar = xmlGetProp(nodeData, X("name"));
80                         if (styleNameChar == nullptr)
81                         {
82                                 continue;
83                         }                       
84                         std::string styleName((char*) styleNameChar);
85                         xmlFree(styleNameChar);
86                         
87                         xmlChar *styleParentStyleNameChar = xmlGetProp(nodeData, X("parent-style-name"));
88                         if (styleParentStyleNameChar == nullptr)
89                         {
90                                 continue;
91                         }
92                         std::string styleParentStyleName((char*) styleParentStyleNameChar);
93                         xmlFree(styleParentStyleNameChar);
94                         
95                         styleList.insert(std::make_pair(styleName, styleParentStyleName));
96                 }
97         
98                 return true;
99         }
101         bool ODT::GenerateTOC(xmlDocPtr document)
102         {
103                 // Look for text:table-of-content element.
104                 xmlXPathContextPtr context;
105                 xmlXPathObjectPtr result;
107                 context = xmlXPathNewContext(document);
108                 xmlXPathRegisterNs(context, X("office"), X("urn:oasis:names:tc:opendocument:xmlns:office:1.0"));
109                 xmlXPathRegisterNs(context, X("text"), X("urn:oasis:names:tc:opendocument:xmlns:text:1.0"));
110                 xmlXPathRegisterNs(context, X("xlink"), X("http://www.w3.org/1999/xlink"));
111                 result = xmlXPathEvalExpression((xmlChar*)"//office:document//office:body//office:text//text:table-of-content//text:index-body//text:p/*", context);
112                 if(xmlXPathNodeSetIsEmpty(result->nodesetval)){
113                         xmlXPathFreeObject(result);
114                         std::cout << "Failed getting table of contents!" << std::endl;
115                         return false;
116                 }
117                 
118                 xmlNodeSetPtr nodeSet = result->nodesetval;
119                 xmlNodePtr meep = nodeSet->nodeTab[0]->children;
120                 
121                 // Generate a list of contents.
122                 
123                 HelpTableOfContentsItem *parentItem = nullptr;
124                 HelpTopicCurrentLevel previousLevel = TOPIC_LEVEL1;
125                 
126                 for (int nodeSeek = 0; nodeSeek < nodeSet->nodeNr; nodeSeek++) {
127                         // Delete the page number at the end.
128                         xmlChar *pageTitleChar = xmlNodeListGetString(document, nodeSet->nodeTab[nodeSeek]->xmlChildrenNode, 1);
129                         xmlNodePtr nodeData = nodeSet->nodeTab[nodeSeek]->parent;
130                         std::string pageTitle((char*) pageTitleChar);
131                         xmlFree(pageTitleChar);
132                         
133                         int deletePageNumberCount = 0;
134                         
135                         for (std::string::iterator pageTitleIter = pageTitle.end(); 
136                                 pageTitleIter != pageTitle.begin();
137                                 pageTitleIter--)
138                         {
139                                 if (*pageTitleIter == ' ')
140                                 {
141                                         break;
142                                 }
143                                 deletePageNumberCount++;
144                         }
145                         
146                         pageTitle.erase(pageTitle.end()-deletePageNumberCount, pageTitle.end());
147                         
148                         // Get the paragraph style.
149                         xmlChar *paragraphStyleChar = xmlGetProp(nodeData, X("style-name"));
150                         std::string paragraphStyle((char*) paragraphStyleChar);
151                         
152                         HelpTableOfContentsItem tocItem;
154                         // Create the TOC item and add it to the list.
155                         
156                         // Determine the level.
157                         
158                         HelpTopicCurrentLevel currentLevel = DetermineTopicLevel(paragraphStyle);
159                         xmlFree(paragraphStyleChar);
160                         
161                         // Get the href to pair the data with later on.
162                         xmlNodePtr nodeAChildData = nodeData->children;
163                         
164                         xmlChar *tocItemHrefChar = xmlGetProp(nodeAChildData, X("href"));
165                         std::string tocItemHref((char*) tocItemHrefChar);
166                         xmlFree(tocItemHrefChar);
167                         
168                         // Remove the hash from the string if it is there.
169                         if (tocItemHref[0] == '#')
170                                 tocItemHref.erase(0,1);
171                         
172                         tocItem.tocItemName = pageTitle;
173                         tocItem.tocItemLevel = currentLevel;
174                         tocItem.tocItemID = tocItemHref;
175                         
176                         tocData.push_back(tocItem);
177                 }
178                 
179                 xmlFree(context);
180                 xmlFree(result);
181                 
182                 return true;
183         }
186         bool ODT::GetTitle(xmlDocPtr document)
187         {
188                 // Look for text:table-of-content element.
189                 xmlXPathContextPtr context;
190                 xmlXPathObjectPtr result;
192                 context = xmlXPathNewContext(document);
193                 xmlXPathRegisterNs(context, X("office"), X("urn:oasis:names:tc:opendocument:xmlns:office:1.0"));
194                 xmlXPathRegisterNs(context, X("dc"), X("http://purl.org/dc/elements/1.1/"));
195                 result = xmlXPathEvalExpression((xmlChar*)"//office:document//office:meta//dc:title", context);
196                 if(xmlXPathNodeSetIsEmpty(result->nodesetval)){
197                         xmlXPathFreeObject(result);
198                         std::cout << "Failed getting title!" << std::endl;
199                         return false;
200                 }
201                 
202                 xmlNodeSetPtr nodeSet = result->nodesetval;
203                 xmlNodePtr meep = nodeSet->nodeTab[0]->children;
204                 
205                 xmlChar *documentTitleChar = xmlNodeListGetString(document, nodeSet->nodeTab[0]->xmlChildrenNode, 1);
206                 std::string documentTitle((char*) documentTitleChar);
207                 
208                 title = documentTitle;
209                 
210                 return true;
211         }
212         
213         bool ODT::GenerateHelpTopics(xmlDocPtr document)
214         {
215                 // Look for bookmarks in the document.
216                 xmlXPathContextPtr context;
217                 xmlXPathObjectPtr result;
219                 context = xmlXPathNewContext(document);
220                 xmlXPathRegisterNs(context, X("office"), X("urn:oasis:names:tc:opendocument:xmlns:office:1.0"));
221                 xmlXPathRegisterNs(context, X("text"), X("urn:oasis:names:tc:opendocument:xmlns:text:1.0"));
222                 xmlXPathRegisterNs(context, X("xlink"), X("http://www.w3.org/1999/xlink"));
223                 result = xmlXPathEvalExpression((xmlChar*)"//office:document//office:body//office:text//text:h//*", context);
224                 if(xmlXPathNodeSetIsEmpty(result->nodesetval)){
225                         xmlXPathFreeObject(result);
226                         std::cout << "Failed generating help topics!" << std::endl;
227                         return false;
228                 }               
229         
230                 xmlNodeSetPtr nodeSet = result->nodesetval;
231         
232                 std::map<std::string, xmlNodePtr> bookmarkList;
233         
234                 for (int nodeSeek = 0; nodeSeek < nodeSet->nodeNr; nodeSeek++) {
235                         xmlNodePtr nodeData = nodeSet->nodeTab[nodeSeek];
236                         
237                         xmlChar *bookmarkIDChar = xmlGetProp(nodeData, X("name"));
238                         
239                         if (bookmarkIDChar == nullptr)
240                                 continue;
241                         
242                         std::string bookmarkID((char*) bookmarkIDChar);
243                         xmlFree(bookmarkIDChar);
244                         bookmarkList.insert(std::make_pair(bookmarkID, nodeData));
245                 }
246         
247                 // Look for each of the help topics.
248                 
249                 for (auto tocItem : tocData)
250                 {
251                         footnoteID = 0;
252                         std::map<std::string, xmlNodePtr>::iterator tocItemIterator;
253                         
254                         tocItemIterator = bookmarkList.find(tocItem.tocItemID);
255                         
256                         if (tocItemIterator == bookmarkList.end())
257                                 continue;
258                                 
259                         // Build the help topic information.
260                         
261                         HelpTopicData helpTopic;
262                         
263                         helpTopic.helpTopicName = tocItem.tocItemName;
264                         helpTopic.helpTopicID = tocItem.tocItemID;
265                         
266                         // Process text up to the next bookmark or closing office:text.
267                         
268                         xmlNodePtr paragraphNodePtr = tocItemIterator->second->parent;
269                         paragraphNodePtr = paragraphNodePtr->next;
270                         
271                         while(paragraphNodePtr != nullptr)
272                         {                       
273                                 const xmlChar *nameChar = paragraphNodePtr->name;
274                                 std::string name((char*) nameChar);
275                                 
276                                 if (name == "text")
277                                 {
278                                         paragraphNodePtr = paragraphNodePtr->next;
279                                         continue;
280                                 }
281                                 
282                                 if (name != "p" && name != "list")
283                                 {
284                                         paragraphNodePtr = paragraphNodePtr->next;
285                                         break;
286                                 }
288                                 // TODO: Get the child nodes first and process them.
289                         
290                                 xmlNodePtr paragraphChildNodePtr = paragraphNodePtr->children;
291                                 
292                                 while(paragraphChildNodePtr != nullptr)
293                                 {
294                                         const xmlChar *childNameChar = paragraphChildNodePtr->name;
295                                         std::string childName((char*) childNameChar);
296                                         
297                                         if (childName == "note")
298                                         {
299                                                 // Check the note-class is a footnote.
300                                                 ProcessNoteNode(paragraphChildNodePtr, &helpTopic);
301                                         }
302                                         paragraphChildNodePtr = paragraphChildNodePtr->next;
303                                 }
305                                 xmlChar *contentyStuffChar = xmlNodeGetContent(paragraphNodePtr);
306                                 std::string contentyStuff((char*) contentyStuffChar);
307                                 
308                                 HelpTopicSection newSection;
309                                 
310                                 newSection.sectionFontSize = FONTSIZE_NORMAL;
311                                 newSection.sectionText = contentyStuff;
312                                 
313                                 helpTopic.helpTopicSections.push_back(newSection);
314                                 
315                                 paragraphNodePtr = paragraphNodePtr->next;
316                         }
317                         
318                         helpTopicData.push_back(helpTopic);
319                 }
320                 
321                 return true;
322         }
323         
324         bool ODT::ProcessLineBreaks(xmlDocPtr document)
325         {
326                 xmlXPathContextPtr context;
327                 xmlXPathObjectPtr result;
329                 context = xmlXPathNewContext(document);
330                 xmlXPathRegisterNs(context, X("office"), X("urn:oasis:names:tc:opendocument:xmlns:office:1.0"));
331                 xmlXPathRegisterNs(context, X("text"), X("urn:oasis:names:tc:opendocument:xmlns:text:1.0"));
332                 xmlXPathRegisterNs(context, X("xlink"), X("http://www.w3.org/1999/xlink"));
333                 result = xmlXPathEvalExpression((xmlChar*)"//text:line-break", context);
334                 if(xmlXPathNodeSetIsEmpty(result->nodesetval)){
335                         xmlXPathFreeObject(result);
336                         std::cout << "Failed generating line breaks!" << std::endl;
337                         return false;
338                 }
339                 
340                 xmlNodeSetPtr nodeSet = result->nodesetval;
341         
342                 for (int nodeSeek = 0; nodeSeek < nodeSet->nodeNr; nodeSeek++) {
343                         xmlNodePtr nodeData = nodeSet->nodeTab[nodeSeek];
344                         std::string linebreakHTML = "<br>";
345                         const xmlChar *linebreakHTMLChar = (const xmlChar*)linebreakHTML.c_str();
347                         xmlNodeSetContent(nodeData, linebreakHTMLChar);
348                 }
349                 
350                 return true;
351         }
353         bool ODT::ProcessImages(xmlDocPtr document)
354         {
355                 xmlXPathContextPtr context;
356                 xmlXPathObjectPtr result;
358                 context = xmlXPathNewContext(document);
359                 xmlXPathRegisterNs(context, X("office"), X("urn:oasis:names:tc:opendocument:xmlns:office:1.0"));
360                 xmlXPathRegisterNs(context, X("text"), X("urn:oasis:names:tc:opendocument:xmlns:text:1.0"));
361                 xmlXPathRegisterNs(context, X("xlink"), X("http://www.w3.org/1999/xlink"));
362                 xmlXPathRegisterNs(context, X("draw"), X("urn:oasis:names:tc:opendocument:xmlns:drawing:1.0"));
363                 result = xmlXPathEvalExpression((xmlChar*)"//draw:frame//draw:image//office:binary-data", context);
364                 if(xmlXPathNodeSetIsEmpty(result->nodesetval)){
365                         xmlXPathFreeObject(result);
366                         std::cout << "Failed generating images!" << std::endl;
367                         return false;
368                 }
369                 
370                 xmlNodeSetPtr nodeSet = result->nodesetval;
371                 
372                 int imageID = 0;
373                 
374                 for (int nodeSeek = 0; nodeSeek < nodeSet->nodeNr; nodeSeek++) {
375                         imageID++;
376                         xmlNodePtr nodeData = nodeSet->nodeTab[nodeSeek];
377                         std::string imageFilename = "image" + std::to_string(imageID) + ".png";
378                         
379                         xmlChar *imageDataChar = xmlNodeGetContent(nodeData);
380                         std::string imageData((char*) imageDataChar);
381                         xmlFree(imageDataChar);
382                         
383                         imageData.erase(std::remove(imageData.begin(), imageData.end(), '\t'), imageData.end());
384                         imageData.erase(std::remove(imageData.begin(), imageData.end(), '\r'), imageData.end());
385                         imageData.erase(std::remove(imageData.begin(), imageData.end(), '\n'), imageData.end());
386                         imageData.erase(std::remove(imageData.begin(), imageData.end(), ' '), imageData.end());
387                         
388                         imageData = base64_decode(imageData);
389                         wxMemoryInputStream imageDataInputStream(imageData.c_str(), imageData.size());
390                         wxImage documentImage;
391                         
392                         documentImage.LoadFile(imageDataInputStream, wxBITMAP_TYPE_PNG);
393                         
394                         wxMemoryFSHandler::AddFile(wxString(imageFilename), documentImage, wxBITMAP_TYPE_PNG);
395                 }
397                 xmlFree(result);                
398                 xmlFree(nodeSet);
399                 
400                 result = xmlXPathEvalExpression((xmlChar*)"//draw:frame//draw:image", context);
401                 if(xmlXPathNodeSetIsEmpty(result->nodesetval)){
402                         xmlXPathFreeObject(result);
403                         std::cout << "Failed generating images!" << std::endl;
404                         return false;
405                 }
406                 
407                 nodeSet = result->nodesetval;
408                 imageID = 0;
409                 
410                 for (int nodeSeek = 0; nodeSeek < nodeSet->nodeNr; nodeSeek++) {
411                         imageID++;
412                         xmlNodePtr nodeData = nodeSet->nodeTab[nodeSeek];
413                         std::string imageHTML = "<img src=\"memory:image" + std::to_string(imageID) + ".png\"><br><br>";
414                         const xmlChar *imageHTMLChar = (const xmlChar*)imageHTML.c_str();
416                         xmlNodeSetContent(nodeData, imageHTMLChar);
417                 }
418                 
419                 return true;
420         }
421         
422         
423         void ODT::ProcessNoteNode(xmlNodePtr nodePtr, HelpTopicData *helpTopic)
424         {
425                 xmlChar *noteClassChar = xmlGetProp(nodePtr, X("note-class"));
426                 
427                 bool footnoteFound = false;
428                 
429                 if (noteClassChar != nullptr)
430                 {
431                         std::string noteClass((char*) noteClassChar);
432                         if (noteClass == "footnote")
433                         {
434                                 xmlNodePtr noteChildNodePtr = nodePtr->children;
435                                 while(noteChildNodePtr != nullptr)
436                                 {
437                                         const xmlChar *childNameChar = noteChildNodePtr->name;
438                                         std::string childName((char*) childNameChar);
439                                         
440                                         if (childName == "note-body")
441                                         {
442                                                 xmlNodePtr noteBodyPtr = noteChildNodePtr->children;
443                                                 xmlChar *noteBodyChar = xmlNodeGetContent(noteBodyPtr);
444                                                 std::string noteBody((char*) noteBodyChar);
445                                                 xmlFree(noteBodyChar);
446                                                 
447                                                 FootnoteSection footnoteSection;
448                                                 footnoteSection.footnoteID = ++footnoteID;
449                                                 footnoteSection.footnoteText = noteBody;
450                                                 helpTopic->helpTopicFootnotes.push_back(footnoteSection);
451                                                 
452                                                 // Delete this node.
453                                                 footnoteFound = true;
454                                         }
455                                         
456                                         noteChildNodePtr = noteChildNodePtr->next;
457                                 }
458                         }
459                 }
460                 
461                 xmlFree(noteClassChar);
462                 
463                 // Replace node with a node containing <sup><a href="#footnoteN">1</a></sup>
464                 
465                 if (footnoteFound)
466                 {
467                         std::string footnoteIDString = std::to_string(footnoteID);
468                         std::string footnoteHTML = "<sup><a href=\"#footnote" + footnoteIDString + "\">" + footnoteIDString +"</a></sup>";
469                         const xmlChar *footnoteHTMLChar = (const xmlChar*)footnoteHTML.c_str();
470                 
471                         xmlNodePtr newNodePtr = xmlNewNode(nullptr, X("footnotePoint"));
472                         xmlNodeSetContent(newNodePtr, footnoteHTMLChar);
473                         
474                         nodePtr = xmlReplaceNode(nodePtr, newNodePtr);
475                         
476                         xmlUnlinkNode(nodePtr);
477                         xmlFreeNode(nodePtr);
478                 }
479         }
480         
481         HelpTopicCurrentLevel ODT::DetermineTopicLevel(std::string styleText)
482         {
483                 std::map<std::string, std::string>::iterator styleTextIterator = styleList.find(styleText);
484         
485                 if (styleTextIterator == styleList.end())
486                         return TOPIC_LEVEL1;
487         
488                 if (styleTextIterator->second == "Contents_20_1")
489                         return TOPIC_LEVEL1;
490                 else if (styleTextIterator->second == "Contents_20_2")
491                         return TOPIC_LEVEL2;
492                 else if (styleTextIterator->second == "Contents_20_3")
493                         return TOPIC_LEVEL3;
494                 else if (styleTextIterator->second == "Contents_20_4")
495                         return TOPIC_LEVEL4;
496         }
Xestia Software Development
Yn Maystri
© 2006 - 2019 Xestia Software Development
Software

Xestia Address Book
Xestia Calendar
Development

Xestia Gelforn
Everything else

About
News
Privacy Policy