diff --git a/src/base/geometry/Transform.cpp b/src/base/geometry/Transform.cpp index 026ccd9..8eb9585 100644 --- a/src/base/geometry/Transform.cpp +++ b/src/base/geometry/Transform.cpp @@ -27,4 +27,17 @@ double Transform::getScaleY() const double Transform::getScaleZ() const { return mScaleZ; +} + +void Transform::setLocation(const Point& loc) +{ + mLocation = loc; +} + +void Transform::setScale(double scaleX, double scaleY, double scaleZ) +{ + mScaleX = scaleX; + mScaleY = scaleY; + mScaleZ = scaleZ; + } \ No newline at end of file diff --git a/src/base/geometry/Transform.h b/src/base/geometry/Transform.h index 208e4ce..e482d3b 100644 --- a/src/base/geometry/Transform.h +++ b/src/base/geometry/Transform.h @@ -7,6 +7,10 @@ class Transform public: Transform(const Point& location = {}, double scaleX = 1.0, double scaleY = 1.0, double scaleZ = 1.0); + void setLocation(const Point& loc); + + void setScale(double scaleX, double scaleY = 1.0, double scaleZ = 1.0); + const Point& getLocation() const; double getScaleX() const; diff --git a/src/base/geometry/points/Point.cpp b/src/base/geometry/points/Point.cpp index bb20266..95f4c52 100644 --- a/src/base/geometry/points/Point.cpp +++ b/src/base/geometry/points/Point.cpp @@ -89,3 +89,10 @@ void Point::apply(const Transform& transform) mY *= transform.getScaleY(); mZ *= transform.getScaleZ(); } + +void Point::move(double x, double y, double z) +{ + mX += x; + mY += y; + mZ += z; +} diff --git a/src/base/geometry/points/Point.h b/src/base/geometry/points/Point.h index 5f8b53d..92f8bc7 100644 --- a/src/base/geometry/points/Point.h +++ b/src/base/geometry/points/Point.h @@ -23,6 +23,8 @@ public: void apply(const Transform& transform); + void move(double x, double y, double z = 0); + double getX() const; double getY() const; diff --git a/src/publishing/CMakeLists.txt b/src/publishing/CMakeLists.txt index a25150f..766fd8f 100644 --- a/src/publishing/CMakeLists.txt +++ b/src/publishing/CMakeLists.txt @@ -10,12 +10,13 @@ list(APPEND publishing_HEADERS pdf/PdfXRefTable.h pdf/PdfWriter.h plotting/GraphPlotter.h - plotting/SvgConverter.h plotting/PlotNode.h plotting/EquationNode.h latex/LatexDocument.h latex/LatexMathExpression.h latex/LatexSymbols.h + svg/SvgNode.h + svg/SvgPainter.h DocumentConverter.h ) @@ -34,10 +35,11 @@ list(APPEND publishing_LIB_INCLUDES latex/LatexMathExpression.cpp latex/LatexSymbols.cpp plotting/GraphPlotter.h - plotting/SvgConverter.cpp plotting/PlotNode.cpp plotting/EquationNode.cpp DocumentConverter.cpp + svg/SvgNode.cpp + svg/SvgPainter.cpp ) add_library(publishing SHARED ${publishing_LIB_INCLUDES} ${publishing_INCLUDES} ${publishing_HEADERS}) @@ -47,6 +49,7 @@ target_include_directories(publishing PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/pdf ${CMAKE_CURRENT_SOURCE_DIR}/plotting ${CMAKE_CURRENT_SOURCE_DIR}/latex + ${CMAKE_CURRENT_SOURCE_DIR}/svg ) set_target_properties( publishing PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON ) target_link_libraries( publishing PUBLIC core web graphics visual_elements) diff --git a/src/publishing/plotting/SvgConverter.h b/src/publishing/plotting/SvgConverter.h deleted file mode 100644 index 716bf1b..0000000 --- a/src/publishing/plotting/SvgConverter.h +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once - -#include - -class SvgDocument; -class SvgShapeElement; -class XmlAttribute; - -class Scene; -class SceneModel; -class SceneText; - -class Transform; - -class SvgConverter -{ -public: - std::unique_ptr convert(Scene* scene, double width = 800.0, double height = 800.0) const; - -private: - void convertMesh(SvgDocument* document, SceneModel* model, bool showOutline = false) const; - - void convertPrimitive(SvgDocument* document, SceneModel* model) const; - - void convertText(SvgDocument* document, SceneText* model) const; - - void setStyle(SceneModel* model, SvgShapeElement* element) const; - - std::unique_ptr toTransform(const Transform& transform) const; -}; diff --git a/src/publishing/svg/SvgNode.cpp b/src/publishing/svg/SvgNode.cpp new file mode 100644 index 0000000..91c498a --- /dev/null +++ b/src/publishing/svg/SvgNode.cpp @@ -0,0 +1,90 @@ +#include "SvgNode.h" + +#include "CircleNode.h" + +#include "SvgShapeElements.h" + +SvgNode::SvgNode(const Point& location) + : AbstractVisualNode(location) +{ + +} + +void SvgNode::setContent(std::unique_ptr doc) +{ + mContent = std::move(doc); + mContentDirty = true; + + mChildren.clear(); + mManagedChildren.clear(); +} + +void SvgNode::updateTransform() +{ + +} + +void SvgNode::createOrUpdateGeometry(SceneInfo* sceneInfo) +{ + if (!mContent->getRoot()) + { + return; + } + + auto viewbox = mContent->getViewBox(); + + for (const auto& svg_element : mContent->getRoot()->getChildren()) + { + std::unique_ptr node; + + if (svg_element->getTagName() == "circle") + { + auto svg_circle = dynamic_cast(svg_element.get()); + auto loc = svg_circle->getLocation(); + auto radius = svg_circle->getRadius(); + auto minor_radius = radius; + + if (svg_circle->getType() == SvgCircle::Type::ELLIPSE) + { + minor_radius = svg_circle->getMinorRadius(); + } + + if (svg_element->hasAttribute("transform")) + { + const auto transform = svg_circle->getTransform(); + loc.move(transform.getLocation().getX(), transform.getLocation().getY()); + radius *= transform.getScaleX(); + minor_radius *= transform.getScaleY(); + } + auto circle_node = std::make_unique(loc, radius); + circle_node->setMinorRadius(minor_radius); + node = std::move(circle_node); + } + + if (!node) + { + continue; + } + + auto raw_node = node.get(); + mManagedChildren.push_back(std::move(node)); + + addChild(raw_node); + } + +} + +void SvgNode::update(SceneInfo* sceneInfo) +{ + if (mContent && mContentDirty) + { + createOrUpdateGeometry(sceneInfo); + mContentDirty = false; + } + + if (mTransformIsDirty) + { + updateTransform(); + mTransformIsDirty = false; + } +} \ No newline at end of file diff --git a/src/publishing/svg/SvgNode.h b/src/publishing/svg/SvgNode.h new file mode 100644 index 0000000..c2c02b5 --- /dev/null +++ b/src/publishing/svg/SvgNode.h @@ -0,0 +1,25 @@ +#pragma once + +#include "AbstractVisualNode.h" +#include "SvgDocument.h" + +#include +#include + +class SvgNode : public AbstractVisualNode +{ +public: + SvgNode(const Point& location); + + void setContent(std::unique_ptr doc); + + void update(SceneInfo* sceneInfo); +private: + void createOrUpdateGeometry(SceneInfo* sceneInfo); + void updateTransform(); + + bool mContentDirty{ true }; + + std::vector > mManagedChildren; + std::unique_ptr mContent; +}; diff --git a/src/publishing/plotting/SvgConverter.cpp b/src/publishing/svg/SvgPainter.cpp similarity index 84% rename from src/publishing/plotting/SvgConverter.cpp rename to src/publishing/svg/SvgPainter.cpp index 72b9d3f..02a12b4 100644 --- a/src/publishing/plotting/SvgConverter.cpp +++ b/src/publishing/svg/SvgPainter.cpp @@ -1,4 +1,4 @@ -#include "SvgConverter.h" +#include "SvgPainter.h" #include "SvgDocument.h" #include "Scene.h" @@ -17,38 +17,38 @@ #include "SvgShapeElements.h" #include "XmlAttribute.h" -std::unique_ptr SvgConverter::convert(Scene* scene, double width, double height) const +std::unique_ptr SvgPainter::paint(Scene* scene, double width, double height) const { auto doc = std::make_unique(); doc->setViewBox(0.0, 0.0, width, height); scene->update(); - for(auto item : scene->getItems()) + for (auto item : scene->getItems()) { if (item->getType() == SceneItem::Type::MODEL) { auto model = dynamic_cast(item); if (model->getGeometry()) { - convertPrimitive(doc.get(), model); + paintPrimitive(doc.get(), model); } else { - convertMesh(doc.get(), model, scene->shouldShowMeshOutline()); + paintMesh(doc.get(), model, scene->shouldShowMeshOutline()); } } else { auto text = dynamic_cast(item); - convertText(doc.get(), text); + paintText(doc.get(), text); } } return std::move(doc); } -void SvgConverter::convertMesh(SvgDocument* document, SceneModel* model, bool showOutline) const +void SvgPainter::paintMesh(SvgDocument* document, SceneModel* model, bool showOutline) const { auto mesh = model->getMesh(); auto transform = model->getTransform(); @@ -87,7 +87,7 @@ void SvgConverter::convertMesh(SvgDocument* document, SceneModel* model, bool sh } } -void SvgConverter::setStyle(SceneModel* model, SvgShapeElement* element) const +void SvgPainter::setStyle(SceneModel* model, SvgShapeElement* element) const { auto transform = model->getTransform(); @@ -111,7 +111,7 @@ void SvgConverter::setStyle(SceneModel* model, SvgShapeElement* element) const element->addAttribute(std::move(toTransform(transform))); } -void SvgConverter::convertPrimitive(SvgDocument* document, SceneModel* model) const +void SvgPainter::paintPrimitive(SvgDocument* document, SceneModel* model) const { if (model->getGeometry()->getType() == AbstractGeometricItem::Type::RECTANGLE) { @@ -139,12 +139,12 @@ void SvgConverter::convertPrimitive(SvgDocument* document, SceneModel* model) co } } -void SvgConverter::convertText(SvgDocument* document, SceneText* model) const +void SvgPainter::paintText(SvgDocument* document, SceneText* model) const { } -std::unique_ptr SvgConverter::toTransform(const Transform& transform) const +std::unique_ptr SvgPainter::toTransform(const Transform& transform) const { auto svg_transform = std::make_unique("transform"); diff --git a/src/publishing/svg/SvgPainter.h b/src/publishing/svg/SvgPainter.h new file mode 100644 index 0000000..cbf9cfa --- /dev/null +++ b/src/publishing/svg/SvgPainter.h @@ -0,0 +1,32 @@ +#pragma once + +#include "AbstractPainter.h" + +#include + +class SvgDocument; +class SvgShapeElement; +class XmlAttribute; + +class Scene; +class SceneModel; +class SceneText; + +class Transform; + +class SvgPainter +{ +public: + std::unique_ptr paint(Scene* scene, double width = 800.0, double height = 800.0) const; + +private: + void paintMesh(SvgDocument* document, SceneModel* model, bool showOutline = false) const; + + void paintPrimitive(SvgDocument* document, SceneModel* model) const; + + void paintText(SvgDocument* document, SceneText* model) const; + + void setStyle(SceneModel* model, SvgShapeElement* element) const; + + std::unique_ptr toTransform(const Transform& transform) const; +}; diff --git a/src/rendering/graphics/directx/DirectX2dPainter.cpp b/src/rendering/graphics/directx/DirectX2dPainter.cpp index 7150ce0..07b38fe 100644 --- a/src/rendering/graphics/directx/DirectX2dPainter.cpp +++ b/src/rendering/graphics/directx/DirectX2dPainter.cpp @@ -3,6 +3,8 @@ #include "DirectX2dInterface.h" #include "AbstractGeometricItem.h" #include "Rectangle.h" +#include "Circle.h" + #include "SceneModel.h" void DirectX2dPainter::startDrawing() @@ -49,6 +51,25 @@ void DirectX2dPainter::paint(SceneModel* model) rt->DrawRectangle(d2d_rect, mSolidBrush.Get(), 1.0f); } } + else if (model->getGeometry()->getType() == AbstractGeometricItem::Type::CIRCLE) + { + const auto loc = model->getTransform().getLocation(); + const auto scale_x = model->getTransform().getScaleX(); + const auto scale_y = model->getTransform().getScaleY(); + + auto circle = dynamic_cast(model->getGeometry()); + const auto radius = circle->getRadius() * scale_x; + const auto radiusy = circle->getMinorRadius() * scale_y; + + D2D1_POINT_2F d2d_centre{ static_cast(loc.getX()), static_cast(loc.getY()) }; + D2D1_ELLIPSE ellipse{ d2d_centre, static_cast(radius), static_cast(radiusy) }; + + if (model->hasOutlineColor()) + { + mSolidBrush->SetColor(toD2dColor(model->getOutlineColor())); + rt->DrawEllipse(ellipse, mSolidBrush.Get(), 1.0f); + } + } } void DirectX2dPainter::setD2dInterface(DirectX2dInterface* d2dIterface) diff --git a/src/web/CMakeLists.txt b/src/web/CMakeLists.txt index b01dc6e..992ae84 100644 --- a/src/web/CMakeLists.txt +++ b/src/web/CMakeLists.txt @@ -29,10 +29,12 @@ list(APPEND web_LIB_INCLUDES html/elements/HtmlParagraphElement.cpp svg/SvgDocument.h svg/SvgWriter.h + svg/SvgReader.h svg/SvgShapeElement.h svg/SvgElement.h svg/elements/SvgShapeElements.h svg/SvgDocument.cpp + svg/SvgReader.cpp svg/SvgWriter.cpp svg/SvgShapeElement.cpp svg/SvgElement.cpp diff --git a/src/web/html/elements/HtmlParagraphElement.cpp b/src/web/html/elements/HtmlParagraphElement.cpp index ad0c45e..9b961d1 100644 --- a/src/web/html/elements/HtmlParagraphElement.cpp +++ b/src/web/html/elements/HtmlParagraphElement.cpp @@ -10,7 +10,7 @@ std::string HtmlParagraphElement::toString(unsigned depth, bool keepInline) cons content += " " + attribute->getName() + "=\"" + attribute->getValue() + "\""; } - const auto num_children = getNumChildren(); + const auto num_children = mChildren.size(); if (num_children == 0 && getText().empty()) { content += "/>\n"; @@ -30,9 +30,9 @@ std::string HtmlParagraphElement::toString(unsigned depth, bool keepInline) cons { content += "\n"; } - for (std::size_t idx=0; idx< getNumChildren(); idx++) + + for(const auto& child : mChildren) { - auto child = getChild(idx); content += child->toString(depth+1, true); } if (num_children>0) diff --git a/src/web/svg/SvgDocument.cpp b/src/web/svg/SvgDocument.cpp index 87b2f84..a28447e 100644 --- a/src/web/svg/SvgDocument.cpp +++ b/src/web/svg/SvgDocument.cpp @@ -1,6 +1,7 @@ #include "SvgDocument.h" #include "XmlAttribute.h" +#include "StringUtils.h" #include @@ -19,10 +20,39 @@ SvgDocument::SvgDocument() void SvgDocument::setViewBox(double x, double y, double w, double h) { - auto viewbox = std::make_unique("viewBox"); std::stringstream sstr; sstr << x << " " << y << " " << w << " " << h; - viewbox->setValue(sstr.str()); + setViewBox(sstr.str()); +} +void SvgDocument::setViewBox(const std::string& data) +{ + auto viewbox = std::make_unique("viewBox"); + viewbox->setValue(data); getRoot()->addAttribute(std::move(viewbox)); } + +SvgDocument::ViewBox SvgDocument::getViewBox() const +{ + ViewBox viewBox; + + if (!getRoot()) + { + return viewBox; + } + + if (auto vb_attr = getRoot()->getAttribute("viewBox"); vb_attr) + { + auto entries = StringUtils::split(vb_attr->getValue()); + if (entries.size() != 4) + { + return viewBox; + } + + viewBox.mX = std::stod(entries[0]); + viewBox.mY = std::stod(entries[1]); + viewBox.mW = std::stod(entries[2]); + viewBox.mH = std::stod(entries[3]); + } + return viewBox; +} diff --git a/src/web/svg/SvgDocument.h b/src/web/svg/SvgDocument.h index a7fb0d1..7beae47 100644 --- a/src/web/svg/SvgDocument.h +++ b/src/web/svg/SvgDocument.h @@ -5,6 +5,18 @@ class SvgDocument : public XmlDocument { public: + struct ViewBox + { + double mX{ 0 }; + double mY{ 0 }; + double mW{ 0 }; + double mH{ 0 }; + }; + SvgDocument(); + + ViewBox getViewBox() const; + + void setViewBox(const std::string& data); void setViewBox(double x, double y, double w, double h); }; diff --git a/src/web/svg/SvgReader.cpp b/src/web/svg/SvgReader.cpp new file mode 100644 index 0000000..d82938d --- /dev/null +++ b/src/web/svg/SvgReader.cpp @@ -0,0 +1,76 @@ +#include "SvgReader.h" + +#include "File.h" +#include "XmlParser.h" +#include "XmlAttribute.h" + +#include "SvgShapeElements.h" + +#include "FileLogger.h" + +std::unique_ptr SvgReader::read(const Path& path) const +{ + auto svg_doc = std::make_unique(); + + File in_file(path); + auto lines = in_file.readLines(); + + XmlParser parser; + for (const auto& line : lines) + { + parser.processLine(line); + } + + auto xml_doc = parser.getDocument(); + + if (xml_doc->getRoot() && xml_doc->getRoot()->getTagName() == "svg") + { + onRoot(xml_doc->getRoot(), svg_doc.get()); + } + return svg_doc; +} + +void SvgReader::onRoot(XmlElement* element, SvgDocument* doc) const +{ + for (const auto& attr : element->getAttributes()) + { + doc->getRoot()->addAttribute(attr.first, attr.second->getValue()); + } + + for (const auto& child : element->getChildren()) + { + onChild(child.get(), doc->getRoot()); + } +} + +void SvgReader::onChild(XmlElement* element, XmlElement* svg_parent) const +{ + std::unique_ptr new_svg; + if (element->getTagName() == "circle") + { + new_svg = std::make_unique(); + } + else if (element->getTagName() == "ellipse") + { + new_svg = std::make_unique(SvgCircle::Type::ELLIPSE); + } + else if (element->getTagName() == "rect") + { + new_svg = std::make_unique(); + } + else + { + return; + } + + for (const auto& attr : element->getAttributes()) + { + new_svg->addAttribute(attr.first, attr.second->getValue()); + } + + for (const auto& child : element->getChildren()) + { + onChild(child.get(), new_svg.get()); + } + svg_parent->addChild(std::move(new_svg)); +} \ No newline at end of file diff --git a/src/web/svg/SvgReader.h b/src/web/svg/SvgReader.h new file mode 100644 index 0000000..d7f709e --- /dev/null +++ b/src/web/svg/SvgReader.h @@ -0,0 +1,20 @@ +#pragma once + +#include "SvgDocument.h" + +#include + +using Path = std::filesystem::path; + +class SvgElement; + +class SvgReader +{ +public: + std::unique_ptr read(const Path& path) const; + +private: + void onRoot(XmlElement* element, SvgDocument* doc) const; + + void onChild(XmlElement* element, XmlElement* svg_parent) const; +}; \ No newline at end of file diff --git a/src/web/svg/SvgShapeElement.cpp b/src/web/svg/SvgShapeElement.cpp index ab23a00..864f609 100644 --- a/src/web/svg/SvgShapeElement.cpp +++ b/src/web/svg/SvgShapeElement.cpp @@ -1,6 +1,7 @@ #include "SvgShapeElement.h" #include "XmlAttribute.h" +#include "StringUtils.h" #include @@ -10,6 +11,96 @@ SvgShapeElement::SvgShapeElement(const std::string& tagName) } +Transform SvgShapeElement::getTransform() const +{ + Transform transform; + const std::string translate_key = "translate"; + const std::string scale_key = "scale"; + + if (auto attr = getAttribute("transform"); attr) + { + const auto val = attr->getValue(); + std::string working_string; + bool in_translate = false; + bool in_scale = false; + for (auto c : val) + { + if (StringUtils::stripSurroundingWhitepsace(working_string) == translate_key) + { + in_translate = true; + working_string.clear(); + continue; + } + else if (StringUtils::stripSurroundingWhitepsace(working_string) == scale_key) + { + in_scale = true; + working_string.clear(); + continue; + } + else if (c == '(') + { + continue; + } + else if (c == ')') + { + if (in_translate) + { + const auto loc = parsePoint(working_string, 0.0); + transform.setLocation(loc); + in_translate = false; + } + else if (in_scale) + { + const auto loc = parsePoint(working_string, 1.0); + transform.setScale(loc.getX(), loc.getY(), loc.getZ()); + in_scale = false; + } + working_string.clear(); + } + else + { + working_string += c; + } + } + } + return transform; +} + +std::string SvgShapeElement::getLabelledContent(const std::string& key, const std::string& content) const +{ + return content.substr(key.size(), content.size() - key.size() - 2); +} + +Point SvgShapeElement::parsePoint(const std::string& pointString, double defaultVal) const +{ + double x = defaultVal; + double y = defaultVal; + double z = defaultVal; + + const auto split = StringUtils::split(pointString); + for (std::size_t idx = 0; idx < split.size(); idx++) + { + if (idx == 0) + { + x = std::stod(split[idx]); + } + else if (idx == 1) + { + y = std::stod(split[idx]); + } + else if (idx == 2) + { + z = std::stod(split[idx]); + } + } + return { x, y, z }; +} + +bool SvgShapeElement::hasTransform() const +{ + return bool(getAttribute("transform")); +} + void SvgShapeElement::setNoFill() { auto attr = std::make_unique("fill"); diff --git a/src/web/svg/SvgShapeElement.h b/src/web/svg/SvgShapeElement.h index 94e394a..869e2da 100644 --- a/src/web/svg/SvgShapeElement.h +++ b/src/web/svg/SvgShapeElement.h @@ -2,12 +2,17 @@ #include "SvgElement.h" #include "Color.h" +#include "Transform.h" class SvgShapeElement : public SvgElement { public: SvgShapeElement(const std::string& tagName); + Transform getTransform() const; + + bool hasTransform() const; + void setFill(const Color& fill); void setNoFill(); @@ -17,4 +22,8 @@ public: void setStrokeColor(const Color& stroke); void setNoStroke(); + +private: + std::string getLabelledContent(const std::string& key, const std::string& content) const; + Point parsePoint(const std::string& pointString, double defaultVal = 0.0) const; }; diff --git a/src/web/svg/SvgWriter.cpp b/src/web/svg/SvgWriter.cpp index 58fc5c2..f7de44c 100644 --- a/src/web/svg/SvgWriter.cpp +++ b/src/web/svg/SvgWriter.cpp @@ -1,8 +1,9 @@ #include "SvgWriter.h" #include "SvgDocument.h" +#include "File.h" -std::string SvgWriter::toString(SvgDocument* document) +std::string SvgWriter::toString(SvgDocument* document) const { std::string content = ""; if (auto root = document->getRoot()) @@ -11,3 +12,9 @@ std::string SvgWriter::toString(SvgDocument* document) } return content; } + +void SvgWriter::toFile(const Path& path, SvgDocument* document) const +{ + File out_file(path); + out_file.writeText(toString(document)); +} diff --git a/src/web/svg/SvgWriter.h b/src/web/svg/SvgWriter.h index 7990384..dcbb8a5 100644 --- a/src/web/svg/SvgWriter.h +++ b/src/web/svg/SvgWriter.h @@ -1,11 +1,16 @@ #pragma once #include +#include + +using Path = std::filesystem::path; class SvgDocument; class SvgWriter { public: - std::string toString(SvgDocument* document); + std::string toString(SvgDocument* document) const; + + void toFile(const Path& path, SvgDocument* document) const; }; diff --git a/src/web/svg/elements/SvgShapeElements.cpp b/src/web/svg/elements/SvgShapeElements.cpp index 9522f7b..40ca60d 100644 --- a/src/web/svg/elements/SvgShapeElements.cpp +++ b/src/web/svg/elements/SvgShapeElements.cpp @@ -9,6 +9,50 @@ SvgCircle::SvgCircle(Type type) } +Point SvgCircle::getLocation() const +{ + double cx = 0.0; + double cy = 0.0; + if (auto attr = getAttribute("cx"); attr) + { + cx = std::stod(attr->getValue()); + } + if (auto attr = getAttribute("cy"); attr) + { + cy = std::stod(attr->getValue()); + } + return { cx, cy }; +} + +double SvgCircle::getRadius() const +{ + double radius = 1.0; + if (auto attr = getAttribute("rx"); attr) + { + radius = std::stod(attr->getValue()); + } + else if (auto attr = getAttribute("r"); attr) + { + radius = std::stod(attr->getValue()); + } + return radius; +} + +SvgCircle::Type SvgCircle::getType() const +{ + return mType; +} + +double SvgCircle::getMinorRadius() const +{ + double radius = 0.0; + if (auto attr = getAttribute("ry"); attr) + { + radius = std::stod(attr->getValue()); + } + return radius; +} + void SvgCircle::setLocation(const Point& loc) { auto cx = std::make_unique("cx"); diff --git a/src/web/svg/elements/SvgShapeElements.h b/src/web/svg/elements/SvgShapeElements.h index 728f1d2..eb374b8 100644 --- a/src/web/svg/elements/SvgShapeElements.h +++ b/src/web/svg/elements/SvgShapeElements.h @@ -16,6 +16,14 @@ public: SvgCircle(Type type = Type::REGULAR); + Point getLocation() const; + + double getRadius() const; + + Type getType() const; + + double getMinorRadius() const; + void setLocation(const Point& loc); void setRadius(double rad); diff --git a/src/web/xml/XmlParser.cpp b/src/web/xml/XmlParser.cpp index 1f5d1dc..fc1cb8f 100644 --- a/src/web/xml/XmlParser.cpp +++ b/src/web/xml/XmlParser.cpp @@ -193,6 +193,7 @@ void XmlParser::onRightBracket() case LS::Await_Attribute_Name: case LS::Await_Attribute_Name_End: case LS::Await_Attribute_Value: + case LS::Await_Tag_Inline_Close: { onTagClose(); break; @@ -220,13 +221,21 @@ void XmlParser::onQuestionMark() void XmlParser::onForwardSlash() { - if(mLineState == LS::Await_Tag_Follower) + if (mLineState == LS::Await_Tag_Follower) { - if(mDocumentState == DS::Await_Element || mDocumentState == DS::Build_Element) + if (mDocumentState == DS::Await_Element || mDocumentState == DS::Build_Element) { mDocumentState = DS::Close_Element; } } + else if(mLineState == LS::Await_Attribute_Name) + { + mLineState = LS::Await_Tag_Inline_Close; + } + else + { + onNonAlphaNumeric(StringUtils::FORWARD_SLASH); + } } void XmlParser::onEquals() @@ -270,6 +279,13 @@ void XmlParser::onTagClose() { onTagNameEnd(); } + else if (mLineState == LS::Await_Tag_Inline_Close) + { + if (!mWorkingElements.empty()) + { + mWorkingElements.pop(); + } + } onElementTagEnd(); } else if(mDocumentState == DS::Close_Element) diff --git a/src/web/xml/XmlParser.h b/src/web/xml/XmlParser.h index f152aeb..3007641 100644 --- a/src/web/xml/XmlParser.h +++ b/src/web/xml/XmlParser.h @@ -27,6 +27,7 @@ public: Await_Tag_Follower, Await_Tag_Name, Await_Tag_Name_End, + Await_Tag_Inline_Close, Await_Attribute_Name, Await_Attribute_Name_End, Await_Attribute_Value, @@ -82,8 +83,6 @@ private: void onAttributeValueEnd(); - void onPrologId(); - void onStartProlog(); void onFinishProlog(); diff --git a/src/web/xml/xml-elements/XmlElement.cpp b/src/web/xml/xml-elements/XmlElement.cpp index d0ed92a..7cdd4cd 100644 --- a/src/web/xml/xml-elements/XmlElement.cpp +++ b/src/web/xml/xml-elements/XmlElement.cpp @@ -34,6 +34,13 @@ void XmlElement::addAttribute(XmlAttributePtr attribute) mAttributes[attribute->getName()] = std::move(attribute); } +void XmlElement::addAttribute(const std::string& name, const std::string& value) +{ + auto attr = std::make_unique(name); + attr->setValue(value); + addAttribute(std::move(attr)); +} + const std::string& XmlElement::getTagName() const { return mTagName; @@ -62,6 +69,11 @@ XmlElement* XmlElement::getFirstChildWithTagName(const std::string& tag) return nullptr; } +bool XmlElement::hasAttribute(const std::string& attribute) const +{ + return (bool)(getAttribute(attribute)); +} + XmlAttribute* XmlElement::getAttribute(const std::string& attributeName) const { if (auto iter = mAttributes.find(attributeName); iter != mAttributes.end()) @@ -76,14 +88,9 @@ const std::unordered_map& XmlElement::getAttribute return mAttributes; } -std::size_t XmlElement::getNumChildren() const +const std::vector >& XmlElement::getChildren() const { - return mChildren.size(); -} - -XmlElement* XmlElement::getChild(std::size_t index) const -{ - return mChildren[index].get(); + return mChildren; } std::string XmlElement::toString(unsigned depth, bool keepInline) const @@ -98,7 +105,7 @@ std::string XmlElement::toString(unsigned depth, bool keepInline) const content += " " + attribute->getName() + "=\"" + attribute->getValue() + "\""; } - const auto num_children = getNumChildren(); + const auto num_children = mChildren.size(); if (num_children == 0 && getText().empty()) { content += "/>" + line_ending; @@ -118,9 +125,9 @@ std::string XmlElement::toString(unsigned depth, bool keepInline) const { content += line_ending; } - for (std::size_t idx=0; idx< getNumChildren(); idx++) + + for(const auto& child : mChildren) { - auto child = getChild(idx); content += child->toString(depth+1, keepInline); } if (num_children>0) diff --git a/src/web/xml/xml-elements/XmlElement.h b/src/web/xml/xml-elements/XmlElement.h index 05b3958..1b3139f 100644 --- a/src/web/xml/xml-elements/XmlElement.h +++ b/src/web/xml/xml-elements/XmlElement.h @@ -17,15 +17,17 @@ public: static std::unique_ptr Create(const std::string& tagName); void addAttribute(XmlAttributePtr attribute); + void addAttribute(const std::string& name, const std::string& value); void addChild(std::unique_ptr child); const std::string& getTagName() const; const std::string& getText() const; + + bool hasAttribute(const std::string& attribute) const; XmlAttribute* getAttribute(const std::string& attribute) const; const std::unordered_map& getAttributes() const; - std::size_t getNumChildren() const; - XmlElement* getChild(std::size_t index) const; + const std::vector >& getChildren() const; XmlElement* getFirstChildWithTagName(const std::string& tag); diff --git a/test/data/circles.svg b/test/data/circles.svg new file mode 100644 index 0000000..8e15448 --- /dev/null +++ b/test/data/circles.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/test/graphics/TestD2dRendering.cpp b/test/graphics/TestD2dRendering.cpp index 3a455ac..55db059 100644 --- a/test/graphics/TestD2dRendering.cpp +++ b/test/graphics/TestD2dRendering.cpp @@ -34,11 +34,9 @@ TEST_CASE(TestD2dWidgetRendering, "graphics") auto scene = gui_app->getMainWindowScene(); Widget widget; - widget.setBackgroundColor({ 0, 200, 0 }); widget.setBounds(300, 300); auto button = Button::Create(); - button->setBackgroundColor({ 200, 0, 0 }); button->setLabel("Test Button"); button->setMaxWidth(100); diff --git a/test/publishing/CMakeLists.txt b/test/publishing/CMakeLists.txt index 947b434..d8d3583 100644 --- a/test/publishing/CMakeLists.txt +++ b/test/publishing/CMakeLists.txt @@ -1,7 +1,8 @@ set(PUBLISHING_UNIT_TEST_FILES publishing/TestPdfWriter.cpp publishing/TestDocumentConverter.cpp - publishing/TestSvgConverter.cpp + publishing/TestSvgConverter.cpp + publishing/TestSvgToNodeConverter.cpp publishing/TestLatexConverter.cpp PARENT_SCOPE ) diff --git a/test/publishing/TestSvgConverter.cpp b/test/publishing/TestSvgConverter.cpp index f3a77f8..866a34e 100644 --- a/test/publishing/TestSvgConverter.cpp +++ b/test/publishing/TestSvgConverter.cpp @@ -1,6 +1,6 @@ #include "SvgWriter.h" #include "SvgDocument.h" -#include "SvgConverter.h" +#include "SvgPainter.h" #include "Scene.h" #include "CircleNode.h" @@ -24,13 +24,10 @@ TEST_CASE(TestSvgConverter, "[publishing]") //rectangle.setFillColor({255, 0, 0}); //scene.addNode(&rectangle); - SvgConverter converter; - auto svg_document = converter.convert(&scene); + SvgPainter painter; + auto svg_document = painter.paint(&scene); svg_document->setViewBox(0, 0, 200, 200); SvgWriter writer; - auto content = writer.toString(svg_document.get()); - - auto outFile = std::make_unique(TestUtils::getTestOutputDir(__FILE__) / "scene.svg"); - outFile->writeText(content); + writer.toFile(TestUtils::getTestOutputDir(__FILE__) / "scene.svg", svg_document.get()); } diff --git a/test/publishing/TestSvgToNodeConverter.cpp b/test/publishing/TestSvgToNodeConverter.cpp new file mode 100644 index 0000000..3fdfb4a --- /dev/null +++ b/test/publishing/TestSvgToNodeConverter.cpp @@ -0,0 +1,25 @@ +#include "TestFramework.h" +#include "TestRenderUtils.h" +#include "TestUtils.h" + +#include "SvgReader.h" +#include "SvgWriter.h" + +#include "SvgNode.h" + +TEST_CASE(TestSvgToNodeConverter, "publishing") +{ + SvgReader svg_reader; + auto svg_doc = svg_reader.read(TestUtils::getTestDataDir() / "circles.svg"); + + SvgWriter svg_writer; + svg_writer.toFile(TestUtils::getTestOutputDir(__FILE__) / "TestSvgToNodeConverter.svg", svg_doc.get()); + + TestRenderer renderer; + + auto svg_node = std::make_unique(Point(0.0, 0.0)); + svg_node->setContent(std::move(svg_doc)); + renderer.getScene()->addNode(svg_node.get()); + + renderer.write(TestUtils::getTestOutputDir(__FILE__) / "TestSvgToNodeConverter.png"); +} \ No newline at end of file diff --git a/test/test_utils/TestRenderUtils.h b/test/test_utils/TestRenderUtils.h index e750e25..e8e37a7 100644 --- a/test/test_utils/TestRenderUtils.h +++ b/test/test_utils/TestRenderUtils.h @@ -5,7 +5,7 @@ #include "AbstractPainter.h" #include "Scene.h" -#include "SvgConverter.h" +#include "SvgPainter.h" #include "SvgWriter.h" #include "SvgDocument.h" @@ -25,6 +25,8 @@ public: mDrawingContext = std::make_unique(mSurface.get()); } + + Scene* getScene() const { return mSurface->getScene(); @@ -47,8 +49,8 @@ public: static void writeSvg(const Path& path, Scene* scene) { - SvgConverter converter; - auto svg_document = converter.convert(scene); + SvgPainter painter; + auto svg_document = painter.paint(scene); SvgWriter writer; auto svg_content = writer.toString(svg_document.get());