Mercurial > repos > blastem
view nuklear_ui/font_android.c @ 2149:9209858b2f74
Fix a couple of bugs in the TOC file parser
author | Michael Pavone <pavone@retrodev.com> |
---|---|
date | Sat, 26 Mar 2022 23:05:08 -0700 |
parents | 78abbabfd58d |
children | 13abdc98379e |
line wrap: on
line source
#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; }