text: font: unifont: add genunifont generator

This new generator converts the unifont hex-encoded data into a C-source
file which then can be compiled statically into the kmscon binary. Please
note that the resulting source file is bigger than 100MB and can take
quite a while to compile.

Signed-off-by: David Herrmann <dh.herrmann@googlemail.com>
This commit is contained in:
David Herrmann 2012-08-25 15:36:55 +02:00
parent ec808aba12
commit e9b523c3f1
3 changed files with 380 additions and 1 deletions

2
.gitignore vendored
View File

@ -25,8 +25,10 @@ stamp-*
.dirstamp
.libs
genshader
genunifont
src/static_shaders.c
src/genshader.c
src/text_font_unifont_data.c
docs/reference/*.txt
docs/reference/*.bak
docs/reference/kmscon.????*

View File

@ -34,7 +34,8 @@ check_PROGRAMS = \
test_vt \
test_input
noinst_PROGRAMS = \
genshader
genshader \
genunifont
noinst_LTLIBRARIES = \
libkmscon-core.la \
libkmscon-static.la
@ -104,6 +105,25 @@ nodist_genshader_SOURCES = \
src/static_shaders.c: $(SHADERS) genshader$(EXEEXT)
./genshader$(EXEEXT) src/static_shaders.c $(SHADERS)
#
# Unifont Generator
# This generates the unifont sources from raw hex-encoded font data.
#
UNIFONT = \
src/text_font_unifont_data.hex
EXTRA_DIST += \
$(UNIFONT)
CLEANFILES += \
src/text_font_unifont_data.c
nodist_genunifont_SOURCES = \
src/genunifont.c
src/text_font_unifont_data.c: $(UNIFONT) genunifont$(EXEEXT)
./genunifont$(EXEEXT) src/text_font_unifont_data.c $(UNIFONT)
#
# libkmscon-core
# This static library contains all the source files used in kmscon. We build

357
src/genunifont.c Normal file
View File

@ -0,0 +1,357 @@
/*
* kmscon - Generate Unifont data files
*
* Copyright (c) 2012 Ted Kotz <ted@kotz.us>
* Copyright (c) 2012 David Herrmann <dh.herrmann@googlemail.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.
*/
/*
* Unifont Generator
* This converts the hex-encoded Unifont data into a C-array that is used by the
* unifont-font-renderer.
*/
#include <errno.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_DATA_SIZE 512
struct unifont_glyph {
struct unifont_glyph *next;
uint32_t codepoint;
uint8_t len;
char data[MAX_DATA_SIZE];
};
static uint8_t hex_val(char c)
{
if (c >= '0' && c <= '9')
return c - '0';
else if (c >= 'a' && c <= 'f')
return c - 'a' + 10;
else if (c >= 'A' && c <= 'F')
return c - 'A' + 10;
fprintf(stderr, "genunifont: invalid hex-code %c\n", c);
return 0;
}
static void print_data_row(FILE *out, char c)
{
static const char *line_map[16] = {
"0x00, 0x00, 0x00, 0x00,",
"0x00, 0x00, 0x00, 0xff,",
"0x00, 0x00, 0xff, 0x00,",
"0x00, 0x00, 0xff, 0xff,",
"0x00, 0xff, 0x00, 0x00,",
"0x00, 0xff, 0x00, 0xff,",
"0x00, 0xff, 0xff, 0x00,",
"0x00, 0xff, 0xff, 0xff,",
"0xff, 0x00, 0x00, 0x00,",
"0xff, 0x00, 0x00, 0xff,",
"0xff, 0x00, 0xff, 0x00,",
"0xff, 0x00, 0xff, 0xff,",
"0xff, 0xff, 0x00, 0x00,",
"0xff, 0xff, 0x00, 0xff,",
"0xff, 0xff, 0xff, 0x00,",
"0xff, 0xff, 0xff, 0xff,",
};
uint8_t idx;
idx = hex_val(c);
if (idx < 16) {
fprintf(out, line_map[idx]);
} else {
fprintf(stderr, "genunifont: invalid value %c\n", c);
fprintf(out, line_map[0]);
}
}
static void print_unifont_glyph(FILE *out, const struct unifont_glyph *g)
{
int width;
size_t i;
switch (g->len) {
case 64:
width = 4;
break;
case 32:
width = 2;
break;
default:
fprintf(stderr, "genunifont: invalid data size");
return;
}
fprintf(out, "\t{ /* %d 0x%x */\n"
"\t\t.buf = {\n"
"\t\t\t.width = %d,\n"
"\t\t\t.height = 16,\n"
"\t\t\t.stride = %d,\n"
"\t\t\t.format = UTERM_FORMAT_GREY,\n"
"\t\t\t.data = (uint8_t[]){\n",
g->codepoint, g->codepoint,
width * 4, width * 4);
for (i = 0; i < g->len; ++i) {
fprintf(out, "\t\t\t\t");
print_data_row(out, g->data[i]);
fprintf(out, "\n");
}
fprintf(out, "\t\t\t},\n\t\t},\n\t},\n");
}
static int build_unifont_glyph(struct unifont_glyph *g, const char *buf)
{
int val;
val = 0;
while (*buf && *buf != ':') {
val <<= 4;
val += hex_val(*buf++);
}
if (*buf++ != ':') {
fprintf(stderr, "genunifont: invalid file format\n");
return -EFAULT;
}
g->codepoint = val;
g->len = 0;
while (*buf && *buf != '\n' && g->len < MAX_DATA_SIZE) {
g->data[g->len] = *buf++;
++g->len;
}
return 0;
}
static void write_name(FILE *out, const char *name)
{
size_t i, len;
len = strlen(name);
for (i = 0; i < len; ++i) {
if ((name[i] >= 'A' && name[i] <= 'Z') ||
(name[i] >= 'a' && name[i] <= 'z') ||
(name[i] >= '0' && name[i] <= '9'))
fwrite(&name[i], 1, 1, out);
else
fwrite("_", 1, 1, out);
}
}
static int parse_single_file(FILE *out, FILE *in, const char *varname)
{
struct unifont_glyph replacement = {
.codepoint = 0,
.len = 32,
.data = "0000007E665A5A7A76767E76767E0000"
};
static const char c0[] = "const struct kmscon_glyph kmscon_";
static const char c1[] = "_glyphs[] = {\n";
static const char c2[] = "};\n\nconst size_t kmscon_";
static const char c3[] = "_len =\n\tsizeof(kmscon_";
static const char c4[] = "_glyphs) /\n\tsizeof(*kmscon_";
static const char c5[] = "_glyphs);\n";
char buf[MAX_DATA_SIZE];
struct unifont_glyph *g, *iter, *list, *last;
int ret, num;
unsigned long status_max, status_cur;
unsigned long perc_prev, perc_now;
if (fseek(in, 0, SEEK_END) != 0) {
fprintf(stderr, "genunifont: cannot seek: %m\n");
return -EFAULT;
}
status_max = ftell(in);
if (status_max < 0) {
fprintf(stderr, "genunifont: cannot ftell: %m\n");
return -EFAULT;
}
if (status_max < 1) {
fprintf(stderr, "genunifont: empty file\n");
return -EFAULT;
}
rewind(in);
list = NULL;
last = NULL;
status_cur = 0;
perc_prev = 0;
perc_now = 0;
fprintf(stderr, "Finished: %3ld%%", perc_now);
while (fgets(buf, sizeof(buf) - 1, in) != NULL) {
perc_now = status_cur * 100 / status_max;
if (perc_now > perc_prev) {
perc_prev = perc_now;
fprintf(stderr, "\b\b\b\b%3ld%%", perc_now);
}
status_cur += strlen(buf);
if (buf[0] == '#')
continue;
g = malloc(sizeof(*g));
if (!g) {
fprintf(stderr, "genunifont: out of memory\n");
return -ENOMEM;
}
memset(g, 0, sizeof(*g));
ret = build_unifont_glyph(g, buf);
if (ret) {
free(g);
continue;
}
if (!list || list->codepoint > g->codepoint) {
g->next = list;
list = g;
if (!last)
last = g;
} else if (list->codepoint == g->codepoint) {
fprintf(stderr, "glyph %d used twice\n",
g->codepoint);
free(g);
} else {
if (last->codepoint < g->codepoint) {
iter = last;
} else {
iter = list;
while (iter->next) {
if (iter->next->codepoint >= g->codepoint)
break;
iter = iter->next;
}
}
if (iter->next) {
if (iter->next->codepoint == g->codepoint) {
fprintf(stderr, "glyph %d used twice\n",
g->codepoint);
free(g);
} else {
g->next = iter->next;
iter->next = g;
}
} else {
iter->next = g;
last = g;
}
}
}
fprintf(stderr, "\n");
fwrite(c0, sizeof(c0) - 1, 1, out);
write_name(out, varname);
fwrite(c1, sizeof(c1) - 1, 1, out);
num = 0;
while (list) {
iter = list;
list = iter->next;
while (num++ < iter->codepoint)
print_unifont_glyph(out, &replacement);
print_unifont_glyph(out, iter);
free(iter);
}
fwrite(c2, sizeof(c2) - 1, 1, out);
write_name(out, varname);
fwrite(c3, sizeof(c3) - 1, 1, out);
write_name(out, varname);
fwrite(c4, sizeof(c4) - 1, 1, out);
write_name(out, varname);
fwrite(c5, sizeof(c5) - 1, 1, out);
return 0;
}
static const char *get_basename(const char *path)
{
const char *res;
res = strrchr(path, '/');
if (!res || !*++res)
return path;
return res;
}
int main(int argc, char **argv)
{
FILE *out, *in;
size_t i;
static const char c0[] = "/* This file was generated "
"by genunifont.c */\n\n"
"#include <stdint.h>\n"
"#include <stdlib.h>\n"
"#include \"text.h\"\n\n";
int ret = EXIT_FAILURE;
if (argc < 2) {
fprintf(stderr, "genunifont: use ./genunifont <outputfile> [<inputfiles> ...]\n");
goto err_out;
}
out = fopen(argv[1], "wb");
if (!out) {
fprintf(stderr, "genunifont: cannot open output %s: %m\n",
argv[1]);
goto err_out;
}
fwrite(c0, sizeof(c0) - 1, 1, out);
for (i = 2; i < argc; ++i) {
fprintf(stderr, "genunifont: parsing input %s\n", argv[i]);
in = fopen(argv[i], "rb");
if (!in) {
fprintf(stderr, "genunifont: cannot open %s: %m\n",
argv[i]);
continue;
}
ret = parse_single_file(out, in, get_basename(argv[i]));
if (ret)
fprintf(stderr, "genunifont: parsing input %s failed",
argv[i]);
fclose(in);
}
ret = EXIT_SUCCESS;
fclose(out);
err_out:
return ret;
}