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.
 
 
 
 
 
 

288 lines
7.2 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 "render.h"
  26. #include "io.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 flush_figlet(context_t *);
  31. static int end_figlet(context_t *);
  32. static int open_font(context_t *cx);
  33. int init_figlet(context_t *cx)
  34. {
  35. if(open_font(cx))
  36. return -1;
  37. cx->feed = feed_figlet;
  38. cx->flush = flush_figlet;
  39. cx->end = end_figlet;
  40. return 0;
  41. }
  42. static int feed_figlet(context_t *cx, uint32_t ch)
  43. {
  44. unsigned int c, w, h, x, y;
  45. switch(ch)
  46. {
  47. case (uint32_t)'\r':
  48. return 0;
  49. case (uint32_t)'\n':
  50. cx->x = 0;
  51. cx->y += cx->height;
  52. return 0;
  53. /* FIXME: handle '\t' */
  54. }
  55. /* Look whether our glyph is available */
  56. for(c = 0; c < cx->glyphs; c++)
  57. if(cx->lookup[c * 2] == ch)
  58. break;
  59. if(c == cx->glyphs)
  60. return 0;
  61. w = cx->lookup[c * 2 + 1];
  62. h = cx->height;
  63. /* Check whether we reached the end of the screen */
  64. if(cx->x && cx->x + w > cx->term_width)
  65. {
  66. cx->x = 0;
  67. cx->y += h;
  68. }
  69. /* Check whether the current canvas is large enough */
  70. if(cx->x + w > cx->w)
  71. cx->w = cx->x + w < cx->term_width ? cx->x + w : cx->term_width;
  72. if(cx->y + h > cx->h)
  73. cx->h = cx->y + h;
  74. cucul_set_canvas_size(cx->cv, cx->w, cx->h);
  75. /* Render our char (FIXME: create a rect-aware cucul_blit_canvas?) */
  76. for(y = 0; y < h; y++)
  77. for(x = 0; x < w; x++)
  78. {
  79. uint32_t tmp = cucul_get_char(cx->image, x, y + c * cx->height);
  80. cucul_put_char(cx->cv, cx->x + x, cx->y + y, tmp);
  81. }
  82. /* Advance cursor */
  83. cx->x += w;
  84. return 0;
  85. }
  86. static int flush_figlet(context_t *cx)
  87. {
  88. cx->torender = cx->cv;
  89. cucul_set_canvas_size(cx->torender, cx->w, cx->h);
  90. cx->x = cx->y = 0;
  91. cx->w = cx->h = 0;
  92. cx->cv = cucul_create_canvas(1, 1);
  93. return 0;
  94. }
  95. static int end_figlet(context_t *cx)
  96. {
  97. cucul_free_canvas(cx->image);
  98. free(cx->lookup);
  99. return 0;
  100. }
  101. static int open_font(context_t *cx)
  102. {
  103. char *data = NULL;
  104. char path[2048];
  105. char buf[2048];
  106. char hardblank[10];
  107. TOIFILE *f;
  108. unsigned int i, j, size, comment_lines;
  109. /* Open font: try .tlf, then .flf */
  110. snprintf(path, 2047, "%s/%s.tlf", cx->dir, cx->font);
  111. path[2047] = '\0';
  112. f = toiopen(path, "r");
  113. if(!f)
  114. {
  115. snprintf(path, 2047, "%s/%s.flf", cx->dir, cx->font);
  116. path[2047] = '\0';
  117. f = toiopen(path, "r");
  118. if(!f)
  119. {
  120. fprintf(stderr, "font `%s' not found\n", path);
  121. return -1;
  122. }
  123. }
  124. /* Read header */
  125. cx->print_direction = 0;
  126. cx->full_layout = 0;
  127. cx->codetag_count = 0;
  128. toigets(buf, 2048, f);
  129. if(sscanf(buf, "%*[ft]lf2a%6s %u %u %u %i %u %u %u %u\n", hardblank,
  130. &cx->height, &cx->baseline, &cx->max_length,
  131. &cx->old_layout, &comment_lines, &cx->print_direction,
  132. &cx->full_layout, &cx->codetag_count) < 6)
  133. {
  134. fprintf(stderr, "font `%s' has invalid header: %s\n", path, buf);
  135. toiclose(f);
  136. return -1;
  137. }
  138. cx->hardblank = cucul_utf8_to_utf32(hardblank, NULL);
  139. /* Skip comment lines */
  140. for(i = 0; i < comment_lines; i++)
  141. toigets(buf, 2048, f);
  142. /* Read mandatory characters (32-127, 196, 214, 220, 228, 246, 252, 223)
  143. * then read additional characters. */
  144. cx->glyphs = 0;
  145. cx->lookup = NULL;
  146. for(i = 0, size = 0; !toieof(f); cx->glyphs++)
  147. {
  148. if((cx->glyphs % 2048) == 0)
  149. cx->lookup = realloc(cx->lookup,
  150. (cx->glyphs + 2048) * 2 * sizeof(int));
  151. if(cx->glyphs < STD_GLYPHS)
  152. {
  153. cx->lookup[cx->glyphs * 2] = 32 + cx->glyphs;
  154. }
  155. else if(cx->glyphs < EXT_GLYPHS)
  156. {
  157. static int const tab[7] = { 196, 214, 220, 228, 246, 252, 223 };
  158. cx->lookup[cx->glyphs * 2] = tab[cx->glyphs - STD_GLYPHS];
  159. }
  160. else
  161. {
  162. if(toigets(buf, 2048, f) == NULL)
  163. break;
  164. /* Ignore blank lines, as in jacky.flf */
  165. if(buf[0] == '\n' || buf[0] == '\r')
  166. continue;
  167. /* Ignore negative indices for now, as in ivrit.flf */
  168. if(buf[0] == '-')
  169. {
  170. for(j = 0; j < cx->height; j++)
  171. toigets(buf, 2048, f);
  172. continue;
  173. }
  174. if(!buf[0] || buf[0] < '0' || buf[0] > '9')
  175. {
  176. free(data);
  177. free(cx->lookup);
  178. fprintf(stderr, "read error at glyph #%u in `%s'\n",
  179. cx->glyphs, path);
  180. return -1;
  181. }
  182. if(buf[1] == 'x')
  183. sscanf(buf, "%x", &cx->lookup[cx->glyphs * 2]);
  184. else
  185. sscanf(buf, "%u", &cx->lookup[cx->glyphs * 2]);
  186. }
  187. cx->lookup[cx->glyphs * 2 + 1] = 0;
  188. for(j = 0; j < cx->height; j++)
  189. {
  190. if(i + 2048 >= size)
  191. data = realloc(data, size += 2048);
  192. toigets(data + i, 2048, f);
  193. i = (uintptr_t)strchr(data + i, 0) - (uintptr_t)data;
  194. }
  195. }
  196. toiclose(f);
  197. if(cx->glyphs < EXT_GLYPHS)
  198. {
  199. free(data);
  200. free(cx->lookup);
  201. fprintf(stderr, "only %u glyphs in `%s', expected at least %u\n",
  202. cx->glyphs, path, EXT_GLYPHS);
  203. return -1;
  204. }
  205. /* Import buffer into canvas */
  206. cx->image = cucul_create_canvas(0, 0);
  207. cucul_import_memory(cx->image, data, i, "utf8");
  208. free(data);
  209. /* Remove EOL characters. For now we ignore hardblanks, don’t do any
  210. * smushing, nor any kind of error checking. */
  211. for(j = 0; j < cx->height * cx->glyphs; j++)
  212. {
  213. unsigned long int ch, oldch = 0;
  214. for(i = cx->max_length; i--;)
  215. {
  216. ch = cucul_get_char(cx->image, i, j);
  217. /* TODO: Replace hardblanks with U+00A0 NO-BREAK SPACE */
  218. if(ch == cx->hardblank)
  219. cucul_put_char(cx->image, i, j, ch = ' ');
  220. //cucul_put_char(cx->image, i, j, ch = 0xa0);
  221. if(oldch && ch != oldch)
  222. {
  223. if(!cx->lookup[j / cx->height * 2 + 1])
  224. cx->lookup[j / cx->height * 2 + 1] = i + 1;
  225. }
  226. else if(oldch && ch == oldch)
  227. cucul_put_char(cx->image, i, j, ' ');
  228. else if(ch != ' ')
  229. {
  230. oldch = ch;
  231. cucul_put_char(cx->image, i, j, ' ');
  232. }
  233. }
  234. }
  235. return 0;
  236. }