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

Last change on this file since 3492 was 3492, checked in by Sam Hocevar, 11 years ago

Split import.c and export.c to create a new text.c codec file in order
to keep source files under 1,000 lines of code.

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