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

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