From 290527ee20fc6a36fe0064aafccbc3a637e25892 Mon Sep 17 00:00:00 2001 From: sammycage Date: Thu, 26 Sep 2024 16:55:48 +0100 Subject: [PATCH] Support manipulation of Text nodes #180 --- include/lunasvg.h | 126 +++++++++++++++++++++++++++++--------- source/lunasvg.cpp | 111 +++++++++++++++++++++++++++------ source/svgelement.cpp | 2 +- source/svgelement.h | 6 +- source/svgparser.cpp | 2 +- source/svgtextelement.cpp | 2 +- 6 files changed, 196 insertions(+), 53 deletions(-) diff --git a/include/lunasvg.h b/include/lunasvg.h index 6b05bef..a4d5680 100644 --- a/include/lunasvg.h +++ b/include/lunasvg.h @@ -26,6 +26,7 @@ #include #include #include +#include #if !defined(LUNASVG_BUILD_STATIC) && (defined(_WIN32) || defined(__CYGWIN__)) #define LUNASVG_EXPORT __declspec(dllexport) @@ -456,19 +457,104 @@ class LUNASVG_API Matrix { float f{0}; ///< The vertical translation offset. }; +class SVGNode; +class SVGTextNode; class SVGElement; -class LUNASVG_API Element { +class Element; +class TextNode; + +class LUNASVG_API Node { public: /** - * @brief Constructs a null element. + * @brief Node */ - Element() = default; + Node() = default; /** - * @internal + * @brief isTextNode + * @return + */ + bool isTextNode() const; + + /** + * @brief isElement + * @return + */ + bool isElement() const; + + /** + * @brief toTextNode + * @return + */ + TextNode toTextNode() const; + + /** + * @brief toElement + * @return + */ + Element toElement() const; + + /** + * @brief Checks if the node is null. + * @return True if the node is null, false otherwise. + */ + bool isNull() const { return m_node == nullptr; } + + /** + * @brief Checks if two nodes are equal. + * @param element The node to compare. + * @return True if equal, otherwise false. + */ + bool operator==(const Node& node) const { return m_node == node.m_node; } + + /** + * @brief Checks if two nodes are not equal. + * @param element The node to compare. + * @return True if not equal, otherwise false. + */ + bool operator!=(const Node& node) const { return m_node != node.m_node; } + +protected: + Node(SVGNode* node); + SVGNode* node() const { return m_node; } + SVGNode* m_node{nullptr}; + friend class Element; +}; + +using NodeList = std::vector; + +class LUNASVG_API TextNode : public Node { +public: + /** + * @brief TextNode + */ + TextNode() = default; + + /** + * @brief data + * @return */ - Element(SVGElement* element) : m_element(element) {} + const std::string& data() const; + + /** + * @brief setData + * @param data + */ + void setData(const std::string& data); + +private: + TextNode(SVGTextNode* text); + SVGTextNode* text() const; + friend class Node; +}; + +class LUNASVG_API Element : public Node { +public: + /** + * @brief Constructs a null element. + */ + Element() = default; /** * @brief Checks if the element has a specific attribute. @@ -544,32 +630,16 @@ class LUNASVG_API Element { Element parentElement() const; /** - * @brief Checks if the element is null. - * @return True if the element is null, false otherwise. - */ - bool isNull() const { return m_element == nullptr; } - - /** - * @brief Checks if two elements are equal. - * @param element The element to compare. - * @return True if equal, otherwise false. - */ - bool operator==(const Element& element) const { return m_element == element.get(); } - - /** - * @brief Checks if two elements are not equal. - * @param element The element to compare. - * @return True if not equal, otherwise false. - */ - bool operator!=(const Element& element) const { return m_element != element.get(); } - - /** - * @internal + * @brief children + * @return */ - SVGElement* get() const { return m_element; } + NodeList children() const; private: - SVGElement* m_element{nullptr}; + Element(SVGElement* element); + SVGElement* element() const; + friend class Node; + friend class Document; }; class SVGRootElement; diff --git a/source/lunasvg.cpp b/source/lunasvg.cpp index 29267b3..5010d35 100644 --- a/source/lunasvg.cpp +++ b/source/lunasvg.cpp @@ -244,41 +244,99 @@ Matrix Matrix::sheared(float shx, float shy) return Transform::sheared(shx, shy); } +Node::Node(SVGNode* node) + : m_node(node) +{ +} + +bool Node::isTextNode() const +{ + return m_node && m_node->isTextNode(); +} + +bool Node::isElement() const +{ + return m_node && m_node->isElement(); +} + +TextNode Node::toTextNode() const +{ + if(m_node && m_node->isTextNode()) + return TextNode(static_cast(m_node)); + return TextNode(); +} + +Element Node::toElement() const +{ + if(m_node && m_node->isElement()) + return Element(static_cast(m_node)); + return Element(); +} + +TextNode::TextNode(SVGTextNode* text) + : Node(text) +{ +} + +const std::string& TextNode::data() const +{ + if(m_node) + return text()->data(); + return emptyString; +} + +void TextNode::setData(const std::string& data) +{ + if(m_node) { + text()->setData(data); + } +} + +SVGTextNode* TextNode::text() const +{ + return static_cast(m_node); +} + +Element::Element(SVGElement* element) + : Node(element) +{ +} + bool Element::hasAttribute(const std::string& name) const { - if(m_element) - return m_element->hasAttribute(name); + if(m_node) + return element()->hasAttribute(name); return false; } const std::string& Element::getAttribute(const std::string& name) const { - if(m_element) - return m_element->getAttribute(name); + if(m_node) + return element()->getAttribute(name); return emptyString; } void Element::setAttribute(const std::string& name, const std::string& value) { - if(m_element) { - m_element->setAttribute(name, value); + if(m_node) { + element()->setAttribute(name, value); } } void Element::render(Bitmap& bitmap, const Matrix& matrix) const { - if(m_element == nullptr || bitmap.isNull()) + if(m_node == nullptr || bitmap.isNull()) return; auto canvas = Canvas::create(bitmap); SVGRenderState state(nullptr, nullptr, matrix, SVGRenderMode::Painting, canvas); - m_element->render(state); + element()->render(state); } Bitmap Element::renderToBitmap(int width, int height, uint32_t backgroundColor) const { - if(m_element == nullptr) + if(m_node == nullptr) return Bitmap(); - auto elementBounds = m_element->localTransform().mapRect(m_element->paintBoundingBox()); + auto elementBounds = element()->localTransform().mapRect(element()->paintBoundingBox()); if(elementBounds.isEmpty()) return Bitmap(); if(width <= 0 && height <= 0) { @@ -302,17 +360,17 @@ Bitmap Element::renderToBitmap(int width, int height, uint32_t backgroundColor) Matrix Element::getLocalMatrix() const { - if(m_element) - return m_element->localTransform(); + if(m_node) + return element()->localTransform(); return Matrix(); } Matrix Element::getGlobalMatrix() const { - if(m_element == nullptr) + if(m_node == nullptr) return Matrix(); - auto transform = m_element->localTransform(); - for(auto parent = m_element->parent(); parent; parent = parent->parent()) + auto transform = element()->localTransform(); + for(auto parent = element()->parent(); parent; parent = parent->parent()) transform.postMultiply(parent->localTransform()); return transform; } @@ -329,18 +387,33 @@ Box Element::getGlobalBoundingBox() const Box Element::getBoundingBox() const { - if(m_element) - return m_element->paintBoundingBox(); + if(m_node) + return element()->paintBoundingBox(); return Box(); } Element Element::parentElement() const { - if(m_element) - return m_element->parent(); + if(m_node) + return element()->parent(); return Element(); } +NodeList Element::children() const +{ + if(m_node == nullptr) + return NodeList(); + NodeList children; + for(const auto& child : element()->children()) + children.push_back(Node(child.get())); + return children; +} + +SVGElement* Element::element() const +{ + return static_cast(m_node); +} + std::unique_ptr Document::loadFromFile(const std::string& filename) { std::ifstream fs; diff --git a/source/svgelement.cpp b/source/svgelement.cpp index 61eee7d..94b42c6 100644 --- a/source/svgelement.cpp +++ b/source/svgelement.cpp @@ -61,7 +61,7 @@ SVGTextNode::SVGTextNode(Document* document) std::unique_ptr SVGTextNode::clone(bool deep) const { auto node = std::make_unique(document()); - node->setText(m_text); + node->setData(m_data); return node; } diff --git a/source/svgelement.h b/source/svgelement.h index 33c2f7c..b91e5ba 100644 --- a/source/svgelement.h +++ b/source/svgelement.h @@ -49,13 +49,13 @@ class SVGTextNode final : public SVGNode { bool isTextNode() const final { return true; } - void setText(std::string text) { m_text = std::move(text); } - const std::string& text() const { return m_text; } + void setData(const std::string& data) { m_data = data; } + const std::string& data() const { return m_data; } std::unique_ptr clone(bool deep) const final; private: - std::string m_text; + std::string m_data; }; class Attribute { diff --git a/source/svgparser.cpp b/source/svgparser.cpp index 61d9363..763991f 100644 --- a/source/svgparser.cpp +++ b/source/svgparser.cpp @@ -706,7 +706,7 @@ bool Document::parse(const char* data, size_t length) styleSheet.parseSheet(buffer); } else { auto node = std::make_unique(this); - node->setText(buffer); + node->setData(buffer); currentElement->addChild(std::move(node)); } }; diff --git a/source/svgtextelement.cpp b/source/svgtextelement.cpp index ed1292d..17c2382 100644 --- a/source/svgtextelement.cpp +++ b/source/svgtextelement.cpp @@ -168,7 +168,7 @@ void SVGTextFragmentsBuilder::build(const SVGTextElement* textElement) void SVGTextFragmentsBuilder::handleText(const SVGTextNode* node) { - const auto& text = node->text(); + const auto& text = node->data(); if(text.empty()) return; auto element = toSVGTextPositioningElement(node->parent());