diff nuklear_ui/font_android.c @ 1839:78abbabfd58d

Get Android build working again and update for SDL 2.0.7 (last version to support older versions of Android)
author Michael Pavone <pavone@retrodev.com>
date Sun, 14 Apr 2019 23:37:11 -0700
parents
children 13abdc98379e
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nuklear_ui/font_android.c	Sun Apr 14 23:37:11 2019 -0700
@@ -0,0 +1,194 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <limits.h>
+#include "../util.h"
+#include "../paths.h"
+#include "sfnt.h"
+
+typedef enum {
+	STATE_DEFAULT,
+	STATE_DECL,
+	STATE_COMMENT,
+	STATE_TAG,
+	STATE_PRE_ATTRIB,
+	STATE_ATTRIB,
+	STATE_PRE_VALUE,
+	STATE_VALUE
+} parse_state;
+
+#define DEFAULT_WEIGHT 400
+
+char *default_font_path(void)
+{
+	//Would probably be better to call into Java for this, but this should do for now
+	FILE *f = fopen("/system/etc/fonts.xml", "rb");
+	if (!f) {
+		return NULL;
+	}
+	long size = file_size(f);
+	char *font_xml = malloc(size+1);
+	if (size != fread(font_xml, 1, size, f)) {
+		free(font_xml);
+		fclose(f);
+		return NULL;
+	}
+	fclose(f);
+	font_xml[size] = 0;
+	
+	char *last_tag = NULL, *last_attrib = NULL, *last_value = NULL;
+	uint8_t last_style_was_normal = 0;
+	char *capture_best = NULL;
+	char *best = NULL;
+	int best_weight_diff = INT_MAX;
+	int last_weight = INT_MAX;
+	parse_state state = STATE_DEFAULT;
+	for(char *cur = font_xml; *cur; ++cur) {
+		switch (state)
+		{
+		case STATE_DEFAULT:
+			if (*cur == '<' && cur[1]) {
+				cur++;
+				switch(*cur)
+				{
+				case '?':
+					state = STATE_DECL;
+					break;
+				case '!':
+					if (cur[1] == '-' && cur[2] == '-') {
+						state = STATE_COMMENT;
+						cur++;
+					} else {
+						debug_message("Invalid comment\n");
+						cur = font_xml + size - 1;
+					}
+					break;
+				default:
+					if (capture_best) {
+						cur[-1] = 0;
+						best = strip_ws(capture_best);
+						capture_best = NULL;
+						best_weight_diff = abs(last_weight - DEFAULT_WEIGHT);
+						debug_message("Found candidate %s with weight %d\n", best, last_weight);
+					}
+					state = STATE_TAG;
+					last_tag = cur;
+					last_attrib = NULL;
+					last_value = NULL;
+					last_weight = INT_MAX;
+					break;
+				}
+			}
+			break;
+		case STATE_DECL:
+			if (*cur == '?' && cur[1] == '>') {
+				cur++;
+				state = STATE_DEFAULT;
+			}
+			break;
+		case STATE_COMMENT:
+			if (*cur == '-' && cur[1] == '-' && cur[2] == '>') {
+				cur += 2;
+				state = STATE_DEFAULT;
+			}
+			break;
+		case STATE_TAG:
+			if (*cur == ' ' || *cur == '\t' || *cur == '\n' || *cur == '\r') {
+				*cur = 0;
+				state = STATE_PRE_ATTRIB;
+			} else if (*cur == '>') {
+				*cur = 0;
+				state = STATE_DEFAULT;
+			}
+			break;
+		case STATE_PRE_ATTRIB:
+			if (!(*cur == ' ' || *cur == '\t' || *cur == '\n' || *cur == '\r')) {
+				if (*cur == '>') {
+					state = STATE_DEFAULT;
+					if (last_style_was_normal && abs(last_weight - DEFAULT_WEIGHT) < best_weight_diff) {
+						capture_best = cur + 1;
+					} else if (best && !strcmp("/family", last_tag)) {
+						debug_message("found family close tag, stopping search\n");
+						cur = font_xml + size - 1;
+					}
+				} else {
+					last_attrib = cur;
+					state = STATE_ATTRIB;
+				}
+			}
+			break;
+		case STATE_ATTRIB:
+			if (*cur == '=') {
+				*cur = 0;
+				state = STATE_PRE_VALUE;
+			} else if (*cur == ' ' || *cur == '\t' || *cur == '\n' || *cur == '\r') {
+				*cur = 0;
+			}
+			break;
+		case STATE_PRE_VALUE:
+			if (*cur == '"') {
+				state = STATE_VALUE;
+				last_value = cur + 1;
+			}
+			break;
+		case STATE_VALUE:
+			if (*cur == '"') {
+				*cur = 0;
+				state = STATE_PRE_ATTRIB;
+				if (!strcmp("weight", last_attrib)) {
+					last_weight = atoi(last_value);
+				} else if (!strcmp("style", last_attrib)) {
+					last_style_was_normal = !strcmp("normal", last_value);
+				}
+			}
+			break;
+		}
+	}
+	if (best) {
+		best = path_append("/system/fonts", best);
+	}
+	free(font_xml);
+	return best;
+}
+
+static uint8_t *try_load_font(char *path, uint32_t *size_out)
+{
+	debug_message("Trying to load font %s\n", path);
+	FILE *f = fopen(path, "rb");
+	free(path);
+	if (!f) {
+		return NULL;
+	}
+	long size = file_size(f);
+	uint8_t *buffer = malloc(size);
+	if (size != fread(buffer, 1, size, f)) {
+		fclose(f);
+		return NULL;
+	}
+	fclose(f);
+	sfnt_container *sfnt = load_sfnt(buffer, size);
+	if (!sfnt) {
+		free(buffer);
+		return NULL;
+	}
+	return sfnt_flatten(sfnt->tables, size_out);
+}
+
+uint8_t *default_font(uint32_t *size_out)
+{
+	char *path = default_font_path();
+	if (!path) {
+		goto error;
+	}
+	uint8_t *ret = try_load_font(path, size_out);
+	if (ret) {
+		return ret;
+	}
+error:
+	//try some likely suspects if we failed to parse fonts.xml or failed to find the indicated font
+	ret = try_load_font("/system/fonts/Roboto-Regular.ttf", size_out);
+	if (!ret) {
+		ret = try_load_font("/system/fonts/DroidSans.ttf", size_out);
+	}
+	return ret;
+}