Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.
 
 
 
 
 
 

1079 řádky
36 KiB

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