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.
 
 
 
 
 
 

904 rivejä
28 KiB

  1. /*
  2. * libcaca Colour ASCII-Art library
  3. * Copyright © 2002—2021 Sam Hocevar <sam@hocevar.net>
  4. * 2006 Jean-Yves Lamoureux <jylam@lnxscene.org>
  5. * All Rights Reserved
  6. *
  7. * This library is free software. It comes without any warranty, to
  8. * the extent permitted by applicable law. You can redistribute it
  9. * and/or modify it under the terms of the Do What the Fuck You Want
  10. * to Public License, Version 2, as published by Sam Hocevar. See
  11. * http://www.wtfpl.net/ for more details.
  12. */
  13. /*
  14. * This file contains text import and export functions
  15. */
  16. #include "config.h"
  17. #if !defined(__KERNEL__)
  18. # include <stdlib.h>
  19. # include <stdio.h>
  20. # include <string.h>
  21. #endif
  22. #include "caca.h"
  23. #include "caca_internals.h"
  24. #include "codec.h"
  25. struct import
  26. {
  27. uint32_t clearattr;
  28. /* ANSI Graphic Rendition Combination Mode */
  29. uint8_t fg, bg; /* ANSI-context fg/bg */
  30. uint8_t dfg, dbg; /* Default fg/bg */
  31. uint8_t bold, blink, italics, negative, concealed, underline;
  32. uint8_t faint, strike, proportional; /* unsupported */
  33. };
  34. static void ansi_parse_grcm(caca_canvas_t *, struct import *,
  35. unsigned int, unsigned int const *);
  36. ssize_t _import_text(caca_canvas_t *cv, void const *data, size_t size)
  37. {
  38. char const *text = (char const *)data;
  39. unsigned int width = 0, height = 0, x = 0, y = 0, i;
  40. caca_set_canvas_size(cv, 0, 0);
  41. for(i = 0; i < size; i++)
  42. {
  43. unsigned char ch = *text++;
  44. if(ch == '\r')
  45. continue;
  46. if(ch == '\n')
  47. {
  48. x = 0;
  49. y++;
  50. continue;
  51. }
  52. if(x >= width || y >= height)
  53. {
  54. if(x >= width)
  55. width = x + 1;
  56. if(y >= height)
  57. height = y + 1;
  58. if (caca_set_canvas_size(cv, width, height) < 0)
  59. return -1;
  60. }
  61. caca_put_char(cv, x, y, ch);
  62. x++;
  63. }
  64. if (y > height)
  65. {
  66. if (caca_set_canvas_size(cv, width, height = y) < 0)
  67. return -1;
  68. }
  69. return (ssize_t)size;
  70. }
  71. ssize_t _import_ansi(caca_canvas_t *cv, void const *data, size_t size, int utf8)
  72. {
  73. struct import im;
  74. unsigned char const *buffer = (unsigned char const*)data;
  75. unsigned int i, j, skip, growx = 0, growy = 0, dummy = 0;
  76. unsigned int width, height;
  77. uint32_t savedattr;
  78. int x = 0, y = 0, save_x = 0, save_y = 0;
  79. if(utf8)
  80. {
  81. width = cv->width;
  82. height = cv->height;
  83. growx = !width;
  84. growy = !height;
  85. x = cv->frames[cv->frame].x;
  86. y = cv->frames[cv->frame].y;
  87. }
  88. else
  89. {
  90. caca_set_canvas_size(cv, width = 80, height = 0);
  91. growx = 0;
  92. growy = 1;
  93. }
  94. if(utf8)
  95. {
  96. im.dfg = CACA_DEFAULT;
  97. im.dbg = CACA_TRANSPARENT;
  98. }
  99. else
  100. {
  101. im.dfg = CACA_LIGHTGRAY;
  102. im.dbg = CACA_BLACK;
  103. }
  104. caca_set_color_ansi(cv, im.dfg, im.dbg);
  105. im.clearattr = caca_get_attr(cv, -1, -1);
  106. ansi_parse_grcm(cv, &im, 1, &dummy);
  107. for(i = 0; i < size; i += skip)
  108. {
  109. uint32_t ch = 0;
  110. int wch = 0;
  111. skip = 1;
  112. if(!utf8 && buffer[i] == '\x1a' && i + 7 < size
  113. && !memcmp(buffer + i + 1, "SAUCE00", 7))
  114. break; /* End before SAUCE data */
  115. else if(buffer[i] == '\r')
  116. {
  117. x = 0;
  118. }
  119. else if(buffer[i] == '\n')
  120. {
  121. x = 0;
  122. y++;
  123. }
  124. else if(buffer[i] == '\t')
  125. {
  126. x = (x + 8) & ~7;
  127. }
  128. else if(buffer[i] == '\x08')
  129. {
  130. if(x > 0)
  131. x--;
  132. }
  133. /* If there are not enough characters to parse the escape sequence,
  134. * wait until the next try. We require 3. */
  135. else if(buffer[i] == '\033' && i + 2 >= size)
  136. break;
  137. /* XXX: What the fuck is this shit? */
  138. else if(buffer[i] == '\033' && buffer[i + 1] == '('
  139. && buffer[i + 2] == 'B')
  140. {
  141. skip += 2;
  142. }
  143. /* Interpret escape commands, as per Standard ECMA-48 "Control
  144. * Functions for Coded Character Sets", 5.4. Control sequences. */
  145. else if(buffer[i] == '\033' && buffer[i + 1] == '[')
  146. {
  147. unsigned int argc = 0, argv[101];
  148. unsigned int param, inter, final;
  149. /* Compute offsets to parameter bytes, intermediate bytes and
  150. * to the final byte. Only the final byte is mandatory, there
  151. * can be zero of the others.
  152. * 0 param=2 inter final final+1
  153. * +-----+------------------+---------------------+-----------------+
  154. * | CSI | parameter bytes | intermediate bytes | final byte |
  155. * | | 0x30 - 0x3f | 0x20 - 0x2f | 0x40 - 0x7e |
  156. * | ^[[ | 0123456789:;<=>? | SPC !"#$%&'()*+,-./ | azAZ@[\]^_`{|}~ |
  157. * +-----+------------------+---------------------+-----------------+
  158. */
  159. param = 2;
  160. for(inter = param; i + inter < size; inter++)
  161. if(buffer[i + inter] < 0x30 || buffer[i + inter] > 0x3f)
  162. break;
  163. for(final = inter; i + final < size; final++)
  164. if(buffer[i + final] < 0x20 || buffer[i + final] > 0x2f)
  165. break;
  166. if(i + final >= size
  167. || buffer[i + final] < 0x40 || buffer[i + final] > 0x7e)
  168. break; /* Invalid Final Byte */
  169. skip += final;
  170. /* Sanity checks */
  171. if(param < inter && buffer[i + param] >= 0x3c)
  172. {
  173. /* Private sequence, only parse what we know */
  174. debug("ansi import: private sequence \"^[[%.*s\"",
  175. final - param + 1, buffer + i + param);
  176. continue; /* Private sequence, skip it entirely */
  177. }
  178. if(final - param > 100)
  179. continue; /* Suspiciously long sequence, skip it */
  180. /* Parse parameter bytes as per ECMA-48 5.4.2: Parameter string
  181. * format */
  182. if(param < inter)
  183. {
  184. argv[0] = 0;
  185. for(j = param; j < inter; j++)
  186. {
  187. if(buffer[i + j] == ';')
  188. argv[++argc] = 0;
  189. else if(buffer[i + j] >= '0' && buffer[i + j] <= '9')
  190. argv[argc] = 10 * argv[argc] + (buffer[i + j] - '0');
  191. }
  192. argc++;
  193. }
  194. /* Interpret final byte. The code representations are given in
  195. * ECMA-48 5.4: Control sequences, and the code definitions are
  196. * given in ECMA-48 8.3: Definition of control functions. */
  197. switch(buffer[i + final])
  198. {
  199. case 'H': /* CUP (0x48) - Cursor Position */
  200. x = (argc > 1 && argv[1] > 0) ? argv[1] - 1 : 0;
  201. y = (argc > 0 && argv[0] > 0) ? argv[0] - 1 : 0;
  202. break;
  203. case 'A': /* CUU (0x41) - Cursor Up */
  204. y -= argc ? argv[0] : 1;
  205. if(y < 0)
  206. y = 0;
  207. break;
  208. case 'B': /* CUD (0x42) - Cursor Down */
  209. y += argc ? argv[0] : 1;
  210. break;
  211. case 'C': /* CUF (0x43) - Cursor Right */
  212. x += argc ? argv[0] : 1;
  213. break;
  214. case 'D': /* CUB (0x44) - Cursor Left */
  215. x -= argc ? argv[0] : 1;
  216. if(x < 0)
  217. x = 0;
  218. break;
  219. case 'G': /* CHA (0x47) - Cursor Character Absolute */
  220. x = (argc && argv[0] > 0) ? argv[0] - 1 : 0;
  221. break;
  222. case 'J': /* ED (0x4a) - Erase In Page */
  223. savedattr = caca_get_attr(cv, -1, -1);
  224. caca_set_attr(cv, im.clearattr);
  225. if(!argc || argv[0] == 0)
  226. {
  227. caca_draw_line(cv, x, y, width, y, ' ');
  228. caca_fill_box(cv, 0, y + 1, width - 1, height - 1, ' ');
  229. }
  230. else if(argv[0] == 1)
  231. {
  232. caca_fill_box(cv, 0, 0, width - 1, y - 1, ' ');
  233. caca_draw_line(cv, 0, y, x, y, ' ');
  234. }
  235. else if(argv[0] == 2)
  236. //x = y = 0;
  237. caca_fill_box(cv, 0, 0, width - 1, height - 1, ' ');
  238. caca_set_attr(cv, savedattr);
  239. break;
  240. case 'K': /* EL (0x4b) - Erase In Line */
  241. if(!argc || argv[0] == 0)
  242. caca_draw_line(cv, x, y, width, y, ' ');
  243. else if(argv[0] == 1)
  244. caca_draw_line(cv, 0, y, x, y, ' ');
  245. else if(argv[0] == 2)
  246. if((unsigned int)x < width)
  247. caca_draw_line(cv, x, y, width - 1, y, ' ');
  248. //x = width;
  249. break;
  250. case 'P': /* DCH (0x50) - Delete Character */
  251. if(!argc || argv[0] == 0)
  252. argv[0] = 1; /* echo -ne 'foobar\r\e[0P\n' */
  253. for(j = 0; (unsigned int)(j + argv[0]) < width; j++)
  254. {
  255. caca_put_char(cv, j, y,
  256. caca_get_char(cv, j + argv[0], y));
  257. caca_put_attr(cv, j, y,
  258. caca_get_attr(cv, j + argv[0], y));
  259. }
  260. #if 0
  261. savedattr = caca_get_attr(cv, -1, -1);
  262. caca_set_attr(cv, im.clearattr);
  263. for( ; (unsigned int)j < width; j++)
  264. caca_put_char(cv, j, y, ' ');
  265. caca_set_attr(cv, savedattr);
  266. #endif
  267. break;
  268. case 'X': /* ECH (0x58) - Erase Character */
  269. if(argc && argv[0])
  270. {
  271. savedattr = caca_get_attr(cv, -1, -1);
  272. caca_set_attr(cv, im.clearattr);
  273. caca_draw_line(cv, x, y, x + argv[0] - 1, y, ' ');
  274. caca_set_attr(cv, savedattr);
  275. }
  276. break;
  277. case 'd': /* VPA (0x64) - Line Position Absolute */
  278. y = (argc && argv[0] > 0) ? argv[0] - 1 : 0;
  279. break;
  280. case 'f': /* HVP (0x66) - Character And Line Position */
  281. x = (argc > 1 && argv[1] > 0) ? argv[1] - 1 : 0;
  282. y = (argc > 0 && argv[0] > 0) ? argv[0] - 1 : 0;
  283. break;
  284. case 'h': /* SM (0x68) - FIXME */
  285. debug("ansi import: set mode %i", argc ? (int)argv[0] : -1);
  286. break;
  287. case 'l': /* RM (0x6c) - FIXME */
  288. debug("ansi import: reset mode %i", argc ? (int)argv[0] : -1);
  289. break;
  290. case 'm': /* SGR (0x6d) - Select Graphic Rendition */
  291. if(argc)
  292. ansi_parse_grcm(cv, &im, argc, argv);
  293. else
  294. ansi_parse_grcm(cv, &im, 1, &dummy);
  295. break;
  296. case 's': /* Private (save cursor position) */
  297. save_x = x;
  298. save_y = y;
  299. break;
  300. case 'u': /* Private (reload cursor position) */
  301. x = save_x;
  302. y = save_y;
  303. break;
  304. default:
  305. debug("ansi import: unknown command \"^[[%.*s\"",
  306. final - param + 1, buffer + i + param);
  307. break;
  308. }
  309. }
  310. /* Parse OSC stuff. */
  311. else if(buffer[i] == '\033' && buffer[i + 1] == ']')
  312. {
  313. char *string;
  314. unsigned int command = 0;
  315. unsigned int mode = 2, semicolon, final;
  316. for(semicolon = mode; i + semicolon < size; semicolon++)
  317. {
  318. if(buffer[i + semicolon] < '0' || buffer[i + semicolon] > '9')
  319. break;
  320. command = 10 * command + (buffer[i + semicolon] - '0');
  321. }
  322. if(i + semicolon >= size || buffer[i + semicolon] != ';')
  323. break; /* Invalid Mode */
  324. for(final = semicolon + 1; i + final < size; final++)
  325. if(buffer[i + final] < 0x20)
  326. break;
  327. if(i + final >= size || buffer[i + final] != '\a')
  328. break; /* Not enough data or no bell found */
  329. /* FIXME: XTerm also reacts to <ESC><backslash> and <ST> */
  330. /* FIXME: differenciate between not enough data (try again)
  331. * and invalid data (print shit) */
  332. skip += final;
  333. string = malloc(final - (semicolon + 1) + 1);
  334. memcpy(string, buffer + (semicolon + 1), final - (semicolon + 1));
  335. string[final - (semicolon + 1)] = '\0';
  336. debug("ansi import: got OSC command %i string '%s'", command,
  337. string);
  338. free(string);
  339. }
  340. /* Form feed means a new frame */
  341. else if (i + 1 < size && buffer[i] == '\f' && buffer[i + 1] == '\n')
  342. {
  343. int f = caca_get_frame_count(cv);
  344. caca_create_frame(cv, f);
  345. caca_set_frame(cv, f);
  346. x = y = 0;
  347. skip++;
  348. }
  349. /* Get the character we’re going to paste */
  350. else if(utf8)
  351. {
  352. size_t bytes;
  353. if(i + 6 < size)
  354. ch = caca_utf8_to_utf32((char const *)(buffer + i), &bytes);
  355. else
  356. {
  357. /* Add a trailing zero to what we're going to read */
  358. char tmp[7];
  359. memcpy(tmp, buffer + i, size - i);
  360. tmp[size - i] = '\0';
  361. ch = caca_utf8_to_utf32(tmp, &bytes);
  362. }
  363. if(!bytes)
  364. {
  365. /* If the Unicode is invalid, assume it was latin1. */
  366. ch = buffer[i];
  367. bytes = 1;
  368. }
  369. wch = caca_utf32_is_fullwidth(ch) ? 2 : 1;
  370. skip += (int)(bytes - 1);
  371. }
  372. else
  373. {
  374. ch = caca_cp437_to_utf32(buffer[i]);
  375. wch = 1;
  376. }
  377. /* Wrap long lines or grow horizontally */
  378. while((unsigned int)x + wch > width)
  379. {
  380. if(growx)
  381. {
  382. savedattr = caca_get_attr(cv, -1, -1);
  383. caca_set_attr(cv, im.clearattr);
  384. if (caca_set_canvas_size(cv, width = x + wch, height) < 0)
  385. return -1;
  386. caca_set_attr(cv, savedattr);
  387. }
  388. else
  389. {
  390. x -= width;
  391. y++;
  392. }
  393. }
  394. /* Scroll or grow vertically */
  395. if((unsigned int)y >= height)
  396. {
  397. savedattr = caca_get_attr(cv, -1, -1);
  398. caca_set_attr(cv, im.clearattr);
  399. if(growy)
  400. {
  401. if (caca_set_canvas_size(cv, width, height = y + 1) < 0)
  402. return -1;
  403. }
  404. else
  405. {
  406. int lines = (y - height) + 1;
  407. for(j = 0; j + lines < height; j++)
  408. {
  409. memcpy(cv->attrs + j * cv->width,
  410. cv->attrs + (j + lines) * cv->width, cv->width * 4);
  411. memcpy(cv->chars + j * cv->width,
  412. cv->chars + (j + lines) * cv->width, cv->width * 4);
  413. }
  414. caca_fill_box(cv, 0, height - lines,
  415. cv->width - 1, height - 1, ' ');
  416. y -= lines;
  417. }
  418. caca_set_attr(cv, savedattr);
  419. }
  420. /* Now paste our character, if any */
  421. if(wch)
  422. {
  423. caca_put_char(cv, x, y, ch);
  424. x += wch;
  425. }
  426. }
  427. if(growy && (unsigned int)y > height)
  428. {
  429. savedattr = caca_get_attr(cv, -1, -1);
  430. caca_set_attr(cv, im.clearattr);
  431. if (caca_set_canvas_size(cv, width, height = y))
  432. return -1;
  433. caca_set_attr(cv, savedattr);
  434. }
  435. cv->frames[cv->frame].x = x;
  436. cv->frames[cv->frame].y = y;
  437. // if(utf8)
  438. // caca_set_attr(cv, savedattr);
  439. return i;
  440. }
  441. /* Generate UTF-8 representation of current canvas. */
  442. void *_export_utf8(caca_canvas_t const *cv, size_t *bytes, int cr)
  443. {
  444. static uint8_t const palette[] =
  445. {
  446. 0, 4, 2, 6, 1, 5, 3, 7,
  447. 8, 12, 10, 14, 9, 13, 11, 15
  448. };
  449. char *data, *cur;
  450. int x, y;
  451. /* 23 bytes assumed for max length per pixel ('\e[5;1;3x;4y;9x;10ym' plus
  452. * 4 max bytes for a UTF-8 character).
  453. * Add height*9 to that (zeroes color at the end and jump to next line) */
  454. *bytes = (cv->height * 9) + (cv->width * cv->height * 23);
  455. cur = data = malloc(*bytes);
  456. for(y = 0; y < cv->height; y++)
  457. {
  458. uint32_t *lineattr = cv->attrs + y * cv->width;
  459. uint32_t *linechar = cv->chars + y * cv->width;
  460. uint8_t prevfg = 0x10;
  461. uint8_t prevbg = 0x10;
  462. for(x = 0; x < cv->width; x++)
  463. {
  464. uint32_t attr = lineattr[x];
  465. uint32_t ch = linechar[x];
  466. uint8_t ansifg, ansibg, fg, bg;
  467. if(ch == CACA_MAGIC_FULLWIDTH)
  468. continue;
  469. ansifg = caca_attr_to_ansi_fg(attr);
  470. ansibg = caca_attr_to_ansi_bg(attr);
  471. fg = ansifg < 0x10 ? palette[ansifg] : 0x10;
  472. bg = ansibg < 0x10 ? palette[ansibg] : 0x10;
  473. /* TODO: the [0 could be omitted in some cases */
  474. if(fg != prevfg || bg != prevbg)
  475. {
  476. cur += sprintf(cur, "\033[0");
  477. if(fg < 8)
  478. cur += sprintf(cur, ";3%d", fg);
  479. else if(fg < 16)
  480. cur += sprintf(cur, ";1;3%d;9%d", fg - 8, fg - 8);
  481. if(bg < 8)
  482. cur += sprintf(cur, ";4%d", bg);
  483. else if(bg < 16)
  484. cur += sprintf(cur, ";5;4%d;10%d", bg - 8, bg - 8);
  485. cur += sprintf(cur, "m");
  486. }
  487. cur += caca_utf32_to_utf8(cur, ch);
  488. prevfg = fg;
  489. prevbg = bg;
  490. }
  491. if(prevfg != 0x10 || prevbg != 0x10)
  492. cur += sprintf(cur, "\033[0m");
  493. cur += sprintf(cur, cr ? "\r\n" : "\n");
  494. }
  495. /* Crop to really used size */
  496. debug("utf8 export: alloc %lu bytes, realloc %lu",
  497. (unsigned long int)*bytes, (unsigned long int)(cur - data));
  498. *bytes = (uintptr_t)(cur - data);
  499. data = realloc(data, *bytes);
  500. return data;
  501. }
  502. /* Generate ANSI representation of current canvas. */
  503. void *_export_ansi(caca_canvas_t const *cv, size_t *bytes)
  504. {
  505. static uint8_t const palette[] =
  506. {
  507. 0, 4, 2, 6, 1, 5, 3, 7,
  508. 8, 12, 10, 14, 9, 13, 11, 15
  509. };
  510. char *data, *cur;
  511. int x, y;
  512. uint8_t prevfg = -1;
  513. uint8_t prevbg = -1;
  514. /* 16 bytes assumed for max length per pixel ('\e[5;1;3x;4ym' plus
  515. * 1 byte for a CP437 character).
  516. * Add height*9 to that (zeroes color at the end and jump to next line) */
  517. *bytes = (cv->height * 9) + (cv->width * cv->height * 16);
  518. cur = data = malloc(*bytes);
  519. for(y = 0; y < cv->height; y++)
  520. {
  521. uint32_t *lineattr = cv->attrs + y * cv->width;
  522. uint32_t *linechar = cv->chars + y * cv->width;
  523. for(x = 0; x < cv->width; x++)
  524. {
  525. uint8_t ansifg = caca_attr_to_ansi_fg(lineattr[x]);
  526. uint8_t ansibg = caca_attr_to_ansi_bg(lineattr[x]);
  527. uint8_t fg = ansifg < 0x10 ? palette[ansifg] : CACA_LIGHTGRAY;
  528. uint8_t bg = ansibg < 0x10 ? palette[ansibg] : CACA_BLACK;
  529. uint32_t ch = linechar[x];
  530. if(ch == CACA_MAGIC_FULLWIDTH)
  531. ch = '?';
  532. if(fg != prevfg || bg != prevbg)
  533. {
  534. cur += sprintf(cur, "\033[0;");
  535. if(fg < 8)
  536. if(bg < 8)
  537. cur += sprintf(cur, "3%d;4%dm", fg, bg);
  538. else
  539. cur += sprintf(cur, "5;3%d;4%dm", fg, bg - 8);
  540. else
  541. if(bg < 8)
  542. cur += sprintf(cur, "1;3%d;4%dm", fg - 8, bg);
  543. else
  544. cur += sprintf(cur, "5;1;3%d;4%dm", fg - 8, bg - 8);
  545. }
  546. *cur++ = caca_utf32_to_cp437(ch);
  547. prevfg = fg;
  548. prevbg = bg;
  549. }
  550. if(cv->width == 80)
  551. {
  552. cur += sprintf(cur, "\033[s\n\033[u");
  553. }
  554. else
  555. {
  556. cur += sprintf(cur, "\033[0m\r\n");
  557. prevfg = -1;
  558. prevbg = -1;
  559. }
  560. }
  561. /* Crop to really used size */
  562. debug("ansi export: alloc %lu bytes, realloc %lu",
  563. (unsigned long int)*bytes, (unsigned long int)(cur - data));
  564. *bytes = (uintptr_t)(cur - data);
  565. data = realloc(data, *bytes);
  566. return data;
  567. }
  568. /* Export a text file with IRC colours */
  569. void *_export_irc(caca_canvas_t const *cv, size_t *bytes)
  570. {
  571. static uint8_t const palette[] =
  572. {
  573. 1, 2, 3, 10, 5, 6, 7, 15, /* Dark */
  574. 14, 12, 9, 11, 4, 13, 8, 0, /* Light */
  575. };
  576. char *data, *cur;
  577. int x, y;
  578. /* 14 bytes assumed for max length per pixel. Worst case scenario:
  579. * ^Cxx,yy 6 bytes
  580. * ^B^B 2 bytes
  581. * ch 6 bytes
  582. * 3 bytes for max length per line. Worst case scenario:
  583. * <spc> 1 byte (for empty lines)
  584. * \r\n 2 bytes
  585. * In real life, the average bytes per pixel value will be around 5.
  586. */
  587. *bytes = 2 + cv->height * (3 + cv->width * 14);
  588. cur = data = malloc(*bytes);
  589. for(y = 0; y < cv->height; y++)
  590. {
  591. uint32_t *lineattr = cv->attrs + y * cv->width;
  592. uint32_t *linechar = cv->chars + y * cv->width;
  593. uint8_t prevfg = 0x10;
  594. uint8_t prevbg = 0x10;
  595. for(x = 0; x < cv->width; x++)
  596. {
  597. uint32_t attr = lineattr[x];
  598. uint32_t ch = linechar[x];
  599. uint8_t ansifg, ansibg, fg, bg;
  600. if(ch == CACA_MAGIC_FULLWIDTH)
  601. continue;
  602. ansifg = caca_attr_to_ansi_fg(attr);
  603. ansibg = caca_attr_to_ansi_bg(attr);
  604. fg = ansifg < 0x10 ? palette[ansifg] : 0x10;
  605. bg = ansibg < 0x10 ? palette[ansibg] : 0x10;
  606. /* TODO: optimise series of same fg / same bg
  607. * don't change fg value if ch == ' '
  608. * make sure the \x03,%d trick works everywhere */
  609. if(bg != prevbg || fg != prevfg)
  610. {
  611. int need_escape = 0;
  612. if(bg == 0x10)
  613. {
  614. if(fg == 0x10)
  615. cur += sprintf(cur, "\x0f");
  616. else
  617. {
  618. if(prevbg == 0x10)
  619. cur += sprintf(cur, "\x03%d", fg);
  620. else
  621. cur += sprintf(cur, "\x0f\x03%d", fg);
  622. if(ch == (uint32_t)',')
  623. need_escape = 1;
  624. }
  625. }
  626. else
  627. {
  628. if(fg == 0x10)
  629. cur += sprintf(cur, "\x0f\x03,%d", bg);
  630. else
  631. cur += sprintf(cur, "\x03%d,%d", fg, bg);
  632. }
  633. if(ch >= (uint32_t)'0' && ch <= (uint32_t)'9')
  634. need_escape = 1;
  635. if(need_escape)
  636. cur += sprintf(cur, "\x02\x02");
  637. }
  638. cur += caca_utf32_to_utf8(cur, ch);
  639. prevfg = fg;
  640. prevbg = bg;
  641. }
  642. /* TODO: do the same the day we optimise whole lines above */
  643. if(!cv->width)
  644. *cur++ = ' ';
  645. *cur++ = '\r';
  646. *cur++ = '\n';
  647. }
  648. /* Crop to really used size */
  649. debug("IRC export: alloc %lu bytes, realloc %lu",
  650. (unsigned long int)*bytes, (unsigned long int)(cur - data));
  651. *bytes = (uintptr_t)(cur - data);
  652. data = realloc(data, *bytes);
  653. return data;
  654. }
  655. /* XXX : ANSI loader helper */
  656. static void ansi_parse_grcm(caca_canvas_t *cv, struct import *im,
  657. unsigned int argc, unsigned int const *argv)
  658. {
  659. static uint8_t const ansi2caca[] =
  660. {
  661. CACA_BLACK, CACA_RED, CACA_GREEN, CACA_BROWN,
  662. CACA_BLUE, CACA_MAGENTA, CACA_CYAN, CACA_LIGHTGRAY
  663. };
  664. unsigned int j;
  665. uint8_t efg, ebg; /* Effective (libcaca) fg/bg */
  666. for(j = 0; j < argc; j++)
  667. {
  668. /* Defined in ECMA-48 8.3.117: SGR - SELECT GRAPHIC RENDITION */
  669. if(argv[j] >= 30 && argv[j] <= 37)
  670. im->fg = ansi2caca[argv[j] - 30];
  671. else if(argv[j] >= 40 && argv[j] <= 47)
  672. im->bg = ansi2caca[argv[j] - 40];
  673. else if(argv[j] >= 90 && argv[j] <= 97)
  674. im->fg = ansi2caca[argv[j] - 90] + 8;
  675. else if(argv[j] >= 100 && argv[j] <= 107)
  676. im->bg = ansi2caca[argv[j] - 100] + 8;
  677. else switch(argv[j])
  678. {
  679. case 0: /* default rendition */
  680. im->fg = im->dfg;
  681. im->bg = im->dbg;
  682. im->bold = im->blink = im->italics = im->negative
  683. = im->concealed = im->underline = im->faint = im->strike
  684. = im->proportional = 0;
  685. break;
  686. case 1: /* bold or increased intensity */
  687. im->bold = 1;
  688. break;
  689. case 2: /* faint, decreased intensity or second colour */
  690. im->faint = 1;
  691. break;
  692. case 3: /* italicized */
  693. im->italics = 1;
  694. break;
  695. case 4: /* singly underlined */
  696. im->underline = 1;
  697. break;
  698. case 5: /* slowly blinking (less then 150 per minute) */
  699. case 6: /* rapidly blinking (150 per minute or more) */
  700. im->blink = 1;
  701. break;
  702. case 7: /* negative image */
  703. im->negative = 1;
  704. break;
  705. case 8: /* concealed characters */
  706. im->concealed = 1;
  707. break;
  708. case 9: /* crossed-out (characters still legible but marked as to be
  709. * deleted */
  710. im->strike = 1;
  711. break;
  712. case 21: /* doubly underlined */
  713. im->underline = 1;
  714. break;
  715. case 22: /* normal colour or normal intensity (neither bold nor
  716. * faint) */
  717. im->bold = im->faint = 0;
  718. break;
  719. case 23: /* not italicized, not fraktur */
  720. im->italics = 0;
  721. break;
  722. case 24: /* not underlined (neither singly nor doubly) */
  723. im->underline = 0;
  724. break;
  725. case 25: /* steady (not blinking) */
  726. im->blink = 0;
  727. break;
  728. case 26: /* (reserved for proportional spacing as specified in CCITT
  729. * Recommendation T.61) */
  730. im->proportional = 1;
  731. break;
  732. case 27: /* positive image */
  733. im->negative = 0;
  734. break;
  735. case 28: /* revealed characters */
  736. im->concealed = 0;
  737. break;
  738. case 29: /* not crossed out */
  739. im->strike = 0;
  740. break;
  741. case 38: /* (reserved for future standardization, intended for setting
  742. * character foreground colour as specified in ISO 8613-6
  743. * [CCITT Recommendation T.416]) */
  744. break;
  745. case 39: /* default display colour (implementation-defined) */
  746. im->fg = im->dfg;
  747. break;
  748. case 48: /* (reserved for future standardization, intended for setting
  749. * character background colour as specified in ISO 8613-6
  750. * [CCITT Recommendation T.416]) */
  751. break;
  752. case 49: /* default background colour (implementation-defined) */
  753. im->bg = im->dbg;
  754. break;
  755. case 50: /* (reserved for cancelling the effect of the rendering
  756. * aspect established by parameter value 26) */
  757. im->proportional = 0;
  758. break;
  759. default:
  760. debug("ansi import: unknown sgr %i", argv[j]);
  761. break;
  762. }
  763. }
  764. if(im->concealed)
  765. {
  766. efg = ebg = CACA_TRANSPARENT;
  767. }
  768. else
  769. {
  770. efg = im->negative ? im->bg : im->fg;
  771. ebg = im->negative ? im->fg : im->bg;
  772. if(im->bold)
  773. {
  774. if(efg < 8)
  775. efg += 8;
  776. else if(efg == CACA_DEFAULT)
  777. efg = CACA_WHITE;
  778. }
  779. }
  780. caca_set_color_ansi(cv, efg, ebg);
  781. }