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

Last change on this file since 1271 was 1271, checked in by Sam Hocevar, 14 years ago
  • Slightly changed the font header information. No one distributes libcaca fonts yet anyway :-)
  • Property svn:keywords set to Id
File size: 13.5 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 1271 2006-10-31 17:03:21Z sam $
7 *
8 *  This program is free software; you can redistribute it and/or
9 *  modify it under the terms of the Do What The Fuck You Want To
10 *  Public License, Version 2, as published by Sam Hocevar. See
11 *  http://sam.zoy.org/wtfpl/COPYING for more details.
12 *
13 * Usage:
14 *   makefont <prefix> <font> <dpi> <bpp>
15 */
16
17#include "config.h"
18#include "common.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 "cucul.h"
31
32#include <pango/pango.h>
33#include <pango/pangoft2.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    0, 0
60};
61
62struct glyph
63{
64    uint32_t unicode;
65    char buf[10];
66    unsigned int same_as;
67    unsigned int data_index;
68};
69
70static void fix_glyph(FT_Bitmap *, uint32_t, unsigned int, unsigned int);
71static int printf_unicode(struct glyph *);
72static int printf_hex(char const *, uint8_t *, int);
73static int printf_u32(char const *, uint32_t);
74static int printf_u16(char const *, uint16_t);
75
76/* Counter for written bytes */
77static int written = 0;
78
79int main(int argc, char *argv[])
80{
81    PangoContext *cx;
82    PangoFontDescription *fd;
83    PangoFontMap *fm;
84    PangoLayout *l;
85    PangoRectangle r;
86
87    FT_Bitmap img;
88    int width, height, blocks, glyphs;
89    unsigned int n, b, i, index, glyph_size, control_size, data_size;
90    uint8_t *glyph_data;
91    struct glyph *gtab;
92
93    unsigned int bpp, dpi;
94    char const *prefix, *font;
95
96    if(argc != 5)
97    {
98        fprintf(stderr, "%s: wrong argument count\n", argv[0]);
99        fprintf(stderr, "usage: %s <prefix> <font> <dpi> <bpp>\n", argv[0]);
100        fprintf(stderr, "eg: %s monospace9 \"Monospace 9\" 96 4\n", argv[0]);
101        return -1;
102    }
103
104    prefix = argv[1];
105    font = argv[2];
106    dpi = atoi(argv[3]);
107    bpp = atoi(argv[4]);
108
109    if(dpi == 0 || (bpp != 1 && bpp != 2 && bpp != 4 && bpp != 8))
110    {
111        fprintf(stderr, "%s: invalid argument\n", argv[0]);
112        return -1;
113    }
114
115    fprintf(stderr, "Font \"%s\", %i dpi, %i bpp\n", font, dpi, bpp);
116
117    /* Initialise Pango */
118    fm = pango_ft2_font_map_new();
119    pango_ft2_font_map_set_resolution(PANGO_FT2_FONT_MAP(fm), dpi, dpi);
120    cx = pango_ft2_font_map_create_context(PANGO_FT2_FONT_MAP(fm));
121
122    l = pango_layout_new(cx);
123    if(!l)
124    {
125        fprintf(stderr, "%s: unable to initialise pango\n", argv[0]);
126        g_object_unref(cx);
127        return -1;
128    }
129
130    fd = pango_font_description_from_string(font);
131    pango_layout_set_font_description(l, fd);
132    pango_font_description_free(fd);
133
134    /* Initialise our FreeType2 bitmap */
135    img.width = 256;
136    img.pitch = 256;
137    img.rows = 256;
138    img.buffer = malloc(256 * 256);
139    img.num_grays = 256;
140    img.pixel_mode = ft_pixel_mode_grays;
141
142    /* Test rendering so that we know the glyph width */
143    pango_layout_set_markup(l, "@", -1);
144    pango_layout_get_extents(l, NULL, &r);
145    width = PANGO_PIXELS(r.width);
146    height = PANGO_PIXELS(r.height);
147    glyph_size = ((width * height) + (8 / bpp) - 1) / (8 / bpp);
148
149    /* Compute blocks and glyphs count */
150    blocks = 0;
151    glyphs = 0;
152    for(b = 0; blocklist[b + 1]; b += 2)
153    {
154        blocks++;
155        glyphs += blocklist[b + 1] - blocklist[b];
156    }
157
158    control_size = 24 + 12 * blocks + 8 * glyphs;
159    data_size = glyph_size * glyphs;
160
161    gtab = malloc(glyphs * sizeof(struct glyph));
162    glyph_data = malloc(data_size);
163
164    /* Let's go! */
165    printf("/* libcucul font file\n");
166    printf(" * \"%s\": %i dpi, %i bpp, %ix%i glyphs\n",
167           font, dpi, bpp, width, height);
168    printf(" * Automatically generated by tools/makefont.c:\n");
169    printf(" *   tools/makefont %s \"%s\" %i %i\n", prefix, font, dpi, bpp);
170    printf(" */\n");
171    printf("\n");
172
173    printf("static unsigned int const %s_size = %i;\n",
174           prefix, 4 + control_size + data_size);
175    printf("static struct {\n");
176    printf("char ");
177    for(i = 0; (i + 1) * STRING_CHUNKS < 8 + control_size + data_size; i++)
178        printf("d%x[%i],%c", i, STRING_CHUNKS, (i % 6) == 5 ? '\n' : ' ');
179    printf("d%x[%i];\n", i, 4 + control_size + data_size - i * STRING_CHUNKS);
180    printf("} %s_data = {\n", prefix);
181    printf("\n");
182
183    printf("/* file: */\n");
184    printf("\"\\xCA\\xCA\" /* caca_header */\n");
185    written += 2;
186    printf("\"FT\" /* caca_file_type */\n");
187    written += 2;
188    printf("\n");
189
190    printf("/* font_header: */\n");
191    printf_u32("\"%s\" /* control_size */\n", control_size);
192    printf_u32("\"%s\" /* data_size */\n", data_size);
193    printf_u16("\"%s\" /* version */\n", 1);
194    printf_u16("\"%s\" /* blocks */\n", blocks);
195    printf_u32("\"%s\" /* glyphs */\n", glyphs);
196    printf_u16("\"%s\" /* bpp */\n", bpp);
197    printf_u16("\"%s\" /* width */\n", width);
198    printf_u16("\"%s\" /* height */\n", height);
199    printf_u16("\"%s\" /* flags */\n", 1);
200    printf("\n");
201
202    printf("/* block_info: */\n");
203    n = 0;
204    for(b = 0; blocklist[b + 1]; b += 2)
205    {
206        printf_u32("\"%s", blocklist[b]);
207        printf_u32("%s", blocklist[b + 1]);
208        printf_u32("%s\"\n", n);
209        n += blocklist[b + 1] - blocklist[b];
210    }
211    printf("\n");
212
213    /* Render all glyphs, so that we can know their offset */
214    n = index = 0;
215    for(b = 0; blocklist[b + 1]; b += 2)
216    {
217        for(i = blocklist[b]; i < blocklist[b + 1]; i++)
218        {
219            int x, y, bytes;
220            unsigned int k;
221
222            gtab[n].unicode = i;
223            bytes = cucul_utf32_to_utf8(gtab[n].buf, gtab[n].unicode);
224            gtab[n].buf[bytes] = '\0';
225
226            /* Render glyph on a bitmap */
227            pango_layout_set_text(l, gtab[n].buf, -1);
228            memset(img.buffer, 0, img.pitch * height);
229            pango_ft2_render_layout(&img, l, 0, 0);
230
231            /* Fix glyphs that we know how to handle better */
232            fix_glyph(&img, gtab[n].unicode, width, height);
233
234            /* Write bitmap as an escaped C string */
235            memset(glyph_data + n * glyph_size, 0, glyph_size);
236            k = 0;
237            for(y = 0; y < height; y++)
238            {
239                for(x = 0; x < width; x++)
240                {
241                    uint8_t pixel = img.buffer[y * img.pitch + x];
242
243                    pixel >>= (8 - bpp);
244                    glyph_data[n * glyph_size + k / 8]
245                        |= pixel << (8 - bpp - (k % 8));
246                    k += bpp;
247                }
248            }
249
250            /* Check whether this is the same glyph as another one. Please
251             * don't bullshit me about sorting, hashing and stuff like that,
252             * our data is small enough for this to work. */
253            for(k = 0; k < n; k++)
254            {
255                if(!memcmp(glyph_data + k * glyph_size,
256                           glyph_data + n * glyph_size, glyph_size))
257                    break;
258            }
259
260            gtab[n].data_index = index;
261            gtab[n].same_as = k;
262
263            if(k == n)
264                index++;
265
266            n++;
267        }
268    }
269
270    printf("/* glyph_info: */\n");
271    n = 0;
272    for(b = 0; blocklist[b + 1]; b += 2)
273    {
274        for(i = blocklist[b]; i < blocklist[b + 1]; i++)
275        {
276            printf_u16("\"%s", width);
277            printf_u16("%s", height);
278            printf_u32("%s\"\n", gtab[gtab[n].same_as].data_index * glyph_size);
279            n++;
280        }
281    }
282    printf("\n");
283
284    printf("/* font_data: */\n");
285    n = 0;
286    for(b = 0; blocklist[b + 1]; b += 2)
287    {
288        for(i = blocklist[b]; i < blocklist[b + 1]; i++)
289        {
290            /* Print glyph value in comment */
291            printf("/* ");
292            printf_unicode(&gtab[n]);
293
294            if(gtab[n].same_as == n)
295                printf_hex(" */ \"%s\"\n",
296                           glyph_data + n * glyph_size, glyph_size);
297            else
298            {
299                printf(" is ");
300                printf_unicode(&gtab[gtab[n].same_as]);
301                printf(" */\n");
302            }
303
304            n++;
305        }
306    }
307
308    printf("};\n");
309
310    free(img.buffer);
311    free(gtab);
312    free(glyph_data);
313    g_object_unref(l);
314    g_object_unref(cx);
315
316    return 0;
317}
318
319/*
320 * XXX: the following functions are local
321 */
322
323static void fix_glyph(FT_Bitmap *i, uint32_t ch,
324                      unsigned int width, unsigned int height)
325{
326    unsigned int x, y;
327
328    switch(ch)
329    {
330    case 0x00002580: /* ▀ */
331        for(y = 0; y < height; y++)
332            for(x = 0; x < width; x++)
333                i->buffer[x + y * i->pitch] = y < height / 2 ? 0xff : 0x00;
334        if(height & 1)
335            for(x = 0; x < width; x++)
336                i->buffer[x + (height / 2) * i->pitch] = 0x7f;
337        break;
338    case 0x00002584: /* ▄ */
339        for(y = 0; y < height; y++)
340            for(x = 0; x < width; x++)
341                i->buffer[x + y * i->pitch] = y < height / 2 ? 0x00 : 0xff;
342        if(height & 1)
343            for(x = 0; x < width; x++)
344                i->buffer[x + (height / 2) * i->pitch] = 0x7f;
345        break;
346    case 0x0000258c: /* ▌ */
347        for(y = 0; y < height; y++)
348            for(x = 0; x < width; x++)
349                i->buffer[x + y * i->pitch] = x < width / 2 ? 0xff : 0x00;
350        if(width & 1)
351            for(y = 0; y < height; y++)
352                i->buffer[(width / 2) + y * i->pitch] = 0x7f;
353        break;
354    case 0x00002590: /* ▐ */
355        for(y = 0; y < height; y++)
356            for(x = 0; x < width; x++)
357                i->buffer[x + y * i->pitch] = x < width / 2 ? 0x00 : 0xff;
358        if(width & 1)
359            for(y = 0; y < height; y++)
360                i->buffer[(width / 2) + y * i->pitch] = 0x7f;
361        break;
362    case 0x000025a0: /* ■ */
363        for(y = 0; y < height; y++)
364            for(x = 0; x < width; x++)
365                i->buffer[x + y * i->pitch] =
366                    (y >= height / 4) && (y < 3 * height / 4) ? 0xff : 0x00;
367        if(height & 3)
368            for(x = 0; x < width; x++) /* FIXME: could be more precise */
369                i->buffer[x + (height / 4) * i->pitch] =
370                    i->buffer[x + (3 * height / 4) * i->pitch] = 0x7f;
371        break;
372    case 0x00002588: /* █ */
373        memset(i->buffer, 0xff, height * i->pitch);
374        break;
375    case 0x00002593: /* ▓ */
376        for(y = 0; y < height; y++)
377            for(x = 0; x < width; x++)
378                i->buffer[x + y * i->pitch] =
379                    ((x + 2 * (y & 1)) & 3) ? 0xff : 0x00;
380        break;
381    case 0x00002592: /* ▒ */
382        for(y = 0; y < height; y++)
383            for(x = 0; x < width; x++)
384                i->buffer[x + y * i->pitch] = ((x + y) & 1) ? 0xff : 0x00;
385        break;
386    case 0x00002591: /* ░ */
387        for(y = 0; y < height; y++)
388            for(x = 0; x < width; x++)
389                i->buffer[x + y * i->pitch] =
390                    ((x + 2 * (y & 1)) & 3) ? 0x00 : 0xff;
391        break;
392    }
393}
394
395static int printf_unicode(struct glyph *g)
396{
397    int written = 0;
398
399    written += printf("U+%.04X: \"", g->unicode);
400
401    if(g->unicode < 0x20 || (g->unicode >= 0x7f && g->unicode <= 0xa0))
402        written += printf("\\x%.02x\"", g->unicode);
403    else
404        written += printf("%s\"", g->buf);
405
406    return written;
407}
408
409static int printf_u32(char const *fmt, uint32_t i)
410{
411    uint32_t ni = hton32(i);
412    return printf_hex(fmt, (uint8_t *)&ni, 4);
413}
414
415static int printf_u16(char const *fmt, uint16_t i)
416{
417    uint16_t ni = hton16(i);
418    return printf_hex(fmt, (uint8_t *)&ni, 2);
419}
420
421static int printf_hex(char const *fmt, uint8_t *data, int bytes)
422{
423    char buf[BUFSIZ];
424    char *parser = buf;
425    int rewind = 0; /* we use this variable to rewind 2 bytes after \000
426                     * was printed when the next char starts with "\", too. */
427
428    while(bytes--)
429    {
430        uint8_t ch = *data++;
431
432        if(written == STRING_CHUNKS)
433        {
434            parser += sprintf(parser, "\", \"");
435            written = 0;
436            rewind = 0;
437        }
438
439        if(ch == '\\' || ch == '"')
440        {
441            parser -= rewind;
442            parser += sprintf(parser, "\\%c", ch);
443            rewind = 0;
444        }
445        else if(ch >= 0x20 && ch < 0x7f)
446        {
447            parser += sprintf(parser, "%c", ch);
448            rewind = 0;
449        }
450        else
451        {
452            parser -= rewind;
453            parser += sprintf(parser, "\\%.03o", ch);
454            rewind = ch ? 0 : 2;
455        }
456
457        written++;
458    }
459
460    parser -= rewind;
461    parser[0] = '\0';
462
463    return printf(fmt, buf);
464}
465
Note: See TracBrowser for help on using the repository browser.