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

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