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

156 lines
4.9 KiB
C++

#ifndef LUNASVG_SVGTEXTELEMENT_H
#define LUNASVG_SVGTEXTELEMENT_H
#include "svgelement.h"
#include <optional>
namespace lunasvg {
class SVGTextPositioningElement;
class SVGTextElement;
struct SVGCharacterPosition {
std::optional<float> x;
std::optional<float> y;
std::optional<float> dx;
std::optional<float> dy;
std::optional<float> rotate;
};
using SVGCharacterPositions = std::map<size_t, SVGCharacterPosition>;
struct SVGTextPosition {
SVGTextPosition(const SVGNode* node, size_t startOffset, size_t endOffset)
: node(node), startOffset(startOffset), endOffset(endOffset)
{}
const SVGNode* node;
size_t startOffset;
size_t endOffset;
};
using SVGTextPositionList = std::vector<SVGTextPosition>;
struct SVGTextFragment {
explicit SVGTextFragment(const SVGTextPositioningElement* element) : element(element) {}
const SVGTextPositioningElement* element;
Transform lengthAdjustTransform;
size_t offset = 0;
size_t length = 0;
bool startsNewTextChunk = false;
float x = 0;
float y = 0;
float width = 0;
float height = 0;
float angle = 0;
};
using SVGTextFragmentList = std::vector<SVGTextFragment>;
class SVGTextFragmentsBuilder {
public:
SVGTextFragmentsBuilder(std::u32string& text, SVGTextFragmentList& fragments);
void build(const SVGTextElement* textElement);
private:
void handleText(const SVGTextNode* node);
void handleElement(const SVGTextPositioningElement* element);
void fillCharacterPositions(const SVGTextPosition& position);
std::u32string& m_text;
SVGTextFragmentList& m_fragments;
SVGCharacterPositions m_characterPositions;
SVGTextPositionList m_textPositions;
size_t m_characterOffset = 0;
float m_x = 0;
float m_y = 0;
};
class SVGTextPositioningElement : public SVGGraphicsElement {
public:
SVGTextPositioningElement(Document* document, ElementID id);
bool isTextPositioningElement() const final { return true; }
const LengthList& x() const { return m_x.values(); }
const LengthList& y() const { return m_y.values(); }
const LengthList& dx() const { return m_dx.values(); }
const LengthList& dy() const { return m_dy.values(); }
const NumberList& rotate() const { return m_rotate.values(); }
const SVGLength& textLength() const { return m_textLength; }
LengthAdjust lengthAdjust() const { return m_lengthAdjust.value(); }
const Font& font() const { return m_font; }
const SVGPaintServer& fill() const { return m_fill; }
const SVGPaintServer& stroke() const { return m_stroke; }
bool isVerticalWritingMode() const { return m_writing_mode == WritingMode::Vertical; }
bool isUprightTextOrientation() const { return m_text_orientation == TextOrientation::Upright; }
float stroke_width() const { return m_stroke_width; }
float letter_spacing() const { return m_letter_spacing; }
float word_spacing() const { return m_word_spacing; }
float baseline_offset() const { return m_baseline_offset; }
AlignmentBaseline alignment_baseline() const { return m_alignment_baseline; }
DominantBaseline dominant_baseline() const { return m_dominant_baseline; }
TextAnchor text_anchor() const { return m_text_anchor; }
WhiteSpace white_space() const { return m_white_space; }
Direction direction() const { return m_direction; }
void layoutElement(const SVGLayoutState& state) override;
private:
float convertBaselineOffset(const BaselineShift& baselineShift) const;
SVGLengthList m_x;
SVGLengthList m_y;
SVGLengthList m_dx;
SVGLengthList m_dy;
SVGNumberList m_rotate;
SVGLength m_textLength;
SVGEnumeration<LengthAdjust> m_lengthAdjust;
Font m_font;
SVGPaintServer m_fill;
SVGPaintServer m_stroke;
float m_stroke_width = 1.f;
float m_letter_spacing = 0.f;
float m_word_spacing = 0.f;
float m_baseline_offset = 0.f;
AlignmentBaseline m_alignment_baseline = AlignmentBaseline::Auto;
DominantBaseline m_dominant_baseline = DominantBaseline::Auto;
TextAnchor m_text_anchor = TextAnchor::Start;
WhiteSpace m_white_space = WhiteSpace::Default;
WritingMode m_writing_mode = WritingMode::Horizontal;
TextOrientation m_text_orientation = TextOrientation::Mixed;
Direction m_direction = Direction::Ltr;
};
class SVGTSpanElement final : public SVGTextPositioningElement {
public:
SVGTSpanElement(Document* document);
};
class SVGTextElement final : public SVGTextPositioningElement {
public:
SVGTextElement(Document* document);
Rect fillBoundingBox() const final { return boundingBox(false); }
Rect strokeBoundingBox() const final { return boundingBox(true); }
void layout(SVGLayoutState& state) final;
void render(SVGRenderState& state) const final;
private:
Rect boundingBox(bool includeStroke) const;
SVGTextFragmentList m_fragments;
std::u32string m_text;
};
} // namespace lunasvg
#endif // LUNASVG_SVGTEXTELEMENT_H