@ -6,6 +6,7 @@
// CHANGELOG
// (minor and older changes stripped away, please see git history for details)
// 2023/07/12: added support for SVG fonts, enable by using '#define IMGUI_ENABLE_FREETYPE_LUNASVG'
// 2023/01/04: fixed a packing issue which in some occurrences would prevent large amount of glyphs from being packed correctly.
// 2021/08/23: fixed crash when FT_Render_Glyph() fails to render a glyph and returns NULL.
// 2021/03/05: added ImGuiFreeTypeBuilderFlags_Bitmap to load bitmap glyphs.
@ -44,6 +45,13 @@
# include FT_GLYPH_H // <freetype/ftglyph.h>
# include FT_SYNTHESIS_H // <freetype/ftsynth.h>
# ifdef IMGUI_ENABLE_FREETYPE_LUNASVG
# include FT_OTSVG_H // <freetype/otsvg.h>
# include FT_BBOX_H // <freetype/ftbbox.h>
# include <lunasvg.h>
# include <memory>
# endif
# ifdef _MSC_VER
# pragma warning (push)
# pragma warning (disable: 4505) // unreferenced local function has been removed (stb stuff)
@ -70,6 +78,14 @@ static void* (*GImGuiFreeTypeAllocFunc)(size_t size, void* user_data) = ImGuiFre
static void ( * GImGuiFreeTypeFreeFunc ) ( void * ptr , void * user_data ) = ImGuiFreeTypeDefaultFreeFunc ;
static void * GImGuiFreeTypeAllocatorUserData = nullptr ;
# ifdef IMGUI_ENABLE_FREETYPE_LUNASVG
FT_Error ImGuiLunasvgPortInit ( FT_Pointer * state ) ;
void ImGuiLunasvgPortFree ( FT_Pointer * state ) ;
FT_Error ImGuiLunasvgPortRender ( FT_GlyphSlot slot , FT_Pointer * _state ) ;
FT_Error ImGuiLunasvgPortPresetSlot ( FT_GlyphSlot slot , FT_Bool cache , FT_Pointer * _state ) ;
# endif
//-------------------------------------------------------------------------
// Code
//-------------------------------------------------------------------------
@ -244,7 +260,19 @@ namespace
// Need an outline for this to work
FT_GlyphSlot slot = Face - > glyph ;
# if ((FREETYPE_MAJOR >= 2) && (FREETYPE_MINOR >= 12))
# ifdef IMGUI_ENABLE_FREETYPE_LUNASVG
IM_ASSERT ( slot - > format = = FT_GLYPH_FORMAT_OUTLINE | | slot - > format = = FT_GLYPH_FORMAT_BITMAP | | slot - > format = = FT_GLYPH_FORMAT_SVG ) ;
# else
IM_ASSERT ( slot - > format ! = FT_GLYPH_FORMAT_SVG & &
" The font contains SVG glyphs, you'll need to enable IMGUI_ENABLE_FREETYPE_LUNASVG "
" in imconfig.h and install required libraries in order to use this font " ) ;
IM_ASSERT ( slot - > format = = FT_GLYPH_FORMAT_OUTLINE | | slot - > format = = FT_GLYPH_FORMAT_BITMAP ) ;
# endif // IMGUI_ENABLE_FREETYPE_LUNASVG
# else
IM_ASSERT ( slot - > format = = FT_GLYPH_FORMAT_OUTLINE | | slot - > format = = FT_GLYPH_FORMAT_BITMAP ) ;
# endif // ((FREETYPE_MAJOR >= 2) && (FREETYPE_MINOR >= 12))
// Apply convenience transform (this is not picking from real "Bold"/"Italic" fonts! Merely applying FreeType helper transform. Oblique == Slanting)
if ( UserFlags & ImGuiFreeTypeBuilderFlags_Bold )
@ -770,6 +798,18 @@ static bool ImFontAtlasBuildWithFreeType(ImFontAtlas* atlas)
// If you don't call FT_Add_Default_Modules() the rest of code may work, but FreeType won't use our custom allocator.
FT_Add_Default_Modules ( ft_library ) ;
# ifdef IMGUI_ENABLE_FREETYPE_LUNASVG
# if ((FREETYPE_MAJOR >= 2) && (FREETYPE_MINOR >= 12))
// Install svg hooks for FreeType
// https://freetype.org/freetype2/docs/reference/ft2-properties.html#svg-hooks
// https://freetype.org/freetype2/docs/reference/ft2-svg_fonts.html#svg_fonts
SVG_RendererHooks hooks = { ImGuiLunasvgPortInit , ImGuiLunasvgPortFree , ImGuiLunasvgPortRender , ImGuiLunasvgPortPresetSlot } ;
FT_Property_Set ( ft_library , " ot-svg " , " svg-hooks " , & hooks ) ;
# else
IM_ASSERT ( ! " IMGUI_ENABLE_FREETYPE_LUNASVG requires FreeType version >= 2.12 " ) ;
# endif // ((FREETYPE_MAJOR >= 2) && (FREETYPE_MINOR >= 12))
# endif // IMGUI_ENABLE_FREETYPE_LUNASVG
bool ret = ImFontAtlasBuildWithFreeTypeEx ( ft_library , atlas , atlas - > FontBuilderFlags ) ;
FT_Done_Library ( ft_library ) ;
@ -790,6 +830,123 @@ void ImGuiFreeType::SetAllocatorFunctions(void* (*alloc_func)(size_t sz, void* u
GImGuiFreeTypeAllocatorUserData = user_data ;
}
# 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
typedef struct LunasvgPortState_
{
FT_Error err = FT_Err_Ok ;
std : : unique_ptr < lunasvg : : Document > svg = nullptr ;
lunasvg : : Matrix matrix ; // Scaled and translated matrix for rendering
} LunasvgPortState , * PLunasvgPortState ;
FT_Error ImGuiLunasvgPortInit ( FT_Pointer * _state )
{
* _state = new LunasvgPortState ( ) ;
return FT_Err_Ok ;
}
void ImGuiLunasvgPortFree ( FT_Pointer * _state )
{
delete * _state ;
}
FT_Error ImGuiLunasvgPortRender ( FT_GlyphSlot slot , FT_Pointer * _state )
{
PLunasvgPortState state = * ( PLunasvgPortState * ) _state ;
// If there was an error while loading the svg in
// `ImGuiLunasvgPortPresetSlot`, the renderer hook
// still get called, so just returns the error.
if ( state - > err ! = FT_Err_Ok ) return state - > err ;
// rows is height, pitch (or stride) equals to width * sizeof(int32)
lunasvg : : Bitmap bitmap ( ( uint8_t * ) slot - > bitmap . buffer , slot - > bitmap . width , slot - > bitmap . rows , slot - > bitmap . pitch ) ;
state - > svg - > setMatrix ( state - > svg - > matrix ( ) . identity ( ) ) ; // Reset the svg matrix to the default value
state - > svg - > render ( bitmap , state - > matrix ) ; // state->matrix is already scaled and translated
state - > err = FT_Err_Ok ;
return state - > err ;
}
FT_Error ImGuiLunasvgPortPresetSlot ( FT_GlyphSlot slot , FT_Bool cache , FT_Pointer * _state )
{
FT_SVG_Document document = ( FT_SVG_Document ) slot - > other ;
PLunasvgPortState state = * ( PLunasvgPortState * ) _state ;
FT_Size_Metrics & metrics = document - > metrics ;
// This function is called twice, once in the `FT_Load_Glyph`
// and another right before `ImGuiLunasvgPortRender`.
// If it's the latter, don't do anything because it's
// already done in the former.
if ( cache ) return state - > err ;
state - > svg = lunasvg : : Document : : loadFromData ( ( const char * ) document - > svg_document , document - > svg_document_length ) ;
if ( state - > svg = = nullptr )
{
state - > err = FT_Err_Invalid_SVG_Document ;
return state - > err ;
}
lunasvg : : Box box = state - > svg - > box ( ) ;
double scale = std : : min ( metrics . x_ppem / box . w , metrics . y_ppem / box . h ) ;
double xx = ( double ) document - > transform . xx / ( 1 < < 16 ) ;
double xy = - ( double ) document - > transform . xy / ( 1 < < 16 ) ;
double yx = - ( double ) document - > transform . yx / ( 1 < < 16 ) ;
double yy = ( double ) document - > transform . yy / ( 1 < < 16 ) ;
double x0 = ( double ) document - > delta . x / 64 * box . w / metrics . x_ppem ;
double y0 = - ( double ) document - > delta . y / 64 * box . h / metrics . y_ppem ;
// This reset the matrix to its default value
state - > matrix . identity ( ) ;
// Scale and transform, we don't translate the svg yet
state - > matrix . scale ( scale , scale ) ;
state - > matrix . transform ( xx , xy , yx , yy , x0 , y0 ) ;
state - > svg - > setMatrix ( state - > matrix ) ;
// Pre-translate the matrix for the rendering step
state - > matrix . translate ( - box . x , - box . y ) ;
// Get the box again after the transformation
box = state - > svg - > box ( ) ;
// Calculate the bitmap size
slot - > bitmap_left = FT_Int ( box . x ) ;
slot - > bitmap_top = FT_Int ( - box . y ) ;
slot - > bitmap . rows = ( unsigned int ) ( ceil ( box . h ) ) ;
slot - > bitmap . width = ( unsigned int ) ( ceil ( box . w ) ) ;
slot - > bitmap . pitch = slot - > bitmap . width * 4 ;
slot - > bitmap . pixel_mode = FT_PIXEL_MODE_BGRA ;
// Compute all the bearings and set them correctly. The outline is
// scaled already, we just need to use the bounding box.
double metrics_width = box . w ;
double metrics_height = box . h ;
double horiBearingX = box . x ;
double horiBearingY = - box . y ;
double vertBearingX = slot - > metrics . horiBearingX / 64.0 - slot - > metrics . horiAdvance / 64.0 / 2.0 ;
double vertBearingY = ( slot - > metrics . vertAdvance / 64.0 - slot - > metrics . height / 64.0 ) / 2.0 ;
slot - > metrics . width = FT_Pos ( round ( metrics_width * 64.0 ) ) ;
slot - > metrics . height = FT_Pos ( round ( metrics_height * 64.0 ) ) ;
slot - > metrics . horiBearingX = FT_Pos ( horiBearingX * 64 ) ;
slot - > metrics . horiBearingY = FT_Pos ( horiBearingY * 64 ) ;
slot - > metrics . vertBearingX = FT_Pos ( vertBearingX * 64 ) ;
slot - > metrics . vertBearingY = FT_Pos ( vertBearingY * 64 ) ;
if ( slot - > metrics . vertAdvance = = 0 )
slot - > metrics . vertAdvance = FT_Pos ( metrics_height * 1.2 * 64.0 ) ;
state - > err = FT_Err_Ok ;
return state - > err ;
}
# endif // !IMGUI_ENABLE_FREETYPE_LUNASVG
//-----------------------------------------------------------------------------
# ifdef __GNUC__