{"id":81,"date":"2019-06-01T21:33:18","date_gmt":"2019-06-01T20:33:18","guid":{"rendered":"https:\/\/blog.inplico.uk\/?p=81"},"modified":"2023-06-16T00:24:58","modified_gmt":"2023-06-15T23:24:58","slug":"formatted-html-example-usage","status":"publish","type":"post","link":"https:\/\/blog.inplico.uk\/?p=81","title":{"rendered":"Formatted html &#8211; example usage (pt.3)"},"content":{"rendered":"<p>If you want to use this class then you will need 2 files (the header and cpp files).\u00a0 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<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\">#include &lt;iostream&gt;\r\n#include &lt;string&gt;\r\n#include \"pretty_html.h\"\r\n\r\nusing std::cout;\r\nusing std::string;\r\n\r\nint main() {\r\n    string htmlPage;\r\n    html_tree dom(\"html\", htmlPage);\r\n\r\n    html_tree *body = dom.new_node(\"body\");\r\n    body-&gt;new_node(\"h1\")-&gt;set_node_content(\"Hello World!\");\r\n\r\n    std::cout &lt;&lt; \"Content-type:text\/html\\r\\n\\r\\n&lt;!DOCTYPE html&gt;\\n\";\r\n    pretty_html::process_nodes(&amp;dom);\r\n\r\n    cout &lt;&lt; htmlPage;\r\n\r\n    return 0;\r\n}<\/pre>\n<p>When ran this will produce a neatly formatted html page page that looks like:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\">&lt;!DOCTYPE html&gt;\r\n&lt;html&gt;\r\n    &lt;body&gt;\r\n        &lt;h1&gt;Hello World!&lt;\/h1&gt;\r\n    &lt;\/body&gt;\r\n&lt;\/html&gt;<\/pre>\n<p>Note that when you pass the primary node to the process_nodes() function you pass it in as a reference.<\/p>\n<p>To build the file application it is simply a case of<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\">g++ .\/*.cpp -o .\/test.cgi -std=c++11<\/pre>\n<p>Note that if you absolutely cannot use c++11 then you will just need to change the declaration of the &#8220;status&#8221; enum so that it is not strictly typed.<\/p>\n<p>&nbsp;<\/p>\n<p>A slightly more complex example:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\">#include &lt;iostream&gt;\r\n#include &lt;string&gt;\r\n#include \"pretty_html.h\"\r\n\r\nusing std::cout;\r\nusing std::string;\r\n\r\nint main() {\r\n    string htmlPage;\r\n    html_tree dom(\"html\", htmlPage);\r\n\r\n    dom.new_node(\"title\")-&gt;set_node_content(\"Pretty HTML Test\");\r\n    dom.new_node(\"meta name=\\\"viewport\\\" content=\\\"width=device-width, initial-scale=1\\\"\");\r\n    dom.new_node(\"link rel=\\\"stylesheet\\\" href=\\\"https:\/\/www.w3schools.com\/w3css\/4\/w3.css\\\"\");\r\n    dom.new_node(\"link rel=\\\"stylesheet\\\" href=\\\"https:\/\/www.w3schools.com\/lib\/w3-theme-teal.css\\\"\");\r\n\r\n    html_tree *body = dom.new_node(\"body\");\r\n\r\n    html_tree *header = body-&gt;new_node(\"header class=\\\"w3-container w3-teal\\\"\");\r\n    header-&gt;new_node(\"h1\")-&gt;set_node_content(\"Testing our pretty html class:\");\r\n\r\n    html_tree *panel = body-&gt;new_node(\"div class=\\\"w3-panel w3-yellow w3-margin-top\\\"\");\r\n    panel-&gt;new_node(\"p\")-&gt;set_node_content(\r\n                \"This is some random paragraph of text placed within a div node. \"\r\n                \"The panel should be yellow.\"\r\n                );\r\n    panel-&gt;new_node(\"p\")-&gt;set_node_content(\r\n                \"This should be the second paragraph within the same container (yellow box)\"\r\n                );\r\n\r\n    body-&gt;new_node(\"br\");\r\n\r\n    body-&gt;new_node(\"p\")-&gt;set_node_content(\r\n                \"If you are calling this from the command line then you will find that\"\r\n                \"it will simply output the page to the standard screen.\"\r\n                );\r\n\r\n    body-&gt;new_node(\"p\")-&gt;set_node_content(\r\n                \"If you wish to validate the html without a webserver then simply copy \"\r\n                \"everything from \\\"&amp;lt;!DOCTYPE html&amp;gt;\\\" onwards and save it as an html page \"\r\n                \"up until the closing html tag \\\"&amp;lt;\/html&amp;gt;\\\".\"\r\n                );\r\n\r\n    std::cout &lt;&lt; \"Content-type:text\/html\\r\\n\\r\\n&lt;!DOCTYPE html&gt;\\n\";\r\n    pretty_html::process_nodes(&amp;dom);\r\n\r\n    cout &lt;&lt; htmlPage;\r\n\r\n    return 0;\r\n<\/pre>\n<p>This will produce a page that looks like:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\">&lt;!DOCTYPE html&gt;\r\n&lt;html&gt;\r\n    &lt;title&gt;Pretty HTML Test&lt;\/title&gt;\r\n    &lt;meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"&gt;\r\n    &lt;link rel=\"stylesheet\" href=\"https:\/\/www.w3schools.com\/w3css\/4\/w3.css\"&gt;\r\n    &lt;link rel=\"stylesheet\" href=\"https:\/\/www.w3schools.com\/lib\/w3-theme-teal.css\"&gt;\r\n    &lt;body&gt;\r\n        &lt;header class=\"w3-container w3-teal\"&gt;\r\n            &lt;h1&gt;Testing our pretty html class:&lt;\/h1&gt;\r\n        &lt;\/header&gt;\r\n        &lt;div class=\"w3-panel w3-yellow w3-margin-top\"&gt;\r\n            &lt;p&gt;This is some random paragraph of text placed within a div node. The panel should be yellow.&lt;\/p&gt;\r\n            &lt;p&gt;This should be the second paragraph within the same container (yellow box)&lt;\/p&gt;\r\n        &lt;\/div&gt;\r\n        &lt;br&gt;\r\n        &lt;p&gt;If you are calling this from the command line then you will find that it will simply output the page to the standard screen.&lt;\/p&gt;\r\n        &lt;p&gt;If you wish to validate the html without a webserver then simply copy everything from \"&amp;lt;!DOCTYPE html&amp;gt;\" onwards and save it as an html page up until the closing html tag \"&amp;lt;\/html&amp;gt;\".&lt;\/p&gt;\r\n    &lt;\/body&gt;\r\n&lt;\/html&gt;<\/pre>\n<p>As you can see this makes it much easier to debug and makes your code much less error prone.\u00a0 The webpage itself will look something like:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-85 aligncenter\" src=\"https:\/\/blog.inplico.uk\/wp-content\/uploads\/2019\/06\/Screen-Shot-2019-06-01-at-21.29.42-300x161.png\" alt=\"\" width=\"544\" height=\"292\" srcset=\"https:\/\/blog.inplico.uk\/wp-content\/uploads\/2019\/06\/Screen-Shot-2019-06-01-at-21.29.42-300x161.png 300w, https:\/\/blog.inplico.uk\/wp-content\/uploads\/2019\/06\/Screen-Shot-2019-06-01-at-21.29.42-768x412.png 768w, https:\/\/blog.inplico.uk\/wp-content\/uploads\/2019\/06\/Screen-Shot-2019-06-01-at-21.29.42.png 844w\" sizes=\"auto, (max-width: 544px) 100vw, 544px\" \/><\/p>\n<p>&nbsp;<\/p>\n<p>Here are the header and cpp:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\">#ifndef _GUARD_PRETTY_HTML_H\r\n#define _GUARD_PRETTY_HTML_H\r\n\r\n#include &lt;vector&gt;\r\n#include &lt;string&gt;\r\n\r\nclass html_tree {\r\n    public:\r\n        enum class status {\r\n            UNPROCESSED,\r\n            OPEN,\r\n            CLOSED\r\n        };\r\n\r\n    private:\r\n        struct TAG_CONTENT {\r\n                std::string text;\r\n                bool        lineBreak;\r\n        };\r\n\r\n        const std::string               m_htmlTag;\r\n        std::string                     &amp;m_htmlPage;\r\n        std::vector&lt;TAG_CONTENT&gt;        m_tagContent;\r\n        status                          m_nodestatus;\r\n\r\n        \/\/Void Tags (should not be closed)\r\n        const std::vector&lt;std::string&gt; voidTags {\r\n            \"area\",\r\n            \"base\",\r\n            \"br\",\r\n            \"col\",\r\n            \"command\",\r\n            \"embed\",\r\n            \"hr\",\r\n            \"img\",\r\n            \"input\",\r\n            \"keygen\",\r\n            \"link\",\r\n            \"meta\",\r\n            \"param\",\r\n            \"source\",\r\n            \"track\",\r\n            \"wbr\"\r\n        };\r\n\r\n        \/\/Blocks (That should not have new lines)\r\n        \/\/This is a work in progress\r\n        const std::vector&lt;std::string&gt; blockTags {\r\n            \"textarea\",\r\n            \"p\",\r\n            \"h1\",\r\n            \"h2\",\r\n            \"h3\",\r\n            \"h4\",\r\n            \"h5\",\r\n            \"h6\",\r\n            \"title\",\r\n            \"a\",\r\n            \"button\",\r\n            \"label\"\r\n        };\r\n\r\n        bool is_special_tag(std::string tag, const std::vector&lt;std::string&gt; &amp;table);\r\n\r\n\r\n    public:\r\n\r\n        std::vector&lt;html_tree*&gt;          childNodes;\r\n        html_tree                       *previousBranch;\r\n        \r\n        html_tree(\r\n                std::string     htmlTag,\r\n                std::string     &amp;htmlPage,\r\n                html_tree       *previousBranch = nullptr\r\n                );\r\n                \r\n        ~html_tree();\r\n        \r\n        html_tree* new_node(std::string htmlTag);\r\n        bool is_leaf_node();\r\n        void open_node(unsigned tabs);\r\n        void close_node(unsigned tabs);\r\n        status closed();\r\n        void set_node_content(std::string content, bool lineBreak = true);\r\n\r\n};\r\n\r\n\r\nnamespace pretty_html {\r\n\r\n    \/\/html_tree *find_first_open_sibling(html_tree *parent);  Now declared static in cpp file.\r\n    void process_nodes(html_tree *primaryNode);\r\n    \r\n}\r\n\r\n#endif<\/pre>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\">#include \"pretty_html.h\"\r\n\r\nusing std::string;\r\nusing std::vector;\r\n\r\nhtml_tree::html_tree(\r\n        std::string     htmlTag,\r\n        std::string     &amp;htmlPage,\r\n        html_tree       *previousBranch\r\n        )\r\n    :\r\n      m_htmlTag(htmlTag),\r\n      m_htmlPage(htmlPage),\r\n      previousBranch(previousBranch) {\r\n\r\n    m_nodestatus    = status::UNPROCESSED;\r\n}\r\n\r\n\r\nhtml_tree::~html_tree() {\r\n    for(html_tree *it : childNodes) {\r\n        delete it;\r\n    }\r\n}\r\n\r\n\r\nbool html_tree::is_special_tag(string tag, const vector&lt;string&gt; &amp;table) {\r\n    bool voidTag = false;\r\n\r\n    for(auto &amp;it : table) {\r\n        if(tag == it) voidTag = true;\r\n    }\r\n\r\n    return (voidTag);\r\n}\r\n\r\n\r\nhtml_tree* html_tree::new_node(string htmlTag) {\r\n    childNodes.push_back(new html_tree(htmlTag, m_htmlPage, this));\r\n    return(childNodes.back());\r\n}\r\n\r\n\r\nbool html_tree::is_leaf_node() {\r\n    return((childNodes.empty()) ? true : false);\r\n}\r\n\r\n\r\nvoid html_tree::open_node(unsigned tabs) {\r\n\r\n    if(tabs)\r\n        m_htmlPage.append(tabs, '\\t');\r\n\r\n    string tag(m_htmlTag.substr(0, m_htmlTag.find_first_of(\" \")));\r\n    bool blockTag(is_special_tag(tag, blockTags));\r\n\r\n    m_htmlPage.append(\"&lt;\" + m_htmlTag + \"&gt;\");\r\n\r\n    if(!blockTag)\r\n        m_htmlPage.append(\"\\n\");\r\n\r\n    if(!m_tagContent.empty()) {\r\n        for(TAG_CONTENT &amp;s : m_tagContent) {\r\n\r\n            if(blockTag) {\r\n                m_htmlPage.append(s.text);\r\n                if((s.lineBreak) &amp;&amp; (&amp;s != &amp;m_tagContent.back()))\r\n                    m_htmlPage.append(\"&lt;br&gt;\");\r\n            }\r\n            else {\r\n                m_htmlPage.append(tabs +1, '\\t');\r\n                m_htmlPage.append(s.text);\r\n                if((s.lineBreak) &amp;&amp; (&amp;s != &amp;m_tagContent.back()))\r\n                    m_htmlPage.append(\"&lt;br&gt;\");\r\n\r\n                m_htmlPage.append(\"\\n\");\r\n            }\r\n        }\r\n    }\r\n    m_nodestatus = status::OPEN;\r\n\r\n}\r\n\r\n\r\nvoid html_tree::close_node(unsigned tabs) {\r\n\r\n    string tag(m_htmlTag.substr(0, m_htmlTag.find_first_of(\" \")));\r\n\r\n    if(!is_special_tag(tag, voidTags)) {\r\n\r\n        if(!is_special_tag(tag, blockTags)) {\r\n            if(tabs)\r\n                m_htmlPage.append(tabs, '\\t');\r\n        }\r\n\r\n            m_htmlPage.append(\"&lt;\/\" + tag + \"&gt;\\n\");\r\n\r\n    }\r\n\r\n    m_nodestatus = status::CLOSED;\r\n\r\n}\r\n\r\n\r\nhtml_tree::status html_tree::closed() { return(m_nodestatus); }\r\nvoid html_tree::set_node_content(string content, bool lineBreak) { m_tagContent.push_back( {content, lineBreak} ); }\r\n\/\/END OF CLASS  --  ^^^^^^^^\r\n\r\nstatic html_tree *find_first_open_sibling(html_tree *parent) {\r\n\r\n    html_tree *workNode = nullptr;\r\n\r\n    for(html_tree *it : parent-&gt;childNodes) {\r\n        if(it-&gt;closed() != html_tree::status::CLOSED) {\r\n            workNode = it;\r\n            break;\r\n        }\r\n\r\n    }\r\n\r\n    return(workNode);\r\n}\r\n\r\nvoid pretty_html::process_nodes(html_tree *primaryNode) {\r\n\r\n\r\n    html_tree *curNode = primaryNode;\r\n    unsigned tabs =0;\r\n    while(primaryNode-&gt;closed() != html_tree::status::CLOSED) {\r\n\r\n        if(curNode-&gt;closed() == html_tree::status::UNPROCESSED) {\r\n            curNode-&gt;open_node(tabs);\r\n            ++tabs;\r\n        }\r\n\r\n        if((curNode-&gt;is_leaf_node()) &amp;&amp; (curNode-&gt;closed() == html_tree::status::OPEN)) {\r\n            --tabs;\r\n            curNode-&gt;close_node(tabs);\r\n        }\r\n\r\n        html_tree *sibling = find_first_open_sibling(curNode);\r\n\r\n        if(sibling) {\r\n            curNode = sibling;\r\n        }\r\n        else {\r\n\r\n            if(curNode-&gt;closed() == html_tree::status::OPEN) {\r\n                --tabs;\r\n                curNode-&gt;close_node(tabs);\r\n            }\r\n\r\n            curNode = curNode-&gt;previousBranch;\r\n\r\n        }\r\n\r\n    }\r\n\r\n<\/pre>\n<p>Just copy and paste the code into 2 files named <strong>pretty_html.h<\/strong> and <strong>pretty_html.cpp<\/strong> 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).<\/p>\n","protected":false},"excerpt":{"rendered":"<p>If you want to use this class then you will need 2 files (the header and cpp files).\u00a0 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 [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[5],"tags":[],"class_list":["post-81","post","type-post","status-publish","format-standard","hentry","category-cgi"],"_links":{"self":[{"href":"https:\/\/blog.inplico.uk\/index.php?rest_route=\/wp\/v2\/posts\/81","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.inplico.uk\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.inplico.uk\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.inplico.uk\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.inplico.uk\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=81"}],"version-history":[{"count":15,"href":"https:\/\/blog.inplico.uk\/index.php?rest_route=\/wp\/v2\/posts\/81\/revisions"}],"predecessor-version":[{"id":444,"href":"https:\/\/blog.inplico.uk\/index.php?rest_route=\/wp\/v2\/posts\/81\/revisions\/444"}],"wp:attachment":[{"href":"https:\/\/blog.inplico.uk\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=81"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.inplico.uk\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=81"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.inplico.uk\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=81"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}