source: libcaca/trunk/tools/makefont.c @ 3407

Last change on this file since 3407 was 3407, checked in by sam, 6 years ago

Added new Unicode blocks to the built-in font.

  • Property svn:keywords set to Id
File size: 14.0 KB
Line 
1/*
2 *  makefont       create libcaca font data
3 *  Copyright (c) 2006 Sam Hocevar <sam@zoy.org>
4 *                All Rights Reserved
5 *
6 *  $Id$
7 *
8 *  This program is free software. It comes without any warranty, to
9 *  the extent permitted by applicable law. You can redistribute it
10 *  and/or modify it under the terms of the Do What The Fuck You Want
11 *  To Public License, Version 2, as published by Sam Hocevar. See
12 *  http://sam.zoy.org/wtfpl/COPYING for more details.
13 *
14 * Usage:
15 *   makefont <prefix> <font> <dpi> <bpp>
16 */
17
18#include "config.h"
19
20#include <stdio.h>
21#include <stdlib.h>
22#include <stdint.h>
23
24#if defined HAVE_ARPA_INET_H
25#   include <arpa/inet.h>
26#elif defined HAVE_NETINET_IN_H
27#   include <netinet/in.h>
28#endif
29
30#include <pango/pango.h>
31#include <pango/pangoft2.h>
32
33#include "caca.h"
34
35/* Split our big strings into chunks of 480 characters, because it is
36 * the multiple of 32 directly below 509, which is the maximum allowed
37 * string size in C89. */
38#define STRING_CHUNKS 480
39
40/* This list is built so that it includes all of ASCII, Latin-1, CP-437,
41 * and the UTF-8 glyphs necessary for canvas rotation and mirroring. */
42static unsigned int const blocklist[] =
43{
44    0x0000, 0x0080, /* Basic latin: A, B, C, a, b, c */
45    0x0080, 0x0100, /* Latin-1 Supplement: Ä, Ç, å, ß */
46    0x0100, 0x0180, /* Latin Extended-A: Ā č Ō œ */
47    0x0180, 0x0250, /* Latin Extended-B: Ǝ Ƹ */
48    0x0250, 0x02b0, /* IPA Extensions: ɐ ɔ ɘ ʌ ʍ */
49    0x0370, 0x0400, /* Greek and Coptic: Λ α β */
50    0x0400, 0x0500, /* Cyrillic: И Я */
51    0x0530, 0x0590, /* Armenian: Ո */
52    0x1401, 0x1677, /* Unified Canadian Aboriginal Syllabics: ᒐ ᗡ */
53    0x1d00, 0x1d80, /* Phonetic Extensions: ᴉ ᵷ */
54    0x2000, 0x2070, /* General Punctuation: ‘’ “” */
55    0x2100, 0x2150, /* Letterlike Symbols: Ⅎ */
56    0x2200, 0x2300, /* Mathematical Operators: ∀ √ ∞ ∙ */
57    0x2300, 0x2400, /* Miscellaneous Technical: ⌐ ⌂ ⌠ ⌡ */
58    0x2500, 0x2580, /* Box Drawing: ═ ║ ╗ ╔ ╩ */
59    0x2580, 0x25a0, /* Block Elements: ▛ ▞ ░ ▒ ▓ */
60    0x25a0, 0x2600, /* Geometric Shapes: ◆ ○ ● */
61    0x2600, 0x2700, /* Miscellaneous Symbols: ♥ ★ ☭ */
62    0x3000, 0x3040, /* CJK Symbols and Punctuation: 。「」 */
63    0x3040, 0x30a0, /* Hiragana: で す */
64    0x30a0, 0x3100, /* Katakana: ロ ル */
65    0xff00, 0xfff0, /* Halfwidth and Fullwidth Forms: A, B, C, a, b, c */
66    0x10400, 0x10450, /* Deseret: 𐐒 𐐋 */
67    0, 0
68};
69
70struct glyph
71{
72    uint32_t unicode;
73    char buf[10];
74    unsigned int same_as;
75    unsigned int data_offset;
76    unsigned int data_width;
77    unsigned int data_size;
78};
79
80static void fix_glyph(FT_Bitmap *, uint32_t, unsigned int, unsigned int);
81static int printf_unicode(struct glyph *);
82static int printf_hex(char const *, uint8_t *, int);
83static int printf_u32(char const *, uint32_t);
84static int printf_u16(char const *, uint16_t);
85
86/* Counter for written bytes */
87static int written = 0;
88
89int main(int argc, char *argv[])
90{
91    PangoContext *cx;
92    PangoFontDescription *fd;
93    PangoFontMap *fm;
94    PangoLayout *l;
95    PangoRectangle r;
96
97    FT_Bitmap img;
98    int stdwidth, fullwidth, height, blocks, glyphs, fullglyphs;
99    unsigned int n, b, i;
100    unsigned int stdsize, fullsize, control_size, data_size, current_offset;
101    uint8_t *glyph_data;
102    struct glyph *gtab;
103
104    unsigned int bpp, dpi;
105    char const *prefix, *font;
106
107    if(argc != 5)
108    {
109        fprintf(stderr, "%s: wrong argument count\n", argv[0]);
110        fprintf(stderr, "usage: %s <prefix> <font> <dpi> <bpp>\n", argv[0]);
111        fprintf(stderr, "eg: %s monospace9 \"Monospace 9\" 96 4\n", argv[0]);
112        return -1;
113    }
114
115    prefix = argv[1];
116    font = argv[2];
117    dpi = atoi(argv[3]);
118    bpp = atoi(argv[4]);
119
120    if(dpi == 0 || (bpp != 1 && bpp != 2 && bpp != 4 && bpp != 8))
121    {
122        fprintf(stderr, "%s: invalid argument\n", argv[0]);
123        return -1;
124    }
125
126    fprintf(stderr, "Font \"%s\", %i dpi, %i bpp\n", font, dpi, bpp);
127
128    /* Initialise Pango */
129    fm = pango_ft2_font_map_new();
130    pango_ft2_font_map_set_resolution(PANGO_FT2_FONT_MAP(fm), dpi, dpi);
131    cx = pango_ft2_font_map_create_context(PANGO_FT2_FONT_MAP(fm));
132
133    l = pango_layout_new(cx);
134    if(!l)
135    {
136        fprintf(stderr, "%s: unable to initialise pango\n", argv[0]);
137        g_object_unref(cx);
138        return -1;
139    }
140
141    fd = pango_font_description_from_string(font);
142    pango_layout_set_font_description(l, fd);
143    pango_font_description_free(fd);
144
145    /* Initialise our FreeType2 bitmap */
146    img.width = 256;
147    img.pitch = 256;
148    img.rows = 256;
149    img.buffer = malloc(256 * 256);
150    img.num_grays = 256;
151    img.pixel_mode = ft_pixel_mode_grays;
152
153    /* Test rendering so that we know the glyph width */
154    pango_layout_set_markup(l, "@", -1);
155    pango_layout_get_extents(l, NULL, &r);
156    stdwidth = PANGO_PIXELS(r.width);
157    fullwidth = stdwidth * 2;
158    height = PANGO_PIXELS(r.height);
159    stdsize = ((stdwidth * height) + (8 / bpp) - 1) / (8 / bpp);
160    fullsize = ((fullwidth * height) + (8 / bpp) - 1) / (8 / bpp);
161
162    /* Compute blocks and glyphs count */
163    blocks = 0;
164    glyphs = 0;
165    fullglyphs = 0;
166    for(b = 0; blocklist[b + 1]; b += 2)
167    {
168        blocks++;
169        glyphs += blocklist[b + 1] - blocklist[b];
170        for(i = blocklist[b]; i < blocklist[b + 1]; i++)
171            if(caca_utf32_is_fullwidth(i))
172                fullglyphs++;
173    }
174
175    control_size = 28 + 12 * blocks + 8 * glyphs;
176    data_size = stdsize * (glyphs - fullglyphs) + fullsize * fullglyphs;
177
178    gtab = malloc(glyphs * sizeof(struct glyph));
179    glyph_data = malloc(data_size);
180
181    /* Let's go! */
182    printf("/* libcaca font file\n");
183    printf(" * \"%s\": %i dpi, %i bpp, %ix%i/%ix%i glyphs\n",
184           font, dpi, bpp, stdwidth, height, fullwidth, height);
185    printf(" * Automatically generated by tools/makefont.c:\n");
186    printf(" *   tools/makefont %s \"%s\" %i %i\n", prefix, font, dpi, bpp);
187    printf(" */\n");
188    printf("\n");
189
190    printf("static unsigned int const %s_size = %i;\n",
191           prefix, 4 + control_size + data_size);
192    printf("static char %s_data[%i] =\n", prefix, 4 + control_size + data_size);
193    printf("{\n");
194
195    printf("/* file: */\n");
196    printf("0xCA,0xCA, /* caca_header */\n");
197    written += 2;
198    printf("'F','T', /* caca_file_type */\n");
199    written += 2;
200    printf("\n");
201
202    printf("/* font_header: */\n");
203    printf_u32("%s /* control_size */\n", control_size);
204    printf_u32("%s /* data_size */\n", data_size);
205    printf_u16("%s /* version */\n", 1);
206    printf_u16("%s /* blocks */\n", blocks);
207    printf_u32("%s /* glyphs */\n", glyphs);
208    printf_u16("%s /* bpp */\n", bpp);
209    printf_u16("%s /* std width */\n", stdwidth);
210    printf_u16("%s /* std height */\n", height);
211    printf_u16("%s /* max width */\n", fullwidth);
212    printf_u16("%s /* max height */\n", height);
213    printf_u16("%s /* flags */\n", 1);
214    printf("\n");
215
216    printf("/* block_info: */\n");
217    n = 0;
218    for(b = 0; blocklist[b + 1]; b += 2)
219    {
220        printf_u32("%s", blocklist[b]);
221        printf_u32("%s", blocklist[b + 1]);
222        printf_u32("%s\n", n);
223        n += blocklist[b + 1] - blocklist[b];
224    }
225    printf("\n");
226
227    /* Render all glyphs, so that we can know their offset */
228    current_offset = n = 0;
229    for(b = 0; blocklist[b + 1]; b += 2)
230    {
231        for(i = blocklist[b]; i < blocklist[b + 1]; i++)
232        {
233            int x, y, bytes, current_width = stdwidth;
234            unsigned int k, current_size = stdsize;
235
236            if(caca_utf32_is_fullwidth(i))
237            {
238                current_width = fullwidth;
239                current_size = fullsize;
240            }
241            gtab[n].unicode = i;
242            bytes = caca_utf32_to_utf8(gtab[n].buf, gtab[n].unicode);
243            gtab[n].buf[bytes] = '\0';
244
245            /* Render glyph on a bitmap */
246            pango_layout_set_text(l, gtab[n].buf, -1);
247            memset(img.buffer, 0, img.pitch * height);
248            pango_ft2_render_layout(&img, l, 0, 0);
249
250            /* Fix glyphs that we know how to handle better */
251            fix_glyph(&img, gtab[n].unicode, current_width, height);
252
253            /* Write bitmap as an escaped C string */
254            memset(glyph_data + current_offset, 0, current_size);
255            k = 0;
256            for(y = 0; y < height; y++)
257            {
258                for(x = 0; x < current_width; x++)
259                {
260                    uint8_t pixel = img.buffer[y * img.pitch + x];
261
262                    pixel >>= (8 - bpp);
263                    glyph_data[current_offset + k / 8]
264                        |= pixel << (8 - bpp - (k % 8));
265                    k += bpp;
266                }
267            }
268
269            /* Check whether this is the same glyph as another one. Please
270             * don't bullshit me about sorting, hashing and stuff like that,
271             * our data is small enough for this to work. */
272            for(k = 0; k < n; k++)
273            {
274                if(gtab[k].data_size != current_size)
275                    continue;
276#if 0
277                if(!memcmp(glyph_data + gtab[k].data_offset,
278                           glyph_data + current_offset, current_size))
279                    break;
280#endif
281            }
282
283            gtab[n].data_offset = current_offset;
284            gtab[n].data_width = current_width;
285            gtab[n].data_size = current_size;
286            gtab[n].same_as = k;
287
288            if(k == n)
289                current_offset += current_size;
290
291            n++;
292        }
293    }
294
295    printf("/* glyph_info: */\n");
296    n = 0;
297    for(b = 0; blocklist[b + 1]; b += 2)
298    {
299        for(i = blocklist[b]; i < blocklist[b + 1]; i++)
300        {
301            printf_u16("%s", gtab[n].data_width);
302            printf_u16("%s", height);
303            printf_u32("%s\n", gtab[gtab[n].same_as].data_offset);
304            n++;
305        }
306    }
307    printf("\n");
308
309    printf("/* font_data: */\n");
310    n = 0;
311    for(b = 0; blocklist[b + 1]; b += 2)
312    {
313        for(i = blocklist[b]; i < blocklist[b + 1]; i++)
314        {
315            /* Print glyph value in comment */
316            printf("/* ");
317            printf_unicode(&gtab[n]);
318
319            if(gtab[n].same_as == n)
320                printf_hex(" */ %s\n",
321                           glyph_data + gtab[n].data_offset, gtab[n].data_size);
322            else
323            {
324                printf(" is ");
325                printf_unicode(&gtab[gtab[n].same_as]);
326                printf(" */\n");
327            }
328
329            n++;
330        }
331    }
332
333    printf("};\n");
334
335    free(img.buffer);
336    free(gtab);
337    free(glyph_data);
338    g_object_unref(l);
339    g_object_unref(cx);
340
341    return 0;
342}
343
344/*
345 * XXX: the following functions are local
346 */
347
348static void fix_glyph(FT_Bitmap *i, uint32_t ch,
349                      unsigned int width, unsigned int height)
350{
351    unsigned int x, y;
352
353    switch(ch)
354    {
355    case 0x00002580: /* ▀ */
356        for(y = 0; y < height; y++)
357            for(x = 0; x < width; x++)
358                i->buffer[x + y * i->pitch] = y < height / 2 ? 0xff : 0x00;
359        if(height & 1)
360            for(x = 0; x < width; x++)
361                i->buffer[x + (height / 2) * i->pitch] = 0x7f;
362        break;
363    case 0x00002584: /* ▄ */
364        for(y = 0; y < height; y++)
365            for(x = 0; x < width; x++)
366                i->buffer[x + y * i->pitch] = y < height / 2 ? 0x00 : 0xff;
367        if(height & 1)
368            for(x = 0; x < width; x++)
369                i->buffer[x + (height / 2) * i->pitch] = 0x7f;
370        break;
371    case 0x0000258c: /* ▌ */
372        for(y = 0; y < height; y++)
373            for(x = 0; x < width; x++)
374                i->buffer[x + y * i->pitch] = x < width / 2 ? 0xff : 0x00;
375        if(width & 1)
376            for(y = 0; y < height; y++)
377                i->buffer[(width / 2) + y * i->pitch] = 0x7f;
378        break;
379    case 0x00002590: /* ▐ */
380        for(y = 0; y < height; y++)
381            for(x = 0; x < width; x++)
382                i->buffer[x + y * i->pitch] = x < width / 2 ? 0x00 : 0xff;
383        if(width & 1)
384            for(y = 0; y < height; y++)
385                i->buffer[(width / 2) + y * i->pitch] = 0x7f;
386        break;
387    case 0x000025a0: /* ■ */
388        for(y = 0; y < height; y++)
389            for(x = 0; x < width; x++)
390                i->buffer[x + y * i->pitch] =
391                    (y >= height / 4) && (y < 3 * height / 4) ? 0xff : 0x00;
392        if(height & 3)
393            for(x = 0; x < width; x++) /* FIXME: could be more precise */
394                i->buffer[x + (height / 4) * i->pitch] =
395                    i->buffer[x + (3 * height / 4) * i->pitch] = 0x7f;
396        break;
397    case 0x00002588: /* █ */
398        memset(i->buffer, 0xff, height * i->pitch);
399        break;
400    case 0x00002593: /* ▓ */
401        for(y = 0; y < height; y++)
402            for(x = 0; x < width; x++)
403                i->buffer[x + y * i->pitch] =
404                    ((x + 2 * (y & 1)) & 3) ? 0xff : 0x00;
405        break;
406    case 0x00002592: /* ▒ */
407        for(y = 0; y < height; y++)
408            for(x = 0; x < width; x++)
409                i->buffer[x + y * i->pitch] = ((x + y) & 1) ? 0xff : 0x00;
410        break;
411    case 0x00002591: /* ░ */
412        for(y = 0; y < height; y++)
413            for(x = 0; x < width; x++)
414                i->buffer[x + y * i->pitch] =
415                    ((x + 2 * (y & 1)) & 3) ? 0x00 : 0xff;
416        break;
417    }
418}
419
420static int printf_unicode(struct glyph *g)
421{
422    int wr = 0;
423
424    wr += printf("U+%.04X: \"", g->unicode);
425
426    if(g->unicode < 0x20 || (g->unicode >= 0x7f && g->unicode <= 0xa0))
427        wr += printf("\\x%.02x\"", g->unicode);
428    else
429        wr += printf("%s\"", g->buf);
430
431    return wr;
432}
433
434static int printf_u32(char const *fmt, uint32_t i)
435{
436    uint32_t ni = htonl(i);
437    return printf_hex(fmt, (uint8_t *)&ni, 4);
438}
439
440static int printf_u16(char const *fmt, uint16_t i)
441{
442    uint16_t ni = htons(i);
443    return printf_hex(fmt, (uint8_t *)&ni, 2);
444}
445
446static int printf_hex(char const *fmt, uint8_t *data, int bytes)
447{
448    char buf[BUFSIZ];
449    char *parser = buf;
450
451    while(bytes--)
452        parser += sprintf(parser, "%i,", (unsigned int)*data++);
453    parser[0] = '\0';
454
455    return printf(fmt, buf);
456}
457
Note: See TracBrowser for help on using the repository browser.