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

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