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

Last change on this file since 1385 was 1385, checked in by Sam Hocevar, 16 years ago
  • Support for ANSI escape codes in the input:

http://zoy.org/~sam/toilet-ansi.png
http://zoy.org/~sam/toilet-ansi2.png

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