diff --git a/src/publishing/CMakeLists.txt b/src/publishing/CMakeLists.txt index d0d8109..d6fee0e 100644 --- a/src/publishing/CMakeLists.txt +++ b/src/publishing/CMakeLists.txt @@ -9,6 +9,8 @@ list(APPEND publishing_HEADERS pdf/PdfStream.h pdf/PdfXRefTable.h pdf/PdfWriter.h + plotting/GraphPlotter.h + plotting/SvgConverter.h ) list(APPEND publishing_LIB_INCLUDES @@ -22,6 +24,8 @@ list(APPEND publishing_LIB_INCLUDES pdf/PdfStream.cpp pdf/PdfXRefTable.cpp pdf/PdfWriter.cpp + plotting/GraphPlotter.h + plotting/SvgConverter.h DocumentConverter.cpp ) @@ -32,6 +36,6 @@ target_include_directories(publishing PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/pdf" ) set_target_properties( publishing PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON ) -target_link_libraries( publishing PUBLIC core web) +target_link_libraries( publishing PUBLIC core web graphics visual_elements) set_property(TARGET publishing PROPERTY FOLDER src) \ No newline at end of file diff --git a/src/publishing/plotting/GraphPlotter.cpp b/src/publishing/plotting/GraphPlotter.cpp new file mode 100644 index 0000000..e69de29 diff --git a/src/publishing/plotting/GraphPlotter.h b/src/publishing/plotting/GraphPlotter.h new file mode 100644 index 0000000..e69de29 diff --git a/src/publishing/plotting/SvgConverter.cpp b/src/publishing/plotting/SvgConverter.cpp new file mode 100644 index 0000000..e69de29 diff --git a/src/publishing/plotting/SvgConverter.h b/src/publishing/plotting/SvgConverter.h new file mode 100644 index 0000000..e69de29 diff --git a/src/web/CMakeLists.txt b/src/web/CMakeLists.txt index a95294b..13f7e21 100644 --- a/src/web/CMakeLists.txt +++ b/src/web/CMakeLists.txt @@ -27,6 +27,11 @@ list(APPEND web_LIB_INCLUDES html/elements/HtmlHeadElement.cpp html/elements/HtmlBodyElement.cpp html/elements/HtmlParagraphElement.cpp + svg/SvgDocument.cpp + svg/SvgWriter.cpp + svg/SvgShapeElement.cpp + svg/SvgElement.cpp + svg/elements/SvgShapeElements.cpp ) # add the executable @@ -34,6 +39,8 @@ add_library(${MODULE_NAME} SHARED ${web_LIB_INCLUDES}) target_include_directories(web PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/svg + ${CMAKE_CURRENT_SOURCE_DIR}/svg/elements ${CMAKE_CURRENT_SOURCE_DIR}/xml ${CMAKE_CURRENT_SOURCE_DIR}/xml/xml-elements ${CMAKE_CURRENT_SOURCE_DIR}/html @@ -41,5 +48,5 @@ target_include_directories(web PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/markdown ) set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER src) -target_link_libraries(${MODULE_NAME} PUBLIC core compiler) +target_link_libraries(${MODULE_NAME} PUBLIC core compiler geometry) set_target_properties( ${MODULE_NAME} PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON ) \ No newline at end of file diff --git a/src/web/html/elements/HtmlParagraphElement.cpp b/src/web/html/elements/HtmlParagraphElement.cpp index 439f72a..ad0c45e 100644 --- a/src/web/html/elements/HtmlParagraphElement.cpp +++ b/src/web/html/elements/HtmlParagraphElement.cpp @@ -5,9 +5,8 @@ std::string HtmlParagraphElement::toString(unsigned depth, bool keepInline) cons const auto prefix = std::string(2*depth, ' '); auto content = prefix + "<" + getTagName(); - for (std::size_t idx=0; idx< getNumAttributes(); idx++) + for (const auto& [key, attribute] : getAttributes()) { - auto attribute = getAttribute(idx); content += " " + attribute->getName() + "=\"" + attribute->getValue() + "\""; } diff --git a/src/web/svg/SvgDocument.cpp b/src/web/svg/SvgDocument.cpp index e69de29..96ce451 100644 --- a/src/web/svg/SvgDocument.cpp +++ b/src/web/svg/SvgDocument.cpp @@ -0,0 +1,28 @@ +#include "SvgDocument.h" + +#include "XmlAttribute.h" + +#include + +SvgDocument::SvgDocument() + : XmlDocument() +{ + auto root = XmlElement::Create("svg"); + + auto xmlns = std::make_unique("xmlns"); + xmlns->setValue("http://www.w3.org/2000/svg"); + + root->addAttribute(std::move(xmlns)); + + setRoot(std::move(root)); +} + +void SvgDocument::setViewBox(unsigned x, unsigned y, unsigned w, unsigned h) +{ + auto viewbox = std::make_unique("viewBox"); + std::stringstream sstr; + sstr << x << " " << y << " " << w << " " << h; + viewbox->setValue(sstr.str()); + + getRoot()->addAttribute(std::move(viewbox)); +} diff --git a/src/web/svg/SvgDocument.h b/src/web/svg/SvgDocument.h index e69de29..c2ee0ed 100644 --- a/src/web/svg/SvgDocument.h +++ b/src/web/svg/SvgDocument.h @@ -0,0 +1,10 @@ +#pragma once + +#include "XmlDocument.h" + +class SvgDocument : public XmlDocument +{ +public: + SvgDocument(); + void setViewBox(unsigned x, unsigned y, unsigned w, unsigned h); +}; diff --git a/src/web/svg/SvgElement.cpp b/src/web/svg/SvgElement.cpp new file mode 100644 index 0000000..e75fc8e --- /dev/null +++ b/src/web/svg/SvgElement.cpp @@ -0,0 +1,7 @@ +#include "SvgElement.h" + +SvgElement::SvgElement(const std::string& tagName) + : XmlElement(tagName) +{ + +} diff --git a/src/web/svg/SvgElement.h b/src/web/svg/SvgElement.h new file mode 100644 index 0000000..6f33688 --- /dev/null +++ b/src/web/svg/SvgElement.h @@ -0,0 +1,9 @@ +#pragma once + +#include "XmlElement.h" + +class SvgElement : public XmlElement +{ +public: + SvgElement(const std::string& tagName); +}; diff --git a/src/web/svg/SvgShapeElement.cpp b/src/web/svg/SvgShapeElement.cpp new file mode 100644 index 0000000..669fa01 --- /dev/null +++ b/src/web/svg/SvgShapeElement.cpp @@ -0,0 +1,41 @@ +#include "SvgShapeElement.h" + +#include "XmlAttribute.h" + +#include + +SvgShapeElement::SvgShapeElement(const std::string& tagName) + : SvgElement(tagName) +{ + +} + +void SvgShapeElement::setFill(const Color& fill) +{ + auto attr = std::make_unique("fill"); + + std::stringstream sstr; + sstr << "rgb(" << fill.getR() << "," << fill.getG() << "," << fill.getB() << ")"; + attr->setValue(sstr.str()); + + addAttribute(std::move(attr)); +} + +void SvgShapeElement::setStrokeWidth(unsigned width) +{ + auto attr = std::make_unique("stroke-width"); + attr->setValue(std::to_string(width)); + + addAttribute(std::move(attr)); +} + +void SvgShapeElement::setStrokeColor(const Color& stroke) +{ + auto attr = std::make_unique("stroke"); + + std::stringstream sstr; + sstr << "rgb(" << stroke.getR() << "," << stroke.getG() << "," << stroke.getB() << ")"; + attr->setValue(sstr.str()); + + addAttribute(std::move(attr)); +} diff --git a/src/web/svg/SvgShapeElement.h b/src/web/svg/SvgShapeElement.h new file mode 100644 index 0000000..a0e34a8 --- /dev/null +++ b/src/web/svg/SvgShapeElement.h @@ -0,0 +1,17 @@ +#pragma once + +#include "SvgElement.h" +#include "Color.h" + +class SvgShapeElement : public SvgElement +{ +public: + SvgShapeElement(const std::string& tagName); + + void setFill(const Color& fill); + + void setStrokeWidth(unsigned width); + + void setStrokeColor(const Color& stroke); + +}; diff --git a/src/web/svg/SvgWriter.cpp b/src/web/svg/SvgWriter.cpp index e69de29..58fc5c2 100644 --- a/src/web/svg/SvgWriter.cpp +++ b/src/web/svg/SvgWriter.cpp @@ -0,0 +1,13 @@ +#include "SvgWriter.h" + +#include "SvgDocument.h" + +std::string SvgWriter::toString(SvgDocument* document) +{ + std::string content = ""; + if (auto root = document->getRoot()) + { + content += root->toString(); + } + return content; +} diff --git a/src/web/svg/SvgWriter.h b/src/web/svg/SvgWriter.h index e69de29..7990384 100644 --- a/src/web/svg/SvgWriter.h +++ b/src/web/svg/SvgWriter.h @@ -0,0 +1,11 @@ +#pragma once + +#include + +class SvgDocument; + +class SvgWriter +{ +public: + std::string toString(SvgDocument* document); +}; diff --git a/src/web/svg/elements/SvgShapeElements.cpp b/src/web/svg/elements/SvgShapeElements.cpp new file mode 100644 index 0000000..297e7d3 --- /dev/null +++ b/src/web/svg/elements/SvgShapeElements.cpp @@ -0,0 +1,62 @@ +#include "SvgShapeElements.h" + +SvgCircle::SvgCircle() + : SvgShapeElement("circle") +{ + +} + +void SvgCircle::setLocation(const DiscretePoint& loc) +{ + auto cx = std::make_unique("cx"); + auto cy = std::make_unique("cy"); + + cx->setValue(std::to_string(loc.GetX())); + cy->setValue(std::to_string(loc.GetY())); + + addAttribute(std::move(cx)); + addAttribute(std::move(cy)); +} + +void SvgCircle::setRadius(unsigned rad) +{ + auto r = std::make_unique("r"); + r->setValue(std::to_string(rad)); + addAttribute(std::move(r)); +} + +SvgRectangle::SvgRectangle() + : SvgShapeElement("rect") +{ + +} + +void SvgRectangle::setLocation(const DiscretePoint& loc) +{ + auto x = std::make_unique("x"); + auto y = std::make_unique("y"); + + x->setValue(std::to_string(loc.GetX())); + y->setValue(std::to_string(loc.GetY())); + + addAttribute(std::move(x)); + addAttribute(std::move(y)); +} + +void SvgRectangle::setWidth(unsigned w) +{ + auto width = std::make_unique("width"); + + width->setValue(std::to_string(w)); + + addAttribute(std::move(width)); +} + +void SvgRectangle::setHeight(unsigned h) +{ + auto height = std::make_unique("height"); + + height->setValue(std::to_string(h)); + + addAttribute(std::move(height)); +} diff --git a/src/web/svg/elements/SvgShapeElements.h b/src/web/svg/elements/SvgShapeElements.h new file mode 100644 index 0000000..66545e7 --- /dev/null +++ b/src/web/svg/elements/SvgShapeElements.h @@ -0,0 +1,28 @@ +#pragma once + +#include "SvgShapeElement.h" +#include "DiscretePoint.h" + +#include "XmlAttribute.h" + +class SvgCircle : public SvgShapeElement +{ +public: + SvgCircle(); + + void setLocation(const DiscretePoint& loc); + + void setRadius(unsigned rad); +}; + +class SvgRectangle : public SvgShapeElement +{ +public: + SvgRectangle(); + + void setLocation(const DiscretePoint& loc); + + void setWidth(unsigned width); + + void setHeight(unsigned height); +}; diff --git a/src/web/xml/XmlWriter.cpp b/src/web/xml/XmlWriter.cpp index ef5e76f..a9e741f 100644 --- a/src/web/xml/XmlWriter.cpp +++ b/src/web/xml/XmlWriter.cpp @@ -9,9 +9,8 @@ std::string XmlWriter::toString(XmlDocument* document) if (auto prolog = document->getProlog()) { content += "getNumAttributes(); idx++) + for (const auto& [key, attribute] : prolog->getAttributes()) { - auto attribute = prolog->getAttribute(idx); content += " " + attribute->getName() + "=\"" + attribute->getValue() + "\""; } content += "?>\n"; diff --git a/src/web/xml/xml-elements/XmlElement.cpp b/src/web/xml/xml-elements/XmlElement.cpp index d2d7b20..d0ed92a 100644 --- a/src/web/xml/xml-elements/XmlElement.cpp +++ b/src/web/xml/xml-elements/XmlElement.cpp @@ -31,7 +31,7 @@ void XmlElement::addChild(XmlElementPtr child) void XmlElement::addAttribute(XmlAttributePtr attribute) { - mAttributes.push_back(std::move(attribute)); + mAttributes[attribute->getName()] = std::move(attribute); } const std::string& XmlElement::getTagName() const @@ -64,28 +64,16 @@ XmlElement* XmlElement::getFirstChildWithTagName(const std::string& tag) XmlAttribute* XmlElement::getAttribute(const std::string& attributeName) const { - for(const auto& attribute : mAttributes) + if (auto iter = mAttributes.find(attributeName); iter != mAttributes.end()) { - if(attribute->getName() == attributeName) - { - return attribute.get(); - } + return iter->second.get(); } return nullptr; } -XmlAttribute* XmlElement::getAttribute(std::size_t index) const +const std::unordered_map& XmlElement::getAttributes() const { - if(index < mAttributes.size()) - { - return mAttributes[index].get(); - } - return nullptr; -} - -std::size_t XmlElement::getNumAttributes() const -{ - return mAttributes.size(); + return mAttributes; } std::size_t XmlElement::getNumChildren() const @@ -105,9 +93,8 @@ std::string XmlElement::toString(unsigned depth, bool keepInline) const std::string line_ending = keepInline ? "" : "\n"; auto content = prefix + "<" + getTagName(); - for (std::size_t idx=0; idx< getNumAttributes(); idx++) + for (const auto& [key, attribute] : getAttributes()) { - auto attribute = getAttribute(idx); content += " " + attribute->getName() + "=\"" + attribute->getValue() + "\""; } diff --git a/src/web/xml/xml-elements/XmlElement.h b/src/web/xml/xml-elements/XmlElement.h index fb1b1f1..05b3958 100644 --- a/src/web/xml/xml-elements/XmlElement.h +++ b/src/web/xml/xml-elements/XmlElement.h @@ -1,10 +1,9 @@ #pragma once - - #include #include #include +#include class XmlAttribute; using XmlAttributePtr = std::unique_ptr; @@ -23,8 +22,7 @@ public: const std::string& getTagName() const; const std::string& getText() const; XmlAttribute* getAttribute(const std::string& attribute) const; - XmlAttribute* getAttribute(std::size_t index) const; - std::size_t getNumAttributes() const; + const std::unordered_map& getAttributes() const; std::size_t getNumChildren() const; XmlElement* getChild(std::size_t index) const; @@ -40,7 +38,7 @@ protected: std::string mTagName; std::string mText; - std::vector mAttributes; + std::unordered_map mAttributes; std::vector > mChildren; }; diff --git a/test/web/CMakeLists.txt b/test/web/CMakeLists.txt index 363ad0a..1dbcc83 100644 --- a/test/web/CMakeLists.txt +++ b/test/web/CMakeLists.txt @@ -1,6 +1,7 @@ set(WEB_UNIT_TEST_FILES web/TestMarkdownParser.cpp web/TestXmlParser.cpp + web/TestSvgWriter.cpp PARENT_SCOPE ) diff --git a/test/web/TestSvgWriter.cpp b/test/web/TestSvgWriter.cpp new file mode 100644 index 0000000..05149c0 --- /dev/null +++ b/test/web/TestSvgWriter.cpp @@ -0,0 +1,33 @@ +#include "SvgWriter.h" +#include "SvgDocument.h" +#include "SvgShapeElements.h" + +#include "File.h" + +#include "TestFramework.h" +#include "TestUtils.h" + +TEST_CASE(TestSvgWriter, "web") +{ + auto document = std::make_unique(); + document->setViewBox(0, 0, 100, 100); + + auto circle = std::make_unique(); + circle->setRadius(20); + circle->setLocation({10, 30}); + circle->setFill({255, 0, 0}); + document->getRoot()->addChild(std::move(circle)); + + auto rectangle = std::make_unique(); + rectangle->setLocation({50, 70}); + rectangle->setWidth(5); + rectangle->setHeight(10); + rectangle->setFill({0, 0, 255}); + document->getRoot()->addChild(std::move(rectangle)); + + SvgWriter writer; + auto content = writer.toString(document.get()); + + auto outFile = std::make_unique(TestUtils::getTestOutputDir(__FILE__) / "test_out.svg"); + outFile->writeText(content); +}