summaryrefslogtreecommitdiffstatsci
diff options
context:
space:
mode:
authorJean-Philippe Bruyère <jp_bruyere@hotmail.com>2026-10-12 17:05:37 +0200
committerJean-Philippe Bruyère <jp_bruyere@hotmail.com>2026-10-12 17:05:37 +0200
commit0f02ed39c8488deef8168abe5fbe2860b6999f24 (patch)
treea4fc6599883aa97cc74244a2cf37118b4c35e291
parent082fb08d50ca705440b14d8414ed07abcd232fa9 (diff)
doc, api refined, debug, wip
-rw-r--r--include/vkvg.h106
-rw-r--r--src/vkvg_context.c15
-rw-r--r--src/vkvg_fonts.c125
-rw-r--r--src/vkvg_fonts.h5
-rw-r--r--tests/font_tests/array.h81
-rw-r--r--tests/font_tests/font_tests.c995
-rw-r--r--tests/font_tests/format12.c154
-rw-r--r--tests/fonts/allfont.c124
-rw-r--r--tests/fonts/fontquery.c137
-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