/* * makefont create libcaca font data * Copyright © 2006—2021 Sam Hocevar * All Rights Reserved * * This program is free software. It comes without any warranty, to * the extent permitted by applicable law. You can redistribute it * and/or modify it under the terms of the Do What the Fuck You Want * to Public License, Version 2, as published by Sam Hocevar. See * http://www.wtfpl.net/ for more details. * * Usage: * makefont */ #include "config.h" #include #include #include #if defined HAVE_ARPA_INET_H # include #elif defined HAVE_NETINET_IN_H # include #endif #include #include #include "caca_stubs.h" #include "caca.h" /* Split our big strings into chunks of 480 characters, because it is * the multiple of 32 directly below 509, which is the maximum allowed * string size in C89. */ #define STRING_CHUNKS 480 /* This list is built so that it includes all of ASCII, Latin-1, CP-437, * and the UTF-8 glyphs necessary for canvas rotation and mirroring. */ static unsigned int const blocklist[] = { 0x0020, 0x0080, /* Basic latin: A, B, C, a, b, c */ #if 0 0x0080, 0x0100, /* Latin-1 Supplement: Ä, Ç, å, ß */ 0x0100, 0x0180, /* Latin Extended-A: Ā č Ō œ */ 0x0180, 0x0250, /* Latin Extended-B: Ǝ Ƹ */ 0x0250, 0x02b0, /* IPA Extensions: ɐ ɔ ɘ ʌ ʍ */ 0x0370, 0x0400, /* Greek and Coptic: Λ α β */ 0x0400, 0x0500, /* Cyrillic: И Я */ 0x0530, 0x0590, /* Armenian: Ո */ 0x1401, 0x1677, /* Unified Canadian Aboriginal Syllabics: ᒐ ᗡ */ 0x1d00, 0x1d80, /* Phonetic Extensions: ᴉ ᵷ */ 0x2000, 0x2070, /* General Punctuation: ‘’ “” */ 0x2100, 0x2150, /* Letterlike Symbols: Ⅎ */ 0x2200, 0x2300, /* Mathematical Operators: ∀ √ ∞ ∙ */ 0x2300, 0x2400, /* Miscellaneous Technical: ⌐ ⌂ ⌠ ⌡ */ 0x2500, 0x2580, /* Box Drawing: ═ ║ ╗ ╔ ╩ */ 0x2580, 0x25a0, /* Block Elements: ▛ ▞ ░ ▒ ▓ */ 0x25a0, 0x2600, /* Geometric Shapes: ◆ ○ ● */ 0x2600, 0x2700, /* Miscellaneous Symbols: ♥ ★ ☭ */ 0x3000, 0x3040, /* CJK Symbols and Punctuation: 。「」 */ 0x3040, 0x30a0, /* Hiragana: で す */ 0x30a0, 0x3100, /* Katakana: ロ ル */ 0xff00, 0xfff0, /* Halfwidth and Fullwidth Forms: A, B, C, a, b, c */ 0x10400, 0x10450, /* Deseret: 𐐒 𐐋 */ #endif 0, 0 }; struct glyph { uint32_t unicode; char buf[10]; unsigned int same_as; unsigned int data_offset; unsigned int data_width; unsigned int data_size; }; static void fix_glyph(FT_Bitmap *, uint32_t, unsigned int, unsigned int); static int printf_unicode(struct glyph *); static int printf_hex(char const *, uint8_t *, int); static int printf_u32(char const *, uint32_t); static int printf_u16(char const *, uint16_t); /* Counter for written bytes */ static int written = 0; int main(int argc, char *argv[]) { PangoContext *cx; PangoFontDescription *fd; PangoFontMap *fm; PangoLayout *l; PangoRectangle r; FT_Bitmap img; int stdwidth, fullwidth, height, blocks, glyphs, fullglyphs; unsigned int n, b, i; unsigned int stdsize, fullsize, control_size, data_size, current_offset; uint8_t *glyph_data; struct glyph *gtab; unsigned int bpp, dpi; char const *prefix, *font; if(argc != 5) { fprintf(stderr, "%s: wrong argument count\n", argv[0]); fprintf(stderr, "usage: %s \n", argv[0]); fprintf(stderr, "eg: %s monospace9 \"Monospace 9\" 96 4\n", argv[0]); return -1; } prefix = argv[1]; font = argv[2]; dpi = atoi(argv[3]); bpp = atoi(argv[4]); if(dpi == 0 || (bpp != 1 && bpp != 2 && bpp != 4 && bpp != 8)) { fprintf(stderr, "%s: invalid argument\n", argv[0]); return -1; } fprintf(stderr, "Font \"%s\", %i dpi, %i bpp\n", font, dpi, bpp); /* Initialise Pango */ fm = pango_ft2_font_map_new(); pango_ft2_font_map_set_resolution(PANGO_FT2_FONT_MAP(fm), dpi, dpi); cx = pango_ft2_font_map_create_context(PANGO_FT2_FONT_MAP(fm)); l = pango_layout_new(cx); if(!l) { fprintf(stderr, "%s: unable to initialise pango\n", argv[0]); g_object_unref(cx); return -1; } fd = pango_font_description_from_string(font); pango_layout_set_font_description(l, fd); pango_font_description_free(fd); /* Initialise our FreeType2 bitmap */ img.width = 256; img.pitch = 256; img.rows = 256; img.buffer = malloc(256 * 256); img.num_grays = 256; img.pixel_mode = ft_pixel_mode_grays; /* Test rendering so that we know the glyph width */ pango_layout_set_markup(l, "@", -1); pango_layout_get_extents(l, NULL, &r); stdwidth = PANGO_PIXELS(r.width); fullwidth = stdwidth * 2; height = PANGO_PIXELS(r.height); stdsize = ((stdwidth * height) + (8 / bpp) - 1) / (8 / bpp); fullsize = ((fullwidth * height) + (8 / bpp) - 1) / (8 / bpp); /* Compute blocks and glyphs count */ blocks = 0; glyphs = 0; fullglyphs = 0; for(b = 0; blocklist[b + 1]; b += 2) { blocks++; glyphs += blocklist[b + 1] - blocklist[b]; for(i = blocklist[b]; i < blocklist[b + 1]; i++) if(caca_utf32_is_fullwidth(i)) fullglyphs++; } control_size = 28 + 12 * blocks + 8 * glyphs; data_size = stdsize * (glyphs - fullglyphs) + fullsize * fullglyphs; gtab = malloc(glyphs * sizeof(struct glyph)); glyph_data = malloc(data_size); /* Let's go! */ printf("/* libcaca font file\n"); printf(" * \"%s\": %i dpi, %i bpp, %ix%i/%ix%i glyphs\n", font, dpi, bpp, stdwidth, height, fullwidth, height); printf(" * Automatically generated by tools/makefont.c:\n"); printf(" * tools/makefont %s \"%s\" %i %i\n", prefix, font, dpi, bpp); printf(" */\n"); printf("\n"); printf("static size_t const %s_size = %i;\n", prefix, 4 + control_size + data_size); printf("static uint8_t const %s_data[%i] =\n", prefix, 4 + control_size + data_size); printf("{\n"); printf("/* file: */\n"); printf("0xCA,0xCA, /* caca_header */\n"); written += 2; printf("'F','T', /* caca_file_type */\n"); written += 2; printf("\n"); printf("/* font_header: */\n"); printf_u32("%s /* control_size */\n", control_size); printf_u32("%s /* data_size */\n", data_size); printf_u16("%s /* version */\n", 1); printf_u16("%s /* blocks */\n", blocks); printf_u32("%s /* glyphs */\n", glyphs); printf_u16("%s /* bpp */\n", bpp); printf_u16("%s /* std width */\n", stdwidth); printf_u16("%s /* std height */\n", height); printf_u16("%s /* max width */\n", fullwidth); printf_u16("%s /* max height */\n", height); printf_u16("%s /* flags */\n", 1); printf("\n"); printf("/* block_info: */\n"); n = 0; for(b = 0; blocklist[b + 1]; b += 2) { printf_u32("%s", blocklist[b]); printf_u32("%s", blocklist[b + 1]); printf_u32("%s\n", n); n += blocklist[b + 1] - blocklist[b]; } printf("\n"); /* Render all glyphs, so that we can know their offset */ current_offset = n = 0; for(b = 0; blocklist[b + 1]; b += 2) { for(i = blocklist[b]; i < blocklist[b + 1]; i++) { int x, y, bytes, current_width = stdwidth; unsigned int k, current_size = stdsize; if(caca_utf32_is_fullwidth(i)) { current_width = fullwidth; current_size = fullsize; } gtab[n].unicode = i; bytes = caca_utf32_to_utf8(gtab[n].buf, gtab[n].unicode); gtab[n].buf[bytes] = '\0'; /* Render glyph on a bitmap */ pango_layout_set_text(l, gtab[n].buf, -1); memset(img.buffer, 0, img.pitch * height); pango_ft2_render_layout(&img, l, 0, 0); /* Fix glyphs that we know how to handle better */ fix_glyph(&img, gtab[n].unicode, current_width, height); /* Write bitmap as an escaped C string */ memset(glyph_data + current_offset, 0, current_size); k = 0; for(y = 0; y < height; y++) { for(x = 0; x < current_width; x++) { uint8_t pixel = img.buffer[y * img.pitch + x]; pixel >>= (8 - bpp); glyph_data[current_offset + k / 8] |= pixel << (8 - bpp - (k % 8)); k += bpp; } } /* Check whether this is the same glyph as another one. Please * don't bullshit me about sorting, hashing and stuff like that, * our data is small enough for this to work. */ for(k = 0; k < n; k++) { if(gtab[k].data_size != current_size) continue; #if 0 if(!memcmp(glyph_data + gtab[k].data_offset, glyph_data + current_offset, current_size)) break; #endif } gtab[n].data_offset = current_offset; gtab[n].data_width = current_width; gtab[n].data_size = current_size; gtab[n].same_as = k; if(k == n) current_offset += current_size; n++; } } printf("/* glyph_info: */\n"); n = 0; for(b = 0; blocklist[b + 1]; b += 2) { for(i = blocklist[b]; i < blocklist[b + 1]; i++) { printf_u16("%s", gtab[n].data_width); printf_u16("%s", height); printf_u32("%s\n", gtab[gtab[n].same_as].data_offset); n++; } } printf("\n"); printf("/* font_data: */\n"); n = 0; for(b = 0; blocklist[b + 1]; b += 2) { for(i = blocklist[b]; i < blocklist[b + 1]; i++) { /* Print glyph value in comment */ printf("/* "); printf_unicode(>ab[n]); if(gtab[n].same_as == n) { char const *lut = " .:nmW@"; printf("\n"); for (int y = 0; y < height; ++y) { for (int x = 0; x < gtab[n].data_width; ++x) { int val = glyph_data[gtab[n].data_offset + y * gtab[n].data_width + x]; char ch = lut[val * val * 7 / 256 / 256]; printf("%c%c", ch, ch); } printf("\n"); } //printf_hex(" */ %s\n", // glyph_data + gtab[n].data_offset, gtab[n].data_size); } else { printf(" is "); printf_unicode(>ab[gtab[n].same_as]); printf(" */\n"); } n++; } } printf("};\n"); free(img.buffer); free(gtab); free(glyph_data); g_object_unref(l); g_object_unref(cx); g_object_unref(fm); return 0; } /* * XXX: the following functions are local */ static void fix_glyph(FT_Bitmap *i, uint32_t ch, unsigned int width, unsigned int height) { unsigned int x, y; switch(ch) { case 0x00002580: /* ▀ */ for(y = 0; y < height; y++) for(x = 0; x < width; x++) i->buffer[x + y * i->pitch] = y < height / 2 ? 0xff : 0x00; if(height & 1) for(x = 0; x < width; x++) i->buffer[x + (height / 2) * i->pitch] = 0x7f; break; case 0x00002584: /* ▄ */ for(y = 0; y < height; y++) for(x = 0; x < width; x++) i->buffer[x + y * i->pitch] = y < height / 2 ? 0x00 : 0xff; if(height & 1) for(x = 0; x < width; x++) i->buffer[x + (height / 2) * i->pitch] = 0x7f; break; case 0x0000258c: /* ▌ */ for(y = 0; y < height; y++) for(x = 0; x < width; x++) i->buffer[x + y * i->pitch] = x < width / 2 ? 0xff : 0x00; if(width & 1) for(y = 0; y < height; y++) i->buffer[(width / 2) + y * i->pitch] = 0x7f; break; case 0x00002590: /* ▐ */ for(y = 0; y < height; y++) for(x = 0; x < width; x++) i->buffer[x + y * i->pitch] = x < width / 2 ? 0x00 : 0xff; if(width & 1) for(y = 0; y < height; y++) i->buffer[(width / 2) + y * i->pitch] = 0x7f; break; case 0x000025a0: /* ■ */ for(y = 0; y < height; y++) for(x = 0; x < width; x++) i->buffer[x + y * i->pitch] = (y >= height / 4) && (y < 3 * height / 4) ? 0xff : 0x00; if(height & 3) for(x = 0; x < width; x++) /* FIXME: could be more precise */ i->buffer[x + (height / 4) * i->pitch] = i->buffer[x + (3 * height / 4) * i->pitch] = 0x7f; break; case 0x00002588: /* █ */ memset(i->buffer, 0xff, height * i->pitch); break; case 0x00002593: /* ▓ */ for(y = 0; y < height; y++) for(x = 0; x < width; x++) i->buffer[x + y * i->pitch] = ((x + 2 * (y & 1)) & 3) ? 0xff : 0x00; break; case 0x00002592: /* ▒ */ for(y = 0; y < height; y++) for(x = 0; x < width; x++) i->buffer[x + y * i->pitch] = ((x + y) & 1) ? 0xff : 0x00; break; case 0x00002591: /* ░ */ for(y = 0; y < height; y++) for(x = 0; x < width; x++) i->buffer[x + y * i->pitch] = ((x + 2 * (y & 1)) & 3) ? 0x00 : 0xff; break; } } static int printf_unicode(struct glyph *g) { int wr = 0; wr += printf("U+%.04X: \"", g->unicode); if(g->unicode < 0x20 || (g->unicode >= 0x7f && g->unicode <= 0xa0)) wr += printf("\\x%.02x\"", g->unicode); else wr += printf("%s\"", g->buf); return wr; } static int printf_u32(char const *fmt, uint32_t i) { uint32_t ni = hton32(i); return printf_hex(fmt, (uint8_t *)&ni, 4); } static int printf_u16(char const *fmt, uint16_t i) { uint16_t ni = hton16(i); return printf_hex(fmt, (uint8_t *)&ni, 2); } static int printf_hex(char const *fmt, uint8_t *data, int bytes) { char buf[BUFSIZ]; char *parser = buf; while(bytes--) parser += sprintf(parser, "%i,", (unsigned int)*data++); parser[0] = '\0'; return printf(fmt, buf); }