Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.
 
 
 
 
 
 

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