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

Last change on this file since 2821 was 2821, checked in by Sam Hocevar, 11 years ago

Starting refactoring to get rid of libcucul. The initial reason for the
split is rendered moot by the plugin system: when enabled, binaries do
not link directly with libX11 or libGL. I hope this is a step towards
more consisteny and clarity.

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