source: toilet/trunk/src/figlet.c @ 1196

Last change on this file since 1196 was 1196, checked in by Sam Hocevar, 14 years ago
  • Fixed the FIGlet renderer. It now has char wrapping and stdin input.
  • Property svn:keywords set to Id
File size: 6.8 KB
Line 
1/*
2 *  TOIlet        The Other Implementation’s letters
3 *  Copyright (c) 2006 Sam Hocevar <sam@zoy.org>
4 *                All Rights Reserved
5 *
6 *  $Id: figlet.c 1196 2006-10-10 07:20: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
14/*
15 * This file contains functions for handling FIGlet fonts.
16 */
17
18#include "config.h"
19
20#if defined(HAVE_INTTYPES_H)
21#   include <inttypes.h>
22#endif
23#include <stdio.h>
24#include <stdlib.h>
25#include <string.h>
26#include <cucul.h>
27
28#include "toilet.h"
29#include "figlet.h"
30
31#define STD_GLYPHS (127 - 32)
32#define EXT_GLYPHS (STD_GLYPHS + 7)
33
34static int feed_figlet(context_t *, uint32_t);
35static int end_figlet(context_t *);
36
37static int open_font(context_t *cx);
38
39int init_figlet(context_t *cx)
40{
41    if(open_font(cx))
42        return -1;
43
44    cx->x = cx->y = 0;
45    cx->w = cx->h = 0;
46    cx->cv = cucul_create_canvas(1, 1);
47
48    cx->feed = feed_figlet;
49    cx->end = end_figlet;
50
51    return 0;
52}
53
54static int feed_figlet(context_t *cx, uint32_t ch)
55{
56    unsigned int c, w, h, x, y;
57
58    switch(ch)
59    {
60        case (uint32_t)'\r':
61            return 0;
62        case (uint32_t)'\n':
63            cx->x = 0;
64            cx->y += cx->height;
65            return 0;
66        /* FIXME: handle '\t' */
67    }
68
69    /* Look whether our glyph is available */
70    for(c = 0; c < cx->glyphs; c++)
71        if(cx->lookup[c * 2] == ch)
72            break;
73
74    if(c == cx->glyphs)
75        return 0;
76
77    w = cx->lookup[c * 2 + 1];
78    h = cx->height;
79
80    /* Check whether we reached the end of the screen */
81    if(cx->x && cx->x + w > cx->term_width)
82    {
83        cx->x = 0;
84        cx->y += h;
85    }
86
87    /* Check whether the current canvas is large enough */
88    if(cx->x + w > cx->w)
89        cx->w = cx->x + w < cx->term_width ? cx->x + w : cx->term_width;
90
91    if(cx->y + h > cx->h)
92        cx->h = cx->y + h;
93
94    cucul_set_canvas_size(cx->cv, cx->w, cx->h);
95
96    /* Render our char (FIXME: create a rect-aware cucul_blit_canvas?) */
97    for(y = 0; y < h; y++)
98        for(x = 0; x < w; x++)
99    {
100        uint32_t tmp = cucul_getchar(cx->image, x, y + c * cx->height);
101        cucul_putchar(cx->cv, cx->x + x, cx->y + y, tmp);
102    }
103
104    /* Advance cursor */
105    cx->x += w;
106
107    return 0;
108}
109
110static int end_figlet(context_t *cx)
111{
112    cucul_free_canvas(cx->image);
113    free(cx->lookup);
114
115    return 0;
116}
117
118static int open_font(context_t *cx)
119{
120    char *data = NULL;
121    char path[2048];
122    char hardblank[10];
123    cucul_buffer_t *b;
124    FILE *f;
125    unsigned int i, j, size, comment_lines;
126
127    /* Open font: try .tlf, then .flf */
128    snprintf(path, 2047, "%s/%s.tlf", cx->dir, cx->font);
129    path[2047] = '\0';
130    f = fopen(path, "r");
131    if(!f)
132    {
133        snprintf(path, 2047, "%s/%s.flf", cx->dir, cx->font);
134        path[2047] = '\0';
135        f = fopen(path, "r");
136        if(!f)
137        {
138            fprintf(stderr, "font `%s' not found\n", path);
139            return -1;
140        }
141    }
142
143    /* Read header */
144    cx->print_direction = 0;
145    cx->full_layout = 0;
146    cx->codetag_count = 0;
147    if(fscanf(f, "%*[ft]lf2a%6s %u %u %u %i %u %u %u %u\n", hardblank,
148              &cx->height, &cx->baseline, &cx->max_length,
149              &cx->old_layout, &comment_lines, &cx->print_direction,
150              &cx->full_layout, &cx->codetag_count) < 6)
151    {
152        fprintf(stderr, "font `%s' has invalid header\n", path);
153        fclose(f);
154        return -1;
155    }
156
157    cx->hardblank = cucul_utf8_to_utf32(hardblank, NULL);
158
159    /* Skip comment lines */
160    for(i = 0; i < comment_lines; i++)
161    {
162        fscanf(f, "%*[^\n]");
163        fscanf(f, "%*c");
164    }
165
166    /* Read mandatory characters (32-127, 196, 214, 220, 228, 246, 252, 223)
167     * then read additional characters. */
168    cx->glyphs = 0;
169    cx->lookup = NULL;
170
171    for(i = 0, size = 0; !feof(f); cx->glyphs++)
172    {
173        if((cx->glyphs % 2048) == 0)
174            cx->lookup = realloc(cx->lookup,
175                                   (cx->glyphs + 2048) * 2 * sizeof(int));
176
177        if(cx->glyphs < STD_GLYPHS)
178        {
179            cx->lookup[cx->glyphs * 2] = 32 + cx->glyphs;
180        }
181        else if(cx->glyphs < EXT_GLYPHS)
182        {
183            static int const tab[7] = { 196, 214, 220, 228, 246, 252, 223 };
184            cx->lookup[cx->glyphs * 2] = tab[cx->glyphs - STD_GLYPHS];
185        }
186        else
187        {
188            char number[10];
189            int ret = fscanf(f, "%s %*[^\n]", number);
190
191            if(ret == EOF)
192                break;
193
194            if(!ret)
195            {
196                free(data);
197                free(cx->lookup);
198                fprintf(stderr, "read error at glyph %u in `%s'\n",
199                                cx->glyphs, path);
200                return -1;
201            }
202
203            if(number[1] == 'x')
204                sscanf(number, "%x", &cx->lookup[cx->glyphs * 2]);
205            else
206                sscanf(number, "%u", &cx->lookup[cx->glyphs * 2]);
207
208            fscanf(f, "%*c");
209        }
210
211        cx->lookup[cx->glyphs * 2 + 1] = 0;
212
213        for(j = 0; j < cx->height; j++)
214        {
215            if(i + 2048 >= size)
216                data = realloc(data, size += 2048);
217
218            fgets(data + i, 2048, f);
219            i = (uintptr_t)strchr(data + i, 0) - (uintptr_t)data;
220        }
221    }
222
223    fclose(f);
224
225    if(cx->glyphs < EXT_GLYPHS)
226    {
227        free(data);
228        free(cx->lookup);
229        fprintf(stderr, "only %u glyphs in `%s', expected at least %u\n",
230                        cx->glyphs, path, EXT_GLYPHS);
231        return -1;
232    }
233
234    /* Import buffer into canvas */
235    b = cucul_load_memory(data, i);
236    cx->image = cucul_import_canvas(b, "utf8");
237    cucul_free_buffer(b);
238    free(data);
239
240    if(!cx->image)
241    {
242        free(cx->lookup);
243        fprintf(stderr, "libcucul could not load data in `%s'\n", path);
244        return -1;
245    }
246
247    /* Remove EOL characters. For now we ignore hardblanks, don’t do any
248     * smushing, nor any kind of error checking. */
249    for(j = 0; j < cx->height * cx->glyphs; j++)
250    {
251        unsigned long int ch, oldch = 0;
252
253        for(i = cx->max_length; i--;)
254        {
255            ch = cucul_getchar(cx->image, i, j);
256
257            /* TODO: Replace hardblanks with U+00A0 NO-BREAK SPACE */
258            if(ch == cx->hardblank)
259                cucul_putchar(cx->image, i, j, ch = ' ');
260                //cucul_putchar(cx->image, i, j, ch = 0xa0);
261
262            if(oldch && ch != oldch)
263            {
264                if(!cx->lookup[j / cx->height * 2 + 1])
265                    cx->lookup[j / cx->height * 2 + 1] = i + 1;
266            }
267            else if(oldch && ch == oldch)
268                cucul_putchar(cx->image, i, j, ' ');
269            else if(ch != ' ')
270            {
271                oldch = ch;
272                cucul_putchar(cx->image, i, j, ' ');
273            }
274        }
275    }
276
277    return 0;
278}
279
Note: See TracBrowser for help on using the repository browser.