Skip to content

Commit

Permalink
Add attributes and properties for language and direction (#563)
Browse files Browse the repository at this point in the history
  • Loading branch information
TriangulumDesire authored Jan 9, 2024
1 parent bb25b88 commit f9b8f6b
Show file tree
Hide file tree
Showing 18 changed files with 285 additions and 31 deletions.
1 change: 1 addition & 0 deletions CMake/FileList.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@ set(Core_PUB_HDR_FILES
${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/StyleTypes.h
${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/SystemInterface.h
${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Texture.h
${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/TextShapingContext.h
${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Traits.h
${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Transform.h
${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/TransformPrimitive.h
Expand Down
1 change: 1 addition & 0 deletions Include/RmlUi/Core.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@
#include "Core/StyleSheetSpecification.h"
#include "Core/StyleTypes.h"
#include "Core/SystemInterface.h"
#include "Core/TextShapingContext.h"
#include "Core/Texture.h"
#include "Core/Transform.h"
#include "Core/TransformPrimitive.h"
Expand Down
10 changes: 9 additions & 1 deletion Include/RmlUi/Core/ComputedValues.h
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ namespace Style {
font_weight(FontWeight::Normal), has_letter_spacing(0), font_style(FontStyle::Normal), has_font_effect(false),
pointer_events(PointerEvents::Auto), focus(Focus::Auto), text_align(TextAlign::Left), text_decoration(TextDecoration::None),
text_transform(TextTransform::None), white_space(WhiteSpace::Normal), word_break(WordBreak::Normal),
line_height_inherit_type(LineHeight::Number)
direction(Direction::Auto), line_height_inherit_type(LineHeight::Number)
{}

// Font face used to render text and resolve ex properties. Does not represent a true property
Expand All @@ -141,9 +141,13 @@ namespace Style {
WhiteSpace white_space : 3;
WordBreak word_break : 2;

Direction direction : 2;

LineHeight::InheritType line_height_inherit_type : 1;
float line_height = 12.f * 1.2f;
float line_height_inherit = 1.2f;

String language = "";
};

struct RareValues {
Expand Down Expand Up @@ -257,6 +261,8 @@ namespace Style {
Colourb color() const { return inherited.color; }
float opacity() const { return inherited.opacity; }
LineHeight line_height() const { return LineHeight(inherited.line_height, inherited.line_height_inherit_type, inherited.line_height_inherit); }
const String& language() const { return inherited.language; }
Direction direction() const { return inherited.direction; }

// -- Rare --
MinWidth min_width() const { return LengthPercentage(rare.min_width_type, rare.min_width); }
Expand Down Expand Up @@ -349,6 +355,8 @@ namespace Style {
void color (Colourb value) { inherited.color = value; }
void opacity (float value) { inherited.opacity = value; }
void line_height (LineHeight value) { inherited.line_height = value.value; inherited.line_height_inherit_type = value.inherit_type; inherited.line_height_inherit = value.inherit_value; }
void language (const String& value) { inherited.language = value; }
void direction (Direction value) { inherited.direction = value; }
// Rare
void min_width (MinWidth value) { rare.min_width_type = value.type; rare.min_width = value.value; }
void max_width (MaxWidth value) { rare.max_width_type = value.type; rare.max_width = value.value; }
Expand Down
10 changes: 6 additions & 4 deletions Include/RmlUi/Core/FontEngineInterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#include "Geometry.h"
#include "Header.h"
#include "StyleTypes.h"
#include "TextShapingContext.h"
#include "Types.h"

namespace Rml {
Expand Down Expand Up @@ -90,11 +91,12 @@ class RMLUICORE_API FontEngineInterface {
/// Called by RmlUi when it wants to retrieve the width of a string when rendered with this handle.
/// @param[in] handle The font handle.
/// @param[in] string The string to measure.
/// @param[in] letter_spacing The letter spacing size in pixels.
/// @param[in] text_shaping_context Additional parameters that provide context for text shaping.
/// @param[in] prior_character The optionally-specified character that immediately precedes the string. This may have an impact on the string
/// width due to kerning.
/// @return The width, in pixels, this string will occupy if rendered with this handle.
virtual int GetStringWidth(FontFaceHandle handle, const String& string, float letter_spacing, Character prior_character = Character::Null);
virtual int GetStringWidth(FontFaceHandle handle, const String& string, const TextShapingContext& text_shaping_context,
Character prior_character = Character::Null);

/// Called by RmlUi when it wants to retrieve the geometry required to render a single line of text.
/// @param[in] face_handle The font handle.
Expand All @@ -103,11 +105,11 @@ class RMLUICORE_API FontEngineInterface {
/// @param[in] position The position of the baseline of the first character to render.
/// @param[in] colour The colour to render the text. Colour alpha is premultiplied with opacity.
/// @param[in] opacity The opacity of the text, should be applied to font effects.
/// @param[in] letter_spacing The letter spacing size in pixels.
/// @param[in] text_shaping_context Additional parameters that provide context for text shaping.
/// @param[out] geometry An array of geometries to generate the geometry into.
/// @return The width, in pixels, of the string geometry.
virtual int GenerateString(FontFaceHandle face_handle, FontEffectsHandle font_effects_handle, const String& string, const Vector2f& position,
const Colourb& colour, float opacity, float letter_spacing, GeometryList& geometry);
const Colourb& colour, float opacity, const TextShapingContext& text_shaping_context, GeometryList& geometry);

/// Called by RmlUi to determine if the text geometry is required to be re-generated. Whenever the returned version
/// is changed, all geometry belonging to the given face handle will be re-generated.
Expand Down
3 changes: 3 additions & 0 deletions Include/RmlUi/Core/ID.h
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,9 @@ enum class PropertyId : uint8_t {
NavDown,
NavLeft,

RmlUi_Language,
RmlUi_Direction,

NumDefinedIds,
FirstCustomId = NumDefinedIds,

Expand Down
2 changes: 2 additions & 0 deletions Include/RmlUi/Core/StyleTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,8 @@ namespace Style {

enum class Nav : uint8_t { None, Auto, Horizontal, Vertical };

enum class Direction : uint8_t { Auto, Ltr, Rtl };

class ComputedValues;

} // namespace Style
Expand Down
47 changes: 47 additions & 0 deletions Include/RmlUi/Core/TextShapingContext.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* This source file is part of RmlUi, the HTML/CSS Interface Middleware
*
* For the latest information, see http://github.com/mikke89/RmlUi
*
* Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd
* Copyright (c) 2019-2023 The RmlUi Team, and contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/

#ifndef RMLUI_CORE_TEXTSHAPINGCONTEXT_H
#define RMLUI_CORE_TEXTSHAPINGCONTEXT_H

#include "StyleTypes.h"
#include "Types.h"

namespace Rml {

/*
Data extracted from the properties of an element to help provide context for text shaping and spacing.
*/
struct TextShapingContext {
const String& language;
Style::Direction text_direction = Style::Direction::Auto;
float letter_spacing = 0.0f; // Measured in pixels.
};

} // namespace Rml
#endif
5 changes: 3 additions & 2 deletions Samples/basic/bitmapfont/src/FontEngineInterfaceBitmap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,14 +74,15 @@ const FontMetrics& FontEngineInterfaceBitmap::GetFontMetrics(FontFaceHandle hand
return handle_bitmap->GetMetrics();
}

int FontEngineInterfaceBitmap::GetStringWidth(FontFaceHandle handle, const String& string, float /*letter_spacing*/, Character prior_character)
int FontEngineInterfaceBitmap::GetStringWidth(FontFaceHandle handle, const String& string, const TextShapingContext& /*text_shaping_context*/,
Character prior_character)
{
auto handle_bitmap = reinterpret_cast<FontFaceBitmap*>(handle);
return handle_bitmap->GetStringWidth(string, prior_character);
}

int FontEngineInterfaceBitmap::GenerateString(FontFaceHandle handle, FontEffectsHandle /*font_effects_handle*/, const String& string,
const Vector2f& position, const Colourb& colour, float /*opacity*/, float /*letter_spacing*/, GeometryList& geometry)
const Vector2f& position, const Colourb& colour, float /*opacity*/, const TextShapingContext& /*text_shaping_context*/, GeometryList& geometry)
{
auto handle_bitmap = reinterpret_cast<FontFaceBitmap*>(handle);
return handle_bitmap->GenerateString(string, position, colour, geometry);
Expand Down
6 changes: 4 additions & 2 deletions Samples/basic/bitmapfont/src/FontEngineInterfaceBitmap.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ using Rml::Style::FontWeight;
using Rml::FontEffectList;
using Rml::FontMetrics;
using Rml::GeometryList;
using Rml::TextShapingContext;

class FontEngineInterfaceBitmap : public Rml::FontEngineInterface {
public:
Expand All @@ -73,11 +74,12 @@ class FontEngineInterfaceBitmap : public Rml::FontEngineInterface {
const FontMetrics& GetFontMetrics(FontFaceHandle handle) override;

/// Called by RmlUi when it wants to retrieve the width of a string when rendered with this handle.
int GetStringWidth(FontFaceHandle handle, const String& string, float letter_spacing, Character prior_character = Character::Null) override;
int GetStringWidth(FontFaceHandle handle, const String& string, const TextShapingContext& text_shaping_context,
Character prior_character = Character::Null) override;

/// Called by RmlUi when it wants to retrieve the geometry required to render a single line of text.
int GenerateString(FontFaceHandle face_handle, FontEffectsHandle font_effects_handle, const String& string, const Vector2f& position,
const Colourb& colour, float opacity, float letter_spacing, GeometryList& geometry) override;
const Colourb& colour, float opacity, const TextShapingContext& text_shaping_context, GeometryList& geometry) override;

/// Called by RmlUi to determine if the text geometry is required to be re-generated.eometry.
int GetVersion(FontFaceHandle handle) override;
Expand Down
26 changes: 26 additions & 0 deletions Source/Core/Element.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1645,6 +1645,32 @@ void Element::OnAttributeChange(const ElementAttributes& changed_attributes)
else if (value.GetType() != Variant::NONE)
Log::Message(Log::LT_WARNING, "Invalid 'style' attribute, string type required. In element: %s", GetAddress().c_str());
}
else if (attribute == "lang")
{
if (value.GetType() == Variant::STRING)
meta->style.SetProperty(PropertyId::RmlUi_Language, Property(value.GetReference<String>(), Unit::STRING));
else if (value.GetType() != Variant::NONE)
Log::Message(Log::LT_WARNING, "Invalid 'lang' attribute, string type required. In element: %s", GetAddress().c_str());
}
else if (attribute == "dir")
{
if (value.GetType() == Variant::STRING)
{
const String& dir_value = value.GetReference<String>();

if (dir_value == "auto")
meta->style.SetProperty(PropertyId::RmlUi_Direction, Property(Style::Direction::Auto));
else if (dir_value == "ltr")
meta->style.SetProperty(PropertyId::RmlUi_Direction, Property(Style::Direction::Ltr));
else if (dir_value == "rtl")
meta->style.SetProperty(PropertyId::RmlUi_Direction, Property(Style::Direction::Rtl));
else
Log::Message(Log::LT_WARNING, "Invalid 'dir' attribute '%s', value must be 'auto', 'ltr', or 'rtl'. In element: %s",
dir_value.c_str(), GetAddress().c_str());
}
else if (value.GetType() != Variant::NONE)
Log::Message(Log::LT_WARNING, "Invalid 'dir' attribute, string type required. In element: %s", GetAddress().c_str());
}
}

// Any change to the attributes may affect which styles apply to the current element, in particular due to attribute selectors, ID selectors, and
Expand Down
7 changes: 7 additions & 0 deletions Source/Core/ElementStyle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -856,6 +856,13 @@ PropertyIdSet ElementStyle::ComputeValues(Style::ComputedValues& values, const S
values.flex_basis(ComputeLengthPercentageAuto(p, font_size, document_font_size, dp_ratio, vp_dimensions));
break;

case PropertyId::RmlUi_Language:
values.language(p->Get<String>());
break;
case PropertyId::RmlUi_Direction:
values.direction(p->Get<Direction>());
break;

// Fetched from element's properties.
case PropertyId::Cursor:
case PropertyId::Transition:
Expand Down
27 changes: 15 additions & 12 deletions Source/Core/ElementText.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
#include "../../Include/RmlUi/Core/GeometryUtilities.h"
#include "../../Include/RmlUi/Core/Profiling.h"
#include "../../Include/RmlUi/Core/Property.h"
#include "../../Include/RmlUi/Core/TextShapingContext.h"
#include "ComputeProperty.h"
#include "ElementDefinition.h"
#include "ElementStyle.h"
Expand Down Expand Up @@ -196,7 +197,7 @@ bool ElementText::GenerateLine(String& line, int& line_length, float& line_width

// Determine how we are processing white-space while formatting the text.
using namespace Style;
auto& computed = GetComputedValues();
const auto& computed = GetComputedValues();
WhiteSpace white_space_property = computed.white_space();
bool collapse_white_space =
white_space_property == WhiteSpace::Normal || white_space_property == WhiteSpace::Nowrap || white_space_property == WhiteSpace::Preline;
Expand All @@ -205,8 +206,7 @@ bool ElementText::GenerateLine(String& line, int& line_length, float& line_width
bool break_at_endline =
white_space_property == WhiteSpace::Pre || white_space_property == WhiteSpace::Prewrap || white_space_property == WhiteSpace::Preline;

float letter_spacing = computed.letter_spacing();

const TextShapingContext text_shaping_context{ computed.language(), computed.direction(), computed.letter_spacing() };
TextTransform text_transform_property = computed.text_transform();
WordBreak word_break = computed.word_break();

Expand All @@ -228,7 +228,7 @@ bool ElementText::GenerateLine(String& line, int& line_length, float& line_width
// Generate the next token and determine its pixel-length.
bool break_line = BuildToken(token, next_token_begin, string_end, line.empty() && trim_whitespace_prefix, collapse_white_space,
break_at_endline, text_transform_property, decode_escape_characters);
int token_width = font_engine_interface->GetStringWidth(font_face_handle, token, letter_spacing, previous_codepoint);
int token_width = font_engine_interface->GetStringWidth(font_face_handle, token, text_shaping_context, previous_codepoint);

// If we're breaking to fit a line box, check if the token can fit on the line before we add it.
if (break_at_line)
Expand All @@ -253,7 +253,7 @@ bool ElementText::GenerateLine(String& line, int& line_length, float& line_width
const char* partial_string_end = StringUtilities::SeekBackwardUTF8(token_begin + i, token_begin);
BuildToken(token, next_token_begin, partial_string_end, line.empty() && trim_whitespace_prefix, collapse_white_space,
break_at_endline, text_transform_property, decode_escape_characters);
token_width = font_engine_interface->GetStringWidth(font_face_handle, token, letter_spacing, previous_codepoint);
token_width = font_engine_interface->GetStringWidth(font_face_handle, token, text_shaping_context, previous_codepoint);

if (force_loop_break_after_next || token_width <= max_token_width)
{
Expand Down Expand Up @@ -353,11 +353,13 @@ void ElementText::OnPropertyChange(const PropertyIdSet& changed_properties)
}
}

if (changed_properties.Contains(PropertyId::FontFamily) || //
changed_properties.Contains(PropertyId::FontWeight) || //
changed_properties.Contains(PropertyId::FontStyle) || //
changed_properties.Contains(PropertyId::FontSize) || //
changed_properties.Contains(PropertyId::LetterSpacing))
if (changed_properties.Contains(PropertyId::FontFamily) || //
changed_properties.Contains(PropertyId::FontWeight) || //
changed_properties.Contains(PropertyId::FontStyle) || //
changed_properties.Contains(PropertyId::FontSize) || //
changed_properties.Contains(PropertyId::LetterSpacing) || //
changed_properties.Contains(PropertyId::RmlUi_Language) || //
changed_properties.Contains(PropertyId::RmlUi_Direction))
{
font_face_changed = true;

Expand Down Expand Up @@ -460,10 +462,11 @@ void ElementText::GenerateGeometry(const FontFaceHandle font_face_handle)

void ElementText::GenerateGeometry(const FontFaceHandle font_face_handle, Line& line)
{
const float letter_spacing = GetComputedValues().letter_spacing();
const auto& computed = GetComputedValues();
const TextShapingContext text_shaping_context{ computed.language(), computed.direction(), computed.letter_spacing() };

line.width = GetFontEngineInterface()->GenerateString(font_face_handle, font_effects_handle, line.text, line.position, colour, opacity,
letter_spacing, geometry);
text_shaping_context, geometry);
}

void ElementText::GenerateDecoration(const FontFaceHandle font_face_handle)
Expand Down
6 changes: 4 additions & 2 deletions Source/Core/ElementUtilities.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#include "../../Include/RmlUi/Core/Factory.h"
#include "../../Include/RmlUi/Core/FontEngineInterface.h"
#include "../../Include/RmlUi/Core/RenderInterface.h"
#include "../../Include/RmlUi/Core/TextShapingContext.h"
#include "DataController.h"
#include "DataModel.h"
#include "DataView.h"
Expand Down Expand Up @@ -155,13 +156,14 @@ float ElementUtilities::GetDensityIndependentPixelRatio(Element* element)

int ElementUtilities::GetStringWidth(Element* element, const String& string, Character prior_character)
{
const float letter_spacing = element->GetComputedValues().letter_spacing();
const auto& computed = element->GetComputedValues();
const TextShapingContext text_shaping_context{ computed.language(), computed.direction(), computed.letter_spacing() };

FontFaceHandle font_face_handle = element->GetFontFaceHandle();
if (font_face_handle == 0)
return 0;

return GetFontEngineInterface()->GetStringWidth(font_face_handle, string, letter_spacing, prior_character);
return GetFontEngineInterface()->GetStringWidth(font_face_handle, string, text_shaping_context, prior_character);
}

bool ElementUtilities::GetClippingRegion(Vector2i& clip_origin, Vector2i& clip_dimensions, Element* element)
Expand Down
10 changes: 6 additions & 4 deletions Source/Core/FontEngineDefault/FontEngineInterfaceDefault.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,17 +71,19 @@ const FontMetrics& FontEngineInterfaceDefault::GetFontMetrics(FontFaceHandle han
return handle_default->GetFontMetrics();
}

int FontEngineInterfaceDefault::GetStringWidth(FontFaceHandle handle, const String& string, float letter_spacing, Character prior_character)
int FontEngineInterfaceDefault::GetStringWidth(FontFaceHandle handle, const String& string, const TextShapingContext& text_shaping_context,
Character prior_character)
{
auto handle_default = reinterpret_cast<FontFaceHandleDefault*>(handle);
return handle_default->GetStringWidth(string, letter_spacing, prior_character);
return handle_default->GetStringWidth(string, text_shaping_context.letter_spacing, prior_character);
}

int FontEngineInterfaceDefault::GenerateString(FontFaceHandle handle, FontEffectsHandle font_effects_handle, const String& string,
const Vector2f& position, const Colourb& colour, float opacity, float letter_spacing, GeometryList& geometry)
const Vector2f& position, const Colourb& colour, float opacity, const TextShapingContext& text_shaping_context, GeometryList& geometry)
{
auto handle_default = reinterpret_cast<FontFaceHandleDefault*>(handle);
return handle_default->GenerateString(geometry, string, position, colour, opacity, letter_spacing, (int)font_effects_handle);
return handle_default->GenerateString(geometry, string, position, colour, opacity, text_shaping_context.letter_spacing,
(int)font_effects_handle);
}

int FontEngineInterfaceDefault::GetVersion(FontFaceHandle handle)
Expand Down
Loading

0 comments on commit f9b8f6b

Please sign in to comment.