hackthelobby/libcaca/tools/makefont.c

475 lines
14 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* makefont create libcaca font data
* Copyright © 2006—2021 Sam Hocevar <sam@hocevar.net>
* 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 <prefix> <font> <dpi> <bpp>
*/
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#if defined HAVE_ARPA_INET_H
# include <arpa/inet.h>
#elif defined HAVE_NETINET_IN_H
# include <netinet/in.h>
#endif
#include <pango/pango.h>
#include <pango/pangoft2.h>
#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: , , , , , */
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 <prefix> <font> <dpi> <bpp>\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(&gtab[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(&gtab[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);
}