BMCO_HELP=tools/bitmapcode.helper\r
BMCO_DIR=../bitmaps\r
\r
+ODTHELPBROWSER=tools/odthelpbrowser/odthelpbrowser\r
+\r
MAINOBJS=main.o convert.o\r
CEOBJS=contacteditor/frmContactEditor.o \\r
contacteditor/frmContactEditor-Business.o \\r
WIDGETOBJS=widgets/XABAccountView.o widgets/XABContactMenu.o \\r
widgets/XABPriorityCtrl.o\r
BMCOOBJS=tools/bitmapcode.o\r
+ODTHELPBROWSEROBJS=tools/odthelpbrowser/main.o \\r
+ tools/odthelpbrowser/ODTHelpBrowser.o \\r
+ tools/odthelpbrowser/frmMain.o \\r
+ tools/odthelpbrowser/odt.o \\r
+ tools/odthelpbrowser/base64.o\r
\r
default:\r
$(MAKE) bitmaphelper\r
imexobjs: $(IMEXOBJS)\r
\r
clean:\r
- rm -f $(XAB_OUT) $(BMCO_HELP) tools/bitmapcode.o *.o \\r
- vcard/*.o common/*.o contacteditor/*.o search/*.o \\r
- widgets/*.o export/*.o import/*.o actmgr/*.o contacteditor/cdo/*.o \\r
- tests/Temp* tests/*.o tests/classes/*.o connobject/*.o \\r
- carddav2/*.o tests/$(XAB_OUT)_test\r
+ rm -f $(XAB_OUT) $(BMCO_HELP) $(ODTHELPBROWSER) \\r
+ tools/bitmapcode.o *.o vcard/*.o common/*.o contacteditor/*.o \\r
+ search/*.o widgets/*.o export/*.o import/*.o actmgr/*.o \\r
+ contacteditor/cdo/*.o tests/Temp* tests/*.o tests/classes/*.o \\r
+ connobject/*.o carddav2/*.o tools/odthelpbrowser/*.o \\r
+ tools/odthelpbrowser/odthelpbrowser tests/$(XAB_OUT)_test\r
\r
distclean: clean\r
rm -f Makefile tests/Makefile \\r
$(CPP) $(CPPFLAGS) $(BMCOOBJS) -o $(BMCO_HELP) $(CPPLIBS)\r
$(BMCO_HELP) bitmaps/\r
\r
+odthelpbrowserobjs: $(ODTHELPBROWSEROBJS)\r
+\r
+odthelpbrowser: odthelpbrowserobjs\r
+ $(CPP) $(CPPFLAGS) $(ODTHELPBROWSEROBJS) -o $(ODTHELPBROWSER) $(CPPLIBS)\r
+\r
install:\r
cp $(XAB_OUT) @BINDIR@/$(XAB_OUT)\r
cp xestiaab.1 @DATAROOTDIR@/man/man1/xestiaab.1\r
--- /dev/null
+///////////////////////////////////////////////////////////////////////////
+// C++ code generated with wxFormBuilder (version Dec 21 2016)
+// http://www.wxformbuilder.org/
+//
+// PLEASE DO "NOT" EDIT THIS FILE!
+///////////////////////////////////////////////////////////////////////////
+
+#include "ODTHelpBrowser.h"
+
+///////////////////////////////////////////////////////////////////////////
+
+frmMainADT::frmMainADT( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxFrame( parent, id, title, pos, size, style )
+{
+ this->SetSizeHints( wxDefaultSize, wxDefaultSize );
+ this->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) );
+
+ wxBoxSizer* szrMain;
+ szrMain = new wxBoxSizer( wxVERTICAL );
+
+ splBrowser = new wxSplitterWindow( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSP_3D );
+ splBrowser->Connect( wxEVT_IDLE, wxIdleEventHandler( frmMainADT::splBrowserOnIdle ), NULL, this );
+
+ pnlHelpTopics = new wxPanel( splBrowser, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
+ wxBoxSizer* szrHelpTopics;
+ szrHelpTopics = new wxBoxSizer( wxVERTICAL );
+
+ treHelpTopics = new wxTreeCtrl( pnlHelpTopics, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTR_DEFAULT_STYLE|wxTR_HIDE_ROOT );
+ szrHelpTopics->Add( treHelpTopics, 1, wxALL|wxEXPAND, 5 );
+
+
+ pnlHelpTopics->SetSizer( szrHelpTopics );
+ pnlHelpTopics->Layout();
+ szrHelpTopics->Fit( pnlHelpTopics );
+ pnlTopicBrowser = new wxPanel( splBrowser, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
+ wxBoxSizer* szrTopicBrowser;
+ szrTopicBrowser = new wxBoxSizer( wxVERTICAL );
+
+ htmPage = new wxHtmlWindow( pnlTopicBrowser, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHW_SCROLLBAR_AUTO|wxSUNKEN_BORDER );
+ szrTopicBrowser->Add( htmPage, 1, wxALL|wxEXPAND, 5 );
+
+
+ pnlTopicBrowser->SetSizer( szrTopicBrowser );
+ pnlTopicBrowser->Layout();
+ szrTopicBrowser->Fit( pnlTopicBrowser );
+ splBrowser->SplitVertically( pnlHelpTopics, pnlTopicBrowser, 248 );
+ szrMain->Add( splBrowser, 1, wxEXPAND, 5 );
+
+
+ this->SetSizer( szrMain );
+ this->Layout();
+
+ this->Centre( wxBOTH );
+
+ // Connect Events
+ treHelpTopics->Connect( wxEVT_COMMAND_TREE_SEL_CHANGED, wxTreeEventHandler( frmMainADT::UpdateHelpTopic ), NULL, this );
+}
+
+frmMainADT::~frmMainADT()
+{
+ // Disconnect Events
+ treHelpTopics->Disconnect( wxEVT_COMMAND_TREE_SEL_CHANGED, wxTreeEventHandler( frmMainADT::UpdateHelpTopic ), NULL, this );
+
+}
--- /dev/null
+///////////////////////////////////////////////////////////////////////////
+// C++ code generated with wxFormBuilder (version Dec 21 2016)
+// http://www.wxformbuilder.org/
+//
+// PLEASE DO "NOT" EDIT THIS FILE!
+///////////////////////////////////////////////////////////////////////////
+
+#ifndef __ODTHELPBROWSER_H__
+#define __ODTHELPBROWSER_H__
+
+#include <wx/artprov.h>
+#include <wx/xrc/xmlres.h>
+#include <wx/treectrl.h>
+#include <wx/gdicmn.h>
+#include <wx/font.h>
+#include <wx/colour.h>
+#include <wx/settings.h>
+#include <wx/string.h>
+#include <wx/sizer.h>
+#include <wx/panel.h>
+#include <wx/html/htmlwin.h>
+#include <wx/splitter.h>
+#include <wx/frame.h>
+
+///////////////////////////////////////////////////////////////////////////
+
+
+///////////////////////////////////////////////////////////////////////////////
+/// Class frmMainADT
+///////////////////////////////////////////////////////////////////////////////
+class frmMainADT : public wxFrame
+{
+ private:
+
+ protected:
+ wxSplitterWindow* splBrowser;
+ wxPanel* pnlHelpTopics;
+ wxTreeCtrl* treHelpTopics;
+ wxPanel* pnlTopicBrowser;
+ wxHtmlWindow* htmPage;
+
+ // Virtual event handlers, overide them in your derived class
+ virtual void UpdateHelpTopic( wxTreeEvent& event ) { event.Skip(); }
+
+
+ public:
+
+ frmMainADT( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxT("ODT Help Browser"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 614,492 ), long style = wxDEFAULT_FRAME_STYLE|wxTAB_TRAVERSAL );
+
+ ~frmMainADT();
+
+ void splBrowserOnIdle( wxIdleEvent& )
+ {
+ splBrowser->SetSashPosition( 248 );
+ splBrowser->Disconnect( wxEVT_IDLE, wxIdleEventHandler( frmMainADT::splBrowserOnIdle ), NULL, this );
+ }
+
+};
+
+#endif //__ODTHELPBROWSER_H__
--- /dev/null
+/*
+ base64.cpp and base64.h
+
+ Copyright (C) 2004-2008 René Nyffenegger
+
+ This source code is provided 'as-is', without any express or implied
+ warranty. In no event will the author be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this source code must not be misrepresented; you must not
+ claim that you wrote the original source code. If you use this source code
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original source code.
+
+ 3. This notice may not be removed or altered from any source distribution.
+
+ René Nyffenegger rene.nyffenegger@adp-gmbh.ch
+
+*/
+
+#include "base64.h"
+#include <iostream>
+
+static const std::string base64_chars =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz"
+ "0123456789+/";
+
+
+static inline bool is_base64(unsigned char c) {
+ return (isalnum(c) || (c == '+') || (c == '/'));
+}
+
+std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len) {
+ std::string ret;
+ int i = 0;
+ int j = 0;
+ unsigned char char_array_3[3];
+ unsigned char char_array_4[4];
+
+ while (in_len--) {
+ char_array_3[i++] = *(bytes_to_encode++);
+ if (i == 3) {
+ char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
+ char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
+ char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
+ char_array_4[3] = char_array_3[2] & 0x3f;
+
+ for(i = 0; (i <4) ; i++)
+ ret += base64_chars[char_array_4[i]];
+ i = 0;
+ }
+ }
+
+ if (i)
+ {
+ for(j = i; j < 3; j++)
+ char_array_3[j] = '\0';
+
+ char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
+ char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
+ char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
+ char_array_4[3] = char_array_3[2] & 0x3f;
+
+ for (j = 0; (j < i + 1); j++)
+ ret += base64_chars[char_array_4[j]];
+
+ while((i++ < 3))
+ ret += '=';
+
+ }
+
+ return ret;
+
+}
+
+std::string base64_decode(std::string const& encoded_string) {
+/*
+ Modified change: swapped 'int in_len' to 'size_t in_len'
+ */
+ size_t in_len = encoded_string.size();
+ int i = 0;
+ int j = 0;
+ int in_ = 0;
+ unsigned char char_array_4[4], char_array_3[3];
+ std::string ret;
+
+ while (in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_])) {
+ char_array_4[i++] = encoded_string[in_]; in_++;
+ if (i ==4) {
+ for (i = 0; i <4; i++)
+ char_array_4[i] = base64_chars.find(char_array_4[i]);
+
+ char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
+ char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
+ char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
+
+ for (i = 0; (i < 3); i++)
+ ret += char_array_3[i];
+ i = 0;
+ }
+ }
+
+ if (i) {
+ for (j = i; j <4; j++)
+ char_array_4[j] = 0;
+
+ for (j = 0; j <4; j++)
+ char_array_4[j] = base64_chars.find(char_array_4[j]);
+
+ char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
+ char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
+ char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
+
+ for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
+ }
+
+ return ret;
+}
+
--- /dev/null
+#include <string>
+
+std::string base64_encode(unsigned char const* , unsigned int len);
+std::string base64_decode(std::string const& s);
--- /dev/null
+#include "frmMain.h"
+
+class tocItemTreeData : public wxTreeItemData
+{
+public:
+ uint32_t itemIndex;
+
+ tocItemTreeData()
+ : wxTreeItemData()
+ {
+ };
+};
+
+frmMain::frmMain( wxWindow* parent, ODT::ODT *document )
+:
+frmMainADT( parent )
+, document( document )
+{
+ wxTreeItemId rootItem = treHelpTopics->AddRoot("Xestia Address Book", -1, -1, nullptr);
+
+ wxTreeItemId parentItem = rootItem;
+ wxTreeItemId previousItem;
+
+ wxTreeItemId lastItemAtLevel[9];
+ lastItemAtLevel[ODT::HelpTopicCurrentLevel::TOPIC_LEVEL1] = rootItem;
+ ODT::HelpTopicCurrentLevel previousLevel;
+ uint32_t itemIndex = 0;
+
+ for (auto tocItem : document->tocData)
+ {
+ if (tocItem.tocItemLevel == ODT::HelpTopicCurrentLevel::TOPIC_LEVEL1)
+ {
+ lastItemAtLevel[0] = treHelpTopics->AppendItem(rootItem, tocItem.tocItemName, -1, -1, nullptr);
+ tocItem.tocItemData = lastItemAtLevel[0].GetID();
+ }
+ else if (tocItem.tocItemLevel == ODT::HelpTopicCurrentLevel::TOPIC_LEVEL2)
+ {
+ lastItemAtLevel[1] = treHelpTopics->AppendItem(lastItemAtLevel[0], tocItem.tocItemName, -1, -1, nullptr);
+ tocItem.tocItemData = lastItemAtLevel[1].GetID();
+ }
+ else if (tocItem.tocItemLevel == ODT::HelpTopicCurrentLevel::TOPIC_LEVEL3)
+ {
+ lastItemAtLevel[2] = treHelpTopics->AppendItem(lastItemAtLevel[1], tocItem.tocItemName, -1, -1, nullptr);
+ tocItem.tocItemData = lastItemAtLevel[2].GetID();
+ }
+ else if (tocItem.tocItemLevel == ODT::HelpTopicCurrentLevel::TOPIC_LEVEL4)
+ {
+ lastItemAtLevel[3] = treHelpTopics->AppendItem(lastItemAtLevel[2], tocItem.tocItemName, -1, -1, nullptr);
+ tocItem.tocItemData = lastItemAtLevel[3].GetID();
+ }
+ else
+ {
+ wxTreeItemId newItem = treHelpTopics->AppendItem(rootItem, tocItem.tocItemName, -1, -1, nullptr);
+ tocItem.tocItemData = newItem.GetID();
+ }
+
+ tocItemTreeData *tocItemIndexData = new tocItemTreeData();
+ tocItemIndexData->itemIndex = itemIndex;
+ treHelpTopics->SetItemData(tocItem.tocItemData, tocItemIndexData);
+
+ itemIndex++;
+ }
+
+ // Load in the images.
+
+
+
+ SetTitle(document->title);
+}
+
+void frmMain::UpdateHelpTopic( wxTreeEvent& event )
+{
+ wxTreeItemId currentTreeItem = event.GetItem();
+ tocItemTreeData *tocItemData = static_cast<tocItemTreeData*>(treHelpTopics->GetItemData(currentTreeItem));
+
+ std::string pageData;
+
+ pageData += "<h2>" + std::string(treHelpTopics->GetItemText(currentTreeItem).ToStdString()) + "</h2>\n";
+
+ // Process the help topic sections.
+
+ bool firstSection = true;
+
+ for (auto helpTopicSection : document->helpTopicData[tocItemData->itemIndex].helpTopicSections)
+ {
+ if (!firstSection)
+ pageData += "<br><br>";
+
+ firstSection = false;
+ pageData += helpTopicSection.sectionText + "\n";
+ }
+
+ // Process the foot note sections.
+
+ if (document->helpTopicData[tocItemData->itemIndex].helpTopicFootnotes.size() > 0)
+ pageData += "<hr>\n";
+
+ for (auto footnoteSection : document->helpTopicData[tocItemData->itemIndex].helpTopicFootnotes)
+ {
+ std::string footnoteID = std::to_string(footnoteSection.footnoteID);
+ pageData += "<a name=\"footnote" + footnoteID + "\"></a><sup>" + footnoteID + "</sup> " + footnoteSection.footnoteText + "<br>\n";
+ }
+
+ htmPage->SetPage((wxString)pageData);
+}
--- /dev/null
+#ifndef __frmMain__
+#define __frmMain__
+
+/**
+@file
+Subclass of frmMain, which is generated by wxFormBuilder.
+*/
+
+#include "ODTHelpBrowser.h"
+#include "odt.h"
+
+#include <wx/listctrl.h>
+
+//// end generated include
+
+/** Implementing frmMain */
+class frmMain : public frmMainADT
+{
+ protected:
+ // Handlers for frmMain events.
+ void UpdateHelpTopic( wxTreeEvent& event );
+
+ ODT::ODT *document;
+ public:
+ /** Constructor */
+ frmMain( wxWindow* parent, ODT::ODT *document );
+ //// end generated class members
+
+};
+
+#endif // __frmMain__
--- /dev/null
+#include <wx/wx.h>
+#include <wx/cmdline.h>
+#include <wx/fs_mem.h>
+
+#include "frmMain.h"
+#include "odt.h"
+#include "version.h"
+
+class ODTHelpBrowser: public wxApp
+{
+ virtual bool OnInit();
+};
+
+IMPLEMENT_APP(ODTHelpBrowser);
+
+ODT::ODT odtDocument;
+
+bool ODTHelpBrowser::OnInit()
+{
+ // Process the first argument as the filename to read.
+
+ static const wxCmdLineEntryDesc cmdLineDescription [] =
+ {
+ { wxCMD_LINE_SWITCH, "h", "help", "Displays help on command line parameters",
+ wxCMD_LINE_VAL_NONE, wxCMD_LINE_OPTION_HELP },
+ { wxCMD_LINE_OPTION, "d", "document", "Flat ODT document to open to display help contacts",
+ wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL },
+ { wxCMD_LINE_SWITCH, "v", "version", "Displays version number",
+ wxCMD_LINE_VAL_NONE, wxCMD_LINE_PARAM_OPTIONAL },
+ { wxCMD_LINE_NONE }
+ };
+
+ wxCmdLineParser ODTHelpBrowserArgs (cmdLineDescription, argc, argv);
+ ODTHelpBrowserArgs.Parse();
+
+ if (ODTHelpBrowserArgs.Found(wxT("h")))
+ return false;
+
+ if (ODTHelpBrowserArgs.Found(wxT("v")))
+ {
+ std::cout << ODTHELPBROWSER_VERSION << std::endl;
+ return false;
+ }
+
+ wxString documentFilename;
+
+ if (!ODTHelpBrowserArgs.Found(wxT("d"), &documentFilename))
+ {
+ std::cout << "No file for the -d switch was given." << std::endl;
+ return false;
+ }
+
+ // Load and process the ODT document.
+
+ std::string fileToLoad = std::string(documentFilename.mb_str());
+
+ if (!odtDocument.LoadDocument(fileToLoad);
+ {
+ std::cout << "Unable to open file " << documentFilename.mb_str() << std::endl;
+ }
+
+ // Setup the form and load in the document data.
+
+ wxFileSystem::AddHandler(new wxMemoryFSHandler);
+
+ frmMain *frame = new frmMain( NULL, &odtDocument );
+
+ frame->Show(true);
+
+ SetTopWindow(frame);
+
+ return true;
+}
\ No newline at end of file
--- /dev/null
+#include "odt.h"
+#include "base64.h"
+
+#include <algorithm>
+#include <iostream>
+#include <cstdlib>
+#include <map>
+
+#include <wx/fs_mem.h>
+#include <wx/string.h>
+#include <wx/mstream.h>
+#include <wx/image.h>
+
+#define X(param) (xmlChar*)param
+
+namespace ODT
+{
+ static int footnoteID = 0;
+
+ ODT::ODT()
+ {
+ wxInitAllImageHandlers();
+ xmlKeepBlanksDefault(0);
+ }
+
+ bool ODT::LoadDocument(std::string document)
+ {
+ // Load the document into the memory.
+ std::ifstream odtDocumentStream;
+ odtDocumentStream.open(document, std::ios::in);
+
+ if (!odtDocumentStream.good())
+ return false;
+
+ std::string odtDocument((std::istreambuf_iterator<char>(odtDocumentStream)),
+ (std::istreambuf_iterator<char>()));
+
+ // Process the XML document
+ xmlDocPtr xmlODTDoc;
+ xmlODTDoc = xmlReadMemory(odtDocument.c_str(), (int)odtDocument.size(), "noname.xml", NULL, 0);
+ if (!ProcessDocument(xmlODTDoc))
+ return false;
+ }
+
+ bool ODT::ProcessDocument(xmlDocPtr document)
+ {
+ std::vector<bool(ODT::ODT::*)(xmlDocPtr)> processFunctions = { &ODT::ODT::GenerateStyleList,
+ &ODT::ODT::GetTitle, &ODT::ODT::GenerateTOC, &ODT::ODT::ProcessLineBreaks,
+ &ODT::ODT::ProcessImages, &ODT::ODT::GenerateHelpTopics };
+
+ for (auto processFunction : processFunctions)
+ if (!(this->*processFunction)(document)) return false;
+
+ return true;
+ }
+
+ bool ODT::GenerateStyleList(xmlDocPtr document)
+ {
+ xmlXPathContextPtr context;
+ xmlXPathObjectPtr result;
+
+ context = xmlXPathNewContext(document);
+ xmlXPathRegisterNs(context, X("office"), X("urn:oasis:names:tc:opendocument:xmlns:office:1.0"));
+ xmlXPathRegisterNs(context, X("text"), X("urn:oasis:names:tc:opendocument:xmlns:text:1.0"));
+ xmlXPathRegisterNs(context, X("xlink"), X("http://www.w3.org/1999/xlink"));
+ xmlXPathRegisterNs(context, X("style"), X("urn:oasis:names:tc:opendocument:xmlns:style:1.0"));
+ result = xmlXPathEvalExpression((xmlChar*)"//office:document//office:automatic-styles//style:style//*", context);
+ if(xmlXPathNodeSetIsEmpty(result->nodesetval)){
+ xmlXPathFreeObject(result);
+ std::cout << "Failed getting style list!" << std::endl;
+ return false;
+ }
+
+ xmlNodeSetPtr nodeSet = result->nodesetval;
+ xmlNodePtr meep = nodeSet->nodeTab[0]->children;
+
+ for (int nodeSeek = 0; nodeSeek < nodeSet->nodeNr; nodeSeek++) {
+ xmlNodePtr nodeData = nodeSet->nodeTab[nodeSeek]->parent;
+ xmlChar *styleNameChar = xmlGetProp(nodeData, X("name"));
+ if (styleNameChar == nullptr)
+ {
+ continue;
+ }
+ std::string styleName((char*) styleNameChar);
+ xmlFree(styleNameChar);
+
+ xmlChar *styleParentStyleNameChar = xmlGetProp(nodeData, X("parent-style-name"));
+ if (styleParentStyleNameChar == nullptr)
+ {
+ continue;
+ }
+ std::string styleParentStyleName((char*) styleParentStyleNameChar);
+ xmlFree(styleParentStyleNameChar);
+
+ styleList.insert(std::make_pair(styleName, styleParentStyleName));
+ }
+
+ return true;
+ }
+
+ bool ODT::GenerateTOC(xmlDocPtr document)
+ {
+ // Look for text:table-of-content element.
+ xmlXPathContextPtr context;
+ xmlXPathObjectPtr result;
+
+ context = xmlXPathNewContext(document);
+ xmlXPathRegisterNs(context, X("office"), X("urn:oasis:names:tc:opendocument:xmlns:office:1.0"));
+ xmlXPathRegisterNs(context, X("text"), X("urn:oasis:names:tc:opendocument:xmlns:text:1.0"));
+ xmlXPathRegisterNs(context, X("xlink"), X("http://www.w3.org/1999/xlink"));
+ result = xmlXPathEvalExpression((xmlChar*)"//office:document//office:body//office:text//text:table-of-content//text:index-body//text:p/*", context);
+ if(xmlXPathNodeSetIsEmpty(result->nodesetval)){
+ xmlXPathFreeObject(result);
+ std::cout << "Failed getting table of contents!" << std::endl;
+ return false;
+ }
+
+ xmlNodeSetPtr nodeSet = result->nodesetval;
+ xmlNodePtr meep = nodeSet->nodeTab[0]->children;
+
+ // Generate a list of contents.
+
+ HelpTableOfContentsItem *parentItem = nullptr;
+ HelpTopicCurrentLevel previousLevel = TOPIC_LEVEL1;
+
+ for (int nodeSeek = 0; nodeSeek < nodeSet->nodeNr; nodeSeek++) {
+ // Delete the page number at the end.
+ xmlChar *pageTitleChar = xmlNodeListGetString(document, nodeSet->nodeTab[nodeSeek]->xmlChildrenNode, 1);
+ xmlNodePtr nodeData = nodeSet->nodeTab[nodeSeek]->parent;
+ std::string pageTitle((char*) pageTitleChar);
+ xmlFree(pageTitleChar);
+
+ int deletePageNumberCount = 0;
+
+ for (std::string::iterator pageTitleIter = pageTitle.end();
+ pageTitleIter != pageTitle.begin();
+ pageTitleIter--)
+ {
+ if (*pageTitleIter == ' ')
+ {
+ break;
+ }
+ deletePageNumberCount++;
+ }
+
+ pageTitle.erase(pageTitle.end()-deletePageNumberCount, pageTitle.end());
+
+ // Get the paragraph style.
+ xmlChar *paragraphStyleChar = xmlGetProp(nodeData, X("style-name"));
+ std::string paragraphStyle((char*) paragraphStyleChar);
+
+ HelpTableOfContentsItem tocItem;
+
+ // Create the TOC item and add it to the list.
+
+ // Determine the level.
+
+ HelpTopicCurrentLevel currentLevel = DetermineTopicLevel(paragraphStyle);
+ xmlFree(paragraphStyleChar);
+
+ // Get the href to pair the data with later on.
+ xmlNodePtr nodeAChildData = nodeData->children;
+
+ xmlChar *tocItemHrefChar = xmlGetProp(nodeAChildData, X("href"));
+ std::string tocItemHref((char*) tocItemHrefChar);
+ xmlFree(tocItemHrefChar);
+
+ // Remove the hash from the string if it is there.
+ if (tocItemHref[0] == '#')
+ tocItemHref.erase(0,1);
+
+ tocItem.tocItemName = pageTitle;
+ tocItem.tocItemLevel = currentLevel;
+ tocItem.tocItemID = tocItemHref;
+
+ tocData.push_back(tocItem);
+ }
+
+ xmlFree(context);
+ xmlFree(result);
+
+ return true;
+ }
+
+
+ bool ODT::GetTitle(xmlDocPtr document)
+ {
+ // Look for text:table-of-content element.
+ xmlXPathContextPtr context;
+ xmlXPathObjectPtr result;
+
+ context = xmlXPathNewContext(document);
+ xmlXPathRegisterNs(context, X("office"), X("urn:oasis:names:tc:opendocument:xmlns:office:1.0"));
+ xmlXPathRegisterNs(context, X("dc"), X("http://purl.org/dc/elements/1.1/"));
+ result = xmlXPathEvalExpression((xmlChar*)"//office:document//office:meta//dc:title", context);
+ if(xmlXPathNodeSetIsEmpty(result->nodesetval)){
+ xmlXPathFreeObject(result);
+ std::cout << "Failed getting title!" << std::endl;
+ return false;
+ }
+
+ xmlNodeSetPtr nodeSet = result->nodesetval;
+ xmlNodePtr meep = nodeSet->nodeTab[0]->children;
+
+ xmlChar *documentTitleChar = xmlNodeListGetString(document, nodeSet->nodeTab[0]->xmlChildrenNode, 1);
+ std::string documentTitle((char*) documentTitleChar);
+
+ title = documentTitle;
+
+ return true;
+ }
+
+ bool ODT::GenerateHelpTopics(xmlDocPtr document)
+ {
+ // Look for bookmarks in the document.
+ xmlXPathContextPtr context;
+ xmlXPathObjectPtr result;
+
+ context = xmlXPathNewContext(document);
+ xmlXPathRegisterNs(context, X("office"), X("urn:oasis:names:tc:opendocument:xmlns:office:1.0"));
+ xmlXPathRegisterNs(context, X("text"), X("urn:oasis:names:tc:opendocument:xmlns:text:1.0"));
+ xmlXPathRegisterNs(context, X("xlink"), X("http://www.w3.org/1999/xlink"));
+ result = xmlXPathEvalExpression((xmlChar*)"//office:document//office:body//office:text//text:h//*", context);
+ if(xmlXPathNodeSetIsEmpty(result->nodesetval)){
+ xmlXPathFreeObject(result);
+ std::cout << "Failed generating help topics!" << std::endl;
+ return false;
+ }
+
+ xmlNodeSetPtr nodeSet = result->nodesetval;
+
+ std::map<std::string, xmlNodePtr> bookmarkList;
+
+ for (int nodeSeek = 0; nodeSeek < nodeSet->nodeNr; nodeSeek++) {
+ xmlNodePtr nodeData = nodeSet->nodeTab[nodeSeek];
+
+ xmlChar *bookmarkIDChar = xmlGetProp(nodeData, X("name"));
+
+ if (bookmarkIDChar == nullptr)
+ continue;
+
+ std::string bookmarkID((char*) bookmarkIDChar);
+ xmlFree(bookmarkIDChar);
+ bookmarkList.insert(std::make_pair(bookmarkID, nodeData));
+ }
+
+ // Look for each of the help topics.
+
+ for (auto tocItem : tocData)
+ {
+ footnoteID = 0;
+ std::map<std::string, xmlNodePtr>::iterator tocItemIterator;
+
+ tocItemIterator = bookmarkList.find(tocItem.tocItemID);
+
+ if (tocItemIterator == bookmarkList.end())
+ continue;
+
+ // Build the help topic information.
+
+ HelpTopicData helpTopic;
+
+ helpTopic.helpTopicName = tocItem.tocItemName;
+ helpTopic.helpTopicID = tocItem.tocItemID;
+
+ // Process text up to the next bookmark or closing office:text.
+
+ xmlNodePtr paragraphNodePtr = tocItemIterator->second->parent;
+ paragraphNodePtr = paragraphNodePtr->next;
+
+ while(paragraphNodePtr != nullptr)
+ {
+ const xmlChar *nameChar = paragraphNodePtr->name;
+ std::string name((char*) nameChar);
+
+ if (name == "text")
+ {
+ paragraphNodePtr = paragraphNodePtr->next;
+ continue;
+ }
+
+ if (name != "p" && name != "list")
+ {
+ paragraphNodePtr = paragraphNodePtr->next;
+ break;
+ }
+
+ // TODO: Get the child nodes first and process them.
+
+ xmlNodePtr paragraphChildNodePtr = paragraphNodePtr->children;
+
+ while(paragraphChildNodePtr != nullptr)
+ {
+ const xmlChar *childNameChar = paragraphChildNodePtr->name;
+ std::string childName((char*) childNameChar);
+
+ if (childName == "note")
+ {
+ // Check the note-class is a footnote.
+ ProcessNoteNode(paragraphChildNodePtr, &helpTopic);
+ }
+ paragraphChildNodePtr = paragraphChildNodePtr->next;
+ }
+
+ xmlChar *contentyStuffChar = xmlNodeGetContent(paragraphNodePtr);
+ std::string contentyStuff((char*) contentyStuffChar);
+
+ HelpTopicSection newSection;
+
+ newSection.sectionFontSize = FONTSIZE_NORMAL;
+ newSection.sectionText = contentyStuff;
+
+ helpTopic.helpTopicSections.push_back(newSection);
+
+ paragraphNodePtr = paragraphNodePtr->next;
+ }
+
+ helpTopicData.push_back(helpTopic);
+ }
+
+ return true;
+ }
+
+ bool ODT::ProcessLineBreaks(xmlDocPtr document)
+ {
+ xmlXPathContextPtr context;
+ xmlXPathObjectPtr result;
+
+ context = xmlXPathNewContext(document);
+ xmlXPathRegisterNs(context, X("office"), X("urn:oasis:names:tc:opendocument:xmlns:office:1.0"));
+ xmlXPathRegisterNs(context, X("text"), X("urn:oasis:names:tc:opendocument:xmlns:text:1.0"));
+ xmlXPathRegisterNs(context, X("xlink"), X("http://www.w3.org/1999/xlink"));
+ result = xmlXPathEvalExpression((xmlChar*)"//text:line-break", context);
+ if(xmlXPathNodeSetIsEmpty(result->nodesetval)){
+ xmlXPathFreeObject(result);
+ std::cout << "Failed generating line breaks!" << std::endl;
+ return false;
+ }
+
+ xmlNodeSetPtr nodeSet = result->nodesetval;
+
+ for (int nodeSeek = 0; nodeSeek < nodeSet->nodeNr; nodeSeek++) {
+ xmlNodePtr nodeData = nodeSet->nodeTab[nodeSeek];
+ std::string linebreakHTML = "<br>";
+ const xmlChar *linebreakHTMLChar = (const xmlChar*)linebreakHTML.c_str();
+
+ xmlNodeSetContent(nodeData, linebreakHTMLChar);
+ }
+
+ return true;
+ }
+
+ bool ODT::ProcessImages(xmlDocPtr document)
+ {
+ xmlXPathContextPtr context;
+ xmlXPathObjectPtr result;
+
+ context = xmlXPathNewContext(document);
+ xmlXPathRegisterNs(context, X("office"), X("urn:oasis:names:tc:opendocument:xmlns:office:1.0"));
+ xmlXPathRegisterNs(context, X("text"), X("urn:oasis:names:tc:opendocument:xmlns:text:1.0"));
+ xmlXPathRegisterNs(context, X("xlink"), X("http://www.w3.org/1999/xlink"));
+ xmlXPathRegisterNs(context, X("draw"), X("urn:oasis:names:tc:opendocument:xmlns:drawing:1.0"));
+ result = xmlXPathEvalExpression((xmlChar*)"//draw:frame//draw:image//office:binary-data", context);
+ if(xmlXPathNodeSetIsEmpty(result->nodesetval)){
+ xmlXPathFreeObject(result);
+ std::cout << "Failed generating images!" << std::endl;
+ return false;
+ }
+
+ xmlNodeSetPtr nodeSet = result->nodesetval;
+
+ int imageID = 0;
+
+ for (int nodeSeek = 0; nodeSeek < nodeSet->nodeNr; nodeSeek++) {
+ imageID++;
+ xmlNodePtr nodeData = nodeSet->nodeTab[nodeSeek];
+ std::string imageFilename = "image" + std::to_string(imageID) + ".png";
+
+ xmlChar *imageDataChar = xmlNodeGetContent(nodeData);
+ std::string imageData((char*) imageDataChar);
+ xmlFree(imageDataChar);
+
+ imageData.erase(std::remove(imageData.begin(), imageData.end(), '\t'), imageData.end());
+ imageData.erase(std::remove(imageData.begin(), imageData.end(), '\r'), imageData.end());
+ imageData.erase(std::remove(imageData.begin(), imageData.end(), '\n'), imageData.end());
+ imageData.erase(std::remove(imageData.begin(), imageData.end(), ' '), imageData.end());
+
+ imageData = base64_decode(imageData);
+ wxMemoryInputStream imageDataInputStream(imageData.c_str(), imageData.size());
+ wxImage documentImage;
+
+ documentImage.LoadFile(imageDataInputStream, wxBITMAP_TYPE_PNG);
+
+ wxMemoryFSHandler::AddFile(wxString(imageFilename), documentImage, wxBITMAP_TYPE_PNG);
+ }
+
+ xmlFree(result);
+ xmlFree(nodeSet);
+
+ result = xmlXPathEvalExpression((xmlChar*)"//draw:frame//draw:image", context);
+ if(xmlXPathNodeSetIsEmpty(result->nodesetval)){
+ xmlXPathFreeObject(result);
+ std::cout << "Failed generating images!" << std::endl;
+ return false;
+ }
+
+ nodeSet = result->nodesetval;
+ imageID = 0;
+
+ for (int nodeSeek = 0; nodeSeek < nodeSet->nodeNr; nodeSeek++) {
+ imageID++;
+ xmlNodePtr nodeData = nodeSet->nodeTab[nodeSeek];
+ std::string imageHTML = "<img src=\"memory:image" + std::to_string(imageID) + ".png\"><br><br>";
+ const xmlChar *imageHTMLChar = (const xmlChar*)imageHTML.c_str();
+
+ xmlNodeSetContent(nodeData, imageHTMLChar);
+ }
+
+ return true;
+ }
+
+
+ void ODT::ProcessNoteNode(xmlNodePtr nodePtr, HelpTopicData *helpTopic)
+ {
+ xmlChar *noteClassChar = xmlGetProp(nodePtr, X("note-class"));
+
+ bool footnoteFound = false;
+
+ if (noteClassChar != nullptr)
+ {
+ std::string noteClass((char*) noteClassChar);
+ if (noteClass == "footnote")
+ {
+ xmlNodePtr noteChildNodePtr = nodePtr->children;
+ while(noteChildNodePtr != nullptr)
+ {
+ const xmlChar *childNameChar = noteChildNodePtr->name;
+ std::string childName((char*) childNameChar);
+
+ if (childName == "note-body")
+ {
+ xmlNodePtr noteBodyPtr = noteChildNodePtr->children;
+ xmlChar *noteBodyChar = xmlNodeGetContent(noteBodyPtr);
+ std::string noteBody((char*) noteBodyChar);
+ xmlFree(noteBodyChar);
+
+ FootnoteSection footnoteSection;
+ footnoteSection.footnoteID = ++footnoteID;
+ footnoteSection.footnoteText = noteBody;
+ helpTopic->helpTopicFootnotes.push_back(footnoteSection);
+
+ // Delete this node.
+ footnoteFound = true;
+ }
+
+ noteChildNodePtr = noteChildNodePtr->next;
+ }
+ }
+ }
+
+ xmlFree(noteClassChar);
+
+ // Replace node with a node containing <sup><a href="#footnoteN">1</a></sup>
+
+ if (footnoteFound)
+ {
+ std::string footnoteIDString = std::to_string(footnoteID);
+ std::string footnoteHTML = "<sup><a href=\"#footnote" + footnoteIDString + "\">" + footnoteIDString +"</a></sup>";
+ const xmlChar *footnoteHTMLChar = (const xmlChar*)footnoteHTML.c_str();
+
+ xmlNodePtr newNodePtr = xmlNewNode(nullptr, X("footnotePoint"));
+ xmlNodeSetContent(newNodePtr, footnoteHTMLChar);
+
+ nodePtr = xmlReplaceNode(nodePtr, newNodePtr);
+
+ xmlUnlinkNode(nodePtr);
+ xmlFreeNode(nodePtr);
+ }
+ }
+
+ HelpTopicCurrentLevel ODT::DetermineTopicLevel(std::string styleText)
+ {
+ std::map<std::string, std::string>::iterator styleTextIterator = styleList.find(styleText);
+
+ if (styleTextIterator == styleList.end())
+ return TOPIC_LEVEL1;
+
+ if (styleTextIterator->second == "Contents_20_1")
+ return TOPIC_LEVEL1;
+ else if (styleTextIterator->second == "Contents_20_2")
+ return TOPIC_LEVEL2;
+ else if (styleTextIterator->second == "Contents_20_3")
+ return TOPIC_LEVEL3;
+ else if (styleTextIterator->second == "Contents_20_4")
+ return TOPIC_LEVEL4;
+ }
+}
\ No newline at end of file
--- /dev/null
+#ifndef __ODT_H__
+#define __ODT_H__
+
+#include <fstream>
+#include <string>
+#include <vector>
+#include <map>
+
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+#include <libxml/xpath.h>
+#include <libxml/xpathInternals.h>
+
+#include <wx/bitmap.h>
+
+namespace ODT
+{
+ enum HelpTopicSectionFontSize
+ {
+ FONTSIZE_NORMAL,
+ FONTSIZE_SECTION,
+ FONTSIZE_TITLE
+ };
+
+ enum HelpTopicCurrentLevel
+ {
+ TOPIC_LEVEL1,
+ TOPIC_LEVEL2,
+ TOPIC_LEVEL3,
+ TOPIC_LEVEL4,
+ TOPIC_LEVEL5,
+ TOPIC_LEVEL6,
+ TOPIC_LEVEL7,
+ TOPIC_LEVEL8,
+ TOPIC_LEVEL9
+ };
+
+ enum HelpTopicSectionStyle
+ {
+ SECTIONSTYLE_NORMAL,
+ };
+
+ struct HelpTopicSection
+ {
+ HelpTopicSectionFontSize sectionFontSize;
+ HelpTopicSectionStyle sectionFontStyle;
+ std::string sectionText;
+ };
+
+ struct FootnoteSection
+ {
+ int footnoteID;
+ std::string footnoteText;
+ };
+
+ struct HelpTopicData
+ {
+ std::string helpTopicName;
+ std::string helpTopicID;
+ std::vector<HelpTopicSection> helpTopicSections;
+ std::vector<FootnoteSection> helpTopicFootnotes;
+ };
+
+ struct HelpTableOfContentsItem
+ {
+ std::string tocItemName;
+ std::string tocItemID;
+ HelpTopicCurrentLevel tocItemLevel;
+ void *tocItemData;
+ };
+
+ class ODT
+ {
+ private:
+ std::map<std::string, std::string> styleList;
+
+ bool ProcessDocument(xmlDocPtr document);
+
+ bool GenerateStyleList(xmlDocPtr document);
+ bool GetTitle(xmlDocPtr document);
+ bool GenerateTOC(xmlDocPtr document);
+ bool ProcessLineBreaks(xmlDocPtr document);
+ bool ProcessImages(xmlDocPtr document);
+ bool GenerateHelpTopics(xmlDocPtr document);
+
+ void TrimString(std::string *stringToProcess);
+ void RemoveNewLines(std::string *stringToProcess);
+
+ void ProcessNoteNode(xmlNodePtr nodePtr, HelpTopicData *helpTopic);
+
+ HelpTopicCurrentLevel DetermineTopicLevel(std::string styleText);
+
+ public:
+ ODT();
+
+ std::string title;
+ std::vector<HelpTableOfContentsItem> tocData;
+ std::vector<HelpTopicData> helpTopicData;
+
+ bool LoadDocument(std::string document);
+ };
+}
+
+#endif
--- /dev/null
+#define ODTHELPBROWSER_VERSION "0.1"
\ No newline at end of file