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

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