From b17f8651ec0a928b0f527419cb6a65a13c8c2ad3 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Sun, 26 Jan 2025 23:39:47 -0800
Subject: [PATCH] Added functions to set the direction and script of text
objects
Also switched direction and script to match HarfBuzz datatypes.
---
CHANGES.txt | 4 +
examples/showfont.c | 3 +-
include/SDL3_ttf/SDL_ttf.h | 149 +++++++++++--------
src/SDL_ttf.c | 293 +++++++++++++++++--------------------
src/SDL_ttf.sym | 4 +
5 files changed, 234 insertions(+), 219 deletions(-)
diff --git a/CHANGES.txt b/CHANGES.txt
index 2b61e537..99af710a 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -12,6 +12,10 @@
- TTF_GetTextEngine()
- TTF_SetTextFont()
- TTF_GetTextFont()
+ - TTF_SetTextDirection()
+ - TTF_GetTextDirection()
+ - TTF_SetTextScript()
+ - TTF_GetTextScript()
- TTF_SetTextColor()
- TTF_SetTextColorFloat()
- TTF_GetTextColor()
diff --git a/examples/showfont.c b/examples/showfont.c
index cb9990d6..3d326bc8 100644
--- a/examples/showfont.c
+++ b/examples/showfont.c
@@ -193,7 +193,8 @@ static void HandleKeyDown(Scene *scene, SDL_Event *event)
case SDLK_R:
/* Toggle layout direction */
- if (TTF_GetFontDirection(scene->font) == TTF_DIRECTION_LTR) {
+ if (TTF_GetFontDirection(scene->font) == TTF_DIRECTION_INVALID ||
+ TTF_GetFontDirection(scene->font) == TTF_DIRECTION_LTR) {
TTF_SetFontDirection(scene->font, TTF_DIRECTION_RTL);
} else if (TTF_GetFontDirection(scene->font) == TTF_DIRECTION_RTL) {
TTF_SetFontDirection(scene->font, TTF_DIRECTION_LTR);
diff --git a/include/SDL3_ttf/SDL_ttf.h b/include/SDL3_ttf/SDL_ttf.h
index 501334d9..31d85c00 100644
--- a/include/SDL3_ttf/SDL_ttf.h
+++ b/include/SDL3_ttf/SDL_ttf.h
@@ -849,34 +849,29 @@ extern SDL_DECLSPEC const char * SDLCALL TTF_GetFontStyleName(const TTF_Font *fo
/**
* Direction flags
*
+ * The values here are chosen to match hb_direction_t.
+ *
* \since This enum is available since SDL_ttf 3.0.0.
*
* \sa TTF_SetFontDirection
*/
typedef enum TTF_Direction
{
- TTF_DIRECTION_LTR = 0, /* Left to Right */
- TTF_DIRECTION_RTL, /* Right to Left */
- TTF_DIRECTION_TTB, /* Top to Bottom */
- TTF_DIRECTION_BTT /* Bottom to Top */
+ TTF_DIRECTION_INVALID = 0,
+ TTF_DIRECTION_LTR = 4, /**< Left to Right */
+ TTF_DIRECTION_RTL, /**< Right to Left */
+ TTF_DIRECTION_TTB, /**< Top to Bottom */
+ TTF_DIRECTION_BTT /**< Bottom to Top */
} TTF_Direction;
/**
- * Set direction to be used for text shaping by a font.
- *
- * Possible direction values are:
- *
- * - `TTF_DIRECTION_LTR` (Left to Right)
- * - `TTF_DIRECTION_RTL` (Right to Left)
- * - `TTF_DIRECTION_TTB` (Top to Bottom)
- * - `TTF_DIRECTION_BTT` (Bottom to Top)
+ * Set the direction to be used for text shaping by a font.
*
- * If SDL_ttf was not built with HarfBuzz support, this function returns
- * false.
+ * This function only supports left-to-right text shaping if SDL_ttf was not built with HarfBuzz support.
*
* This updates any TTF_Text objects using this font.
*
- * \param font the font to specify a direction for.
+ * \param font the font to modify.
* \param direction the new direction for text to flow.
* \returns true on success or false on failure; call SDL_GetError() for more
* information.
@@ -889,17 +884,9 @@ typedef enum TTF_Direction
extern SDL_DECLSPEC bool SDLCALL TTF_SetFontDirection(TTF_Font *font, TTF_Direction direction);
/**
- * Get direction to be used for text shaping by a font.
- *
- * Possible direction values are:
+ * Get the direction to be used for text shaping by a font.
*
- * - `TTF_DIRECTION_LTR` (Left to Right)
- * - `TTF_DIRECTION_RTL` (Right to Left)
- * - `TTF_DIRECTION_TTB` (Top to Bottom)
- * - `TTF_DIRECTION_BTT` (Bottom to Top)
- *
- * If SDL_ttf was not built with HarfBuzz support, this function returns
- * TTF_DIRECTION_LTR.
+ * This defaults to TTF_DIRECTION_INVALID if it hasn't been set.
*
* \param font the font to query.
* \returns the direction to be used for text shaping.
@@ -912,72 +899,48 @@ extern SDL_DECLSPEC bool SDLCALL TTF_SetFontDirection(TTF_Font *font, TTF_Direct
extern SDL_DECLSPEC TTF_Direction SDLCALL TTF_GetFontDirection(TTF_Font *font);
/**
- * Set script to be used for text shaping by a font.
- *
- * The supplied script value must be a null-terminated string of exactly four
- * characters.
+ * Set the script to be used for text shaping by a font.
*
- * If SDL_ttf was not built with HarfBuzz support, this function returns
- * false.
+ * This returns false if SDL_ttf isn't build with HarfBuzz support.
*
* This updates any TTF_Text objects using this font.
*
- * \param font the font to specify a script name for.
- * \param script null-terminated string of exactly 4 characters.
+ * \param font the font to modify.
+ * \param script a script tag in the format used by HarfBuzz.
* \returns true on success or false on failure; call SDL_GetError() for more
* information.
*
- * \threadsafety This function is not thread-safe.
+ * \threadsafety This function should be called on the thread that created the
+ * font.
*
* \since This function is available since SDL_ttf 3.0.0.
*/
-extern SDL_DECLSPEC bool SDLCALL TTF_SetFontScript(TTF_Font *font, const char *script);
+extern SDL_DECLSPEC bool SDLCALL TTF_SetFontScript(TTF_Font *font, Uint32 script);
/**
* Get the script used for text shaping a font.
*
- * The supplied script value will be a null-terminated string of exactly four
- * characters.
- *
- * If SDL_ttf was not built with HarfBuzz support, this function returns
- * false.
- *
* \param font the font to query.
- * \param script a pointer filled in with the script used by `ch`.
- * \param script_size the size of the script buffer, which must be at least 5
- * characters.
- * \returns true on success or false on failure; call SDL_GetError() for more
- * information.
+ * \returns a script tag in the format used by HarfBuzz.
*
* \threadsafety This function should be called on the thread that created the
* font.
*
* \since This function is available since SDL_ttf 3.0.0.
*/
-extern SDL_DECLSPEC bool SDLCALL TTF_GetFontScript(TTF_Font *font, char *script, size_t script_size);
+extern SDL_DECLSPEC Uint32 SDLCALL TTF_GetFontScript(TTF_Font *font);
/**
* Get the script used by a 32-bit codepoint.
*
- * The supplied script value will be a null-terminated string of exactly four
- * characters.
- *
- * If SDL_ttf was not built with HarfBuzz support, this function returns
- * false.
- *
* \param ch the character code to check.
- * \param script a pointer filled in with the script used by `ch`.
- * \param script_size the size of the script buffer, which must be at least 5
- * characters.
- * \returns true on success or false on failure; call SDL_GetError() for more
- * information.
+ * \returns a script tag in the format used by HarfBuzz on success, or 0 on failure; call SDL_GetError() for more information.
*
- * \threadsafety This function should be called on the thread that created the
- * font.
+ * \threadsafety This function is thread-safe.
*
* \since This function is available since SDL_ttf 3.0.0.
*/
-extern SDL_DECLSPEC bool SDLCALL TTF_GetGlyphScript(Uint32 ch, char *script, size_t script_size);
+extern SDL_DECLSPEC Uint32 SDLCALL TTF_GetGlyphScript(Uint32 ch);
/**
* Set language to be used for text shaping by a font.
@@ -1965,6 +1928,70 @@ extern SDL_DECLSPEC bool SDLCALL TTF_SetTextFont(TTF_Text *text, TTF_Font *font)
*/
extern SDL_DECLSPEC TTF_Font * SDLCALL TTF_GetTextFont(TTF_Text *text);
+/**
+ * Set the direction to be used for text shaping a text object.
+ *
+ * This function only supports left-to-right text shaping if SDL_ttf was not built with HarfBuzz support.
+ *
+ * \param text the text to modify.
+ * \param direction the new direction for text to flow.
+ * \returns true on success or false on failure; call SDL_GetError() for more
+ * information.
+ *
+ * \threadsafety This function should be called on the thread that created the
+ * text.
+ *
+ * \since This function is available since SDL_ttf 3.0.0.
+ */
+extern SDL_DECLSPEC bool SDLCALL TTF_SetTextDirection(TTF_Text *text, TTF_Direction direction);
+
+/**
+ * Get the direction to be used for text shaping a text object.
+ *
+ * This defaults to the direction of the font used by the text object.
+ *
+ * \param text the text to query.
+ * \returns the direction to be used for text shaping.
+ *
+ * \threadsafety This function should be called on the thread that created the
+ * text.
+ *
+ * \since This function is available since SDL_ttf 3.0.0.
+ */
+extern SDL_DECLSPEC TTF_Direction SDLCALL TTF_GetTextDirection(TTF_Text *text);
+
+/**
+ * Set the script to be used for text shaping a text object.
+ *
+ * This returns false if SDL_ttf isn't build with HarfBuzz support.
+ *
+ * \param text the text to modify.
+ * \param script a script tag in the format used by HarfBuzz.
+ * \returns true on success or false on failure; call SDL_GetError() for more
+ * information.
+ *
+ * \threadsafety This function should be called on the thread that created the
+ * text.
+ *
+ * \since This function is available since SDL_ttf 3.0.0.
+ */
+extern SDL_DECLSPEC bool SDLCALL TTF_SetTextScript(TTF_Text *text, Uint32 script);
+
+/**
+ * Get the script used for text shaping a text object.
+ *
+ * This defaults to the script of the font used by the text object.
+ *
+ * \param text the text to query.
+ * \returns a script tag in the format used by HarfBuzz.
+ *
+ * \threadsafety This function should be called on the thread that created the
+ * text.
+ *
+ * \since This function is available since SDL_ttf 3.0.0.
+ */
+extern SDL_DECLSPEC Uint32 SDLCALL TTF_GetTextScript(TTF_Text *text);
+
/**
* Set the color of a text object.
*
diff --git a/src/SDL_ttf.c b/src/SDL_ttf.c
index 8159597c..7e611c4d 100644
--- a/src/SDL_ttf.c
+++ b/src/SDL_ttf.c
@@ -236,6 +236,8 @@ typedef struct GlyphPositions {
} GlyphPositions;
typedef struct CachedGlyphPositions {
+ TTF_Direction direction;
+ Uint32 script;
char *text;
size_t length;
GlyphPositions positions;
@@ -311,12 +313,10 @@ struct TTF_Font {
int render_subpixel;
#if TTF_USE_HARFBUZZ
hb_font_t *hb_font;
- // If HB_SCRIPT_INVALID, use global default script
- hb_script_t hb_script;
- // If HB_DIRECTION_INVALID, use global default direction
- hb_direction_t hb_direction;
hb_language_t hb_language;
#endif
+ Uint32 script;
+ TTF_Direction direction;
bool render_sdf;
// Extra layout setting for wrapped text
@@ -1023,21 +1023,16 @@ static void BG_NEON(const TTF_Image *image, Uint8 *destination, Sint32 srcskip,
#endif
// Underline and Strikethrough style. Draw a line at the given row.
-static void Draw_Line(TTF_Font *font, const SDL_Surface *textbuf, int column, int row, int line_width, int line_thickness, Uint32 color, const render_mode_t render_mode)
+static void Draw_Line(TTF_Direction direction, const SDL_Surface *textbuf, int column, int row, int line_width, int line_thickness, Uint32 color, const render_mode_t render_mode)
{
int tmp = row + line_thickness - textbuf->h;
int x_offset = column * SDL_BYTESPERPIXEL(textbuf->format);
Uint8 *dst = (Uint8 *)textbuf->pixels + row * textbuf->pitch + x_offset;
-#if TTF_USE_HARFBUZZ
- hb_direction_t hb_direction = font->hb_direction;
// No Underline/Strikethrough style if direction is vertical
- if (hb_direction == HB_DIRECTION_TTB || hb_direction == HB_DIRECTION_BTT) {
+ if (direction == TTF_DIRECTION_TTB || direction == TTF_DIRECTION_BTT) {
return;
}
-#else
- (void) font;
-#endif
/* Not needed because of "font->height = SDL_max(font->height, bottom_row);".
* But if you patch to render textshaping and break line in middle of a cluster,
@@ -1070,21 +1065,16 @@ static void Draw_Line(TTF_Font *font, const SDL_Surface *textbuf, int column, in
}
}
-static void Draw_Line_TextEngine(TTF_Font *font, int width, int height, int column, int row, int line_width, int line_thickness, TTF_DrawOperation *ops, int *current_op)
+static void Draw_Line_TextEngine(TTF_Direction direction, int width, int height, int column, int row, int line_width, int line_thickness, TTF_DrawOperation *ops, int *current_op)
{
int op_index = *current_op;
TTF_DrawOperation *op = &ops[op_index];
int tmp = row + line_thickness - height;
-#if TTF_USE_HARFBUZZ
- hb_direction_t hb_direction = font->hb_direction;
// No Underline/Strikethrough style if direction is vertical
- if (hb_direction == HB_DIRECTION_TTB || hb_direction == HB_DIRECTION_BTT) {
+ if (direction == TTF_DIRECTION_TTB || direction == TTF_DIRECTION_BTT) {
return;
}
-#else
- (void) font;
-#endif
/* Not needed because of "font->height = SDL_max(font->height, bottom_row);".
* But if you patch to render textshaping and break line in middle of a cluster,
@@ -2093,8 +2083,6 @@ TTF_Font *TTF_OpenFontWithProperties(SDL_PropertiesID props)
* you will get mismatching advances and raster. */
hb_ft_font_set_load_flags(font->hb_font, FT_LOAD_DEFAULT | font->ft_load_target);
- font->hb_direction = HB_DIRECTION_LTR;
- font->hb_script = HB_SCRIPT_UNKNOWN;
font->hb_language = hb_language_from_string("", -1);
#endif
@@ -3163,7 +3151,7 @@ bool TTF_GetGlyphKerning(TTF_Font *font, Uint32 previous_ch, Uint32 ch, int *ker
return true;
}
-static bool CollectGlyphsFromFont(TTF_Font *font, const char *text, size_t length, GlyphPositions *positions)
+static bool CollectGlyphsFromFont(TTF_Font *font, const char *text, size_t length, TTF_Direction direction, Uint32 script, GlyphPositions *positions)
{
#if TTF_USE_HARFBUZZ
// Create a buffer for harfbuzz to use
@@ -3175,12 +3163,12 @@ static bool CollectGlyphsFromFont(TTF_Font *font, const char *text, size_t lengt
// Set global configuration
hb_buffer_set_language(hb_buffer, font->hb_language);
- hb_buffer_set_direction(hb_buffer, font->hb_direction);
- hb_buffer_set_script(hb_buffer, font->hb_script);
- hb_buffer_guess_segment_properties(hb_buffer);
+ hb_buffer_set_direction(hb_buffer, (hb_direction_t)direction);
+ hb_buffer_set_script(hb_buffer, script);
// Layout the text
hb_buffer_add_utf8(hb_buffer, text, (int)length, 0, -1);
+ hb_buffer_guess_segment_properties(hb_buffer);
hb_feature_t userfeatures[1];
userfeatures[0].tag = HB_TAG('k','e','r','n');
@@ -3340,7 +3328,7 @@ static bool ReplaceGlyphPositions(GlyphPositions *positions, int start, int leng
return true;
}
-static bool CollectGlyphsWithFallbacks(TTF_Font *font, const char *text, size_t length, GlyphPositions *positions, TTF_Font *initial_font)
+static bool CollectGlyphsWithFallbacks(TTF_Font *font, const char *text, size_t length, TTF_Direction direction, Uint32 script, GlyphPositions *positions, TTF_Font *initial_font)
{
if (!initial_font) {
initial_font = font;
@@ -3349,7 +3337,7 @@ static bool CollectGlyphsWithFallbacks(TTF_Font *font, const char *text, size_t
return true;
}
- if (!CollectGlyphsFromFont(font, text, length, positions)) {
+ if (!CollectGlyphsFromFont(font, text, length, direction, script, positions)) {
return false;
}
@@ -3372,7 +3360,7 @@ static bool CollectGlyphsWithFallbacks(TTF_Font *font, const char *text, size_t
SDL_zero(span);
int span_offset = positions->pos[start].offset;
int span_length = pos->offset - span_offset;
- CollectGlyphsWithFallbacks(fallback->font, text + span_offset, span_length, &span, initial_font);
+ CollectGlyphsWithFallbacks(fallback->font, text + span_offset, span_length, direction, script, &span, initial_font);
if (span.len > 0) {
ReplaceGlyphPositions(positions, start, (i - start), &span);
SDL_free(span.pos);
@@ -3387,7 +3375,7 @@ static bool CollectGlyphsWithFallbacks(TTF_Font *font, const char *text, size_t
SDL_zero(span);
int span_offset = positions->pos[start].offset;
int span_length = (int)(length - span_offset);
- CollectGlyphsWithFallbacks(fallback->font, text + span_offset, span_length, &span, initial_font);
+ CollectGlyphsWithFallbacks(fallback->font, text + span_offset, span_length, direction, script, &span, initial_font);
if (span.len > 0) {
ReplaceGlyphPositions(positions, start, (positions->len - start), &span);
SDL_free(span.pos);
@@ -3399,9 +3387,9 @@ static bool CollectGlyphsWithFallbacks(TTF_Font *font, const char *text, size_t
return true;
}
-static bool CollectGlyphs(TTF_Font *font, const char *text, size_t length, GlyphPositions *positions)
+static bool CollectGlyphs(TTF_Font *font, const char *text, size_t length, TTF_Direction direction, Uint32 script, GlyphPositions *positions)
{
- if (!CollectGlyphsWithFallbacks(font, text, length, positions, NULL)) {
+ if (!CollectGlyphsWithFallbacks(font, text, length, direction, script, positions, NULL)) {
return false;
}
@@ -3415,14 +3403,16 @@ static bool CollectGlyphs(TTF_Font *font, const char *text, size_t length, Glyph
return true;
}
-static GlyphPositions *GetCachedGlyphPositions(TTF_Font *font, const char *text, size_t length)
+static GlyphPositions *GetCachedGlyphPositions(TTF_Font *font, const char *text, size_t length, TTF_Direction direction, Uint32 script)
{
CachedGlyphPositions *cached;
font->positions = NULL;
for (unsigned int i = 0; i < SDL_arraysize(font->cached_positions); ++i) {
cached = &font->cached_positions[i];
- if (length == cached->length &&
+ if (direction == cached->direction &&
+ script == cached->script &&
+ length == cached->length &&
SDL_memcmp(text, cached->text, length) == 0) {
#ifdef DEBUG_TTF_CACHE
SDL_Log("Found cached positions for '%s'\n", cached->text);
@@ -3439,6 +3429,8 @@ static GlyphPositions *GetCachedGlyphPositions(TTF_Font *font, const char *text,
font->positions = &cached->positions;
SDL_free(cached->text);
+ cached->direction = direction;
+ cached->script = script;
cached->text = (char *)SDL_malloc(length + 1);
if (cached->text) {
SDL_memcpy(cached->text, text, length);
@@ -3449,7 +3441,7 @@ static GlyphPositions *GetCachedGlyphPositions(TTF_Font *font, const char *text,
return NULL;
}
- if (!CollectGlyphs(font, text, length, font->positions)) {
+ if (!CollectGlyphs(font, text, length, direction, script, font->positions)) {
cached->length = 0;
return NULL;
}
@@ -3460,7 +3452,7 @@ static GlyphPositions *GetCachedGlyphPositions(TTF_Font *font, const char *text,
return font->positions;
}
-static bool TTF_Size_Internal(TTF_Font *font, const char *text, size_t length, int *w, int *h, int *xstart, int *ystart, bool measure_width, int max_width, int *measured_width, size_t *measured_length)
+static bool TTF_Size_Internal(TTF_Font *font, const char *text, size_t length, TTF_Direction direction, Uint32 script, int *w, int *h, int *xstart, int *ystart, bool measure_width, int max_width, int *measured_width, size_t *measured_length)
{
int x = 0, y = 0;
int pos_x, pos_y;
@@ -3493,7 +3485,7 @@ static bool TTF_Size_Internal(TTF_Font *font, const char *text, size_t length, i
// Reset buffer
font->num_clusters = 0;
- GlyphPositions *positions = GetCachedGlyphPositions(font, text, length);
+ GlyphPositions *positions = GetCachedGlyphPositions(font, text, length, direction, script);
if (!positions) {
return false;
}
@@ -3596,7 +3588,7 @@ bool TTF_GetStringSize(TTF_Font *font, const char *text, size_t length, int *w,
if (!length && text) {
length = SDL_strlen(text);
}
- return TTF_Size_Internal(font, text, length, w, h, NULL, NULL, NO_MEASUREMENT);
+ return TTF_Size_Internal(font, text, length, font->direction, font->script, w, h, NULL, NULL, NO_MEASUREMENT);
}
bool TTF_MeasureString(TTF_Font *font, const char *text, size_t length, int max_width, int *measured_width, size_t *measured_length)
@@ -3604,7 +3596,7 @@ bool TTF_MeasureString(TTF_Font *font, const char *text, size_t length, int max_
if (!length && text) {
length = SDL_strlen(text);
}
- return TTF_Size_Internal(font, text, length, NULL, NULL, NULL, NULL, true, max_width, measured_width, measured_length);
+ return TTF_Size_Internal(font, text, length, font->direction, font->script, NULL, NULL, NULL, NULL, true, max_width, measured_width, measured_length);
}
static SDL_Surface* TTF_Render_Internal(TTF_Font *font, const char *text, size_t length, SDL_Color fg, SDL_Color bg, const render_mode_t render_mode)
@@ -3637,7 +3629,7 @@ static SDL_Surface* TTF_Render_Internal(TTF_Font *font, const char *text, size_t
#endif
// Get the dimensions of the text surface
- if (!TTF_Size_Internal(font, text, length, &width, &height, &xstart, &ystart, NO_MEASUREMENT) || !width) {
+ if (!TTF_Size_Internal(font, text, length, font->direction, font->script, &width, &height, &xstart, &ystart, NO_MEASUREMENT) || !width) {
SDL_SetError("Text has zero width");
goto failure;
}
@@ -3667,11 +3659,11 @@ static SDL_Surface* TTF_Render_Internal(TTF_Font *font, const char *text, size_t
// Apply underline or strikethrough style, if needed
if (TTF_HANDLE_STYLE_UNDERLINE(font)) {
- Draw_Line(font, textbuf, 0, ystart + font->underline_top_row, width, font->line_thickness, color, render_mode);
+ Draw_Line(font->direction, textbuf, 0, ystart + font->underline_top_row, width, font->line_thickness, color, render_mode);
}
if (TTF_HANDLE_STYLE_STRIKETHROUGH(font)) {
- Draw_Line(font, textbuf, 0, ystart + font->strikethrough_top_row, width, font->line_thickness, color, render_mode);
+ Draw_Line(font->direction, textbuf, 0, ystart + font->strikethrough_top_row, width, font->line_thickness, color, render_mode);
}
return textbuf;
@@ -3755,7 +3747,7 @@ static bool CharacterIsNewLine(Uint32 c)
return false;
}
-static bool GetWrappedLines(TTF_Font *font, const char *text, size_t length, int xoffset, int wrap_width, bool trim_whitespace, TTF_Line **lines, int *num_lines, int *w, int *h)
+static bool GetWrappedLines(TTF_Font *font, const char *text, size_t length, TTF_Direction direction, Uint32 script, int xoffset, int wrap_width, bool trim_whitespace, TTF_Line **lines, int *num_lines, int *w, int *h)
{
int width, height;
int i, numLines = 0, rowHeight;
@@ -3781,7 +3773,7 @@ static bool GetWrappedLines(TTF_Font *font, const char *text, size_t length, int
}
// Get the dimensions of the text surface
- if (!TTF_GetStringSize(font, text, length, &width, &height) || !width) {
+ if (!TTF_Size_Internal(font, text, length, direction, script, &width, &height, NULL, NULL, NO_MEASUREMENT)|| !width) {
return SDL_SetError("Text has zero width");
}
@@ -3833,7 +3825,7 @@ static bool GetWrappedLines(TTF_Font *font, const char *text, size_t length, int
max_width = SDL_max(max_width - xoffset, 1);
}
size_t max_length = 0;
- if (!TTF_MeasureString(font, spot, left, max_width, NULL, &max_length)) {
+ if (!TTF_Size_Internal(font, spot, left, direction, script, NULL, NULL, NULL, NULL, true, max_width, NULL, &max_length)) {
SDL_SetError("Error measure text");
goto done;
}
@@ -3916,7 +3908,7 @@ static bool GetWrappedLines(TTF_Font *font, const char *text, size_t length, int
for (i = 0; i < numLines; i++) {
int w_tmp, h_tmp;
- if (TTF_GetStringSize(font, strLines[i].text, strLines[i].length, &w_tmp, &h_tmp)) {
+ if (TTF_Size_Internal(font, strLines[i].text, strLines[i].length, font->direction, font->script, &w_tmp, &h_tmp, NULL, NULL, NO_MEASUREMENT)) {
width = SDL_max(w_tmp, width);
}
}
@@ -3959,7 +3951,7 @@ static bool GetWrappedLines(TTF_Font *font, const char *text, size_t length, int
bool TTF_GetStringSizeWrapped(TTF_Font *font, const char *text, size_t length, int wrap_width, int *w, int *h)
{
- return GetWrappedLines(font, text, length, 0, wrap_width, true, NULL, NULL, w, h);
+ return GetWrappedLines(font, text, length, font->direction, font->script, 0, wrap_width, true, NULL, NULL, w, h);
}
static SDL_Surface* TTF_Render_Wrapped_Internal(TTF_Font *font, const char *text, size_t length, SDL_Color fg, SDL_Color bg, int wrap_width, const render_mode_t render_mode)
@@ -3970,7 +3962,7 @@ static SDL_Surface* TTF_Render_Wrapped_Internal(TTF_Font *font, const char *text
int i, numLines = 0;
TTF_Line *strLines = NULL;
- if (!GetWrappedLines(font, text, length, 0, wrap_width, true, &strLines, &numLines, &width, &height)) {
+ if (!GetWrappedLines(font, text, length, font->direction, font->script, 0, wrap_width, true, &strLines, &numLines, &width, &height)) {
return NULL;
}
@@ -4012,7 +4004,7 @@ static SDL_Surface* TTF_Render_Wrapped_Internal(TTF_Font *font, const char *text
int xstart, ystart, line_width, xoffset;
// Initialize xstart, ystart and compute positions
- if (!TTF_Size_Internal(font, strLines[i].text, strLines[i].length, &line_width, NULL, &xstart, &ystart, NO_MEASUREMENT)) {
+ if (!TTF_Size_Internal(font, strLines[i].text, strLines[i].length, font->direction, font->script, &line_width, NULL, &xstart, &ystart, NO_MEASUREMENT)) {
goto failure;
}
@@ -4036,11 +4028,11 @@ static SDL_Surface* TTF_Render_Wrapped_Internal(TTF_Font *font, const char *text
// Apply underline or strikethrough style, if needed
if (TTF_HANDLE_STYLE_UNDERLINE(font)) {
- Draw_Line(font, textbuf, xoffset, ystart + font->underline_top_row, line_width, font->line_thickness, color, render_mode);
+ Draw_Line(font->direction, textbuf, xoffset, ystart + font->underline_top_row, line_width, font->line_thickness, color, render_mode);
}
if (TTF_HANDLE_STYLE_STRIKETHROUGH(font)) {
- Draw_Line(font, textbuf, xoffset, ystart + font->strikethrough_top_row, line_width, font->line_thickness, color, render_mode);
+ Draw_Line(font->direction, textbuf, xoffset, ystart + font->strikethrough_top_row, line_width, font->line_thickness, color, render_mode);
}
}
@@ -4081,6 +4073,8 @@ SDL_Surface* TTF_RenderText_LCD_Wrapped(TTF_Font *font, const char *text, size_t
struct TTF_TextLayout
{
+ TTF_Direction direction;
+ Uint32 script;
int font_height;
int wrap_length;
bool wrap_whitespace_visible;
@@ -4242,8 +4236,10 @@ static bool LayoutText(TTF_Text *text)
int num_clusters = 0, max_clusters = 0, cluster_offset;
int *lines = NULL;
bool result = false;
+ TTF_Direction direction = TTF_GetTextDirection(text);
+ Uint32 script = TTF_GetTextScript(text);
- if (!GetWrappedLines(font, text->text, length, text->internal->x, wrap_width, trim_whitespace, &strLines, &numLines, &width, &height)) {
+ if (!GetWrappedLines(font, text->text, length, direction, script, text->internal->x, wrap_width, trim_whitespace, &strLines, &numLines, &width, &height)) {
return true;
}
height += text->internal->y;
@@ -4284,7 +4280,7 @@ static bool LayoutText(TTF_Text *text)
}
// Initialize xstart, ystart and compute positions
- if (!TTF_Size_Internal(font, strLines[i].text, strLines[i].length, &line_width, NULL, &xstart, &ystart, NO_MEASUREMENT)) {
+ if (!TTF_Size_Internal(font, strLines[i].text, strLines[i].length, direction, script, &line_width, NULL, &xstart, &ystart, NO_MEASUREMENT)) {
goto done;
}
@@ -4338,11 +4334,11 @@ static bool LayoutText(TTF_Text *text)
// Apply underline or strikethrough style, if needed
if (TTF_HANDLE_STYLE_UNDERLINE(font)) {
- Draw_Line_TextEngine(font, width, height, xoffset, ystart + font->underline_top_row, line_width, font->line_thickness, ops, &num_ops);
+ Draw_Line_TextEngine(direction, width, height, xoffset, ystart + font->underline_top_row, line_width, font->line_thickness, ops, &num_ops);
}
if (TTF_HANDLE_STYLE_STRIKETHROUGH(font)) {
- Draw_Line_TextEngine(font, width, height, xoffset, ystart + font->strikethrough_top_row, line_width, font->line_thickness, ops, &num_ops);
+ Draw_Line_TextEngine(direction, width, height, xoffset, ystart + font->strikethrough_top_row, line_width, font->line_thickness, ops, &num_ops);
}
}
cluster = &clusters[num_clusters++];
@@ -4464,6 +4460,59 @@ TTF_Font *TTF_GetTextFont(TTF_Text *text)
return text->internal->font;
}
+bool TTF_SetTextDirection(TTF_Text *text, TTF_Direction direction)
+{
+ TTF_CHECK_POINTER("text", text, false);
+
+ if (direction == text->internal->layout->direction) {
+ return true;
+ }
+
+#if !TTF_USE_HARFBUZZ
+ if (direction != TTF_DIRECTION_INVALID && direction != TTF_DIRECTION_LTR) {
+ return SDL_Unsupported();
+ }
+#endif
+
+ text->internal->layout->direction = direction;
+ text->internal->needs_layout_update = true;
+ return true;
+}
+
+TTF_Direction TTF_GetTextDirection(TTF_Text *text)
+{
+ TTF_CHECK_POINTER("text", text, TTF_DIRECTION_INVALID);
+
+ if (text->internal->layout->direction != TTF_DIRECTION_INVALID) {
+ return text->internal->layout->direction;
+ }
+ return TTF_GetFontDirection(text->internal->font);
+}
+
+bool TTF_SetTextScript(TTF_Text *text, Uint32 script)
+{
+ TTF_CHECK_POINTER("text", text, false);
+
+#if TTF_USE_HARFBUZZ
+ text->internal->layout->script = script;
+ text->internal->needs_layout_update = true;
+ return true;
+#else
+ (void) script;
+ return SDL_Unsupported();
+#endif
+}
+
+Uint32 TTF_GetTextScript(TTF_Text *text)
+{
+ TTF_CHECK_POINTER("text", text, 0);
+
+ if (text->internal->layout->script != 0) {
+ return text->internal->layout->script;
+ }
+ return TTF_GetFontScript(text->internal->font);
+}
+
bool TTF_SetTextColor(TTF_Text *text, Uint8 r, Uint8 g, Uint8 b, Uint8 a)
{
const float fR = (float)r / 255.0f;
@@ -4962,7 +5011,7 @@ TTF_SubString **TTF_GetTextSubStringsForRange(TTF_Text *text, int offset, int le
SDL_copyp(substring, &substring1);
if (length == 0) {
substring->length = 0;
- if (TTF_GetFontDirection(text->internal->font) != TTF_DIRECTION_RTL) {
+ if (TTF_GetTextDirection(text) != TTF_DIRECTION_RTL) {
substring->rect.x += substring->rect.w;
}
substring->rect.w = 0;
@@ -5036,8 +5085,8 @@ bool TTF_GetTextSubStringForPoint(TTF_Text *text, int x, int y, TTF_SubString *s
return true;
}
- TTF_D
(Patch may be truncated, please check the link at the top of this post.)