If you want to use this class then you will need 2 files (the header and cpp files). Please note that there is no error checking or logging in these examples and if you are using them in a production environment then you will need to add your own, but you should be able to just copy them into your programs directory and it should build without any special requirements except for -std=c++11
#include <iostream> #include <string> #include "pretty_html.h" using std::cout; using std::string; int main() { string htmlPage; html_tree dom("html", htmlPage); html_tree *body = dom.new_node("body"); body->new_node("h1")->set_node_content("Hello World!"); std::cout << "Content-type:text/html\r\n\r\n<!DOCTYPE html>\n"; pretty_html::process_nodes(&dom); cout << htmlPage; return 0; }
When ran this will produce a neatly formatted html page page that looks like:
<!DOCTYPE html> <html> <body> <h1>Hello World!</h1> </body> </html>
Note that when you pass the primary node to the process_nodes() function you pass it in as a reference.
To build the file application it is simply a case of
g++ ./*.cpp -o ./test.cgi -std=c++11
Note that if you absolutely cannot use c++11 then you will just need to change the declaration of the “status” enum so that it is not strictly typed.
A slightly more complex example:
#include <iostream> #include <string> #include "pretty_html.h" using std::cout; using std::string; int main() { string htmlPage; html_tree dom("html", htmlPage); dom.new_node("title")->set_node_content("Pretty HTML Test"); dom.new_node("meta name=\"viewport\" content=\"width=device-width, initial-scale=1\""); dom.new_node("link rel=\"stylesheet\" href=\"https://www.w3schools.com/w3css/4/w3.css\""); dom.new_node("link rel=\"stylesheet\" href=\"https://www.w3schools.com/lib/w3-theme-teal.css\""); html_tree *body = dom.new_node("body"); html_tree *header = body->new_node("header class=\"w3-container w3-teal\""); header->new_node("h1")->set_node_content("Testing our pretty html class:"); html_tree *panel = body->new_node("div class=\"w3-panel w3-yellow w3-margin-top\""); panel->new_node("p")->set_node_content( "This is some random paragraph of text placed within a div node. " "The panel should be yellow." ); panel->new_node("p")->set_node_content( "This should be the second paragraph within the same container (yellow box)" ); body->new_node("br"); body->new_node("p")->set_node_content( "If you are calling this from the command line then you will find that" "it will simply output the page to the standard screen." ); body->new_node("p")->set_node_content( "If you wish to validate the html without a webserver then simply copy " "everything from \"<!DOCTYPE html>\" onwards and save it as an html page " "up until the closing html tag \"</html>\"." ); std::cout << "Content-type:text/html\r\n\r\n<!DOCTYPE html>\n"; pretty_html::process_nodes(&dom); cout << htmlPage; return 0;
This will produce a page that looks like:
<!DOCTYPE html> <html> <title>Pretty HTML Test</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css"> <link rel="stylesheet" href="https://www.w3schools.com/lib/w3-theme-teal.css"> <body> <header class="w3-container w3-teal"> <h1>Testing our pretty html class:</h1> </header> <div class="w3-panel w3-yellow w3-margin-top"> <p>This is some random paragraph of text placed within a div node. The panel should be yellow.</p> <p>This should be the second paragraph within the same container (yellow box)</p> </div> <br> <p>If you are calling this from the command line then you will find that it will simply output the page to the standard screen.</p> <p>If you wish to validate the html without a webserver then simply copy everything from "<!DOCTYPE html>" onwards and save it as an html page up until the closing html tag "</html>".</p> </body> </html>
As you can see this makes it much easier to debug and makes your code much less error prone. The webpage itself will look something like:
Here are the header and cpp:
#ifndef _GUARD_PRETTY_HTML_H #define _GUARD_PRETTY_HTML_H #include <vector> #include <string> class html_tree { public: enum class status { UNPROCESSED, OPEN, CLOSED }; private: struct TAG_CONTENT { std::string text; bool lineBreak; }; const std::string m_htmlTag; std::string &m_htmlPage; std::vector<TAG_CONTENT> m_tagContent; status m_nodestatus; //Void Tags (should not be closed) const std::vector<std::string> voidTags { "area", "base", "br", "col", "command", "embed", "hr", "img", "input", "keygen", "link", "meta", "param", "source", "track", "wbr" }; //Blocks (That should not have new lines) //This is a work in progress const std::vector<std::string> blockTags { "textarea", "p", "h1", "h2", "h3", "h4", "h5", "h6", "title", "a", "button", "label" }; bool is_special_tag(std::string tag, const std::vector<std::string> &table); public: std::vector<html_tree*> childNodes; html_tree *previousBranch; html_tree( std::string htmlTag, std::string &htmlPage, html_tree *previousBranch = nullptr ); ~html_tree(); html_tree* new_node(std::string htmlTag); bool is_leaf_node(); void open_node(unsigned tabs); void close_node(unsigned tabs); status closed(); void set_node_content(std::string content, bool lineBreak = true); }; namespace pretty_html { //html_tree *find_first_open_sibling(html_tree *parent); Now declared static in cpp file. void process_nodes(html_tree *primaryNode); } #endif
#include "pretty_html.h" using std::string; using std::vector; html_tree::html_tree( std::string htmlTag, std::string &htmlPage, html_tree *previousBranch ) : m_htmlTag(htmlTag), m_htmlPage(htmlPage), previousBranch(previousBranch) { m_nodestatus = status::UNPROCESSED; } html_tree::~html_tree() { for(html_tree *it : childNodes) { delete it; } } bool html_tree::is_special_tag(string tag, const vector<string> &table) { bool voidTag = false; for(auto &it : table) { if(tag == it) voidTag = true; } return (voidTag); } html_tree* html_tree::new_node(string htmlTag) { childNodes.push_back(new html_tree(htmlTag, m_htmlPage, this)); return(childNodes.back()); } bool html_tree::is_leaf_node() { return((childNodes.empty()) ? true : false); } void html_tree::open_node(unsigned tabs) { if(tabs) m_htmlPage.append(tabs, '\t'); string tag(m_htmlTag.substr(0, m_htmlTag.find_first_of(" "))); bool blockTag(is_special_tag(tag, blockTags)); m_htmlPage.append("<" + m_htmlTag + ">"); if(!blockTag) m_htmlPage.append("\n"); if(!m_tagContent.empty()) { for(TAG_CONTENT &s : m_tagContent) { if(blockTag) { m_htmlPage.append(s.text); if((s.lineBreak) && (&s != &m_tagContent.back())) m_htmlPage.append("<br>"); } else { m_htmlPage.append(tabs +1, '\t'); m_htmlPage.append(s.text); if((s.lineBreak) && (&s != &m_tagContent.back())) m_htmlPage.append("<br>"); m_htmlPage.append("\n"); } } } m_nodestatus = status::OPEN; } void html_tree::close_node(unsigned tabs) { string tag(m_htmlTag.substr(0, m_htmlTag.find_first_of(" "))); if(!is_special_tag(tag, voidTags)) { if(!is_special_tag(tag, blockTags)) { if(tabs) m_htmlPage.append(tabs, '\t'); } m_htmlPage.append("</" + tag + ">\n"); } m_nodestatus = status::CLOSED; } html_tree::status html_tree::closed() { return(m_nodestatus); } void html_tree::set_node_content(string content, bool lineBreak) { m_tagContent.push_back( {content, lineBreak} ); } //END OF CLASS -- ^^^^^^^^ static html_tree *find_first_open_sibling(html_tree *parent) { html_tree *workNode = nullptr; for(html_tree *it : parent->childNodes) { if(it->closed() != html_tree::status::CLOSED) { workNode = it; break; } } return(workNode); } void pretty_html::process_nodes(html_tree *primaryNode) { html_tree *curNode = primaryNode; unsigned tabs =0; while(primaryNode->closed() != html_tree::status::CLOSED) { if(curNode->closed() == html_tree::status::UNPROCESSED) { curNode->open_node(tabs); ++tabs; } if((curNode->is_leaf_node()) && (curNode->closed() == html_tree::status::OPEN)) { --tabs; curNode->close_node(tabs); } html_tree *sibling = find_first_open_sibling(curNode); if(sibling) { curNode = sibling; } else { if(curNode->closed() == html_tree::status::OPEN) { --tabs; curNode->close_node(tabs); } curNode = curNode->previousBranch; } }
Just copy and paste the code into 2 files named pretty_html.h and pretty_html.cpp and use them directly in your own code (NOTE: While there are no dependencies you probably want to install the cgicc libraries in order to make this useful).
Recent Comments