diff options
Diffstat (limited to 'hextobdf.c')
| -rw-r--r-- | hextobdf.c | 221 |
1 files changed, 221 insertions, 0 deletions
diff --git a/hextobdf.c b/hextobdf.c new file mode 100644 index 0000000..68d8f42 --- /dev/null +++ b/hextobdf.c @@ -0,0 +1,221 @@ +/* + * NAME + * hextobdf - convert font from hex format to bdf + * + * EXAMPLE USAGE + * hextobdf -w 12 -h 22 < gallant.hex > gallant.bdf + * + * LIMITATIONS + * Only for gallant font, due to hard-coded font properties. + */ +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <inttypes.h> +#include <locale.h> +#include <wchar.h> +#include <unistd.h> + +#define PixelWidth 12 +#define PixelHeight 22 +#define MAX_LINE 1024 +#define MAX_GLYPHS 131072 + +struct glyph { + wint_t codepoint; + char *bitmap; +}; + +void parse_options(int aArgc, char **aArgv); +void usage(int aStatus); +void errx(const char *aFormat, ...); +void parse_font_dimensions(FILE *aFile); +void parse_font_line(const char *aLine, struct glyph *aGlyph); +void output_bdf_preamble(void); +void output_bdf_char(int aChar); +void *xmalloc(size_t aSize); + +size_t gWidth = PixelWidth; +size_t gHeight = PixelHeight; +size_t gLineNr = 0; +size_t gBytes = 0; // per one row of pixels in a regular glyph +size_t gDblBytes = 0; // per one row of pixels in a dbl width glyph +struct glyph *gGlyph = NULL; +int gGlyphs = 0; + +// start the ball rolling. +// +int main(int aArgc, char **aArgv) { + if (!setlocale(LC_CTYPE, "")) + errx("Can't set the locale. Check LANG, LC_CTYPE, LC_ALL.\n"); + gGlyph = xmalloc(MAX_GLYPHS * sizeof *gGlyph); + parse_options(aArgc, aArgv); + parse_font_dimensions(stdin); + char buf[MAX_LINE]; + while (fgets(buf, MAX_LINE, stdin)) { + ++gLineNr; + parse_font_line(buf, &gGlyph[gGlyphs]); + ++gGlyphs; + } + fprintf(stderr, "found %d glyphs\n", gGlyphs); + output_bdf_preamble(); + for (int i = 0; i < gGlyphs; ++i) { + output_bdf_char(i); + } + puts("ENDFONT"); + return EXIT_SUCCESS; +} + +// Output the gallant BDF preamble. +// +void output_bdf_preamble(void) { + puts("STARTFONT 2.1"); + puts("FONT -sun-gallant-medium-r-normal--22-220-75-75-c-120-iso10646-1"); + puts("SIZE 22 75 75"); + puts("FONTBOUNDINGBOX 22 12 0 -5"); + puts("STARTPROPERTIES 18"); + puts("FONTNAME_REGISTRY \"\""); + puts("FOUNDRY \"sun\""); + puts("FAMILY_NAME \"gallant\""); + puts("WEIGHT_NAME \"medium\""); + puts("SLANT \"r\""); + puts("SETWIDTH_NAME \"normal\""); + puts("ADD_STYLE_NAME \"\""); + puts("PIXEL_SIZE 22"); + puts("POINT_SIZE 220"); + puts("RESOLUTION_X 75"); + puts("RESOLUTION_Y 75"); + puts("SPACING \"c\""); + puts("AVERAGE_WIDTH 120"); + puts("CHARSET_REGISTRY \"iso10646\""); + puts("CHARSET_ENCODING \"1\""); + puts("FONT_ASCENT 17"); + puts("FONT_DESCENT 5"); + puts("DEFAULT_CHAR 65533"); + puts("ENDPROPERTIES"); + printf("CHARS %d\n", gGlyphs); +} + +// Output data for a single STARTCHAR to stdout. +// +void output_bdf_char(int aChar) { + size_t hexdigits; + printf("STARTCHAR U+%04x\n", gGlyph[aChar].codepoint); + printf("ENCODING %d\n", gGlyph[aChar].codepoint); + if (wcwidth(gGlyph[aChar].codepoint) == 2) { + puts("SWIDTH 1000 0\nDWIDTH 24 0\nBBX 24 22 0 -5"); + hexdigits = 2 * gDblBytes; + } + else { + puts("SWIDTH 500 0\nDWIDTH 12 0\nBBX 12 22 0 -5"); + hexdigits = 2 * gBytes; + } + puts("BITMAP"); + const char *p = gGlyph[aChar].bitmap; + for (size_t h = 0; h < gHeight; ++h) { + for (size_t i = 0; i < hexdigits; ++i) { + putchar(*p); + ++p; + } + putchar('\n'); + } + puts("ENDCHAR"); +} + +// Parse one line of font hex data and store result in glyph. +// +void parse_font_line(const char *aLine, struct glyph *aGlyph) { + char c; + if (sscanf(aLine, "%" SCNx32 "%c", &aGlyph->codepoint, &c) == 2 && c == ':') { + const char *colon = strchr(aLine, ':'); + const size_t hexlen = strlen(colon + 1) - 1; // Minus newline. + + if (wcwidth(aGlyph->codepoint) == 2) { + if (hexlen != gHeight * gDblBytes * 2) + errx("line %d: expected %zu hexdigits for double width glyph, got %zu\n", gLineNr, gHeight * gDblBytes * 2, hexlen); + } + else { + if (hexlen != gHeight * gBytes * 2) + errx("line %d: expected %zu hexdigits for normal width glyph, got %zu\n", gLineNr, gHeight * gBytes * 2, hexlen); + } + aGlyph->bitmap = xmalloc(hexlen); + memcpy(aGlyph->bitmap, colon + 1, hexlen); + } + else + errx("expected codepoint:hexdata in line %d\n", gLineNr); +} + +// Parse the command line options. +// +void parse_options(int aArgc, char **aArgv) { + int ch; + while ((ch = getopt(aArgc, aArgv, "w:h:")) != -1) { + switch (ch) { + case 'h': + if (sscanf(optarg, "%zu", &gHeight) != 1) + errx("can't convert '%s' to height integer\n", optarg); + break; + case 'w': + if (sscanf(optarg, "%zu", &gWidth) != 1) + errx("can't convert '%s' to width integer\n", optarg); + break; + default: + usage(EXIT_FAILURE); + } + } + if (gWidth != PixelWidth || gHeight != PixelHeight) + errx("dimensions do not match gallant font's 12x22\n"); +} + +// Parse the font's Width: and Height: directives. +// +void parse_font_dimensions(FILE *aFile) { + char line[MAX_LINE] = { 0 }; + for (int i = 1; i <= 2; ++i) { + if (fgets(line, sizeof line, aFile) != NULL) { + if (sscanf(line, " # Width: %zu", &gWidth) != 1) + if (sscanf(line, " # Height: %zu", &gHeight) != 1) + errx("line %d must be '# Width or Height: number'\n", i); + } + else + errx("could not read line %d\n", i); + } + if (gWidth != PixelWidth || gHeight != PixelHeight) + errx("dimensions do not match gallant font's 12x22\n"); + gBytes = (gWidth + 7) / 8; + gDblBytes = (2 * gWidth + 7) / 8; +} + + +// Output usage message and exit with status. +// +void usage(int aStatus) { + fprintf(stderr, "usage: hextobdf [options]\n"); + fprintf(stderr, "Options [default]:\n"); + fprintf(stderr, " -h height height in pixels [%d]\n", PixelHeight); + fprintf(stderr, " -w width width in pixels [%d]\n", PixelWidth); + fprintf(stderr, "\nReads hex font from stdin and writes bdf font to stdout\n"); + exit(aStatus); +} + +// Allocate memory and exit on failure. +// +void *xmalloc(size_t aSize) { + void *const mem = malloc(aSize); + if (mem == NULL) + errx("failed to allocate %zu bytes\n", aSize); + return mem; +} + +// Print formatted message on stderr and exit. +// +void errx(const char *aFormat, ...) { + va_list ap; + va_start(ap, aFormat); + vfprintf(stderr, aFormat, ap); + va_end(ap); + exit(EXIT_FAILURE); +} + +/* vim: set tabstop=4 shiftwidth=4 expandtab fileformat=unix: */ |
