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.
 
 
 
 
 
 

989 lines
33 KiB

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