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.

преди 18 години
преди 18 години
преди 18 години
преди 18 години
преди 18 години
преди 18 години
преди 18 години
преди 18 години
преди 18 години
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797
  1. /*
  2. * libcucul Canvas for ultrafast compositing of Unicode letters
  3. * Copyright (c) 2002-2006 Sam Hocevar <sam@zoy.org>
  4. * 2006 Jean-Yves Lamoureux <jylam@lnxscene.org>
  5. * All Rights Reserved
  6. *
  7. * $Id$
  8. *
  9. * This library is free software; you can redistribute it and/or
  10. * modify it under the terms of the Do What The Fuck You Want To
  11. * Public License, Version 2, as published by Sam Hocevar. See
  12. * http://sam.zoy.org/wtfpl/COPYING for more details.
  13. */
  14. /*
  15. * This file contains various export functions
  16. */
  17. #include "config.h"
  18. #include "common.h"
  19. #if !defined(__KERNEL__)
  20. # if defined(HAVE_ERRNO_H)
  21. # include <errno.h>
  22. # endif
  23. # include <stdlib.h>
  24. # include <stdio.h>
  25. # include <string.h>
  26. #endif
  27. #include "cucul.h"
  28. #include "cucul_internals.h"
  29. static void export_caca(cucul_canvas_t *, cucul_buffer_t *);
  30. static void export_ansi(cucul_canvas_t *, cucul_buffer_t *);
  31. static void export_utf8(cucul_canvas_t *, cucul_buffer_t *);
  32. static void export_html(cucul_canvas_t *, cucul_buffer_t *);
  33. static void export_html3(cucul_canvas_t *, cucul_buffer_t *);
  34. static void export_irc(cucul_canvas_t *, cucul_buffer_t *);
  35. static void export_ps(cucul_canvas_t *, cucul_buffer_t *);
  36. static void export_svg(cucul_canvas_t *, cucul_buffer_t *);
  37. static void export_tga(cucul_canvas_t *, cucul_buffer_t *);
  38. /** \brief Export a canvas into a foreign format.
  39. *
  40. * This function exports a libcucul canvas into various foreign formats such
  41. * as ANSI art, HTML, IRC colours, etc. One should use cucul_get_buffer_data()
  42. * and cucul_get_buffer_size() to access the buffer contents. The allocated
  43. * data is valid until cucul_free_buffer() is called.
  44. *
  45. * Valid values for \c format are:
  46. *
  47. * \li \c "caca": export native libcaca files.
  48. *
  49. * \li \c "ansi": export ANSI art (CP437 charset with ANSI colour codes).
  50. *
  51. * \li \c "html": export an HTML page with CSS information.
  52. *
  53. * \li \c "html3": export an HTML table that should be compatible with
  54. * most navigators, including textmode ones.
  55. *
  56. * \li \c "irc": export UTF-8 text with mIRC colour codes.
  57. *
  58. * \li \c "ps": export a PostScript document.
  59. *
  60. * \li \c "svg": export an SVG vector image.
  61. *
  62. * \li \c "tga": export a TGA image.
  63. *
  64. * If an error occurs, NULL is returned and \b errno is set accordingly:
  65. * - \c EINVAL Invalid format requested.
  66. * - \c ENOMEM Not enough memory to allocate output buffer.
  67. *
  68. * \param cv A libcucul canvas
  69. * \param format A string describing the requested output format.
  70. * \return A libcucul buffer, or NULL in case of error.
  71. */
  72. cucul_buffer_t * cucul_export_canvas(cucul_canvas_t *cv, char const *format)
  73. {
  74. cucul_buffer_t *ex;
  75. ex = malloc(sizeof(cucul_buffer_t));
  76. if(!ex)
  77. {
  78. #if defined(HAVE_ERRNO_H)
  79. errno = ENOMEM;
  80. #endif
  81. return NULL;
  82. }
  83. ex->size = 0;
  84. ex->data = NULL;
  85. ex->user_data = 0;
  86. if(!strcasecmp("caca", format))
  87. export_caca(cv, ex);
  88. else if(!strcasecmp("ansi", format))
  89. export_ansi(cv, ex);
  90. else if(!strcasecmp("utf8", format))
  91. export_utf8(cv, ex);
  92. else if(!strcasecmp("html", format))
  93. export_html(cv, ex);
  94. else if(!strcasecmp("html3", format))
  95. export_html3(cv, ex);
  96. else if(!strcasecmp("irc", format))
  97. export_irc(cv, ex);
  98. else if(!strcasecmp("ps", format))
  99. export_ps(cv, ex);
  100. else if(!strcasecmp("svg", format))
  101. export_svg(cv, ex);
  102. else if(!strcasecmp("tga", format))
  103. export_tga(cv, ex);
  104. if(ex->size == 0)
  105. {
  106. free(ex);
  107. #if defined(HAVE_ERRNO_H)
  108. errno = EINVAL;
  109. #endif
  110. return NULL;
  111. }
  112. return ex;
  113. }
  114. /** \brief Get available export formats
  115. *
  116. * Return a list of available export formats. The list is a NULL-terminated
  117. * array of strings, interleaving a string containing the internal value for
  118. * the export format, to be used with cucul_export_canvas(), and a string
  119. * containing the natural language description for that export format.
  120. *
  121. * This function never fails.
  122. *
  123. * \return An array of strings.
  124. */
  125. char const * const * cucul_get_export_list(void)
  126. {
  127. static char const * const list[] =
  128. {
  129. "caca", "native libcaca format",
  130. "ansi", "ANSI",
  131. "utf8", "UTF-8 with ANSI escape codes",
  132. "html", "HTML",
  133. "html3", "backwards-compatible HTML",
  134. "irc", "IRC with mIRC colours",
  135. "ps", "PostScript document",
  136. "svg", "SVG vector image",
  137. "tga", "TGA image",
  138. NULL, NULL
  139. };
  140. return list;
  141. }
  142. /*
  143. * XXX: the following functions are local.
  144. */
  145. /* Generate a native libcaca canvas file. */
  146. static void export_caca(cucul_canvas_t *cv, cucul_buffer_t *ex)
  147. {
  148. uint32_t *attr = cv->attr;
  149. uint32_t *chars = cv->chars;
  150. char *cur;
  151. uint32_t w, h;
  152. unsigned int n;
  153. /* 16 bytes for the canvas, 8 bytes for each character cell. */
  154. ex->size = 16 + 8 * cv->width * cv->height;
  155. ex->data = malloc(ex->size);
  156. cur = ex->data;
  157. w = cv->width;
  158. h = cv->height;
  159. cur += sprintf(cur, "CACACANV%c%c%c%c%c%c%c%c",
  160. (unsigned char)(w >> 24), (unsigned char)((w >> 16) & 0xff),
  161. (unsigned char)((w >> 8) & 0xff), (unsigned char)(w & 0xff),
  162. (unsigned char)(h >> 24), (unsigned char)((h >> 16) & 0xff),
  163. (unsigned char)((h >> 8) & 0xff), (unsigned char)(h & 0xff));
  164. for(n = cv->height * cv->width; n--; )
  165. {
  166. uint32_t ch = *chars++;
  167. uint32_t a = *attr++;
  168. *cur++ = ch >> 24;
  169. *cur++ = (ch >> 16) & 0xff;
  170. *cur++ = (ch >> 8) & 0xff;
  171. *cur++ = ch & 0xff;
  172. *cur++ = a >> 24;
  173. *cur++ = (a >> 16) & 0xff;
  174. *cur++ = (a >> 8) & 0xff;
  175. *cur++ = a & 0xff;
  176. }
  177. }
  178. /* Generate UTF-8 representation of current canvas. */
  179. static void export_utf8(cucul_canvas_t *cv, cucul_buffer_t *ex)
  180. {
  181. static uint8_t const palette[] =
  182. {
  183. 0, 4, 2, 6, 1, 5, 3, 7,
  184. 8, 12, 10, 14, 9, 13, 11, 15
  185. };
  186. char *cur;
  187. unsigned int x, y;
  188. /* 23 bytes assumed for max length per pixel ('\e[5;1;3x;4y;9x;10ym' plus
  189. * 4 max bytes for a UTF-8 character).
  190. * Add height*9 to that (zeroes color at the end and jump to next line) */
  191. ex->size = (cv->height * 9) + (cv->width * cv->height * 23);
  192. ex->data = malloc(ex->size);
  193. cur = ex->data;
  194. for(y = 0; y < cv->height; y++)
  195. {
  196. uint32_t *lineattr = cv->attr + y * cv->width;
  197. uint32_t *linechar = cv->chars + y * cv->width;
  198. uint8_t prevfg = -1;
  199. uint8_t prevbg = -1;
  200. for(x = 0; x < cv->width; x++)
  201. {
  202. uint8_t fg = palette[_cucul_argb32_to_ansi4fg(lineattr[x])];
  203. uint8_t bg = palette[_cucul_argb32_to_ansi4bg(lineattr[x])];
  204. uint32_t ch = linechar[x];
  205. if(fg != prevfg || bg != prevbg)
  206. {
  207. cur += sprintf(cur, "\033[0;");
  208. if(fg < 8)
  209. if(bg < 8)
  210. cur += sprintf(cur, "3%d;4%dm", fg, bg);
  211. else
  212. cur += sprintf(cur, "5;3%d;4%d;10%dm",
  213. fg, bg - 8, bg - 8);
  214. else
  215. if(bg < 8)
  216. cur += sprintf(cur, "1;3%d;4%d;9%dm",
  217. fg - 8, bg, fg - 8);
  218. else
  219. cur += sprintf(cur, "5;1;3%d;4%d;9%d;10%dm",
  220. fg - 8, bg - 8, fg - 8, bg - 8);
  221. }
  222. cur += cucul_utf32_to_utf8(cur, ch);
  223. prevfg = fg;
  224. prevbg = bg;
  225. }
  226. cur += sprintf(cur, "\033[0m\r\n");
  227. }
  228. /* Crop to really used size */
  229. ex->size = (uintptr_t)(cur - ex->data);
  230. ex->data = realloc(ex->data, ex->size);
  231. }
  232. /* Generate ANSI representation of current canvas. */
  233. static void export_ansi(cucul_canvas_t *cv, cucul_buffer_t *ex)
  234. {
  235. static uint8_t const palette[] =
  236. {
  237. 0, 4, 2, 6, 1, 5, 3, 7,
  238. 8, 12, 10, 14, 9, 13, 11, 15
  239. };
  240. char *cur;
  241. unsigned int x, y;
  242. uint8_t prevfg = -1;
  243. uint8_t prevbg = -1;
  244. /* 16 bytes assumed for max length per pixel ('\e[5;1;3x;4ym' plus
  245. * 1 byte for a CP437 character).
  246. * Add height*9 to that (zeroes color at the end and jump to next line) */
  247. ex->size = (cv->height * 9) + (cv->width * cv->height * 16);
  248. ex->data = malloc(ex->size);
  249. cur = ex->data;
  250. for(y = 0; y < cv->height; y++)
  251. {
  252. uint32_t *lineattr = cv->attr + y * cv->width;
  253. uint32_t *linechar = cv->chars + y * cv->width;
  254. for(x = 0; x < cv->width; x++)
  255. {
  256. uint8_t fg = palette[_cucul_argb32_to_ansi4fg(lineattr[x])];
  257. uint8_t bg = palette[_cucul_argb32_to_ansi4bg(lineattr[x])];
  258. uint32_t ch = linechar[x];
  259. if(fg != prevfg || bg != prevbg)
  260. {
  261. cur += sprintf(cur, "\033[0;");
  262. if(fg < 8)
  263. if(bg < 8)
  264. cur += sprintf(cur, "3%d;4%dm", fg, bg);
  265. else
  266. cur += sprintf(cur, "5;3%d;4%dm", fg, bg - 8);
  267. else
  268. if(bg < 8)
  269. cur += sprintf(cur, "1;3%d;4%dm", fg - 8, bg);
  270. else
  271. cur += sprintf(cur, "5;1;3%d;4%dm", fg - 8, bg - 8);
  272. }
  273. *cur++ = cucul_utf32_to_cp437(ch);
  274. prevfg = fg;
  275. prevbg = bg;
  276. }
  277. if(cv->width == 80)
  278. {
  279. cur += sprintf(cur, "\033[s\n\033[u");
  280. }
  281. else
  282. {
  283. cur += sprintf(cur, "\033[0m\r\n");
  284. prevfg = -1;
  285. prevbg = -1;
  286. }
  287. }
  288. /* Crop to really used size */
  289. ex->size = (uintptr_t)(cur - ex->data);
  290. ex->data = realloc(ex->data, ex->size);
  291. }
  292. /* Generate HTML representation of current canvas. */
  293. static void export_html(cucul_canvas_t *cv, cucul_buffer_t *ex)
  294. {
  295. char *cur;
  296. unsigned int x, y, len;
  297. /* The HTML header: less than 1000 bytes
  298. * A line: 7 chars for "<br />\n"
  299. * A glyph: 47 chars for "<span style="color:#xxx;background-color:#xxx">"
  300. * up to 9 chars for "&#xxxxxx;", far less for pure ASCII
  301. * 7 chars for "</span>" */
  302. ex->size = 1000 + cv->height * (7 + cv->width * (47 + 9 + 7));
  303. ex->data = malloc(ex->size);
  304. cur = ex->data;
  305. /* HTML header */
  306. cur += sprintf(cur, "<html><head>\n");
  307. cur += sprintf(cur, "<title>Generated by libcaca %s</title>\n", VERSION);
  308. cur += sprintf(cur, "</head><body>\n");
  309. cur += sprintf(cur, "<div cellpadding='0' cellspacing='0' style='%s'>\n",
  310. "font-family: monospace, fixed; font-weight: bold;");
  311. for(y = 0; y < cv->height; y++)
  312. {
  313. uint32_t *lineattr = cv->attr + y * cv->width;
  314. uint32_t *linechar = cv->chars + y * cv->width;
  315. for(x = 0; x < cv->width; x += len)
  316. {
  317. cur += sprintf(cur, "<span style=\"color:#%.03x;"
  318. "background-color:#%.03x\">",
  319. _cucul_argb32_to_rgb12fg(lineattr[x]),
  320. _cucul_argb32_to_rgb12bg(lineattr[x]));
  321. for(len = 0;
  322. x + len < cv->width && lineattr[x + len] == lineattr[x];
  323. len++)
  324. {
  325. if(linechar[x + len] <= 0x00000020)
  326. cur += sprintf(cur, "&nbsp;");
  327. else if(linechar[x + len] < 0x00000080)
  328. cur += sprintf(cur, "%c",
  329. (unsigned char)linechar[x + len]);
  330. else
  331. cur += sprintf(cur, "&#%i;",
  332. (unsigned int)linechar[x + len]);
  333. }
  334. cur += sprintf(cur, "</span>");
  335. }
  336. /* New line */
  337. cur += sprintf(cur, "<br />\n");
  338. }
  339. cur += sprintf(cur, "</div></body></html>\n");
  340. /* Crop to really used size */
  341. ex->size = strlen(ex->data) + 1;
  342. ex->data = realloc(ex->data, ex->size);
  343. }
  344. /* Export an HTML3 document. This function is way bigger than export_html(),
  345. * but permits viewing in old browsers (or limited ones such as links). It
  346. * will not work under gecko (mozilla rendering engine) unless you set a
  347. * correct header. */
  348. static void export_html3(cucul_canvas_t *cv, cucul_buffer_t *ex)
  349. {
  350. char *cur;
  351. unsigned int x, y, len;
  352. /* The HTML table markup: less than 1000 bytes
  353. * A line: 10 chars for "<tr></tr>\n"
  354. * A glyph: 40 chars for "<td bgcolor=#xxxxxx><font color=#xxxxxx>"
  355. * up to 9 chars for "&#xxxxxx;", far less for pure ASCII
  356. * 12 chars for "</font></td>" */
  357. ex->size = 1000 + cv->height * (10 + cv->width * (40 + 9 + 12));
  358. ex->data = malloc(ex->size);
  359. cur = ex->data;
  360. /* Table */
  361. cur += sprintf(cur, "<table cols='%d' cellpadding='0' cellspacing='0'>\n",
  362. cv->height);
  363. for(y = 0; y < cv->height; y++)
  364. {
  365. uint32_t *lineattr = cv->attr + y * cv->width;
  366. uint32_t *linechar = cv->chars + y * cv->width;
  367. cur += sprintf(cur, "<tr>");
  368. for(x = 0; x < cv->width; x += len)
  369. {
  370. unsigned int i;
  371. /* Use colspan option to factor cells with same attributes
  372. * (see below) */
  373. len = 1;
  374. while(x + len < cv->width && lineattr[x + len] == lineattr[x])
  375. len++;
  376. cur += sprintf(cur, "<td bgcolor=#%.06lx", (unsigned long int)
  377. _cucul_argb32_to_rgb24bg(lineattr[x]));
  378. if(len > 1)
  379. cur += sprintf(cur, " colspan=%d", len);
  380. cur += sprintf(cur, "><font color=#%.06lx>", (unsigned long int)
  381. _cucul_argb32_to_rgb24fg(lineattr[x]));
  382. for(i = 0; i < len; i++)
  383. {
  384. if(linechar[x + i] <= 0x00000020)
  385. cur += sprintf(cur, "&nbsp;");
  386. else if(linechar[x + i] < 0x00000080)
  387. cur += sprintf(cur, "%c", (unsigned char)linechar[x + i]);
  388. else
  389. cur += sprintf(cur, "&#%i;", (unsigned int)linechar[x + i]);
  390. }
  391. cur += sprintf(cur, "</font></td>");
  392. }
  393. cur += sprintf(cur, "</tr>\n");
  394. }
  395. /* Footer */
  396. cur += sprintf(cur, "</table>\n");
  397. /* Crop to really used size */
  398. ex->size = (uintptr_t)(cur - ex->data);
  399. ex->data = realloc(ex->data, ex->size);
  400. }
  401. /* Export a text file with IRC colours */
  402. static void export_irc(cucul_canvas_t *cv, cucul_buffer_t *ex)
  403. {
  404. static uint8_t const palette[] =
  405. {
  406. 1, 2, 3, 10, 5, 6, 7, 15, /* Dark */
  407. 14, 12, 9, 11, 4, 13, 8, 0, /* Light */
  408. };
  409. char *cur;
  410. unsigned int x, y;
  411. /* 11 bytes assumed for max length per pixel. Worst case scenario:
  412. * ^Cxx,yy 6 bytes
  413. * ^B^B 2 bytes
  414. * ch 1 byte
  415. * \r\n 2 bytes
  416. * In real life, the average bytes per pixel value will be around 5.
  417. */
  418. ex->size = 2 + (cv->width * cv->height * 11);
  419. ex->data = malloc(ex->size);
  420. cur = ex->data;
  421. for(y = 0; y < cv->height; y++)
  422. {
  423. uint32_t *lineattr = cv->attr + y * cv->width;
  424. uint32_t *linechar = cv->chars + y * cv->width;
  425. uint8_t prevfg = -1;
  426. uint8_t prevbg = -1;
  427. for(x = 0; x < cv->width; x++)
  428. {
  429. uint8_t fg = palette[_cucul_argb32_to_ansi4fg(lineattr[x])];
  430. uint8_t bg = palette[_cucul_argb32_to_ansi4bg(lineattr[x])];
  431. uint32_t ch = linechar[x];
  432. #if 0
  433. if(bg == prevbg)
  434. {
  435. if(fg == prevfg)
  436. ; /* Same fg/bg, do nothing */
  437. else if(ch == (uint32_t)' ')
  438. fg = prevfg; /* Hackety hack */
  439. else
  440. {
  441. cur += sprintf(cur, "\x03%d", fg);
  442. if(ch >= (uint32_t)'0' && ch <= (uint32_t)'9')
  443. cur += sprintf(cur, "\x02\x02");
  444. }
  445. }
  446. else
  447. {
  448. if(fg == prevfg)
  449. cur += sprintf(cur, "\x03,%d", bg);
  450. else
  451. cur += sprintf(cur, "\x03%d,%d", fg, bg);
  452. if(ch >= (uint32_t)'0' && ch <= (uint32_t)'9')
  453. cur += sprintf(cur, "\x02\x02");
  454. }
  455. #else
  456. if(bg != prevbg || fg != prevfg)
  457. {
  458. cur += sprintf(cur, "\x03%d,%d", fg, bg);
  459. if(ch >= (uint32_t)'0' && ch <= (uint32_t)'9')
  460. cur += sprintf(cur, "\x02\x02");
  461. }
  462. #endif
  463. *cur++ = ch & 0x7f;
  464. prevfg = fg;
  465. prevbg = bg;
  466. }
  467. *cur++ = '\r';
  468. *cur++ = '\n';
  469. }
  470. /* Crop to really used size */
  471. ex->size = (uintptr_t)(cur - ex->data);
  472. ex->data = realloc(ex->data, ex->size);
  473. }
  474. /* Export a PostScript document. */
  475. static void export_ps(cucul_canvas_t *cv, cucul_buffer_t *ex)
  476. {
  477. static char const *ps_header =
  478. "%!\n"
  479. "%% libcaca PDF export\n"
  480. "%%LanguageLevel: 2\n"
  481. "%%Pages: 1\n"
  482. "%%DocumentData: Clean7Bit\n"
  483. "/csquare {\n"
  484. " newpath\n"
  485. " 0 0 moveto\n"
  486. " 0 1 rlineto\n"
  487. " 1 0 rlineto\n"
  488. " 0 -1 rlineto\n"
  489. " closepath\n"
  490. " setrgbcolor\n"
  491. " fill\n"
  492. "} def\n"
  493. "/S {\n"
  494. " Show\n"
  495. "} bind def\n"
  496. "/Courier-Bold findfont\n"
  497. "8 scalefont\n"
  498. "setfont\n"
  499. "gsave\n"
  500. "6 10 scale\n";
  501. char *cur;
  502. unsigned int x, y;
  503. /* 200 is arbitrary but should be ok */
  504. ex->size = strlen(ps_header) + (cv->width * cv->height * 200);
  505. ex->data = malloc(ex->size);
  506. cur = ex->data;
  507. /* Header */
  508. cur += sprintf(cur, "%s", ps_header);
  509. cur += sprintf(cur, "0 %d translate\n", cv->height);
  510. /* Background, drawn using csquare macro defined in header */
  511. for(y = cv->height; y--; )
  512. {
  513. uint32_t *lineattr = cv->attr + y * cv->width;
  514. for(x = 0; x < cv->width; x++)
  515. {
  516. uint8_t argb[8];
  517. _cucul_argb32_to_argb4(*lineattr++, argb);
  518. cur += sprintf(cur, "1 0 translate\n %f %f %f csquare\n",
  519. (float)argb[1] * (1.0 / 0xf),
  520. (float)argb[2] * (1.0 / 0xf),
  521. (float)argb[3] * (1.0 / 0xf));
  522. }
  523. /* Return to beginning of the line, and jump to the next one */
  524. cur += sprintf(cur, "-%d 1 translate\n", cv->width);
  525. }
  526. cur += sprintf(cur, "grestore\n"); /* Restore transformation matrix */
  527. cur += sprintf(cur, "0 %d translate\n", cv->height*10);
  528. for(y = cv->height; y--; )
  529. {
  530. uint32_t *lineattr = cv->attr + (cv->height - y - 1) * cv->width;
  531. uint32_t *linechar = cv->chars + (cv->height - y - 1) * cv->width;
  532. for(x = 0; x < cv->width; x++)
  533. {
  534. uint8_t argb[8];
  535. uint32_t ch = *linechar++;
  536. _cucul_argb32_to_argb4(*lineattr++, argb);
  537. cur += sprintf(cur, "newpath\n");
  538. cur += sprintf(cur, "%d %d moveto\n", (x + 1) * 6, y * 10 + 2);
  539. cur += sprintf(cur, "%f %f %f setrgbcolor\n",
  540. (float)argb[5] * (1.0 / 0xf),
  541. (float)argb[6] * (1.0 / 0xf),
  542. (float)argb[7] * (1.0 / 0xf));
  543. if(ch < 0x00000020)
  544. cur += sprintf(cur, "(?) show\n");
  545. else if(ch >= 0x00000080)
  546. cur += sprintf(cur, "(?) show\n");
  547. else switch((uint8_t)(ch & 0x7f))
  548. {
  549. case '\\':
  550. case '(':
  551. case ')':
  552. cur += sprintf(cur, "(\\%c) show\n", (unsigned char)ch);
  553. break;
  554. default:
  555. cur += sprintf(cur, "(%c) show\n", (unsigned char)ch);
  556. break;
  557. }
  558. }
  559. }
  560. cur += sprintf(cur, "showpage\n");
  561. /* Crop to really used size */
  562. ex->size = (uintptr_t)(cur - ex->data);
  563. ex->data = realloc(ex->data, ex->size);
  564. }
  565. /* Export an SVG vector image */
  566. static void export_svg(cucul_canvas_t *cv, cucul_buffer_t *ex)
  567. {
  568. static char const svg_header[] =
  569. "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
  570. "<svg width=\"%d\" height=\"%d\" viewBox=\"0 0 %d %d\""
  571. " xmlns=\"http://www.w3.org/2000/svg\""
  572. " xmlns:xlink=\"http://www.w3.org/1999/xlink\""
  573. " xml:space=\"preserve\" version=\"1.1\" baseProfile=\"full\">\n";
  574. char *cur;
  575. unsigned int x, y;
  576. /* 200 is arbitrary but should be ok */
  577. ex->size = strlen(svg_header) + (cv->width * cv->height * 200);
  578. ex->data = malloc(ex->size);
  579. cur = ex->data;
  580. /* Header */
  581. cur += sprintf(cur, svg_header, cv->width * 6, cv->height * 10,
  582. cv->width * 6, cv->height * 10);
  583. cur += sprintf(cur, " <g id=\"mainlayer\" font-size=\"12\">\n");
  584. /* Background */
  585. for(y = 0; y < cv->height; y++)
  586. {
  587. uint32_t *lineattr = cv->attr + y * cv->width;
  588. for(x = 0; x < cv->width; x++)
  589. {
  590. cur += sprintf(cur, "<rect style=\"fill:#%.03x\" x=\"%d\" y=\"%d\""
  591. " width=\"6\" height=\"10\"/>\n",
  592. _cucul_argb32_to_rgb12bg(*lineattr++),
  593. x * 6, y * 10);
  594. }
  595. }
  596. /* Text */
  597. for(y = 0; y < cv->height; y++)
  598. {
  599. uint32_t *lineattr = cv->attr + y * cv->width;
  600. uint32_t *linechar = cv->chars + y * cv->width;
  601. for(x = 0; x < cv->width; x++)
  602. {
  603. uint32_t ch = *linechar++;
  604. cur += sprintf(cur, "<text style=\"fill:#%.03x\" "
  605. "x=\"%d\" y=\"%d\">",
  606. _cucul_argb32_to_rgb12fg(*lineattr++),
  607. x * 6, (y * 10) + 10);
  608. if(ch < 0x00000020)
  609. *cur++ = '?';
  610. else if(ch > 0x0000007f)
  611. cur += cucul_utf32_to_utf8(cur, ch);
  612. else switch((uint8_t)ch)
  613. {
  614. case '>': cur += sprintf(cur, "&gt;"); break;
  615. case '<': cur += sprintf(cur, "&lt;"); break;
  616. case '&': cur += sprintf(cur, "&amp;"); break;
  617. default: *cur++ = ch; break;
  618. }
  619. cur += sprintf(cur, "</text>\n");
  620. }
  621. }
  622. cur += sprintf(cur, " </g>\n");
  623. cur += sprintf(cur, "</svg>\n");
  624. /* Crop to really used size */
  625. ex->size = (uintptr_t)(cur - ex->data);
  626. ex->data = realloc(ex->data, ex->size);
  627. }
  628. /* Export a TGA image */
  629. static void export_tga(cucul_canvas_t *cv, cucul_buffer_t *ex)
  630. {
  631. char const * const * fontlist;
  632. char * cur;
  633. cucul_font_t *f;
  634. unsigned int i, w, h;
  635. fontlist = cucul_get_font_list();
  636. if(!fontlist[0])
  637. return;
  638. f = cucul_load_font(fontlist[0], 0);
  639. w = cucul_get_canvas_width(cv) * cucul_get_font_width(f);
  640. h = cucul_get_canvas_height(cv) * cucul_get_font_height(f);
  641. ex->size = w * h * 4 + 18; /* 32 bpp + 18 bytes for the header */
  642. ex->data = malloc(ex->size);
  643. cur = ex->data;
  644. /* ID Length */
  645. cur += sprintf(cur, "%c", 0);
  646. /* Color Map Type: no colormap */
  647. cur += sprintf(cur, "%c", 0);
  648. /* Image Type: uncompressed truecolor */
  649. cur += sprintf(cur, "%c", 2);
  650. /* Color Map Specification: no color map */
  651. memset(cur, 0, 5); cur += 5;
  652. /* Image Specification */
  653. cur += sprintf(cur, "%c%c", 0, 0); /* X Origin */
  654. cur += sprintf(cur, "%c%c", 0, 0); /* Y Origin */
  655. cur += sprintf(cur, "%c%c", w & 0xff, w >> 8); /* Width */
  656. cur += sprintf(cur, "%c%c", h & 0xff, h >> 8); /* Height */
  657. cur += sprintf(cur, "%c", 32); /* Pixel Depth */
  658. cur += sprintf(cur, "%c", 40); /* Image Descriptor */
  659. /* Image ID: no ID */
  660. /* Color Map Data: no colormap */
  661. /* Image Data */
  662. cucul_render_canvas(cv, f, cur, w, h, 4 * w);
  663. /* Swap bytes. What a waste of time. */
  664. for(i = 0; i < w * h * 4; i += 4)
  665. {
  666. char w;
  667. w = cur[i]; cur[i] = cur[i + 3]; cur[i + 3] = w;
  668. w = cur[i + 1]; cur[i + 1] = cur[i + 2]; cur[i + 2] = w;
  669. }
  670. cucul_free_font(f);
  671. }