|
|
@@ -0,0 +1,261 @@ |
|
|
|
/* |
|
|
|
* libcucul Canvas for ultrafast compositing of Unicode letters |
|
|
|
* Copyright (c) 2006-2007 Sam Hocevar <sam@zoy.org> |
|
|
|
* All Rights Reserved |
|
|
|
* |
|
|
|
* $Id$ |
|
|
|
* |
|
|
|
* This library is free software. It comes without any warranty, to |
|
|
|
* the extent permitted by applicable law. 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 FIGlet and TOIlet font handling functions. |
|
|
|
*/ |
|
|
|
|
|
|
|
#include "config.h" |
|
|
|
#include "common.h" |
|
|
|
|
|
|
|
#if !defined(__KERNEL__) |
|
|
|
# include <stdio.h> |
|
|
|
# include <stdlib.h> |
|
|
|
# include <string.h> |
|
|
|
#endif |
|
|
|
|
|
|
|
#include "cucul.h" |
|
|
|
#include "cucul_internals.h" |
|
|
|
|
|
|
|
struct cucul_figfont |
|
|
|
{ |
|
|
|
/* Used by the FIGlet driver */ |
|
|
|
unsigned long int hardblank; |
|
|
|
unsigned int height, baseline, max_length; |
|
|
|
int old_layout; |
|
|
|
unsigned int print_direction, full_layout, codetag_count; |
|
|
|
unsigned int glyphs; |
|
|
|
cucul_canvas_t *fontcv; |
|
|
|
unsigned int *lookup; |
|
|
|
}; |
|
|
|
|
|
|
|
int cucul_canvas_set_figfont(cucul_canvas_t *cv, char const *path) |
|
|
|
{ |
|
|
|
cucul_figfont_t *ff = NULL; |
|
|
|
|
|
|
|
if(path) |
|
|
|
{ |
|
|
|
ff = _cucul_open_figfont(path); |
|
|
|
if(!ff) |
|
|
|
return -1; |
|
|
|
} |
|
|
|
|
|
|
|
if(cv->ff) |
|
|
|
_cucul_free_figfont(cv->ff); |
|
|
|
|
|
|
|
cv->ff = ff; |
|
|
|
|
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
#define STD_GLYPHS (127 - 32) |
|
|
|
#define EXT_GLYPHS (STD_GLYPHS + 7) |
|
|
|
|
|
|
|
cucul_figfont_t * _cucul_open_figfont(char const *path) |
|
|
|
{ |
|
|
|
char altpath[2048]; |
|
|
|
char buf[2048]; |
|
|
|
char hardblank[10]; |
|
|
|
cucul_figfont_t *ff; |
|
|
|
char *data = NULL; |
|
|
|
cucul_file_t *f; |
|
|
|
unsigned int i, j, size, comment_lines; |
|
|
|
|
|
|
|
ff = malloc(sizeof(cucul_figfont_t)); |
|
|
|
if(!ff) |
|
|
|
{ |
|
|
|
seterrno(ENOMEM); |
|
|
|
return NULL; |
|
|
|
} |
|
|
|
|
|
|
|
/* Open font: if not found, try .tlf, then .flf */ |
|
|
|
f = _cucul_file_open(path, "r"); |
|
|
|
if(!f) |
|
|
|
{ |
|
|
|
snprintf(altpath, 2047, "%s.tlf", path); |
|
|
|
altpath[2047] = '\0'; |
|
|
|
f = _cucul_file_open(altpath, "r"); |
|
|
|
} |
|
|
|
if(!f) |
|
|
|
{ |
|
|
|
snprintf(altpath, 2047, "%s.flf", path); |
|
|
|
altpath[2047] = '\0'; |
|
|
|
f = _cucul_file_open(altpath, "r"); |
|
|
|
} |
|
|
|
if(!f) |
|
|
|
{ |
|
|
|
free(ff); |
|
|
|
seterrno(ENOENT); |
|
|
|
return NULL; |
|
|
|
} |
|
|
|
|
|
|
|
/* Read header */ |
|
|
|
ff->print_direction = 0; |
|
|
|
ff->full_layout = 0; |
|
|
|
ff->codetag_count = 0; |
|
|
|
_cucul_file_gets(buf, 2048, f); |
|
|
|
if(sscanf(buf, "%*[ft]lf2a%6s %u %u %u %i %u %u %u %u\n", hardblank, |
|
|
|
&ff->height, &ff->baseline, &ff->max_length, |
|
|
|
&ff->old_layout, &comment_lines, &ff->print_direction, |
|
|
|
&ff->full_layout, &ff->codetag_count) < 6) |
|
|
|
{ |
|
|
|
debug("figfont error: `%s' has invalid header: %s", path, buf); |
|
|
|
_cucul_file_close(f); |
|
|
|
free(ff); |
|
|
|
seterrno(EINVAL); |
|
|
|
return NULL; |
|
|
|
} |
|
|
|
|
|
|
|
if(ff->old_layout < -1 || ff->old_layout > 63 || ff->full_layout > 32767 |
|
|
|
|| ((ff->full_layout & 0x80) && (ff->full_layout & 0x3f) == 0 |
|
|
|
&& ff->old_layout)) |
|
|
|
{ |
|
|
|
debug("figfont error: `%s' has invalid layout %i/%u", |
|
|
|
path, ff->old_layout, ff->full_layout); |
|
|
|
_cucul_file_close(f); |
|
|
|
free(ff); |
|
|
|
seterrno(EINVAL); |
|
|
|
return NULL; |
|
|
|
} |
|
|
|
|
|
|
|
ff->hardblank = cucul_utf8_to_utf32(hardblank, NULL); |
|
|
|
|
|
|
|
/* Skip comment lines */ |
|
|
|
for(i = 0; i < comment_lines; i++) |
|
|
|
_cucul_file_gets(buf, 2048, f); |
|
|
|
|
|
|
|
/* Read mandatory characters (32-127, 196, 214, 220, 228, 246, 252, 223) |
|
|
|
* then read additional characters. */ |
|
|
|
ff->glyphs = 0; |
|
|
|
ff->lookup = NULL; |
|
|
|
|
|
|
|
for(i = 0, size = 0; !_cucul_file_eof(f); ff->glyphs++) |
|
|
|
{ |
|
|
|
if((ff->glyphs % 2048) == 0) |
|
|
|
ff->lookup = realloc(ff->lookup, |
|
|
|
(ff->glyphs + 2048) * 2 * sizeof(int)); |
|
|
|
|
|
|
|
if(ff->glyphs < STD_GLYPHS) |
|
|
|
{ |
|
|
|
ff->lookup[ff->glyphs * 2] = 32 + ff->glyphs; |
|
|
|
} |
|
|
|
else if(ff->glyphs < EXT_GLYPHS) |
|
|
|
{ |
|
|
|
static int const tab[7] = { 196, 214, 220, 228, 246, 252, 223 }; |
|
|
|
ff->lookup[ff->glyphs * 2] = tab[ff->glyphs - STD_GLYPHS]; |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
if(_cucul_file_gets(buf, 2048, f) == NULL) |
|
|
|
break; |
|
|
|
|
|
|
|
/* Ignore blank lines, as in jacky.flf */ |
|
|
|
if(buf[0] == '\n' || buf[0] == '\r') |
|
|
|
continue; |
|
|
|
|
|
|
|
/* Ignore negative indices for now, as in ivrit.flf */ |
|
|
|
if(buf[0] == '-') |
|
|
|
{ |
|
|
|
for(j = 0; j < ff->height; j++) |
|
|
|
_cucul_file_gets(buf, 2048, f); |
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
if(!buf[0] || buf[0] < '0' || buf[0] > '9') |
|
|
|
{ |
|
|
|
debug("figfont error: glyph #%u in `%s'", ff->glyphs, path); |
|
|
|
free(data); |
|
|
|
free(ff->lookup); |
|
|
|
free(ff); |
|
|
|
seterrno(EINVAL); |
|
|
|
return NULL; |
|
|
|
} |
|
|
|
|
|
|
|
if(buf[1] == 'x') |
|
|
|
sscanf(buf, "%x", &ff->lookup[ff->glyphs * 2]); |
|
|
|
else |
|
|
|
sscanf(buf, "%u", &ff->lookup[ff->glyphs * 2]); |
|
|
|
} |
|
|
|
|
|
|
|
ff->lookup[ff->glyphs * 2 + 1] = 0; |
|
|
|
|
|
|
|
for(j = 0; j < ff->height; j++) |
|
|
|
{ |
|
|
|
if(i + 2048 >= size) |
|
|
|
data = realloc(data, size += 2048); |
|
|
|
|
|
|
|
_cucul_file_gets(data + i, 2048, f); |
|
|
|
i = (uintptr_t)strchr(data + i, 0) - (uintptr_t)data; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
_cucul_file_close(f); |
|
|
|
|
|
|
|
if(ff->glyphs < EXT_GLYPHS) |
|
|
|
{ |
|
|
|
debug("figfont error: only %u glyphs in `%s', expected at least %u", |
|
|
|
ff->glyphs, path, EXT_GLYPHS); |
|
|
|
free(data); |
|
|
|
free(ff->lookup); |
|
|
|
free(ff); |
|
|
|
seterrno(EINVAL); |
|
|
|
return NULL; |
|
|
|
} |
|
|
|
|
|
|
|
/* Import buffer into canvas */ |
|
|
|
ff->fontcv = cucul_create_canvas(0, 0); |
|
|
|
cucul_import_memory(ff->fontcv, data, i, "utf8"); |
|
|
|
free(data); |
|
|
|
|
|
|
|
/* Remove EOL characters. For now we ignore hardblanks, don’t do any |
|
|
|
* smushing, nor any kind of error checking. */ |
|
|
|
for(j = 0; j < ff->height * ff->glyphs; j++) |
|
|
|
{ |
|
|
|
unsigned long int ch, oldch = 0; |
|
|
|
|
|
|
|
for(i = ff->max_length; i--;) |
|
|
|
{ |
|
|
|
ch = cucul_get_char(ff->fontcv, i, j); |
|
|
|
|
|
|
|
/* Replace hardblanks with U+00A0 NO-BREAK SPACE */ |
|
|
|
if(ch == ff->hardblank) |
|
|
|
cucul_put_char(ff->fontcv, i, j, ch = 0xa0); |
|
|
|
|
|
|
|
if(oldch && ch != oldch) |
|
|
|
{ |
|
|
|
if(!ff->lookup[j / ff->height * 2 + 1]) |
|
|
|
ff->lookup[j / ff->height * 2 + 1] = i + 1; |
|
|
|
} |
|
|
|
else if(oldch && ch == oldch) |
|
|
|
cucul_put_char(ff->fontcv, i, j, ' '); |
|
|
|
else if(ch != ' ') |
|
|
|
{ |
|
|
|
oldch = ch; |
|
|
|
cucul_put_char(ff->fontcv, i, j, ' '); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return ff; |
|
|
|
} |
|
|
|
|
|
|
|
int _cucul_free_figfont(cucul_figfont_t *ff) |
|
|
|
{ |
|
|
|
cucul_free_canvas(ff->fontcv); |
|
|
|
free(ff->lookup); |
|
|
|
free(ff); |
|
|
|
|
|
|
|
return 0; |
|
|
|
} |
|
|
|
|