Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add attributes and properties for language and direction #563

Merged
merged 3 commits into from
Jan 9, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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->value.GetReference<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: 17 additions & 10 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 @@ -205,7 +206,9 @@ 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 +231,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 +256,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 +356,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 +465,12 @@ 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 TextShapingContext text_shaping_context{
GetComputedValues().language(), GetComputedValues().direction(), GetComputedValues().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
9 changes: 7 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,17 @@ 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 TextShapingContext text_shaping_context{
element->GetComputedValues().language(),
element->GetComputedValues().direction(),
element->GetComputedValues().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
4 changes: 2 additions & 2 deletions Source/Core/FontEngineDefault/FontEngineInterfaceDefault.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,11 @@ class RMLUICORE_API FontEngineInterfaceDefault : public FontEngineInterface {
const FontMetrics& GetFontMetrics(FontFaceHandle handle) override;

/// Returns the width a string will take up if rendered with this handle.
int GetStringWidth(FontFaceHandle, const String& string, float letter_spacing, Character prior_character) override;
int GetStringWidth(FontFaceHandle, const String& string, const TextShapingContext& text_shaping_context, Character prior_character) override;

/// Generates the geometry required to render a single line of text.
int GenerateString(FontFaceHandle, FontEffectsHandle, const String& string, const Vector2f& position, const Colourb& colour, float opacity,
float letter_spacing, GeometryList& geometry) override;
const TextShapingContext& text_shaping_context, GeometryList& geometry) override;

/// Returns the current version of the font face.
int GetVersion(FontFaceHandle handle) override;
Expand Down
Loading