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

Last change on this file since 4787 was 4644, checked in by Sam Hocevar, 12 years ago

Mark the font data as const. We no longer modify it in place.

  • Property svn:keywords set to Id
File size: 14.0 KB
Line 
1/*
2 *  makefont      create libcaca font data
3 *  Copyright (c) 2006-2010 Sam Hocevar <sam@hocevar.net>
4 *                All Rights Reserved
5 *
6 *  This program is free software. It comes without any warranty, to
7 *  the extent permitted by applicable law. You can redistribute it
8 *  and/or modify it under the terms of the Do What The Fuck You Want
9 *  To Public License, Version 2, as published by Sam Hocevar. See
10 *  http://sam.zoy.org/wtfpl/COPYING for more details.
11 *
12 * Usage:
13 *   makefont <prefix> <font> <dpi> <bpp>
14 */
15
16#include "config.h"
17
18#include <stdio.h>
19#include <stdlib.h>
20#include <stdint.h>
21
22#if defined HAVE_ARPA_INET_H
23#   include <arpa/inet.h>
24#elif defined HAVE_NETINET_IN_H
25#   include <netinet/in.h>
26#endif
27
28#include <pango/pango.h>
29#include <pango/pangoft2.h>
30
31#include "caca_stubs.h"
32#include "caca.h"
33
34/* Split our big strings into chunks of 480 characters, because it is
35 * the multiple of 32 directly below 509, which is the maximum allowed
36 * string size in C89. */
37#define STRING_CHUNKS 480
38
39/* This list is built so that it includes all of ASCII, Latin-1, CP-437,
40 * and the UTF-8 glyphs necessary for canvas rotation and mirroring. */
41static unsigned int const blocklist[] =
42{
43    0x0000, 0x0080, /* Basic latin: A, B, C, a, b, c */
44    0x0080, 0x0100, /* Latin-1 Supplement: Ä, Ç, å, ß */
45    0x0100, 0x0180, /* Latin Extended-A: Ā č Ō œ */
46    0x0180, 0x0250, /* Latin Extended-B: Ǝ Ƹ */
47    0x0250, 0x02b0, /* IPA Extensions: ɐ ɔ ɘ ʌ ʍ */
48    0x0370, 0x0400, /* Greek and Coptic: Λ α β */
49    0x0400, 0x0500, /* Cyrillic: И Я */
50    0x0530, 0x0590, /* Armenian: Ո */
51    0x1401, 0x1677, /* Unified Canadian Aboriginal Syllabics: ᒐ ᗡ */
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    0x10400, 0x10450, /* Deseret: 𐐒 𐐋 */
66    0, 0
67};
68
69struct glyph
70{
71    uint32_t unicode;
72    char buf[10];
73    unsigned int same_as;
74    unsigned int data_offset;
75    unsigned int data_width;
76    unsigned int data_size;
77};
78
79static void fix_glyph(FT_Bitmap *, uint32_t, unsigned int, unsigned int);
80static int printf_unicode(struct glyph *);
81static int printf_hex(char const *, uint8_t *, int);
82static int printf_u32(char const *, uint32_t);
83static int printf_u16(char const *, uint16_t);
84
85/* Counter for written bytes */
86static int written = 0;
87
88int main(int argc, char *argv[])
89{
90    PangoContext *cx;
91    PangoFontDescription *fd;
92    PangoFontMap *fm;
93    PangoLayout *l;
94    PangoRectangle r;
95
96    FT_Bitmap img;
97    int stdwidth, fullwidth, height, blocks, glyphs, fullglyphs;
98    unsigned int n, b, i;
99    unsigned int stdsize, fullsize, control_size, data_size, current_offset;
100    uint8_t *glyph_data;
101    struct glyph *gtab;
102
103    unsigned int bpp, dpi;
104    char const *prefix, *font;
105
106    if(argc != 5)
107    {
108        fprintf(stderr, "%s: wrong argument count\n", argv[0]);
109        fprintf(stderr, "usage: %s <prefix> <font> <dpi> <bpp>\n", argv[0]);
110        fprintf(stderr, "eg: %s monospace9 \"Monospace 9\" 96 4\n", argv[0]);
111        return -1;
112    }
113
114    prefix = argv[1];
115    font = argv[2];
116    dpi = atoi(argv[3]);
117    bpp = atoi(argv[4]);
118
119    if(dpi == 0 || (bpp != 1 && bpp != 2 && bpp != 4 && bpp != 8))
120    {
121        fprintf(stderr, "%s: invalid argument\n", argv[0]);
122        return -1;
123    }
124
125    fprintf(stderr, "Font \"%s\", %i dpi, %i bpp\n", font, dpi, bpp);
126
127    /* Initialise Pango */
128    fm = pango_ft2_font_map_new();
129    pango_ft2_font_map_set_resolution(PANGO_FT2_FONT_MAP(fm), dpi, dpi);
130    cx = pango_ft2_font_map_create_context(PANGO_FT2_FONT_MAP(fm));
131
132    l = pango_layout_new(cx);
133    if(!l)
134    {
135        fprintf(stderr, "%s: unable to initialise pango\n", argv[0]);
136        g_object_unref(cx);
137        return -1;
138    }
139
140    fd = pango_font_description_from_string(font);
141    pango_layout_set_font_description(l, fd);
142    pango_font_description_free(fd);
143
144    /* Initialise our FreeType2 bitmap */
145    img.width = 256;
146    img.pitch = 256;
147    img.rows = 256;
148    img.buffer = malloc(256 * 256);
149    img.num_grays = 256;
150    img.pixel_mode = ft_pixel_mode_grays;
151
152    /* Test rendering so that we know the glyph width */
153    pango_layout_set_markup(l, "@", -1);
154    pango_layout_get_extents(l, NULL, &r);
155    stdwidth = PANGO_PIXELS(r.width);
156    fullwidth = stdwidth * 2;
157    height = PANGO_PIXELS(r.height);
158    stdsize = ((stdwidth * height) + (8 / bpp) - 1) / (8 / bpp);
159    fullsize = ((fullwidth * height) + (8 / bpp) - 1) / (8 / bpp);
160
161    /* Compute blocks and glyphs count */
162    blocks = 0;
163    glyphs = 0;
164    fullglyphs = 0;
165    for(b = 0; blocklist[b + 1]; b += 2)
166    {
167        blocks++;
168        glyphs += blocklist[b + 1] - blocklist[b];
169        for(i = blocklist[b]; i < blocklist[b + 1]; i++)
170            if(caca_utf32_is_fullwidth(i))
171                fullglyphs++;
172    }
173
174    control_size = 28 + 12 * blocks + 8 * glyphs;
175    data_size = stdsize * (glyphs - fullglyphs) + fullsize * fullglyphs;
176
177    gtab = malloc(glyphs * sizeof(struct glyph));
178    glyph_data = malloc(data_size);
179
180    /* Let's go! */
181    printf("/* libcaca font file\n");
182    printf(" * \"%s\": %i dpi, %i bpp, %ix%i/%ix%i glyphs\n",
183           font, dpi, bpp, stdwidth, height, fullwidth, height);
184    printf(" * Automatically generated by tools/makefont.c:\n");
185    printf(" *   tools/makefont %s \"%s\" %i %i\n", prefix, font, dpi, bpp);
186    printf(" */\n");
187    printf("\n");
188
189    printf("static size_t const %s_size = %i;\n",
190           prefix, 4 + control_size + data_size);
191    printf("static uint8_t const %s_data[%i] =\n",
192           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 = hton32(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 = hton16(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.