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

Last change on this file since 1768 was 1768, checked in by Sam Hocevar, 12 years ago
  • Added glyphs to our default fonts: + Geometric Shapes, Halfwidth and Fullwidth Forms (as suggested by Ben

Wiley Sittler)

+ Miscellaneous Symbols (as suggested by Mohammed Adnène Trojette)

  • Property svn:keywords set to Id
File size: 14.9 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 1768 2007-06-27 12:48:34Z 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#include "common.h"
20
21#include <stdio.h>
22#include <stdlib.h>
23#include <stdint.h>
24
25#if defined(HAVE_ARPA_INET_H)
26#   include <arpa/inet.h>
27#elif defined(HAVE_NETINET_IN_H)
28#   include <netinet/in.h>
29#endif
30
31#include "cucul.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    0x25a0, 0x2600, /* Geometric Shapes: ◆ ○ ● */
61    0x2600, 0x2700, /* Miscellaneous Symbols: ♥ ★ ☭ */
62    0x3000, 0x3040, /* CJK Symbols and Punctuation: 。「」 */
63    0x3040, 0x30a0, /* Hiragana: で す */
64    0x30a0, 0x3100, /* Katakana: ロ ル */
65    0xff00, 0xfff0, /* Halfwidth and Fullwidth Forms: A, B, C, a, b, c */
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(cucul_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("/* libcucul 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 unsigned int const %s_size = %i;\n",
190           prefix, 4 + control_size + data_size);
191    printf("static struct {\n");
192    printf("char ");
193    for(i = 0; (i + 1) * STRING_CHUNKS < 8 + control_size + data_size; i++)
194        printf("d%x[%i],%c", i, STRING_CHUNKS, (i % 6) == 5 ? '\n' : ' ');
195    printf("d%x[%i];\n", i, 4 + control_size + data_size - i * STRING_CHUNKS);
196    printf("} %s_data = {\n", prefix);
197    printf("\n");
198
199    printf("/* file: */\n");
200    printf("\"\\xCA\\xCA\" /* caca_header */\n");
201    written += 2;
202    printf("\"FT\" /* caca_file_type */\n");
203    written += 2;
204    printf("\n");
205
206    printf("/* font_header: */\n");
207    printf_u32("\"%s\" /* control_size */\n", control_size);
208    printf_u32("\"%s\" /* data_size */\n", data_size);
209    printf_u16("\"%s\" /* version */\n", 1);
210    printf_u16("\"%s\" /* blocks */\n", blocks);
211    printf_u32("\"%s\" /* glyphs */\n", glyphs);
212    printf_u16("\"%s\" /* bpp */\n", bpp);
213    printf_u16("\"%s\" /* std width */\n", stdwidth);
214    printf_u16("\"%s\" /* std height */\n", height);
215    printf_u16("\"%s\" /* max width */\n", fullwidth);
216    printf_u16("\"%s\" /* max height */\n", height);
217    printf_u16("\"%s\" /* flags */\n", 1);
218    printf("\n");
219
220    printf("/* block_info: */\n");
221    n = 0;
222    for(b = 0; blocklist[b + 1]; b += 2)
223    {
224        printf_u32("\"%s", blocklist[b]);
225        printf_u32("%s", blocklist[b + 1]);
226        printf_u32("%s\"\n", n);
227        n += blocklist[b + 1] - blocklist[b];
228    }
229    printf("\n");
230
231    /* Render all glyphs, so that we can know their offset */
232    current_offset = n = 0;
233    for(b = 0; blocklist[b + 1]; b += 2)
234    {
235        for(i = blocklist[b]; i < blocklist[b + 1]; i++)
236        {
237            int x, y, bytes, current_width = stdwidth;
238            unsigned int k, current_size = stdsize;
239
240            if(cucul_utf32_is_fullwidth(i))
241            {
242                current_width = fullwidth;
243                current_size = fullsize;
244            }
245            gtab[n].unicode = i;
246            bytes = cucul_utf32_to_utf8(gtab[n].buf, gtab[n].unicode);
247            gtab[n].buf[bytes] = '\0';
248
249            /* Render glyph on a bitmap */
250            pango_layout_set_text(l, gtab[n].buf, -1);
251            memset(img.buffer, 0, img.pitch * height);
252            pango_ft2_render_layout(&img, l, 0, 0);
253
254            /* Fix glyphs that we know how to handle better */
255            fix_glyph(&img, gtab[n].unicode, current_width, height);
256
257            /* Write bitmap as an escaped C string */
258            memset(glyph_data + current_offset, 0, current_size);
259            k = 0;
260            for(y = 0; y < height; y++)
261            {
262                for(x = 0; x < current_width; x++)
263                {
264                    uint8_t pixel = img.buffer[y * img.pitch + x];
265
266                    pixel >>= (8 - bpp);
267                    glyph_data[current_offset + k / 8]
268                        |= pixel << (8 - bpp - (k % 8));
269                    k += bpp;
270                }
271            }
272
273            /* Check whether this is the same glyph as another one. Please
274             * don't bullshit me about sorting, hashing and stuff like that,
275             * our data is small enough for this to work. */
276            for(k = 0; k < n; k++)
277            {
278                if(gtab[k].data_size != current_size)
279                    continue;
280#if 0
281                if(!memcmp(glyph_data + gtab[k].data_offset,
282                           glyph_data + current_offset, current_size))
283                    break;
284#endif
285            }
286
287            gtab[n].data_offset = current_offset;
288            gtab[n].data_width = current_width;
289            gtab[n].data_size = current_size;
290            gtab[n].same_as = k;
291
292            if(k == n)
293                current_offset += current_size;
294
295            n++;
296        }
297    }
298
299    printf("/* glyph_info: */\n");
300    n = 0;
301    for(b = 0; blocklist[b + 1]; b += 2)
302    {
303        for(i = blocklist[b]; i < blocklist[b + 1]; i++)
304        {
305            printf_u16("\"%s", gtab[n].data_width);
306            printf_u16("%s", height);
307            printf_u32("%s\"\n", gtab[gtab[n].same_as].data_offset);
308            n++;
309        }
310    }
311    printf("\n");
312
313    printf("/* font_data: */\n");
314    n = 0;
315    for(b = 0; blocklist[b + 1]; b += 2)
316    {
317        for(i = blocklist[b]; i < blocklist[b + 1]; i++)
318        {
319            /* Print glyph value in comment */
320            printf("/* ");
321            printf_unicode(&gtab[n]);
322
323            if(gtab[n].same_as == n)
324                printf_hex(" */ \"%s\"\n",
325                           glyph_data + gtab[n].data_offset, gtab[n].data_size);
326            else
327            {
328                printf(" is ");
329                printf_unicode(&gtab[gtab[n].same_as]);
330                printf(" */\n");
331            }
332
333            n++;
334        }
335    }
336
337    printf("};\n");
338
339    free(img.buffer);
340    free(gtab);
341    free(glyph_data);
342    g_object_unref(l);
343    g_object_unref(cx);
344
345    return 0;
346}
347
348/*
349 * XXX: the following functions are local
350 */
351
352static void fix_glyph(FT_Bitmap *i, uint32_t ch,
353                      unsigned int width, unsigned int height)
354{
355    unsigned int x, y;
356
357    switch(ch)
358    {
359    case 0x00002580: /* ▀ */
360        for(y = 0; y < height; y++)
361            for(x = 0; x < width; x++)
362                i->buffer[x + y * i->pitch] = y < height / 2 ? 0xff : 0x00;
363        if(height & 1)
364            for(x = 0; x < width; x++)
365                i->buffer[x + (height / 2) * i->pitch] = 0x7f;
366        break;
367    case 0x00002584: /* ▄ */
368        for(y = 0; y < height; y++)
369            for(x = 0; x < width; x++)
370                i->buffer[x + y * i->pitch] = y < height / 2 ? 0x00 : 0xff;
371        if(height & 1)
372            for(x = 0; x < width; x++)
373                i->buffer[x + (height / 2) * i->pitch] = 0x7f;
374        break;
375    case 0x0000258c: /* ▌ */
376        for(y = 0; y < height; y++)
377            for(x = 0; x < width; x++)
378                i->buffer[x + y * i->pitch] = x < width / 2 ? 0xff : 0x00;
379        if(width & 1)
380            for(y = 0; y < height; y++)
381                i->buffer[(width / 2) + y * i->pitch] = 0x7f;
382        break;
383    case 0x00002590: /* ▐ */
384        for(y = 0; y < height; y++)
385            for(x = 0; x < width; x++)
386                i->buffer[x + y * i->pitch] = x < width / 2 ? 0x00 : 0xff;
387        if(width & 1)
388            for(y = 0; y < height; y++)
389                i->buffer[(width / 2) + y * i->pitch] = 0x7f;
390        break;
391    case 0x000025a0: /* ■ */
392        for(y = 0; y < height; y++)
393            for(x = 0; x < width; x++)
394                i->buffer[x + y * i->pitch] =
395                    (y >= height / 4) && (y < 3 * height / 4) ? 0xff : 0x00;
396        if(height & 3)
397            for(x = 0; x < width; x++) /* FIXME: could be more precise */
398                i->buffer[x + (height / 4) * i->pitch] =
399                    i->buffer[x + (3 * height / 4) * i->pitch] = 0x7f;
400        break;
401    case 0x00002588: /* █ */
402        memset(i->buffer, 0xff, height * i->pitch);
403        break;
404    case 0x00002593: /* ▓ */
405        for(y = 0; y < height; y++)
406            for(x = 0; x < width; x++)
407                i->buffer[x + y * i->pitch] =
408                    ((x + 2 * (y & 1)) & 3) ? 0xff : 0x00;
409        break;
410    case 0x00002592: /* ▒ */
411        for(y = 0; y < height; y++)
412            for(x = 0; x < width; x++)
413                i->buffer[x + y * i->pitch] = ((x + y) & 1) ? 0xff : 0x00;
414        break;
415    case 0x00002591: /* ░ */
416        for(y = 0; y < height; y++)
417            for(x = 0; x < width; x++)
418                i->buffer[x + y * i->pitch] =
419                    ((x + 2 * (y & 1)) & 3) ? 0x00 : 0xff;
420        break;
421    }
422}
423
424static int printf_unicode(struct glyph *g)
425{
426    int wr = 0;
427
428    wr += printf("U+%.04X: \"", g->unicode);
429
430    if(g->unicode < 0x20 || (g->unicode >= 0x7f && g->unicode <= 0xa0))
431        wr += printf("\\x%.02x\"", g->unicode);
432    else
433        wr += printf("%s\"", g->buf);
434
435    return wr;
436}
437
438static int printf_u32(char const *fmt, uint32_t i)
439{
440    uint32_t ni = hton32(i);
441    return printf_hex(fmt, (uint8_t *)&ni, 4);
442}
443
444static int printf_u16(char const *fmt, uint16_t i)
445{
446    uint16_t ni = hton16(i);
447    return printf_hex(fmt, (uint8_t *)&ni, 2);
448}
449
450static int printf_hex(char const *fmt, uint8_t *data, int bytes)
451{
452    char buf[BUFSIZ];
453    char *parser = buf;
454    int rev = 0; /* we use this variable to rewind 2 bytes after \000
455                  * was printed when the next char starts with "\", too. */
456
457    while(bytes--)
458    {
459        uint8_t ch = *data++;
460
461        if(written == STRING_CHUNKS)
462        {
463            parser += sprintf(parser, "\", \"");
464            written = 0;
465            rev = 0;
466        }
467
468        if(ch == '\\' || ch == '"')
469        {
470            parser -= rev;
471            parser += sprintf(parser, "\\%c", ch);
472            rev = 0;
473        }
474        else if(ch >= 0x20 && ch < 0x7f)
475        {
476            parser += sprintf(parser, "%c", ch);
477            rev = 0;
478        }
479        else
480        {
481            parser -= rev;
482            parser += sprintf(parser, "\\%.03o", ch);
483            rev = ch ? 0 : 2;
484        }
485
486        written++;
487    }
488
489    parser -= rev;
490    parser[0] = '\0';
491
492    return printf(fmt, buf);
493}
494
Note: See TracBrowser for help on using the repository browser.