source: libcaca/trunk/cucul/export.c @ 1283

Last change on this file since 1283 was 1283, checked in by Sam Hocevar, 13 years ago
  • Updated the caca export format so that it supports multiple frames.
  • Updated the caca importer to reflect that; only one frame is read at the moment.
  • Added an "utf8cr" export format for UTF-8 + CRLF exports.
  • Updated cacaserver to reflect file format changes.
  • Property svn:keywords set to Id
File size: 28.9 KB
Line 
1/*
2 *  libcucul      Canvas for ultrafast compositing of Unicode letters
3 *  Copyright (c) 2002-2006 Sam Hocevar <sam@zoy.org>
4 *                2006 Jean-Yves Lamoureux <jylam@lnxscene.org>
5 *                All Rights Reserved
6 *
7 *  $Id: export.c 1283 2006-11-05 22:20:51Z sam $
8 *
9 *  This library is free software; you can redistribute it and/or
10 *  modify it under the terms of the Do What The Fuck You Want To
11 *  Public License, Version 2, as published by Sam Hocevar. See
12 *  http://sam.zoy.org/wtfpl/COPYING for more details.
13 */
14
15/*
16 *  This file contains various export functions
17 */
18
19#include "config.h"
20#include "common.h"
21
22#if !defined(__KERNEL__)
23#   if defined(HAVE_ERRNO_H)
24#       include <errno.h>
25#   endif
26#   include <stdlib.h>
27#   include <stdio.h>
28#   include <string.h>
29#endif
30
31#include "cucul.h"
32#include "cucul_internals.h"
33
34static inline int sprintu32(char *s, uint32_t x)
35{
36    s[0] = (uint8_t)(x >> 24);
37    s[1] = (uint8_t)(x >> 16) & 0xff;
38    s[2] = (uint8_t)(x >>  8) & 0xff;
39    s[3] = (uint8_t)(x      ) & 0xff;
40    return 4;
41}
42
43static inline int sprintu16(char *s, uint16_t x)
44{
45    s[0] = (uint8_t)(x >>  8) & 0xff;
46    s[1] = (uint8_t)(x      ) & 0xff;
47    return 2;
48}
49
50static int export_caca(cucul_canvas_t *, cucul_buffer_t *);
51static int export_ansi(cucul_canvas_t *, cucul_buffer_t *);
52static int export_utf8(cucul_canvas_t *, cucul_buffer_t *, int);
53static int export_html(cucul_canvas_t *, cucul_buffer_t *);
54static int export_html3(cucul_canvas_t *, cucul_buffer_t *);
55static int export_irc(cucul_canvas_t *, cucul_buffer_t *);
56static int export_ps(cucul_canvas_t *, cucul_buffer_t *);
57static int export_svg(cucul_canvas_t *, cucul_buffer_t *);
58static int export_tga(cucul_canvas_t *, cucul_buffer_t *);
59
60/** \brief Export a canvas into a foreign format.
61 *
62 *  This function exports a libcucul canvas into various foreign formats such
63 *  as ANSI art, HTML, IRC colours, etc. One should use cucul_get_buffer_data()
64 *  and cucul_get_buffer_size() to access the buffer contents. The allocated
65 *  data is valid until cucul_free_buffer() is called.
66 *
67 *  Valid values for \c format are:
68 *  - \c "caca": export native libcaca files.
69 *  - \c "ansi": export ANSI art (CP437 charset with ANSI colour codes).
70 *  - \c "html": export an HTML page with CSS information.
71 *  - \c "html3": export an HTML table that should be compatible with
72 *    most navigators, including textmode ones.
73 *  - \c "irc": export UTF-8 text with mIRC colour codes.
74 *  - \c "ps": export a PostScript document.
75 *  - \c "svg": export an SVG vector image.
76 *  - \c "tga": export a TGA image.
77 *
78 *  If an error occurs, NULL is returned and \b errno is set accordingly:
79 *  - \c EINVAL Unsupported format requested.
80 *  - \c ENOMEM Not enough memory to allocate output buffer.
81 *
82 *  \param cv A libcucul canvas
83 *  \param format A string describing the requested output format.
84 *  \return A libcucul buffer, or NULL in case of error.
85 */
86cucul_buffer_t * cucul_export_canvas(cucul_canvas_t *cv, char const *format)
87{
88    cucul_buffer_t *ex;
89    int ret = -1;
90
91    ex = malloc(sizeof(cucul_buffer_t));
92    if(!ex)
93    {
94#if defined(HAVE_ERRNO_H)
95        errno = ENOMEM;
96#endif
97        return NULL;
98    }
99
100    ex->size = 0;
101    ex->data = NULL;
102    ex->user_data = 0;
103
104    if(!strcasecmp("caca", format))
105        ret = export_caca(cv, ex);
106    else if(!strcasecmp("ansi", format))
107        ret = export_ansi(cv, ex);
108    else if(!strcasecmp("utf8", format))
109        ret = export_utf8(cv, ex, 0);
110    else if(!strcasecmp("utf8cr", format))
111        ret = export_utf8(cv, ex, 1);
112    else if(!strcasecmp("html", format))
113        ret = export_html(cv, ex);
114    else if(!strcasecmp("html3", format))
115        ret = export_html3(cv, ex);
116    else if(!strcasecmp("irc", format))
117        ret = export_irc(cv, ex);
118    else if(!strcasecmp("ps", format))
119        ret = export_ps(cv, ex);
120    else if(!strcasecmp("svg", format))
121        ret = export_svg(cv, ex);
122    else if(!strcasecmp("tga", format))
123        ret = export_tga(cv, ex);
124
125    if(ret < 0)
126    {
127        free(ex);
128#if defined(HAVE_ERRNO_H)
129        errno = EINVAL;
130#endif
131        return NULL;
132    }
133
134    return ex;
135}
136
137/** \brief Get available export formats
138 *
139 *  Return a list of available export formats. The list is a NULL-terminated
140 *  array of strings, interleaving a string containing the internal value for
141 *  the export format, to be used with cucul_export_canvas(), and a string
142 *  containing the natural language description for that export format.
143 *
144 *  This function never fails.
145 *
146 *  \return An array of strings.
147 */
148char const * const * cucul_get_export_list(void)
149{
150    static char const * const list[] =
151    {
152        "caca", "native libcaca format",
153        "ansi", "ANSI",
154        "utf8", "UTF-8 with ANSI escape codes",
155        "utf8cr", "UTF-8 with ANSI escape codes and MS-DOS \\r",
156        "html", "HTML",
157        "html3", "backwards-compatible HTML",
158        "irc", "IRC with mIRC colours",
159        "ps", "PostScript document",
160        "svg", "SVG vector image",
161        "tga", "TGA image",
162        NULL, NULL
163    };
164
165    return list;
166}
167
168/*
169 * XXX: the following functions are local.
170 */
171
172/* Generate a native libcaca canvas file. */
173static int export_caca(cucul_canvas_t *cv, cucul_buffer_t *ex)
174{
175    uint32_t *attrs = cv->attrs;
176    uint32_t *chars = cv->chars;
177    char *cur;
178    unsigned int n;
179
180    /* 44 bytes for the header:
181     *  - 4 bytes for "\xCA\xCA" + "CV"
182     *  - 16 bytes for the canvas header
183     *  - 24 bytes for the frame info
184     * 8 bytes for each character cell */
185    ex->size = 44 + 8 * cv->width * cv->height;
186    ex->data = malloc(ex->size);
187
188    cur = ex->data;
189
190    /* magic */
191    cur += sprintf(cur, "%s", "\xCA\xCA" "CV");
192
193    /* canvas_header */
194    cur += sprintu32(cur, 16 + 24);
195    cur += sprintu32(cur, cv->width * cv->height * 8);
196    cur += sprintu16(cur, 0x0001);
197    cur += sprintu32(cur, 1);
198    cur += sprintu16(cur, 0x0000);
199
200    /* frame_info */
201    cur += sprintu32(cur, cv->width);
202    cur += sprintu32(cur, cv->height);
203    cur += sprintu32(cur, 0);
204    cur += sprintu32(cur, cv->curattr);
205    cur += sprintu32(cur, 0);
206    cur += sprintu32(cur, 0);
207
208    /* canvas_data */
209    for(n = cv->height * cv->width; n--; )
210    {
211        cur += sprintu32(cur, *chars++);
212        cur += sprintu32(cur, *attrs++);
213    }
214
215    return 0;
216}
217
218/*
219 * The libcaca canvas format, version 1
220 * ------------------------------------
221 *
222 * All types are big endian.
223 *
224 * struct
225 * {
226 * magic:
227 *    uint8_t caca_header[2];    // "\xCA\xCA"
228 *    uint8_t caca_file_type[2]; // "CV"
229 *
230 * canvas_header:
231 *    uint32_t control_size;     // Control size (canvas_data - canvas_header)
232 *    uint32_t data_size;        // Data size (EOF - canvas_data)
233 *
234 *    uint16_t version;          // Canvas format version
235 *                               //  bit 0: set to 1 if canvas is compatible
236 *                               //         with version 1 of the format
237 *                               //  bits 1-15: unused yet, must be 0
238 *
239 *    uint32_t frames;           // Frame count
240 *
241 *    uint16_t flags;            // Feature flags
242 *                               //  bits 0-15: unused yet, must be 0
243 *
244 * frame_info:
245 *    struct
246 *    {
247 *       uint32_t width;         // Frame width
248 *       uint32_t height;        // Frame height
249 *       uint32_t duration;      // Frame duration in milliseconds, 0 to
250 *                               // not specify a duration
251 *       uint32_t attr;          // Graphics context attribute
252 *       int32_t handle_x;       // Handle X coordinate
253 *       int32_t handle_y;       // Handle Y coordinate
254 *    }
255 *    frame_list[frames];
256 *
257 * control_extension_1:
258 * control_extension_2:
259 *    ...
260 * control_extension_N:
261 *    ...                        // reserved for future use
262 *
263 * canvas_data:
264 *    uint8_t data[data_size];   // canvas data
265 *
266 * data_extension_1:
267 * data_extension_2:
268 *    ...
269 * data_extension_N:
270 *    ...                        // reserved for future use
271 * };
272 */
273
274/* Generate UTF-8 representation of current canvas. */
275static int export_utf8(cucul_canvas_t *cv, cucul_buffer_t *ex, int cr)
276{
277    static uint8_t const palette[] =
278    {
279        0,  4,  2,  6, 1,  5,  3,  7,
280        8, 12, 10, 14, 9, 13, 11, 15
281    };
282
283    char *cur;
284    unsigned int x, y;
285
286    /* 23 bytes assumed for max length per pixel ('\e[5;1;3x;4y;9x;10ym' plus
287     * 4 max bytes for a UTF-8 character).
288     * Add height*9 to that (zeroes color at the end and jump to next line) */
289    ex->size = (cv->height * 9) + (cv->width * cv->height * 23);
290    ex->data = malloc(ex->size);
291
292    cur = ex->data;
293
294    for(y = 0; y < cv->height; y++)
295    {
296        uint32_t *lineattr = cv->attrs + y * cv->width;
297        uint32_t *linechar = cv->chars + y * cv->width;
298
299        uint8_t prevfg = 0x10;
300        uint8_t prevbg = 0x10;
301
302        for(x = 0; x < cv->width; x++)
303        {
304            uint32_t attr = lineattr[x];
305            uint32_t ch = linechar[x];
306            uint8_t fg, bg;
307
308            if(ch == CUCUL_MAGIC_FULLWIDTH)
309                continue;
310
311            fg = (((attr >> 4) & 0x3fff) == (CUCUL_DEFAULT | 0x40)) ?
312                     0x10 : palette[_cucul_attr_to_ansi4fg(attr)];
313            bg = (((attr >> 18) & 0x3fff) == (CUCUL_TRANSPARENT | 0x40)) ?
314                     0x10 : palette[_cucul_attr_to_ansi4bg(attr)];
315
316            /* TODO: the [0 could be omitted in some cases */
317            if(fg != prevfg || bg != prevbg)
318            {
319                cur += sprintf(cur, "\033[0");
320
321                if(fg < 8)
322                    cur += sprintf(cur, ";3%d", fg);
323                else if(fg < 16)
324                    cur += sprintf(cur, ";1;3%d;9%d", fg - 8, fg - 8);
325
326                if(bg < 8)
327                    cur += sprintf(cur, ";4%d", bg);
328                else if(bg < 16)
329                    cur += sprintf(cur, ";5;4%d;10%d", bg - 8, bg - 8);
330
331                cur += sprintf(cur, "m");
332            }
333
334            cur += cucul_utf32_to_utf8(cur, ch);
335
336            prevfg = fg;
337            prevbg = bg;
338        }
339
340        if(prevfg != 0x10 || prevbg != 0x10)
341            cur += sprintf(cur, "\033[0m");
342
343        cur += sprintf(cur, cr ? "\r\n" : "\n");
344    }
345
346    /* Crop to really used size */
347    ex->size = (uintptr_t)(cur - ex->data);
348    ex->data = realloc(ex->data, ex->size);
349
350    return 0;
351}
352
353/* Generate ANSI representation of current canvas. */
354static int export_ansi(cucul_canvas_t *cv, cucul_buffer_t *ex)
355{
356    static uint8_t const palette[] =
357    {
358        0,  4,  2,  6, 1,  5,  3,  7,
359        8, 12, 10, 14, 9, 13, 11, 15
360    };
361
362    char *cur;
363    unsigned int x, y;
364
365    uint8_t prevfg = -1;
366    uint8_t prevbg = -1;
367
368    /* 16 bytes assumed for max length per pixel ('\e[5;1;3x;4ym' plus
369     * 1 byte for a CP437 character).
370     * Add height*9 to that (zeroes color at the end and jump to next line) */
371    ex->size = (cv->height * 9) + (cv->width * cv->height * 16);
372    ex->data = malloc(ex->size);
373
374    cur = ex->data;
375
376    for(y = 0; y < cv->height; y++)
377    {
378        uint32_t *lineattr = cv->attrs + y * cv->width;
379        uint32_t *linechar = cv->chars + y * cv->width;
380
381        for(x = 0; x < cv->width; x++)
382        {
383            uint8_t fg = palette[_cucul_attr_to_ansi4fg(lineattr[x])];
384            uint8_t bg = palette[_cucul_attr_to_ansi4bg(lineattr[x])];
385            uint32_t ch = linechar[x];
386
387            if(ch == CUCUL_MAGIC_FULLWIDTH)
388                ch = '?';
389
390            if(fg != prevfg || bg != prevbg)
391            {
392                cur += sprintf(cur, "\033[0;");
393
394                if(fg < 8)
395                    if(bg < 8)
396                        cur += sprintf(cur, "3%d;4%dm", fg, bg);
397                    else
398                        cur += sprintf(cur, "5;3%d;4%dm", fg, bg - 8);
399                else
400                    if(bg < 8)
401                        cur += sprintf(cur, "1;3%d;4%dm", fg - 8, bg);
402                    else
403                        cur += sprintf(cur, "5;1;3%d;4%dm", fg - 8, bg - 8);
404            }
405
406            *cur++ = cucul_utf32_to_cp437(ch);
407
408            prevfg = fg;
409            prevbg = bg;
410        }
411
412        if(cv->width == 80)
413        {
414            cur += sprintf(cur, "\033[s\n\033[u");
415        }
416        else
417        {
418            cur += sprintf(cur, "\033[0m\r\n");
419            prevfg = -1;
420            prevbg = -1;
421        }
422    }
423
424    /* Crop to really used size */
425    ex->size = (uintptr_t)(cur - ex->data);
426    ex->data = realloc(ex->data, ex->size);
427
428    return 0;
429}
430
431/* Generate HTML representation of current canvas. */
432static int export_html(cucul_canvas_t *cv, cucul_buffer_t *ex)
433{
434    char *cur;
435    unsigned int x, y, len;
436
437    /* The HTML header: less than 1000 bytes
438     * A line: 7 chars for "<br />\n"
439     * A glyph: 47 chars for "<span style="color:#xxx;background-color:#xxx">"
440     *          83 chars for ";font-weight..."
441     *          up to 9 chars for "&#xxxxxx;", far less for pure ASCII
442     *          7 chars for "</span>" */
443    ex->size = 1000 + cv->height * (7 + cv->width * (47 + 83 + 9 + 7));
444    ex->data = malloc(ex->size);
445
446    cur = ex->data;
447
448    /* HTML header */
449    cur += sprintf(cur, "<html><head>\n");
450    cur += sprintf(cur, "<title>Generated by libcaca %s</title>\n", VERSION);
451    cur += sprintf(cur, "</head><body>\n");
452
453    cur += sprintf(cur, "<div cellpadding='0' cellspacing='0' style='%s'>\n",
454                        "font-family: monospace, fixed; font-weight: bold;");
455
456    for(y = 0; y < cv->height; y++)
457    {
458        uint32_t *lineattr = cv->attrs + y * cv->width;
459        uint32_t *linechar = cv->chars + y * cv->width;
460
461        for(x = 0; x < cv->width; x += len)
462        {
463            cur += sprintf(cur, "<span style=\"color:#%.03x;"
464                                "background-color:#%.03x",
465                                _cucul_attr_to_rgb12fg(lineattr[x]),
466                                _cucul_attr_to_rgb12bg(lineattr[x]));
467            if(lineattr[x] & CUCUL_BOLD)
468                cur += sprintf(cur, ";font-weight:bold");
469            if(lineattr[x] & CUCUL_ITALICS)
470                cur += sprintf(cur, ";font-style:italic");
471            if(lineattr[x] & CUCUL_UNDERLINE)
472                cur += sprintf(cur, ";text-decoration:underline");
473            if(lineattr[x] & CUCUL_BLINK)
474                cur += sprintf(cur, ";text-decoration:blink");
475            cur += sprintf(cur, "\">");
476
477            for(len = 0;
478                x + len < cv->width && lineattr[x + len] == lineattr[x];
479                len++)
480            {
481                if(linechar[x + len] == CUCUL_MAGIC_FULLWIDTH)
482                    ;
483                else if(linechar[x + len] <= 0x00000020)
484                    cur += sprintf(cur, "&nbsp;");
485                else if(linechar[x + len] < 0x00000080)
486                    cur += sprintf(cur, "%c",
487                                   (unsigned char)linechar[x + len]);
488                else
489                    cur += sprintf(cur, "&#%i;",
490                                   (unsigned int)linechar[x + len]);
491            }
492            cur += sprintf(cur, "</span>");
493        }
494        /* New line */
495        cur += sprintf(cur, "<br />\n");
496    }
497
498    cur += sprintf(cur, "</div></body></html>\n");
499
500    /* Crop to really used size */
501    ex->size = strlen(ex->data) + 1;
502    ex->data = realloc(ex->data, ex->size);
503
504    return 0;
505}
506
507/* Export an HTML3 document. This function is way bigger than export_html(),
508 * but permits viewing in old browsers (or limited ones such as links). It
509 * will not work under gecko (mozilla rendering engine) unless you set a
510 * correct header. */
511static int export_html3(cucul_canvas_t *cv, cucul_buffer_t *ex)
512{
513    char *cur;
514    unsigned int x, y, len;
515
516    /* The HTML table markup: less than 1000 bytes
517     * A line: 10 chars for "<tr></tr>\n"
518     * A glyph: 40 chars for "<td bgcolor=#xxxxxx><font color=#xxxxxx>"
519     *          up to 36 chars for "<b><i><u><blink></blink></u></i></b>"
520     *          up to 9 chars for "&#xxxxxx;", far less for pure ASCII
521     *          12 chars for "</font></td>" */
522    ex->size = 1000 + cv->height * (10 + cv->width * (40 + 36 + 9 + 12));
523    ex->data = malloc(ex->size);
524
525    cur = ex->data;
526
527    /* Table */
528    cur += sprintf(cur, "<table cols='%d' cellpadding='0' cellspacing='0'>\n",
529                        cv->height);
530
531    for(y = 0; y < cv->height; y++)
532    {
533        uint32_t *lineattr = cv->attrs + y * cv->width;
534        uint32_t *linechar = cv->chars + y * cv->width;
535
536        cur += sprintf(cur, "<tr>");
537
538        for(x = 0; x < cv->width; x += len)
539        {
540            unsigned int i;
541
542            /* Use colspan option to factor cells with same attributes
543             * (see below) */
544            len = 1;
545            while(x + len < cv->width && lineattr[x + len] == lineattr[x])
546                len++;
547
548            cur += sprintf(cur, "<td bgcolor=#%.06lx", (unsigned long int)
549                           _cucul_attr_to_rgb24bg(lineattr[x]));
550
551            if(len > 1)
552                cur += sprintf(cur, " colspan=%d", len);
553
554            cur += sprintf(cur, "><font color=#%.06lx>", (unsigned long int)
555                           _cucul_attr_to_rgb24fg(lineattr[x]));
556
557            if(lineattr[x] & CUCUL_BOLD)
558                cur += sprintf(cur, "<b>");
559            if(lineattr[x] & CUCUL_ITALICS)
560                cur += sprintf(cur, "<i>");
561            if(lineattr[x] & CUCUL_UNDERLINE)
562                cur += sprintf(cur, "<u>");
563            if(lineattr[x] & CUCUL_BLINK)
564                cur += sprintf(cur, "<blink>");
565
566            for(i = 0; i < len; i++)
567            {
568                if(linechar[x + i] == CUCUL_MAGIC_FULLWIDTH)
569                    ;
570                else if(linechar[x + i] <= 0x00000020)
571                    cur += sprintf(cur, "&nbsp;");
572                else if(linechar[x + i] < 0x00000080)
573                    cur += sprintf(cur, "%c", (unsigned char)linechar[x + i]);
574                else
575                    cur += sprintf(cur, "&#%i;", (unsigned int)linechar[x + i]);
576            }
577
578            if(lineattr[x] & CUCUL_BLINK)
579                cur += sprintf(cur, "</blink>");
580            if(lineattr[x] & CUCUL_UNDERLINE)
581                cur += sprintf(cur, "</u>");
582            if(lineattr[x] & CUCUL_ITALICS)
583                cur += sprintf(cur, "</i>");
584            if(lineattr[x] & CUCUL_BOLD)
585                cur += sprintf(cur, "</b>");
586
587            cur += sprintf(cur, "</font></td>");
588        }
589        cur += sprintf(cur, "</tr>\n");
590    }
591
592    /* Footer */
593    cur += sprintf(cur, "</table>\n");
594
595    /* Crop to really used size */
596    ex->size = (uintptr_t)(cur - ex->data);
597    ex->data = realloc(ex->data, ex->size);
598
599    return 0;
600}
601
602/* Export a text file with IRC colours */
603static int export_irc(cucul_canvas_t *cv, cucul_buffer_t *ex)
604{
605    static uint8_t const palette[] =
606    {
607        1, 2, 3, 10, 5, 6, 7, 15, /* Dark */
608        14, 12, 9, 11, 4, 13, 8, 0, /* Light */
609    };
610
611    char *cur;
612    unsigned int x, y;
613
614    /* 14 bytes assumed for max length per pixel. Worst case scenario:
615     * ^Cxx,yy   6 bytes
616     * ^B^B      2 bytes
617     * ch        6 bytes
618     * 3 bytes for max length per line. Worst case scenario:
619     * <spc>     1 byte (for empty lines)
620     * \r\n      2 bytes
621     * In real life, the average bytes per pixel value will be around 5.
622     */
623
624    ex->size = 2 + cv->height * (3 + cv->width * 14);
625    ex->data = malloc(ex->size);
626
627    cur = ex->data;
628
629    for(y = 0; y < cv->height; y++)
630    {
631        uint32_t *lineattr = cv->attrs + y * cv->width;
632        uint32_t *linechar = cv->chars + y * cv->width;
633
634        uint8_t prevfg = 0x10;
635        uint8_t prevbg = 0x10;
636
637        for(x = 0; x < cv->width; x++)
638        {
639            uint32_t attr = lineattr[x];
640            uint8_t fg = palette[_cucul_attr_to_ansi4fg(attr)];
641            uint8_t bg = palette[_cucul_attr_to_ansi4bg(attr)];
642            uint32_t ch = linechar[x];
643
644            if(ch == CUCUL_MAGIC_FULLWIDTH)
645                continue;
646
647            if(((attr >> 4) & 0x3fff) == (CUCUL_DEFAULT | 0x40))
648                fg = 0x10;
649
650            if(((attr >> 18) & 0x3fff) == (CUCUL_TRANSPARENT | 0x40))
651                bg = 0x10;
652
653            /* TODO: optimise series of same fg / same bg
654             *       don't change fg value if ch == ' '
655             *       make sure the \x03,%d trick works everywhere */
656            if(bg != prevbg || fg != prevfg)
657            {
658                int need_escape = 0;
659
660                if(bg == 0x10)
661                {
662                    if(fg == 0x10)
663                        cur += sprintf(cur, "\x0f");
664                    else
665                    {
666                        if(prevbg == 0x10)
667                            cur += sprintf(cur, "\x03%d", fg);
668                        else
669                            cur += sprintf(cur, "\x0f\x03%d", fg);
670
671                        if(ch == (uint32_t)',')
672                            need_escape = 1;
673                    }
674                }
675                else
676                {
677                    if(fg == 0x10)
678                        cur += sprintf(cur, "\x0f\x03,%d", bg);
679                    else
680                        cur += sprintf(cur, "\x03%d,%d", fg, bg);
681                }
682
683                if(ch >= (uint32_t)'0' && ch <= (uint32_t)'9')
684                    need_escape = 1;
685
686                if(need_escape)
687                    cur += sprintf(cur, "\x02\x02");
688            }
689
690            cur += cucul_utf32_to_utf8(cur, ch);
691            prevfg = fg;
692            prevbg = bg;
693        }
694
695        /* TODO: do the same the day we optimise whole lines above */
696        if(!cv->width)
697            *cur++ = ' ';
698
699        *cur++ = '\r';
700        *cur++ = '\n';
701    }
702
703    /* Crop to really used size */
704    ex->size = (uintptr_t)(cur - ex->data);
705    ex->data = realloc(ex->data, ex->size);
706
707    return 0;
708}
709
710/* Export a PostScript document. */
711static int export_ps(cucul_canvas_t *cv, cucul_buffer_t *ex)
712{
713    static char const *ps_header =
714        "%!\n"
715        "%% libcaca PDF export\n"
716        "%%LanguageLevel: 2\n"
717        "%%Pages: 1\n"
718        "%%DocumentData: Clean7Bit\n"
719        "/csquare {\n"
720        "  newpath\n"
721        "  0 0 moveto\n"
722        "  0 1 rlineto\n"
723        "  1 0 rlineto\n"
724        "  0 -1 rlineto\n"
725        "  closepath\n"
726        "  setrgbcolor\n"
727        "  fill\n"
728        "} def\n"
729        "/S {\n"
730        "  Show\n"
731        "} bind def\n"
732        "/Courier-Bold findfont\n"
733        "8 scalefont\n"
734        "setfont\n"
735        "gsave\n"
736        "6 10 scale\n";
737
738    char *cur;
739    unsigned int x, y;
740
741    /* 200 is arbitrary but should be ok */
742    ex->size = strlen(ps_header) + 100 + cv->height * (32 + cv->width * 200);
743    ex->data = malloc(ex->size);
744
745    cur = ex->data;
746
747    /* Header */
748    cur += sprintf(cur, "%s", ps_header);
749    cur += sprintf(cur, "0 %d translate\n", cv->height);
750
751    /* Background, drawn using csquare macro defined in header */
752    for(y = cv->height; y--; )
753    {
754        uint32_t *lineattr = cv->attrs + y * cv->width;
755
756        for(x = 0; x < cv->width; x++)
757        {
758            uint8_t argb[8];
759            _cucul_attr_to_argb4(*lineattr++, argb);
760            cur += sprintf(cur, "1 0 translate\n %f %f %f csquare\n",
761                           (float)argb[1] * (1.0 / 0xf),
762                           (float)argb[2] * (1.0 / 0xf),
763                           (float)argb[3] * (1.0 / 0xf));
764        }
765
766        /* Return to beginning of the line, and jump to the next one */
767        cur += sprintf(cur, "-%d 1 translate\n", cv->width);
768    }
769
770    cur += sprintf(cur, "grestore\n"); /* Restore transformation matrix */
771    cur += sprintf(cur, "0 %d translate\n", cv->height*10);
772
773    for(y = cv->height; y--; )
774    {
775        uint32_t *lineattr = cv->attrs + (cv->height - y - 1) * cv->width;
776        uint32_t *linechar = cv->chars + (cv->height - y - 1) * cv->width;
777
778        for(x = 0; x < cv->width; x++)
779        {
780            uint8_t argb[8];
781            uint32_t ch = *linechar++;
782
783            _cucul_attr_to_argb4(*lineattr++, argb);
784
785            cur += sprintf(cur, "newpath\n");
786            cur += sprintf(cur, "%d %d moveto\n", (x + 1) * 6, y * 10 + 2);
787            cur += sprintf(cur, "%f %f %f setrgbcolor\n",
788                           (float)argb[5] * (1.0 / 0xf),
789                           (float)argb[6] * (1.0 / 0xf),
790                           (float)argb[7] * (1.0 / 0xf));
791
792            if(ch < 0x00000020)
793                cur += sprintf(cur, "(?) show\n");
794            else if(ch >= 0x00000080)
795                cur += sprintf(cur, "(?) show\n");
796            else switch((uint8_t)(ch & 0x7f))
797            {
798                case '\\':
799                case '(':
800                case ')':
801                    cur += sprintf(cur, "(\\%c) show\n", (unsigned char)ch);
802                    break;
803                default:
804                    cur += sprintf(cur, "(%c) show\n", (unsigned char)ch);
805                    break;
806            }
807        }
808    }
809
810    cur += sprintf(cur, "showpage\n");
811
812    /* Crop to really used size */
813    ex->size = (uintptr_t)(cur - ex->data);
814    ex->data = realloc(ex->data, ex->size);
815
816    return 0;
817}
818
819/* Export an SVG vector image */
820static int export_svg(cucul_canvas_t *cv, cucul_buffer_t *ex)
821{
822    static char const svg_header[] =
823        "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
824        "<svg width=\"%d\" height=\"%d\" viewBox=\"0 0 %d %d\""
825        " xmlns=\"http://www.w3.org/2000/svg\""
826        " xmlns:xlink=\"http://www.w3.org/1999/xlink\""
827        " xml:space=\"preserve\" version=\"1.1\"  baseProfile=\"full\">\n";
828
829    char *cur;
830    unsigned int x, y;
831
832    /* 200 is arbitrary but should be ok */
833    ex->size = strlen(svg_header) + 128 + cv->width * cv->height * 200;
834    ex->data = malloc(ex->size);
835
836    cur = ex->data;
837
838    /* Header */
839    cur += sprintf(cur, svg_header, cv->width * 6, cv->height * 10,
840                                    cv->width * 6, cv->height * 10);
841
842    cur += sprintf(cur, " <g id=\"mainlayer\" font-size=\"10\""
843                        " style=\"font-family: monospace\">\n");
844
845    /* Background */
846    for(y = 0; y < cv->height; y++)
847    {
848        uint32_t *lineattr = cv->attrs + y * cv->width;
849
850        for(x = 0; x < cv->width; x++)
851        {
852            cur += sprintf(cur, "<rect style=\"fill:#%.03x\" x=\"%d\" y=\"%d\""
853                                " width=\"6\" height=\"10\"/>\n",
854                                _cucul_attr_to_rgb12bg(*lineattr++),
855                                x * 6, y * 10);
856        }
857    }
858
859    /* Text */
860    for(y = 0; y < cv->height; y++)
861    {
862        uint32_t *lineattr = cv->attrs + y * cv->width;
863        uint32_t *linechar = cv->chars + y * cv->width;
864
865        for(x = 0; x < cv->width; x++)
866        {
867            uint32_t ch = *linechar++;
868
869            if(ch == ' ' || ch == CUCUL_MAGIC_FULLWIDTH)
870            {
871                lineattr++;
872                continue;
873            }
874
875            cur += sprintf(cur, "<text style=\"fill:#%.03x\" "
876                                "x=\"%d\" y=\"%d\">",
877                                _cucul_attr_to_rgb12fg(*lineattr++),
878                                x * 6, (y * 10) + 8);
879
880            if(ch < 0x00000020)
881                *cur++ = '?';
882            else if(ch > 0x0000007f)
883                cur += cucul_utf32_to_utf8(cur, ch);
884            else switch((uint8_t)ch)
885            {
886                case '>': cur += sprintf(cur, "&gt;"); break;
887                case '<': cur += sprintf(cur, "&lt;"); break;
888                case '&': cur += sprintf(cur, "&amp;"); break;
889                default: *cur++ = ch; break;
890            }
891            cur += sprintf(cur, "</text>\n");
892        }
893    }
894
895    cur += sprintf(cur, " </g>\n");
896    cur += sprintf(cur, "</svg>\n");
897
898    /* Crop to really used size */
899    ex->size = (uintptr_t)(cur - ex->data);
900    ex->data = realloc(ex->data, ex->size);
901
902    return 0;
903}
904
905/* Export a TGA image */
906static int export_tga(cucul_canvas_t *cv, cucul_buffer_t *ex)
907{
908    char const * const *fontlist;
909    char * cur;
910    cucul_font_t *f;
911    unsigned int i, w, h;
912
913    fontlist = cucul_get_font_list();
914    if(!fontlist[0])
915        return -1;
916
917    f = cucul_load_font(fontlist[0], 0);
918
919    w = cucul_get_canvas_width(cv) * cucul_get_font_width(f);
920    h = cucul_get_canvas_height(cv) * cucul_get_font_height(f);
921
922    ex->size = w * h * 4 + 18; /* 32 bpp + 18 bytes for the header */
923    ex->data = malloc(ex->size);
924
925    cur = ex->data;
926
927    /* ID Length */
928    cur += sprintf(cur, "%c", 0);
929    /* Color Map Type: no colormap */
930    cur += sprintf(cur, "%c", 0);
931    /* Image Type: uncompressed truecolor */
932    cur += sprintf(cur, "%c", 2);
933    /* Color Map Specification: no color map */
934    memset(cur, 0, 5); cur += 5;
935
936    /* Image Specification */
937    cur += sprintf(cur, "%c%c", 0, 0); /* X Origin */
938    cur += sprintf(cur, "%c%c", 0, 0); /* Y Origin */
939    cur += sprintf(cur, "%c%c", w & 0xff, w >> 8); /* Width */
940    cur += sprintf(cur, "%c%c", h & 0xff, h >> 8); /* Height */
941    cur += sprintf(cur, "%c", 32); /* Pixel Depth */
942    cur += sprintf(cur, "%c", 40); /* Image Descriptor */
943
944    /* Image ID: no ID */
945    /* Color Map Data: no colormap */
946
947    /* Image Data */
948    cucul_render_canvas(cv, f, cur, w, h, 4 * w);
949
950    /* Swap bytes. What a waste of time. */
951    for(i = 0; i < w * h * 4; i += 4)
952    {
953        char w;
954        w = cur[i]; cur[i] = cur[i + 3]; cur[i + 3] = w;
955        w = cur[i + 1]; cur[i + 1] = cur[i + 2]; cur[i + 2] = w;
956    }
957
958    cucul_free_font(f);
959
960    return 0;
961}
962
Note: See TracBrowser for help on using the repository browser.