source: libcaca/trunk/caca/export.c @ 2821

Last change on this file since 2821 was 2821, checked in by Sam Hocevar, 11 years ago

Starting refactoring to get rid of libcucul. The initial reason for the
split is rendered moot by the plugin system: when enabled, binaries do
not link directly with libX11 or libGL. I hope this is a step towards
more consisteny and clarity.

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