SDL_ttf: Added TTF_OpenFontWithProperties()

From 01cffe5fc61cbb0755657c260d09b45ce145c70d Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Thu, 26 Sep 2024 12:12:20 -0700
Subject: [PATCH] Added TTF_OpenFontWithProperties()

This simplifies the myriad functions to open a font with different parameters that accumulated in SDL_ttf 2.x.
---
 docs/README-migration.md   |   6 ++
 include/SDL3_ttf/SDL_ttf.h | 179 +++++--------------------------------
 src/SDL_ttf.c              |  89 +++++++++---------
 src/SDL_ttf.sym            |   7 +-
 4 files changed, 75 insertions(+), 206 deletions(-)

diff --git a/docs/README-migration.md b/docs/README-migration.md
index 2e75a466..966e4d61 100644
--- a/docs/README-migration.md
+++ b/docs/README-migration.md
@@ -58,6 +58,12 @@ The following functions have been renamed:
 The following functions have been removed:
 * TTF_ByteSwappedUNICODE()
 * TTF_MeasureUNICODE()
+* TTF_OpenFontDPI() - replaced with TTF_OpenFontWithProperties()
+* TTF_OpenFontDPIIO() - replaced with TTF_OpenFontWithProperties()
+* TTF_OpenFontIndex() - replaced with TTF_OpenFontWithProperties()
+* TTF_OpenFontIndexDPI() - replaced with TTF_OpenFontWithProperties()
+* TTF_OpenFontIndexDPIIO() - replaced with TTF_OpenFontWithProperties()
+* TTF_OpenFontIndexIO() - replaced with TTF_OpenFontWithProperties()
 * TTF_RenderUNICODE_Blended()
 * TTF_RenderUNICODE_Blended_Wrapped()
 * TTF_RenderUNICODE_LCD()
diff --git a/include/SDL3_ttf/SDL_ttf.h b/include/SDL3_ttf/SDL_ttf.h
index 81792a09..8ce8a7ff 100644
--- a/include/SDL3_ttf/SDL_ttf.h
+++ b/include/SDL3_ttf/SDL_ttf.h
@@ -143,7 +143,7 @@ extern SDL_DECLSPEC bool SDLCALL TTF_Init(void);
  *
  * \param file path to font file.
  * \param ptsize point size to use for the newly-opened font.
- * \returns a valid TTF_Font, or NULL on error.
+ * \returns a valid TTF_Font, or NULL on failure; call SDL_GetError() for more information.
  *
  * \since This function is available since SDL_ttf 3.0.0.
  *
@@ -151,30 +151,6 @@ extern SDL_DECLSPEC bool SDLCALL TTF_Init(void);
  */
 extern SDL_DECLSPEC TTF_Font * SDLCALL TTF_OpenFont(const char *file, int ptsize);
 
-/**
- * Create a font from a file, using a specified face index.
- *
- * Some .fon fonts will have several sizes embedded in the file, so the point
- * size becomes the index of choosing which size. If the value is too high,
- * the last indexed size will be the default.
- *
- * Some fonts have multiple "faces" included. The index specifies which face
- * to use from the font file. Font files with only one face should specify
- * zero for the index.
- *
- * When done with the returned TTF_Font, use TTF_CloseFont() to dispose of it.
- *
- * \param file path to font file.
- * \param ptsize point size to use for the newly-opened font.
- * \param index index of the face in the font file.
- * \returns a valid TTF_Font, or NULL on error.
- *
- * \since This function is available since SDL_ttf 3.0.0.
- *
- * \sa TTF_CloseFont
- */
-extern SDL_DECLSPEC TTF_Font * SDLCALL TTF_OpenFontIndex(const char *file, int ptsize, long index);
-
 /**
  * Create a font from an SDL_IOStream, using a specified point size.
  *
@@ -191,7 +167,7 @@ extern SDL_DECLSPEC TTF_Font * SDLCALL TTF_OpenFontIndex(const char *file, int p
  * \param closeio true to close `src` when the font is closed, false to leave
  *                it open.
  * \param ptsize point size to use for the newly-opened font.
- * \returns a valid TTF_Font, or NULL on error.
+ * \returns a valid TTF_Font, or NULL on failure; call SDL_GetError() for more information.
  *
  * \since This function is available since SDL_ttf 3.0.0.
  *
@@ -200,145 +176,34 @@ extern SDL_DECLSPEC TTF_Font * SDLCALL TTF_OpenFontIndex(const char *file, int p
 extern SDL_DECLSPEC TTF_Font * SDLCALL TTF_OpenFontIO(SDL_IOStream *src, bool closeio, int ptsize);
 
 /**
- * Create a font from an SDL_IOStream, using a specified face index.
- *
- * Some .fon fonts will have several sizes embedded in the file, so the point
- * size becomes the index of choosing which size. If the value is too high,
- * the last indexed size will be the default.
- *
- * If `closeio` is true `src` will be automatically closed once the font is
- * closed. Otherwise you should close `src` yourself after closing the font.
+ * Create a font with the specified properties.
  *
- * Some fonts have multiple "faces" included. The index specifies which face
- * to use from the font file. Font files with only one face should specify
- * zero for the index.
+ * These are the supported properties:
  *
- * When done with the returned TTF_Font, use TTF_CloseFont() to dispose of it.
+ * - `TTF_PROP_FONT_FILENAME_STRING`: the font file to open, if an SDL_IOStream isn't being used. This is required if `TTF_PROP_FONT_IOSTREAM_POINTER` isn't set.
+ * - `TTF_PROP_FONT_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_FILENAME_STRING` isn't set.
+ * - `TTF_PROP_FONT_IOSTREAM_AUTOCLOSE_BOOLEAN`: true if closing the font should also close the associated SDL_IOStream.
+ * - `TTF_PROP_FONT_SIZE_NUMBER`: the point size of the font. Some .fon fonts will have several sizes embedded in the file, so the point size becomes the index of choosing which size. If the value is too high, the last indexed size will be the default.
+ * - `TTF_PROP_FONT_FACE_NUMBER`: the face index of the font, if the font contains multiple font faces.
+ * - `TTF_PROP_FONT_HORIZONTAL_DPI_NUMBER`: the horizontal DPI to use for font rendering, defaults to `TTF_PROP_FONT_VERTICAL_DPI_NUMBER` if set, or 72 otherwise.
+ * - `TTF_PROP_FONT_VERTICAL_DPI_NUMBER`: the vertical DPI to use for font rendering, defaults to `TTF_PROP_FONT_HORIZONTAL_DPI_NUMBER` if set, or 72 otherwise.
  *
- * \param src an SDL_IOStream to provide a font file's data.
- * \param closeio true to close `src` when the font is closed, false to leave
- *                it open.
- * \param ptsize point size to use for the newly-opened font.
- * \param index index of the face in the font file.
- * \returns a valid TTF_Font, or NULL on error.
+ * \param props the properties to use.
+ * \returns a valid TTF_Font, or NULL on failure; call SDL_GetError() for more information.
  *
  * \since This function is available since SDL_ttf 3.0.0.
  *
  * \sa TTF_CloseFont
  */
-extern SDL_DECLSPEC TTF_Font * SDLCALL TTF_OpenFontIndexIO(SDL_IOStream *src, bool closeio, int ptsize, long index);
+extern SDL_DECLSPEC TTF_Font * SDLCALL TTF_OpenFontWithProperties(SDL_PropertiesID props);
 
-/**
- * Create a font from a file, using target resolutions (in DPI).
- *
- * DPI scaling only applies to scalable fonts (e.g. TrueType).
- *
- * Some .fon fonts will have several sizes embedded in the file, so the point
- * size becomes the index of choosing which size. If the value is too high,
- * the last indexed size will be the default.
- *
- * When done with the returned TTF_Font, use TTF_CloseFont() to dispose of it.
- *
- * \param file path to font file.
- * \param ptsize point size to use for the newly-opened font.
- * \param hdpi the target horizontal DPI.
- * \param vdpi the target vertical DPI.
- * \returns a valid TTF_Font, or NULL on error.
- *
- * \since This function is available since SDL_ttf 3.0.0.
- *
- * \sa TTF_CloseFont
- */
-extern SDL_DECLSPEC TTF_Font * SDLCALL TTF_OpenFontDPI(const char *file, int ptsize, unsigned int hdpi, unsigned int vdpi);
-
-/**
- * Create a font from a file, using target resolutions (in DPI).
- *
- * DPI scaling only applies to scalable fonts (e.g. TrueType).
- *
- * Some .fon fonts will have several sizes embedded in the file, so the point
- * size becomes the index of choosing which size. If the value is too high,
- * the last indexed size will be the default.
- *
- * Some fonts have multiple "faces" included. The index specifies which face
- * to use from the font file. Font files with only one face should specify
- * zero for the index.
- *
- * When done with the returned TTF_Font, use TTF_CloseFont() to dispose of it.
- *
- * \param file path to font file.
- * \param ptsize point size to use for the newly-opened font.
- * \param index index of the face in the font file.
- * \param hdpi the target horizontal DPI.
- * \param vdpi the target vertical DPI.
- * \returns a valid TTF_Font, or NULL on error.
- *
- * \since This function is available since SDL_ttf 3.0.0.
- *
- * \sa TTF_CloseFont
- */
-extern SDL_DECLSPEC TTF_Font * SDLCALL TTF_OpenFontIndexDPI(const char *file, int ptsize, long index, unsigned int hdpi, unsigned int vdpi);
-
-/**
- * Opens a font from an SDL_IOStream with target resolutions (in DPI).
- *
- * DPI scaling only applies to scalable fonts (e.g. TrueType).
- *
- * Some .fon fonts will have several sizes embedded in the file, so the point
- * size becomes the index of choosing which size. If the value is too high,
- * the last indexed size will be the default.
- *
- * If `closeio` is true `src` will be automatically closed once the font is
- * closed. Otherwise you should close `src` yourself after closing the font.
- *
- * When done with the returned TTF_Font, use TTF_CloseFont() to dispose of it.
- *
- * \param src an SDL_IOStream to provide a font file's data.
- * \param closeio true to close `src` when the font is closed, false to leave
- *                it open.
- * \param ptsize point size to use for the newly-opened font.
- * \param hdpi the target horizontal DPI.
- * \param vdpi the target vertical DPI.
- * \returns a valid TTF_Font, or NULL on error.
- *
- * \since This function is available since SDL_ttf 3.0.0.
- *
- * \sa TTF_CloseFont
- */
-extern SDL_DECLSPEC TTF_Font * SDLCALL TTF_OpenFontDPIIO(SDL_IOStream *src, bool closeio, int ptsize, unsigned int hdpi, unsigned int vdpi);
-
-/**
- * Opens a font from an SDL_IOStream with target resolutions (in DPI).
- *
- * DPI scaling only applies to scalable fonts (e.g. TrueType).
- *
- * Some .fon fonts will have several sizes embedded in the file, so the point
- * size becomes the index of choosing which size. If the value is too high,
- * the last indexed size will be the default.
- *
- * If `closeio` is true `src` will be automatically closed once the font is
- * closed. Otherwise you should close `src` yourself after closing the font.
- *
- * Some fonts have multiple "faces" included. The index specifies which face
- * to use from the font file. Font files with only one face should specify
- * zero for the index.
- *
- * When done with the returned TTF_Font, use TTF_CloseFont() to dispose of it.
- *
- * \param src an SDL_IOStream to provide a font file's data.
- * \param closeio true to close `src` when the font is closed, false to leave
- *                it open.
- * \param ptsize point size to use for the newly-opened font.
- * \param index index of the face in the font file.
- * \param hdpi the target horizontal DPI.
- * \param vdpi the target vertical DPI.
- * \returns a valid TTF_Font, or NULL on error.
- *
- * \since This function is available since SDL_ttf 3.0.0.
- *
- * \sa TTF_CloseFont
- */
-extern SDL_DECLSPEC TTF_Font * SDLCALL TTF_OpenFontIndexDPIIO(SDL_IOStream *src, bool closeio, int ptsize, long index, unsigned int hdpi, unsigned int vdpi);
+#define TTF_PROP_FONT_FILENAME_STRING               "SDL_ttf.font.filename"
+#define TTF_PROP_FONT_IOSTREAM_POINTER              "SDL_ttf.font.iostream"
+#define TTF_PROP_FONT_IOSTREAM_AUTOCLOSE_BOOLEAN    "SDL_ttf.font.iostream.autoclose"
+#define TTF_PROP_FONT_SIZE_NUMBER                   "SDL_ttf.font.size"
+#define TTF_PROP_FONT_FACE_NUMBER                   "SDL_ttf.font.face"
+#define TTF_PROP_FONT_HORIZONTAL_DPI_NUMBER         "SDL_ttf.font.hdpi"
+#define TTF_PROP_FONT_VERTICAL_DPI_NUMBER           "SDL_ttf.font.vdpi"
 
 /**
  * Set a font's size dynamically.
@@ -1191,7 +1056,7 @@ extern SDL_DECLSPEC int SDLCALL TTF_WasInit(void);
  * \param font the font to query.
  * \param previous_ch the previous character's code, 32 bits.
  * \param ch the current character's code, 32 bits.
- * \returns The kerning size between the two specified characters, in pixels, or -1 on error.
+ * \returns The kerning size between the two specified characters, in pixels, or -1 on failure; call SDL_GetError() for more information.
  *
  * \since This function is available since SDL_ttf 3.0.0.
  */
diff --git a/src/SDL_ttf.c b/src/SDL_ttf.c
index c191a996..ab9e01ce 100644
--- a/src/SDL_ttf.c
+++ b/src/SDL_ttf.c
@@ -1722,8 +1722,15 @@ static unsigned long IOread(
     return SDL_ReadIO(src, buffer, count);
 }
 
-TTF_Font* TTF_OpenFontIndexDPIIO(SDL_IOStream *src, bool closeio, int ptsize, long index, unsigned int hdpi, unsigned int vdpi)
-{
+TTF_Font *TTF_OpenFontWithProperties(SDL_PropertiesID props)
+{
+    const char *file = SDL_GetStringProperty(props, TTF_PROP_FONT_FILENAME_STRING, NULL);
+    SDL_IOStream *src = SDL_GetPointerProperty(props, TTF_PROP_FONT_IOSTREAM_POINTER, NULL);
+    bool closeio = SDL_GetBooleanProperty(props, TTF_PROP_FONT_IOSTREAM_AUTOCLOSE_BOOLEAN, false);
+    int ptsize = (int)SDL_GetNumberProperty(props, TTF_PROP_FONT_SIZE_NUMBER, 0);
+    long index = (long)SDL_GetNumberProperty(props, TTF_PROP_FONT_FACE_NUMBER, 0);
+    unsigned int hdpi = (unsigned int)SDL_GetNumberProperty(props, TTF_PROP_FONT_HORIZONTAL_DPI_NUMBER, 0);
+    unsigned int vdpi = (unsigned int)SDL_GetNumberProperty(props, TTF_PROP_FONT_VERTICAL_DPI_NUMBER, 0);
     TTF_Font *font;
     FT_Error error;
     FT_Face face;
@@ -1741,8 +1748,16 @@ TTF_Font* TTF_OpenFontIndexDPIIO(SDL_IOStream *src, bool closeio, int ptsize, lo
     }
 
     if (!src) {
-        SDL_SetError("Passed a NULL font source");
-        return NULL;
+        if (!file) {
+            SDL_SetError("You must set either TTF_PROP_FONT_FILENAME_STRING or TTF_PROP_FONT_IOSTREAM_POINTER");
+            return NULL;
+        }
+
+        src = SDL_IOFromFile(file, "rb");
+        if (!src) {
+            return NULL;
+        }
+        closeio = true;
     }
 
     /* Check to make sure we can seek in this stream */
@@ -1868,6 +1883,33 @@ TTF_Font* TTF_OpenFontIndexDPIIO(SDL_IOStream *src, bool closeio, int ptsize, lo
     return font;
 }
 
+TTF_Font *TTF_OpenFont(const char *file, int ptsize)
+{
+    TTF_Font *font = NULL;
+    SDL_PropertiesID props = SDL_CreateProperties();
+    if (props) {
+        SDL_SetStringProperty(props, TTF_PROP_FONT_FILENAME_STRING, file);
+        SDL_SetNumberProperty(props, TTF_PROP_FONT_SIZE_NUMBER, ptsize);
+        font = TTF_OpenFontWithProperties(props);
+        SDL_DestroyProperties(props);
+    }
+    return font;
+}
+
+TTF_Font *TTF_OpenFontIO(SDL_IOStream *src, bool closeio, int ptsize)
+{
+    TTF_Font *font = NULL;
+    SDL_PropertiesID props = SDL_CreateProperties();
+    if (props) {
+        SDL_SetPointerProperty(props, TTF_PROP_FONT_IOSTREAM_POINTER, src);
+        SDL_SetBooleanProperty(props, TTF_PROP_FONT_IOSTREAM_AUTOCLOSE_BOOLEAN, closeio);
+        SDL_SetNumberProperty(props, TTF_PROP_FONT_SIZE_NUMBER, ptsize);
+        font = TTF_OpenFontWithProperties(props);
+        SDL_DestroyProperties(props);
+    }
+    return font;
+}
+
 bool TTF_SetFontSizeDPI(TTF_Font *font, int ptsize, unsigned int hdpi, unsigned int vdpi)
 {
     FT_Face face = font->face;
@@ -1992,45 +2034,6 @@ static int TTF_initFontMetrics(TTF_Font *font)
     return 0;
 }
 
-TTF_Font* TTF_OpenFontDPIIO(SDL_IOStream *src, bool closeio, int ptsize, unsigned int hdpi, unsigned int vdpi)
-{
-    return TTF_OpenFontIndexDPIIO(src, closeio, ptsize, 0, hdpi, vdpi);
-}
-
-TTF_Font* TTF_OpenFontIndexIO(SDL_IOStream *src, bool closeio, int ptsize, long index)
-{
-    return TTF_OpenFontIndexDPIIO(src, closeio, ptsize, index, 0, 0);
-}
-
-TTF_Font* TTF_OpenFontIndexDPI(const char *file, int ptsize, long index, unsigned int hdpi, unsigned int vdpi)
-{
-    SDL_IOStream *rw = SDL_IOFromFile(file, "rb");
-    if ( rw == NULL ) {
-        return NULL;
-    }
-    return TTF_OpenFontIndexDPIIO(rw, 1, ptsize, index, hdpi, vdpi);
-}
-
-TTF_Font* TTF_OpenFontIO(SDL_IOStream *src, bool closeio, int ptsize)
-{
-    return TTF_OpenFontIndexIO(src, closeio, ptsize, 0);
-}
-
-TTF_Font* TTF_OpenFontDPI(const char *file, int ptsize, unsigned int hdpi, unsigned int vdpi)
-{
-    return TTF_OpenFontIndexDPI(file, ptsize, 0, hdpi, vdpi);
-}
-
-TTF_Font* TTF_OpenFontIndex(const char *file, int ptsize, long index)
-{
-    return TTF_OpenFontIndexDPI(file, ptsize, index, 0, 0);
-}
-
-TTF_Font* TTF_OpenFont(const char *file, int ptsize)
-{
-    return TTF_OpenFontIndex(file, ptsize, 0);
-}
-
 static void Flush_Glyph_Image(TTF_Image *image) {
     if (image->buffer) {
         SDL_free(image->buffer);
diff --git a/src/SDL_ttf.sym b/src/SDL_ttf.sym
index 4128a2d5..85750db6 100644
--- a/src/SDL_ttf.sym
+++ b/src/SDL_ttf.sym
@@ -24,13 +24,8 @@ SDL3_ttf_0.0.0 {
     TTF_IsFontScalable;
     TTF_MeasureText;
     TTF_OpenFont;
-    TTF_OpenFontDPI;
-    TTF_OpenFontDPIIO;
     TTF_OpenFontIO;
-    TTF_OpenFontIndex;
-    TTF_OpenFontIndexDPI;
-    TTF_OpenFontIndexDPIIO;
-    TTF_OpenFontIndexIO;
+    TTF_OpenFontWithProperties;
     TTF_Quit;
     TTF_RenderGlyph_Blended;
     TTF_RenderGlyph_LCD;