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

Last change on this file since 4333 was 4333, checked in by sam, 4 years ago

Large source code cleanup, getting rid of spaces, tabs, and svn keywords.

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