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