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

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