Files
autosample/vendor/lunasvg/source/svgelement.h
2026-03-11 19:15:36 -04:00

499 lines
14 KiB
C++

#ifndef LUNASVG_SVGELEMENT_H
#define LUNASVG_SVGELEMENT_H
#include "lunasvg.h"
#include "svgproperty.h"
#include <string>
#include <forward_list>
#include <list>
#include <map>
namespace lunasvg {
class Document;
class SVGElement;
class SVGRootElement;
class SVGNode {
public:
SVGNode(Document* document)
: m_document(document)
{}
virtual ~SVGNode() = default;
virtual bool isTextNode() const { return false; }
virtual bool isElement() const { return false; }
virtual bool isPaintElement() const { return false; }
virtual bool isGraphicsElement() const { return false; }
virtual bool isGeometryElement() const { return false; }
virtual bool isTextPositioningElement() const { return false; }
Document* document() const { return m_document; }
SVGRootElement* rootElement() const { return m_document->rootElement(); }
SVGElement* parentElement() const { return m_parentElement; }
void setParentElement(SVGElement* parent) { m_parentElement = parent; }
bool isRootElement() const { return m_parentElement == nullptr; }
virtual std::unique_ptr<SVGNode> clone(bool deep) const = 0;
private:
SVGNode(const SVGNode&) = delete;
SVGNode& operator=(const SVGNode&) = delete;
Document* m_document;
SVGElement* m_parentElement = nullptr;
};
class SVGTextNode final : public SVGNode {
public:
SVGTextNode(Document* document);
bool isTextNode() const final { return true; }
const std::string& data() const { return m_data; }
void setData(const std::string& data);
std::unique_ptr<SVGNode> clone(bool deep) const final;
private:
std::string m_data;
};
class Attribute {
public:
Attribute() = default;
Attribute(int specificity, PropertyID id, std::string value)
: m_specificity(specificity), m_id(id), m_value(std::move(value))
{}
int specificity() const { return m_specificity; }
PropertyID id() const { return m_id; }
const std::string& value() const { return m_value; }
private:
int m_specificity;
PropertyID m_id;
std::string m_value;
};
using AttributeList = std::forward_list<Attribute>;
enum class ElementID : uint8_t {
Unknown = 0,
Star,
Circle,
ClipPath,
Defs,
Ellipse,
G,
Image,
Line,
LinearGradient,
Marker,
Mask,
Path,
Pattern,
Polygon,
Polyline,
RadialGradient,
Rect,
Stop,
Style,
Svg,
Symbol,
Text,
Tspan,
Use
};
ElementID elementid(std::string_view name);
using SVGNodeList = std::list<std::unique_ptr<SVGNode>>;
using SVGPropertyList = std::forward_list<SVGProperty*>;
class SVGMarkerElement;
class SVGClipPathElement;
class SVGMaskElement;
class SVGPaintElement;
class SVGLayoutState;
class SVGRenderState;
extern const std::string emptyString;
class SVGElement : public SVGNode {
public:
static std::unique_ptr<SVGElement> create(Document* document, ElementID id);
SVGElement(Document* document, ElementID id);
virtual ~SVGElement() = default;
bool hasAttribute(std::string_view name) const;
const std::string& getAttribute(std::string_view name) const;
bool setAttribute(std::string_view name, const std::string& value);
const Attribute* findAttribute(PropertyID id) const;
bool hasAttribute(PropertyID id) const;
const std::string& getAttribute(PropertyID id) const;
bool setAttribute(int specificity, PropertyID id, const std::string& value);
void setAttributes(const AttributeList& attributes);
bool setAttribute(const Attribute& attribute);
virtual void parseAttribute(PropertyID id, const std::string& value);
SVGElement* previousElement() const;
SVGElement* nextElement() const;
SVGNode* addChild(std::unique_ptr<SVGNode> child);
SVGNode* firstChild() const;
SVGNode* lastChild() const;
ElementID id() const { return m_id; }
const AttributeList& attributes() const { return m_attributes; }
const SVGPropertyList& properties() const { return m_properties; }
const SVGNodeList& children() const { return m_children; }
virtual Transform localTransform() const { return Transform::Identity; }
virtual Rect fillBoundingBox() const;
virtual Rect strokeBoundingBox() const;
virtual Rect paintBoundingBox() const;
SVGMarkerElement* getMarker(std::string_view id) const;
SVGClipPathElement* getClipper(std::string_view id) const;
SVGMaskElement* getMasker(std::string_view id) const;
SVGPaintElement* getPainter(std::string_view id) const;
SVGElement* elementFromPoint(float x, float y);
template<typename T>
void transverse(T callback);
void addProperty(SVGProperty& value);
SVGProperty* getProperty(PropertyID id) const;
Size currentViewportSize() const;
float font_size() const { return m_font_size; }
void cloneChildren(SVGElement* parentElement) const;
std::unique_ptr<SVGNode> clone(bool deep) const final;
virtual void build();
virtual void layoutElement(const SVGLayoutState& state);
void layoutChildren(SVGLayoutState& state);
virtual void layout(SVGLayoutState& state);
void renderChildren(SVGRenderState& state) const;
virtual void render(SVGRenderState& state) const;
bool isDisplayNone() const { return m_display == Display::None; }
bool isOverflowHidden() const { return m_overflow == Overflow::Hidden; }
bool isVisibilityHidden() const { return m_visibility != Visibility::Visible; }
bool isHiddenElement() const;
bool isPointableElement() const;
const SVGClipPathElement* clipper() const { return m_clipper; }
const SVGMaskElement* masker() const { return m_masker; }
float opacity() const { return m_opacity; }
bool isElement() const final { return true; }
private:
mutable Rect m_paintBoundingBox = Rect::Invalid;
const SVGClipPathElement* m_clipper = nullptr;
const SVGMaskElement* m_masker = nullptr;
float m_opacity = 1.f;
float m_font_size = 12.f;
Display m_display = Display::Inline;
Overflow m_overflow = Overflow::Visible;
Visibility m_visibility = Visibility::Visible;
PointerEvents m_pointer_events = PointerEvents::Auto;
ElementID m_id;
AttributeList m_attributes;
SVGPropertyList m_properties;
SVGNodeList m_children;
};
inline const SVGElement* toSVGElement(const SVGNode* node)
{
if(node && node->isElement())
return static_cast<const SVGElement*>(node);
return nullptr;
}
inline SVGElement* toSVGElement(SVGNode* node)
{
if(node && node->isElement())
return static_cast<SVGElement*>(node);
return nullptr;
}
inline SVGElement* toSVGElement(const std::unique_ptr<SVGNode>& node)
{
return toSVGElement(node.get());
}
template<typename T>
inline void SVGElement::transverse(T callback)
{
callback(this);
for(const auto& child : m_children) {
if(auto element = toSVGElement(child)) {
element->transverse(callback);
}
}
}
class SVGStyleElement final : public SVGElement {
public:
SVGStyleElement(Document* document);
};
class SVGFitToViewBox {
public:
SVGFitToViewBox(SVGElement* element);
const SVGRect& viewBox() const { return m_viewBox; }
const SVGPreserveAspectRatio& preserveAspectRatio() const { return m_preserveAspectRatio; }
Transform viewBoxToViewTransform(const Size& viewportSize) const;
Rect getClipRect(const Size& viewportSize) const;
private:
SVGRect m_viewBox;
SVGPreserveAspectRatio m_preserveAspectRatio;
};
class SVGURIReference {
public:
SVGURIReference(SVGElement* element);
const SVGString& href() const { return m_href; }
const std::string& hrefString() const { return m_href.value(); }
SVGElement* getTargetElement(const Document* document) const;
private:
SVGString m_href;
};
class SVGPaintServer {
public:
SVGPaintServer() = default;
SVGPaintServer(const SVGPaintElement* element, const Color& color, float opacity)
: m_element(element), m_color(color), m_opacity(opacity)
{}
bool isRenderable() const { return m_opacity > 0.f && (m_element || m_color.alpha() > 0); }
const SVGPaintElement* element() const { return m_element; }
const Color& color() const { return m_color; }
float opacity() const { return m_opacity; }
bool applyPaint(SVGRenderState& state) const;
private:
const SVGPaintElement* m_element = nullptr;
Color m_color = Color::Transparent;
float m_opacity = 0.f;
};
class SVGGraphicsElement : public SVGElement {
public:
SVGGraphicsElement(Document* document, ElementID id);
bool isGraphicsElement() const final { return true; }
const SVGTransform& transform() const { return m_transform; }
Transform localTransform() const override { return m_transform.value(); }
SVGPaintServer getPaintServer(const Paint& paint, float opacity) const;
StrokeData getStrokeData(const SVGLayoutState& state) const;
private:
SVGTransform m_transform;
};
class SVGSVGElement : public SVGGraphicsElement, public SVGFitToViewBox {
public:
SVGSVGElement(Document* document);
const SVGLength& x() const { return m_x; }
const SVGLength& y() const { return m_y; }
const SVGLength& width() const { return m_width; }
const SVGLength& height() const { return m_height; }
Transform localTransform() const override;
void render(SVGRenderState& state) const override;
private:
SVGLength m_x;
SVGLength m_y;
SVGLength m_width;
SVGLength m_height;
};
class SVGRootElement final : public SVGSVGElement {
public:
SVGRootElement(Document* document);
float intrinsicWidth() const { return m_intrinsicWidth; }
float intrinsicHeight() const { return m_intrinsicHeight; }
void setNeedsLayout() { m_intrinsicWidth = -1.f; }
bool needsLayout() const { return m_intrinsicWidth == -1.f; }
SVGRootElement* layoutIfNeeded();
SVGElement* getElementById(std::string_view id) const;
void addElementById(const std::string& id, SVGElement* element);
void layout(SVGLayoutState& state) final;
void forceLayout();
private:
std::map<std::string, SVGElement*, std::less<>> m_idCache;
float m_intrinsicWidth{-1.f};
float m_intrinsicHeight{-1.f};
};
class SVGUseElement final : public SVGGraphicsElement, public SVGURIReference {
public:
SVGUseElement(Document* document);
const SVGLength& x() const { return m_x; }
const SVGLength& y() const { return m_y; }
const SVGLength& width() const { return m_width; }
const SVGLength& height() const { return m_height; }
Transform localTransform() const final;
void render(SVGRenderState& state) const final;
void build() final;
private:
std::unique_ptr<SVGElement> cloneTargetElement(SVGElement* targetElement);
SVGLength m_x;
SVGLength m_y;
SVGLength m_width;
SVGLength m_height;
};
class SVGImageElement final : public SVGGraphicsElement {
public:
SVGImageElement(Document* document);
const SVGLength& x() const { return m_x; }
const SVGLength& y() const { return m_y; }
const SVGLength& width() const { return m_width; }
const SVGLength& height() const { return m_height; }
const SVGPreserveAspectRatio& preserveAspectRatio() const { return m_preserveAspectRatio; }
const Bitmap& image() const { return m_image; }
Rect fillBoundingBox() const final;
Rect strokeBoundingBox() const final;
void render(SVGRenderState& state) const final;
void parseAttribute(PropertyID id, const std::string& value) final;
private:
SVGLength m_x;
SVGLength m_y;
SVGLength m_width;
SVGLength m_height;
SVGPreserveAspectRatio m_preserveAspectRatio;
Bitmap m_image;
};
class SVGSymbolElement final : public SVGGraphicsElement, public SVGFitToViewBox {
public:
SVGSymbolElement(Document* document);
};
class SVGGElement final : public SVGGraphicsElement {
public:
SVGGElement(Document* document);
void render(SVGRenderState& state) const final;
};
class SVGDefsElement final : public SVGGraphicsElement {
public:
SVGDefsElement(Document* document);
};
class SVGMarkerElement final : public SVGElement, public SVGFitToViewBox {
public:
SVGMarkerElement(Document* document);
const SVGLength& refX() const { return m_refX; }
const SVGLength& refY() const { return m_refY; }
const SVGLength& markerWidth() const { return m_markerWidth; }
const SVGLength& markerHeight() const { return m_markerHeight; }
const SVGEnumeration<MarkerUnits>& markerUnits() const { return m_markerUnits; }
const SVGAngle& orient() const { return m_orient; }
Point refPoint() const;
Size markerSize() const;
Transform markerTransform(const Point& origin, float angle, float strokeWidth) const;
Rect markerBoundingBox(const Point& origin, float angle, float strokeWidth) const;
void renderMarker(SVGRenderState& state, const Point& origin, float angle, float strokeWidth) const;
Transform localTransform() const final;
private:
SVGLength m_refX;
SVGLength m_refY;
SVGLength m_markerWidth;
SVGLength m_markerHeight;
SVGEnumeration<MarkerUnits> m_markerUnits;
SVGAngle m_orient;
};
class SVGClipPathElement final : public SVGGraphicsElement {
public:
SVGClipPathElement(Document* document);
const SVGEnumeration<Units>& clipPathUnits() const { return m_clipPathUnits; }
Rect clipBoundingBox(const SVGElement* element) const;
void applyClipMask(SVGRenderState& state) const;
void applyClipPath(SVGRenderState& state) const;
bool requiresMasking() const;
private:
SVGEnumeration<Units> m_clipPathUnits;
};
class SVGMaskElement final : public SVGElement {
public:
SVGMaskElement(Document* document);
const SVGLength& x() const { return m_x; }
const SVGLength& y() const { return m_y; }
const SVGLength& width() const { return m_width; }
const SVGLength& height() const { return m_height; }
const SVGEnumeration<Units>& maskUnits() const { return m_maskUnits; }
const SVGEnumeration<Units>& maskContentUnits() const { return m_maskContentUnits; }
Rect maskRect(const SVGElement* element) const;
Rect maskBoundingBox(const SVGElement* element) const;
void applyMask(SVGRenderState& state) const;
void layoutElement(const SVGLayoutState& state) final;
private:
SVGLength m_x;
SVGLength m_y;
SVGLength m_width;
SVGLength m_height;
SVGEnumeration<Units> m_maskUnits;
SVGEnumeration<Units> m_maskContentUnits;
MaskType m_mask_type = MaskType::Luminance;
};
} // namespace lunasvg
#endif // LUNASVG_SVGELEMENT_H