You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

275 lines
6.8 KiB

  1. /*
  2. * TOIlet The Other Implementation’s letters
  3. * Copyright (c) 2006 Sam Hocevar <sam@zoy.org>
  4. * All Rights Reserved
  5. *
  6. * $Id$
  7. *
  8. * This program is free software; you can redistribute it and/or
  9. * modify it under the terms of the Do What The Fuck You Want To
  10. * Public License, Version 2, as published by Sam Hocevar. See
  11. * http://sam.zoy.org/wtfpl/COPYING for more details.
  12. */
  13. /*
  14. * This file contains functions for handling FIGlet fonts.
  15. */
  16. #include "config.h"
  17. #if defined(HAVE_INTTYPES_H)
  18. # include <inttypes.h>
  19. #endif
  20. #include <stdio.h>
  21. #include <stdlib.h>
  22. #include <string.h>
  23. #include <cucul.h>
  24. #include "toilet.h"
  25. #include "io.h"
  26. #include "figlet.h"
  27. #define STD_GLYPHS (127 - 32)
  28. #define EXT_GLYPHS (STD_GLYPHS + 7)
  29. static int feed_figlet(context_t *, uint32_t);
  30. static int end_figlet(context_t *);
  31. static int open_font(context_t *cx);
  32. int init_figlet(context_t *cx)
  33. {
  34. if(open_font(cx))
  35. return -1;
  36. cx->x = cx->y = 0;
  37. cx->w = cx->h = 0;
  38. cx->cv = cucul_create_canvas(1, 1);
  39. cx->feed = feed_figlet;
  40. cx->end = end_figlet;
  41. return 0;
  42. }
  43. static int feed_figlet(context_t *cx, uint32_t ch)
  44. {
  45. unsigned int c, w, h, x, y;
  46. switch(ch)
  47. {
  48. case (uint32_t)'\r':
  49. return 0;
  50. case (uint32_t)'\n':
  51. cx->x = 0;
  52. cx->y += cx->height;
  53. return 0;
  54. /* FIXME: handle '\t' */
  55. }
  56. /* Look whether our glyph is available */
  57. for(c = 0; c < cx->glyphs; c++)
  58. if(cx->lookup[c * 2] == ch)
  59. break;
  60. if(c == cx->glyphs)
  61. return 0;
  62. w = cx->lookup[c * 2 + 1];
  63. h = cx->height;
  64. /* Check whether we reached the end of the screen */
  65. if(cx->x && cx->x + w > cx->term_width)
  66. {
  67. cx->x = 0;
  68. cx->y += h;
  69. }
  70. /* Check whether the current canvas is large enough */
  71. if(cx->x + w > cx->w)
  72. cx->w = cx->x + w < cx->term_width ? cx->x + w : cx->term_width;
  73. if(cx->y + h > cx->h)
  74. cx->h = cx->y + h;
  75. cucul_set_canvas_size(cx->cv, cx->w, cx->h);
  76. /* Render our char (FIXME: create a rect-aware cucul_blit_canvas?) */
  77. for(y = 0; y < h; y++)
  78. for(x = 0; x < w; x++)
  79. {
  80. uint32_t tmp = cucul_getchar(cx->image, x, y + c * cx->height);
  81. cucul_putchar(cx->cv, cx->x + x, cx->y + y, tmp);
  82. }
  83. /* Advance cursor */
  84. cx->x += w;
  85. return 0;
  86. }
  87. static int end_figlet(context_t *cx)
  88. {
  89. cucul_free_canvas(cx->image);
  90. free(cx->lookup);
  91. return 0;
  92. }
  93. static int open_font(context_t *cx)
  94. {
  95. char *data = NULL;
  96. char path[2048];
  97. char buf[2048];
  98. char hardblank[10];
  99. cucul_buffer_t *b;
  100. TOIFILE *f;
  101. unsigned int i, j, size, comment_lines;
  102. /* Open font: try .tlf, then .flf */
  103. snprintf(path, 2047, "%s/%s.tlf", cx->dir, cx->font);
  104. path[2047] = '\0';
  105. f = toiopen(path, "r");
  106. if(!f)
  107. {
  108. snprintf(path, 2047, "%s/%s.flf", cx->dir, cx->font);
  109. path[2047] = '\0';
  110. f = toiopen(path, "r");
  111. if(!f)
  112. {
  113. fprintf(stderr, "font `%s' not found\n", path);
  114. return -1;
  115. }
  116. }
  117. /* Read header */
  118. cx->print_direction = 0;
  119. cx->full_layout = 0;
  120. cx->codetag_count = 0;
  121. toigets(buf, 2048, f);
  122. if(sscanf(buf, "%*[ft]lf2a%6s %u %u %u %i %u %u %u %u\n", hardblank,
  123. &cx->height, &cx->baseline, &cx->max_length,
  124. &cx->old_layout, &comment_lines, &cx->print_direction,
  125. &cx->full_layout, &cx->codetag_count) < 6)
  126. {
  127. fprintf(stderr, "font `%s' has invalid header: %s\n", path, buf);
  128. toiclose(f);
  129. return -1;
  130. }
  131. cx->hardblank = cucul_utf8_to_utf32(hardblank, NULL);
  132. /* Skip comment lines */
  133. for(i = 0; i < comment_lines; i++)
  134. toigets(buf, 2048, f);
  135. /* Read mandatory characters (32-127, 196, 214, 220, 228, 246, 252, 223)
  136. * then read additional characters. */
  137. cx->glyphs = 0;
  138. cx->lookup = NULL;
  139. for(i = 0, size = 0; !toieof(f); cx->glyphs++)
  140. {
  141. if((cx->glyphs % 2048) == 0)
  142. cx->lookup = realloc(cx->lookup,
  143. (cx->glyphs + 2048) * 2 * sizeof(int));
  144. if(cx->glyphs < STD_GLYPHS)
  145. {
  146. cx->lookup[cx->glyphs * 2] = 32 + cx->glyphs;
  147. }
  148. else if(cx->glyphs < EXT_GLYPHS)
  149. {
  150. static int const tab[7] = { 196, 214, 220, 228, 246, 252, 223 };
  151. cx->lookup[cx->glyphs * 2] = tab[cx->glyphs - STD_GLYPHS];
  152. }
  153. else
  154. {
  155. if(toigets(buf, 2048, f) == NULL)
  156. break;
  157. if(!buf[0])
  158. {
  159. free(data);
  160. free(cx->lookup);
  161. fprintf(stderr, "read error at glyph %u in `%s'\n",
  162. cx->glyphs, path);
  163. return -1;
  164. }
  165. if(buf[1] == 'x')
  166. sscanf(buf, "%x", &cx->lookup[cx->glyphs * 2]);
  167. else
  168. sscanf(buf, "%u", &cx->lookup[cx->glyphs * 2]);
  169. }
  170. cx->lookup[cx->glyphs * 2 + 1] = 0;
  171. for(j = 0; j < cx->height; j++)
  172. {
  173. if(i + 2048 >= size)
  174. data = realloc(data, size += 2048);
  175. toigets(data + i, 2048, f);
  176. i = (uintptr_t)strchr(data + i, 0) - (uintptr_t)data;
  177. }
  178. }
  179. toiclose(f);
  180. if(cx->glyphs < EXT_GLYPHS)
  181. {
  182. free(data);
  183. free(cx->lookup);
  184. fprintf(stderr, "only %u glyphs in `%s', expected at least %u\n",
  185. cx->glyphs, path, EXT_GLYPHS);
  186. return -1;
  187. }
  188. /* Import buffer into canvas */
  189. b = cucul_load_memory(data, i);
  190. cx->image = cucul_import_canvas(b, "utf8");
  191. cucul_free_buffer(b);
  192. free(data);
  193. if(!cx->image)
  194. {
  195. free(cx->lookup);
  196. fprintf(stderr, "libcucul could not load data in `%s'\n", path);
  197. return -1;
  198. }
  199. /* Remove EOL characters. For now we ignore hardblanks, don’t do any
  200. * smushing, nor any kind of error checking. */
  201. for(j = 0; j < cx->height * cx->glyphs; j++)
  202. {
  203. unsigned long int ch, oldch = 0;
  204. for(i = cx->max_length; i--;)
  205. {
  206. ch = cucul_getchar(cx->image, i, j);
  207. /* TODO: Replace hardblanks with U+00A0 NO-BREAK SPACE */
  208. if(ch == cx->hardblank)
  209. cucul_putchar(cx->image, i, j, ch = ' ');
  210. //cucul_putchar(cx->image, i, j, ch = 0xa0);
  211. if(oldch && ch != oldch)
  212. {
  213. if(!cx->lookup[j / cx->height * 2 + 1])
  214. cx->lookup[j / cx->height * 2 + 1] = i + 1;
  215. }
  216. else if(oldch && ch == oldch)
  217. cucul_putchar(cx->image, i, j, ' ');
  218. else if(ch != ' ')
  219. {
  220. oldch = ch;
  221. cucul_putchar(cx->image, i, j, ' ');
  222. }
  223. }
  224. }
  225. return 0;
  226. }