From 6fd280bb5b27842a0ef2977798566c37bd4e1d0e Mon Sep 17 00:00:00 2001 From: Ashish Gupta Date: Wed, 5 Apr 2017 21:39:01 +0200 Subject: Add kolibrios/ dir : Step 1 towards porting this to Kolibri OS --- frontends/kolibrios/fb/convert_font.c | 1215 +++++++++++++++++++++++++++++++++ 1 file changed, 1215 insertions(+) create mode 100644 frontends/kolibrios/fb/convert_font.c (limited to 'frontends/kolibrios/fb/convert_font.c') diff --git a/frontends/kolibrios/fb/convert_font.c b/frontends/kolibrios/fb/convert_font.c new file mode 100644 index 000000000..010af857a --- /dev/null +++ b/frontends/kolibrios/fb/convert_font.c @@ -0,0 +1,1215 @@ +/* + * Copyright 2014 Michael Drake + * Copyright 2014 Vincent Sanders + * + * This file is part of the convert_font tool used to convert font + * glyph data into a compilable representation. + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define GLYPH_LEN 16 +#define BUCKETS 512 +#define CHUNK_SIZE (64 * 1024) +#define HEADER_MAX 2000 + +#define SECTION_SIZE (sizeof(uint16_t) * 256) + +const char *labels[4] = { + " Regular", + " Italic", + " Bold", + "Bold & Italic" +}; + +const char *var_lables[4] = { + "fb_regular", + "fb_italic", + "fb_bold", + "fb_bold_italic" +}; + +const char *short_labels[4] = { + " ", + " i", + "b ", + "bi" +}; + +enum font_style { + REGULAR = 0, + ITALIC = (1 << 0), + BOLD = (1 << 1), + ITALIC_BOLD = (1 << 2) +}; + +enum log_level { + LOG_DEBUG, + LOG_INFO, + LOG_RESULT, + LOG_WARNING, + LOG_ERROR +}; + +enum log_level level; + +typedef struct glyph_entry { + union { + uint32_t u32[GLYPH_LEN / 4]; + uint8_t u8[GLYPH_LEN]; + } data; + uint32_t index; + struct glyph_entry *next; +} glyph_entry; + +/** Scratch glyph for generated code points */ +uint8_t code_point[GLYPH_LEN]; + +/** Hash table */ +glyph_entry *ht[BUCKETS]; + +#define LOG(lev, fmt, ...) \ + if (lev >= level) \ + printf(fmt, ##__VA_ARGS__); + +/** + * Get hash for glyph data + * \param g Glyph data (GLYPH_LEN bytes) + * \return glyph's hash + */ +static inline uint32_t glyph_hash(const uint8_t *g) +{ + uint32_t hash = 0x811c9dc5; + unsigned int len = GLYPH_LEN; + + while (len > 0) { + hash *= 0x01000193; + hash ^= *g++; + len--; + } + + return hash; +} + + +/** + * Check whether glyphs are identical (compares glyph data) + * + * \param g1 First glyph's data (GLYPH_LEN bytes) + * \param g2 Second glyph's data (GLYPH_LEN bytes) + * \return true iff both glyphs are identical, else false + */ +static inline bool glyphs_match(const uint8_t *g1, const uint8_t *g2) +{ + return (memcmp(g1, g2, GLYPH_LEN) == 0); +} + + +/** + * Add a glyph to a hash chain (or free, and return pointer to existing glyph) + * + * Note that if new glyph already exists in chain, it is freed and a pointer to + * the existing glyph is returned. If the glyph does not exist in the chain + * it is added and its pointer is returned. + * + * \param head Head of hash chain + * \param new New glyph to add (may be freed) + * \return pointer to glyph in hash chain + */ +static glyph_entry * glyph_add_to_chain(glyph_entry **head, glyph_entry *new) +{ + glyph_entry *e = *head; + + if (*head == NULL) { + new->next = NULL; + *head = new; + return new; + } + + do { + if (glyphs_match(new->data.u8, e->data.u8)) { + free(new); + return e; + } + if (e->next == NULL) + break; + e = e->next; + } while (1); + + new->next = e->next; + e->next = new; + return new; +} + + +/** + * Free a glyph entry chain + * + * \param head Head of hash chain + */ +static void free_chain(glyph_entry *head) +{ + glyph_entry *e = head; + + if (head == NULL) + return; + + while (e != NULL) { + head = e->next; + free(e); + e = head; + }; +} + + +/** + * Add new glyph to hash table (or free, and return pointer to existing glyph) + * + * Note that if new glyph already exists in table, it is freed and a pointer to + * the existing glyph is returned. If the glyph does not exist in the table + * it is added and its pointer is returned. + * + * \param new New glyph to add (may be freed) + * \return pointer to glyph in hash table + */ +static glyph_entry * glyph_add_to_table(glyph_entry *new) +{ + uint32_t hash = glyph_hash(new->data.u8); + + return glyph_add_to_chain(&ht[hash % BUCKETS], new); +} + + +/** + * Free glyph table. + */ +static void free_table(void) +{ + int i; + + for (i = 0; i < BUCKETS; i++) { + free_chain(ht[i]); + } +} + +struct parse_context { + enum { + START, + IN_HEADER, + BEFORE_ID, + GLYPH_ID, + BEFORE_GLYPH_DATA, + IN_GLYPH_DATA + } state; /**< Current parser state */ + + union { + struct { + bool new_line; + } in_header; + struct { + bool new_line; + bool u; + } before_id; + struct { + int c; + } g_id; + struct { + bool new_line; + bool prev_h; + bool prev_s; + int c; + } before_gd; + struct { + int line; + int pos; + int styles; + int line_styles; + glyph_entry *e[4]; + } in_gd; + } data; /**< The state specific data */ + + int id; /**< Current ID */ + + int codepoints; /**< Glyphs containing codepoints */ + int count[4]; /**< Count of glyphs in file */ +}; + +struct font_data { + char header[HEADER_MAX]; + int header_len; + + uint8_t section_table[4][256]; + uint8_t sec_count[4]; + uint16_t *sections[4]; + + glyph_entry *e[0xffff]; + int glyphs; +}; + +bool generate_font_header(const char *path, struct font_data *data) +{ + FILE *fp; + int s; + + fp = fopen(path, "wb"); + if (fp == NULL) { + LOG(LOG_ERROR, "Couldn't open header file \"%s\"\n", path); + return false; + } + + fprintf(fp, "/*\n"); + fwrite(data->header, 1, data->header_len, fp); + fprintf(fp, " */\n\n"); + fprintf(fp, "/* Don't edit this file, it was generated from the " + "plain text source data. */\n\n"); + + + for (s = 0; s < 4; s++) { + fprintf(fp, "const uint8_t *%s_section_table;\n", + var_lables[s]); + fprintf(fp, "const uint16_t *%s_sections;\n", + var_lables[s]); + + } + + fprintf(fp, "const uint8_t *font_glyph_data;\n"); + + fprintf(fp, "\n\n"); + + fclose(fp); + + return true; + +} + +bool generate_font_source(const char *path, struct font_data *data) +{ + int s, i, y; + int limit; + FILE *fp; + + fp = fopen(path, "wb"); + if (fp == NULL) { + LOG(LOG_ERROR, "Couldn't open output file \"%s\"\n", path); + return false; + } + + fprintf(fp, "/*\n"); + fwrite(data->header, 1, data->header_len, fp); + fprintf(fp, " */\n\n"); + fprintf(fp, "/* Don't edit this file, it was generated from the " + "plain text source data. */\n\n"); + + fprintf(fp, "#include \n"); + fprintf(fp, "\n"); + + for (s = 0; s < 4; s++) { + + fprintf(fp, "static const uint8_t %s_section_table_c[256] = {\n", + var_lables[s]); + + for (i = 0; i < 256; i++) { + if (i == 255) + fprintf(fp, "0x%.2X\n", + data->section_table[s][i]); + else if (i % 8 == 7) + fprintf(fp, "0x%.2X,\n", + data->section_table[s][i]); + else if (i % 8 == 0) + fprintf(fp, "\t0x%.2X, ", + data->section_table[s][i]); + else + fprintf(fp, "0x%.2X, ", + data->section_table[s][i]); + } + + fprintf(fp, "};\nconst uint8_t *%s_section_table = &%s_section_table_c[0];\n\n", + var_lables[s], var_lables[s]); + fprintf(fp, "static const uint16_t %s_sections_c[%i] = {\n", + var_lables[s], data->sec_count[s] * 256); + + limit = data->sec_count[s] * 256; + for (i = 0; i < limit; i++) { + uint16_t offset = data->sections[s][i]; + if (i == limit - 1) + fprintf(fp, "0x%.4X\n", offset); + else if (i % 4 == 3) + fprintf(fp, "0x%.4X,\n", offset); + else if (i % 4 == 0) + fprintf(fp, "\t0x%.4X, ", offset); + else + fprintf(fp, "0x%.4X, ", offset); + } + + fprintf(fp, "};\nconst uint16_t *%s_sections = &%s_sections_c[0];\n\n", var_lables[s], var_lables[s]); + } + + fprintf(fp, "static const uint8_t font_glyph_data_c[%i] = {\n", + (data->glyphs + 1) * 16); + + fprintf(fp, "\t0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,\n" + "\t0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,\n"); + + limit = data->glyphs; + for (i = 0; i < limit; i++) { + glyph_entry *e = data->e[i]; + + for (y = 0; y < 16; y++) { + if (i == limit - 1 && y == 15) + fprintf(fp, "0x%.2X\n", e->data.u8[y]); + else if (y % 8 == 7) + fprintf(fp, "0x%.2X,\n", e->data.u8[y]); + else if (y % 8 == 0) + fprintf(fp, "\t0x%.2X, ", e->data.u8[y]); + else + fprintf(fp, "0x%.2X, ", e->data.u8[y]); + } + } + + fprintf(fp, "};\n"); + fprintf(fp, "const uint8_t *font_glyph_data = &font_glyph_data_c[0];\n\n"); + + fclose(fp); + + return true; +} + +static bool add_glyph_to_data(glyph_entry *add, int id, int style, + struct font_data *d) +{ + glyph_entry *e; + int offset; + int s; + + /* Find out if 'add' is unique, and get its unique table entry */ + e = glyph_add_to_table(add); + if (e == add) { + /* Unique glyph */ + d->e[d->glyphs++] = e; + e->index = d->glyphs; + if (d->glyphs >= 0xfffd) { + LOG(LOG_ERROR, " Too many glyphs for internal data " + "representation\n"); + return false; + } + } else { + /* Duplicate glyph */ + LOG(LOG_DEBUG, " U+%.4X (%s) is duplicate\n", + id, short_labels[style]); + } + + /* Find glyph's section */ + s = id / 256; + + /* Allocate section if needed */ + if ((s == 0 && d->sections[style] == NULL) || + (s != 0 && d->section_table[style][s] == 0)) { + size_t size = (d->sec_count[style] + 1) * SECTION_SIZE; + uint16_t *temp = realloc(d->sections[style], size); + if (temp == NULL) { + LOG(LOG_ERROR, " Couldn't increase sections " + "allocation\n"); + return false; + } + memset(temp + d->sec_count[style] * 256, 0, + SECTION_SIZE); + d->section_table[style][s] = d->sec_count[style]; + d->sections[style] = temp; + d->sec_count[style]++; + } + + offset = d->section_table[style][s] * 256 + (id & 0xff); + d->sections[style][offset] = e->index; + + return true; +} + + +static bool check_glyph_data_valid(int pos, char c) +{ + int offset = pos % 11; + + if (pos == 44) { + if (c != '\n') { + LOG(LOG_ERROR, " Invalid glyph data: " + "expecting '\\n', got '%c' (%i)\n", + c, c); + return false; + } else { + return true; + } + } else if (pos < 3) { + if (c != ' ') { + LOG(LOG_ERROR, " Invalid glyph data: " + "expecting ' ', got '%c' (%i)\n", + c, c); + return false; + } else { + return true; + } + } else if (offset == 0) { + if (c != '\n' && c != ' ') { + LOG(LOG_ERROR, " Invalid glyph data: " + "expecting '\\n' or ' ', " + "got '%c' (%i)\n", + c, c); + return false; + } else { + return true; + } + } else if (offset < 3) { + if (c != ' ') { + LOG(LOG_ERROR, " Invalid glyph data: " + "expecting ' ', got '%c' (%i)\n", + c, c); + return false; + } else { + return true; + } + } else if (offset >= 3 && pos < 11) { + if (c != '.' && c != '#') { + LOG(LOG_ERROR, " Invalid glyph data: " + "expecting '.' or '#', " + "got '%c' (%i)\n", + c, c); + return false; + } else { + return true; + } + } + + /* offset must be >=3 */ + if (c != '.' && c != '#' && c != ' ') { + LOG(LOG_ERROR, " Invalid glyph data: " + "expecting '.', '#', or ' ', " + "got '%c' (%i)\n", + c, c); + return false; + } + + return true; +} + +#define SEVEN_SET ((1 << 0) | (1 << 1) | (1 << 2) | (1 << 3) | \ + (1 << 4) | (1 << 5) | (1 << 6)) + +#define THREE_SSS ((1 << 0) | (1 << 1) | (1 << 2)) +#define THREE_S_S ((1 << 0) | (1 << 2)) +#define THREE__SS ((1 << 0) | (1 << 1) ) +#define THREE_SS_ ( (1 << 1) | (1 << 2)) +#define THREE_S__ (1 << 2) +#define THREE__S_ (1 << 1) +#define THREE___S (1 << 0) + +uint8_t frag[16][5] = { + { THREE_SSS, + THREE_S_S, + THREE_S_S, + THREE_S_S, + THREE_SSS }, + + { THREE__S_, + THREE_SS_, + THREE__S_, + THREE__S_, + THREE_SSS }, + + { THREE_SS_, + THREE___S, + THREE__S_, + THREE_S__, + THREE_SSS }, + + { THREE_SS_, + THREE___S, + THREE_SS_, + THREE___S, + THREE_SS_ }, + + { THREE_S_S, + THREE_S_S, + THREE_SSS, + THREE___S, + THREE___S }, + + { THREE_SSS, + THREE_S__, + THREE_SSS, + THREE___S, + THREE_SSS }, + + { THREE__SS, + THREE_S__, + THREE_SSS, + THREE_S_S, + THREE_SSS }, + + { THREE_SSS, + THREE___S, + THREE__S_, + THREE__S_, + THREE__S_ }, + + { THREE_SSS, + THREE_S_S, + THREE_SSS, + THREE_S_S, + THREE_SSS }, + + { THREE_SSS, + THREE_S_S, + THREE_SSS, + THREE___S, + THREE___S }, + + { THREE__S_, + THREE_S_S, + THREE_SSS, + THREE_S_S, + THREE_S_S }, + + { THREE_SS_, + THREE_S_S, + THREE_SS_, + THREE_S_S, + THREE_SS_ }, + + { THREE__S_, + THREE_S_S, + THREE_S__, + THREE_S_S, + THREE__S_ }, + + { THREE_SS_, + THREE_S_S, + THREE_S_S, + THREE_S_S, + THREE_SS_ }, + + { THREE_SSS, + THREE_S__, + THREE_SS_, + THREE_S__, + THREE_SSS }, + + { THREE_SSS, + THREE_S__, + THREE_SS_, + THREE_S__, + THREE_S__ } +}; + +void build_codepoint(int id, bool italic, uint8_t *code_point) +{ + int shift = 0; + int l; + int r; + + if (!italic) + shift = 1; + + l = (id >> 12); + r = 0xf & (id >> 8); + + code_point[ 0] = 0; + code_point[ 1] = SEVEN_SET << shift; + code_point[ 2] = 0; + + code_point[ 3] = (frag[l][0] << (4 + shift)) | (frag[r][0] << shift); + code_point[ 4] = (frag[l][1] << (4 + shift)) | (frag[r][1] << shift); + code_point[ 5] = (frag[l][2] << (4 + shift)) | (frag[r][2] << shift); + code_point[ 6] = (frag[l][3] << (4 + shift)) | (frag[r][3] << shift); + code_point[ 7] = (frag[l][4] << (4 + shift)) | (frag[r][4] << shift); + + code_point[ 8] = 0; + + shift = 1; + + l = 0xf & (id >> 4); + r = 0xf & id ; + + code_point[ 9] = (frag[l][0] << (4 + shift)) | (frag[r][0] << shift); + code_point[10] = (frag[l][1] << (4 + shift)) | (frag[r][1] << shift); + code_point[11] = (frag[l][2] << (4 + shift)) | (frag[r][2] << shift); + code_point[12] = (frag[l][3] << (4 + shift)) | (frag[r][3] << shift); + code_point[13] = (frag[l][4] << (4 + shift)) | (frag[r][4] << shift); + + code_point[14] = 0; + code_point[15] = SEVEN_SET << shift; +} + +#undef SEVEN_SET +#undef THREE_SSS +#undef THREE_S_S +#undef THREE__SS +#undef THREE_SS_ +#undef THREE_S__ +#undef THREE__S_ +#undef THREE___S + +static bool glyph_is_codepoint(const glyph_entry *e, int id, int style) +{ + bool italic = false; + + if (style == 1 || style == 3) { + italic = true; + } + + build_codepoint(id, italic, code_point); + + return glyphs_match(code_point, e->data.u8); +} + + +static bool parse_glyph_data(struct parse_context *ctx, char c, + struct font_data *d) +{ + int glyph = ctx->data.in_gd.pos / 11; + int g_pos = ctx->data.in_gd.pos % 11 - 3; + uint8_t *row; + bool ok; + int i; + + /* Check that character is valid */ + if (check_glyph_data_valid(ctx->data.in_gd.pos, c) == false) { + LOG(LOG_ERROR, " Error in U+%.4X data: " + "glyph line: %i, pos: %i\n", + ctx->id, + ctx->data.in_gd.line, + ctx->data.in_gd.pos); + goto error; + } + + /* Allocate glyph data if needed */ + if (ctx->data.in_gd.line == 0 && + (c == '.' || c == '#')) { + if (ctx->data.in_gd.e[glyph] == NULL) { + ctx->data.in_gd.e[glyph] = + calloc(sizeof(struct glyph_entry), 1); + if (ctx->data.in_gd.e[glyph] == NULL) { + LOG(LOG_ERROR, " Couldn't allocate memory for " + "glyph entry\n"); + goto error; + } + + ctx->data.in_gd.styles |= 1 << glyph; + } + } + + /* Build glyph data */ + if (c == '#') { + row = &ctx->data.in_gd.e[glyph]->data.u8[ctx->data.in_gd.line]; + *row += 1 << (7 - g_pos); + + ctx->data.in_gd.line_styles |= 1 << glyph; + } else if (c == '.') { + ctx->data.in_gd.line_styles |= 1 << glyph; + } + + /* Deal with current position */ + if (c == '\n') { + if (ctx->data.in_gd.line == 0) { + if (ctx->data.in_gd.e[0] == NULL) { + LOG(LOG_ERROR, " Error in U+%.4X data: " + "\"Regular\" glyph style must " + "be present\n", ctx->id); + goto error; + } + } else if (ctx->data.in_gd.styles != + ctx->data.in_gd.line_styles) { + LOG(LOG_ERROR, " Error in U+%.4X data: " + "glyph line: %i " + "styles don't match first line\n", + ctx->id, + ctx->data.in_gd.line); + goto error; + } + + ctx->data.in_gd.pos = 0; + ctx->data.in_gd.line++; + ctx->data.in_gd.line_styles = 0; + } else { + ctx->data.in_gd.pos++; + } + + /* If we've got all the glyph data, tidy up and advance state */ + if (ctx->data.in_gd.line == 16) { + for (i = 0; i < 4; i++) { + if (ctx->data.in_gd.e[i] != NULL) { + ctx->count[i] += 1; + if (glyph_is_codepoint(ctx->data.in_gd.e[i], + ctx->id, i)) { + LOG(LOG_DEBUG, " U+%.4X (%s) is " + "codepoint\n", + ctx->id, + short_labels[i]); + ctx->codepoints += 1; + free(ctx->data.in_gd.e[i]); + ctx->data.in_gd.e[i] = NULL; + continue; + } + + ok = add_glyph_to_data(ctx->data.in_gd.e[i], + ctx->id, i, d); + if (!ok) { + goto error; + } + } + } + + ctx->data.before_id.new_line = false; + ctx->data.before_id.u = false; + ctx->state = BEFORE_ID; + } + + return true; + +error: + + for (i = 0; i < 4; i++) { + free(ctx->data.in_gd.e[i]); + } + + return false; +} + +static void parse_init(struct parse_context *ctx) +{ + memset(ctx, 0, sizeof(struct parse_context)); +} + +static bool get_hex_digit_value(char c, int *v) +{ + if (c >= '0' && c <= '9') + *v = (c - '0'); + else if (c >= 'A' && c <= 'F') + *v = (10 + c - 'A'); + else { + LOG(LOG_ERROR, "Invalid hex digit '%c' (%i)\n", c, c); + return false; + } + + return true; +} + +static bool assemble_codepoint(const char* c, int n, int *id) +{ + bool ok; + int v; + + ok = get_hex_digit_value(*c, &v); + if (!ok) { + return false; + } + + *id += v << (4 * (3 - n)); + + return true; +} + +static bool parse_chunk(struct parse_context *ctx, const char *buf, size_t len, + struct font_data *d) +{ + int i; + bool ok; + int count[4]; + const char *pos = buf; + const char *end = buf + len; + + for (i = 0; i < 4; i++) { + count[i] = ctx->count[i]; + } + + while (pos < end) { + if (*pos == '\r') { + LOG(LOG_ERROR, "Detected \'\\r\': Bad line ending\n"); + return false; + } + + switch (ctx->state) { + case START: + if (*pos != '*') { + LOG(LOG_ERROR, "First character must be '*'\n"); + printf("Got: %c (%i)\n", *pos, *pos); + return false; + } + d->header_len = 0; + ctx->data.in_header.new_line = true; + ctx->state = IN_HEADER; + + /* Fall through */ + case IN_HEADER: + if (ctx->data.in_header.new_line == true) { + if (*pos != '*') { + LOG(LOG_INFO, " Got header " + "(%i bytes)\n", + d->header_len); + LOG(LOG_DEBUG, " Header:\n\n%.*s\n", + d->header_len, + d->header); + ctx->data.before_id.new_line = false; + ctx->data.before_id.u = false; + ctx->state = BEFORE_ID; + continue; + } else if (*pos == '*') { + d->header[d->header_len++] = ' '; + } + ctx->data.in_header.new_line = false; + + } else if (*pos == '\n') { + ctx->data.in_header.new_line = true; + } + + if (d->header_len == HEADER_MAX) { + LOG(LOG_ERROR, " Header too long " + "(>%i bytes)\n", + d->header_len); + return false; + } + + d->header[d->header_len++] = *pos; + break; + + case BEFORE_ID: + if (*pos == '+' && + ctx->data.before_id.new_line == true && + ctx->data.before_id.u == true) { + ctx->data.g_id.c = 0; + ctx->id = 0; + ctx->state = GLYPH_ID; + break; + + } else if (*pos == 'U' && + ctx->data.before_id.new_line == true) { + ctx->data.before_id.u = true; + + } else if (*pos == '\n') { + ctx->data.before_id.new_line = true; + ctx->data.before_id.u = false; + + } else { + ctx->data.before_id.new_line = false; + ctx->data.before_id.u = false; + } + break; + + case GLYPH_ID: + ok = assemble_codepoint(pos, ctx->data.g_id.c++, + &ctx->id); + if (!ok) { + LOG(LOG_ERROR, " Invalid glyph ID\n"); + return false; + } + + if (ctx->data.g_id.c == 4) { + ctx->data.before_gd.new_line = false; + ctx->data.before_gd.prev_h = false; + ctx->data.before_gd.prev_s = false; + ctx->data.before_gd.c = 0; + ctx->state = BEFORE_GLYPH_DATA; + break; + } + break; + + case BEFORE_GLYPH_DATA: + /* Skip until end of dashed line */ + if (*pos == '\n' && ctx->data.before_gd.c == 53) { + ctx->state = IN_GLYPH_DATA; + ctx->data.in_gd.e[0] = NULL; + ctx->data.in_gd.e[1] = NULL; + ctx->data.in_gd.e[2] = NULL; + ctx->data.in_gd.e[3] = NULL; + ctx->data.in_gd.line = 0; + ctx->data.in_gd.pos = 0; + ctx->data.in_gd.line_styles = 0; + ctx->data.in_gd.styles = 0; + break; + + } else if (*pos == '\n') { + ctx->data.before_gd.new_line = true; + ctx->data.before_gd.prev_h = false; + ctx->data.before_gd.prev_s = false; + ctx->data.before_gd.c = 0; + } else if (*pos == '-' && + ctx->data.before_gd.new_line == true) { + assert(ctx->data.before_gd.c == 0); + ctx->data.before_gd.new_line = false; + ctx->data.before_gd.c++; + ctx->data.before_gd.prev_h = true; + } else if (*pos == ' ' && + ctx->data.before_gd.prev_h == true) { + assert(ctx->data.before_gd.prev_s == false); + ctx->data.before_gd.c++; + ctx->data.before_gd.prev_h = false; + ctx->data.before_gd.prev_s = true; + } else if (*pos == '-' && + ctx->data.before_gd.prev_s == true) { + assert(ctx->data.before_gd.prev_h == false); + ctx->data.before_gd.c++; + ctx->data.before_gd.prev_h = true; + ctx->data.before_gd.prev_s = false; + } else { + ctx->data.before_gd.new_line = false; + ctx->data.before_gd.prev_h = false; + ctx->data.before_gd.prev_s = false; + ctx->data.before_gd.c = 0; + } + break; + + case IN_GLYPH_DATA: + ok = parse_glyph_data(ctx, *pos, d); + if (!ok) { + return false; + } + + break; + } + + pos++; + } + + for (i = 0; i < 4; i++) { + LOG(LOG_DEBUG, " %s: %i gylphs\n", labels[i], + ctx->count[i] - count[i]); + } + + return true; +} + + +bool load_font(const char *path, struct font_data **data) +{ + struct parse_context ctx; + struct font_data *d; + size_t file_len; + size_t done; + size_t len; + int count; + char *buf; + FILE *fp; + bool ok; + int i; + + *data = NULL; + + fp = fopen(path, "rb"); + if (fp == NULL) { + LOG(LOG_ERROR, "Couldn't open font data file\n"); + return false; + } + + d = calloc(sizeof(struct font_data), 1); + if (d == NULL) { + LOG(LOG_ERROR, "Couldn't allocate memory for font data\n"); + fclose(fp); + return false; + } + + /* Find filesize */ + fseek(fp, 0L, SEEK_END); + file_len = ftell(fp); + if (file_len == -1) { + LOG(LOG_ERROR, "Could not size input file\n"); + free(d); + fclose(fp); + return false; + } + fseek(fp, 0L, SEEK_SET); + LOG(LOG_DEBUG, "Input size: %zu bytes\n", file_len); + + /* Allocate buffer for data chunks */ + buf = malloc(CHUNK_SIZE); + if (buf == NULL) { + LOG(LOG_ERROR, "Couldn't allocate memory for input buffer\n"); + free(d); + fclose(fp); + return false; + } + + /* Initialise parser */ + parse_init(&ctx); + + LOG(LOG_DEBUG, "Using chunk size of %i bytes\n", CHUNK_SIZE); + + /* Parse the input file in chunks */ + for (done = 0; done < file_len; done += CHUNK_SIZE) { + LOG(LOG_INFO, "Parsing input chunk %zu\n", done / CHUNK_SIZE); + + /* Read chunk */ + len = fread(buf, 1, CHUNK_SIZE, fp); + if (file_len - done < CHUNK_SIZE && + len != file_len - done) { + LOG(LOG_WARNING, "Last chunk has suspicious size\n"); + } else if (file_len - done >= CHUNK_SIZE && + len != CHUNK_SIZE) { + LOG(LOG_ERROR, "Problem reading file\n"); + free(buf); + free(d); + fclose(fp); + return false; + } + + /* Parse chunk */ + ok = parse_chunk(&ctx, buf, len, d); + if (!ok) { + free(buf); + free(d); + fclose(fp); + return false; + } + LOG(LOG_DEBUG, "Parsed %zu bytes\n", done + len); + } + + fclose(fp); + + if (ctx.state != BEFORE_ID) { + LOG(LOG_ERROR, "Unexpected end of file\n"); + free(buf); + free(d); + return false; + } + + LOG(LOG_INFO, "Parsing complete:\n"); + count = 0; + for (i = 0; i < 4; i++) { + LOG(LOG_INFO, " %s: %i gylphs\n", labels[i], ctx.count[i]); + count += ctx.count[i]; + } + + LOG(LOG_RESULT, " Total %i gylphs " + "(of which %i unique, %i codepoints, %i duplicates)\n", + count, d->glyphs, ctx.codepoints, + count - d->glyphs - ctx.codepoints); + + free(buf); + + *data = d; + return true; +} + +static void log_usage(const char *argv0) +{ + level = LOG_INFO; + LOG(LOG_INFO, + "Usage:\n" + "\t%s [options] \n" + "\n" + "Options:\n" + "\t--help -h Display this text\n" + "\t--quiet -q Don't show warnings\n" + "\t--verbose -v Verbose output\n" + "\t--debug -d Full debug output\n", + argv0); +} + +int main(int argc, char** argv) +{ + const char *in_path = NULL; + const char *out_path = NULL; + char *header_path = NULL; + struct font_data *data; + bool ok; + int i; + int opt; + + level = LOG_RESULT; + + /* Handle program arguments */ + struct option long_options[] = { + { "help", no_argument, NULL, 'h' }, + { "quiet", no_argument, NULL, 'q' }, + { "verbose", no_argument, NULL, 'v' }, + { "debug", no_argument, NULL, 'd' }, + { "header", required_argument, NULL, 'H' }, + }; + + while ((opt = getopt_long(argc, argv, "hqvdH:", long_options, NULL)) != -1) { + switch (opt) { + case 'q': + level = LOG_WARNING; + break; + + case 'v': + level = LOG_INFO; + break; + + case 'd': + level = LOG_DEBUG; + break; + + case 'H': + header_path = strdup(optarg); + break; + + case 'h': + log_usage(argv[0]); + free(header_path); + return EXIT_SUCCESS; + + default: + log_usage(argv[0]); + free(header_path); + return EXIT_FAILURE; + } + } + + if ((argc - optind) < 2) { + log_usage(argv[0]); + free(header_path); + return EXIT_FAILURE; + } + + in_path = argv[optind]; + out_path = argv[optind + 1]; + + LOG(LOG_DEBUG, "Using input path: \"%s\"\n", in_path); + LOG(LOG_DEBUG, "Using output path: \"%s\"\n", out_path); + + ok = load_font(in_path, &data); + if (!ok) { + free_table(); + free(header_path); + return EXIT_FAILURE; + } + + ok = generate_font_source(out_path, data); + if (ok && (header_path != NULL)) { + ok = generate_font_header(header_path, data); + } + free(header_path); + free_table(); + for (i = 0; i < 4; i++) { + free(data->sections[i]); + } + free(data); + if (!ok) { + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} -- cgit v1.2.3