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

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