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

Last change on this file since 960 was 960, checked in by Sam Hocevar, 14 years ago
  • Elite trick in the font generator to cope with C89's string length limitation of 509 characters. Instead of doing:

char foo[10] = "abcdefghij";

we now do:

struct { char f1[4], f2[4], f3[2]; } foo = { "abcd", "efgh", "ij" };

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