diff options
| author | Jean-Philippe Bruyère <jp_bruyere@hotmail.com> | 2026-10-12 17:05:37 +0200 |
|---|---|---|
| committer | Jean-Philippe Bruyère <jp_bruyere@hotmail.com> | 2026-10-12 17:05:37 +0200 |
| commit | 0f02ed39c8488deef8168abe5fbe2860b6999f24 (patch) | |
| tree | a4fc6599883aa97cc74244a2cf37118b4c35e291 | |
| parent | 082fb08d50ca705440b14d8414ed07abcd232fa9 (diff) | |
doc, api refined, debug, wip
| -rw-r--r-- | include/vkvg.h | 106 | ||||
| -rw-r--r-- | src/vkvg_context.c | 15 | ||||
| -rw-r--r-- | src/vkvg_fonts.c | 125 | ||||
| -rw-r--r-- | src/vkvg_fonts.h | 5 | ||||
| -rw-r--r-- | tests/font_tests/array.h | 81 | ||||
| -rw-r--r-- | tests/font_tests/font_tests.c | 995 | ||||
| -rw-r--r-- | tests/font_tests/format12.c | 154 | ||||
| -rw-r--r-- | tests/fonts/allfont.c | 124 | ||||
| -rw-r--r-- | tests/fonts/fontquery.c | 137 | ||||
| -rw-r--r-- | tests/vkvg-svg/svg.c (renamed from tests/svg.c) | 0 |
10 files changed, 339 insertions, 1403 deletions
diff --git a/include/vkvg.h b/include/vkvg.h index 8356c44..e6f2b52 100644 --- a/include/vkvg.h +++ b/include/vkvg.h @@ -1,24 +1,6 @@ -/* - * Copyright (c) 2018-2025 Jean-Philippe Bruyère <jp_bruyere@hotmail.com> - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the - * Software, and to permit persons to whom the Software is furnished to do so, subject - * to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ +// Copyright (c) 2018-2026 Jean-Philippe Bruyère <jp_bruyere@hotmail.com> +// +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) #ifndef VKVG_H #define VKVG_H @@ -146,14 +128,14 @@ typedef enum { VKVG_STATUS_FILE_NOT_FOUND, /*!< */ VKVG_STATUS_INVALID_DASH, /*!< invalid value for a dash setting */ VKVG_STATUS_INVALID_RECT, /*!< rectangle with height or width equal to 0. */ - VKVG_STATUS_TIMEOUT, /*!< waiting for a vulkan operation to finish resulted in a fence timeout (5 seconds)*/ - VKVG_STATUS_DEVICE_ERROR, /*!< vkvg device initialization error */ + VKVG_STATUS_TIMEOUT, /*!< waiting for a vulkan operation to finish resulted in a fence timeout (5 seconds)*/ + VKVG_STATUS_DEVICE_ERROR, /*!< vkvg device initialization error */ VKVG_STATUS_INVALID_DEVICE_CREATE_INFO, /*!< vkvg device initialization error */ - VKVG_STATUS_INVALID_IMAGE, /*!< */ - VKVG_STATUS_INVALID_SURFACE, /*!< */ - VKVG_STATUS_INVALID_FONT, /*!< Unresolved font name*/ - VKVG_STATUS_IN_CACHE, /*!< This is not an error, context is stored in cache, not usable until recovered for another - context creation. */ + VKVG_STATUS_INVALID_IMAGE, /*!< */ + VKVG_STATUS_INVALID_SURFACE, /*!< */ + VKVG_STATUS_INVALID_FONT, /*!< Unresolved font name*/ + VKVG_STATUS_IN_CACHE, /*!< This is not an error, context is stored in cache, not usable until recovered for another + context creation. */ VKVG_STATUS_ENUM_MAX = 0x7FFFFFFF } vkvg_status_t; @@ -338,22 +320,64 @@ typedef struct _vkvg_text_run_t* VkvgText; /** * @brief Opaque pointer on a vkvg font. * - * VkvgFont - * + * VkvgFont holds a single type face at a specific size. Creating multiple size for the same type face will share + * underlying font resources. See @ref vkvg_font_create and @ref vkvg_font_create_from_file. + * VkvgFont object control font caching lifecycle. Keeping a reference to a VkvgFont ensure all caching are kept. + * When done with a font, call @ref vkvg_font_destroy to release all associated resources and cache entries. + * Font cache and all other font resources are released on device destroy. */ typedef struct _vkvg_font_t* VkvgFont; -/* - * @brief Create a new VkvgFont. +/** + * @brief Create a new @ref VkvgFont at the specified size from the provided query string. + * + * If fontconfig is enable, query strings with be parsed with fontconfig textural representation engine: + * <families>-<point sizes>:<name1>=<values1>:<name2>=<values2>... + * + * @param dev Each font instance will be bound to a @ref VkvgDevice which holds the font cache. + * @param queryString + * @param pointSize The font size in point unit, 1/72 of an inch. + * @return A new VkvgFont or null if an error occured. */ vkvg_public VkvgFont vkvg_font_create (VkvgDevice dev, const char* queryString, float pointSize); +/** + * @brief Create a new @ref VkvgFont from the provided font file, face index and size. + * @param dev VkvgDevice holding the font cache. + * @param fontFilePath a valid font file path. + * @param faceIndex Usually 0, except for variable fonts or font file with multiple faces. + * @param pointSize The font size in point unit, 1/72 of an inch. + * @return + */ vkvg_public VkvgFont vkvg_font_create_from_file (VkvgDevice dev, const char* fontFilePath, int faceIndex, float pointSize); -/* +/** * @brief Get underlying font face object pointer, if freetype is enable, the FT_Face will be returned. + * @param font a valid VkvgFont. + * @return the underlying native face object. */ vkvg_public void* vkvg_font_get_face (VkvgFont font); +/** + * @brief Increment reference counter of font + * @param font a valid VkvgFont. + * @return + */ vkvg_public VkvgFont vkvg_font_reference (VkvgFont font); +/** + * @brief Query current reference count from font. + * @param font a valid VkvgFont. + * @return The current reference count of the font. + */ vkvg_public uint32_t vkvg_font_get_reference_count (VkvgFont font); +/** + * @brief Query the current status of the font, VKVG_STATUS_SUCCESS means no error. + * @param font a valid VkvgFont. + * @return the current @ref vkvg_status_t of the font. + */ vkvg_public vkvg_status_t vkvg_font_status (VkvgFont font); +/** + * @brief Release resources associated with that font. + * This will release cached glyph entries, but not the native buffer of face if other fonts + * have been created with them. On device destroy, all remaining font resources will be released. + * @param font a valid VkvgFont. + */ vkvg_public void vkvg_font_destroy (VkvgFont font); #if VKVG_DBG_STATS /** @@ -1711,6 +1735,15 @@ vkvg_public void vkvg_set_text_direction(VkvgContext ctx, vkvg_direction_t direc */ vkvg_public void vkvg_set_font (VkvgContext ctx, VkvgFont font); /** + * @brief Get active font for provided context. + * + * Returned font will have its reference count increased by one. Once done with it, + * @ref vkvg_font_destroy must be called. + * @param ctx a valid vkvg @ref context + * @return the currently active font of the context. + */ +vkvg_public VkvgFont vkvg_get_font (VkvgContext ctx); +/** * @brief Show a string of text. * * Show the provided null-terminated utf8 string of text a the current position using the @@ -1723,23 +1756,26 @@ vkvg_public void vkvg_show_text(VkvgContext ctx, const char* utf8); /** * @brief Gets the extents for a string of text. * + * Gets the extents for a string of text as drawn with the active font of the provided context. * @param ctx a valid vkvg @ref context * @param utf8 A null-terminated utf8 encoded string of text. * @param extents */ vkvg_public void vkvg_text_extents(VkvgContext ctx, const char* utf8, vkvg_text_extents_t* extents); /** - * @brief Gets the font extents for the currently selected font. + * @brief Gets the font extents. * + * Gets the font extents for the currently active font for the provided context. * @param ctx a valid vkvg @ref context * @param extents A valid #vkvg_font_extents_t handle to be filled with current font extents. */ vkvg_public void vkvg_font_extents(VkvgContext ctx, vkvg_font_extents_t* extents); -// text run holds harfbuz datas, and prevent recreating them multiple times for the same line of text. +// /** * @brief Create a new text run. * + * Text run holds harfbuz datas, and prevent recreating them multiple times for the same line of text. * @param ctx a valid vkvg @ref context * @param text Null terminated utf8 string. * @return VkvgText diff --git a/src/vkvg_context.c b/src/vkvg_context.c index 129972e..79dcd53 100644 --- a/src/vkvg_context.c +++ b/src/vkvg_context.c @@ -1184,6 +1184,11 @@ void vkvg_set_font (VkvgContext ctx, VkvgFont font) { ctx->currentFont = font; vkvg_font_reference (font); } +VkvgFont vkvg_get_font (VkvgContext ctx) { + if (vkvg_status(ctx)) + return NULL; + return vkvg_font_reference(ctx->currentFont); +} void vkvg_select_font_face(VkvgContext ctx, const char* name) { if (vkvg_status(ctx)) return; @@ -1248,18 +1253,18 @@ VkvgText vkvg_text_run_create(VkvgContext ctx, const char* text) { if (vkvg_status(ctx)) return NULL; VkvgText tr = (vkvg_text_run_t*)calloc(1, sizeof(vkvg_text_run_t)); - text_run_init(ctx, text, -1, tr); + text_run_init(ctx->currentFont, ctx->textDirection, text, -1, tr); return tr; } VkvgText vkvg_text_run_create_with_length(VkvgContext ctx, const char* text, uint32_t length) { if (vkvg_status(ctx)) return NULL; VkvgText tr = (vkvg_text_run_t*)calloc(1, sizeof(vkvg_text_run_t)); - text_run_init(ctx, text, length, tr); + text_run_init(ctx->currentFont, ctx->textDirection, text, length, tr); return tr; } uint32_t vkvg_text_run_get_glyph_count(VkvgText textRun) { return textRun->glyph_count; } -void vkvg_text_run_get_glyph_position(VkvgText textRun, uint32_t index, vkvg_glyph_info_t* pGlyphInfo) { +void vkvg_text_run_get_glyph_position(VkvgText textRun, uint32_t index, vkvg_glyph_info_t* pGlyphInfo) { if (index >= textRun->glyph_count) { *pGlyphInfo = (vkvg_glyph_info_t){0}; return; @@ -1287,9 +1292,9 @@ void vkvg_text_extents(VkvgContext ctx, const char* text, vkvg_text_extents_t* e _font_cache_text_extents(ctx, text, -1, extents); } void vkvg_font_extents(VkvgContext ctx, vkvg_font_extents_t* extents) { - if (vkvg_status(ctx)) + if (vkvg_status(ctx) || vkvg_font_status(ctx->currentFont)) return; - _font_cache_font_extents(ctx, extents); + _font_cache_font_extents(ctx->currentFont, extents); } void vkvg_save(VkvgContext ctx) { diff --git a/src/vkvg_fonts.c b/src/vkvg_fonts.c index f507fd2..9e6f2cf 100644 --- a/src/vkvg_fonts.c +++ b/src/vkvg_fonts.c @@ -10,9 +10,55 @@ #include <locale.h> #include <string.h> #include <wchar.h> -//#include <stdio.h> -#ifndef VKVG_USE_FREETYPE +#ifdef VKVG_USE_FREETYPE +#define CHECK_FACE_FLAG(flags, flag, buffer) \ +do { \ + if ((flags) & (flag)) { \ + if (buffer[0] != '\0') strcat(buffer, " | "); \ + strcat(buffer, #flag); \ + } \ +} while (0) + + /** + * Converts face_flags bitmask to a human-readable string. + * Make sure the output buffer is large enough (e.g., 512 bytes). + */ + void ft_face_flags_to_string(FT_Long face_flags, char* out_str, size_t max_len) { + if (!out_str || max_len == 0) return; + out_str[0] = '\0'; + + CHECK_FACE_FLAG(face_flags, FT_FACE_FLAG_SCALABLE, out_str); + CHECK_FACE_FLAG(face_flags, FT_FACE_FLAG_FIXED_SIZES, out_str); + CHECK_FACE_FLAG(face_flags, FT_FACE_FLAG_FIXED_WIDTH, out_str); + CHECK_FACE_FLAG(face_flags, FT_FACE_FLAG_SFNT, out_str); + CHECK_FACE_FLAG(face_flags, FT_FACE_FLAG_HORIZONTAL, out_str); + CHECK_FACE_FLAG(face_flags, FT_FACE_FLAG_VERTICAL, out_str); + CHECK_FACE_FLAG(face_flags, FT_FACE_FLAG_KERNING, out_str); + CHECK_FACE_FLAG(face_flags, FT_FACE_FLAG_MULTIPLE_MASTERS, out_str); + CHECK_FACE_FLAG(face_flags, FT_FACE_FLAG_GLYPH_NAMES, out_str); + CHECK_FACE_FLAG(face_flags, FT_FACE_FLAG_EXTERNAL_STREAM, out_str); + CHECK_FACE_FLAG(face_flags, FT_FACE_FLAG_HINTER, out_str); + CHECK_FACE_FLAG(face_flags, FT_FACE_FLAG_CID_KEYED, out_str); + CHECK_FACE_FLAG(face_flags, FT_FACE_FLAG_TRICKY, out_str); + CHECK_FACE_FLAG(face_flags, FT_FACE_FLAG_COLOR, out_str); + + if (out_str[0] == '\0') { + strncpy(out_str, "NONE", max_len); + } +} +#define FT_CHECK_RES(f) \ +{ \ + FT_Error res = (f); \ + if (res) { \ + char tmp[1024]; \ + ft_face_flags_to_string (face->face->face_flags, tmp, 1024); \ + LOGE("FreeType error is 0x%x in %s at line %d, flags: %s\n", res, __FILE__, __LINE__, tmp);\ + free(font); \ + return (VkvgFont)&_vkvg_status_null_pointer; \ + } \ +} +#else #define STBTT_assert(x) ((void)0) #define STB_TRUETYPE_IMPLEMENTATION #include "stb_truetype.h" @@ -648,11 +694,11 @@ hb_buffer_t* _get_hb_buffer(VkvgText textRun, const char* text, int length) { return buf; } #endif -void _font_lock_size (VkvgContext ctx, VkvgFont font) { +void _font_lock_size (VkvgFont font) { #ifdef VKVG_USE_FREETYPE FT_Face face = font->face->face; if (face->size != font->ftSize) { - if (ctx->dev->threadAware) + if (font->dev->threadAware) mtx_lock(&font->face->mutex); FT_CHECK_RESULT(FT_Activate_Size(font->ftSize)); #ifdef VKVG_USE_HARFBUZZ @@ -665,8 +711,7 @@ void _font_lock_size (VkvgContext ctx, VkvgFont font) { } // retrieve global font extends of context's current font as defined by FreeType -void _font_cache_font_extents(VkvgContext ctx, vkvg_font_extents_t* extents) { - VkvgFont font = ctx->currentFont; +void _font_cache_font_extents(VkvgFont font, vkvg_font_extents_t* extents) { #ifdef VKVG_USE_FREETYPE FT_Face face = font->face->face; /*_font_lock_size(ctx, font); @@ -704,7 +749,7 @@ void _font_cache_text_extents(VkvgContext ctx, const char* text, int length, vkv } vkvg_text_run_t tr = {0}; - text_run_init(ctx, text, length, &tr); + text_run_init(ctx->currentFont, ctx->textDirection, text, length, &tr); if (ctx->status) return; @@ -716,7 +761,7 @@ void _font_cache_text_extents(VkvgContext ctx, const char* text, int length, vkv // text is expected as utf8 encoded void _font_cache_show_text(VkvgContext ctx, const char* text) { vkvg_text_run_t tr = {0}; - text_run_init(ctx, text, -1, &tr); + text_run_init(ctx->currentFont, ctx->textDirection, text, -1, &tr); if (ctx->status) return; @@ -727,13 +772,12 @@ void _font_cache_show_text(VkvgContext ctx, const char* text) { } // if length is < 0, text must be null terminated, else it contains glyph count -void text_run_init(VkvgContext ctx, const char* text, int length, VkvgText textRun) { - textRun->dev = ctx->dev; - textRun->font = ctx->currentFont; - textRun->textDirection = ctx->textDirection; +void text_run_init(VkvgFont font, vkvg_direction_t textDirection, const char* text, int length, VkvgText textRun) { + textRun->font = font; + textRun->textDirection = textDirection; vkvg_font_reference (textRun->font); - _font_lock_size(ctx, textRun->font); + _font_lock_size(textRun->font); #ifdef VKVG_USE_HARFBUZZ textRun->hbBuf = _get_hb_buffer(textRun, text, length); @@ -767,7 +811,7 @@ void text_run_init(VkvgContext ctx, const char* text, int length, VkvgText textR #endif #ifdef VKVG_USE_FREETYPE - if (ctx->dev->threadAware) + if (font->dev->threadAware) mtx_unlock(&textRun->font->face->mutex); #endif @@ -816,8 +860,9 @@ void text_run_show_text(VkvgContext ctx, VkvgText tr) { LOCK_FONTCACHE(ctx->dev) + VkvgDevice dev = tr->font->dev; for (uint32_t i = 0; i < glyph_count; ++i) { - glyph_ref cr = get_cached_glyph(tr->dev, f, glyph_info[i].codepoint); + glyph_ref cr = get_cached_glyph(dev, f, glyph_info[i].codepoint); if (vkvg_unlikely (f->status)) break; @@ -888,55 +933,6 @@ void _show_texture(vkvg_context* ctx) { } #endif -#ifdef VKVG_USE_FREETYPE -#define CHECK_FACE_FLAG(flags, flag, buffer) \ -do { \ - if ((flags) & (flag)) { \ - if (buffer[0] != '\0') strcat(buffer, " | "); \ - strcat(buffer, #flag); \ - } \ -} while (0) - -/** - * Converts face_flags bitmask to a human-readable string. - * Make sure the output buffer is large enough (e.g., 512 bytes). - */ -void ft_face_flags_to_string(FT_Long face_flags, char* out_str, size_t max_len) { - if (!out_str || max_len == 0) return; - out_str[0] = '\0'; - - CHECK_FACE_FLAG(face_flags, FT_FACE_FLAG_SCALABLE, out_str); - CHECK_FACE_FLAG(face_flags, FT_FACE_FLAG_FIXED_SIZES, out_str); - CHECK_FACE_FLAG(face_flags, FT_FACE_FLAG_FIXED_WIDTH, out_str); - CHECK_FACE_FLAG(face_flags, FT_FACE_FLAG_SFNT, out_str); - CHECK_FACE_FLAG(face_flags, FT_FACE_FLAG_HORIZONTAL, out_str); - CHECK_FACE_FLAG(face_flags, FT_FACE_FLAG_VERTICAL, out_str); - CHECK_FACE_FLAG(face_flags, FT_FACE_FLAG_KERNING, out_str); - CHECK_FACE_FLAG(face_flags, FT_FACE_FLAG_MULTIPLE_MASTERS, out_str); - CHECK_FACE_FLAG(face_flags, FT_FACE_FLAG_GLYPH_NAMES, out_str); - CHECK_FACE_FLAG(face_flags, FT_FACE_FLAG_EXTERNAL_STREAM, out_str); - CHECK_FACE_FLAG(face_flags, FT_FACE_FLAG_HINTER, out_str); - CHECK_FACE_FLAG(face_flags, FT_FACE_FLAG_CID_KEYED, out_str); - CHECK_FACE_FLAG(face_flags, FT_FACE_FLAG_TRICKY, out_str); - CHECK_FACE_FLAG(face_flags, FT_FACE_FLAG_COLOR, out_str); - - if (out_str[0] == '\0') { - strncpy(out_str, "NONE", max_len); - } -} -#define FT_CHECK_RES(f) \ -{ \ - FT_Error res = (f); \ - if (res) { \ - char tmp[1024]; \ - ft_face_flags_to_string (face->face->face_flags, tmp, 1024); \ - LOGE("FreeType error is 0x%x in %s at line %d, flags: %s\n", res, __FILE__, __LINE__, tmp);\ - free(font); \ - return (VkvgFont)&_vkvg_status_null_pointer; \ - } \ -} -#endif - void* vkvg_font_get_face(VkvgFont font) { #ifdef VKVG_USE_FREETYPE return font->face->face; @@ -1029,7 +1025,6 @@ VkvgFont vkvg_font_create (VkvgDevice dev, const char* query, float pointSize) { VkvgFont vkvg_font_reference(VkvgFont font) { if (!vkvg_font_status(font)) atomic_fetch_add_explicit(&font->references, 1, memory_order_relaxed); - return font; } uint32_t vkvg_font_get_reference_count(VkvgFont font) { diff --git a/src/vkvg_fonts.h b/src/vkvg_fonts.h index ca21860..e898ec8 100644 --- a/src/vkvg_fonts.h +++ b/src/vkvg_fonts.h @@ -287,7 +287,6 @@ typedef struct { // Precompute everything necessary to measure and draw one line of text, usefull to draw the same text multiple times. typedef struct _vkvg_text_run_t { VkvgFont font; /* vkvg font structure pointer */ - VkvgDevice dev; /* vkvg device associated with this text run */ vkvg_direction_t textDirection; /* horizontal or vertical */ vkvg_text_extents_t extents; /* store computed text extends */ const char* text; /* utf8 char array of text*/ @@ -306,10 +305,10 @@ void _font_cache_destroy(VkvgDevice dev); bool _font_cache_load_font_file_in_memory(vkvg_font_face_t* fontId); void _font_cache_show_text(VkvgContext ctx, const char* text); void _font_cache_text_extents(VkvgContext ctx, const char* text, int length, vkvg_text_extents_t* extents); -void _font_cache_font_extents(VkvgContext ctx, vkvg_font_extents_t* extents); +void _font_cache_font_extents(VkvgFont font, vkvg_font_extents_t* extents); void _font_cache_update_context_descset(VkvgContext ctx); -void text_run_init(VkvgContext ctx, const char* text, int length, VkvgText textRun); +void text_run_init(VkvgFont font, vkvg_direction_t textDirection, const char* text, int length, VkvgText textRun); void text_run_term(VkvgText textRun); void text_run_show_text(VkvgContext ctx, VkvgText tr); // Draw text run #endif diff --git a/tests/font_tests/array.h b/tests/font_tests/array.h deleted file mode 100644 index 8ae05d3..0000000 --- a/tests/font_tests/array.h +++ /dev/null @@ -1,81 +0,0 @@ -#ifndef ARRAY_H_INCLUDED -#define ARRAY_H_INCLUDED - -#include <stdint.h> -#include <stdbool.h> -#include <stdlib.h> - -#ifndef ARRAY_ID -#define ARRAY_ID(x) (x) -#endif -#ifndef ARRAY_EXP_GROWTH -#define ARRAY_EXP_GROWTH 0 -#endif -#ifndef ARRAY_ELASTICITY -#define ARRAY_ELASTICITY 10 -#endif -#ifndef ARRAY_INIT -#define ARRAY_INIT 4 -#endif -#ifndef ARRAY_ELEMENT_TYPE -#define ARRAY_ELEMENT_TYPE void * -#endif - - - -typedef struct _array_t { - uint32_t count; - uint32_t size; - ARRAY_ELEMENT_TYPE *elements; -} array_t; - -struct _array_t * array_create(); -void array_destroy(struct _array_t *arr); -void array_clean(struct _array_t *arr); -static inline void array_add(struct _array_t *arr, ARRAY_ELEMENT_TYPE element); -static inline void array_add_unique(struct _array_t *arr, ARRAY_ELEMENT_TYPE element); - -#ifdef ARRAY_IMPLEMENTATION -array_t *array_create() { - array_t *arr = (array_t *)malloc(sizeof(array_t)); - arr->elements = (ARRAY_ELEMENT_TYPE *)malloc(ARRAY_INIT * sizeof(ARRAY_ELEMENT_TYPE)); - arr->count = 0; - arr->size = ARRAY_INIT; - return arr; -} - -void array_destroy(array_t *arr) { - free(arr->elements); - free(arr); -} -void _array_add(array_t *arr, ARRAY_ELEMENT_TYPE element, bool unique) { - if (unique) { - for(int i = 0; i < arr->count; ++i) { - if (ARRAY_ID(arr->elements[i]) == ARRAY_ID(element)) - return; - } - } - if (arr->size == arr->count) { - if (ARRAY_EXP_GROWTH) { - arr->size *= ARRAY_EXP_GROWTH; - } else { - arr->size += ARRAY_ELASTICITY; - } - arr->elements = (ARRAY_ELEMENT_TYPE *)realloc(arr->elements, (arr->size) * sizeof(ARRAY_ELEMENT_TYPE)); - } - // memcpy (&arr->elements[arr->count], &element, sizeof (ARRAY_ELEMENT_TYPE)); - arr->elements[arr->count] = element; - arr->count++; -} -static inline void array_add(array_t *arr, ARRAY_ELEMENT_TYPE element) { - _array_add(arr,element,false); -} -static inline void array_add_unique(array_t *arr, ARRAY_ELEMENT_TYPE element) { - _array_add(arr,element,true); -} - -void array_reset(array_t *arr) { - arr->count = 0; -} -#endif -#endif diff --git a/tests/font_tests/font_tests.c b/tests/font_tests/font_tests.c deleted file mode 100644 index 024328e..0000000 --- a/tests/font_tests/font_tests.c +++ /dev/null @@ -1,995 +0,0 @@ -#include <ft2build.h> -//#include "freetype/freetype.h" -#include FT_FREETYPE_H -#include FT_SFNT_NAMES_H -#include FT_TRUETYPE_TABLES_H -#include FT_TRUETYPE_IDS_H -#include FT_MULTIPLE_MASTERS_H - - -#include <fontconfig/fontconfig.h> - -/* - serif Liberation Serif, DejaVu Serif, Times - sans-serif Liberation Sans, DejaVu Sans, Arial - monospace Liberation Mono, DejaVu Sans Mono - cursive Comic Neue, URW Chancery - fantasy Impact, Western --------- - system-ui - emoji noto color emoji - math - fangsong traditional chinese -*/ -typedef enum { - - OldstyleSerifs = 0x0100, - NoClassification = 0x0100, - IBMRoundedLegibility = 0x0101, - Garalde = 0x0102, - Venetian = 0x0103, - ModifiedGaralde = 0x0104, - DutchModern = 0x0105, - Centaur = 0x0106, - NoStyle = 0x0107, - Contemporary = 0x0108, - TransitionalSerifs = 0x0200, - DirectLine = 0x0201, - ScriptStyle = 0x0202, - Neoclassical = 0x0203, - Modern = 0x0204, - Clarendon = 0x0205, - SlabSerif = 0x0206, - Typewriter = 0x0207, - ModernSerifs = 0x0300, - Italian = 0x0301, - ModernSerifsScriptStyle = 0x0302, - ClarendonSerifs = 0x0400, - ModernSerifsClarendon = 0x0401, - ModernClarendon = 0x0402, - TraditionalClarendon = 0x0403, - NewspaperClarendon = 0x0404, - StubSerif = 0x0405, - Monospaced = 0x0406, - FreeformSerifs = 0x0500, - FreeformSerifsModern = 0x0501, - Traditional = 0x0502, - SansSerif = 0x0700, - IBMNeogrotesqueGothic = 0x0701, - Humanist = 0x0702, - LowcontrastGrotesque = 0x0703, - HighcontrastGrotesque = 0x0704, - NeogrotesqueGothic = 0x0705, - ModifiedNeogrotesqueGothic = 0x0706, - GeometricGrotesque = 0x0707, - GeometricHumanist = 0x0708, - GrotesqueGothic = 0x0709, - Ornamentals = 0x0800, - - Engraved = 0x0801, - Novelty = 0x0802, - Retro = 0x0803, - Synthetic = 0x0804, - - Scripts = 0x0900, - Uncial = 0x0901, - BrushCalligraphy = 0x0902, - FormalCalligraphy = 0x0903, - MonolineCalligraphy = 0x0904, - TraditionalCalligraphy = 0x0905, - ContemporaryCalligraphy = 0x0906, - Cursive = 0x0907, - Calligraphic = 0x0908, - InlineOutline = 0x0909, - Symbolic = 0x0A00, - MixedSerif = 0x0A01, - OldstyleSerif = 0x0A02, - NeoclassicalSerif = 0x0A03, - ModernSerif = 0x0A04, - ClarendonSerif = 0x0A05, - SymbolicSansSerif = 0x0A07, - Ornamental = 0x0A08, - Script = 0x0A09, - Texture = 0x0A0A, -} font_class; - -/* -Class 0x01 — Oldstyle Serifs - 0x0100 (Subclass 0x00): No Classification - 0x0101 (Subclass 0x01): IBM Rounded Legibility - 0x0102 (Subclass 0x02): Garalde - 0x0103 (Subclass 0x03): Venetian - 0x0104 (Subclass 0x04): Modified Garalde - 0x0105 (Subclass 0x05): Dutch Modern - 0x0106 (Subclass 0x06): Centaur - 0x0107 (Subclass 0x07): No Style - 0x0108 (Subclass 0x08): Contemporary - -Class 0x02 — Transitional Serifs - 0x0200 (Subclass 0x00): No Classification - 0x0201 (Subclass 0x01): Direct Line - 0x0202 (Subclass 0x02): Script Style - 0x0203 (Subclass 0x03): Neo-classical - 0x0204 (Subclass 0x04): Modern - 0x0205 (Subclass 0x05): Clarendon - 0x0206 (Subclass 0x06): Slab Serif - 0x0207 (Subclass 0x07): Typewriter - -Class 0x03 — Modern Serifs - 0x0300 (Subclass 0x00): No Classification - 0x0301 (Subclass 0x01): Italian - 0x0302 (Subclass 0x02): Script Style - -Class 0x04 — Clarendon Serifs - 0x0400 (Subclass 0x00): No Classification - 0x0401 (Subclass 0x01): Clarendon - 0x0402 (Subclass 0x02): Modern Clarendon - 0x0403 (Subclass 0x03): Traditional Clarendon - 0x0404 (Subclass 0x04): Newspaper Clarendon - 0x0405 (Subclass 0x05): Stub Serif - 0x0406 (Subclass 0x06): Monospaced - -Class 0x05 — Freeform Serifs - 0x0500 (Subclass 0x00): No Classification - 0x0501 (Subclass 0x01): Modern - 0x0502 (Subclass 0x02): Traditional - -Class 0x07 — Sans Serif - 0x0700 (Subclass 0x00): No Classification - 0x0701 (Subclass 0x01): IBM Neo-grotesque Gothic - 0x0702 (Subclass 0x02): Humanist - 0x0703 (Subclass 0x03): Low-contrast Grotesque - 0x0704 (Subclass 0x04): High-contrast Grotesque - 0x0705 (Subclass 0x05): Neo-grotesque Gothic - 0x0706 (Subclass 0x06): Modified Neo-grotesque Gothic - 0x0707 (Subclass 0x07): Geometric Grotesque - 0x0708 (Subclass 0x08): Geometric Humanist - 0x0709 (Subclass 0x09): Grotesque Gothic - -Class 0x08 — Ornamentals - 0x0800 (Subclass 0x00): No Classification - 0x0801 (Subclass 0x01): Engraved - 0x0802 (Subclass 0x02): Novelty - 0x0803 (Subclass 0x03): Retro - 0x0804 (Subclass 0x04): Synthetic - -Class 0x09 — Scripts - 0x0900 (Subclass 0x00): No Classification - 0x0901 (Subclass 0x01): Uncial - 0x0902 (Subclass 0x02): Brush Calligraphy - 0x0903 (Subclass 0x03): Formal Calligraphy - 0x0904 (Subclass 0x04): Monoline Calligraphy - 0x0905 (Subclass 0x05): Traditional Calligraphy - 0x0906 (Subclass 0x06): Contemporary Calligraphy - 0x0907 (Subclass 0x07): Cursive - 0x0908 (Subclass 0x08): Calligraphic - 0x0909 (Subclass 0x09): Inline / Outline - -Class 0x0A — Symbolic - 0x0A00 (Subclass 0x00): No Classification - 0x0A01 (Subclass 0x01): Mixed Serif - 0x0A02 (Subclass 0x02): Oldstyle Serif - 0x0A03 (Subclass 0x03): Neo-classical Serif - 0x0A04 (Subclass 0x04): Modern Serif - 0x0A05 (Subclass 0x05): Clarendon Serif - 0x0A07 (Subclass 0x07): Sans Serif - 0x0A08 (Subclass 0x08): Ornamental - 0x0A09 (Subclass 0x09): Script - 0x0A0A (Subclass 0x0A): Texture -*/ - -#include <sys/types.h> -#include <sys/stat.h> -#include <dirent.h> -#include <stdlib.h> -#include <stdio.h> -#include <stddef.h> -#include <stdint.h> -#include <ctype.h> - -#include <stdarg.h> -#include <string.h> - -#define MAX_PATH_LENGTH 1024 -#define MAX_FAM_LENGTH 64 - -typedef struct { - uint32_t hash; - char string[MAX_FAM_LENGTH]; -} hashed_string_t; - -#define ARRAY_INIT 16 -#define ARRAY_EXP_GROWTH 2 -#define ARRAY_ELEMENT_TYPE hashed_string_t -#define ARRAY_ID(x) ((x).hash) - -#define ARRAY_IMPLEMENTATION -#include "array.h" - -static char* filename = NULL; -static char* directory = NULL; -struct dirent* dir = NULL; - -struct stat file_stat; - - -#define NORMAL_COLOR "\x1B[0m" -#define GREEN "\x1B[32m" -#define BLUE "\x1B[34m" - -static int svg_file_count; -static float maxScroll; - -/* not defined in c11, ok to define here only for sample */ -#ifndef DT_DIR -#define DT_DIR 4 -#endif - -/** - * @brief Computes the 64-bit FNV-1a hash of a ascii string lowering case. - * - * @param str Pointer to the start of the null terminated ascii string to hash. - * @return uint64_t The 64-bit hash value. - */ -static inline uint64_t fnv1a_64_str(const char *const restrict str) { - uint64_t hash = 0xcbf29ce484222325ULL; - const uint8_t *restrict data = (const uint8_t *)str; - - while (*data) { - uint8_t c = *data; - // Fast branchless lowercase for ASCII font names/paths - if (c >= 'A' && c <= 'Z') { - c |= 0x20; - } - hash ^= c; - hash *= 0x00000100000001B3ULL; - data++; - } - - return hash; -} -#define FNV_32_PRIME 0x01000193 -#define FNV_32_OFFSET 0x811C9DC5 - -static inline uint32_t fnv1a_32_str(const char *const restrict str) { - const uint8_t *restrict data = (const uint8_t *)str; - uint32_t hash = FNV_32_OFFSET; - while (*data) { - uint8_t c = *data; - // Fast branchless lowercase for ASCII font names/paths - if (c >= 'A' && c <= 'Z') { - c |= 0x20; - } - hash ^= c; - hash *= FNV_32_PRIME; - data++; - } - return hash; -} - -void convert_utf16be_to_ascii(const FT_Byte* src, FT_UInt src_len, char* dest, FT_UInt dest_max_len) { - FT_UInt d_idx = 0; - // Iterate 2 bytes at a time - for (FT_UInt s_idx = 0; s_idx < src_len && d_idx < (dest_max_len - 1); s_idx += 2) { - // In UTF-16BE, the second byte contains the ASCII value for basic English - FT_Byte high_byte = src[s_idx]; - FT_Byte low_byte = src[s_idx + 1]; - - if (high_byte == 0) { - dest[d_idx++] = (char)low_byte; // Standard English character - } else { - dest[d_idx++] = '?'; // Non-ASCII character fallback - } - } - dest[d_idx] = '\0'; // Properly null-terminate the string -} - - -void print_help_and_exit() { - printf("\nUsage: fonttest [options] [path]\n\n"); - exit(-1); -} -#define CHECK_FACE_FLAG(flags, flag, buffer) \ - do { \ - if ((flags) & (flag)) { \ - if (buffer[0] != '\0') strcat(buffer, " | "); \ - strcat(buffer, #flag); \ - } \ - } while (0) - -/** - * Converts face_flags bitmask to a human-readable string. - * Make sure the output buffer is large enough (e.g., 512 bytes). - */ -void ft_face_flags_to_string(FT_Long face_flags, char* out_str, size_t max_len) { - if (!out_str || max_len == 0) return; - out_str[0] = '\0'; - - CHECK_FACE_FLAG(face_flags, FT_FACE_FLAG_SCALABLE, out_str); - CHECK_FACE_FLAG(face_flags, FT_FACE_FLAG_FIXED_SIZES, out_str); - CHECK_FACE_FLAG(face_flags, FT_FACE_FLAG_FIXED_WIDTH, out_str); - CHECK_FACE_FLAG(face_flags, FT_FACE_FLAG_SFNT, out_str); - CHECK_FACE_FLAG(face_flags, FT_FACE_FLAG_HORIZONTAL, out_str); - CHECK_FACE_FLAG(face_flags, FT_FACE_FLAG_VERTICAL, out_str); - CHECK_FACE_FLAG(face_flags, FT_FACE_FLAG_KERNING, out_str); - CHECK_FACE_FLAG(face_flags, FT_FACE_FLAG_MULTIPLE_MASTERS, out_str); - CHECK_FACE_FLAG(face_flags, FT_FACE_FLAG_GLYPH_NAMES, out_str); - CHECK_FACE_FLAG(face_flags, FT_FACE_FLAG_EXTERNAL_STREAM, out_str); - CHECK_FACE_FLAG(face_flags, FT_FACE_FLAG_HINTER, out_str); - CHECK_FACE_FLAG(face_flags, FT_FACE_FLAG_CID_KEYED, out_str); - CHECK_FACE_FLAG(face_flags, FT_FACE_FLAG_TRICKY, out_str); - CHECK_FACE_FLAG(face_flags, FT_FACE_FLAG_COLOR, out_str); - - if (out_str[0] == '\0') { - strncpy(out_str, "NONE", max_len); - } -} - -// Define explicit variation axis tags using FreeType's macro wrapper -#define AXIS_TAG_WGHT FT_MAKE_TAG('w', 'g', 'h', 't') -#define AXIS_TAG_WDTH FT_MAKE_TAG('w', 'd', 't', 'h') -#define AXIS_TAG_SLNT FT_MAKE_TAG('s', 'l', 'n', 't') -#define AXIS_TAG_ITAL FT_MAKE_TAG('i', 't', 'a', 'l') - -typedef enum { - font_weight_unset = 0, - font_weight_thin = 100, - font_weight_extra_light = 200, - font_weight_light = 300, - font_weight_normal = 400, - font_weight_medium = 500, - font_weight_semi_bold = 600, - font_weight_bold = 700, - font_weight_extra_bold = 800, - font_weight_black = 900, -} font_weight; - -typedef enum { - font_width_unset = 0, - font_width_ultra_condensed = 1,//50% - font_width_extra_condensed = 2, - font_width_condensed = 3, - font_width_semi_condensed = 4, - font_width_normal = 5, - font_width_semi_expanded = 6, - font_width_expanded = 7, - font_width_extra_expanded = 8, - font_width_ultra_expanded = 9,//150% -} font_width; - -typedef enum { - font_slant_unset = 0, - font_slant_italic = 0b0000000000000001, - font_slant_regular = 0b0000000001000000, - font_slant_oblic = 0b0000001000000000 -} font_slant; - - -FT_Library library; - -typedef struct { - uint64_t pathHash; - uint32_t nameHash; - uint32_t styleHash; -} font_id_t; - -typedef struct { - const char *txtFontPattern; // font config textual representation of pattern - const char *family; - const char *style; - font_weight weight; - font_width width; - font_slant slant; - uint32_t flags; -} vkvg_font_create_info_t; - -font_id_t font_id_cache[7000]; -array_t* font_names; -array_t* font_sub_names; - -uint32_t fontIndex = 0; - - -const char* nameid_trimmed(const char* str) { - return str + 11; -} - - - -#define SFNT_NAME_VASE(value) \ -case value : \ - return nameid_trimmed(#value); - -const char* sfnt_nameid_to_string(int id) { - switch(id) { - SFNT_NAME_VASE(TT_NAME_ID_COPYRIGHT) - SFNT_NAME_VASE(TT_NAME_ID_FONT_FAMILY) - SFNT_NAME_VASE(TT_NAME_ID_FONT_SUBFAMILY) - SFNT_NAME_VASE(TT_NAME_ID_UNIQUE_ID) - SFNT_NAME_VASE(TT_NAME_ID_FULL_NAME) - SFNT_NAME_VASE(TT_NAME_ID_VERSION_STRING) - SFNT_NAME_VASE(TT_NAME_ID_PS_NAME) - SFNT_NAME_VASE(TT_NAME_ID_TRADEMARK) - SFNT_NAME_VASE(TT_NAME_ID_MANUFACTURER) - SFNT_NAME_VASE(TT_NAME_ID_DESIGNER) - SFNT_NAME_VASE(TT_NAME_ID_DESCRIPTION) - SFNT_NAME_VASE(TT_NAME_ID_VENDOR_URL) - SFNT_NAME_VASE(TT_NAME_ID_DESIGNER_URL) - SFNT_NAME_VASE(TT_NAME_ID_LICENSE) - SFNT_NAME_VASE(TT_NAME_ID_LICENSE_URL) - SFNT_NAME_VASE(TT_NAME_ID_TYPOGRAPHIC_FAMILY) - SFNT_NAME_VASE(TT_NAME_ID_TYPOGRAPHIC_SUBFAMILY) - SFNT_NAME_VASE(TT_NAME_ID_MAC_FULL_NAME) - SFNT_NAME_VASE(TT_NAME_ID_SAMPLE_TEXT) - SFNT_NAME_VASE(TT_NAME_ID_CID_FINDFONT_NAME) - SFNT_NAME_VASE(TT_NAME_ID_WWS_FAMILY) - SFNT_NAME_VASE(TT_NAME_ID_WWS_SUBFAMILY) - SFNT_NAME_VASE(TT_NAME_ID_LIGHT_BACKGROUND) - SFNT_NAME_VASE(TT_NAME_ID_DARK_BACKGROUND) - SFNT_NAME_VASE(TT_NAME_ID_VARIATIONS_PREFIX) - - } - return "\0"; -} - -static inline void strn_tolower_copy(char *restrict dest, const char *restrict src, size_t len) { - if (!dest || !src) return; - - for (size_t i = 0; i < len; ++i) { - // cast to unsigned char is required by standard to prevent undefined behavior with negative char values - dest[i] = (char)tolower((unsigned char)src[i]); - } - - dest[len] = '\0'; // Explicitly enforce null-termination -} -static inline bool sfnt_name_is_english(const FT_Short platform_id, const FT_Short language_id) { - return (platform_id == 3 && language_id == 0x0409) || - (platform_id == 0 && language_id == 0) || - (platform_id == 1 && language_id == 0); -} - -void process_font_file (const char* const fontDir, const char* fontFile) { - char path[MAX_PATH_LENGTH]; - sprintf(path, "%s/%s", fontDir, fontFile); - FT_Face face; - font_weight weight = font_weight_normal; - font_width width = font_width_normal; - font_slant slant = font_slant_regular; - FT_Int16 fntClass = 0; - - FT_Error error = FT_New_Face(library, path, 0, &face ); - if (error) { - fprintf(stdout, "error opening font: %s\n%s\n", path, FT_Error_String(error)); - return; - } - - printf("%-100s:", path); - printf("\tnum faces: %ld (%ld):\t%-40s%-20s#:%ld\n", face->num_faces, face->face_index, face->family_name, face->style_name,face->num_glyphs); - char flags[1024]; - ft_face_flags_to_string(face->face_flags, flags, 1024); - printf("\tFlags: %s\n", flags); - - if (face->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS) { - FT_MM_Var* mm_var; - FT_Get_MM_Var(face, &mm_var); - printf("\tAxis: %4d Named Instances: %4d\n", mm_var->num_axis, mm_var->num_namedstyles); - for (int i = 0; i < mm_var->num_namedstyles; ++i) { - printf("\t%4d %u\n", i, mm_var->namedstyle[i].strid); - } - //mm_var->axis[0].; - FT_Done_MM_Var(library, mm_var); - } - - TT_OS2* os2_table = (TT_OS2*)FT_Get_Sfnt_Table(face, FT_SFNT_OS2); - if (os2_table) { - // 1. Get the real numeric weight (e.g., 400, 700) - weight = os2_table->usWeightClass; - width = os2_table->usWidthClass; - slant = os2_table->fsSelection; - fntClass = os2_table->sFamilyClass; - } - - printf("\tClass %04x Weight: %4d width: %4d slant: %4d\n",fntClass, weight, width, slant); - - - FT_UInt sfntCount = FT_Get_Sfnt_Name_Count(face); - FT_SfntName name; - char stringAscii[1024]; - - hashed_string_t famName = {0}; - hashed_string_t subFamName = {0}; - hashed_string_t typoFamName = {0}; - hashed_string_t typoSubFamName = {0}; - - for (int i = 0; i < sfntCount; ++i) { - error = FT_Get_Sfnt_Name(face, i, &name); - if (error) { - fprintf(stdout, "error reading sfnt name #%d, %s\n", i, FT_Error_String(error)); - continue; - } - /*FT_SfntLangTag langTag = {0}; - error = FT_Get_Sfnt_LangTag(face, name.language_id, &langTag); - if (langTag.string_len < 1) { - continue; - } - convert_utf16be_to_ascii(langTag.string, langTag.string_len, langTagAscii, 64);*/ - if (name.string_len < 1 || !sfnt_name_is_english(name.platform_id, name.language_id)) - continue; - if (name.name_id == TT_NAME_ID_FONT_FAMILY || - name.name_id == TT_NAME_ID_FONT_SUBFAMILY || - name.name_id == TT_NAME_ID_FULL_NAME || - name.name_id == TT_NAME_ID_TYPOGRAPHIC_FAMILY || - name.name_id == TT_NAME_ID_TYPOGRAPHIC_SUBFAMILY || - //name.name_id == TT_NAME_ID_WWS_FAMILY || - //name.name_id == TT_NAME_ID_WWS_SUBFAMILY || - name.name_id == TT_NAME_ID_UNIQUE_ID) { - - /*fprintf(stdout, "sfntName(%1d:%3d:%4d:%6d): %-20s = ", name.platform_id, name.encoding_id, name.language_id, name.name_id, sfnt_nameid_to_string(name.name_id)); - if (name.platform_id == 3) { - convert_utf16be_to_ascii(name.string, name.string_len, stringAscii, 1024); - fprintf(stdout, "%s\n", stringAscii); - } else { - fprintf(stdout, "%.*s\n", name.string_len, name.string); - }*/ - - fflush(stdout); - } else - continue; - - if (name.platform_id == 3) { - convert_utf16be_to_ascii(name.string, name.string_len, stringAscii, 1024); - } else { - strncpy(stringAscii, (char*)name.string, name.string_len < 1023 ? name.string_len : 1023); - stringAscii[name.string_len < 1023 ? name.string_len : 1023] = '\0'; - } - - switch (name.name_id) { - case TT_NAME_ID_TYPOGRAPHIC_FAMILY: - if (typoFamName.hash) - continue; - strn_tolower_copy(typoFamName.string, stringAscii, strlen(stringAscii)); - typoFamName.hash = fnv1a_32_str(typoFamName.string); - break; - case TT_NAME_ID_TYPOGRAPHIC_SUBFAMILY: - if (typoSubFamName.hash) - continue; - strn_tolower_copy(typoSubFamName.string, stringAscii, strlen(stringAscii)); - typoSubFamName.hash = fnv1a_32_str(typoSubFamName.string); - break; - case TT_NAME_ID_FONT_FAMILY: - if (famName.hash) - continue; - strn_tolower_copy(famName.string, stringAscii, strlen(stringAscii)); - famName.hash = fnv1a_32_str(famName.string); - break; - case TT_NAME_ID_FONT_SUBFAMILY: - if (subFamName.hash) - continue; - strn_tolower_copy(subFamName.string, stringAscii, strlen(stringAscii)); - subFamName.hash = fnv1a_32_str(subFamName.string); - break; - default: - break; - } - - - - } - - fflush(stdout); - - uint64_t pathHash = fnv1a_64_str(path); - if (pathHash) { - font_id_cache[fontIndex] = (font_id_t) { pathHash, fnv1a_32_str(face->family_name), fnv1a_32_str(face->style_name)}; - fontIndex++; - } else { - fprintf(stdout, "Path hash = 0 for %s\n", path); - } - - if (typoFamName.hash) - array_add_unique(font_names, typoFamName); - else if (famName.hash) - array_add_unique(font_names, famName); - if (typoSubFamName.hash) - array_add_unique(font_sub_names, typoSubFamName); - else if (subFamName.hash) - array_add_unique(font_sub_names, subFamName); - - FT_Done_Face(face); -} -void parse_font_dir (const char* const dir_path) { - char path[MAX_PATH_LENGTH]; - DIR* dir = opendir(dir_path); - if (!dir) { - printf("error opening directory: %s\n", dir_path); - return; - } - rewinddir(dir); - - struct dirent* de; - while ((de = readdir(dir)) != NULL) { - if (de->d_type == DT_DIR) { - if (de->d_name[0] == '.') - continue; - sprintf(path, "%s/%s", dir_path, de->d_name); - parse_font_dir(path); - } else { - if (!strcasecmp(strrchr(de->d_name, '\0') - 4, ".otf") || - !strcasecmp(strrchr(de->d_name, '\0') - 4, ".ttf") || - !strcasecmp(strrchr(de->d_name, '\0') - 5, ".woff") || - !strcasecmp(strrchr(de->d_name, '\0') - 6, ".woff2")) { - - process_font_file(dir_path, de->d_name); - } - } - } - closedir(dir); -} - -void sort_fonts_by_path_hash(font_id_t *array, size_t n) { - if (n < 2) return; - - // Allocate a temporary buffer for the 16-byte structs - font_id_t *buffer = (font_id_t *)malloc(n * sizeof(font_id_t)); - if (!buffer) return; - - font_id_t *src = array; - font_id_t *dst = buffer; - - // 8 passes of Radix Sort (8 bits per pass = 64 bits total) - for (int pass = 0; pass < 8; pass++) { - size_t count[256] = {0}; // 256 buckets for an 8-bit chunk - int shift = pass * 8; - - // 1. Calculate histograms directly using the unsigned pathHash - for (size_t i = 0; i < n; i++) { - uint8_t byte = (src[i].pathHash >> shift) & 0xFF; - count[byte]++; - } - - // 2. Transform counts into exclusive prefix sums (offsets) - size_t total = 0; - for (int i = 0; i < 256; i++) { - size_t old_count = count[i]; - count[i] = total; - total += old_count; - } - - // 3. Scatter entire structs safely into the destination buffer - for (size_t i = 0; i < n; i++) { - uint8_t byte = (src[i].pathHash >> shift) & 0xFF; - dst[count[byte]++] = src[i]; // Copies the full 16 bytes cleanly - } - - // Ping-pong buffer pointers to avoid unnecessary copying - font_id_t *temp = src; - src = dst; - dst = temp; - } - - // If the final sorted data landed in the temporary buffer, copy it back - if (src != array) { - memcpy(array, buffer, n * sizeof(font_id_t)); - } - - free(buffer); -} -void radix_sort_structs(font_id_t *array, size_t n) { - if (n < 2) return; - - // Allocate pointer arrays for the radix passes - font_id_t **src = (font_id_t **)malloc(n * sizeof(font_id_t *)); - font_id_t **dst = (font_id_t **)malloc(n * sizeof(font_id_t *)); - if (!src || !dst) { free(src); free(dst); return; } - - // Initialize the source pointer array - for (size_t i = 0; i < n; i++) { - src[i] = &array[i]; - } - - // 8 passes of Radix Sort (8 bits per pass) - for (int pass = 0; pass < 8; pass++) { - size_t count[256] = {0}; - int shift = pass * 8; - - // 1. Histogram (Extract the byte from the 64-bit ID) - // For signed int64_t, we flip the sign bit on the final pass (pass 7) - for (size_t i = 0; i < n; i++) { - uint64_t key = (uint64_t)src[i]->pathHash; - if (pass == 7) key ^= 0x8000000000000000ULL; // Handle signed values - uint8_t byte = (key >> shift) & 0xFF; - count[byte]++; - } - - // 2. Prefix Sums - size_t total = 0; - for (int i = 0; i < 256; i++) { - size_t old_count = count[i]; - count[i] = total; - total += old_count; - } - - // 3. Scatter pointers - for (size_t i = 0; i < n; i++) { - uint64_t key = (uint64_t)src[i]->pathHash; - if (pass == 7) key ^= 0x8000000000000000ULL; - uint8_t byte = (key >> shift) & 0xFF; - dst[count[byte]++] = src[i]; - } - - // Ping-pong pointer swap - font_id_t **temp = src; - src = dst; - dst = temp; - } - - // Reorder the original array in-place using cycle-walking (O(N) time, O(1) extra space) - // 'src' now holds the correct final pointers in sorted order. - for (size_t i = 0; i < n; i++) { - if (src[i] != &array[i]) { - font_id_t temp_struct = array[i]; - size_t j = i; - while (1) { - // Find where the element currently at array[j] belongs - size_t k = (src[j] - array); - if (k == i) { - array[j] = temp_struct; - src[j] = &array[j]; - break; - } - array[j] = array[k]; - src[j] = &array[j]; - j = k; - } - } - } - - free(src); - free(dst); -} - -void font_config_tests () { - if (!FcInit()) { - fprintf(stderr, "Fatal Error: Failed to initialize Fontconfig.\n"); - return; - } - - // Load the system configuration and default user fonts - FcConfig* config = FcConfigGetCurrent(); - - // 2. Create an empty pattern filter (empty means "Match everything") - FcPattern* blank_pattern = FcPatternCreate(); - - // 3. Declare exactly what metadata elements you want to extract - // Passing trailing 0/NULL tells the builder array arguments are complete - FcObjectSet* requested_elements = FcObjectSetBuild(FC_FAMILY, FC_STYLE, FC_FILE, (char *)0); - - // 4. Generate the sorted unique array list of matching fonts - FcFontSet* font_database = FcFontList(config, blank_pattern, requested_elements); - - if (font_database) { - printf("Successfully indexed %d font variants.\n\n", font_database->nfont); - - for (int i = 0; i < font_database->nfont; i++) { - FcPattern* font_entry = font_database->fonts[i]; - - FcChar8* family_name = NULL; - FcChar8* style_variant = NULL; - FcChar8* file_path = NULL; - - // Extract the strings cleanly out of the font structure - if (FcPatternGetString(font_entry, FC_FAMILY, 0, &family_name) == FcResultMatch && - FcPatternGetString(font_entry, FC_STYLE, 0, &style_variant) == FcResultMatch) { - - // Optional: Also fetch the file system location if you need to open it in FreeType - FcPatternGetString(font_entry, FC_FILE, 0, &file_path); - - printf("[%d] Family: %s \n Style: %s\n Path: %s\n\n", - i + 1, family_name, style_variant, file_path ? (char*)file_path : "Unknown"); - } - } - - // 5. Always free memory assets to prevent memory leaks - FcFontSetDestroy(font_database); - } - - FcObjectSetDestroy(requested_elements); - FcPatternDestroy(blank_pattern); - FcFini(); // Clean up environment space -} - -void font_config_sandboxing() { - setenv("FONTCONFIG_SYSROOT", "/tmp", 1); - - // 1. Create a brand new, completely blank configuration instance - FcConfig* local_config = FcConfigCreate(); - if (!local_config) { - fprintf(stderr, "Failed to create custom Fontconfig configuration.\n"); - return; - } - - // 2. Define your local asset path - const FcChar8* font_dir = (const FcChar8*)"/usr/local/share/fonts"; - - // 3. Force Fontconfig to scan this directory and add it to the local config instance - // This parses the font binaries in this directory into memory. - if (!FcConfigAppFontAddDir(local_config, font_dir)) { - fprintf(stderr, "Failed to register application font directory: %s\n", font_dir); - FcConfigDestroy(local_config); - return; - } - - // 4. (Optional but Recommended) Bind this config as the active thread configuration. - // This ensures subsequent generic Fc calls use your lightweight sandbox. - FcConfigSetCurrent(local_config); - - printf("Fontconfig sandboxed successfully. Scanning only: %s\n\n", font_dir); - - // ========================================================================= - // Example: Let's query our sandbox for a font to prove it works - // ========================================================================= - - // Create a request pattern - FcPattern* request = FcPatternCreate(); - FcPatternAddString(request, FC_FAMILY, (const FcChar8*)"sans-serif"); - - // Apply substitutions using our custom config object instead of NULL (system default) - FcConfigSubstitute(local_config, request, FcMatchPattern); - FcDefaultSubstitute(request); - - FcResult result; - // Match against our clean, local configuration file list - FcPattern* matched_font = FcFontMatch(local_config, request, &result); - - if (matched_font) { - FcChar8* file_path = NULL; - FcChar8* family_name = NULL; - - if (FcPatternGetString(matched_font, FC_FILE, 0, &file_path) == FcResultMatch && - FcPatternGetString(matched_font, FC_FAMILY, 0, &family_name) == FcResultMatch) { - - printf("Successfully matched!\n"); - printf("Family: %s\n", family_name); - printf("File Path: %s\n", file_path); - } - FcPatternDestroy(matched_font); - } else { - printf("No matching font found in the local asset folder.\n"); - } - - // 5. Cleanup - FcPatternDestroy(request); - - // This unloads the memory assets allocated by FcConfigCreate - FcConfigDestroy(local_config); - - // Clean up global libraries state - FcFini(); -} - -/* requesting variable font - * -FcPattern* request = FcPatternCreate(); -FcPatternAddString(request, FC_FAMILY, (const FcChar8*)"Roboto Flex"); -FcPatternAddString(request, FC_FONT_VARIATIONS, (const FcChar8*)"wght=625,wdth=80"); -*/ -/* Fallback chain - // 1. Initialize Fontconfig library and load system configuration - FcConfig* config = FcInitLoadConfigAndFonts(); - if (!config) return 1; - - // 2. Create an empty pattern - FcPattern* pat = FcPatternCreate(); - - // 3. Programmatically define the fallback chain (Order: Highest -> Lowest) - // The last argument 'FcTrue' appends the font to the end of the list - FcPatternAddString(pat, FC_FAMILY, (const FcChar8*)"CustomWebFont", FcTrue); - FcPatternAddString(pat, FC_FAMILY, (const FcChar8*)"Liberation Sans", FcTrue); - FcPatternAddString(pat, FC_FAMILY, (const FcChar8*)"DejaVu Sans", FcTrue); - FcPatternAddString(pat, FC_FAMILY, (const FcChar8*)"sans-serif", FcTrue); // Generic system fallback - - // Add other attributes if needed (e.g., matching bold) - FcPatternAddInteger(pat, FC_WEIGHT, FC_WEIGHT_BOLD); - - // 4. Perform standard configuration-defined substitutions - // This hooks into system/user XML configurations (~/.config/fontconfig/fonts.conf) - // to fill in missing properties or alias maps. - FcConfigSubstitute(config, pat, -*/ - -void setup_variable_font(FcPattern* matched_font) { - // 1. Verify if the font handles open vector variables - FcBool is_variable = FcFalse; - if (FcPatternGetBool(matched_font, FC_VARIABLE, 0, &is_variable) == FcResultMatch && is_variable) { - printf("This matched font file is variable-capable!\n"); - } - - // 2. Extract specific axis configuration parameters requested by a pattern string - // This returns a string formatted exactly like CSS: "wght=525.0,wdth=85.5" - FcChar8* variations_string = NULL; - if (FcPatternGetString(matched_font, FC_FONT_VARIATIONS, 0, &variations_string) == FcResultMatch) { - printf("Requested layout coordinates: %s\n", variations_string); - - // 3. You can pass this string directly to your rendering subsystem. - // Inside FreeType, you convert this string into an array of FT_Fixed coordinates - // and apply it directly via: - // FT_Set_Var_Design_Coordinates(face, num_coords, coords); - } -} - -int main(int argc, char* argv[]) { - int i = 1; - char* output = NULL; - - while (i < argc) { - int argLen = strlen(argv[i]); - if (argv[i][0] == '-') { - - if (argLen < 2) - print_help_and_exit(); - - switch (argv[i][1]) { - case 'f': - if (argc < ++i + 1) - print_help_and_exit(); - filename = argv[i]; - break; - default: - print_help_and_exit(); - } - } else - directory = argv[i]; - i++; - } - if (!directory) - print_help_and_exit(); - - FT_Error error = FT_Init_FreeType( &library ); - if (error) { - perror("error initializing freetype library\n"); - exit(-1); - } - - //font_config_tests(); - font_config_sandboxing(); - - - - - //font_id_cache = array_create(); - -/* - font_names = array_create(); - font_sub_names = array_create(); - - if (directory) { - parse_font_dir(directory); - } - printf("Total font files: %d\n", fontIndex); - radix_sort_structs(font_id_cache, fontIndex); -*/ - - - /*for (int i = 0; i < fontIndex; ++i) { - printf("%lu %u %u\n", font_id_cache[i].pathHash, font_id_cache[i].nameHash, font_id_cache[i].styleHash); - } - - for (int i = 0; i < fontIndex - 1; ++i) { - if (font_id_cache[i].pathHash == font_id_cache[i+1].pathHash) - printf("Collision: %lu %u %u\n", font_id_cache[i].pathHash, font_id_cache[i].nameHash, font_id_cache[i].styleHash); - }*/ - /*printf("=== Font Names: %-5d ===\n", font_names->count); - for (int i = 0; i < font_names->count; ++i) { - printf("%s\n", font_names->elements[i].string); - } - printf("=============================\n"); - printf("=== Font Sub Names: %-5d ===\n", font_sub_names->count); - for (int i = 0; i < font_sub_names->count; ++i) { - printf("%s\n", font_sub_names->elements[i].string); - } - printf("==============================\n");*/ - - // array_destroy(font_names); - // array_destroy(font_sub_names); - -} diff --git a/tests/font_tests/format12.c b/tests/font_tests/format12.c deleted file mode 100644 index af5e717..0000000 --- a/tests/font_tests/format12.c +++ /dev/null @@ -1,154 +0,0 @@ -#include <stdio.h> -#include <stdlib.h> -#include <stdint.h> - -#include <ft2build.h> -#include FT_FREETYPE_H -#include FT_TRUETYPE_TABLES_H // Required for SFNT structure macros - -/* --- OpenType Binary Byte Order Swapping Helpers --- */ -static uint16_t read_u16(const uint8_t* p) { - return (p[0] << 8) | p[1]; -} - -static uint32_t read_u32(const uint8_t* p) { - return ((uint32_t)p[0] << 24) | ((uint32_t)p[1] << 16) | ((uint32_t)p[2] << 8) | p[3]; -} - -/* --- Format 12 Structural Group Definition --- */ -typedef struct { - uint32_t startCharCode; - uint32_t endCharCode; - uint32_t startGlyphID; -} Format12Group; - -void parse_raw_sfnt_cmap(FT_Face face) { - // 1. Ensure the loaded font is an SFNT-based format (TrueType/OpenType) - if (!FT_IS_SFNT(face)) { - printf("Error: Loaded font face is not an SFNT (TrueType/OpenType) font.\n"); - return; - } - - // 2. Query FreeType to check if the 'cmap' table exists and find its length - FT_ULong table_len = 0; - FT_Error err = FT_Load_Sfnt_Table(face, TTAG_cmap, 0, NULL, &table_len); - if (err || table_len == 0) { - printf("Error: Could not read 'cmap' table metrics.\n"); - return; - } - - // 3. Allocate a memory buffer and download the raw 'cmap' bytes - uint8_t* cmap_raw = (uint8_t*)malloc(table_len); - err = FT_Load_Sfnt_Table(face, TTAG_cmap, 0, cmap_raw, &table_len); - if (err) { - printf("Error: Failed downloading raw 'cmap' block.\n"); - free(cmap_raw); - return; - } - - printf("Successfully extracted raw 'cmap' table. Size: %lu bytes.\n", table_len); - - /* --- Parse the CMap Index Header --- */ - // Bytes 0-1: version (must be 0) - // Bytes 2-3: numSubtables (number of encoding tables) - uint16_t numSubtables = read_u16(cmap_raw + 2); - printf("Number of encoding subtables found: %d\n\n", numSubtables); - - uint32_t format12_offset = 0; - - /* --- Loop Through Subtables to Find Format 12 --- */ - // The subtable dictionary begins at byte 4. Each entry is 8 bytes. - for (uint16_t i = 0; i < numSubtables; i++) { - uint32_t entry_offset = 4 + (i * 8); - uint16_t platform_id = read_u16(cmap_raw + entry_offset); - uint16_t encoding_id = read_u16(cmap_raw + entry_offset + 2); - uint32_t sub_offset = read_u32(cmap_raw + entry_offset + 4); - - // Peek at the first 2 bytes of the subtable to check its Format ID - uint16_t format = read_u16(cmap_raw + sub_offset); - - printf(" Subtable [%d]: Platform %d, Encoding %d, Format %d -> Offset 0x%X\n", - i, platform_id, encoding_id, format, sub_offset); - - if (format == 12) { - format12_offset = sub_offset; - // Stop looping if we find a standard Unicode/Windows full-plane table - if (platform_id == 3 && encoding_id == 10) { - break; - } - } - } - - if (format12_offset == 0) { - printf("\nThis font does not contain a Format 12 subtable (likely BMP-only/Format 4).\n"); - free(cmap_raw); - return; - } - - /* --- Parse the Format 12 Subtable --- */ - printf("\n--- Parsing Format 12 Subtable at Offset 0x%X ---\n", format12_offset); - const uint8_t* f12_ptr = cmap_raw + format12_offset; - - // Header Structure for Format 12: - // uint16 format (12) -> bytes 0-1 - // uint16 reserved -> bytes 2-3 - // uint32 length -> bytes 4-7 - // uint32 language -> bytes 8-11 - // uint32 numGroups -> bytes 12-15 - uint32_t f12_length = read_u32(f12_ptr + 4); - uint32_t numGroups = read_u32(f12_ptr + 12); - - printf(" Subtable Byte Length: %u\n", f12_length); - printf(" Total Compressed Segmented Groups: %u\n\n", numGroups); - - // Group Array begins at byte 16. Each group is 12 bytes. - const uint8_t* groups_ptr = f12_ptr + 16; - - // Print out the structural boundaries mapped by the font designer - for (uint32_t g = 0; g < numGroups; g++) { - uint32_t g_offset = g * 12; - Format12Group group; - group.startCharCode = read_u32(groups_ptr + g_offset); - group.endCharCode = read_u32(groups_ptr + g_offset + 4); - group.startGlyphID = read_u32(groups_ptr + g_offset + 8); - - printf(" Group %-4u: Unicode [U+%06X to U+%06X] -> maps to starting Glyph ID: %u\n", - g, group.startCharCode, group.endCharCode, group.startGlyphID); - - // Safety break to prevent console flooding on giant fonts like Noto Sans - if (g >= 15) { - printf(" ... (%u more groups omitted) ...\n", numGroups - 16); - break; - } - } - - // Clean up local buffers - free(cmap_raw); -} - -int main(int argc, char** argv) { - if (argc < 2) { - printf("Usage: %s <path_to_font.ttf>\n", argv[0]); - return 1; - } - - FT_Library library; - if (FT_Init_FreeType(&library)) { - printf("Could not init FreeType.\n"); - return 1; - } - - FT_Face face; - if (FT_New_Face(library, argv[1], 0, &face)) { - printf("Could not load font file %s\n", argv[1]); - FT_Done_FreeType(library); - return 1; - } - - parse_raw_sfnt_cmap(face); - - FT_Done_Face(face); - FT_Done_FreeType(library); - return 0; -} - diff --git a/tests/fonts/allfont.c b/tests/fonts/allfont.c index e9f94a9..5278b37 100644 --- a/tests/fonts/allfont.c +++ b/tests/fonts/allfont.c @@ -17,6 +17,7 @@ float fontSize = 28.0f; int curFontStartIdx = 0; bool redraw = true; +bool onlyVariable = false; #define MAX_STRING_LEN 1024 @@ -73,9 +74,11 @@ static const LocaleSample script_samples[] = { bool try_get_sample_by_fc_lang(const char* fc_lang, char* str) { const char* sample = NULL; if (fc_lang) { - for (int i = 1; i < 37; i++) { - if (strncmp(fc_lang, script_samples[i].lang_code, strlen(script_samples[i].lang_code)) == 0) { + for (int i = 0; i < 37; i++) { + if (!strncmp(fc_lang, script_samples[i].lang_code, strlen(script_samples[i].lang_code)) && + strlen(script_samples[i].lang_code) == strlen(fc_lang)) { sample = script_samples[i].sample_text; + break; } } @@ -157,7 +160,7 @@ bool try_get_sample_string(FcPattern* font_entry, FT_Face face, char* str) { if (!lang) continue; if (try_get_sample_by_fc_lang((char*)lang, str)) - break; + printf("sample found for %s\n", lang); } FcStrListDone(lang_list); @@ -174,80 +177,93 @@ void draw(VkvgSurface surfFont) { VkvgContext ctx = vkvg_create(surfFont); vkvg_clear(ctx); - float penX = 10.f; + float penY = 50.f; char tmp[1024]; + VkvgFont mono = vkvg_font_create(device, "mono", 12); + for (int i = curFontStartIdx; i < font_database->nfont; i++) { FcPattern* font_entry = font_database->fonts[i]; FcChar8* family_name = NULL; FcChar8* style_variant = NULL; FcChar8* file_path = NULL; + FcBool variable = false; + float penX = 10.f; - if (FcPatternGetString(font_entry, FC_FAMILY, 0, &family_name) == FcResultMatch && - FcPatternGetString(font_entry, FC_STYLE, 0, &style_variant) == FcResultMatch) { + FcPatternGetString(font_entry, FC_FAMILY, 0, &family_name); + FcPatternGetString(font_entry, FC_STYLE, 0, &style_variant); + FcPatternGetBool(font_entry, FC_VARIABLE, 0, &variable); + FcPatternGetString(font_entry, FC_FILE, 0, &file_path); + sprintf(tmp, "%s", family_name); - FcPatternGetString(font_entry, FC_FILE, 0, &file_path); - sprintf(tmp, "%s", family_name); + if (onlyVariable && !variable) + continue; + VkvgFont font = vkvg_font_create (device, tmp, fontSize); + if (vkvg_font_status(font)) { + printf("Error trying to load %s, from %s\n", tmp, file_path); + fflush(stdout); + continue; + } + sprintf(tmp, "%s : %s", family_name, style_variant); + vkvg_set_font (ctx, mono); + vkvg_move_to (ctx, penX, penY); + vkvg_set_source_rgb(ctx,0.8f,0.8f,0.8f); + vkvg_show_text(ctx, tmp); + penX += 350; - VkvgFont font = vkvg_font_create (device, tmp, fontSize); - if (vkvg_font_status(font)) { - printf("Error trying to load %s, from %s\n", tmp, file_path); - fflush(stdout); - continue; - } - vkvg_set_font (ctx, font); + vkvg_set_font (ctx, font); #ifdef VKVG_USE_FREETYPE - FT_Face face = (FT_Face)vkvg_font_get_face(font); - if (!try_get_sample_string(font_entry, face, tmp)) - sprintf(tmp, "%s : %s", family_name, style_variant); -#else + FT_Face face = (FT_Face)vkvg_font_get_face(font); + if (!try_get_sample_string(font_entry, face, tmp)) sprintf(tmp, "%s : %s", family_name, style_variant); +#else + sprintf(tmp, "%s : %s", family_name, style_variant); #endif - vkvg_font_extents_t fe = {0}; - vkvg_font_extents(ctx, &fe); - VkvgText tr = vkvg_text_run_create(ctx, tmp); - vkvg_text_extents_t extents = {0}; - vkvg_text_run_get_extents(tr, &extents); - vkvg_rectangle(ctx, penX, penY - fe.ascent, extents.width, extents.height); - vkvg_set_line_width(ctx, 1); - vkvg_set_source_rgba(ctx,0.5f,0.5f,1, 0.7f); - vkvg_stroke(ctx); + vkvg_font_extents_t fe = {0}; + vkvg_font_extents(ctx, &fe); + VkvgText tr = vkvg_text_run_create(ctx, tmp); + vkvg_text_extents_t extents = {0}; + vkvg_text_run_get_extents(tr, &extents); + vkvg_rectangle(ctx, penX, penY - fe.ascent, extents.width, extents.height); + vkvg_set_line_width(ctx, 1); + vkvg_set_source_rgba(ctx,0.5f,0.5f,1, 0.7f); + vkvg_stroke(ctx); - vkvg_move_to (ctx, penX, penY); - vkvg_set_source_rgba(ctx,1,0,0,0.5f); - vkvg_arc(ctx, penX, penY, 3, 0, 2 * M_PI); - vkvg_fill(ctx); + vkvg_move_to (ctx, penX, penY); + vkvg_set_source_rgba(ctx,1,0,0,0.5f); + vkvg_arc(ctx, penX, penY, 3, 0, 2 * M_PI); + vkvg_fill(ctx); - vkvg_move_to (ctx, penX, penY); - vkvg_set_source_rgb(ctx,1,1,1); - vkvg_show_text_run(ctx,tr); + vkvg_move_to (ctx, penX, penY); + vkvg_set_source_rgb(ctx,1,1,1); + vkvg_show_text_run(ctx,tr); - vkvg_flush(ctx); + vkvg_flush(ctx); - penY += fontSize * 1.5f; + penY += fe.height; - if (vkvg_font_status(font)) { - printf("Font in error %s\n", file_path); - fflush(stdout); - //clear font->dev ref to have clean vulkan termination - vkvg_device_destroy(device); - continue; - } - - vkvg_font_destroy(font); - vkvg_text_run_destroy(tr); + if (vkvg_font_status(font)) { + printf("Font in error %s\n", file_path); + fflush(stdout); + //clear font->dev ref to have clean vulkan termination + vkvg_device_destroy(device); + continue; } + + vkvg_font_destroy(font); + vkvg_text_run_destroy(tr); + if (penY > test_height - 100) break; tmp[0] = '\0'; } - + vkvg_font_destroy(mono); vkvg_destroy(ctx); } #else @@ -272,6 +288,9 @@ static void key_callback(GLFWwindow* window, int key, int scancode, int action, if (fontSize > 5.f) fontSize--; break; + case GLFW_KEY_F4: + onlyVariable = !onlyVariable; + break; } redraw = true; } @@ -321,7 +340,7 @@ int main(int argc, char* argv[]) { vkh_app_get_inst(e->app), vkh_device_get_phy(e->dev), vkh_device_get_vkdev(e->dev), - e->gQFamIdx, 0}; + e->gQFamIdx, 0, false}; device = vkvg_device_create(&info); surf = vkvg_surface_create(device, test_width, test_height); @@ -337,9 +356,10 @@ int main(int argc, char* argv[]) { } FcConfig* config = FcConfigGetCurrent(); FcPattern* request = FcPatternCreate(); - FcObjectSet* requested_elements = FcObjectSetBuild(FC_FAMILY, FC_STYLE, FC_FILE, FC_LANG, (char *)0); - FcPatternAddBool(request, FC_SCALABLE, FcTrue); - FcPatternAddString(request, FC_FONTFORMAT, (const FcChar8 *)"TrueType"); + FcObjectSet* requested_elements = FcObjectSetBuild(FC_FAMILY, FC_STYLE, FC_FILE, FC_LANG, FC_VARIABLE, (char *)0); + //FcPatternAddBool(request, FC_VARIABLE, FcTrue); + //FcPatternAddBool(request, FC_SCALABLE, FcTrue); + //FcPatternAddString(request, FC_FONTFORMAT, (const FcChar8 *)"TrueType"); font_database = FcFontList(config, request, requested_elements); #endif diff --git a/tests/fonts/fontquery.c b/tests/fonts/fontquery.c index 5bfe10f..7f8e988 100644 --- a/tests/fonts/fontquery.c +++ b/tests/fonts/fontquery.c @@ -14,7 +14,7 @@ #include <string.h> -float fontSize = 28.0f; +float fontSize = 24.0f; int curFontStartIdx = 0; bool redraw = true; @@ -73,9 +73,11 @@ static const LocaleSample script_samples[] = { bool try_get_sample_by_fc_lang(const char* fc_lang, char* str) { const char* sample = NULL; if (fc_lang) { - for (int i = 1; i < 37; i++) { - if (strncmp(fc_lang, script_samples[i].lang_code, strlen(script_samples[i].lang_code)) == 0) { + for (int i = 0; i < 37; i++) { + if (!strncmp(fc_lang, script_samples[i].lang_code, strlen(script_samples[i].lang_code)) && + strlen(script_samples[i].lang_code) == strlen(fc_lang)) { sample = script_samples[i].sample_text; + printf("sample found for %s->%s\n", fc_lang, script_samples[i].lang_code); break; } } @@ -173,9 +175,43 @@ VkvgFont sans16 = NULL; char txtBuffer[MAX_TXT_LEN]; int txtBuffPtr = 0; bool vertical = false; +bool variable = false; float uiFontSize = 18.f; +void encode_utf8(uint64_t cp, char* buffer) { + // 1. Filter out invalid Unicode code points + if (cp > 0x10FFFF || (cp >= 0xD800 && cp <= 0xDFFF)) { + buffer[0] = '\0'; // Return an empty string for invalid code points + return; + } + + // 2. Encode based on the value range of the code point + if (cp <= 0x7F) { + // 1 byte sequence + buffer[0] = (char)cp; + buffer[1] = '\0'; + } else if (cp <= 0x7FF) { + // 2 byte sequence + buffer[0] = (char)(0xC0 | (cp >> 6)); + buffer[1] = (char)(0x80 | (cp & 0x3F)); + buffer[2] = '\0'; + } else if (cp <= 0xFFFF) { + // 3 byte sequence + buffer[0] = (char)(0xE0 | (cp >> 12)); + buffer[1] = (char)(0x80 | ((cp >> 6) & 0x3F)); + buffer[2] = (char)(0x80 | (cp & 0x3F)); + buffer[3] = '\0'; + } else { + // 4 byte sequence + buffer[0] = (char)(0xF0 | (cp >> 18)); + buffer[1] = (char)(0x80 | ((cp >> 12) & 0x3F)); + buffer[2] = (char)(0x80 | ((cp >> 6) & 0x3F)); + buffer[3] = (char)(0x80 | (cp & 0x3F)); + buffer[4] = '\0'; + } +} + void draw(VkvgSurface surfFont) { if (!font_database) return; @@ -203,7 +239,7 @@ void draw(VkvgSurface surfFont) { strcpy(txtBuffCopy,txtBuffer); FcPattern* request = FcPatternCreate(); - FcObjectSet* requested_elements = FcObjectSetBuild(FC_FAMILY, FC_STYLE, FC_FILE, FC_LANG, (char *)0); + FcObjectSet* requested_elements = FcObjectSetBuild(FC_FAMILY, FC_STYLE, FC_FILE, FC_LANG, FC_VARIABLE, (char *)0); char *style = strstr(txtBuffCopy, ":"); if (style) { @@ -216,22 +252,27 @@ void draw(VkvgSurface surfFont) { } } FcPatternAddString(request, FC_FAMILY, (const FcChar8 *)txtBuffCopy); - FcPatternAddBool(request, FC_SCALABLE, FcTrue); - FcPatternAddString(request, FC_FONTFORMAT, (const FcChar8 *)"TrueType"); + if (variable) + FcPatternAddBool(request, FC_VARIABLE, FcTrue); + //FcPatternAddBool(request, FC_SCALABLE, FcTrue); + //FcPatternAddString(request, FC_FONTFORMAT, (const FcChar8 *)"TrueType"); - //FcConfigSubstitute(config, request, FcMatchPattern); - //FcDefaultSubstitute(request); + FcConfigSubstitute(config, request, FcMatchPattern); + FcDefaultSubstitute(request); FcFontSet* fonts = FcFontList(config, request, requested_elements); FcChar8* family_name = NULL; FcChar8* style_variant = NULL; FcChar8* fontFile = NULL; + FcBool var = false; int index = 0; float penX = 10; float penY = 50; + + FcPattern* font = NULL; if (fonts && fonts->nfont == 1) { font = fonts->fonts[0]; @@ -240,6 +281,7 @@ void draw(VkvgSurface surfFont) { FcPatternGetInteger (font, FC_INDEX, 0, &index); FcPatternGetString (font, FC_FAMILY, 0, &family_name); FcPatternGetString (font, FC_STYLE, 0, &style_variant); + FcPatternGetBool(font, FC_VARIABLE, 0, &var); VkvgFont f = vkvg_font_create_from_file(device, (char*)fontFile, index, fontSize); if (vkvg_font_status(f)) { printf("Error trying to load %s, from %s\n", txtBuffCopy, fontFile); @@ -251,7 +293,7 @@ void draw(VkvgSurface surfFont) { vkvg_set_text_direction (ctx, VKVG_VERTICAL); penX = 200; - penY = 200; + penY = 100; sprintf(txtBuffCopy, "%s : %s", family_name, style_variant); vkvg_font_extents_t fe = {0}; @@ -273,6 +315,61 @@ void draw(VkvgSurface surfFont) { vkvg_set_source_rgb(ctx,1,1,1); vkvg_show_text_run(ctx,tr); + penX = 50; + penY = 150; + + FcLangSet* langset = NULL; + FcChar8* lang = NULL; + if (FcPatternGetLangSet(font, FC_LANG, 0, &langset) == FcResultMatch) { + FcStrSet *lang_set = FcLangSetGetLangs(langset); + if (lang_set) { + FcStrList *lang_list = FcStrListCreate(lang_set); + if (lang_list) { + while ((lang = FcStrListNext(lang_list))) { + if (!lang) + continue; + if (try_get_sample_by_fc_lang((char*)lang, tmp)) { + vkvg_move_to (ctx, penX, penY); + vkvg_show_text(ctx, tmp); + penY += fe.height; + } + } + } + FcStrListDone(lang_list); + } + FcStrSetDestroy(lang_set); + } + + /*int charPerLine = (test_width - 100) / fe.max_x_advance; + int visibleLines = (test_height - 150) / fe.height; + int totLines = (double)0xffffffff / (double)charPerLine; + + char buff[10]; + + int initI = curFontStartIdx * charPerLine; + + for (uint64_t i = initI; i < 0xffffffff; ++i) { + encode_utf8(i, buff); + vkvg_move_to (ctx, penX, penY); + vkvg_show_text(ctx, buff); + + penX += fe.max_x_advance; + if (penX > test_width - 50 - fe.max_x_advance) { + penY += fe.height; + penX = 50; + } + if (penY > test_height) + break; + } + + vkvg_rectangle(ctx, test_width - 50, 150, 20, test_height - 170); + vkvg_stroke(ctx); + + float lineH = (float)(test_height - 170) / (float)totLines; + + vkvg_rectangle(ctx, test_width - 48, 150 + lineH * (float)initI, 16, 2);//lineH * visibleLines); + vkvg_fill(ctx);*/ + vkvg_flush(ctx); vkvg_text_run_destroy(tr); vkvg_font_destroy(f); @@ -283,15 +380,19 @@ void draw(VkvgSurface surfFont) { FcPatternGetInteger (font, FC_INDEX, 0, &index); FcPatternGetString (font, FC_FAMILY, 0, &family_name); FcPatternGetString (font, FC_STYLE, 0, &style_variant); + FcPatternGetBool(font, FC_VARIABLE, 0, &var); + + if (variable && !var) + continue; if (family_name && strstr((char*)family_name, (char*)txtBuffCopy)) { if (!style || (style && style_variant && strstr((char*)style_variant, style))) { - sprintf(tmp, "%s:%s", family_name,style_variant); + sprintf(tmp, "%s:%s index=%d %s", family_name,style_variant, index, fontFile); vkvg_move_to(ctx, penX, penY); vkvg_show_text(ctx, tmp); penY+=fe.height; - if (penY > test_height) + if (penY > test_height - 20) break; } } @@ -332,6 +433,16 @@ static void key_callback(GLFWwindow* window, int key, int scancode, int action, } txtBuffer[txtBuffPtr]='\0'; break; + case GLFW_KEY_PAGE_DOWN: + curFontStartIdx += 0xff; + break; + case GLFW_KEY_PAGE_UP: + curFontStartIdx -= 0xff; + curFontStartIdx = MAX(curFontStartIdx, 0); + break; + case GLFW_KEY_F3: + variable = !variable; + break; case GLFW_KEY_F4: vertical = !vertical; break; @@ -348,7 +459,7 @@ static void mouse_move_callback(GLFWwindow* window, double x, double y) { static void scroll_callback(GLFWwindow* window, double x, double y) { #ifdef VKVG_USE_FONTCONFIG - const int step = 5; + const int step = 10; if (y > 0.f) { if (curFontStartIdx > 0) curFontStartIdx -= step; @@ -405,7 +516,7 @@ int main(int argc, char* argv[]) { } config = FcConfigGetCurrent(); FcPattern* request = FcPatternCreate(); - FcObjectSet* requested_elements = FcObjectSetBuild(FC_FAMILY, FC_STYLE, FC_FILE, FC_LANG, (char *)0); + FcObjectSet* requested_elements = FcObjectSetBuild(FC_FAMILY, FC_STYLE, FC_FILE, FC_LANG, FC_VARIABLE, (char *)0); FcPatternAddBool(request, FC_SCALABLE, FcTrue); FcPatternAddString(request, FC_FONTFORMAT, (const FcChar8 *)"TrueType"); diff --git a/tests/svg.c b/tests/vkvg-svg/svg.c index e271875..e271875 100644 --- a/tests/svg.c +++ b/tests/vkvg-svg/svg.c |