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

Revision 3582, 33.4 KB checked in by sam, 4 years ago (diff)

Coding style: remove tabs and trailing spaces.

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