Skip to content

Commit

Permalink
Added TTF_CopyFont()
Browse files Browse the repository at this point in the history
  • Loading branch information
slouken committed Feb 1, 2025
1 parent fae53d2 commit dd1add2
Show file tree
Hide file tree
Showing 3 changed files with 135 additions and 35 deletions.
26 changes: 24 additions & 2 deletions include/SDL3_ttf/SDL_ttf.h
Original file line number Diff line number Diff line change
Expand Up @@ -186,10 +186,10 @@ extern SDL_DECLSPEC TTF_Font * SDLCALL TTF_OpenFontIO(SDL_IOStream *src, bool cl
*
* - `TTF_PROP_FONT_CREATE_FILENAME_STRING`: the font file to open, if an
* SDL_IOStream isn't being used. This is required if
* `TTF_PROP_FONT_CREATE_IOSTREAM_POINTER` isn't set.
* `TTF_PROP_FONT_CREATE_IOSTREAM_POINTER` and `TTF_PROP_FONT_CREATE_EXISTING_FONT` aren't set.
* - `TTF_PROP_FONT_CREATE_IOSTREAM_POINTER`: an SDL_IOStream containing the
* font to be opened. This should not be closed until the font is closed.
* This is required if `TTF_PROP_FONT_CREATE_FILENAME_STRING` isn't set.
* This is required if `TTF_PROP_FONT_CREATE_FILENAME_STRING` and `TTF_PROP_FONT_CREATE_EXISTING_FONT` aren't set.
* - `TTF_PROP_FONT_CREATE_IOSTREAM_OFFSET_NUMBER`: the offset in the iostream
* for the beginning of the font, defaults to 0.
* - `TTF_PROP_FONT_CREATE_IOSTREAM_AUTOCLOSE_BOOLEAN`: true if closing the
Expand All @@ -206,6 +206,7 @@ extern SDL_DECLSPEC TTF_Font * SDLCALL TTF_OpenFontIO(SDL_IOStream *src, bool cl
* - `TTF_PROP_FONT_CREATE_VERTICAL_DPI_NUMBER`: the vertical DPI to use for
* font rendering, defaults to `TTF_PROP_FONT_CREATE_HORIZONTAL_DPI_NUMBER`
* if set, or 72 otherwise.
* - `TTF_PROP_FONT_CREATE_EXISTING_FONT`: an optional TTF_Font that, if set, will be used as the font data source and the initial size and style of the new font.
*
* \param props the properties to use.
* \returns a valid TTF_Font, or NULL on failure; call SDL_GetError() for more
Expand All @@ -227,6 +228,27 @@ extern SDL_DECLSPEC TTF_Font * SDLCALL TTF_OpenFontWithProperties(SDL_Properties
#define TTF_PROP_FONT_CREATE_FACE_NUMBER "SDL_ttf.font.create.face"
#define TTF_PROP_FONT_CREATE_HORIZONTAL_DPI_NUMBER "SDL_ttf.font.create.hdpi"
#define TTF_PROP_FONT_CREATE_VERTICAL_DPI_NUMBER "SDL_ttf.font.create.vdpi"
#define TTF_PROP_FONT_CREATE_EXISTING_FONT "SDL_ttf.font.create.existing_font"

/**
* Create a copy of an existing font.
*
* The copy will be distinct from the original, but will share the font file and have the same size and style as the original.
*
* When done with the returned TTF_Font, use TTF_CloseFont() to dispose of it.
*
* \param existing_font the font to copy.
* \returns a valid TTF_Font, or NULL on failure; call SDL_GetError() for more
* information.
*
* \threadsafety This function should be called on the thread that created the
* original font.
*
* \since This function is available since SDL_ttf 3.0.0.
*
* \sa TTF_CloseFont
*/
extern SDL_DECLSPEC TTF_Font * SDLCALL TTF_CopyFont(TTF_Font *existing_font);

/**
* Get the properties associated with a font.
Expand Down
143 changes: 110 additions & 33 deletions src/SDL_ttf.c
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@
(unsigned long)' ' )
#endif // !FT_HAS_SVG

#define TTF_PROP_IOSTREAM_REFCOUNT "SDL_ttf.font.src.refcount"

/**
* ZERO WIDTH NO-BREAKSPACE (Unicode byte order mark)
*/
Expand Down Expand Up @@ -282,6 +284,7 @@ struct TTF_Font {

// Freetype2 maintains all sorts of useful info itself
FT_Face face;
long face_index;

// Properties exposed to the application
SDL_PropertiesID props;
Expand Down Expand Up @@ -1963,14 +1966,27 @@ static unsigned long IOread(
return (unsigned long)SDL_ReadIO(font->src, buffer, count);
}

static void TTF_CloseFontSource(SDL_IOStream *src)
{
SDL_PropertiesID src_props = SDL_GetIOProperties(src);
int refcount = (int)SDL_GetNumberProperty(src_props, TTF_PROP_IOSTREAM_REFCOUNT, 0);
if (refcount > 0) {
--refcount;
SDL_SetNumberProperty(src_props, TTF_PROP_IOSTREAM_REFCOUNT, refcount);
return;
}
SDL_CloseIO(src);
}

TTF_Font *TTF_OpenFontWithProperties(SDL_PropertiesID props)
{
TTF_Font *existing_font = SDL_GetPointerProperty(props, TTF_PROP_FONT_CREATE_EXISTING_FONT, NULL);
const char *file = SDL_GetStringProperty(props, TTF_PROP_FONT_CREATE_FILENAME_STRING, NULL);
SDL_IOStream *src = SDL_GetPointerProperty(props, TTF_PROP_FONT_CREATE_IOSTREAM_POINTER, NULL);
Sint64 src_offset = SDL_GetNumberProperty(props, TTF_PROP_FONT_CREATE_IOSTREAM_OFFSET_NUMBER, 0);
bool closeio = SDL_GetBooleanProperty(props, TTF_PROP_FONT_CREATE_IOSTREAM_AUTOCLOSE_BOOLEAN, false);
float ptsize = SDL_GetFloatProperty(props, TTF_PROP_FONT_CREATE_SIZE_FLOAT, 0);
long index = (long)SDL_GetNumberProperty(props, TTF_PROP_FONT_CREATE_FACE_NUMBER, 0);
long face_index = (long)SDL_GetNumberProperty(props, TTF_PROP_FONT_CREATE_FACE_NUMBER, -1);
unsigned int hdpi = (unsigned int)SDL_GetNumberProperty(props, TTF_PROP_FONT_CREATE_HORIZONTAL_DPI_NUMBER, 0);
unsigned int vdpi = (unsigned int)SDL_GetNumberProperty(props, TTF_PROP_FONT_CREATE_VERTICAL_DPI_NUMBER, 0);
TTF_Font *font;
Expand All @@ -1990,25 +2006,40 @@ TTF_Font *TTF_OpenFontWithProperties(SDL_PropertiesID props)
return NULL;
}

if (!src) {
if (!file) {
SDL_SetError("You must set either TTF_PROP_FONT_CREATE_FILENAME_STRING or TTF_PROP_FONT_CREATE_IOSTREAM_POINTER");
return NULL;
if (existing_font) {
if (!src) {
src = existing_font->src;
src_offset = existing_font->src_offset;
closeio = existing_font->closeio;

if (closeio) {
SDL_PropertiesID src_props = SDL_GetIOProperties(src);
int refcount = (int)SDL_GetNumberProperty(src_props, TTF_PROP_IOSTREAM_REFCOUNT, 0);
++refcount;
SDL_SetNumberProperty(src_props, TTF_PROP_IOSTREAM_REFCOUNT, refcount);
}
}

src = SDL_IOFromFile(file, "rb");
} else {
if (!src) {
return NULL;
if (!file) {
SDL_SetError("You must set either TTF_PROP_FONT_CREATE_FILENAME_STRING or TTF_PROP_FONT_CREATE_IOSTREAM_POINTER");
return NULL;
}

src = SDL_IOFromFile(file, "rb");
if (!src) {
return NULL;
}
closeio = true;
}
closeio = true;
}

// Check to make sure we can seek in this stream
position = SDL_TellIO(src);
if (position < 0) {
SDL_SetError("Can't seek in stream");
if (closeio) {
SDL_CloseIO(src);
TTF_CloseFontSource(src);
}
return NULL;
}
Expand All @@ -2017,31 +2048,57 @@ TTF_Font *TTF_OpenFontWithProperties(SDL_PropertiesID props)
if (font == NULL) {
SDL_SetError("Out of memory");
if (closeio) {
SDL_CloseIO(src);
TTF_CloseFontSource(src);
}
return NULL;
}

if (file) {
const char *name = SDL_strrchr(file, '/');
if (name) {
name += 1;
} else {
name = SDL_strrchr(file, '\\');
font->src = src;
font->src_offset = src_offset;
font->closeio = closeio;
font->generation = 1;

if (existing_font) {
if (existing_font->name) {
font->name = SDL_strdup(existing_font->name);
}
if (face_index == -1) {
face_index = existing_font->face_index;
}
if (ptsize == 0.0f) {
ptsize = existing_font->ptsize;
}
if (hdpi == 0) {
hdpi = existing_font->hdpi;
}
if (vdpi == 0) {
vdpi = existing_font->vdpi;
}
} else {
if (file) {
const char *name = SDL_strrchr(file, '/');
if (name) {
name += 1;
} else {
name = file;
name = SDL_strrchr(file, '\\');
if (name) {
name += 1;
} else {
name = file;
}
}
font->name = SDL_strdup(name);
}
font->name = SDL_strdup(name);
}
font->src = src;
font->src_offset = src_offset;
font->closeio = closeio;
font->generation = 1;
font->hdpi = TTF_DEFAULT_DPI;
font->vdpi = TTF_DEFAULT_DPI;
if (face_index < 0) {
face_index = 0;
}
if (hdpi == 0) {
hdpi = TTF_DEFAULT_DPI;
}
if (vdpi == 0) {
vdpi = TTF_DEFAULT_DPI;
}

font->text = SDL_CreateHashTable(NULL, 16, SDL_HashPointer, SDL_KeyMatchPointer, NULL, false, false);
if (!font->text) {
Expand Down Expand Up @@ -2078,14 +2135,15 @@ TTF_Font *TTF_OpenFontWithProperties(SDL_PropertiesID props)
font->args.stream = stream;

SDL_LockMutex(TTF_state.lock);
error = FT_Open_Face(TTF_state.library, &font->args, index, &font->face);
error = FT_Open_Face(TTF_state.library, &font->args, face_index, &face);
SDL_UnlockMutex(TTF_state.lock);
if (error || font->face == NULL) {
if (error || face == NULL) {
TTF_SetFTError("Couldn't load font file", error);
TTF_CloseFont(font);
return NULL;
}
face = font->face;
font->face = face;
font->face_index = face_index;

// Set charmap for loaded font
found = 0;
Expand Down Expand Up @@ -2122,10 +2180,17 @@ TTF_Font *TTF_OpenFontWithProperties(SDL_PropertiesID props)
}

// Set the default font style
font->style = TTF_STYLE_NORMAL;
font->outline = 0;
font->ft_load_target = FT_LOAD_TARGET_NORMAL;
TTF_SetFontKerning(font, true);
if (existing_font) {
font->style = existing_font->style;
font->outline = existing_font->outline;
font->ft_load_target = existing_font->ft_load_target;
font->enable_kerning = existing_font->enable_kerning;
} else {
font->style = TTF_STYLE_NORMAL;
font->outline = 0;
font->ft_load_target = FT_LOAD_TARGET_NORMAL;
TTF_SetFontKerning(font, true);
}

#if TTF_USE_HARFBUZZ
font->hb_font = hb_ft_font_create(face, NULL);
Expand Down Expand Up @@ -2179,6 +2244,18 @@ TTF_Font *TTF_OpenFontIO(SDL_IOStream *src, bool closeio, float ptsize)
return font;
}

TTF_Font *TTF_CopyFont(TTF_Font *existing_font)
{
TTF_Font *font = NULL;
SDL_PropertiesID props = SDL_CreateProperties();
if (props) {
SDL_SetPointerProperty(props, TTF_PROP_FONT_CREATE_EXISTING_FONT, existing_font);
font = TTF_OpenFontWithProperties(props);
SDL_DestroyProperties(props);
}
return font;
}

static bool AddFontTextReference(TTF_Font *font, TTF_Text *text)
{
return SDL_InsertIntoHashTable(font->text, text, NULL);
Expand Down Expand Up @@ -6056,7 +6133,7 @@ void TTF_CloseFont(TTF_Font *font)
SDL_free(font->args.stream);
}
if (font->closeio) {
SDL_CloseIO(font->src);
TTF_CloseFontSource(font->src);
}
SDL_free(font->name);
SDL_free(font);
Expand Down
1 change: 1 addition & 0 deletions src/SDL_ttf.sym
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ SDL3_ttf_0.0.0 {
TTF_AppendTextString;
TTF_ClearFallbackFonts;
TTF_CloseFont;
TTF_CopyFont;
TTF_CreateGPUTextEngine;
TTF_CreateGPUTextEngineWithProperties;
TTF_CreateRendererTextEngine;
Expand Down

0 comments on commit dd1add2

Please sign in to comment.