/* * TOIlet The Other Implementation’s letters * Copyright (c) 2006 Sam Hocevar * 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 #endif #include #include #include #include #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, don’t 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; }