475 lines
14 KiB
C
475 lines
14 KiB
C
/*
|
||
* 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: 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 <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(>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);
|
||
}
|
||
|