changeset 2480:369a52e302e2

Try multiple results from fc-match on Linux rather than assuming the first choice font will be suitable
author Michael Pavone <pavone@retrodev.com>
date Sat, 30 Mar 2024 14:26:48 -0700
parents 29baf8d5a579
children f0645adddf0d
files nuklear_ui/font.c nuklear_ui/sfnt.c nuklear_ui/sfnt.h
diffstat 3 files changed, 80 insertions(+), 25 deletions(-) [+]
line wrap: on
line diff
--- a/nuklear_ui/font.c	Fri Mar 08 23:14:08 2024 -0800
+++ b/nuklear_ui/font.c	Sat Mar 30 14:26:48 2024 -0700
@@ -5,21 +5,25 @@
 #include "../util.h"
 #include "sfnt.h"
 
-char *default_font_path(void)
+char **preferred_font_paths(uint32_t *num_out)
 {
+	char ** ret;
 #ifdef FONT_PATH
 	FILE *f = fopen(FONT_PATH, "rb");
 	if (f) {
 		fclose(f);
-		return strdup(FONT_PATH);
+		ret = calloc(1, sizeof(char*));
+		ret[0] = strdup(FONT_PATH);
+		*num_out = 1;
+		return ret;
 	}
 #endif
 	//TODO: specify language dynamically once BlastEm is localized
-	FILE *fc_pipe = popen("fc-match :lang=en -f '%{file}'", "r");
+	FILE *fc_pipe = popen("fc-match -s -f '%{file}\n' :lang=en", "r");
 	if (!fc_pipe) {
 		return NULL;
 	}
-	size_t buf_size = 128;
+	size_t buf_size = 4096;
 	char *buffer = NULL;
 	size_t total = 0, read = 0;
 	do {
@@ -33,35 +37,73 @@
 	} while (read == (buf_size - total));
 	total += read;
 	buffer[total] = 0;
-	
-	return buffer;
+	*num_out = 0;
+	for (size_t i = 0; i < total; i++)
+	{
+		if (buffer[i] == '\n') {
+			buffer[i] = 0;
+			if (i + 1 != total) {
+				(*num_out)++;
+			}
+		}
+	}
+	ret = calloc(*num_out, sizeof(char*));
+	size_t entry = 0;
+	ret[entry++] = buffer;
+	for (size_t i = 0; i < total - 1 && entry < *num_out; i++)
+	{
+		if (!buffer[i]) {
+			ret[entry++] = buffer + i + 1;
+		}
+	}
+	return ret;
 }
 
 uint8_t *default_font(uint32_t *size_out)
 {
-	char *path = default_font_path();
-	if (!path) {
-		goto error;
-	}
-	FILE *f = fopen(path, "rb");
-	free(path);
-	if (!f) {
+	uint8_t *ret = NULL;
+	uint32_t num_fonts;
+	char **paths = preferred_font_paths(&num_fonts);
+	if (!paths) {
 		goto error;
 	}
-	long size = file_size(f);
-	uint8_t *buffer = malloc(size);
-	if (size != fread(buffer, 1, size, f)) {
+	for (uint32_t i = 0; i < num_fonts && !ret; i++)
+	{
+		FILE *f = fopen(paths[i], "rb");
+		if (!f) {
+			fprintf(stderr, "Failed to open font file %s\n", paths[i]);
+			continue;
+		}
+		long size = file_size(f);
+		uint8_t *buffer = malloc(size);
+		if (size != fread(buffer, 1, size, f)) {
+			fprintf(stderr, "Failed to read font file %s\n", paths[i]);
+			fclose(f);
+			continue;
+		}
 		fclose(f);
-		goto error;
+		sfnt_container *sfnt = load_sfnt(buffer, size);
+		if (!sfnt) {
+			fprintf(stderr, "File %s does not contain SFNT resources\n", paths[i]);
+			free(buffer);
+			continue;
+		}
+		for (uint8_t j = 0; j < sfnt->num_fonts; j++)
+		{
+			if (sfnt_has_truetype_glyphs(sfnt->tables + j)) {
+				ret = sfnt_flatten(sfnt->tables + j, size_out);
+				sfnt = NULL;
+				break;
+			}
+			fprintf(stderr, "Font %s in file %s doesn't have TrueType glyphs\n", sfnt_name(sfnt->tables + j, SFNT_POSTSCRIPT), paths[i]);
+		}
+		if (sfnt) {
+			sfnt_free(sfnt);
+		}
 	}
-	fclose(f);
-	sfnt_container *sfnt = load_sfnt(buffer, size);
-	if (!sfnt) {
-		free(buffer);
-		goto error;
-	}
-	return sfnt_flatten(sfnt->tables, size_out);
+	free(paths[0]);
+	free(paths);
 error:
 	//TODO: try to find a suitable font in /usr/share/fonts as a fallback
-	return NULL;
+	return ret;
 }
\ No newline at end of file
--- a/nuklear_ui/sfnt.c	Fri Mar 08 23:14:08 2024 -0800
+++ b/nuklear_ui/sfnt.c	Sat Mar 30 14:26:48 2024 -0700
@@ -209,6 +209,18 @@
 	}
 }
 
+uint8_t sfnt_has_truetype_glyphs(sfnt_table *sfnt)
+{
+	uint32_t table_size;
+	uint8_t *table = sfnt_find_table(sfnt, "glyf", &table_size);
+	if (!table) {
+		return 0;
+	}
+	table = sfnt_find_table(sfnt, "loca", &table_size);
+	return table != NULL;
+	
+}
+
 uint8_t *sfnt_flatten(sfnt_table *sfnt, uint32_t *size_out)
 {
 	uint8_t *ret = NULL;;
--- a/nuklear_ui/sfnt.h	Fri Mar 08 23:14:08 2024 -0800
+++ b/nuklear_ui/sfnt.h	Sat Mar 30 14:26:48 2024 -0700
@@ -37,6 +37,7 @@
 
 sfnt_container *load_sfnt(uint8_t *buffer, uint32_t size);
 char *sfnt_name(sfnt_table *sfnt, uint16_t name_type);
+uint8_t sfnt_has_truetype_glyphs(sfnt_table *sfnt);
 uint8_t *sfnt_flatten(sfnt_table *sfnt, uint32_t *size_out);
 sfnt_table *sfnt_subfamily_by_names(sfnt_container *sfnt, const char **names);
 void sfnt_free(sfnt_container *sfnt);