source: libcaca/trunk/cucul/figfont.c @ 2110

Last change on this file since 2110 was 2110, checked in by Sam Hocevar, 13 years ago
  • Implemented cucul_canvas_set_figfont() from TOIlet’s open_font().
File size: 6.8 KB
Line 
1/*
2 *  libcucul      Canvas for ultrafast compositing of Unicode letters
3 *  Copyright (c) 2006-2007 Sam Hocevar <sam@zoy.org>
4 *                All Rights Reserved
5 *
6 *  $Id$
7 *
8 *  This library is free software. It comes 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
12 *  http://sam.zoy.org/wtfpl/COPYING for more details.
13 */
14
15/*
16 *  This file contains FIGlet and TOIlet font handling functions.
17 */
18
19#include "config.h"
20#include "common.h"
21
22#if !defined(__KERNEL__)
23#   include <stdio.h>
24#   include <stdlib.h>
25#   include <string.h>
26#endif
27
28#include "cucul.h"
29#include "cucul_internals.h"
30
31struct cucul_figfont
32{
33    /* Used by the FIGlet driver */
34    unsigned long int hardblank;
35    unsigned int height, baseline, max_length;
36    int old_layout;
37    unsigned int print_direction, full_layout, codetag_count;
38    unsigned int glyphs;
39    cucul_canvas_t *fontcv;
40    unsigned int *lookup;
41};
42
43int cucul_canvas_set_figfont(cucul_canvas_t *cv, char const *path)
44{
45    cucul_figfont_t *ff = NULL;
46
47    if(path)
48    {
49        ff = _cucul_open_figfont(path);
50        if(!ff)
51            return -1;
52    }
53
54    if(cv->ff)
55        _cucul_free_figfont(cv->ff);
56
57    cv->ff = ff;
58
59    return 0;
60}
61
62#define STD_GLYPHS (127 - 32)
63#define EXT_GLYPHS (STD_GLYPHS + 7)
64
65cucul_figfont_t * _cucul_open_figfont(char const *path)
66{
67    char altpath[2048];
68    char buf[2048];
69    char hardblank[10];
70    cucul_figfont_t *ff;
71    char *data = NULL;
72    cucul_file_t *f;
73    unsigned int i, j, size, comment_lines;
74
75    ff = malloc(sizeof(cucul_figfont_t));
76    if(!ff)
77    {
78        seterrno(ENOMEM);
79        return NULL;
80    }
81
82    /* Open font: if not found, try .tlf, then .flf */
83    f = _cucul_file_open(path, "r");
84    if(!f)
85    {
86        snprintf(altpath, 2047, "%s.tlf", path);
87        altpath[2047] = '\0';
88        f = _cucul_file_open(altpath, "r");
89    }
90    if(!f)
91    {
92        snprintf(altpath, 2047, "%s.flf", path);
93        altpath[2047] = '\0';
94        f = _cucul_file_open(altpath, "r");
95    }
96    if(!f)
97    {
98        free(ff);
99        seterrno(ENOENT);
100        return NULL;
101    }
102
103    /* Read header */
104    ff->print_direction = 0;
105    ff->full_layout = 0;
106    ff->codetag_count = 0;
107    _cucul_file_gets(buf, 2048, f);
108    if(sscanf(buf, "%*[ft]lf2a%6s %u %u %u %i %u %u %u %u\n", hardblank,
109              &ff->height, &ff->baseline, &ff->max_length,
110              &ff->old_layout, &comment_lines, &ff->print_direction,
111              &ff->full_layout, &ff->codetag_count) < 6)
112    {
113        debug("figfont error: `%s' has invalid header: %s", path, buf);
114        _cucul_file_close(f);
115        free(ff);
116        seterrno(EINVAL);
117        return NULL;
118    }
119
120    if(ff->old_layout < -1 || ff->old_layout > 63 || ff->full_layout > 32767
121        || ((ff->full_layout & 0x80) && (ff->full_layout & 0x3f) == 0
122            && ff->old_layout))
123    {
124        debug("figfont error: `%s' has invalid layout %i/%u",
125                path, ff->old_layout, ff->full_layout);
126        _cucul_file_close(f);
127        free(ff);
128        seterrno(EINVAL);
129        return NULL;
130    }
131
132    ff->hardblank = cucul_utf8_to_utf32(hardblank, NULL);
133
134    /* Skip comment lines */
135    for(i = 0; i < comment_lines; i++)
136        _cucul_file_gets(buf, 2048, f);
137
138    /* Read mandatory characters (32-127, 196, 214, 220, 228, 246, 252, 223)
139     * then read additional characters. */
140    ff->glyphs = 0;
141    ff->lookup = NULL;
142
143    for(i = 0, size = 0; !_cucul_file_eof(f); ff->glyphs++)
144    {
145        if((ff->glyphs % 2048) == 0)
146            ff->lookup = realloc(ff->lookup,
147                                   (ff->glyphs + 2048) * 2 * sizeof(int));
148
149        if(ff->glyphs < STD_GLYPHS)
150        {
151            ff->lookup[ff->glyphs * 2] = 32 + ff->glyphs;
152        }
153        else if(ff->glyphs < EXT_GLYPHS)
154        {
155            static int const tab[7] = { 196, 214, 220, 228, 246, 252, 223 };
156            ff->lookup[ff->glyphs * 2] = tab[ff->glyphs - STD_GLYPHS];
157        }
158        else
159        {
160            if(_cucul_file_gets(buf, 2048, f) == NULL)
161                break;
162
163            /* Ignore blank lines, as in jacky.flf */
164            if(buf[0] == '\n' || buf[0] == '\r')
165                continue;
166
167            /* Ignore negative indices for now, as in ivrit.flf */
168            if(buf[0] == '-')
169            {
170                for(j = 0; j < ff->height; j++)
171                    _cucul_file_gets(buf, 2048, f);
172                continue;
173            }
174
175            if(!buf[0] || buf[0] < '0' || buf[0] > '9')
176            {
177                debug("figfont error: glyph #%u in `%s'", ff->glyphs, path);
178                free(data);
179                free(ff->lookup);
180                free(ff);
181                seterrno(EINVAL);
182                return NULL;
183            }
184
185            if(buf[1] == 'x')
186                sscanf(buf, "%x", &ff->lookup[ff->glyphs * 2]);
187            else
188                sscanf(buf, "%u", &ff->lookup[ff->glyphs * 2]);
189        }
190
191        ff->lookup[ff->glyphs * 2 + 1] = 0;
192
193        for(j = 0; j < ff->height; j++)
194        {
195            if(i + 2048 >= size)
196                data = realloc(data, size += 2048);
197
198            _cucul_file_gets(data + i, 2048, f);
199            i = (uintptr_t)strchr(data + i, 0) - (uintptr_t)data;
200        }
201    }
202
203    _cucul_file_close(f);
204
205    if(ff->glyphs < EXT_GLYPHS)
206    {
207        debug("figfont error: only %u glyphs in `%s', expected at least %u",
208                        ff->glyphs, path, EXT_GLYPHS);
209        free(data);
210        free(ff->lookup);
211        free(ff);
212        seterrno(EINVAL);
213        return NULL;
214    }
215
216    /* Import buffer into canvas */
217    ff->fontcv = cucul_create_canvas(0, 0);
218    cucul_import_memory(ff->fontcv, data, i, "utf8");
219    free(data);
220
221    /* Remove EOL characters. For now we ignore hardblanks, don’t do any
222     * smushing, nor any kind of error checking. */
223    for(j = 0; j < ff->height * ff->glyphs; j++)
224    {
225        unsigned long int ch, oldch = 0;
226
227        for(i = ff->max_length; i--;)
228        {
229            ch = cucul_get_char(ff->fontcv, i, j);
230
231            /* Replace hardblanks with U+00A0 NO-BREAK SPACE */
232            if(ch == ff->hardblank)
233                cucul_put_char(ff->fontcv, i, j, ch = 0xa0);
234
235            if(oldch && ch != oldch)
236            {
237                if(!ff->lookup[j / ff->height * 2 + 1])
238                    ff->lookup[j / ff->height * 2 + 1] = i + 1;
239            }
240            else if(oldch && ch == oldch)
241                cucul_put_char(ff->fontcv, i, j, ' ');
242            else if(ch != ' ')
243            {
244                oldch = ch;
245                cucul_put_char(ff->fontcv, i, j, ' ');
246            }
247        }
248    }
249
250    return ff;
251}
252
253int _cucul_free_figfont(cucul_figfont_t *ff)
254{
255    cucul_free_canvas(ff->fontcv);
256    free(ff->lookup);
257    free(ff);
258
259    return 0;
260}
261
Note: See TracBrowser for help on using the repository browser.