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.
 
 
 
 
 
 

1100 regels
37 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 various 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. /* Big endian */
  26. static inline int sprintu32(char *s, uint32_t x)
  27. {
  28. s[0] = (uint8_t)(x >> 24);
  29. s[1] = (uint8_t)(x >> 16) & 0xff;
  30. s[2] = (uint8_t)(x >> 8) & 0xff;
  31. s[3] = (uint8_t)(x ) & 0xff;
  32. return 4;
  33. }
  34. /* Big endian */
  35. static inline int sprintu16(char *s, uint16_t x)
  36. {
  37. s[0] = (uint8_t)(x >> 8) & 0xff;
  38. s[1] = (uint8_t)(x ) & 0xff;
  39. return 2;
  40. }
  41. static inline int write_u8(char *s, uint8_t x)
  42. {
  43. s[0] = x;
  44. return 1;
  45. }
  46. static inline int write_string(char *s, char const *d)
  47. {
  48. int n = 0;
  49. for (; d[n]; ++n)
  50. s[n] = d[n];
  51. return n;
  52. }
  53. static void *export_caca(caca_canvas_t const *, size_t *);
  54. static void *export_html(caca_canvas_t const *, size_t *);
  55. static void *export_html3(caca_canvas_t const *, size_t *);
  56. static void *export_bbfr(caca_canvas_t const *, size_t *);
  57. static void *export_ps(caca_canvas_t const *, size_t *);
  58. static void *export_svg(caca_canvas_t const *, size_t *);
  59. static void *export_tga(caca_canvas_t const *, size_t *);
  60. static void *export_troff(caca_canvas_t const *, size_t *);
  61. /** \brief Export a canvas into a foreign format.
  62. *
  63. * This function exports a libcaca canvas into various foreign formats such
  64. * as ANSI art, HTML, IRC colours, etc. The returned pointer should be passed
  65. * to free() to release the allocated storage when it is no longer needed.
  66. *
  67. * Valid values for \c format are:
  68. * - \c "caca": export native libcaca files.
  69. * - \c "ansi": export ANSI art (CP437 charset with ANSI colour codes).
  70. * - \c "html": export an HTML page with CSS information.
  71. * - \c "html3": export an HTML table that should be compatible with
  72. * most navigators, including textmode ones.
  73. * - \c "irc": export UTF-8 text with mIRC colour codes.
  74. * - \c "ps": export a PostScript document.
  75. * - \c "svg": export an SVG vector image.
  76. * - \c "tga": export a TGA image.
  77. * - \c "troff": export a troff source.
  78. *
  79. * If an error occurs, NULL is returned and \b errno is set accordingly:
  80. * - \c EINVAL Unsupported format requested.
  81. * - \c ENOMEM Not enough memory to allocate output buffer.
  82. *
  83. * \param cv A libcaca canvas
  84. * \param format A string describing the requested output format.
  85. * \param bytes A pointer to a size_t where the number of allocated bytes
  86. * will be written.
  87. * \return A pointer to the exported memory area, or NULL in case of error.
  88. */
  89. void *caca_export_canvas_to_memory(caca_canvas_t const *cv, char const *format,
  90. size_t *bytes)
  91. {
  92. if(!strcasecmp("caca", format))
  93. return export_caca(cv, bytes);
  94. if(!strcasecmp("ansi", format))
  95. return _export_ansi(cv, bytes);
  96. if(!strcasecmp("utf8", format))
  97. return _export_utf8(cv, bytes, 0);
  98. if(!strcasecmp("utf8cr", format))
  99. return _export_utf8(cv, bytes, 1);
  100. if(!strcasecmp("html", format))
  101. return export_html(cv, bytes);
  102. if(!strcasecmp("html3", format))
  103. return export_html3(cv, bytes);
  104. if(!strcasecmp("bbfr", format))
  105. return export_bbfr(cv, bytes);
  106. if(!strcasecmp("irc", format))
  107. return _export_irc(cv, bytes);
  108. if(!strcasecmp("ps", format))
  109. return export_ps(cv, bytes);
  110. if(!strcasecmp("svg", format))
  111. return export_svg(cv, bytes);
  112. if(!strcasecmp("tga", format))
  113. return export_tga(cv, bytes);
  114. if(!strcasecmp("troff", format))
  115. return export_troff(cv, bytes);
  116. seterrno(EINVAL);
  117. return NULL;
  118. }
  119. /** \brief Export a canvas portion into a foreign format.
  120. *
  121. * This function exports a portion of a \e libcaca canvas into various
  122. * formats. For more information, see caca_export_canvas_to_memory().
  123. *
  124. * If an error occurs, NULL is returned and \b errno is set accordingly:
  125. * - \c EINVAL Unsupported format requested or invalid coordinates.
  126. * - \c ENOMEM Not enough memory to allocate output buffer.
  127. *
  128. * \param cv A libcaca canvas
  129. * \param x The leftmost coordinate of the area to export.
  130. * \param y The topmost coordinate of the area to export.
  131. * \param w The width of the area to export.
  132. * \param h The height of the area to export.
  133. * \param format A string describing the requested output format.
  134. * \param bytes A pointer to a size_t where the number of allocated bytes
  135. * will be written.
  136. * \return A pointer to the exported memory area, or NULL in case of error.
  137. */
  138. void *caca_export_area_to_memory(caca_canvas_t const *cv, int x, int y, int w,
  139. int h, char const *format, size_t *bytes)
  140. {
  141. caca_canvas_t *tmp;
  142. void *ret;
  143. if(w < 0 || h < 0 || x < 0 || y < 0
  144. || x + w > cv->width || y + h > cv->height)
  145. {
  146. seterrno(EINVAL);
  147. return NULL;
  148. }
  149. /* TODO: we need to spare the blit here by exporting the area we want. */
  150. tmp = caca_create_canvas(w, h);
  151. caca_blit(tmp, -x, -y, cv, NULL);
  152. ret = caca_export_canvas_to_memory(tmp, format, bytes);
  153. caca_free_canvas(tmp);
  154. return ret;
  155. }
  156. /** \brief Get available export formats
  157. *
  158. * Return a list of available export formats. The list is a NULL-terminated
  159. * array of strings, interleaving a string containing the internal value for
  160. * the export format, to be used with caca_export_memory(), and a string
  161. * containing the natural language description for that export format.
  162. *
  163. * This function never fails.
  164. *
  165. * \return An array of strings.
  166. */
  167. char const * const * caca_get_export_list(void)
  168. {
  169. static char const * const list[] =
  170. {
  171. "caca", "native libcaca format",
  172. "ansi", "ANSI",
  173. "utf8", "UTF-8 with ANSI escape codes",
  174. "utf8cr", "UTF-8 with ANSI escape codes and MS-DOS \\r",
  175. "html", "HTML",
  176. "html3", "backwards-compatible HTML",
  177. "bbfr", "BBCode (French)",
  178. "irc", "IRC with mIRC colours",
  179. "ps", "PostScript document",
  180. "svg", "SVG vector image",
  181. "tga", "TGA image",
  182. "troff", "troff source",
  183. NULL, NULL
  184. };
  185. return list;
  186. }
  187. /*
  188. * XXX: the following functions are local.
  189. */
  190. /* Generate a native libcaca canvas file. */
  191. static void *export_caca(caca_canvas_t const *cv, size_t *bytes)
  192. {
  193. char *data, *cur;
  194. int f, n;
  195. /* 52 bytes for the header:
  196. * - 4 bytes for "\xCA\xCA" + "CV"
  197. * - 16 bytes for the canvas header
  198. * - 32 bytes for the frame info
  199. * 8 bytes for each character cell */
  200. *bytes = 20 + (32 + 8 * cv->width * cv->height) * cv->framecount;
  201. cur = data = malloc(*bytes);
  202. /* magic */
  203. cur += sprintf(cur, "%s", "\xCA\xCA" "CV");
  204. /* canvas_header */
  205. cur += sprintu32(cur, 16 + 32 * cv->framecount);
  206. cur += sprintu32(cur, cv->width * cv->height * 8 * cv->framecount);
  207. cur += sprintu16(cur, 0x0001);
  208. cur += sprintu32(cur, cv->framecount);
  209. cur += sprintu16(cur, 0x0000);
  210. /* frame_info */
  211. for(f = 0; f < cv->framecount; f++)
  212. {
  213. cur += sprintu32(cur, cv->width);
  214. cur += sprintu32(cur, cv->height);
  215. cur += sprintu32(cur, 0);
  216. cur += sprintu32(cur, cv->curattr);
  217. cur += sprintu32(cur, cv->frames[f].x);
  218. cur += sprintu32(cur, cv->frames[f].y);
  219. cur += sprintu32(cur, cv->frames[f].handlex);
  220. cur += sprintu32(cur, cv->frames[f].handley);
  221. }
  222. /* canvas_data */
  223. for(f = 0; f < cv->framecount; f++)
  224. {
  225. uint32_t *attrs = cv->frames[f].attrs;
  226. uint32_t *chars = cv->frames[f].chars;
  227. for(n = cv->height * cv->width; n--; )
  228. {
  229. cur += sprintu32(cur, *chars++);
  230. cur += sprintu32(cur, *attrs++);
  231. }
  232. }
  233. return data;
  234. }
  235. /* Generate HTML representation of current canvas. */
  236. static void *export_html(caca_canvas_t const *cv, size_t *bytes)
  237. {
  238. char *data, *cur;
  239. int x, y, len;
  240. /* The HTML header: less than 1000 bytes
  241. * A line: 7 chars for "<br />\n"
  242. * A glyph: 47 chars for "<span style="color:#xxx;background-color:#xxx">"
  243. * 83 chars for ";font-weight..."
  244. * up to 10 chars for "&#xxxxxxx;", far less for pure ASCII
  245. * 7 chars for "</span>" */
  246. *bytes = 1000 + cv->height * (7 + cv->width * (47 + 83 + 10 + 7));
  247. cur = data = malloc(*bytes);
  248. /* HTML header */
  249. cur += sprintf(cur, "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\"\n");
  250. cur += sprintf(cur, " \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n");
  251. cur += sprintf(cur, "<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"en\" xml:lang=\"en\">");
  252. cur += sprintf(cur, "<head>\n");
  253. cur += sprintf(cur, "<title>Generated by libcaca %s</title>\n",
  254. caca_get_version());
  255. cur += sprintf(cur, "</head><body>\n");
  256. cur += sprintf(cur, "<div style=\"%s\">\n",
  257. "font-family: monospace, fixed; font-weight: bold;");
  258. for(y = 0; y < cv->height; y++)
  259. {
  260. uint32_t *lineattr = cv->attrs + y * cv->width;
  261. uint32_t *linechar = cv->chars + y * cv->width;
  262. for(x = 0; x < cv->width; x += len)
  263. {
  264. cur += sprintf(cur, "<span style=\"");
  265. if(caca_attr_to_ansi_fg(lineattr[x]) != CACA_DEFAULT)
  266. cur += sprintf(cur, ";color:#%.03x",
  267. caca_attr_to_rgb12_fg(lineattr[x]));
  268. if(caca_attr_to_ansi_bg(lineattr[x]) < 0x10)
  269. cur += sprintf(cur, ";background-color:#%.03x",
  270. caca_attr_to_rgb12_bg(lineattr[x]));
  271. if(lineattr[x] & CACA_BOLD)
  272. cur += sprintf(cur, ";font-weight:bold");
  273. if(lineattr[x] & CACA_ITALICS)
  274. cur += sprintf(cur, ";font-style:italic");
  275. if(lineattr[x] & CACA_UNDERLINE)
  276. cur += sprintf(cur, ";text-decoration:underline");
  277. if(lineattr[x] & CACA_BLINK)
  278. cur += sprintf(cur, ";text-decoration:blink");
  279. cur += sprintf(cur, "\">");
  280. for(len = 0;
  281. x + len < cv->width && lineattr[x + len] == lineattr[x];
  282. len++)
  283. {
  284. if(linechar[x + len] == CACA_MAGIC_FULLWIDTH)
  285. ;
  286. else if((linechar[x + len] <= 0x00000020)
  287. ||
  288. ((linechar[x + len] >= 0x0000007f)
  289. &&
  290. (linechar[x + len] <= 0x000000a0)))
  291. {
  292. /* Control characters and space converted to
  293. * U+00A0 NO-BREAK SPACE, a.k.a. "&nbsp;" in HTML,
  294. * but we use the equivalent numeric character
  295. * reference &#160; so this will work in plain
  296. * XHTML with no DTD too. */
  297. cur += sprintf(cur, "&#160;");
  298. }
  299. else if(linechar[x + len] == '&')
  300. cur += sprintf(cur, "&amp;");
  301. else if(linechar[x + len] == '<')
  302. cur += sprintf(cur, "&lt;");
  303. else if(linechar[x + len] == '>')
  304. cur += sprintf(cur, "&gt;");
  305. else if(linechar[x + len] == '\"')
  306. cur += sprintf(cur, "&quot;");
  307. else if(linechar[x + len] == '\'')
  308. cur += sprintf(cur, "&#39;");
  309. else if(linechar[x + len] < 0x00000080)
  310. cur += write_u8(cur, (uint8_t)linechar[x + len]);
  311. else if((linechar[x + len] <= 0x0010fffd)
  312. &&
  313. ((linechar[x + len] & 0x0000fffe) != 0x0000fffe)
  314. &&
  315. ((linechar[x + len] < 0x0000d800)
  316. ||
  317. (linechar[x + len] > 0x0000dfff)))
  318. cur += sprintf(cur, "&#%i;", (unsigned int)linechar[x + len]);
  319. else
  320. /* non-character codepoints become U+FFFD
  321. * REPLACEMENT CHARACTER */
  322. cur += sprintf(cur, "&#%i;", (unsigned int)0x0000fffd);
  323. }
  324. cur += sprintf(cur, "</span>");
  325. }
  326. /* New line */
  327. cur += sprintf(cur, "<br />\n");
  328. }
  329. cur += sprintf(cur, "</div></body></html>\n");
  330. /* Crop to really used size */
  331. debug("html export: alloc %lu bytes, realloc %lu",
  332. (unsigned long int)*bytes, (unsigned long int)(cur - data));
  333. *bytes = (uintptr_t)(cur - data);
  334. data = realloc(data, *bytes);
  335. return data;
  336. }
  337. /* Export an HTML3 document. This function is way bigger than export_html(),
  338. * but permits viewing in old browsers (or limited ones such as links). It
  339. * will not work under gecko (mozilla rendering engine) unless you set a
  340. * correct header. */
  341. static void *export_html3(caca_canvas_t const *cv, size_t *bytes)
  342. {
  343. char *data, *cur;
  344. int x, y, len;
  345. int has_multi_cell_row = 0;
  346. unsigned char *cell_boundary_bitmap;
  347. /* Table */
  348. cell_boundary_bitmap = (unsigned char *) malloc((cv->width + 7) / 8);
  349. if(cell_boundary_bitmap)
  350. memset((void *) cell_boundary_bitmap, 0, (cv->width + 7) / 8);
  351. for(y = 0; y < cv->height; y++)
  352. {
  353. uint32_t *lineattr = cv->attrs + y * cv->width;
  354. uint32_t *linechar = cv->chars + y * cv->width;
  355. for(x = 1; x < cv->width; x++)
  356. if((! (cell_boundary_bitmap
  357. ?
  358. (cell_boundary_bitmap[x / 8] & (1 << (x % 8)))
  359. :
  360. has_multi_cell_row))
  361. &&
  362. (((linechar[x - 1] == CACA_MAGIC_FULLWIDTH)
  363. &&
  364. (! caca_utf32_is_fullwidth(linechar[x])))
  365. ||
  366. (caca_attr_to_ansi_bg(lineattr[x - 1])
  367. !=
  368. caca_attr_to_ansi_bg(lineattr[x]))
  369. ||
  370. ((caca_attr_to_ansi_bg(lineattr[x]) < 0x10)
  371. ?
  372. (_caca_attr_to_rgb24bg(lineattr[x - 1])
  373. !=
  374. _caca_attr_to_rgb24bg(lineattr[x]))
  375. :
  376. 0)))
  377. {
  378. has_multi_cell_row = 1;
  379. if(cell_boundary_bitmap)
  380. cell_boundary_bitmap[x / 8] |= 1 << (x % 8);
  381. }
  382. }
  383. /* The HTML table markup: less than 1000 bytes
  384. * A line: 10 chars for "<tr></tr>\n"
  385. * A glyph: up to 48 chars for "<td bgcolor=\"#xxxxxx\"><tt><font color=\"#xxxxxx\">"
  386. * up to 36 chars for "<b><i><u><blink></blink></u></i></b>"
  387. * up to 10 chars for "&#xxxxxxx;" (far less for pure ASCII)
  388. * 17 chars for "</font></tt></td>" */
  389. *bytes = 1000 + cv->height * (10 + cv->width * (48 + 36 + 10 + 17));
  390. cur = data = malloc(*bytes);
  391. cur += sprintf(cur, "<table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" summary=\"[libcaca canvas export]\">\n");
  392. for(y = 0; y < cv->height; y++)
  393. {
  394. uint32_t *lineattr = cv->attrs + y * cv->width;
  395. uint32_t *linechar = cv->chars + y * cv->width;
  396. cur += sprintf(cur, "<tr>");
  397. for(x = 0; x < cv->width; x += len)
  398. {
  399. int i, needfont = 0;
  400. int nonblank = 0;
  401. /* Use colspan option to factor cells with same attributes
  402. * (see below) */
  403. len = 1;
  404. while((x + len < cv->width)
  405. &&
  406. ((y
  407. &&
  408. (linechar[x + len] > 0x00000020)
  409. &&
  410. ((linechar[x + len] < 0x0000007f)
  411. ||
  412. (linechar[x + len] > 0x000000a0)))
  413. ||
  414. (! (cell_boundary_bitmap
  415. ?
  416. (cell_boundary_bitmap[(x + len) / 8] & (1 << ((x + len) % 8)))
  417. :
  418. has_multi_cell_row))
  419. ||
  420. (linechar[x + len] == CACA_MAGIC_FULLWIDTH)
  421. ||
  422. (cv->height == 1))
  423. &&
  424. ((linechar[x + len - 1] != CACA_MAGIC_FULLWIDTH)
  425. ||
  426. caca_utf32_is_fullwidth(linechar[x + len]))
  427. &&
  428. (caca_attr_to_ansi_bg(lineattr[x + len])
  429. ==
  430. caca_attr_to_ansi_bg(lineattr[x]))
  431. &&
  432. ((caca_attr_to_ansi_bg(lineattr[x]) < 0x10)
  433. ?
  434. (_caca_attr_to_rgb24bg(lineattr[x + len])
  435. ==
  436. _caca_attr_to_rgb24bg(lineattr[x]))
  437. :
  438. 1))
  439. len++;
  440. for(i = 0; i < len; i++)
  441. if(! ((linechar[x + i] <= 0x00000020)
  442. ||
  443. ((linechar[x + i] >= 0x0000007f)
  444. &&
  445. (linechar[x + i] <= 0x000000a0))))
  446. nonblank = 1;
  447. cur += sprintf(cur, "<td");
  448. if(caca_attr_to_ansi_bg(lineattr[x]) < 0x10)
  449. cur += sprintf(cur, " bgcolor=\"#%.06lx\"", (unsigned long int)
  450. _caca_attr_to_rgb24bg(lineattr[x]));
  451. if(has_multi_cell_row && (len > 1))
  452. {
  453. int colspan;
  454. colspan = len;
  455. if(cell_boundary_bitmap)
  456. for(i = 0; i < len; i ++)
  457. if(i
  458. &&
  459. ! (cell_boundary_bitmap[(x + i) / 8]
  460. &
  461. (1 << ((x + i) % 8))))
  462. colspan --;
  463. if(colspan > 1)
  464. cur += sprintf(cur, " colspan=\"%d\"", colspan);
  465. }
  466. cur += sprintf(cur, ">");
  467. cur += sprintf(cur, "<tt>");
  468. for(i = 0; i < len; i++)
  469. {
  470. if(nonblank
  471. &&
  472. ((! i)
  473. ||
  474. (lineattr[x + i] != lineattr[x + i - 1])))
  475. {
  476. needfont = (caca_attr_to_ansi_fg(lineattr[x + i])
  477. !=
  478. CACA_DEFAULT);
  479. if(needfont)
  480. cur += sprintf(cur, "<font color=\"#%.06lx\">",
  481. (unsigned long int)
  482. _caca_attr_to_rgb24fg(lineattr[x + i]));
  483. if(lineattr[x + i] & CACA_BOLD)
  484. cur += sprintf(cur, "<b>");
  485. if(lineattr[x + i] & CACA_ITALICS)
  486. cur += sprintf(cur, "<i>");
  487. if(lineattr[x + i] & CACA_UNDERLINE)
  488. cur += sprintf(cur, "<u>");
  489. if(lineattr[x + i] & CACA_BLINK)
  490. cur += sprintf(cur, "<blink>");
  491. }
  492. if(linechar[x + i] == CACA_MAGIC_FULLWIDTH)
  493. ;
  494. else if((linechar[x + i] <= 0x00000020)
  495. ||
  496. ((linechar[x + i] >= 0x0000007f)
  497. &&
  498. (linechar[x + i] <= 0x000000a0)))
  499. {
  500. /* Control characters and space converted to
  501. * U+00A0 NO-BREAK SPACE, a.k.a. "&nbsp;" in HTML,
  502. * but we use the equivalent numeric character
  503. * reference &#160; so this will work in plain
  504. * XHTML with no DTD too. */
  505. cur += sprintf(cur, "&#160;");
  506. }
  507. else if(linechar[x + i] == '&')
  508. cur += sprintf(cur, "&amp;");
  509. else if(linechar[x + i] == '<')
  510. cur += sprintf(cur, "&lt;");
  511. else if(linechar[x + i] == '>')
  512. cur += sprintf(cur, "&gt;");
  513. else if(linechar[x + i] == '\"')
  514. cur += sprintf(cur, "&quot;");
  515. else if(linechar[x + i] == '\'')
  516. cur += sprintf(cur, "&#39;");
  517. else if(linechar[x + i] < 0x00000080)
  518. cur += write_u8(cur, (uint8_t)linechar[x + i]);
  519. else if((linechar[x + i] <= 0x0010fffd)
  520. &&
  521. ((linechar[x + i] & 0x0000fffe) != 0x0000fffe)
  522. &&
  523. ((linechar[x + i] < 0x0000d800)
  524. ||
  525. (linechar[x + i] > 0x0000dfff)))
  526. cur += sprintf(cur, "&#%i;", (unsigned int)linechar[x + i]);
  527. else
  528. /* non-character codepoints become U+FFFD
  529. * REPLACEMENT CHARACTER */
  530. cur += sprintf(cur, "&#%i;", (unsigned int)0x0000fffd);
  531. if (nonblank
  532. &&
  533. (((i + 1) == len)
  534. ||
  535. (lineattr[x + i + 1] != lineattr[x + i])))
  536. {
  537. if(lineattr[x + i] & CACA_BLINK)
  538. cur += sprintf(cur, "</blink>");
  539. if(lineattr[x + i] & CACA_UNDERLINE)
  540. cur += sprintf(cur, "</u>");
  541. if(lineattr[x + i] & CACA_ITALICS)
  542. cur += sprintf(cur, "</i>");
  543. if(lineattr[x + i] & CACA_BOLD)
  544. cur += sprintf(cur, "</b>");
  545. if(needfont)
  546. cur += sprintf(cur, "</font>");
  547. }
  548. }
  549. cur += sprintf(cur, "</tt>");
  550. cur += sprintf(cur, "</td>");
  551. }
  552. cur += sprintf(cur, "</tr>\n");
  553. }
  554. /* Footer */
  555. cur += sprintf(cur, "</table>\n");
  556. /* Free working memory */
  557. if (cell_boundary_bitmap)
  558. free((void *) cell_boundary_bitmap);
  559. /* Crop to really used size */
  560. debug("html3 export: alloc %lu bytes, realloc %lu",
  561. (unsigned long int)*bytes, (unsigned long int)(cur - data));
  562. *bytes = (uintptr_t)(cur - data);
  563. data = realloc(data, *bytes);
  564. return data;
  565. }
  566. static void *export_bbfr(caca_canvas_t const *cv, size_t *bytes)
  567. {
  568. char *data, *cur;
  569. int x, y, len;
  570. /* The font markup: less than 100 bytes
  571. * A line: 1 char for "\n"
  572. * A glyph: 22 chars for "[f=#xxxxxx][c=#xxxxxx]"
  573. * up to 21 chars for "[g][i][s][/s][/i][/g]"
  574. * up to 6 chars for the UTF-8 glyph
  575. * 8 chars for "[/c][/f]" */
  576. *bytes = 100 + cv->height * (1 + cv->width * (22 + 21 + 6 + 8));
  577. cur = data = malloc(*bytes);
  578. /* Table */
  579. cur += sprintf(cur, "[font=Courier New]");
  580. for(y = 0; y < cv->height; y++)
  581. {
  582. uint32_t *lineattr = cv->attrs + y * cv->width;
  583. uint32_t *linechar = cv->chars + y * cv->width;
  584. for(x = 0; x < cv->width; x += len)
  585. {
  586. int i, needback, needfront;
  587. /* Use colspan option to factor cells with same attributes
  588. * (see below) */
  589. len = 1;
  590. if(linechar[x] == ' ')
  591. while(x + len < cv->width && lineattr[x + len] == lineattr[x]
  592. && linechar[x] == ' ')
  593. len++;
  594. else
  595. while(x + len < cv->width && lineattr[x + len] == lineattr[x]
  596. && linechar[x] != ' ')
  597. len++;
  598. needback = caca_attr_to_ansi_bg(lineattr[x]) < 0x10;
  599. needfront = caca_attr_to_ansi_fg(lineattr[x]) < 0x10;
  600. if(needback)
  601. cur += sprintf(cur, "[f=#%.06lx]", (unsigned long int)
  602. _caca_attr_to_rgb24bg(lineattr[x]));
  603. if(linechar[x] == ' ')
  604. cur += sprintf(cur, "[c=#%.06lx]", (unsigned long int)
  605. _caca_attr_to_rgb24bg(lineattr[x]));
  606. else if(needfront)
  607. cur += sprintf(cur, "[c=#%.06lx]", (unsigned long int)
  608. _caca_attr_to_rgb24fg(lineattr[x]));
  609. if(lineattr[x] & CACA_BOLD)
  610. cur += sprintf(cur, "[g]");
  611. if(lineattr[x] & CACA_ITALICS)
  612. cur += sprintf(cur, "[i]");
  613. if(lineattr[x] & CACA_UNDERLINE)
  614. cur += sprintf(cur, "[s]");
  615. if(lineattr[x] & CACA_BLINK)
  616. ; /* FIXME */
  617. for(i = 0; i < len; i++)
  618. {
  619. if(linechar[x + i] == CACA_MAGIC_FULLWIDTH)
  620. ;
  621. else if(linechar[x + i] == ' ')
  622. *cur++ = '_';
  623. else
  624. cur += caca_utf32_to_utf8(cur, linechar[x + i]);
  625. }
  626. if(lineattr[x] & CACA_BLINK)
  627. ; /* FIXME */
  628. if(lineattr[x] & CACA_UNDERLINE)
  629. cur += sprintf(cur, "[/s]");
  630. if(lineattr[x] & CACA_ITALICS)
  631. cur += sprintf(cur, "[/i]");
  632. if(lineattr[x] & CACA_BOLD)
  633. cur += sprintf(cur, "[/g]");
  634. if(linechar[x] == ' ' || needfront)
  635. cur += sprintf(cur, "[/c]");
  636. if(needback)
  637. cur += sprintf(cur, "[/f]");
  638. }
  639. cur += sprintf(cur, "\n");
  640. }
  641. /* Footer */
  642. cur += sprintf(cur, "[/font]\n");
  643. /* Crop to really used size */
  644. debug("bbfr export: alloc %lu bytes, realloc %lu",
  645. (unsigned long int)*bytes, (unsigned long int)(cur - data));
  646. *bytes = (uintptr_t)(cur - data);
  647. data = realloc(data, *bytes);
  648. return data;
  649. }
  650. /* Export a PostScript document. */
  651. static void *export_ps(caca_canvas_t const *cv, size_t *bytes)
  652. {
  653. static char const *ps_header =
  654. "%!\n"
  655. "%% libcaca PDF export\n"
  656. "%%LanguageLevel: 2\n"
  657. "%%Pages: 1\n"
  658. "%%DocumentData: Clean7Bit\n"
  659. "/csquare {\n"
  660. " newpath\n"
  661. " 0 0 moveto\n"
  662. " 0 1 rlineto\n"
  663. " 1 0 rlineto\n"
  664. " 0 -1 rlineto\n"
  665. " closepath\n"
  666. " setrgbcolor\n"
  667. " fill\n"
  668. "} def\n"
  669. "/S {\n"
  670. " Show\n"
  671. "} bind def\n"
  672. "/Courier-Bold findfont\n"
  673. "8 scalefont\n"
  674. "setfont\n"
  675. "gsave\n"
  676. "6 10 scale\n";
  677. char *data, *cur;
  678. int x, y;
  679. /* 200 is arbitrary but should be ok */
  680. *bytes = strlen(ps_header) + 100 + cv->height * (32 + cv->width * 200);
  681. cur = data = malloc(*bytes);
  682. /* Header */
  683. cur += sprintf(cur, "%s", ps_header);
  684. cur += sprintf(cur, "0 %d translate\n", cv->height);
  685. /* Background, drawn using csquare macro defined in header */
  686. for(y = cv->height; y--; )
  687. {
  688. uint32_t *lineattr = cv->attrs + y * cv->width;
  689. for(x = 0; x < cv->width; x++)
  690. {
  691. uint8_t argb[8];
  692. caca_attr_to_argb64(*lineattr++, argb);
  693. cur += sprintf(cur, "1 0 translate\n %f %f %f csquare\n",
  694. (float)argb[1] * (1.0 / 0xf),
  695. (float)argb[2] * (1.0 / 0xf),
  696. (float)argb[3] * (1.0 / 0xf));
  697. }
  698. /* Return to beginning of the line, and jump to the next one */
  699. cur += sprintf(cur, "-%d 1 translate\n", cv->width);
  700. }
  701. cur += sprintf(cur, "grestore\n"); /* Restore transformation matrix */
  702. cur += sprintf(cur, "0 %d translate\n", cv->height*10);
  703. for(y = cv->height; y--; )
  704. {
  705. uint32_t *lineattr = cv->attrs + (cv->height - y - 1) * cv->width;
  706. uint32_t *linechar = cv->chars + (cv->height - y - 1) * cv->width;
  707. for(x = 0; x < cv->width; x++)
  708. {
  709. uint8_t argb[8];
  710. uint32_t ch = *linechar++;
  711. caca_attr_to_argb64(*lineattr++, argb);
  712. cur += sprintf(cur, "newpath\n");
  713. cur += sprintf(cur, "%d %d moveto\n", (x + 1) * 6, y * 10 + 2);
  714. cur += sprintf(cur, "%f %f %f setrgbcolor\n",
  715. (float)argb[5] * (1.0 / 0xf),
  716. (float)argb[6] * (1.0 / 0xf),
  717. (float)argb[7] * (1.0 / 0xf));
  718. if(ch < 0x00000020)
  719. cur += sprintf(cur, "(?) show\n");
  720. else if(ch >= 0x00000080)
  721. cur += sprintf(cur, "(?) show\n");
  722. else switch((uint8_t)(ch & 0x7f))
  723. {
  724. case '\\':
  725. case '(':
  726. case ')':
  727. cur += sprintf(cur, "(\\%c) show\n", (uint8_t)ch);
  728. break;
  729. default:
  730. cur += sprintf(cur, "(%c) show\n", (uint8_t)ch);
  731. break;
  732. }
  733. }
  734. }
  735. cur += sprintf(cur, "showpage\n");
  736. /* Crop to really used size */
  737. debug("PS export: alloc %lu bytes, realloc %lu",
  738. (unsigned long int)*bytes, (unsigned long int)(cur - data));
  739. *bytes = (uintptr_t)(cur - data);
  740. data = realloc(data, *bytes);
  741. return data;
  742. }
  743. /* Export an SVG vector image */
  744. static void *export_svg(caca_canvas_t const *cv, size_t *bytes)
  745. {
  746. static char const svg_header[] =
  747. "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
  748. "<svg width=\"%d\" height=\"%d\" viewBox=\"0 0 %d %d\""
  749. " xmlns=\"http://www.w3.org/2000/svg\""
  750. " xmlns:xlink=\"http://www.w3.org/1999/xlink\""
  751. " xml:space=\"preserve\" version=\"1.1\" baseProfile=\"full\">\n";
  752. char *data, *cur;
  753. int x, y;
  754. /* Use 200 as a safety value for character information size
  755. *
  756. * Worst case for background, 68 chars:
  757. * <rect style="fill:#fff" x="65535" y="65535" width="6" height="10"/>\n
  758. * Worst case for foreground, 97 chars:
  759. * <text style="fill:#fff" font-weight="bold" font-style="italic" x="65535" y="65535">xxxxxx</text>\n
  760. */
  761. *bytes = strlen(svg_header) + 128 + cv->width * cv->height * 200;
  762. cur = data = malloc(*bytes);
  763. /* Header */
  764. cur += sprintf(cur, svg_header, cv->width * 6, cv->height * 10,
  765. cv->width * 6, cv->height * 10);
  766. cur += sprintf(cur, " <g id=\"mainlayer\" font-size=\"10\""
  767. " style=\"font-family: monospace\">\n");
  768. /* Background */
  769. for(y = 0; y < cv->height; y++)
  770. {
  771. uint32_t *lineattr = cv->attrs + y * cv->width;
  772. for(x = 0; x < cv->width; x++)
  773. {
  774. cur += sprintf(cur, "<rect style=\"fill:#%.03x\" x=\"%d\" y=\"%d\""
  775. " width=\"6\" height=\"10\"/>\n",
  776. caca_attr_to_rgb12_bg(*lineattr++),
  777. x * 6, y * 10);
  778. }
  779. }
  780. /* Text */
  781. for(y = 0; y < cv->height; y++)
  782. {
  783. uint32_t *lineattr = cv->attrs + y * cv->width;
  784. uint32_t *linechar = cv->chars + y * cv->width;
  785. for(x = 0; x < cv->width; x++)
  786. {
  787. uint32_t ch = *linechar++;
  788. if(ch == ' ' || ch == CACA_MAGIC_FULLWIDTH)
  789. {
  790. lineattr++;
  791. continue;
  792. }
  793. cur += sprintf(cur, "<text style=\"fill:#%.03x\"%s%s "
  794. "x=\"%d\" y=\"%d\">",
  795. caca_attr_to_rgb12_fg(*lineattr),
  796. (*lineattr & CACA_BOLD) ? " font-weight=\"bold\"" : "",
  797. (*lineattr & CACA_ITALICS) ? " font-style=\"italic\"" : "",
  798. x * 6, (y * 10) + 8);
  799. lineattr++;
  800. if(ch < 0x00000020)
  801. *cur++ = '?';
  802. else if(ch > 0x0000007f)
  803. cur += caca_utf32_to_utf8(cur, ch);
  804. else switch((uint8_t)ch)
  805. {
  806. case '>': cur += sprintf(cur, "&gt;"); break;
  807. case '<': cur += sprintf(cur, "&lt;"); break;
  808. case '&': cur += sprintf(cur, "&amp;"); break;
  809. default: *cur++ = (uint8_t)ch; break;
  810. }
  811. cur += sprintf(cur, "</text>\n");
  812. }
  813. }
  814. cur += sprintf(cur, " </g>\n");
  815. cur += sprintf(cur, "</svg>\n");
  816. /* Crop to really used size */
  817. debug("SVG export: alloc %lu bytes, realloc %lu",
  818. (unsigned long int)*bytes, (unsigned long int)(cur - data));
  819. *bytes = (uintptr_t)(cur - data);
  820. data = realloc(data, *bytes);
  821. return data;
  822. }
  823. /* Export a TGA image */
  824. static void *export_tga(caca_canvas_t const *cv, size_t *bytes)
  825. {
  826. char const * const *fontlist;
  827. char *data, *cur;
  828. caca_font_t *f;
  829. int i, w, h;
  830. fontlist = caca_get_font_list();
  831. if(!fontlist[0])
  832. {
  833. seterrno(EINVAL);
  834. return NULL;
  835. }
  836. f = caca_load_font(fontlist[0], 0);
  837. w = caca_get_canvas_width(cv) * caca_get_font_width(f);
  838. h = caca_get_canvas_height(cv) * caca_get_font_height(f);
  839. *bytes = w * h * 4 + 18; /* 32 bpp + 18 bytes for the header */
  840. cur = data = malloc(*bytes);
  841. /* ID Length */
  842. cur += write_u8(cur, 0);
  843. /* Color Map Type: no colormap */
  844. cur += write_u8(cur, 0);
  845. /* Image Type: uncompressed truecolor */
  846. cur += write_u8(cur, 2);
  847. /* Color Map Specification: no color map */
  848. memset(cur, 0, 5); cur += 5;
  849. /* Image Specification */
  850. cur += sprintf(cur, "%c%c", 0, 0); /* X Origin */
  851. cur += sprintf(cur, "%c%c", 0, 0); /* Y Origin */
  852. cur += sprintf(cur, "%c%c", w & 0xff, w >> 8); /* Width */
  853. cur += sprintf(cur, "%c%c", h & 0xff, h >> 8); /* Height */
  854. cur += write_u8(cur, 32); /* Pixel Depth */
  855. cur += write_u8(cur, 40); /* Image Descriptor */
  856. /* Image ID: no ID */
  857. /* Color Map Data: no colormap */
  858. /* Image Data */
  859. caca_render_canvas(cv, f, cur, w, h, 4 * w);
  860. /* Swap bytes. What a waste of time. */
  861. for(i = 0; i < w * h * 4; i += 4)
  862. {
  863. char c;
  864. c = cur[i]; cur[i] = cur[i + 3]; cur[i + 3] = c;
  865. c = cur[i + 1]; cur[i + 1] = cur[i + 2]; cur[i + 2] = c;
  866. }
  867. caca_free_font(f);
  868. return data;
  869. }
  870. /* Generate troff representation of current canvas. */
  871. static void *export_troff(caca_canvas_t const *cv, size_t *bytes)
  872. {
  873. char *data, *cur;
  874. int x, y;
  875. uint32_t prevfg = 0;
  876. uint32_t prevbg = 0;
  877. int started = 0;
  878. /* Each char is at most
  879. * 2x\mM (2x10)
  880. * + \fB + \fI + \fR (9)
  881. * + 4 bytes = 33
  882. * Each line has a \n (1) and maybe 0xc2 0xa0 (2)
  883. * Header has .nf\n (3)
  884. */
  885. *bytes = 3 + cv->height * 3 + (cv->width * cv->height * 33);
  886. cur = data = malloc(*bytes);
  887. cur += write_string(cur, ".nf\n");
  888. prevfg = 0;
  889. prevbg = 0;
  890. started = 0;
  891. for(y = 0; y < cv->height; y++)
  892. {
  893. uint32_t *lineattr = cv->attrs + y * cv->width;
  894. uint32_t *linechar = cv->chars + y * cv->width;
  895. for(x = 0; x < cv->width; x++)
  896. {
  897. static char const * ansi2troff[16] =
  898. {
  899. /* Dark */
  900. "black", "blue", "green", "cyan",
  901. "red", "magenta", "yellow", "white",
  902. /* Bright */
  903. "black", "blue", "green", "cyan",
  904. "red", "magenta", "yellow", "white",
  905. };
  906. uint8_t fg = caca_attr_to_ansi_fg(lineattr[x]);
  907. uint8_t bg = caca_attr_to_ansi_bg(lineattr[x]);
  908. uint32_t ch = linechar[x];
  909. if(fg != prevfg || !started)
  910. cur += sprintf(cur, "\\m[%s]", ansi2troff[fg]);
  911. if(bg != prevbg || !started)
  912. cur += sprintf(cur, "\\M[%s]", ansi2troff[bg]);
  913. if(lineattr[x] & CACA_BOLD)
  914. cur += sprintf(cur, "\\fB");
  915. if(lineattr[x] & CACA_ITALICS)
  916. cur += sprintf(cur, "\\fI");
  917. if(ch == '\\')
  918. cur += sprintf(cur, "\\\\");
  919. else if(ch == ' ')
  920. {
  921. /* Use unbreakable space at line ends, else spaces are dropped */
  922. if(x == 0 || x == cv->width-1)
  923. cur += sprintf(cur, "%c%c", 0xc2, 0xa0);
  924. else
  925. cur += caca_utf32_to_utf8(cur, ch);
  926. }
  927. else
  928. cur += caca_utf32_to_utf8(cur, ch);
  929. if(lineattr[x] & (CACA_BOLD|CACA_ITALICS))
  930. cur += sprintf(cur, "\\fR");
  931. prevfg = fg;
  932. prevbg = bg;
  933. started = 1;
  934. }
  935. cur += write_u8(cur, '\n');
  936. }
  937. /* Crop to really used size */
  938. debug("troff export: alloc %lu bytes, realloc %lu",
  939. (unsigned long int)*bytes, (unsigned long int)(cur - data));
  940. *bytes = (uintptr_t)(cur - data);
  941. data = realloc(data, *bytes);
  942. return data;
  943. }
  944. /*
  945. * XXX: The following functions are aliases.
  946. */
  947. void *caca_export_memory(caca_canvas_t const *, char const *,
  948. size_t *) CACA_ALIAS(caca_export_canvas_to_memory);