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

Last change on this file since 3582 was 3582, checked in by sam, 5 years ago

Coding style: remove tabs and trailing spaces.

  • Property svn:keywords set to Id
File size: 33.4 KB
Line 
1/*
2 *  libcaca       Colour ASCII-Art library
3 *  Copyright (c) 2002-2009 Sam Hocevar <sam@hocevar.net>
4 *                2006 Jean-Yves Lamoureux <jylam@lnxscene.org>
5 *                All Rights Reserved
6 *
7 *  $Id$
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#include "codec.h"
31
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
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 *);
55
56/** \brief Export a canvas into a foreign format.
57 *
58 *  This function exports a libcaca canvas into various foreign formats such
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.
61 *
62 *  Valid values for \c format are:
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.
72 *
73 *  If an error occurs, NULL is returned and \b errno is set accordingly:
74 *  - \c EINVAL Unsupported format requested.
75 *  - \c ENOMEM Not enough memory to allocate output buffer.
76 *
77 *  \param cv A libcaca canvas
78 *  \param format A string describing the requested output format.
79 *  \param bytes A pointer to a size_t where the number of allocated bytes
80 *         will be written.
81 *  \return A pointer to the exported memory area, or NULL in case of error.
82 */
83void *caca_export_canvas_to_memory(caca_canvas_t const *cv, char const *format,
84                                   size_t *bytes)
85{
86    if(!strcasecmp("caca", format))
87        return export_caca(cv, bytes);
88
89    if(!strcasecmp("ansi", format))
90        return _export_ansi(cv, bytes);
91
92    if(!strcasecmp("utf8", format))
93        return _export_utf8(cv, bytes, 0);
94
95    if(!strcasecmp("utf8cr", format))
96        return _export_utf8(cv, bytes, 1);
97
98    if(!strcasecmp("html", format))
99        return export_html(cv, bytes);
100
101    if(!strcasecmp("html3", format))
102        return export_html3(cv, bytes);
103
104    if(!strcasecmp("bbfr", format))
105        return export_bbfr(cv, bytes);
106
107    if(!strcasecmp("irc", format))
108        return _export_irc(cv, bytes);
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
119    seterrno(EINVAL);
120    return NULL;
121}
122
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
149        || x + w > cv->width || y + h > cv->height)
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
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
170 *  the export format, to be used with caca_export_memory(), and a string
171 *  containing the natural language description for that export format.
172 *
173 *  This function never fails.
174 *
175 *  \return An array of strings.
176 */
177char const * const * caca_get_export_list(void)
178{
179    static char const * const list[] =
180    {
181        "caca", "native libcaca format",
182        "ansi", "ANSI",
183        "utf8", "UTF-8 with ANSI escape codes",
184        "utf8cr", "UTF-8 with ANSI escape codes and MS-DOS \\r",
185        "html", "HTML",
186        "html3", "backwards-compatible HTML",
187        "bbfr", "BBCode (French)",
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
202/* Generate a native libcaca canvas file. */
203static void *export_caca(caca_canvas_t const *cv, size_t *bytes)
204{
205    char *data, *cur;
206    int f, n;
207
208    /* 52 bytes for the header:
209     *  - 4 bytes for "\xCA\xCA" + "CV"
210     *  - 16 bytes for the canvas header
211     *  - 32 bytes for the frame info
212     * 8 bytes for each character cell */
213    *bytes = 20 + (32 + 8 * cv->width * cv->height) * cv->framecount;
214    cur = data = malloc(*bytes);
215
216    /* magic */
217    cur += sprintf(cur, "%s", "\xCA\xCA" "CV");
218
219    /* canvas_header */
220    cur += sprintu32(cur, 16 + 32 * cv->framecount);
221    cur += sprintu32(cur, cv->width * cv->height * 8 * cv->framecount);
222    cur += sprintu16(cur, 0x0001);
223    cur += sprintu32(cur, cv->framecount);
224    cur += sprintu16(cur, 0x0000);
225
226    /* frame_info */
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    }
238
239    /* canvas_data */
240    for(f = 0; f < cv->framecount; f++)
241    {
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        }
250    }
251
252    return data;
253}
254
255/* Generate HTML representation of current canvas. */
256static void *export_html(caca_canvas_t const *cv, size_t *bytes)
257{
258    char *data, *cur;
259    int x, y, len;
260
261    /* The HTML header: less than 1000 bytes
262     * A line: 7 chars for "<br />\n"
263     * A glyph: 47 chars for "<span style="color:#xxx;background-color:#xxx">"
264     *          83 chars for ";font-weight..."
265     *          up to 10 chars for "&#xxxxxxx;", far less for pure ASCII
266     *          7 chars for "</span>" */
267    *bytes = 1000 + cv->height * (7 + cv->width * (47 + 83 + 10 + 7));
268    cur = data = malloc(*bytes);
269
270    /* HTML header */
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");
276    cur += sprintf(cur, "<title>Generated by libcaca %s</title>\n",
277                        caca_get_version());
278    cur += sprintf(cur, "</head><body>\n");
279
280    cur += sprintf(cur, "<div style=\"%s\">\n",
281                        "font-family: monospace, fixed; font-weight: bold;");
282
283    for(y = 0; y < cv->height; y++)
284    {
285        uint32_t *lineattr = cv->attrs + y * cv->width;
286        uint32_t *linechar = cv->chars + y * cv->width;
287
288        for(x = 0; x < cv->width; x += len)
289        {
290            cur += sprintf(cur, "<span style=\"");
291            if(caca_attr_to_ansi_fg(lineattr[x]) != CACA_DEFAULT)
292                cur += sprintf(cur, ";color:#%.03x",
293                               caca_attr_to_rgb12_fg(lineattr[x]));
294            if(caca_attr_to_ansi_bg(lineattr[x]) < 0x10)
295                cur += sprintf(cur, ";background-color:#%.03x",
296                               caca_attr_to_rgb12_bg(lineattr[x]));
297            if(lineattr[x] & CACA_BOLD)
298                cur += sprintf(cur, ";font-weight:bold");
299            if(lineattr[x] & CACA_ITALICS)
300                cur += sprintf(cur, ";font-style:italic");
301            if(lineattr[x] & CACA_UNDERLINE)
302                cur += sprintf(cur, ";text-decoration:underline");
303            if(lineattr[x] & CACA_BLINK)
304                cur += sprintf(cur, ";text-decoration:blink");
305            cur += sprintf(cur, "\">");
306
307            for(len = 0;
308                x + len < cv->width && lineattr[x + len] == lineattr[x];
309                len++)
310            {
311                if(linechar[x + len] == CACA_MAGIC_FULLWIDTH)
312                    ;
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;");
336                else if(linechar[x + len] < 0x00000080)
337                    cur += sprintf(cur, "%c", (uint8_t)linechar[x + len]);
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]);
346                else
347                    /* non-character codepoints become U+FFFD
348                     * REPLACEMENT CHARACTER */
349                    cur += sprintf(cur, "&#%i;", (unsigned int)0x0000fffd);
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 */
360    debug("html export: alloc %lu bytes, realloc %lu",
361          (unsigned long int)*bytes, (unsigned long int)(cur - data));
362    *bytes = (uintptr_t)(cur - data);
363    data = realloc(data, *bytes);
364
365    return data;
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. */
372static void *export_html3(caca_canvas_t const *cv, size_t *bytes)
373{
374    char *data, *cur;
375    int x, y, len;
376    int has_multi_cell_row = 0;
377    unsigned char *cell_boundary_bitmap;
378
379    /* Table */
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);
383    for(y = 0; y < cv->height; y++)
384    {
385        uint32_t *lineattr = cv->attrs + y * cv->width;
386        uint32_t *linechar = cv->chars + y * cv->width;
387
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))
394               &&
395               (((linechar[x - 1] == CACA_MAGIC_FULLWIDTH)
396                 &&
397                 (! caca_utf32_is_fullwidth(linechar[x])))
398                ||
399                (caca_attr_to_ansi_bg(lineattr[x - 1])
400                 !=
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)))
410            {
411                has_multi_cell_row = 1;
412                if(cell_boundary_bitmap)
413                    cell_boundary_bitmap[x / 8] |= 1 << (x % 8);
414            }
415    }
416
417    /* The HTML table markup: less than 1000 bytes
418     * A line: 10 chars for "<tr></tr>\n"
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>"
421     *          up to 10 chars for "&#xxxxxxx;" (far less for pure ASCII)
422     *          17 chars for "</font></tt></td>" */
423    *bytes = 1000 + cv->height * (10 + cv->width * (48 + 36 + 10 + 17));
424    cur = data = malloc(*bytes);
425
426    cur += sprintf(cur, "<table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" summary=\"[libcaca canvas export]\">\n");
427
428    for(y = 0; y < cv->height; y++)
429    {
430        uint32_t *lineattr = cv->attrs + y * cv->width;
431        uint32_t *linechar = cv->chars + y * cv->width;
432
433        cur += sprintf(cur, "<tr>");
434
435        for(x = 0; x < cv->width; x += len)
436        {
437            int i, needfont = 0;
438            int nonblank = 0;
439
440            /* Use colspan option to factor cells with same attributes
441             * (see below) */
442            len = 1;
443            while((x + len < cv->width)
444                  &&
445                  ((y
446                    &&
447                    (linechar[x + len] > 0x00000020)
448                    &&
449                    ((linechar[x + len] < 0x0000007f)
450                     ||
451                     (linechar[x + len] > 0x000000a0)))
452                   ||
453                   (! (cell_boundary_bitmap
454                       ?
455                       (cell_boundary_bitmap[(x + len) / 8] & (1 << ((x + len) % 8)))
456                       :
457                       has_multi_cell_row))
458                   ||
459                   (linechar[x + len] == CACA_MAGIC_FULLWIDTH)
460                   ||
461                   (cv->height == 1))
462                  &&
463                  ((linechar[x + len - 1] != CACA_MAGIC_FULLWIDTH)
464                   ||
465                   caca_utf32_is_fullwidth(linechar[x + len]))
466                  &&
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])
474                    ==
475                    _caca_attr_to_rgb24bg(lineattr[x]))
476                   :
477                   1))
478                len++;
479
480            for(i = 0; i < len; i++)
481                if(! ((linechar[x + i] <= 0x00000020)
482                      ||
483                      ((linechar[x + i] >= 0x0000007f)
484                       &&
485                       (linechar[x + i] <= 0x000000a0))))
486                    nonblank = 1;
487
488            cur += sprintf(cur, "<td");
489
490            if(caca_attr_to_ansi_bg(lineattr[x]) < 0x10)
491                cur += sprintf(cur, " bgcolor=\"#%.06lx\"", (unsigned long int)
492                               _caca_attr_to_rgb24bg(lineattr[x]));
493
494            if(has_multi_cell_row && (len > 1))
495            {
496                int colspan;
497
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
511            cur += sprintf(cur, ">");
512
513            cur += sprintf(cur, "<tt>");
514
515            for(i = 0; i < len; i++)
516            {
517                if(nonblank
518                   &&
519                   ((! i)
520                    ||
521                    (lineattr[x + i] != lineattr[x + i - 1])))
522                {
523                    needfont = (caca_attr_to_ansi_fg(lineattr[x + i])
524                                !=
525                                CACA_DEFAULT);
526
527                    if(needfont)
528                        cur += sprintf(cur, "<font color=\"#%.06lx\">",
529                                       (unsigned long int)
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
542                if(linechar[x + i] == CACA_MAGIC_FULLWIDTH)
543                    ;
544                else if((linechar[x + i] <= 0x00000020)
545                        ||
546                        ((linechar[x + i] >= 0x0000007f)
547                         &&
548                         (linechar[x + i] <= 0x000000a0)))
549                {
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
554                     * XHTML with no DTD too. */
555                    cur += sprintf(cur, "&#160;");
556                }
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;");
567                else if(linechar[x + i] < 0x00000080)
568                    cur += sprintf(cur, "%c", (uint8_t)linechar[x + i]);
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]);
577                else
578                    /* non-character codepoints become U+FFFD
579                     * REPLACEMENT CHARACTER */
580                    cur += sprintf(cur, "&#%i;", (unsigned int)0x0000fffd);
581
582                if (nonblank
583                    &&
584                    (((i + 1) == len)
585                     ||
586                     (lineattr[x + i + 1] != lineattr[x + i])))
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                }
600            }
601
602            cur += sprintf(cur, "</tt>");
603            cur += sprintf(cur, "</td>");
604        }
605        cur += sprintf(cur, "</tr>\n");
606    }
607
608    /* Footer */
609    cur += sprintf(cur, "</table>\n");
610
611    /* Free working memory */
612    if (cell_boundary_bitmap)
613        free((void *) cell_boundary_bitmap);
614
615    /* Crop to really used size */
616    debug("html3 export: alloc %lu bytes, realloc %lu",
617          (unsigned long int)*bytes, (unsigned long int)(cur - data));
618    *bytes = (uintptr_t)(cur - data);
619    data = realloc(data, *bytes);
620
621    return data;
622}
623
624static void *export_bbfr(caca_canvas_t const *cv, size_t *bytes)
625{
626    char *data, *cur;
627    int x, y, len;
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        {
648            int i, needback, needfront;
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
662            needback = caca_attr_to_ansi_bg(lineattr[x]) < 0x10;
663            needfront = caca_attr_to_ansi_fg(lineattr[x]) < 0x10;
664
665            if(needback)
666                cur += sprintf(cur, "[f=#%.06lx]", (unsigned long int)
667                               _caca_attr_to_rgb24bg(lineattr[x]));
668
669            if(linechar[x] == ' ')
670                cur += sprintf(cur, "[c=#%.06lx]", (unsigned long int)
671                               _caca_attr_to_rgb24bg(lineattr[x]));
672            else if(needfront)
673                cur += sprintf(cur, "[c=#%.06lx]", (unsigned long int)
674                               _caca_attr_to_rgb24fg(lineattr[x]));
675
676            if(lineattr[x] & CACA_BOLD)
677                cur += sprintf(cur, "[g]");
678            if(lineattr[x] & CACA_ITALICS)
679                cur += sprintf(cur, "[i]");
680            if(lineattr[x] & CACA_UNDERLINE)
681                cur += sprintf(cur, "[s]");
682            if(lineattr[x] & CACA_BLINK)
683                ; /* FIXME */
684
685            for(i = 0; i < len; i++)
686            {
687                if(linechar[x + i] == CACA_MAGIC_FULLWIDTH)
688                    ;
689                else if(linechar[x + i] == ' ')
690                    *cur++ = '_';
691                else
692                    cur += caca_utf32_to_utf8(cur, linechar[x + i]);
693            }
694
695            if(lineattr[x] & CACA_BLINK)
696                ; /* FIXME */
697            if(lineattr[x] & CACA_UNDERLINE)
698                cur += sprintf(cur, "[/s]");
699            if(lineattr[x] & CACA_ITALICS)
700                cur += sprintf(cur, "[/i]");
701            if(lineattr[x] & CACA_BOLD)
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
724/* Export a PostScript document. */
725static void *export_ps(caca_canvas_t const *cv, size_t *bytes)
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
752    char *data, *cur;
753    int x, y;
754
755    /* 200 is arbitrary but should be ok */
756    *bytes = strlen(ps_header) + 100 + cv->height * (32 + cv->width * 200);
757    cur = data = malloc(*bytes);
758
759    /* Header */
760    cur += sprintf(cur, "%s", ps_header);
761    cur += sprintf(cur, "0 %d translate\n", cv->height);
762
763    /* Background, drawn using csquare macro defined in header */
764    for(y = cv->height; y--; )
765    {
766        uint32_t *lineattr = cv->attrs + y * cv->width;
767
768        for(x = 0; x < cv->width; x++)
769        {
770            uint8_t argb[8];
771            caca_attr_to_argb64(*lineattr++, argb);
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 */
779        cur += sprintf(cur, "-%d 1 translate\n", cv->width);
780    }
781
782    cur += sprintf(cur, "grestore\n"); /* Restore transformation matrix */
783    cur += sprintf(cur, "0 %d translate\n", cv->height*10);
784
785    for(y = cv->height; y--; )
786    {
787        uint32_t *lineattr = cv->attrs + (cv->height - y - 1) * cv->width;
788        uint32_t *linechar = cv->chars + (cv->height - y - 1) * cv->width;
789
790        for(x = 0; x < cv->width; x++)
791        {
792            uint8_t argb[8];
793            uint32_t ch = *linechar++;
794
795            caca_attr_to_argb64(*lineattr++, argb);
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
804            if(ch < 0x00000020)
805                cur += sprintf(cur, "(?) show\n");
806            else if(ch >= 0x00000080)
807                cur += sprintf(cur, "(?) show\n");
808            else switch((uint8_t)(ch & 0x7f))
809            {
810                case '\\':
811                case '(':
812                case ')':
813                    cur += sprintf(cur, "(\\%c) show\n", (uint8_t)ch);
814                    break;
815                default:
816                    cur += sprintf(cur, "(%c) show\n", (uint8_t)ch);
817                    break;
818            }
819        }
820    }
821
822    cur += sprintf(cur, "showpage\n");
823
824    /* Crop to really used size */
825    debug("PS export: alloc %lu bytes, realloc %lu",
826          (unsigned long int)*bytes, (unsigned long int)(cur - data));
827    *bytes = (uintptr_t)(cur - data);
828    data = realloc(data, *bytes);
829
830    return data;
831}
832
833/* Export an SVG vector image */
834static void *export_svg(caca_canvas_t const *cv, size_t *bytes)
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
843    char *data, *cur;
844    int x, y;
845
846    /* 200 is arbitrary but should be ok */
847    *bytes = strlen(svg_header) + 128 + cv->width * cv->height * 200;
848    cur = data = malloc(*bytes);
849
850    /* Header */
851    cur += sprintf(cur, svg_header, cv->width * 6, cv->height * 10,
852                                    cv->width * 6, cv->height * 10);
853
854    cur += sprintf(cur, " <g id=\"mainlayer\" font-size=\"10\""
855                        " style=\"font-family: monospace\">\n");
856
857    /* Background */
858    for(y = 0; y < cv->height; y++)
859    {
860        uint32_t *lineattr = cv->attrs + y * cv->width;
861
862        for(x = 0; x < cv->width; x++)
863        {
864            cur += sprintf(cur, "<rect style=\"fill:#%.03x\" x=\"%d\" y=\"%d\""
865                                " width=\"6\" height=\"10\"/>\n",
866                                caca_attr_to_rgb12_bg(*lineattr++),
867                                x * 6, y * 10);
868        }
869    }
870
871    /* Text */
872    for(y = 0; y < cv->height; y++)
873    {
874        uint32_t *lineattr = cv->attrs + y * cv->width;
875        uint32_t *linechar = cv->chars + y * cv->width;
876
877        for(x = 0; x < cv->width; x++)
878        {
879            uint32_t ch = *linechar++;
880
881            if(ch == ' ' || ch == CACA_MAGIC_FULLWIDTH)
882            {
883                lineattr++;
884                continue;
885            }
886
887            cur += sprintf(cur, "<text style=\"fill:#%.03x\" "
888                                "x=\"%d\" y=\"%d\">",
889                                caca_attr_to_rgb12_fg(*lineattr++),
890                                x * 6, (y * 10) + 8);
891
892            if(ch < 0x00000020)
893                *cur++ = '?';
894            else if(ch > 0x0000007f)
895                cur += caca_utf32_to_utf8(cur, ch);
896            else switch((uint8_t)ch)
897            {
898                case '>': cur += sprintf(cur, "&gt;"); break;
899                case '<': cur += sprintf(cur, "&lt;"); break;
900                case '&': cur += sprintf(cur, "&amp;"); break;
901                default: *cur++ = (uint8_t)ch; break;
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 */
911    debug("SVG export: alloc %lu bytes, realloc %lu",
912          (unsigned long int)*bytes, (unsigned long int)(cur - data));
913    *bytes = (uintptr_t)(cur - data);
914    data = realloc(data, *bytes);
915
916    return data;
917}
918
919/* Export a TGA image */
920static void *export_tga(caca_canvas_t const *cv, size_t *bytes)
921{
922    char const * const *fontlist;
923    char *data, *cur;
924    caca_font_t *f;
925    int i, w, h;
926
927    fontlist = caca_get_font_list();
928    if(!fontlist[0])
929    {
930        seterrno(EINVAL);
931        return NULL;
932    }
933
934    f = caca_load_font(fontlist[0], 0);
935
936    w = caca_get_canvas_width(cv) * caca_get_font_width(f);
937    h = caca_get_canvas_height(cv) * caca_get_font_height(f);
938
939    *bytes = w * h * 4 + 18; /* 32 bpp + 18 bytes for the header */
940    cur = data = malloc(*bytes);
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 */
963    caca_render_canvas(cv, f, cur, w, h, 4 * w);
964
965    /* Swap bytes. What a waste of time. */
966    for(i = 0; i < w * h * 4; i += 4)
967    {
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;
971    }
972
973    caca_free_font(f);
974
975    return data;
976}
977
978/*
979 * XXX: The following functions are aliases.
980 */
981
982void *cucul_export_memory(cucul_canvas_t const *, char const *,
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);
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.