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

Last change on this file since 2111 was 2111, checked in by Sam Hocevar, 13 years ago

=?utf-8?q?=20=20*=20Implement=20cucul=5Fput=5Ffigchar()=20to=20paste=20FIGlet/TOIlet=20characters=20in

=20=20=20=20=20=20=20=20=20=20=E2=96=8C=20=E2=96=8C=20=20=20=E2=96=9C=E2=96=9C=20=20=20=20=C2=A0=E2=96=8C=20=E2=96=8C=20=20=20=20=20=20=E2=96=9C=20=20=20=E2=96=8C=E2=96=90
=20=20=20=20=20=20=20=20=20=20=E2=96=99=E2=96=84=E2=96=8C=E2=96=9E=E2=96=80=E2=96=96=E2=96=90=E2=96=90=20=E2=96=9E=E2=96=80=E2=96=96=C2=A0=E2=96=8C=E2=96=96=E2=96=8C=E2=96=9E=E2=96=80=E2=96=96=E2=96=99=E2=96=80=E2=96=96=E2=96=90=20=E2=96=9E=E2=96=80=E2=96=8C=E2=96=90
=20=20=20=20=20=20=20=20=20=20=E2=96=8C=20=E2=96=8C=E2=96=9B=E2=96=80=20=E2=96=90=E2=96=90=20=E2=96=8C=20=E2=96=8C=C2=A0=E2=96=99=E2=96=9A=E2=96=8C=E2=96=8C=20=E2=96=8C=E2=96=8C=20=C2=A0=E2=96=90=20=E2=96=8C=20=E2=96=8C=E2=96=9D
=20=20=20=20=20=20=20=20=20=20=E2=96=98=20=E2=96=98=E2=96=9D=E2=96=80=E2=96=98=20=E2=96=98=E2=96=98=E2=96=9D=E2=96=80=20=C2=A0=E2=96=98=20=E2=96=98=E2=96=9D=E2=96=80=20=E2=96=98=20=20=20=E2=96=98=E2=96=9D=E2=96=80=E2=96=98=E2=96=9D?=
MIME-Version: 1.0
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: 8bit

File size: 14.4 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/*
20 *  FIXME: this file needs huge cleanup to be usable
21 */
22
23#include "config.h"
24#include "common.h"
25
26#if !defined(__KERNEL__)
27#   include <stdio.h>
28#   include <stdlib.h>
29#   include <string.h>
30#endif
31
32#include "cucul.h"
33#include "cucul_internals.h"
34
35struct cucul_figfont
36{
37    unsigned int term_width;
38    int x, y, w, h, lines;
39
40    enum { H_DEFAULT, H_KERN, H_SMUSH, H_NONE, H_OVERLAP } hmode;
41    unsigned int hsmushrule;
42    unsigned long int hardblank;
43    unsigned int height, baseline, max_length;
44    int old_layout;
45    unsigned int print_direction, full_layout, codetag_count;
46    unsigned int glyphs;
47    cucul_canvas_t *fontcv, *charcv;
48    int *left, *right; /* Unused yet */
49    unsigned int *lookup;
50};
51
52static uint32_t hsmush(uint32_t ch1, uint32_t ch2, unsigned int rule);
53static cucul_figfont_t * open_figfont(char const *);
54static int free_figfont(cucul_figfont_t *);
55
56int cucul_canvas_set_figfont(cucul_canvas_t *cv, char const *path)
57{
58    cucul_figfont_t *ff = NULL;
59
60    if(path)
61    {
62        ff = open_figfont(path);
63        if(!ff)
64            return -1;
65    }
66
67    if(cv->ff)
68    {
69        cucul_free_canvas(cv->ff->charcv);
70        free(cv->ff->left);
71        free(cv->ff->right);
72        free_figfont(cv->ff);
73    }
74
75    cv->ff = ff;
76
77    if(!path)
78        return 0;
79
80    /* from TOIlet’s main.c */
81    ff->term_width = 80;
82    ff->hmode = H_DEFAULT;
83
84    /* from TOIlet’s render.c */
85    ff->x = ff->y = 0;
86    ff->w = ff->h = 0;
87    ff->lines = 0;
88    cucul_set_canvas_size(cv, 0, 0); /* XXX */
89
90    /* from TOIlet’s figlet.c */
91    if(ff->full_layout & 0x3f)
92        ff->hsmushrule = ff->full_layout & 0x3f;
93    else if(ff->old_layout > 0)
94        ff->hsmushrule = ff->old_layout;
95
96    switch(ff->hmode)
97    {
98    case H_DEFAULT:
99        if(ff->old_layout == -1)
100            ff->hmode = H_NONE;
101        else if(ff->old_layout == 0 && (ff->full_layout & 0xc0) == 0x40)
102            ff->hmode = H_KERN;
103        else if((ff->old_layout & 0x3f) && (ff->full_layout & 0x3f)
104                 && (ff->full_layout & 0x80))
105        {
106            ff->hmode = H_SMUSH;
107            ff->hsmushrule = ff->full_layout & 0x3f;
108        }
109        else if(ff->old_layout == 0 && (ff->full_layout & 0xbf) == 0x80)
110        {
111            ff->hmode = H_SMUSH;
112            ff->hsmushrule = 0x3f;
113        }
114        else
115            ff->hmode = H_OVERLAP;
116        break;
117    default:
118        break;
119    }
120
121    ff->charcv = cucul_create_canvas(ff->max_length - 2, ff->height);
122
123    ff->left = malloc(ff->height * sizeof(int));
124    ff->right = malloc(ff->height * sizeof(int));
125
126    cv->ff = ff;
127
128    return 0;
129}
130
131int cucul_put_figchar(cucul_canvas_t *cv, unsigned long int ch)
132{
133    cucul_figfont_t *ff = cv->ff;
134    unsigned int c, w, h, x, y, overlap, extra, xleft, xright;
135
136    switch(ch)
137    {
138        case (uint32_t)'\r':
139            return 0;
140        case (uint32_t)'\n':
141            ff->x = 0;
142            ff->y += ff->height;
143            return 0;
144        /* FIXME: handle '\t' */
145    }
146
147    /* Look whether our glyph is available */
148    for(c = 0; c < ff->glyphs; c++)
149        if(ff->lookup[c * 2] == ch)
150            break;
151
152    if(c == ff->glyphs)
153        return 0;
154
155    w = ff->lookup[c * 2 + 1];
156    h = ff->height;
157
158    cucul_set_canvas_handle(ff->fontcv, 0, c * ff->height);
159    cucul_blit(ff->charcv, 0, 0, ff->fontcv, NULL);
160
161    /* Check whether we reached the end of the screen */
162    if(ff->x && ff->x + w > ff->term_width)
163    {
164        ff->x = 0;
165        ff->y += h;
166    }
167
168    /* Compute how much the next character will overlap */
169    switch(ff->hmode)
170    {
171    case H_SMUSH:
172    case H_KERN:
173    case H_OVERLAP:
174        extra = (ff->hmode == H_OVERLAP);
175        overlap = w;
176        for(y = 0; y < h; y++)
177        {
178            /* Compute how much spaces we can eat from the new glyph */
179            for(xright = 0; xright < overlap; xright++)
180                if(cucul_get_char(ff->charcv, xright, y) != ' ')
181                    break;
182
183            /* Compute how much spaces we can eat from the previous glyph */
184            for(xleft = 0; xright + xleft < overlap && xleft < ff->x; xleft++)
185                if(cucul_get_char(cv, ff->x - 1 - xleft, ff->y + y) != ' ')
186                    break;
187
188            /* Handle overlapping */
189            if(ff->hmode == H_OVERLAP && xleft < ff->x)
190                xleft++;
191
192            /* Handle smushing */
193            if(ff->hmode == H_SMUSH)
194            {
195                if(xleft < ff->x &&
196                    hsmush(cucul_get_char(cv, ff->x - 1 - xleft, ff->y + y),
197                          cucul_get_char(ff->charcv, xright, y),
198                          ff->hsmushrule))
199                    xleft++;
200            }
201
202            if(xleft + xright < overlap)
203                overlap = xleft + xright;
204        }
205        break;
206    case H_NONE:
207        overlap = 0;
208        break;
209    default:
210        return -1;
211    }
212
213    /* Check whether the current canvas is large enough */
214    if(ff->x + w - overlap > ff->w)
215        ff->w = ff->x + w - overlap < ff->term_width
216              ? ff->x + w - overlap : ff->term_width;
217
218    if(ff->y + h > ff->h)
219        ff->h = ff->y + h;
220
221#if 0 /* deactivated for libcaca insertion */
222    if(attr)
223        cucul_set_attr(cv, attr);
224#endif
225    cucul_set_canvas_size(cv, ff->w, ff->h);
226
227    /* Render our char (FIXME: create a rect-aware cucul_blit_canvas?) */
228    for(y = 0; y < h; y++)
229        for(x = 0; x < w; x++)
230    {
231        uint32_t ch1, ch2;
232        //uint32_t tmpat = cucul_get_attr(ff->fontcv, x, y + c * ff->height);
233        ch2 = cucul_get_char(ff->charcv, x, y);
234        if(ch2 == ' ')
235            continue;
236        ch1 = cucul_get_char(cv, ff->x + x - overlap, ff->y + y);
237        /* FIXME: this could be changed to cucul_put_attr() when the
238         * function is fixed in libcucul */
239        //cucul_set_attr(cv, tmpat);
240        if(ch1 == ' ' || ff->hmode != H_SMUSH)
241            cucul_put_char(cv, ff->x + x - overlap, ff->y + y, ch2);
242        else
243            cucul_put_char(cv, ff->x + x - overlap, ff->y + y,
244                           hsmush(ch1, ch2, ff->hsmushrule));
245        //cucul_put_attr(cv, ff->x + x, ff->y + y, tmpat);
246    }
247
248    /* Advance cursor */
249    ff->x += w - overlap;
250
251    return 0;
252}
253
254static int flush_figlet(cucul_canvas_t *cv)
255{
256    cucul_figfont_t *ff = cv->ff;
257    unsigned int x, y;
258
259    //ff->torender = cv;
260    //cucul_set_canvas_size(ff->torender, ff->w, ff->h);
261    cucul_set_canvas_size(cv, ff->w, ff->h);
262
263    /* FIXME: do this somewhere else, or record hardblank positions */
264    for(y = 0; y < ff->h; y++)
265        for(x = 0; x < ff->w; x++)
266            if(cucul_get_char(cv, x, y) == 0xa0)
267            {
268                uint32_t attr = cucul_get_attr(cv, x, y);
269                cucul_put_char(cv, x, y, ' ');
270                cucul_put_attr(cv, x, y, attr);
271            }
272
273    ff->x = ff->y = 0;
274    ff->w = ff->h = 0;
275
276    //cv = cucul_create_canvas(1, 1); /* XXX */
277
278    /* from render.c */
279    ff->lines += cucul_get_canvas_height(cv);
280
281    return 0;
282}
283
284#define STD_GLYPHS (127 - 32)
285#define EXT_GLYPHS (STD_GLYPHS + 7)
286
287cucul_figfont_t * open_figfont(char const *path)
288{
289    char altpath[2048];
290    char buf[2048];
291    char hardblank[10];
292    cucul_figfont_t *ff;
293    char *data = NULL;
294    cucul_file_t *f;
295    unsigned int i, j, size, comment_lines;
296
297    ff = malloc(sizeof(cucul_figfont_t));
298    if(!ff)
299    {
300        seterrno(ENOMEM);
301        return NULL;
302    }
303
304    /* Open font: if not found, try .tlf, then .flf */
305    f = _cucul_file_open(path, "r");
306    if(!f)
307    {
308        snprintf(altpath, 2047, "%s.tlf", path);
309        altpath[2047] = '\0';
310        f = _cucul_file_open(altpath, "r");
311    }
312    if(!f)
313    {
314        snprintf(altpath, 2047, "%s.flf", path);
315        altpath[2047] = '\0';
316        f = _cucul_file_open(altpath, "r");
317    }
318    if(!f)
319    {
320        free(ff);
321        seterrno(ENOENT);
322        return NULL;
323    }
324
325    /* Read header */
326    ff->print_direction = 0;
327    ff->full_layout = 0;
328    ff->codetag_count = 0;
329    _cucul_file_gets(buf, 2048, f);
330    if(sscanf(buf, "%*[ft]lf2a%6s %u %u %u %i %u %u %u %u\n", hardblank,
331              &ff->height, &ff->baseline, &ff->max_length,
332              &ff->old_layout, &comment_lines, &ff->print_direction,
333              &ff->full_layout, &ff->codetag_count) < 6)
334    {
335        debug("figfont error: `%s' has invalid header: %s", path, buf);
336        _cucul_file_close(f);
337        free(ff);
338        seterrno(EINVAL);
339        return NULL;
340    }
341
342    if(ff->old_layout < -1 || ff->old_layout > 63 || ff->full_layout > 32767
343        || ((ff->full_layout & 0x80) && (ff->full_layout & 0x3f) == 0
344            && ff->old_layout))
345    {
346        debug("figfont error: `%s' has invalid layout %i/%u",
347                path, ff->old_layout, ff->full_layout);
348        _cucul_file_close(f);
349        free(ff);
350        seterrno(EINVAL);
351        return NULL;
352    }
353
354    ff->hardblank = cucul_utf8_to_utf32(hardblank, NULL);
355
356    /* Skip comment lines */
357    for(i = 0; i < comment_lines; i++)
358        _cucul_file_gets(buf, 2048, f);
359
360    /* Read mandatory characters (32-127, 196, 214, 220, 228, 246, 252, 223)
361     * then read additional characters. */
362    ff->glyphs = 0;
363    ff->lookup = NULL;
364
365    for(i = 0, size = 0; !_cucul_file_eof(f); ff->glyphs++)
366    {
367        if((ff->glyphs % 2048) == 0)
368            ff->lookup = realloc(ff->lookup,
369                                   (ff->glyphs + 2048) * 2 * sizeof(int));
370
371        if(ff->glyphs < STD_GLYPHS)
372        {
373            ff->lookup[ff->glyphs * 2] = 32 + ff->glyphs;
374        }
375        else if(ff->glyphs < EXT_GLYPHS)
376        {
377            static int const tab[7] = { 196, 214, 220, 228, 246, 252, 223 };
378            ff->lookup[ff->glyphs * 2] = tab[ff->glyphs - STD_GLYPHS];
379        }
380        else
381        {
382            if(_cucul_file_gets(buf, 2048, f) == NULL)
383                break;
384
385            /* Ignore blank lines, as in jacky.flf */
386            if(buf[0] == '\n' || buf[0] == '\r')
387                continue;
388
389            /* Ignore negative indices for now, as in ivrit.flf */
390            if(buf[0] == '-')
391            {
392                for(j = 0; j < ff->height; j++)
393                    _cucul_file_gets(buf, 2048, f);
394                continue;
395            }
396
397            if(!buf[0] || buf[0] < '0' || buf[0] > '9')
398            {
399                debug("figfont error: glyph #%u in `%s'", ff->glyphs, path);
400                free(data);
401                free(ff->lookup);
402                free(ff);
403                seterrno(EINVAL);
404                return NULL;
405            }
406
407            if(buf[1] == 'x')
408                sscanf(buf, "%x", &ff->lookup[ff->glyphs * 2]);
409            else
410                sscanf(buf, "%u", &ff->lookup[ff->glyphs * 2]);
411        }
412
413        ff->lookup[ff->glyphs * 2 + 1] = 0;
414
415        for(j = 0; j < ff->height; j++)
416        {
417            if(i + 2048 >= size)
418                data = realloc(data, size += 2048);
419
420            _cucul_file_gets(data + i, 2048, f);
421            i = (uintptr_t)strchr(data + i, 0) - (uintptr_t)data;
422        }
423    }
424
425    _cucul_file_close(f);
426
427    if(ff->glyphs < EXT_GLYPHS)
428    {
429        debug("figfont error: only %u glyphs in `%s', expected at least %u",
430                        ff->glyphs, path, EXT_GLYPHS);
431        free(data);
432        free(ff->lookup);
433        free(ff);
434        seterrno(EINVAL);
435        return NULL;
436    }
437
438    /* Import buffer into canvas */
439    ff->fontcv = cucul_create_canvas(0, 0);
440    cucul_import_memory(ff->fontcv, data, i, "utf8");
441    free(data);
442
443    /* Remove EOL characters. For now we ignore hardblanks, don’t do any
444     * smushing, nor any kind of error checking. */
445    for(j = 0; j < ff->height * ff->glyphs; j++)
446    {
447        unsigned long int ch, oldch = 0;
448
449        for(i = ff->max_length; i--;)
450        {
451            ch = cucul_get_char(ff->fontcv, i, j);
452
453            /* Replace hardblanks with U+00A0 NO-BREAK SPACE */
454            if(ch == ff->hardblank)
455                cucul_put_char(ff->fontcv, i, j, ch = 0xa0);
456
457            if(oldch && ch != oldch)
458            {
459                if(!ff->lookup[j / ff->height * 2 + 1])
460                    ff->lookup[j / ff->height * 2 + 1] = i + 1;
461            }
462            else if(oldch && ch == oldch)
463                cucul_put_char(ff->fontcv, i, j, ' ');
464            else if(ch != ' ')
465            {
466                oldch = ch;
467                cucul_put_char(ff->fontcv, i, j, ' ');
468            }
469        }
470    }
471
472    return ff;
473}
474
475int free_figfont(cucul_figfont_t *ff)
476{
477    cucul_free_canvas(ff->fontcv);
478    free(ff->lookup);
479    free(ff);
480
481    return 0;
482}
483
484static uint32_t hsmush(uint32_t ch1, uint32_t ch2, unsigned int rule)
485{
486    /* Rule 1 */
487    if((rule & 0x01) && ch1 == ch2 && ch1 != 0xa0)
488        return ch2;
489
490    if(ch1 < 0x80 && ch2 < 0x80)
491    {
492        char const charlist[] = "|/\\[]{}()<>";
493        char *tmp1, *tmp2;
494
495        /* Rule 2 */
496        if(rule & 0x02)
497        {
498            if(ch1 == '_' && strchr(charlist, ch2))
499                return ch2;
500
501            if(ch2 == '_' && strchr(charlist, ch1))
502                return ch1;
503        }
504
505        /* Rule 3 */
506        if((rule & 0x04) &&
507           (tmp1 = strchr(charlist, ch1)) && (tmp2 = strchr(charlist, ch2)))
508        {
509            int cl1 = (tmp1 + 1 - charlist) / 2;
510            int cl2 = (tmp2 + 1 - charlist) / 2;
511
512            if(cl1 < cl2)
513                return ch2;
514            if(cl1 > cl2)
515                return ch1;
516        }
517
518        /* Rule 4 */
519        if(rule & 0x08)
520        {
521            uint16_t s = ch1 + ch2;
522            uint16_t p = ch1 * ch2;
523
524            if(p == 15375 /* '{' * '}' */
525                || p == 8463 /* '[' * ']' */
526                || (p == 1640 && s == 81)) /* '(' *|+ ')' */
527                return '|';
528        }
529
530        /* Rule 5 */
531        if(rule & 0x10)
532        {
533            switch((ch1 << 8) | ch2)
534            {
535                case 0x2f5c: return '|'; /* /\ */
536                case 0x5c2f: return 'Y'; /* \/ */
537                case 0x3e3c: return 'X'; /* >< */
538            }
539        }
540
541        /* Rule 6 */
542        if((rule & 0x20) && ch1 == ch2 && ch1 == 0xa0)
543            return 0xa0;
544    }
545
546    return 0;
547}
548
Note: See TracBrowser for help on using the repository browser.