Skip to content

Commit

Permalink
Support manipulation of Text nodes #180
Browse files Browse the repository at this point in the history
  • Loading branch information
sammycage committed Sep 26, 2024
1 parent 6e138c0 commit 290527e
Show file tree
Hide file tree
Showing 6 changed files with 196 additions and 53 deletions.
126 changes: 98 additions & 28 deletions include/lunasvg.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include <cstdint>
#include <memory>
#include <string>
#include <vector>

#if !defined(LUNASVG_BUILD_STATIC) && (defined(_WIN32) || defined(__CYGWIN__))
#define LUNASVG_EXPORT __declspec(dllexport)
Expand Down Expand Up @@ -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<Node>;

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.
Expand Down Expand Up @@ -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;
Expand Down
111 changes: 92 additions & 19 deletions source/lunasvg.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<SVGTextNode*>(m_node));
return TextNode();
}

Element Node::toElement() const
{
if(m_node && m_node->isElement())
return Element(static_cast<SVGElement*>(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<SVGTextNode*>(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) {
Expand All @@ -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;
}
Expand All @@ -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<SVGElement*>(m_node);
}

std::unique_ptr<Document> Document::loadFromFile(const std::string& filename)
{
std::ifstream fs;
Expand Down
2 changes: 1 addition & 1 deletion source/svgelement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ SVGTextNode::SVGTextNode(Document* document)
std::unique_ptr<SVGNode> SVGTextNode::clone(bool deep) const
{
auto node = std::make_unique<SVGTextNode>(document());
node->setText(m_text);
node->setData(m_data);
return node;
}

Expand Down
6 changes: 3 additions & 3 deletions source/svgelement.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<SVGNode> clone(bool deep) const final;

private:
std::string m_text;
std::string m_data;
};

class Attribute {
Expand Down
2 changes: 1 addition & 1 deletion source/svgparser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -706,7 +706,7 @@ bool Document::parse(const char* data, size_t length)
styleSheet.parseSheet(buffer);
} else {
auto node = std::make_unique<SVGTextNode>(this);
node->setText(buffer);
node->setData(buffer);
currentElement->addChild(std::move(node));
}
};
Expand Down
2 changes: 1 addition & 1 deletion source/svgtextelement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down

0 comments on commit 290527e

Please sign in to comment.