Change the behavior of FORMATTING_PRETTY to include tabs.

FORMATTING_NEWLINE can be used if you want to preserve the old behavior.
This commit is contained in:
Jacob 2024-05-07 17:26:11 +02:00
parent 804309d323
commit 21ddd1d82c
5 changed files with 89 additions and 31 deletions

View file

@ -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:

View file

@ -82,12 +82,18 @@ int main() {
headSection.push_back(docpp::HTML::HTMLElement("style", {}, css)); // <style>body { background-color: black; color: white; }</style>
/* Add a paragraph element to the body section. */
bodySection.push_back(docpp::HTML::HTMLElement("p", {}, "Hello, world!")); // <p>Hello, world!</p>
/* 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.")); // <p>This is the footer.</p>
docpp::HTML::HTMLSection divSection(docpp::HTML::SECTION_DIV, {{docpp::HTML::HTMLProperty("id", "main")}}); // <div id="main"></div>
/* Add a header element and a paragraph element to the div section. */
divSection.push_back(docpp::HTML::HTMLElement("h1", {}, "Hello world!")); // <h1>Hello world!</h1>
divSection.push_back(docpp::HTML::HTMLElement("p", {}, "This is a paragraph.")); // <p>This is a paragraph.</p>
/* 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);

View file

@ -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<std::string, std::vector<CSSProperty>> 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);

View file

@ -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> 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 += "</" + this->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::CSSElement> 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);

View file

@ -41,7 +41,7 @@ SCENARIO("Test HTML", "[HTML]") {
const std::string expected_html{"<!DOCTYPE html><html><head><title>Test Title</title></head><body><h1>Test Header</h1><p>Test Paragraph</p><p id=\"test_id\">Test Paragraph With ID</p><div><p>Test Paragraph In Div</p></div><p id=\"test_id\" class=\"class1 class2 class3\">Test Paragraph With ID And Class</p></body><footer></footer></html>"};
REQUIRE(doc.get() == expected_html);
REQUIRE(doc.get(docpp::HTML::FORMATTING_PRETTY) == "<!DOCTYPE html>\n<html>\n<head>\n<title>Test Title</title>\n</head>\n<body>\n<h1>Test Header</h1>\n<p>Test Paragraph</p>\n<p id=\"test_id\">Test Paragraph With ID</p>\n<div>\n<p>Test Paragraph In Div</p>\n</div>\n<p id=\"test_id\" class=\"class1 class2 class3\">Test Paragraph With ID And Class</p>\n</body>\n<footer>\n</footer>\n</html>");
REQUIRE(doc.get(docpp::HTML::FORMATTING_NEWLINE) == "<!DOCTYPE html>\n<html>\n<head>\n<title>Test Title</title>\n</head>\n<body>\n<h1>Test Header</h1>\n<p>Test Paragraph</p>\n<p id=\"test_id\">Test Paragraph With ID</p>\n<div>\n<p>Test Paragraph In Div</p>\n</div>\n<p id=\"test_id\" class=\"class1 class2 class3\">Test Paragraph With ID And Class</p>\n</body>\n<footer>\n</footer>\n</html>");
};
auto test2 = []() {
@ -54,7 +54,7 @@ SCENARIO("Test HTML", "[HTML]") {
section.erase(docpp::HTML::HTMLElement("p", {}, "Test 2"));
REQUIRE(section.get() == "<html><p>Test 1</p><p>Test 3</p></html>");
REQUIRE(section.get(docpp::HTML::FORMATTING_PRETTY) == "<html>\n<p>Test 1</p>\n<p>Test 3</p>\n</html>");
REQUIRE(section.get(docpp::HTML::FORMATTING_NEWLINE) == "<html>\n<p>Test 1</p>\n<p>Test 3</p>\n</html>");
};
auto test3 = []() {
@ -68,7 +68,7 @@ SCENARIO("Test HTML", "[HTML]") {
section.insert(pos, docpp::HTML::HTMLElement("p", {}, "Test 2.5"));
REQUIRE(section.get() == "<html><p>Test 1</p><p>Test 2.5</p><p>Test 3</p></html>");
REQUIRE(section.get(docpp::HTML::FORMATTING_PRETTY) == "<html>\n<p>Test 1</p>\n<p>Test 2.5</p>\n<p>Test 3</p>\n</html>");
REQUIRE(section.get(docpp::HTML::FORMATTING_NEWLINE) == "<html>\n<p>Test 1</p>\n<p>Test 2.5</p>\n<p>Test 3</p>\n</html>");
};
auto test4 = []() {
@ -83,7 +83,7 @@ SCENARIO("Test HTML", "[HTML]") {
section.erase(pos);
REQUIRE(section.get() == "<html><p>Test 1</p><p>Test 3</p></html>");
REQUIRE(section.get(docpp::HTML::FORMATTING_PRETTY) == "<html>\n<p>Test 1</p>\n<p>Test 3</p>\n</html>");
REQUIRE(section.get(docpp::HTML::FORMATTING_NEWLINE) == "<html>\n<p>Test 1</p>\n<p>Test 3</p>\n</html>");
};
auto test5 = []() {
@ -103,7 +103,7 @@ SCENARIO("Test HTML", "[HTML]") {
doc.set(section);
REQUIRE(doc.get() == "<!DOCTYPE html><html><div><p>Test 1</p><div><p>Test 2</p></div></div></html>");
REQUIRE(doc.get(docpp::HTML::FORMATTING_PRETTY) == "<!DOCTYPE html>\n<html>\n<div>\n<p>Test 1</p>\n<div>\n<p>Test 2</p>\n</div>\n</div>\n</html>");
REQUIRE(doc.get(docpp::HTML::FORMATTING_NEWLINE) == "<!DOCTYPE html>\n<html>\n<div>\n<p>Test 1</p>\n<div>\n<p>Test 2</p>\n</div>\n</div>\n</html>");
};
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() == "<!DOCTYPE html><html><p>Test 4</p><p>Test 5</p><p>Test 6</p><p>Test 7</p></html>");
};
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) == "<!DOCTYPE html>\n<html>\n\t<p>Test 1</p>\n\t<p>Test 2</p>\n\t<p>Test 3</p>\n</html>");
};
std::vector<void (*)()> tests{
test1,
test2,
@ -301,6 +313,7 @@ SCENARIO("Test HTML", "[HTML]") {
test14,
test15,
test16,
test17,
};
for (const auto& test : tests) {