From 21ddd1d82c91cad88d10b6da5e5e38ca1fb95c95 Mon Sep 17 00:00:00 2001 From: speedie Date: Tue, 7 May 2024 17:26:11 +0200 Subject: [PATCH] Change the behavior of FORMATTING_PRETTY to include tabs. FORMATTING_NEWLINE can be used if you want to preserve the old behavior. --- README.md | 13 ++++++++++ examples/hello-world.cpp | 16 ++++++++---- include/docpp.hpp | 12 +++++---- src/docpp.cpp | 54 +++++++++++++++++++++++++++++----------- tests/test.cpp | 25 ++++++++++++++----- 5 files changed, 89 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index 28ec653..1b9a41a 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,19 @@ Small C++ library for generating XML, HTML and CSS. +## Features + +- HTML, CSS and XML document generation and deserialization +- Sensible indentation for pretty-formatting. +- Modern C++ API +- No dependencies, other than the standard library +- Windows, macOS, Linux and *BSD support +- LGPL license + +## Not yet implemented + +- HTML/XML/CSS serialization (parsing from e.g. file) + ## Installation To install the library, you can utilize the provided CMakeLists.txt file: diff --git a/examples/hello-world.cpp b/examples/hello-world.cpp index 7844e18..d4464b3 100644 --- a/examples/hello-world.cpp +++ b/examples/hello-world.cpp @@ -82,12 +82,18 @@ int main() { headSection.push_back(docpp::HTML::HTMLElement("style", {}, css)); // - /* Add a paragraph element to the body section. */ - bodySection.push_back(docpp::HTML::HTMLElement("p", {}, "Hello, world!")); //

Hello, world!

- - /* Likewise, add a paragraph element to the footer section. */ + /* Add a paragraph element to the footer section. */ footerSection.push_back(docpp::HTML::HTMLElement("p", {}, "This is the footer.")); //

This is the footer.

+ docpp::HTML::HTMLSection divSection(docpp::HTML::SECTION_DIV, {{docpp::HTML::HTMLProperty("id", "main")}}); //
+ + /* Add a header element and a paragraph element to the div section. */ + divSection.push_back(docpp::HTML::HTMLElement("h1", {}, "Hello world!")); //

Hello world!

+ divSection.push_back(docpp::HTML::HTMLElement("p", {}, "This is a paragraph.")); //

This is a paragraph.

+ + /* Add the div section to the body section. */ + bodySection.push_back(divSection); + /* Now, let's add the header, body and footer section to the html section. * The order does matter, because an identifier is used internally. You can get this identifier by e.g. using find(). */ @@ -102,7 +108,7 @@ int main() { std::ofstream file("hello-world.html"); /* Optionally, you can use the get() method with the docpp::HTML::FORMATTING_PRETTY argument to get a *slightly* more readable document. - * It still doesn't look hand-made, but it's readable at least. The same goes for the CSS document. + * The same goes for the CSS document. Or, you can use docpp::HTML::FORMATTING_NEWLINE to get a document with elements separated by newlines. */ file << doc.get(docpp::HTML::FORMATTING_PRETTY); diff --git a/include/docpp.hpp b/include/docpp.hpp index 6f08926..42dd3b2 100644 --- a/include/docpp.hpp +++ b/include/docpp.hpp @@ -33,6 +33,7 @@ namespace docpp { TYPE_NON_CLOSED, FORMATTING_NONE, FORMATTING_PRETTY, + FORMATTING_NEWLINE, }; /** @@ -231,7 +232,7 @@ namespace docpp { * @brief Get the element in the form of an HTML tag. * @return std::string The tag of the element */ - std::string get(const int formatting = FORMATTING_NONE) const; + std::string get(const int formatting = FORMATTING_NONE, const int tabc = 0) const; /** * @brief Get the tag of the element @@ -412,7 +413,7 @@ namespace docpp { * @brief Dump the entire section. * @return std::string The section */ - std::string get(const int formatting = FORMATTING_NONE) const; + std::string get(const int formatting = FORMATTING_NONE, const int tabc = 0) const; HTMLSection operator=(const HTMLSection& section); void operator+=(const HTMLElement& element); @@ -439,7 +440,7 @@ namespace docpp { * @param std::string The type to return * @return std::string The document */ - std::string get(const int formatting = FORMATTING_NONE) const; + std::string get(const int formatting = FORMATTING_NONE, const int tabc = 0) const; /** * @brief Get the section @@ -482,6 +483,7 @@ namespace docpp { enum { FORMATTING_NONE, FORMATTING_PRETTY, + FORMATTING_NEWLINE, }; class CSSProperty { @@ -641,7 +643,7 @@ namespace docpp { * @brief Get the element * @return std::pair> The element */ - std::string get(const int formatting = FORMATTING_NONE) const; + std::string get(const int formatting = FORMATTING_NONE, const int tabc = 0) const; /** * @brief Get the tag of the element * @return std::string The tag of the element @@ -749,7 +751,7 @@ namespace docpp { * @brief Get the stylesheet * @return std::string The stylesheet */ - std::string get(const int formatting = FORMATTING_NONE) const; + std::string get(const int formatting = FORMATTING_NONE, const int tabc = 0) const; CSSStylesheet operator=(const CSSStylesheet& stylesheet); void operator+=(const CSSElement& element); diff --git a/src/docpp.cpp b/src/docpp.cpp index b66b223..15f3eb0 100644 --- a/src/docpp.cpp +++ b/src/docpp.cpp @@ -184,9 +184,15 @@ void docpp::HTML::HTMLElement::set(const std::string& tag, const HTMLElementProp this->type = type; } -std::string docpp::HTML::HTMLElement::get(const int formatting) const { +std::string docpp::HTML::HTMLElement::get(const int formatting, const int tabc) const { std::string ret{}; + if (formatting == docpp::HTML::FORMATTING_PRETTY) { + for (int i{0}; i < tabc; i++) { + ret += "\t"; + } + } + ret += "<" + this->tag; for (const auto& it : this->properties.getProperties()) { @@ -206,7 +212,7 @@ std::string docpp::HTML::HTMLElement::get(const int formatting) const { ret += this->data + "/>"; } - if (formatting == docpp::HTML::FORMATTING_PRETTY) { + if (formatting == docpp::HTML::FORMATTING_PRETTY || formatting == docpp::HTML::FORMATTING_NEWLINE) { ret += "\n"; } @@ -456,9 +462,15 @@ std::vector docpp::HTML::HTMLSection::getHTMLSections( return std::move(ret); } -std::string docpp::HTML::HTMLSection::get(const int formatting) const { +std::string docpp::HTML::HTMLSection::get(const int formatting, const int tabc) const { std::string ret{}; + if (formatting == docpp::HTML::FORMATTING_PRETTY) { + for (int i{0}; i < tabc; i++) { + ret += "\t"; + } + } + ret += "<" + this->tag; for (const auto& it : this->properties.getProperties()) { @@ -470,22 +482,28 @@ std::string docpp::HTML::HTMLSection::get(const int formatting) const { ret += ">"; - if (formatting == docpp::HTML::FORMATTING_PRETTY) { + if (formatting == docpp::HTML::FORMATTING_PRETTY || formatting == docpp::HTML::FORMATTING_NEWLINE) { ret += "\n"; } for (int i{0}; i < this->index; i++) { if (this->elements.find(i) != this->elements.end()) { - ret += this->elements.at(i).get(formatting); + ret += this->elements.at(i).get(formatting, tabc + 1); } else if (this->sections.find(i) != this->sections.end()) { - ret += this->sections.at(i).get(formatting); + ret += this->sections.at(i).get(formatting, tabc + 1); - if (formatting == docpp::HTML::FORMATTING_PRETTY) { + if (formatting == docpp::HTML::FORMATTING_PRETTY || formatting == docpp::HTML::FORMATTING_NEWLINE) { ret += "\n"; } } } + if (formatting == docpp::HTML::FORMATTING_PRETTY) { + for (int i{0}; i < tabc; i++) { + ret += "\t"; + } + } + ret += "tag + ">"; return std::move(ret); @@ -509,8 +527,8 @@ void docpp::HTML::HTMLSection::swap(const HTMLSection& section1, const HTMLSecti this->swap(this->find(section1), this->find(section2)); } -std::string docpp::HTML::HTMLDocument::get(const int formatting) const { - return this->doctype + (formatting == FORMATTING_PRETTY ? "\n" : "") + this->document.get(formatting); +std::string docpp::HTML::HTMLDocument::get(const int formatting, const int tabc) const { + return this->doctype + (formatting == FORMATTING_PRETTY ? "\n" : formatting == FORMATTING_NEWLINE ? "\n" : "") + this->document.get(formatting, tabc); } docpp::HTML::HTMLSection& docpp::HTML::HTMLDocument::getSection() { @@ -689,13 +707,13 @@ void docpp::CSS::CSSElement::swap(const CSSProperty& property1, const CSSPropert this->swap(this->find(property1), this->find(property2)); } -std::string docpp::CSS::CSSElement::get(const int formatting) const { +std::string docpp::CSS::CSSElement::get(const int formatting, const int tabc) const { std::string ret{}; if (this->element.first.compare("")) { ret += this->element.first + " {"; - if (formatting == docpp::CSS::FORMATTING_PRETTY) { + if (formatting == docpp::CSS::FORMATTING_PRETTY || formatting == docpp::CSS::FORMATTING_NEWLINE) { ret += "\n"; } @@ -703,16 +721,22 @@ std::string docpp::CSS::CSSElement::get(const int formatting) const { if (!it.getKey().compare("")) continue; if (!it.getValue().compare("")) continue; + if (formatting == docpp::CSS::FORMATTING_PRETTY) { + for (int i{0}; i < tabc + 1; i++) { + ret += "\t"; + } + } + ret += it.getKey() + ": " + it.getValue() + ";"; - if (formatting == docpp::CSS::FORMATTING_PRETTY) { + if (formatting == docpp::CSS::FORMATTING_PRETTY || formatting == docpp::CSS::FORMATTING_NEWLINE) { ret += "\n"; } } ret += "}"; - if (formatting == docpp::CSS::FORMATTING_PRETTY) { + if (formatting == docpp::CSS::FORMATTING_PRETTY || formatting == docpp::CSS::FORMATTING_NEWLINE) { ret += "\n"; } } @@ -821,11 +845,11 @@ std::vector docpp::CSS::CSSStylesheet::getElements() con return this->elements; } -std::string docpp::CSS::CSSStylesheet::get(const int formatting) const { +std::string docpp::CSS::CSSStylesheet::get(const int formatting, const int tabc) const { std::string ret{}; for (const auto& it : this->elements) { - ret += it.get(formatting); + ret += it.get(formatting, tabc); } return std::move(ret); diff --git a/tests/test.cpp b/tests/test.cpp index 233aa29..1d87b14 100644 --- a/tests/test.cpp +++ b/tests/test.cpp @@ -41,7 +41,7 @@ SCENARIO("Test HTML", "[HTML]") { const std::string expected_html{"Test Title

Test Header

Test Paragraph

Test Paragraph With ID

Test Paragraph In Div

Test Paragraph With ID And Class

"}; REQUIRE(doc.get() == expected_html); - REQUIRE(doc.get(docpp::HTML::FORMATTING_PRETTY) == "\n\n\nTest Title\n\n\n

Test Header

\n

Test Paragraph

\n

Test Paragraph With ID

\n
\n

Test Paragraph In Div

\n
\n

Test Paragraph With ID And Class

\n\n
\n
\n"); + REQUIRE(doc.get(docpp::HTML::FORMATTING_NEWLINE) == "\n\n\nTest Title\n\n\n

Test Header

\n

Test Paragraph

\n

Test Paragraph With ID

\n
\n

Test Paragraph In Div

\n
\n

Test Paragraph With ID And Class

\n\n
\n
\n"); }; auto test2 = []() { @@ -54,7 +54,7 @@ SCENARIO("Test HTML", "[HTML]") { section.erase(docpp::HTML::HTMLElement("p", {}, "Test 2")); REQUIRE(section.get() == "

Test 1

Test 3

"); - REQUIRE(section.get(docpp::HTML::FORMATTING_PRETTY) == "\n

Test 1

\n

Test 3

\n"); + REQUIRE(section.get(docpp::HTML::FORMATTING_NEWLINE) == "\n

Test 1

\n

Test 3

\n"); }; auto test3 = []() { @@ -68,7 +68,7 @@ SCENARIO("Test HTML", "[HTML]") { section.insert(pos, docpp::HTML::HTMLElement("p", {}, "Test 2.5")); REQUIRE(section.get() == "

Test 1

Test 2.5

Test 3

"); - REQUIRE(section.get(docpp::HTML::FORMATTING_PRETTY) == "\n

Test 1

\n

Test 2.5

\n

Test 3

\n"); + REQUIRE(section.get(docpp::HTML::FORMATTING_NEWLINE) == "\n

Test 1

\n

Test 2.5

\n

Test 3

\n"); }; auto test4 = []() { @@ -83,7 +83,7 @@ SCENARIO("Test HTML", "[HTML]") { section.erase(pos); REQUIRE(section.get() == "

Test 1

Test 3

"); - REQUIRE(section.get(docpp::HTML::FORMATTING_PRETTY) == "\n

Test 1

\n

Test 3

\n"); + REQUIRE(section.get(docpp::HTML::FORMATTING_NEWLINE) == "\n

Test 1

\n

Test 3

\n"); }; auto test5 = []() { @@ -103,7 +103,7 @@ SCENARIO("Test HTML", "[HTML]") { doc.set(section); REQUIRE(doc.get() == "

Test 1

Test 2

"); - REQUIRE(doc.get(docpp::HTML::FORMATTING_PRETTY) == "\n\n
\n

Test 1

\n
\n

Test 2

\n
\n
\n"); + REQUIRE(doc.get(docpp::HTML::FORMATTING_NEWLINE) == "\n\n
\n

Test 1

\n
\n

Test 2

\n
\n
\n"); }; auto test6 = []() { @@ -113,7 +113,7 @@ SCENARIO("Test HTML", "[HTML]") { css.push_back(element); REQUIRE(css.get() == "p {color: red;font-size: 16px;font-family: Arial;}"); - REQUIRE(css.get(docpp::CSS::FORMATTING_PRETTY) == "p {\ncolor: red;\nfont-size: 16px;\nfont-family: Arial;\n}\n"); + REQUIRE(css.get(docpp::CSS::FORMATTING_NEWLINE) == "p {\ncolor: red;\nfont-size: 16px;\nfont-family: Arial;\n}\n"); }; auto test7 = []() { @@ -284,6 +284,18 @@ SCENARIO("Test HTML", "[HTML]") { REQUIRE(doc.get() == "

Test 4

Test 5

Test 6

Test 7

"); }; + auto test17 = []() { + docpp::HTML::HTMLSection section = docpp::HTML::HTMLSection(docpp::HTML::SECTION_HTML, {}); + + section.push_back(docpp::HTML::HTMLElement("p", {}, "Test 1")); + section.push_back(docpp::HTML::HTMLElement("p", {}, "Test 2")); + section.push_back(docpp::HTML::HTMLElement("p", {}, "Test 3")); + + docpp::HTML::HTMLDocument doc{section}; + + REQUIRE(doc.get(docpp::HTML::FORMATTING_PRETTY) == "\n\n\t

Test 1

\n\t

Test 2

\n\t

Test 3

\n"); + }; + std::vector tests{ test1, test2, @@ -301,6 +313,7 @@ SCENARIO("Test HTML", "[HTML]") { test14, test15, test16, + test17, }; for (const auto& test : tests) {