mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-04-20 03:25:13 +00:00
LibWeb: Layout/Shape font-variant-* css properties
This commit is contained in:
parent
1c42d6831b
commit
083f4f3d08
Notes:
github-actions[bot]
2024-12-17 18:08:20 +00:00
Author: https://github.com/jdahlin Commit: https://github.com/LadybirdBrowser/ladybird/commit/083f4f3d084 Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/2197 Reviewed-by: https://github.com/AtkinsSJ Reviewed-by: https://github.com/gmta ✅ Reviewed-by: https://github.com/kalenikaliaksandr Reviewed-by: https://github.com/tcl3
8 changed files with 310 additions and 14 deletions
|
@ -58,13 +58,13 @@ ScaledFontMetrics ScaledFont::metrics() const
|
|||
return metrics;
|
||||
}
|
||||
|
||||
float ScaledFont::width(StringView view) const { return measure_text_width(Utf8View(view), *this); }
|
||||
float ScaledFont::width(Utf8View const& view) const { return measure_text_width(view, *this); }
|
||||
float ScaledFont::width(StringView view) const { return measure_text_width(Utf8View(view), *this, {}); }
|
||||
float ScaledFont::width(Utf8View const& view) const { return measure_text_width(view, *this, {}); }
|
||||
|
||||
float ScaledFont::glyph_width(u32 code_point) const
|
||||
{
|
||||
auto string = String::from_code_point(code_point);
|
||||
return measure_text_width(Utf8View(string), *this);
|
||||
return measure_text_width(Utf8View(string), *this, {});
|
||||
}
|
||||
|
||||
NonnullRefPtr<ScaledFont> ScaledFont::scaled_with_size(float point_size) const
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
|
||||
namespace Gfx {
|
||||
|
||||
RefPtr<GlyphRun> shape_text(FloatPoint baseline_start, float letter_spacing, Utf8View string, Gfx::Font const& font, GlyphRun::TextType text_type)
|
||||
RefPtr<GlyphRun> shape_text(FloatPoint baseline_start, float letter_spacing, Utf8View string, Gfx::Font const& font, GlyphRun::TextType text_type, ShapeFeatures const& features)
|
||||
{
|
||||
hb_buffer_t* buffer = hb_buffer_create();
|
||||
ScopeGuard destroy_buffer = [&]() { hb_buffer_destroy(buffer); };
|
||||
|
@ -24,7 +24,22 @@ RefPtr<GlyphRun> shape_text(FloatPoint baseline_start, float letter_spacing, Utf
|
|||
Vector<hb_glyph_info_t> const input_glyph_info({ glyph_info, glyph_count });
|
||||
|
||||
auto* hb_font = font.harfbuzz_font();
|
||||
hb_shape(hb_font, buffer, nullptr, 0);
|
||||
hb_feature_t const* hb_features_data = nullptr;
|
||||
Vector<hb_feature_t> hb_features;
|
||||
if (!features.is_empty()) {
|
||||
hb_features.ensure_capacity(features.size());
|
||||
for (auto const& feature : features) {
|
||||
hb_features.append({
|
||||
.tag = HB_TAG(feature.tag[0], feature.tag[1], feature.tag[2], feature.tag[3]),
|
||||
.value = feature.value,
|
||||
.start = 0,
|
||||
.end = HB_FEATURE_GLOBAL_END,
|
||||
});
|
||||
}
|
||||
hb_features_data = hb_features.data();
|
||||
}
|
||||
|
||||
hb_shape(hb_font, buffer, hb_features_data, features.size());
|
||||
|
||||
glyph_info = hb_buffer_get_glyph_infos(buffer, &glyph_count);
|
||||
auto* positions = hb_buffer_get_glyph_positions(buffer, &glyph_count);
|
||||
|
@ -45,12 +60,14 @@ RefPtr<GlyphRun> shape_text(FloatPoint baseline_start, float letter_spacing, Utf
|
|||
point.translate_by(letter_spacing, 0);
|
||||
}
|
||||
|
||||
hb_buffer_reset(buffer);
|
||||
|
||||
return adopt_ref(*new Gfx::GlyphRun(move(glyph_run), font, text_type, point.x()));
|
||||
}
|
||||
|
||||
float measure_text_width(Utf8View const& string, Gfx::Font const& font)
|
||||
float measure_text_width(Utf8View const& string, Gfx::Font const& font, ShapeFeatures const& features)
|
||||
{
|
||||
auto glyph_run = shape_text({}, 0, string, font, GlyphRun::TextType::Common);
|
||||
auto glyph_run = shape_text({}, 0, string, font, GlyphRun::TextType::Common, features);
|
||||
return glyph_run->width();
|
||||
}
|
||||
|
||||
|
|
|
@ -26,6 +26,13 @@ struct DrawGlyph {
|
|||
}
|
||||
};
|
||||
|
||||
typedef struct ShapeFeature {
|
||||
char tag[4];
|
||||
u32 value;
|
||||
} ShapeFeature;
|
||||
|
||||
using ShapeFeatures = Vector<ShapeFeature, 4>;
|
||||
|
||||
class GlyphRun : public RefCounted<GlyphRun> {
|
||||
public:
|
||||
enum class TextType {
|
||||
|
@ -60,7 +67,7 @@ private:
|
|||
float m_width { 0 };
|
||||
};
|
||||
|
||||
RefPtr<GlyphRun> shape_text(FloatPoint baseline_start, float letter_spacing, Utf8View string, Gfx::Font const& font, GlyphRun::TextType);
|
||||
float measure_text_width(Utf8View const& string, Gfx::Font const& font);
|
||||
RefPtr<GlyphRun> shape_text(FloatPoint baseline_start, float letter_spacing, Utf8View string, Gfx::Font const& font, GlyphRun::TextType, ShapeFeatures const& features);
|
||||
float measure_text_width(Utf8View const& string, Gfx::Font const& font, ShapeFeatures const& features);
|
||||
|
||||
}
|
||||
|
|
|
@ -591,7 +591,7 @@ CanvasRenderingContext2D::PreparedText CanvasRenderingContext2D::prepare_text(By
|
|||
Gfx::FloatPoint anchor { 0, 0 };
|
||||
auto physical_alignment = Gfx::TextAlignment::CenterLeft;
|
||||
|
||||
auto glyph_run = Gfx::shape_text(anchor, 0, replaced_text.code_points(), *font, Gfx::GlyphRun::TextType::Ltr);
|
||||
auto glyph_run = Gfx::shape_text(anchor, 0, replaced_text.code_points(), *font, Gfx::GlyphRun::TextType::Ltr, {});
|
||||
|
||||
// 8. Let result be an array constructed by iterating over each glyph in the inline box from left to right (if any), adding to the array, for each glyph, the shape of the glyph as it is in the inline box, positioned on a coordinate space using CSS pixels with its origin is at the anchor point.
|
||||
PreparedText prepared_text { glyph_run, physical_alignment, { 0, 0, static_cast<int>(glyph_run->width()), static_cast<int>(height) } };
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibGfx/Font/FontVariant.h>
|
||||
#include <LibWeb/Layout/BreakNode.h>
|
||||
#include <LibWeb/Layout/InlineFormattingContext.h>
|
||||
#include <LibWeb/Layout/InlineLevelIterator.h>
|
||||
|
@ -200,6 +201,261 @@ Gfx::GlyphRun::TextType InlineLevelIterator::resolve_text_direction_from_context
|
|||
return Gfx::GlyphRun::TextType::ContextDependent;
|
||||
}
|
||||
|
||||
HashMap<StringView, u8> InlineLevelIterator::shape_features_map() const
|
||||
{
|
||||
HashMap<StringView, u8> features;
|
||||
|
||||
auto& computed_values = m_current_node->computed_values();
|
||||
|
||||
// 6.4 https://drafts.csswg.org/css-fonts/#font-variant-ligatures-prop
|
||||
auto ligature_or_null = computed_values.font_variant_ligatures();
|
||||
if (ligature_or_null.has_value()) {
|
||||
auto ligature = ligature_or_null.release_value();
|
||||
if (ligature.none) {
|
||||
/* nothing */
|
||||
} else {
|
||||
switch (ligature.common) {
|
||||
case Gfx::FontVariantLigatures::Common::Common:
|
||||
// Enables display of common ligatures (OpenType features: liga, clig).
|
||||
features.set("liga"sv, 1);
|
||||
features.set("clig"sv, 1);
|
||||
break;
|
||||
case Gfx::FontVariantLigatures::Common::NoCommon:
|
||||
// Disables display of common ligatures (OpenType features: liga, clig).
|
||||
features.set("liga"sv, 0);
|
||||
features.set("clig"sv, 0);
|
||||
break;
|
||||
case Gfx::FontVariantLigatures::Common::Unset:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (ligature.discretionary) {
|
||||
case Gfx::FontVariantLigatures::Discretionary::Discretionary:
|
||||
// Enables display of discretionary ligatures (OpenType feature: dlig).
|
||||
features.set("dlig"sv, 1);
|
||||
break;
|
||||
case Gfx::FontVariantLigatures::Discretionary::NoDiscretionary:
|
||||
// Disables display of discretionary ligatures (OpenType feature: dlig).
|
||||
features.set("dlig"sv, 0);
|
||||
break;
|
||||
case Gfx::FontVariantLigatures::Discretionary::Unset:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (ligature.historical) {
|
||||
case Gfx::FontVariantLigatures::Historical::Historical:
|
||||
// Enables display of historical ligatures (OpenType feature: hlig).
|
||||
features.set("hlig"sv, 1);
|
||||
break;
|
||||
case Gfx::FontVariantLigatures::Historical::NoHistorical:
|
||||
// Disables display of historical ligatures (OpenType feature: hlig).
|
||||
features.set("hlig"sv, 0);
|
||||
break;
|
||||
case Gfx::FontVariantLigatures::Historical::Unset:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (ligature.contextual) {
|
||||
case Gfx::FontVariantLigatures::Contextual::Contextual:
|
||||
// Enables display of contextual ligatures (OpenType feature: calt).
|
||||
features.set("calt"sv, 1);
|
||||
break;
|
||||
case Gfx::FontVariantLigatures::Contextual::NoContextual:
|
||||
// Disables display of contextual ligatures (OpenType feature: calt).
|
||||
features.set("calt"sv, 0);
|
||||
break;
|
||||
case Gfx::FontVariantLigatures::Contextual::Unset:
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// A value of normal specifies that common default features are enabled, as described in detail in the next section.
|
||||
features.set("liga"sv, 1);
|
||||
features.set("clig"sv, 1);
|
||||
}
|
||||
|
||||
// 6.5 https://drafts.csswg.org/css-fonts/#font-variant-position-prop
|
||||
switch (computed_values.font_variant_position()) {
|
||||
case CSS::FontVariantPosition::Normal:
|
||||
// None of the features listed below are enabled.
|
||||
break;
|
||||
case CSS::FontVariantPosition::Sub:
|
||||
// Enables display of subscripts (OpenType feature: subs).
|
||||
features.set("subs"sv, 1);
|
||||
break;
|
||||
case CSS::FontVariantPosition::Super:
|
||||
// Enables display of superscripts (OpenType feature: sups).
|
||||
features.set("sups"sv, 1);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// 6.6 https://drafts.csswg.org/css-fonts/#font-variant-caps-prop
|
||||
switch (computed_values.font_variant_caps()) {
|
||||
case CSS::FontVariantCaps::Normal:
|
||||
// None of the features listed below are enabled.
|
||||
break;
|
||||
case CSS::FontVariantCaps::SmallCaps:
|
||||
// Enables display of small capitals (OpenType feature: smcp). Small-caps glyphs typically use the form of uppercase letters but are reduced to the size of lowercase letters.
|
||||
features.set("smcp"sv, 1);
|
||||
break;
|
||||
case CSS::FontVariantCaps::AllSmallCaps:
|
||||
// Enables display of small capitals for both upper and lowercase letters (OpenType features: c2sc, smcp).
|
||||
features.set("c2sc"sv, 1);
|
||||
features.set("smcp"sv, 1);
|
||||
break;
|
||||
case CSS::FontVariantCaps::PetiteCaps:
|
||||
// Enables display of petite capitals (OpenType feature: pcap).
|
||||
features.set("pcap"sv, 1);
|
||||
break;
|
||||
case CSS::FontVariantCaps::AllPetiteCaps:
|
||||
// Enables display of petite capitals for both upper and lowercase letters (OpenType features: c2pc, pcap).
|
||||
features.set("c2pc"sv, 1);
|
||||
features.set("pcap"sv, 1);
|
||||
break;
|
||||
case CSS::FontVariantCaps::Unicase:
|
||||
// Enables display of mixture of small capitals for uppercase letters with normal lowercase letters (OpenType feature: unic).
|
||||
features.set("unic"sv, 1);
|
||||
break;
|
||||
case CSS::FontVariantCaps::TitlingCaps:
|
||||
// Enables display of titling capitals (OpenType feature: titl).
|
||||
features.set("titl"sv, 1);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// 6.7 https://drafts.csswg.org/css-fonts/#font-variant-numeric-prop
|
||||
auto numeric_or_null = computed_values.font_variant_numeric();
|
||||
if (numeric_or_null.has_value()) {
|
||||
auto numeric = numeric_or_null.release_value();
|
||||
if (numeric.figure == Gfx::FontVariantNumeric::Figure::Oldstyle) {
|
||||
// Enables display of old-style numerals (OpenType feature: onum).
|
||||
features.set("onum"sv, 1);
|
||||
} else if (numeric.figure == Gfx::FontVariantNumeric::Figure::Lining) {
|
||||
// Enables display of lining numerals (OpenType feature: lnum).
|
||||
features.set("lnum"sv, 1);
|
||||
}
|
||||
|
||||
if (numeric.spacing == Gfx::FontVariantNumeric::Spacing::Proportional) {
|
||||
// Enables display of proportional numerals (OpenType feature: pnum).
|
||||
features.set("pnum"sv, 1);
|
||||
} else if (numeric.spacing == Gfx::FontVariantNumeric::Spacing::Tabular) {
|
||||
// Enables display of tabular numerals (OpenType feature: tnum).
|
||||
features.set("tnum"sv, 1);
|
||||
}
|
||||
|
||||
if (numeric.fraction == Gfx::FontVariantNumeric::Fraction::Diagonal) {
|
||||
// Enables display of diagonal fractions (OpenType feature: frac).
|
||||
features.set("frac"sv, 1);
|
||||
} else if (numeric.fraction == Gfx::FontVariantNumeric::Fraction::Stacked) {
|
||||
// Enables display of stacked fractions (OpenType feature: afrc).
|
||||
features.set("afrc"sv, 1);
|
||||
features.set("afrc"sv, 1);
|
||||
}
|
||||
|
||||
if (numeric.ordinal) {
|
||||
// Enables display of letter forms used with ordinal numbers (OpenType feature: ordn).
|
||||
features.set("ordn"sv, 1);
|
||||
}
|
||||
if (numeric.slashed_zero) {
|
||||
// Enables display of slashed zeros (OpenType feature: zero).
|
||||
features.set("zero"sv, 1);
|
||||
}
|
||||
}
|
||||
|
||||
// 6.10 https://drafts.csswg.org/css-fonts/#font-variant-east-asian-prop
|
||||
auto east_asian_or_null = computed_values.font_variant_east_asian();
|
||||
if (east_asian_or_null.has_value()) {
|
||||
auto east_asian = east_asian_or_null.release_value();
|
||||
switch (east_asian.variant) {
|
||||
case Gfx::FontVariantEastAsian::Variant::Jis78:
|
||||
// Enables display of JIS78 forms (OpenType feature: jp78).
|
||||
features.set("jp78"sv, 1);
|
||||
break;
|
||||
case Gfx::FontVariantEastAsian::Variant::Jis83:
|
||||
// Enables display of JIS83 forms (OpenType feature: jp83).
|
||||
features.set("jp83"sv, 1);
|
||||
break;
|
||||
case Gfx::FontVariantEastAsian::Variant::Jis90:
|
||||
// Enables display of JIS90 forms (OpenType feature: jp90).
|
||||
features.set("jp90"sv, 1);
|
||||
break;
|
||||
case Gfx::FontVariantEastAsian::Variant::Jis04:
|
||||
// Enables display of JIS04 forms (OpenType feature: jp04).
|
||||
features.set("jp04"sv, 1);
|
||||
break;
|
||||
case Gfx::FontVariantEastAsian::Variant::Simplified:
|
||||
// Enables display of simplified forms (OpenType feature: smpl).
|
||||
features.set("smpl"sv, 1);
|
||||
break;
|
||||
case Gfx::FontVariantEastAsian::Variant::Traditional:
|
||||
// Enables display of traditional forms (OpenType feature: trad).
|
||||
features.set("trad"sv, 1);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
switch (east_asian.width) {
|
||||
case Gfx::FontVariantEastAsian::Width::FullWidth:
|
||||
// Enables display of full-width forms (OpenType feature: fwid).
|
||||
features.set("fwid"sv, 1);
|
||||
break;
|
||||
case Gfx::FontVariantEastAsian::Width::Proportional:
|
||||
// Enables display of proportional-width forms (OpenType feature: pwid).
|
||||
features.set("pwid"sv, 1);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (east_asian.ruby) {
|
||||
// Enables display of ruby forms (OpenType feature: ruby).
|
||||
features.set("ruby"sv, 1);
|
||||
}
|
||||
}
|
||||
|
||||
return features;
|
||||
}
|
||||
|
||||
Gfx::ShapeFeatures InlineLevelIterator::create_and_merge_font_features() const
|
||||
{
|
||||
HashMap<StringView, u8> merged_features;
|
||||
auto& computed_values = m_inline_formatting_context.containing_block().computed_values();
|
||||
|
||||
// https://www.w3.org/TR/css-fonts-3/#feature-precedence
|
||||
|
||||
// FIXME 1. Font features enabled by default, including features required for a given script.
|
||||
|
||||
// FIXME 2. If the font is defined via an @font-face rule, the font features implied by the font-feature-settings descriptor in the @font-face rule.
|
||||
|
||||
// 3. Font features implied by the value of the ‘font-variant’ property, the related ‘font-variant’ subproperties and any other CSS property that uses OpenType features (e.g. the ‘font-kerning’ property).
|
||||
for (auto& it : shape_features_map()) {
|
||||
merged_features.set(it.key, it.value);
|
||||
}
|
||||
|
||||
// FIXME 4. Feature settings determined by properties other than ‘font-variant’ or ‘font-feature-settings’. For example, setting a non-default value for the ‘letter-spacing’ property disables common ligatures.
|
||||
|
||||
// 5. Font features implied by the value of ‘font-feature-settings’ property.
|
||||
auto resolution_context = CSS::Length::ResolutionContext::for_layout_node(*m_current_node.ptr());
|
||||
auto font_feature_settings = computed_values.font_feature_settings();
|
||||
if (font_feature_settings.has_value()) {
|
||||
auto const& feature_settings = font_feature_settings.value();
|
||||
for (auto const& [key, feature_value] : feature_settings) {
|
||||
merged_features.set(key, feature_value.resolved(resolution_context));
|
||||
}
|
||||
}
|
||||
|
||||
Gfx::ShapeFeatures shape_features;
|
||||
shape_features.ensure_capacity(merged_features.size());
|
||||
|
||||
for (auto& it : merged_features) {
|
||||
shape_features.append({ { it.key[0], it.key[1], it.key[2], it.key[3] }, static_cast<u32>(it.value) });
|
||||
}
|
||||
|
||||
return shape_features;
|
||||
}
|
||||
|
||||
Optional<InlineLevelIterator::Item> InlineLevelIterator::next_without_lookahead()
|
||||
{
|
||||
if (!m_current_node)
|
||||
|
@ -293,7 +549,8 @@ Optional<InlineLevelIterator::Item> InlineLevelIterator::next_without_lookahead(
|
|||
x = tab_stop_dist.to_float();
|
||||
}
|
||||
|
||||
auto glyph_run = Gfx::shape_text({ x, 0 }, letter_spacing.to_float(), chunk.view, chunk.font, text_type);
|
||||
auto shape_features = create_and_merge_font_features();
|
||||
auto glyph_run = Gfx::shape_text({ x, 0 }, letter_spacing.to_float(), chunk.view, chunk.font, text_type, shape_features);
|
||||
|
||||
CSSPixels chunk_width = CSSPixels::nearest_value_for(glyph_run->width());
|
||||
|
||||
|
|
|
@ -68,6 +68,9 @@ private:
|
|||
|
||||
void add_extra_box_model_metrics_to_item(Item&, bool add_leading_metrics, bool add_trailing_metrics);
|
||||
|
||||
HashMap<StringView, u8> shape_features_map() const;
|
||||
Gfx::ShapeFeatures create_and_merge_font_features() const;
|
||||
|
||||
Layout::Node const* next_inline_node_in_pre_order(Layout::Node const& current, Layout::Node const* stay_within);
|
||||
|
||||
Layout::InlineFormattingContext& m_inline_formatting_context;
|
||||
|
|
|
@ -473,12 +473,24 @@ void NodeWithStyle::apply_style(const CSS::StyleProperties& computed_style)
|
|||
if (auto box_sizing = computed_style.box_sizing(); box_sizing.has_value())
|
||||
computed_values.set_box_sizing(box_sizing.release_value());
|
||||
|
||||
if (auto maybe_font_variant = computed_style.font_variant(); maybe_font_variant.has_value())
|
||||
computed_values.set_font_variant(maybe_font_variant.release_value());
|
||||
if (auto maybe_font_language_override = computed_style.font_language_override(); maybe_font_language_override.has_value())
|
||||
computed_values.set_font_language_override(maybe_font_language_override.release_value());
|
||||
if (auto maybe_font_feature_settings = computed_style.font_feature_settings(); maybe_font_feature_settings.has_value())
|
||||
computed_values.set_font_feature_settings(maybe_font_feature_settings.release_value());
|
||||
if (auto maybe_font_variant_alternates = computed_style.font_variant_alternates(); maybe_font_variant_alternates.has_value())
|
||||
computed_values.set_font_variant_alternates(maybe_font_variant_alternates.release_value());
|
||||
if (auto maybe_font_variant_caps = computed_style.font_variant_caps(); maybe_font_variant_caps.has_value())
|
||||
computed_values.set_font_variant_caps(maybe_font_variant_caps.release_value());
|
||||
if (auto maybe_font_variant_east_asian = computed_style.font_variant_east_asian(); maybe_font_variant_east_asian.has_value())
|
||||
computed_values.set_font_variant_east_asian(maybe_font_variant_east_asian.release_value());
|
||||
if (auto maybe_font_variant_emoji = computed_style.font_variant_emoji(); maybe_font_variant_emoji.has_value())
|
||||
computed_values.set_font_variant_emoji(maybe_font_variant_emoji.release_value());
|
||||
if (auto maybe_font_variant_ligatures = computed_style.font_variant_ligatures(); maybe_font_variant_ligatures.has_value())
|
||||
computed_values.set_font_variant_ligatures(maybe_font_variant_ligatures.release_value());
|
||||
if (auto maybe_font_variant_numeric = computed_style.font_variant_numeric(); maybe_font_variant_numeric.has_value())
|
||||
computed_values.set_font_variant_numeric(maybe_font_variant_numeric.release_value());
|
||||
if (auto maybe_font_variant_position = computed_style.font_variant_position(); maybe_font_variant_position.has_value())
|
||||
computed_values.set_font_variant_position(maybe_font_variant_position.release_value());
|
||||
if (auto maybe_font_variation_settings = computed_style.font_variation_settings(); maybe_font_variation_settings.has_value())
|
||||
computed_values.set_font_variation_settings(maybe_font_variation_settings.release_value());
|
||||
|
||||
|
|
|
@ -230,7 +230,7 @@ void DisplayListRecorder::draw_text(Gfx::IntRect const& rect, String raw_text, G
|
|||
if (rect.is_empty())
|
||||
return;
|
||||
|
||||
auto glyph_run = Gfx::shape_text({}, 0, raw_text.code_points(), font, Gfx::GlyphRun::TextType::Ltr);
|
||||
auto glyph_run = Gfx::shape_text({}, 0, raw_text.code_points(), font, Gfx::GlyphRun::TextType::Ltr, {});
|
||||
float baseline_x = 0;
|
||||
if (alignment == Gfx::TextAlignment::CenterLeft) {
|
||||
baseline_x = rect.x();
|
||||
|
|
Loading…
Add table
Reference in a new issue