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

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