diff --git a/imgui.h b/imgui.h index 936ce0add..9e1ecbf5a 100644 --- a/imgui.h +++ b/imgui.h @@ -3527,6 +3527,26 @@ struct ImTextureData // [SECTION] Font API (ImFontConfig, ImFontGlyph, ImFontAtlasFlags, ImFontAtlas, ImFontGlyphRangesBuilder, ImFont) //----------------------------------------------------------------------------- +// Helper macro to create OpenType axis tags (e.g., IM_FONT_TAG('w','g','h','t') for weight) +#define IM_FONT_TAG(c1,c2,c3,c4) (((ImU32)(ImU8)(c1)<<24)|((ImU32)(ImU8)(c2)<<16)|((ImU32)(ImU8)(c3)<<8)|((ImU32)(ImU8)(c4))) + +// Variable font axis setting (for use with ImFontConfigVarAxes) +struct ImFontConfigVarAxis +{ + ImU32 Tag; // OpenType axis tag (use IM_FONT_TAG('w','g','h','t'), etc.) + float Value; // Axis value + ImFontConfigVarAxis() { memset(this, 0, sizeof(*this)); } + ImFontConfigVarAxis(ImU32 tag, float value) { Tag = tag; Value = value; } +}; + +// Container for variable font axis settings (pointer + count pattern) +struct ImFontConfigVarAxes +{ + const ImFontConfigVarAxis* Axes; // Pointer to axis array (data must persist as long as font is alive) + int AxesCount; // Number of entries in Axes array + ImFontConfigVarAxes() { memset(this, 0, sizeof(*this)); } +}; + // A font input/source (we may rename this to ImFontSource in the future) struct ImFontConfig { @@ -3557,6 +3577,9 @@ struct ImFontConfig float RasterizerDensity; // 1.0f // [LEGACY: this only makes sense when ImGuiBackendFlags_RendererHasTextures is not supported] DPI scale multiplier for rasterization. Not altering other font metrics: makes it easy to swap between e.g. a 100% and a 400% fonts for a zooming display, or handle Retina screen. IMPORTANT: If you change this it is expected that you increase/decrease font scale roughly to the inverse of this, otherwise quality may look lowered. float ExtraSizeScale; // 1.0f // Extra rasterizer scale over SizePixels. + // Variable font settings (requires FreeType loader) + ImFontConfigVarAxes VarAxes; // {} // [Variable fonts] Generic axis settings for any OpenType axis. Use IM_FONT_TAG() macro for tags. + // [Internal] ImFontFlags Flags; // Font flags (don't use just yet, will be exposed in upcoming 1.92.X updates) ImFont* DstFont; // Target font (as we merging fonts, multiple ImFontConfig may target the same font) diff --git a/misc/freetype/imgui_freetype.cpp b/misc/freetype/imgui_freetype.cpp index c358d89db..2d5cf727d 100644 --- a/misc/freetype/imgui_freetype.cpp +++ b/misc/freetype/imgui_freetype.cpp @@ -48,6 +48,8 @@ #include FT_GLYPH_H // #include FT_SIZES_H // #include FT_SYNTHESIS_H // +#include FT_MULTIPLE_MASTERS_H // +#include FT_TRUETYPE_TABLES_H // // Handle LunaSVG and PlutoSVG #if defined(IMGUI_ENABLE_FREETYPE_LUNASVG) && defined(IMGUI_ENABLE_FREETYPE_PLUTOSVG) @@ -433,6 +435,38 @@ static bool ImGui_ImplFreeType_FontBakedInit(ImFontAtlas* atlas, ImFontConfig* s FT_New_Size(bd_font_data->FtFace, &bd_baked_data->FtSize); FT_Activate_Size(bd_baked_data->FtSize); + // Variable font axis support - configure design BEFORE size request + FT_MM_Var* mm_var = nullptr; + if (FT_Get_MM_Var(bd_font_data->FtFace, &mm_var) == 0 && mm_var != nullptr) + { + FT_Fixed* coords = (FT_Fixed*)IM_ALLOC(mm_var->num_axis * sizeof(FT_Fixed)); + // Initialize to font's default values (not current values which may be cached) + for (FT_UInt i = 0; i < mm_var->num_axis; i++) + coords[i] = mm_var->axis[i].def; + + // Helper to clamp and set axis value + auto SetAxis = [&](FT_UInt i, float value) { + float axis_min = (float)mm_var->axis[i].minimum / 65536.0f; + float axis_max = (float)mm_var->axis[i].maximum / 65536.0f; + if (value < axis_min) value = axis_min; + if (value > axis_max) value = axis_max; + coords[i] = (FT_Fixed)(value * 65536.0f); + }; + + // Apply axis settings from VarAxes (user-provided values override font defaults) + for (FT_UInt i = 0; i < mm_var->num_axis; i++) + { + FT_ULong tag = mm_var->axis[i].tag; + for (int j = 0; j < src->VarAxes.AxesCount; j++) + if (tag == (FT_ULong)src->VarAxes.Axes[j].Tag) + SetAxis(i, src->VarAxes.Axes[j].Value); + } + + FT_Set_Var_Design_Coordinates(bd_font_data->FtFace, mm_var->num_axis, coords); + IM_FREE(coords); + FT_Done_MM_Var(bd_font_data->FtFace->glyph->library, mm_var); + } + // Vuhdo 2017: "I'm not sure how to deal with font sizes properly. As far as I understand, currently ImGui assumes that the 'pixel_height' // is a maximum height of an any given glyph, i.e. it's the sum of font's ascender and descender. Seems strange to me. // FT_Set_Pixel_Sizes() doesn't seem to get us the same result." @@ -606,6 +640,66 @@ bool ImGuiFreeType::DebugEditFontLoaderFlags(unsigned int* p_font_loader_flags) return edited; } +int ImGuiFreeType::GetFontAxes(const char* filename, ImFontVarAxisInfo* out_axes, int max_axes) +{ + // Initialize FreeType library (temporary, just for this query) + FT_Library ft_library; + if (FT_Init_FreeType(&ft_library) != 0) + return -1; + + // Load font face + FT_Face ft_face; + if (FT_New_Face(ft_library, filename, 0, &ft_face) != 0) + { + FT_Done_FreeType(ft_library); + return -1; + } + + // Query variable font axes + int result = 0; + FT_MM_Var* mm_var = nullptr; + if (FT_Get_MM_Var(ft_face, &mm_var) == 0 && mm_var != nullptr) + { + result = (int)mm_var->num_axis; + + // Fill output array if provided + if (out_axes != nullptr) + { + int count = (result < max_axes) ? result : max_axes; + for (int i = 0; i < count; i++) + { + const FT_Var_Axis& axis = mm_var->axis[i]; + out_axes[i].Tag = (ImU32)axis.tag; + out_axes[i].Minimum = (float)axis.minimum / 65536.0f; + out_axes[i].Default = (float)axis.def / 65536.0f; + out_axes[i].Maximum = (float)axis.maximum / 65536.0f; + + // Copy axis name (FreeType provides null-terminated string) + if (axis.name) + { + size_t len = strlen(axis.name); + if (len >= sizeof(out_axes[i].Name)) + len = sizeof(out_axes[i].Name) - 1; + memcpy(out_axes[i].Name, axis.name, len); + out_axes[i].Name[len] = '\0'; + } + else + { + out_axes[i].Name[0] = '\0'; + } + } + } + + FT_Done_MM_Var(ft_library, mm_var); + } + + // Cleanup + FT_Done_Face(ft_face); + FT_Done_FreeType(ft_library); + + return result; +} + #ifdef IMGUI_ENABLE_FREETYPE_LUNASVG // For more details, see https://gitlab.freedesktop.org/freetype/freetype-demos/-/blob/master/src/rsvg-port.c // The original code from the demo is licensed under CeCILL-C Free Software License Agreement (https://gitlab.freedesktop.org/freetype/freetype/-/blob/master/LICENSE.TXT) diff --git a/misc/freetype/imgui_freetype.h b/misc/freetype/imgui_freetype.h index 85313699d..0440998d2 100644 --- a/misc/freetype/imgui_freetype.h +++ b/misc/freetype/imgui_freetype.h @@ -58,6 +58,16 @@ enum ImGuiFreeTypeLoaderFlags_ typedef ImGuiFreeTypeLoaderFlags_ ImGuiFreeTypeBuilderFlags_; #endif +// Variable font axis information (returned by GetFontAxes) +struct ImFontVarAxisInfo +{ + ImU32 Tag; // OpenType axis tag (use IM_FONT_TAG to compare, e.g., IM_FONT_TAG('w','g','h','t')) + char Name[64]; // Human-readable axis name (e.g., "Weight", "Width", "Optical Size") + float Minimum; // Minimum axis value + float Default; // Default axis value + float Maximum; // Maximum axis value +}; + namespace ImGuiFreeType { // This is automatically assigned when using '#define IMGUI_ENABLE_FREETYPE'. @@ -73,6 +83,12 @@ namespace ImGuiFreeType // Display UI to edit ImFontAtlas::FontLoaderFlags (shared) or ImFontConfig::FontLoaderFlags (single source) IMGUI_API bool DebugEditFontLoaderFlags(ImGuiFreeTypeLoaderFlags* p_font_loader_flags); + // Query variable font axes from a font file (standalone utility, no atlas required) + // Returns number of axes found, or -1 on error (file not found, not a valid font, etc.) + // If out_axes is NULL, just returns the axis count without writing data. + // Axes array should have at least 'max_axes' elements. + IMGUI_API int GetFontAxes(const char* filename, ImFontVarAxisInfo* out_axes, int max_axes); + // Obsolete names (will be removed) #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS //IMGUI_API const ImFontBuilderIO* GetBuilderForFreeType(); // Renamed/changed in 1.92. Change 'io.Fonts->FontBuilderIO = ImGuiFreeType::GetBuilderForFreeType()' to 'io.Fonts->SetFontLoader(ImGuiFreeType::GetFontLoader())' if you need runtime selection.