Skip to content

Commit

Permalink
Add alignment-baseline and dominant-baseline support
Browse files Browse the repository at this point in the history
  • Loading branch information
sammycage committed Dec 19, 2024
1 parent aa604e9 commit 90942f1
Show file tree
Hide file tree
Showing 8 changed files with 168 additions and 1 deletion.
8 changes: 8 additions & 0 deletions source/graphics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -517,6 +517,14 @@ float Font::height() const
return ascent + descent;
}

float Font::xHeight() const
{
plutovg_rect_t extents = {0};
if(m_size > 0.f && !m_face.isNull())
plutovg_font_face_get_glyph_metrics(m_face.get(), m_size, 'x', nullptr, nullptr, &extents);
return extents.h;
}

float Font::measureText(const std::u32string_view& text) const
{
if(m_size > 0.f && !m_face.isNull())
Expand Down
1 change: 1 addition & 0 deletions source/graphics.h
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,7 @@ class Font {
float ascent() const;
float descent() const;
float height() const;
float xHeight() const;

float measureText(const std::u32string_view& text) const;

Expand Down
47 changes: 47 additions & 0 deletions source/svglayoutstate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,46 @@ static FontStyle parseFontStyle(const std::string_view& input)
return parseEnumValue(input, entries, FontStyle::Normal);
}

static AlignmentBaseline parseAlignmentBaseline(const std::string_view& input)
{
static const SVGEnumerationEntry<AlignmentBaseline> entries[] = {
{AlignmentBaseline::Auto, "auto"},
{AlignmentBaseline::Baseline, "baseline"},
{AlignmentBaseline::BeforeEdge, "before-edge"},
{AlignmentBaseline::TextBeforeEdge, "text-before-edge"},
{AlignmentBaseline::Middle, "middle"},
{AlignmentBaseline::Central, "central"},
{AlignmentBaseline::AfterEdge, "after-edge"},
{AlignmentBaseline::TextAfterEdge, "text-after-edge"},
{AlignmentBaseline::Ideographic, "ideographic"},
{AlignmentBaseline::Alphabetic, "alphabetic"},
{AlignmentBaseline::Hanging, "hanging"},
{AlignmentBaseline::Mathematical, "mathematical"}
};

return parseEnumValue(input, entries, AlignmentBaseline::Auto);
}

static DominantBaseline parseDominantBaseline(const std::string_view& input)
{
static const SVGEnumerationEntry<DominantBaseline> entries[] = {
{DominantBaseline::Auto, "auto"},
{DominantBaseline::UseScript, "use-script"},
{DominantBaseline::NoChange, "no-change"},
{DominantBaseline::ResetSize, "reset-size"},
{DominantBaseline::Ideographic, "ideographic"},
{DominantBaseline::Alphabetic, "alphabetic"},
{DominantBaseline::Hanging, "hanging"},
{DominantBaseline::Mathematical, "mathematical"},
{DominantBaseline::Central, "central"},
{DominantBaseline::Middle, "middle"},
{DominantBaseline::TextAfterEdge, "text-after-edge"},
{DominantBaseline::TextBeforeEdge, "text-before-edge"}
};

return parseEnumValue(input, entries, DominantBaseline::Auto);
}

static Direction parseDirection(const std::string_view& input)
{
static const SVGEnumerationEntry<Direction> entries[] = {
Expand Down Expand Up @@ -329,6 +369,7 @@ SVGLayoutState::SVGLayoutState(const SVGLayoutState& parent, const SVGElement* e
, m_clip_rule(parent.clip_rule())
, m_font_weight(parent.font_weight())
, m_font_style(parent.font_style())
, m_dominant_baseline(parent.dominant_baseline())
, m_text_anchor(parent.text_anchor())
, m_white_space(parent.white_space())
, m_direction(parent.direction())
Expand Down Expand Up @@ -405,6 +446,12 @@ SVGLayoutState::SVGLayoutState(const SVGLayoutState& parent, const SVGElement* e
case PropertyID::Font_Style:
m_font_style = parseFontStyle(input);
break;
case PropertyID::Alignment_Baseline:
m_alignment_baseline = parseAlignmentBaseline(input);
break;
case PropertyID::Dominant_Baseline:
m_dominant_baseline = parseDominantBaseline(input);
break;
case PropertyID::Direction:
m_direction = parseDirection(input);
break;
Expand Down
6 changes: 6 additions & 0 deletions source/svglayoutstate.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ class SVGLayoutState {
FontWeight font_weight() const { return m_font_weight; }
FontStyle font_style() const { return m_font_style; }

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; }
Expand Down Expand Up @@ -89,6 +92,9 @@ class SVGLayoutState {
FontWeight m_font_weight = FontWeight::Normal;
FontStyle m_font_style = FontStyle::Normal;

AlignmentBaseline m_alignment_baseline = AlignmentBaseline::Auto;
DominantBaseline m_dominant_baseline = DominantBaseline::Auto;

TextAnchor m_text_anchor = TextAnchor::Start;
WhiteSpace m_white_space = WhiteSpace::Default;
Direction m_direction = Direction::Ltr;
Expand Down
2 changes: 2 additions & 0 deletions source/svgproperty.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,12 +71,14 @@ PropertyID csspropertyid(const std::string_view& name)
std::string_view name;
PropertyID value;
} table[] = {
{"alignment-baseline", PropertyID::Alignment_Baseline},
{"baseline-shift", PropertyID::Baseline_Shift},
{"clip-path", PropertyID::Clip_Path},
{"clip-rule", PropertyID::Clip_Rule},
{"color", PropertyID::Color},
{"direction", PropertyID::Direction},
{"display", PropertyID::Display},
{"dominant-baseline", PropertyID::Dominant_Baseline},
{"fill", PropertyID::Fill},
{"fill-opacity", PropertyID::Fill_Opacity},
{"fill-rule", PropertyID::Fill_Rule},
Expand Down
32 changes: 32 additions & 0 deletions source/svgproperty.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ namespace lunasvg {

enum class PropertyID : uint8_t {
Unknown = 0,
Alignment_Baseline,
Baseline_Shift,
Class,
ClipPathUnits,
Expand All @@ -20,6 +21,7 @@ enum class PropertyID : uint8_t {
D,
Direction,
Display,
Dominant_Baseline,
Dx,
Dy,
Fill,
Expand Down Expand Up @@ -162,6 +164,36 @@ enum class FontWeight : uint8_t {
Bold
};

enum class AlignmentBaseline : uint8_t {
Auto,
Baseline,
BeforeEdge,
TextBeforeEdge,
Middle,
Central,
AfterEdge,
TextAfterEdge,
Ideographic,
Alphabetic,
Hanging,
Mathematical
};

enum class DominantBaseline : uint8_t {
Auto,
UseScript,
NoChange,
ResetSize,
Ideographic,
Alphabetic,
Hanging,
Mathematical,
Central,
Middle,
TextAfterEdge,
TextBeforeEdge
};

enum class TextAnchor : uint8_t {
Start,
Middle,
Expand Down
69 changes: 68 additions & 1 deletion source/svgtextelement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,76 @@ inline const SVGTextPositioningElement* toSVGTextPositioningElement(const SVGNod
return static_cast<const SVGTextPositioningElement*>(node);
}

static AlignmentBaseline resolveDominantBaseline(const SVGTextPositioningElement* element)
{
switch(element->dominant_baseline()) {
case DominantBaseline::Auto:
case DominantBaseline::UseScript:
case DominantBaseline::NoChange:
case DominantBaseline::ResetSize:
return AlignmentBaseline::Auto;
case DominantBaseline::Ideographic:
return AlignmentBaseline::Ideographic;
case DominantBaseline::Alphabetic:
return AlignmentBaseline::Alphabetic;
case DominantBaseline::Hanging:
return AlignmentBaseline::Hanging;
case DominantBaseline::Mathematical:
return AlignmentBaseline::Mathematical;
case DominantBaseline::Central:
return AlignmentBaseline::Central;
case DominantBaseline::Middle:
return AlignmentBaseline::Middle;
case DominantBaseline::TextAfterEdge:
return AlignmentBaseline::TextAfterEdge;
case DominantBaseline::TextBeforeEdge:
return AlignmentBaseline::TextBeforeEdge;
default:
assert(false);
}

return AlignmentBaseline::Auto;
}

static float calculateBaselineOffset(const SVGTextPositioningElement* element)
{
auto offset = element->baseline_offset();
for(auto parent = element->parent(); parent->isTextPositioningElement(); parent = parent->parent())
for(auto parent = element->parent(); parent->isTextPositioningElement(); parent = parent->parent()) {
offset += toSVGTextPositioningElement(parent)->baseline_offset();
}

auto baseline = element->alignment_baseline();
if(baseline == AlignmentBaseline::Baseline || baseline == AlignmentBaseline::Auto) {
baseline = resolveDominantBaseline(element);
}

const auto& font = element->font();
switch(baseline) {
case AlignmentBaseline::BeforeEdge:
case AlignmentBaseline::TextBeforeEdge:
offset -= font.ascent();
break;
case AlignmentBaseline::Middle:
offset -= font.xHeight() / 2.f;
break;
case AlignmentBaseline::Central:
offset -= (font.ascent() - font.descent()) / 2.f;
break;
case AlignmentBaseline::AfterEdge:
case AlignmentBaseline::TextAfterEdge:
case AlignmentBaseline::Ideographic:
offset -= font.descent();
break;
case AlignmentBaseline::Hanging:
offset -= font.ascent() * 8.f / 10.f;
break;
case AlignmentBaseline::Mathematical:
offset -= font.ascent() / 2.f;
break;
default:
break;
}

return offset;
}

Expand Down Expand Up @@ -289,6 +354,8 @@ void SVGTextPositioningElement::layoutElement(const SVGLayoutState& state)
LengthContext lengthContext(this);
m_stroke_width = lengthContext.valueForLength(state.stroke_width(), LengthDirection::Diagonal);
m_baseline_offset = convertBaselineOffset(state.baseline_shit());
m_alignment_baseline = state.alignment_baseline();
m_dominant_baseline = state.dominant_baseline();
m_text_anchor = state.text_anchor();
m_white_space = state.white_space();
m_direction = state.direction();
Expand Down
4 changes: 4 additions & 0 deletions source/svgtextelement.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ class SVGTextPositioningElement : public SVGGraphicsElement {

float stroke_width() const { return m_stroke_width; }
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; }
Expand All @@ -103,6 +105,8 @@ class SVGTextPositioningElement : public SVGGraphicsElement {

float m_stroke_width = 1.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;
Direction m_direction = Direction::Ltr;
Expand Down

0 comments on commit 90942f1

Please sign in to comment.