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

Last change on this file since 3235 was 3235, checked in by bsittler, 11 years ago

do not expand tabs here. instead, importers need to understand '\t'
and fill the canvas up to the tab stop with it. once that works,
support will be added here (treating it as space.) also, fewer columns
are now generated. apparently generating a minimal number of them
fixes a bunch of the table-layout bugs.

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