toilet/src/figlet.c

274 lines
6.8 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* TOIlet The Other Implementations letters
* Copyright (c) 2006 Sam Hocevar <sam@zoy.org>
* All Rights Reserved
*
* $Id$
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the Do What The Fuck You Want To
* Public License, Version 2, as published by Sam Hocevar. See
* http://sam.zoy.org/wtfpl/COPYING for more details.
*/
/*
* This file contains functions for handling FIGlet fonts.
*/
#include "config.h"
#if defined(HAVE_INTTYPES_H)
# include <inttypes.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <cucul.h>
#include "toilet.h"
#include "io.h"
#include "figlet.h"
#define STD_GLYPHS (127 - 32)
#define EXT_GLYPHS (STD_GLYPHS + 7)
static int feed_figlet(context_t *, uint32_t);
static int end_figlet(context_t *);
static int open_font(context_t *cx);
int init_figlet(context_t *cx)
{
if(open_font(cx))
return -1;
cx->x = cx->y = 0;
cx->w = cx->h = 0;
cx->cv = cucul_create_canvas(1, 1);
cx->feed = feed_figlet;
cx->end = end_figlet;
return 0;
}
static int feed_figlet(context_t *cx, uint32_t ch)
{
unsigned int c, w, h, x, y;
switch(ch)
{
case (uint32_t)'\r':
return 0;
case (uint32_t)'\n':
cx->x = 0;
cx->y += cx->height;
return 0;
/* FIXME: handle '\t' */
}
/* Look whether our glyph is available */
for(c = 0; c < cx->glyphs; c++)
if(cx->lookup[c * 2] == ch)
break;
if(c == cx->glyphs)
return 0;
w = cx->lookup[c * 2 + 1];
h = cx->height;
/* Check whether we reached the end of the screen */
if(cx->x && cx->x + w > cx->term_width)
{
cx->x = 0;
cx->y += h;
}
/* Check whether the current canvas is large enough */
if(cx->x + w > cx->w)
cx->w = cx->x + w < cx->term_width ? cx->x + w : cx->term_width;
if(cx->y + h > cx->h)
cx->h = cx->y + h;
cucul_set_canvas_size(cx->cv, cx->w, cx->h);
/* Render our char (FIXME: create a rect-aware cucul_blit_canvas?) */
for(y = 0; y < h; y++)
for(x = 0; x < w; x++)
{
uint32_t tmp = cucul_getchar(cx->image, x, y + c * cx->height);
cucul_putchar(cx->cv, cx->x + x, cx->y + y, tmp);
}
/* Advance cursor */
cx->x += w;
return 0;
}
static int end_figlet(context_t *cx)
{
cucul_free_canvas(cx->image);
free(cx->lookup);
return 0;
}
static int open_font(context_t *cx)
{
char *data = NULL;
char path[2048];
char buf[2048];
char hardblank[10];
cucul_buffer_t *b;
TOIFILE *f;
unsigned int i, j, size, comment_lines;
/* Open font: try .tlf, then .flf */
snprintf(path, 2047, "%s/%s.tlf", cx->dir, cx->font);
path[2047] = '\0';
f = toiopen(path, "r");
if(!f)
{
snprintf(path, 2047, "%s/%s.flf", cx->dir, cx->font);
path[2047] = '\0';
f = toiopen(path, "r");
if(!f)
{
fprintf(stderr, "font `%s' not found\n", path);
return -1;
}
}
/* Read header */
cx->print_direction = 0;
cx->full_layout = 0;
cx->codetag_count = 0;
toigets(buf, 2048, f);
if(sscanf(buf, "%*[ft]lf2a%6s %u %u %u %i %u %u %u %u\n", hardblank,
&cx->height, &cx->baseline, &cx->max_length,
&cx->old_layout, &comment_lines, &cx->print_direction,
&cx->full_layout, &cx->codetag_count) < 6)
{
fprintf(stderr, "font `%s' has invalid header: %s\n", path, buf);
toiclose(f);
return -1;
}
cx->hardblank = cucul_utf8_to_utf32(hardblank, NULL);
/* Skip comment lines */
for(i = 0; i < comment_lines; i++)
toigets(buf, 2048, f);
/* Read mandatory characters (32-127, 196, 214, 220, 228, 246, 252, 223)
* then read additional characters. */
cx->glyphs = 0;
cx->lookup = NULL;
for(i = 0, size = 0; !toieof(f); cx->glyphs++)
{
if((cx->glyphs % 2048) == 0)
cx->lookup = realloc(cx->lookup,
(cx->glyphs + 2048) * 2 * sizeof(int));
if(cx->glyphs < STD_GLYPHS)
{
cx->lookup[cx->glyphs * 2] = 32 + cx->glyphs;
}
else if(cx->glyphs < EXT_GLYPHS)
{
static int const tab[7] = { 196, 214, 220, 228, 246, 252, 223 };
cx->lookup[cx->glyphs * 2] = tab[cx->glyphs - STD_GLYPHS];
}
else
{
if(toigets(buf, 2048, f) == NULL)
break;
if(!buf[0])
{
free(data);
free(cx->lookup);
fprintf(stderr, "read error at glyph %u in `%s'\n",
cx->glyphs, path);
return -1;
}
if(buf[1] == 'x')
sscanf(buf, "%x", &cx->lookup[cx->glyphs * 2]);
else
sscanf(buf, "%u", &cx->lookup[cx->glyphs * 2]);
}
cx->lookup[cx->glyphs * 2 + 1] = 0;
for(j = 0; j < cx->height; j++)
{
if(i + 2048 >= size)
data = realloc(data, size += 2048);
toigets(data + i, 2048, f);
i = (uintptr_t)strchr(data + i, 0) - (uintptr_t)data;
}
}
toiclose(f);
if(cx->glyphs < EXT_GLYPHS)
{
free(data);
free(cx->lookup);
fprintf(stderr, "only %u glyphs in `%s', expected at least %u\n",
cx->glyphs, path, EXT_GLYPHS);
return -1;
}
/* Import buffer into canvas */
b = cucul_load_memory(data, i);
cx->image = cucul_import_canvas(b, "utf8");
cucul_free_buffer(b);
free(data);
if(!cx->image)
{
free(cx->lookup);
fprintf(stderr, "libcucul could not load data in `%s'\n", path);
return -1;
}
/* Remove EOL characters. For now we ignore hardblanks, dont do any
* smushing, nor any kind of error checking. */
for(j = 0; j < cx->height * cx->glyphs; j++)
{
unsigned long int ch, oldch = 0;
for(i = cx->max_length; i--;)
{
ch = cucul_getchar(cx->image, i, j);
/* TODO: Replace hardblanks with U+00A0 NO-BREAK SPACE */
if(ch == cx->hardblank)
cucul_putchar(cx->image, i, j, ch = ' ');
//cucul_putchar(cx->image, i, j, ch = 0xa0);
if(oldch && ch != oldch)
{
if(!cx->lookup[j / cx->height * 2 + 1])
cx->lookup[j / cx->height * 2 + 1] = i + 1;
}
else if(oldch && ch == oldch)
cucul_putchar(cx->image, i, j, ' ');
else if(ch != ' ')
{
oldch = ch;
cucul_putchar(cx->image, i, j, ' ');
}
}
}
return 0;
}