source: libcaca/trunk/caca/codec/export.c @ 3223

Last change on this file since 3223 was 3223, checked in by bsittler, 12 years ago

work around table layout bugs by ensuring that if there are multiple
cells on any row of the table then the very first row does not use the
cell-merging optimization; this appears to fix the last of the table
layout bugs seen in truecolor.php; also fix the foreground and
background color bugs seen in truecolor.php.

  • Property svn:keywords set to Id
File size: 35.7 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 3223 2008-11-02 21:18:51Z bsittler $
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    int maxcols;
463    int has_multi_cell_row = 0;
464
465    /* Table */
466    maxcols = 0;
467    for(y = 0; y < cv->height; y++)
468    {
469        uint32_t *lineattr = cv->attrs + y * cv->width;
470        uint32_t *linechar = cv->chars + y * cv->width;
471        int cols = 0;
472
473        for(x = 0; x < cv->width; x++)
474        {
475            if((! has_multi_cell_row)
476               &&
477               (x > 1)
478               &&
479               (caca_attr_to_ansi_bg(lineattr[x - 1])
480                !=
481                caca_attr_to_ansi_bg(lineattr[x]))
482               &&
483               ((caca_attr_to_ansi_bg(lineattr[x]) < 0x10)
484                ?
485                (_caca_attr_to_rgb24bg(lineattr[x - 1])
486                 !=
487                 _caca_attr_to_rgb24bg(lineattr[x]))
488                :
489                0))
490            {
491                has_multi_cell_row = 1;
492            }
493            if(linechar[x] == 0x00000009)
494                while((cols + 1) % 8)
495                    cols ++;
496            cols ++;
497        }
498        if (cols > maxcols)
499            maxcols = cols;
500    }
501   
502    /* The HTML table markup: less than 1000 bytes
503     * A line: 10 chars for "<tr></tr>\n"
504     * A glyph: up to 48 chars for "<td bgcolor=\"#xxxxxx\"><tt><font color=\"#xxxxxx\">"
505     *          up to 36 chars for "<b><i><u><blink></blink></u></i></b>"
506     *          up to 9 chars for "&#xxxxxx;" (far less for pure ASCII)
507     *          17 chars for "</font></tt></td>" */
508    *bytes = 1000 + cv->height * (10 + maxcols * (48 + 36 + 9 + 17));
509    cur = data = malloc(*bytes);
510
511    cur += sprintf(cur, "<table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" summary=\"[libcaca canvas html3 export]\">\n");
512
513    for(y = 0; y < cv->height; y++)
514    {
515        uint32_t *lineattr = cv->attrs + y * cv->width;
516        uint32_t *linechar = cv->chars + y * cv->width;
517        int taboff = 0;
518
519        cur += sprintf(cur, "<tr>");
520
521        for(x = 0; x < cv->width; x += len)
522        {
523            int i, needfont = 0;
524            int thistab = 0;
525
526            /* Use colspan option to factor cells with same attributes
527             * (see below) */
528            len = 1;
529            while((y || ! has_multi_cell_row)
530                  &&
531                  (x + len < cv->width)
532                  &&
533                  (caca_attr_to_ansi_bg(lineattr[x + len])
534                   ==
535                   caca_attr_to_ansi_bg(lineattr[x]))
536                  &&
537                  ((caca_attr_to_ansi_bg(lineattr[x]) < 0x10)
538                   ?
539                   (_caca_attr_to_rgb24bg(lineattr[x + len])
540                    ==
541                    _caca_attr_to_rgb24bg(lineattr[x]))
542                   :
543                   1))
544                len++;
545
546            for(i = 0; i < len; i++)
547                if(linechar[x + i] == 0x00000009)
548                    while((x + i + taboff + thistab + 1) % 8)
549                        thistab ++;
550
551            cur += sprintf(cur, "<td");
552
553            if(caca_attr_to_ansi_bg(lineattr[x]) < 0x10)
554                cur += sprintf(cur, " bgcolor=\"#%.06lx\"", (unsigned long int)
555                               _caca_attr_to_rgb24bg(lineattr[x]));
556
557            if((len + thistab) > 1)
558                cur += sprintf(cur, " colspan=\"%d\"", len + thistab);
559
560            cur += sprintf(cur, ">");
561
562            cur += sprintf(cur, "<tt>");
563
564            for(i = 0; i < len; i++)
565            {
566                if((! i) || (lineattr[x + i] != lineattr[x + i - 1]))
567                {
568                    needfont = (caca_attr_to_ansi_fg(lineattr[x + i]) != CACA_DEFAULT);
569
570                    if(needfont)
571                        cur += sprintf(cur, "<font color=\"#%.06lx\">", (unsigned long int)
572                                       _caca_attr_to_rgb24fg(lineattr[x + i]));
573
574                    if(lineattr[x + i] & CACA_BOLD)
575                        cur += sprintf(cur, "<b>");
576                    if(lineattr[x + i] & CACA_ITALICS)
577                        cur += sprintf(cur, "<i>");
578                    if(lineattr[x + i] & CACA_UNDERLINE)
579                        cur += sprintf(cur, "<u>");
580                    if(lineattr[x + i] & CACA_BLINK)
581                        cur += sprintf(cur, "<blink>");
582                }
583
584                if(linechar[x + i] == CACA_MAGIC_FULLWIDTH)
585                    ;
586                else if(linechar[x + i] <= 0x00000020)
587                {
588                    /* Control characters and space converted to
589                     * U+00A0 NO-BREAK SPACE, a.k.a. "&nbsp;" in HTML,
590                     * but we use the equivalent numeric character
591                     * reference &#160; so this will work in plain
592                     * XHTML with no DTD too. We also expand tabs
593                     * here, since they are not honored in HTML. */
594                    if(linechar[x + i] == 0x00000009)
595                    {
596                        while((x + i + taboff + 1) % 8)
597                        {
598                            cur += sprintf(cur, "&#160;");
599                            taboff ++;
600                        }
601                    }
602                    cur += sprintf(cur, "&#160;");
603                }
604                else if(linechar[x + i] == '&')
605                    cur += sprintf(cur, "&amp;");
606                else if(linechar[x + i] == '<')
607                    cur += sprintf(cur, "&lt;");
608                else if(linechar[x + i] == '>')
609                    cur += sprintf(cur, "&gt;");
610                else if(linechar[x + i] == '\"')
611                    cur += sprintf(cur, "&quot;");
612                else if(linechar[x + i] == '\'')
613                    cur += sprintf(cur, "&#39;");
614                else if(linechar[x + i] < 0x00000080)
615                    cur += sprintf(cur, "%c", (uint8_t)linechar[x + i]);
616                else
617                    cur += sprintf(cur, "&#%i;", (unsigned int)linechar[x + i]);
618
619                if (((i + 1) == len) || (lineattr[x + i + 1] != lineattr[x + i]))
620                {
621                    if(lineattr[x + i] & CACA_BLINK)
622                        cur += sprintf(cur, "</blink>");
623                    if(lineattr[x + i] & CACA_UNDERLINE)
624                        cur += sprintf(cur, "</u>");
625                    if(lineattr[x + i] & CACA_ITALICS)
626                        cur += sprintf(cur, "</i>");
627                    if(lineattr[x + i] & CACA_BOLD)
628                        cur += sprintf(cur, "</b>");
629
630                    if(needfont)
631                        cur += sprintf(cur, "</font>");
632                }
633            }
634
635            cur += sprintf(cur, "</tt>");
636            cur += sprintf(cur, "</td>");
637        }
638        cur += sprintf(cur, "</tr>\n");
639    }
640
641    /* Footer */
642    cur += sprintf(cur, "</table>\n");
643
644    /* Crop to really used size */
645    debug("html3 export: alloc %lu bytes, realloc %lu",
646          (unsigned long int)*bytes, (unsigned long int)(cur - data));
647    *bytes = (uintptr_t)(cur - data);
648    data = realloc(data, *bytes);
649
650    return data;
651}
652
653static void *export_bbfr(caca_canvas_t const *cv, size_t *bytes)
654{
655    char *data, *cur;
656    int x, y, len;
657
658    /* The font markup: less than 100 bytes
659     * A line: 1 char for "\n"
660     * A glyph: 22 chars for "[f=#xxxxxx][c=#xxxxxx]"
661     *          up to 21 chars for "[g][i][s][/s][/i][/g]"
662     *          up to 6 chars for the UTF-8 glyph
663     *          8 chars for "[/c][/f]" */
664    *bytes = 100 + cv->height * (1 + cv->width * (22 + 21 + 6 + 8));
665    cur = data = malloc(*bytes);
666
667    /* Table */
668    cur += sprintf(cur, "[font=Courier New]");
669
670    for(y = 0; y < cv->height; y++)
671    {
672        uint32_t *lineattr = cv->attrs + y * cv->width;
673        uint32_t *linechar = cv->chars + y * cv->width;
674
675        for(x = 0; x < cv->width; x += len)
676        {
677            int i, needback, needfront;
678
679            /* Use colspan option to factor cells with same attributes
680             * (see below) */
681            len = 1;
682            if(linechar[x] == ' ')
683                while(x + len < cv->width && lineattr[x + len] == lineattr[x]
684                        && linechar[x] == ' ')
685                    len++;
686            else
687                while(x + len < cv->width && lineattr[x + len] == lineattr[x]
688                        && linechar[x] != ' ')
689                    len++;
690
691            needback = caca_attr_to_ansi_bg(lineattr[x]) < 0x10;
692            needfront = caca_attr_to_ansi_fg(lineattr[x]) < 0x10;
693
694            if(needback)
695                cur += sprintf(cur, "[f=#%.06lx]", (unsigned long int)
696                               _caca_attr_to_rgb24bg(lineattr[x]));
697
698            if(linechar[x] == ' ')
699                cur += sprintf(cur, "[c=#%.06lx]", (unsigned long int)
700                               _caca_attr_to_rgb24bg(lineattr[x]));
701            else if(needfront)
702                cur += sprintf(cur, "[c=#%.06lx]", (unsigned long int)
703                               _caca_attr_to_rgb24fg(lineattr[x]));
704
705            if(lineattr[x] & CACA_BOLD)
706                cur += sprintf(cur, "[g]");
707            if(lineattr[x] & CACA_ITALICS)
708                cur += sprintf(cur, "[i]");
709            if(lineattr[x] & CACA_UNDERLINE)
710                cur += sprintf(cur, "[s]");
711            if(lineattr[x] & CACA_BLINK)
712                ; /* FIXME */
713
714            for(i = 0; i < len; i++)
715            {
716                if(linechar[x + i] == CACA_MAGIC_FULLWIDTH)
717                    ;
718                else if(linechar[x + i] == ' ')
719                    *cur++ = '_';
720                else
721                    cur += caca_utf32_to_utf8(cur, linechar[x + i]);
722            }
723
724            if(lineattr[x] & CACA_BLINK)
725                ; /* FIXME */
726            if(lineattr[x] & CACA_UNDERLINE)
727                cur += sprintf(cur, "[/s]");
728            if(lineattr[x] & CACA_ITALICS)
729                cur += sprintf(cur, "[/i]");
730            if(lineattr[x] & CACA_BOLD)
731                cur += sprintf(cur, "[/g]");
732
733            if(linechar[x] == ' ' || needfront)
734                cur += sprintf(cur, "[/c]");
735            if(needback)
736                cur += sprintf(cur, "[/f]");
737        }
738        cur += sprintf(cur, "\n");
739    }
740
741    /* Footer */
742    cur += sprintf(cur, "[/font]\n");
743
744    /* Crop to really used size */
745    debug("bbfr export: alloc %lu bytes, realloc %lu",
746          (unsigned long int)*bytes, (unsigned long int)(cur - data));
747    *bytes = (uintptr_t)(cur - data);
748    data = realloc(data, *bytes);
749
750    return data;
751}
752
753/* Export a text file with IRC colours */
754static void *export_irc(caca_canvas_t const *cv, size_t *bytes)
755{
756    static uint8_t const palette[] =
757    {
758        1, 2, 3, 10, 5, 6, 7, 15, /* Dark */
759        14, 12, 9, 11, 4, 13, 8, 0, /* Light */
760    };
761
762    char *data, *cur;
763    int x, y;
764
765    /* 14 bytes assumed for max length per pixel. Worst case scenario:
766     * ^Cxx,yy   6 bytes
767     * ^B^B      2 bytes
768     * ch        6 bytes
769     * 3 bytes for max length per line. Worst case scenario:
770     * <spc>     1 byte (for empty lines)
771     * \r\n      2 bytes
772     * In real life, the average bytes per pixel value will be around 5.
773     */
774
775    *bytes = 2 + cv->height * (3 + cv->width * 14);
776    cur = data = malloc(*bytes);
777
778    for(y = 0; y < cv->height; y++)
779    {
780        uint32_t *lineattr = cv->attrs + y * cv->width;
781        uint32_t *linechar = cv->chars + y * cv->width;
782
783        uint8_t prevfg = 0x10;
784        uint8_t prevbg = 0x10;
785
786        for(x = 0; x < cv->width; x++)
787        {
788            uint32_t attr = lineattr[x];
789            uint32_t ch = linechar[x];
790            uint8_t ansifg, ansibg, fg, bg;
791
792            if(ch == CACA_MAGIC_FULLWIDTH)
793                continue;
794
795            ansifg = caca_attr_to_ansi_fg(attr);
796            ansibg = caca_attr_to_ansi_bg(attr);
797
798            fg = ansifg < 0x10 ? palette[ansifg] : 0x10;
799            bg = ansibg < 0x10 ? palette[ansibg] : 0x10;
800
801            /* TODO: optimise series of same fg / same bg
802             *       don't change fg value if ch == ' '
803             *       make sure the \x03,%d trick works everywhere */
804            if(bg != prevbg || fg != prevfg)
805            {
806                int need_escape = 0;
807
808                if(bg == 0x10)
809                {
810                    if(fg == 0x10)
811                        cur += sprintf(cur, "\x0f");
812                    else
813                    {
814                        if(prevbg == 0x10)
815                            cur += sprintf(cur, "\x03%d", fg);
816                        else
817                            cur += sprintf(cur, "\x0f\x03%d", fg);
818
819                        if(ch == (uint32_t)',')
820                            need_escape = 1;
821                    }
822                }
823                else
824                {
825                    if(fg == 0x10)
826                        cur += sprintf(cur, "\x0f\x03,%d", bg);
827                    else
828                        cur += sprintf(cur, "\x03%d,%d", fg, bg);
829                }
830
831                if(ch >= (uint32_t)'0' && ch <= (uint32_t)'9')
832                    need_escape = 1;
833
834                if(need_escape)
835                    cur += sprintf(cur, "\x02\x02");
836            }
837
838            cur += caca_utf32_to_utf8(cur, ch);
839            prevfg = fg;
840            prevbg = bg;
841        }
842
843        /* TODO: do the same the day we optimise whole lines above */
844        if(!cv->width)
845            *cur++ = ' ';
846
847        *cur++ = '\r';
848        *cur++ = '\n';
849    }
850
851    /* Crop to really used size */
852    debug("IRC export: alloc %lu bytes, realloc %lu",
853          (unsigned long int)*bytes, (unsigned long int)(cur - data));
854    *bytes = (uintptr_t)(cur - data);
855    data = realloc(data, *bytes);
856
857    return data;
858}
859
860/* Export a PostScript document. */
861static void *export_ps(caca_canvas_t const *cv, size_t *bytes)
862{
863    static char const *ps_header =
864        "%!\n"
865        "%% libcaca PDF export\n"
866        "%%LanguageLevel: 2\n"
867        "%%Pages: 1\n"
868        "%%DocumentData: Clean7Bit\n"
869        "/csquare {\n"
870        "  newpath\n"
871        "  0 0 moveto\n"
872        "  0 1 rlineto\n"
873        "  1 0 rlineto\n"
874        "  0 -1 rlineto\n"
875        "  closepath\n"
876        "  setrgbcolor\n"
877        "  fill\n"
878        "} def\n"
879        "/S {\n"
880        "  Show\n"
881        "} bind def\n"
882        "/Courier-Bold findfont\n"
883        "8 scalefont\n"
884        "setfont\n"
885        "gsave\n"
886        "6 10 scale\n";
887
888    char *data, *cur;
889    int x, y;
890
891    /* 200 is arbitrary but should be ok */
892    *bytes = strlen(ps_header) + 100 + cv->height * (32 + cv->width * 200);
893    cur = data = malloc(*bytes);
894
895    /* Header */
896    cur += sprintf(cur, "%s", ps_header);
897    cur += sprintf(cur, "0 %d translate\n", cv->height);
898
899    /* Background, drawn using csquare macro defined in header */
900    for(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            uint8_t argb[8];
907            caca_attr_to_argb64(*lineattr++, argb);
908            cur += sprintf(cur, "1 0 translate\n %f %f %f csquare\n",
909                           (float)argb[1] * (1.0 / 0xf),
910                           (float)argb[2] * (1.0 / 0xf),
911                           (float)argb[3] * (1.0 / 0xf));
912        }
913
914        /* Return to beginning of the line, and jump to the next one */
915        cur += sprintf(cur, "-%d 1 translate\n", cv->width);
916    }
917
918    cur += sprintf(cur, "grestore\n"); /* Restore transformation matrix */
919    cur += sprintf(cur, "0 %d translate\n", cv->height*10);
920
921    for(y = cv->height; y--; )
922    {
923        uint32_t *lineattr = cv->attrs + (cv->height - y - 1) * cv->width;
924        uint32_t *linechar = cv->chars + (cv->height - y - 1) * cv->width;
925
926        for(x = 0; x < cv->width; x++)
927        {
928            uint8_t argb[8];
929            uint32_t ch = *linechar++;
930
931            caca_attr_to_argb64(*lineattr++, argb);
932
933            cur += sprintf(cur, "newpath\n");
934            cur += sprintf(cur, "%d %d moveto\n", (x + 1) * 6, y * 10 + 2);
935            cur += sprintf(cur, "%f %f %f setrgbcolor\n",
936                           (float)argb[5] * (1.0 / 0xf),
937                           (float)argb[6] * (1.0 / 0xf),
938                           (float)argb[7] * (1.0 / 0xf));
939
940            if(ch < 0x00000020)
941                cur += sprintf(cur, "(?) show\n");
942            else if(ch >= 0x00000080)
943                cur += sprintf(cur, "(?) show\n");
944            else switch((uint8_t)(ch & 0x7f))
945            {
946                case '\\':
947                case '(':
948                case ')':
949                    cur += sprintf(cur, "(\\%c) show\n", (uint8_t)ch);
950                    break;
951                default:
952                    cur += sprintf(cur, "(%c) show\n", (uint8_t)ch);
953                    break;
954            }
955        }
956    }
957
958    cur += sprintf(cur, "showpage\n");
959
960    /* Crop to really used size */
961    debug("PS export: alloc %lu bytes, realloc %lu",
962          (unsigned long int)*bytes, (unsigned long int)(cur - data));
963    *bytes = (uintptr_t)(cur - data);
964    data = realloc(data, *bytes);
965
966    return data;
967}
968
969/* Export an SVG vector image */
970static void *export_svg(caca_canvas_t const *cv, size_t *bytes)
971{
972    static char const svg_header[] =
973        "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
974        "<svg width=\"%d\" height=\"%d\" viewBox=\"0 0 %d %d\""
975        " xmlns=\"http://www.w3.org/2000/svg\""
976        " xmlns:xlink=\"http://www.w3.org/1999/xlink\""
977        " xml:space=\"preserve\" version=\"1.1\"  baseProfile=\"full\">\n";
978
979    char *data, *cur;
980    int x, y;
981
982    /* 200 is arbitrary but should be ok */
983    *bytes = strlen(svg_header) + 128 + cv->width * cv->height * 200;
984    cur = data = malloc(*bytes);
985
986    /* Header */
987    cur += sprintf(cur, svg_header, cv->width * 6, cv->height * 10,
988                                    cv->width * 6, cv->height * 10);
989
990    cur += sprintf(cur, " <g id=\"mainlayer\" font-size=\"10\""
991                        " style=\"font-family: monospace\">\n");
992
993    /* Background */
994    for(y = 0; y < cv->height; y++)
995    {
996        uint32_t *lineattr = cv->attrs + y * cv->width;
997
998        for(x = 0; x < cv->width; x++)
999        {
1000            cur += sprintf(cur, "<rect style=\"fill:#%.03x\" x=\"%d\" y=\"%d\""
1001                                " width=\"6\" height=\"10\"/>\n",
1002                                caca_attr_to_rgb12_bg(*lineattr++),
1003                                x * 6, y * 10);
1004        }
1005    }
1006
1007    /* Text */
1008    for(y = 0; y < cv->height; y++)
1009    {
1010        uint32_t *lineattr = cv->attrs + y * cv->width;
1011        uint32_t *linechar = cv->chars + y * cv->width;
1012
1013        for(x = 0; x < cv->width; x++)
1014        {
1015            uint32_t ch = *linechar++;
1016
1017            if(ch == ' ' || ch == CACA_MAGIC_FULLWIDTH)
1018            {
1019                lineattr++;
1020                continue;
1021            }
1022
1023            cur += sprintf(cur, "<text style=\"fill:#%.03x\" "
1024                                "x=\"%d\" y=\"%d\">",
1025                                caca_attr_to_rgb12_fg(*lineattr++),
1026                                x * 6, (y * 10) + 8);
1027
1028            if(ch < 0x00000020)
1029                *cur++ = '?';
1030            else if(ch > 0x0000007f)
1031                cur += caca_utf32_to_utf8(cur, ch);
1032            else switch((uint8_t)ch)
1033            {
1034                case '>': cur += sprintf(cur, "&gt;"); break;
1035                case '<': cur += sprintf(cur, "&lt;"); break;
1036                case '&': cur += sprintf(cur, "&amp;"); break;
1037                default: *cur++ = ch; break;
1038            }
1039            cur += sprintf(cur, "</text>\n");
1040        }
1041    }
1042
1043    cur += sprintf(cur, " </g>\n");
1044    cur += sprintf(cur, "</svg>\n");
1045
1046    /* Crop to really used size */
1047    debug("SVG export: alloc %lu bytes, realloc %lu",
1048          (unsigned long int)*bytes, (unsigned long int)(cur - data));
1049    *bytes = (uintptr_t)(cur - data);
1050    data = realloc(data, *bytes);
1051
1052    return data;
1053}
1054
1055/* Export a TGA image */
1056static void *export_tga(caca_canvas_t const *cv, size_t *bytes)
1057{
1058    char const * const *fontlist;
1059    char *data, *cur;
1060    caca_font_t *f;
1061    int i, w, h;
1062
1063    fontlist = caca_get_font_list();
1064    if(!fontlist[0])
1065    {
1066        seterrno(EINVAL);
1067        return NULL;
1068    }
1069
1070    f = caca_load_font(fontlist[0], 0);
1071
1072    w = caca_get_canvas_width(cv) * caca_get_font_width(f);
1073    h = caca_get_canvas_height(cv) * caca_get_font_height(f);
1074
1075    *bytes = w * h * 4 + 18; /* 32 bpp + 18 bytes for the header */
1076    cur = data = malloc(*bytes);
1077
1078    /* ID Length */
1079    cur += sprintf(cur, "%c", 0);
1080    /* Color Map Type: no colormap */
1081    cur += sprintf(cur, "%c", 0);
1082    /* Image Type: uncompressed truecolor */
1083    cur += sprintf(cur, "%c", 2);
1084    /* Color Map Specification: no color map */
1085    memset(cur, 0, 5); cur += 5;
1086
1087    /* Image Specification */
1088    cur += sprintf(cur, "%c%c", 0, 0); /* X Origin */
1089    cur += sprintf(cur, "%c%c", 0, 0); /* Y Origin */
1090    cur += sprintf(cur, "%c%c", w & 0xff, w >> 8); /* Width */
1091    cur += sprintf(cur, "%c%c", h & 0xff, h >> 8); /* Height */
1092    cur += sprintf(cur, "%c", 32); /* Pixel Depth */
1093    cur += sprintf(cur, "%c", 40); /* Image Descriptor */
1094
1095    /* Image ID: no ID */
1096    /* Color Map Data: no colormap */
1097
1098    /* Image Data */
1099    caca_render_canvas(cv, f, cur, w, h, 4 * w);
1100
1101    /* Swap bytes. What a waste of time. */
1102    for(i = 0; i < w * h * 4; i += 4)
1103    {
1104        char c;
1105        c = cur[i]; cur[i] = cur[i + 3]; cur[i + 3] = c;
1106        c = cur[i + 1]; cur[i + 1] = cur[i + 2]; cur[i + 2] = c;
1107    }
1108
1109    caca_free_font(f);
1110
1111    return data;
1112}
1113
1114/*
1115 * XXX: The following functions are aliases.
1116 */
1117
1118void *cucul_export_memory(cucul_canvas_t const *, char const *,
1119                          size_t *) CACA_ALIAS(caca_export_memory);
1120char const * const * cucul_get_export_list(void)
1121         CACA_ALIAS(caca_get_export_list);
1122
Note: See TracBrowser for help on using the repository browser.