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

Last change on this file since 1332 was 1332, checked in by Sam Hocevar, 13 years ago
  • Get rid of _cucul_attr_to_ansi4[fb]g() and replace all calls with the now official cucul_attr_to_ansi_[fb]g().
  • 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 1332 2006-11-11 08:44:14Z 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 ansifg, ansibg, fg, bg;
292
293            if(ch == CUCUL_MAGIC_FULLWIDTH)
294                continue;
295
296            ansifg = cucul_attr_to_ansi_fg(attr);
297            ansibg = cucul_attr_to_ansi_bg(attr);
298
299            fg = ansifg < 0x10 ? palette[ansifg] : 0x10;
300            bg = ansibg < 0x10 ? palette[ansibg] : 0x10;
301
302            /* TODO: the [0 could be omitted in some cases */
303            if(fg != prevfg || bg != prevbg)
304            {
305                cur += sprintf(cur, "\033[0");
306
307                if(fg < 8)
308                    cur += sprintf(cur, ";3%d", fg);
309                else if(fg < 16)
310                    cur += sprintf(cur, ";1;3%d;9%d", fg - 8, fg - 8);
311
312                if(bg < 8)
313                    cur += sprintf(cur, ";4%d", bg);
314                else if(bg < 16)
315                    cur += sprintf(cur, ";5;4%d;10%d", bg - 8, bg - 8);
316
317                cur += sprintf(cur, "m");
318            }
319
320            cur += cucul_utf32_to_utf8(cur, ch);
321
322            prevfg = fg;
323            prevbg = bg;
324        }
325
326        if(prevfg != 0x10 || prevbg != 0x10)
327            cur += sprintf(cur, "\033[0m");
328
329        cur += sprintf(cur, cr ? "\r\n" : "\n");
330    }
331
332    /* Crop to really used size */
333    *bytes = (uintptr_t)(cur - data);
334    data = realloc(data, *bytes);
335
336    return data;
337}
338
339/* Generate ANSI representation of current canvas. */
340static void *export_ansi(cucul_canvas_t *cv, unsigned long int *bytes)
341{
342    static uint8_t const palette[] =
343    {
344        0,  4,  2,  6, 1,  5,  3,  7,
345        8, 12, 10, 14, 9, 13, 11, 15
346    };
347
348    char *data, *cur;
349    unsigned int x, y;
350
351    uint8_t prevfg = -1;
352    uint8_t prevbg = -1;
353
354    /* 16 bytes assumed for max length per pixel ('\e[5;1;3x;4ym' plus
355     * 1 byte for a CP437 character).
356     * Add height*9 to that (zeroes color at the end and jump to next line) */
357    *bytes = (cv->height * 9) + (cv->width * cv->height * 16);
358    cur = data = malloc(*bytes);
359
360    for(y = 0; y < cv->height; y++)
361    {
362        uint32_t *lineattr = cv->attrs + y * cv->width;
363        uint32_t *linechar = cv->chars + y * cv->width;
364
365        for(x = 0; x < cv->width; x++)
366        {
367            uint8_t ansifg = cucul_attr_to_ansi_fg(lineattr[x]);
368            uint8_t ansibg = cucul_attr_to_ansi_bg(lineattr[x]);
369            uint8_t fg = ansifg < 0x10 ? palette[ansifg] : CUCUL_LIGHTGRAY;
370            uint8_t bg = ansibg < 0x10 ? palette[ansibg] : CUCUL_BLACK;
371            uint32_t ch = linechar[x];
372
373            if(ch == CUCUL_MAGIC_FULLWIDTH)
374                ch = '?';
375
376            if(fg != prevfg || bg != prevbg)
377            {
378                cur += sprintf(cur, "\033[0;");
379
380                if(fg < 8)
381                    if(bg < 8)
382                        cur += sprintf(cur, "3%d;4%dm", fg, bg);
383                    else
384                        cur += sprintf(cur, "5;3%d;4%dm", fg, bg - 8);
385                else
386                    if(bg < 8)
387                        cur += sprintf(cur, "1;3%d;4%dm", fg - 8, bg);
388                    else
389                        cur += sprintf(cur, "5;1;3%d;4%dm", fg - 8, bg - 8);
390            }
391
392            *cur++ = cucul_utf32_to_cp437(ch);
393
394            prevfg = fg;
395            prevbg = bg;
396        }
397
398        if(cv->width == 80)
399        {
400            cur += sprintf(cur, "\033[s\n\033[u");
401        }
402        else
403        {
404            cur += sprintf(cur, "\033[0m\r\n");
405            prevfg = -1;
406            prevbg = -1;
407        }
408    }
409
410    /* Crop to really used size */
411    *bytes = (uintptr_t)(cur - data);
412    data = realloc(data, *bytes);
413
414    return data;
415}
416
417/* Generate HTML representation of current canvas. */
418static void *export_html(cucul_canvas_t *cv, unsigned long int *bytes)
419{
420    char *data, *cur;
421    unsigned int x, y, len;
422
423    /* The HTML header: less than 1000 bytes
424     * A line: 7 chars for "<br />\n"
425     * A glyph: 47 chars for "<span style="color:#xxx;background-color:#xxx">"
426     *          83 chars for ";font-weight..."
427     *          up to 9 chars for "&#xxxxxx;", far less for pure ASCII
428     *          7 chars for "</span>" */
429    *bytes = 1000 + cv->height * (7 + cv->width * (47 + 83 + 9 + 7));
430    cur = data = malloc(*bytes);
431
432    /* HTML header */
433    cur += sprintf(cur, "<html><head>\n");
434    cur += sprintf(cur, "<title>Generated by libcaca %s</title>\n", VERSION);
435    cur += sprintf(cur, "</head><body>\n");
436
437    cur += sprintf(cur, "<div cellpadding='0' cellspacing='0' style='%s'>\n",
438                        "font-family: monospace, fixed; font-weight: bold;");
439
440    for(y = 0; y < cv->height; y++)
441    {
442        uint32_t *lineattr = cv->attrs + y * cv->width;
443        uint32_t *linechar = cv->chars + y * cv->width;
444
445        for(x = 0; x < cv->width; x += len)
446        {
447            cur += sprintf(cur, "<span style=\"color:#%.03x;"
448                                "background-color:#%.03x",
449                                _cucul_attr_to_rgb12fg(lineattr[x]),
450                                _cucul_attr_to_rgb12bg(lineattr[x]));
451            if(lineattr[x] & CUCUL_BOLD)
452                cur += sprintf(cur, ";font-weight:bold");
453            if(lineattr[x] & CUCUL_ITALICS)
454                cur += sprintf(cur, ";font-style:italic");
455            if(lineattr[x] & CUCUL_UNDERLINE)
456                cur += sprintf(cur, ";text-decoration:underline");
457            if(lineattr[x] & CUCUL_BLINK)
458                cur += sprintf(cur, ";text-decoration:blink");
459            cur += sprintf(cur, "\">");
460
461            for(len = 0;
462                x + len < cv->width && lineattr[x + len] == lineattr[x];
463                len++)
464            {
465                if(linechar[x + len] == CUCUL_MAGIC_FULLWIDTH)
466                    ;
467                else if(linechar[x + len] <= 0x00000020)
468                    cur += sprintf(cur, "&nbsp;");
469                else if(linechar[x + len] < 0x00000080)
470                    cur += sprintf(cur, "%c",
471                                   (unsigned char)linechar[x + len]);
472                else
473                    cur += sprintf(cur, "&#%i;",
474                                   (unsigned int)linechar[x + len]);
475            }
476            cur += sprintf(cur, "</span>");
477        }
478        /* New line */
479        cur += sprintf(cur, "<br />\n");
480    }
481
482    cur += sprintf(cur, "</div></body></html>\n");
483
484    /* Crop to really used size */
485    *bytes = (uintptr_t)(cur - data);
486    data = realloc(data, *bytes);
487
488    return data;
489}
490
491/* Export an HTML3 document. This function is way bigger than export_html(),
492 * but permits viewing in old browsers (or limited ones such as links). It
493 * will not work under gecko (mozilla rendering engine) unless you set a
494 * correct header. */
495static void *export_html3(cucul_canvas_t *cv, unsigned long int *bytes)
496{
497    char *data, *cur;
498    unsigned int x, y, len;
499
500    /* The HTML table markup: less than 1000 bytes
501     * A line: 10 chars for "<tr></tr>\n"
502     * A glyph: 40 chars for "<td bgcolor=#xxxxxx><font color=#xxxxxx>"
503     *          up to 36 chars for "<b><i><u><blink></blink></u></i></b>"
504     *          up to 9 chars for "&#xxxxxx;", far less for pure ASCII
505     *          12 chars for "</font></td>" */
506    *bytes = 1000 + cv->height * (10 + cv->width * (40 + 36 + 9 + 12));
507    cur = data = malloc(*bytes);
508
509    /* Table */
510    cur += sprintf(cur, "<table cols='%d' cellpadding='0' cellspacing='0'>\n",
511                        cv->height);
512
513    for(y = 0; y < cv->height; y++)
514    {
515        uint32_t *lineattr = cv->attrs + y * cv->width;
516        uint32_t *linechar = cv->chars + y * cv->width;
517
518        cur += sprintf(cur, "<tr>");
519
520        for(x = 0; x < cv->width; x += len)
521        {
522            unsigned int i;
523
524            /* Use colspan option to factor cells with same attributes
525             * (see below) */
526            len = 1;
527            while(x + len < cv->width && lineattr[x + len] == lineattr[x])
528                len++;
529
530            cur += sprintf(cur, "<td bgcolor=#%.06lx", (unsigned long int)
531                           _cucul_attr_to_rgb24bg(lineattr[x]));
532
533            if(len > 1)
534                cur += sprintf(cur, " colspan=%d", len);
535
536            cur += sprintf(cur, "><font color=#%.06lx>", (unsigned long int)
537                           _cucul_attr_to_rgb24fg(lineattr[x]));
538
539            if(lineattr[x] & CUCUL_BOLD)
540                cur += sprintf(cur, "<b>");
541            if(lineattr[x] & CUCUL_ITALICS)
542                cur += sprintf(cur, "<i>");
543            if(lineattr[x] & CUCUL_UNDERLINE)
544                cur += sprintf(cur, "<u>");
545            if(lineattr[x] & CUCUL_BLINK)
546                cur += sprintf(cur, "<blink>");
547
548            for(i = 0; i < len; i++)
549            {
550                if(linechar[x + i] == CUCUL_MAGIC_FULLWIDTH)
551                    ;
552                else if(linechar[x + i] <= 0x00000020)
553                    cur += sprintf(cur, "&nbsp;");
554                else if(linechar[x + i] < 0x00000080)
555                    cur += sprintf(cur, "%c", (unsigned char)linechar[x + i]);
556                else
557                    cur += sprintf(cur, "&#%i;", (unsigned int)linechar[x + i]);
558            }
559
560            if(lineattr[x] & CUCUL_BLINK)
561                cur += sprintf(cur, "</blink>");
562            if(lineattr[x] & CUCUL_UNDERLINE)
563                cur += sprintf(cur, "</u>");
564            if(lineattr[x] & CUCUL_ITALICS)
565                cur += sprintf(cur, "</i>");
566            if(lineattr[x] & CUCUL_BOLD)
567                cur += sprintf(cur, "</b>");
568
569            cur += sprintf(cur, "</font></td>");
570        }
571        cur += sprintf(cur, "</tr>\n");
572    }
573
574    /* Footer */
575    cur += sprintf(cur, "</table>\n");
576
577    /* Crop to really used size */
578    *bytes = (uintptr_t)(cur - data);
579    data = realloc(data, *bytes);
580
581    return data;
582}
583
584/* Export a text file with IRC colours */
585static void *export_irc(cucul_canvas_t *cv, unsigned long int *bytes)
586{
587    static uint8_t const palette[] =
588    {
589        1, 2, 3, 10, 5, 6, 7, 15, /* Dark */
590        14, 12, 9, 11, 4, 13, 8, 0, /* Light */
591    };
592
593    char *data, *cur;
594    unsigned int x, y;
595
596    /* 14 bytes assumed for max length per pixel. Worst case scenario:
597     * ^Cxx,yy   6 bytes
598     * ^B^B      2 bytes
599     * ch        6 bytes
600     * 3 bytes for max length per line. Worst case scenario:
601     * <spc>     1 byte (for empty lines)
602     * \r\n      2 bytes
603     * In real life, the average bytes per pixel value will be around 5.
604     */
605
606    *bytes = 2 + cv->height * (3 + cv->width * 14);
607    cur = data = malloc(*bytes);
608
609    for(y = 0; y < cv->height; y++)
610    {
611        uint32_t *lineattr = cv->attrs + y * cv->width;
612        uint32_t *linechar = cv->chars + y * cv->width;
613
614        uint8_t prevfg = 0x10;
615        uint8_t prevbg = 0x10;
616
617        for(x = 0; x < cv->width; x++)
618        {
619            uint32_t attr = lineattr[x];
620            uint32_t ch = linechar[x];
621            uint8_t ansifg, ansibg, fg, bg;
622
623            if(ch == CUCUL_MAGIC_FULLWIDTH)
624                continue;
625
626            ansifg = cucul_attr_to_ansi_fg(attr);
627            ansibg = cucul_attr_to_ansi_bg(attr);
628
629            fg = ansifg < 0x10 ? palette[ansifg] : 0x10;
630            bg = ansibg < 0x10 ? palette[ansibg] : 0x10;
631
632            /* TODO: optimise series of same fg / same bg
633             *       don't change fg value if ch == ' '
634             *       make sure the \x03,%d trick works everywhere */
635            if(bg != prevbg || fg != prevfg)
636            {
637                int need_escape = 0;
638
639                if(bg == 0x10)
640                {
641                    if(fg == 0x10)
642                        cur += sprintf(cur, "\x0f");
643                    else
644                    {
645                        if(prevbg == 0x10)
646                            cur += sprintf(cur, "\x03%d", fg);
647                        else
648                            cur += sprintf(cur, "\x0f\x03%d", fg);
649
650                        if(ch == (uint32_t)',')
651                            need_escape = 1;
652                    }
653                }
654                else
655                {
656                    if(fg == 0x10)
657                        cur += sprintf(cur, "\x0f\x03,%d", bg);
658                    else
659                        cur += sprintf(cur, "\x03%d,%d", fg, bg);
660                }
661
662                if(ch >= (uint32_t)'0' && ch <= (uint32_t)'9')
663                    need_escape = 1;
664
665                if(need_escape)
666                    cur += sprintf(cur, "\x02\x02");
667            }
668
669            cur += cucul_utf32_to_utf8(cur, ch);
670            prevfg = fg;
671            prevbg = bg;
672        }
673
674        /* TODO: do the same the day we optimise whole lines above */
675        if(!cv->width)
676            *cur++ = ' ';
677
678        *cur++ = '\r';
679        *cur++ = '\n';
680    }
681
682    /* Crop to really used size */
683    *bytes = (uintptr_t)(cur - data);
684    data = realloc(data, *bytes);
685
686    return data;
687}
688
689/* Export a PostScript document. */
690static void *export_ps(cucul_canvas_t *cv, unsigned long int *bytes)
691{
692    static char const *ps_header =
693        "%!\n"
694        "%% libcaca PDF export\n"
695        "%%LanguageLevel: 2\n"
696        "%%Pages: 1\n"
697        "%%DocumentData: Clean7Bit\n"
698        "/csquare {\n"
699        "  newpath\n"
700        "  0 0 moveto\n"
701        "  0 1 rlineto\n"
702        "  1 0 rlineto\n"
703        "  0 -1 rlineto\n"
704        "  closepath\n"
705        "  setrgbcolor\n"
706        "  fill\n"
707        "} def\n"
708        "/S {\n"
709        "  Show\n"
710        "} bind def\n"
711        "/Courier-Bold findfont\n"
712        "8 scalefont\n"
713        "setfont\n"
714        "gsave\n"
715        "6 10 scale\n";
716
717    char *data, *cur;
718    unsigned int x, y;
719
720    /* 200 is arbitrary but should be ok */
721    *bytes = strlen(ps_header) + 100 + cv->height * (32 + cv->width * 200);
722    cur = data = malloc(*bytes);
723
724    /* Header */
725    cur += sprintf(cur, "%s", ps_header);
726    cur += sprintf(cur, "0 %d translate\n", cv->height);
727
728    /* Background, drawn using csquare macro defined in header */
729    for(y = cv->height; y--; )
730    {
731        uint32_t *lineattr = cv->attrs + y * cv->width;
732
733        for(x = 0; x < cv->width; x++)
734        {
735            uint8_t argb[8];
736            _cucul_attr_to_argb4(*lineattr++, argb);
737            cur += sprintf(cur, "1 0 translate\n %f %f %f csquare\n",
738                           (float)argb[1] * (1.0 / 0xf),
739                           (float)argb[2] * (1.0 / 0xf),
740                           (float)argb[3] * (1.0 / 0xf));
741        }
742
743        /* Return to beginning of the line, and jump to the next one */
744        cur += sprintf(cur, "-%d 1 translate\n", cv->width);
745    }
746
747    cur += sprintf(cur, "grestore\n"); /* Restore transformation matrix */
748    cur += sprintf(cur, "0 %d translate\n", cv->height*10);
749
750    for(y = cv->height; y--; )
751    {
752        uint32_t *lineattr = cv->attrs + (cv->height - y - 1) * cv->width;
753        uint32_t *linechar = cv->chars + (cv->height - y - 1) * cv->width;
754
755        for(x = 0; x < cv->width; x++)
756        {
757            uint8_t argb[8];
758            uint32_t ch = *linechar++;
759
760            _cucul_attr_to_argb4(*lineattr++, argb);
761
762            cur += sprintf(cur, "newpath\n");
763            cur += sprintf(cur, "%d %d moveto\n", (x + 1) * 6, y * 10 + 2);
764            cur += sprintf(cur, "%f %f %f setrgbcolor\n",
765                           (float)argb[5] * (1.0 / 0xf),
766                           (float)argb[6] * (1.0 / 0xf),
767                           (float)argb[7] * (1.0 / 0xf));
768
769            if(ch < 0x00000020)
770                cur += sprintf(cur, "(?) show\n");
771            else if(ch >= 0x00000080)
772                cur += sprintf(cur, "(?) show\n");
773            else switch((uint8_t)(ch & 0x7f))
774            {
775                case '\\':
776                case '(':
777                case ')':
778                    cur += sprintf(cur, "(\\%c) show\n", (unsigned char)ch);
779                    break;
780                default:
781                    cur += sprintf(cur, "(%c) show\n", (unsigned char)ch);
782                    break;
783            }
784        }
785    }
786
787    cur += sprintf(cur, "showpage\n");
788
789    /* Crop to really used size */
790    *bytes = (uintptr_t)(cur - data);
791    data = realloc(data, *bytes);
792
793    return data;
794}
795
796/* Export an SVG vector image */
797static void *export_svg(cucul_canvas_t *cv, unsigned long int *bytes)
798{
799    static char const svg_header[] =
800        "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
801        "<svg width=\"%d\" height=\"%d\" viewBox=\"0 0 %d %d\""
802        " xmlns=\"http://www.w3.org/2000/svg\""
803        " xmlns:xlink=\"http://www.w3.org/1999/xlink\""
804        " xml:space=\"preserve\" version=\"1.1\"  baseProfile=\"full\">\n";
805
806    char *data, *cur;
807    unsigned int x, y;
808
809    /* 200 is arbitrary but should be ok */
810    *bytes = strlen(svg_header) + 128 + cv->width * cv->height * 200;
811    cur = data = malloc(*bytes);
812
813    /* Header */
814    cur += sprintf(cur, svg_header, cv->width * 6, cv->height * 10,
815                                    cv->width * 6, cv->height * 10);
816
817    cur += sprintf(cur, " <g id=\"mainlayer\" font-size=\"10\""
818                        " style=\"font-family: monospace\">\n");
819
820    /* Background */
821    for(y = 0; y < cv->height; y++)
822    {
823        uint32_t *lineattr = cv->attrs + y * cv->width;
824
825        for(x = 0; x < cv->width; x++)
826        {
827            cur += sprintf(cur, "<rect style=\"fill:#%.03x\" x=\"%d\" y=\"%d\""
828                                " width=\"6\" height=\"10\"/>\n",
829                                _cucul_attr_to_rgb12bg(*lineattr++),
830                                x * 6, y * 10);
831        }
832    }
833
834    /* Text */
835    for(y = 0; y < cv->height; y++)
836    {
837        uint32_t *lineattr = cv->attrs + y * cv->width;
838        uint32_t *linechar = cv->chars + y * cv->width;
839
840        for(x = 0; x < cv->width; x++)
841        {
842            uint32_t ch = *linechar++;
843
844            if(ch == ' ' || ch == CUCUL_MAGIC_FULLWIDTH)
845            {
846                lineattr++;
847                continue;
848            }
849
850            cur += sprintf(cur, "<text style=\"fill:#%.03x\" "
851                                "x=\"%d\" y=\"%d\">",
852                                _cucul_attr_to_rgb12fg(*lineattr++),
853                                x * 6, (y * 10) + 8);
854
855            if(ch < 0x00000020)
856                *cur++ = '?';
857            else if(ch > 0x0000007f)
858                cur += cucul_utf32_to_utf8(cur, ch);
859            else switch((uint8_t)ch)
860            {
861                case '>': cur += sprintf(cur, "&gt;"); break;
862                case '<': cur += sprintf(cur, "&lt;"); break;
863                case '&': cur += sprintf(cur, "&amp;"); break;
864                default: *cur++ = ch; break;
865            }
866            cur += sprintf(cur, "</text>\n");
867        }
868    }
869
870    cur += sprintf(cur, " </g>\n");
871    cur += sprintf(cur, "</svg>\n");
872
873    /* Crop to really used size */
874    *bytes = (uintptr_t)(cur - data);
875    data = realloc(data, *bytes);
876
877    return data;
878}
879
880/* Export a TGA image */
881static void *export_tga(cucul_canvas_t *cv, unsigned long int *bytes)
882{
883    char const * const *fontlist;
884    char *data, *cur;
885    cucul_font_t *f;
886    unsigned int i, w, h;
887
888    fontlist = cucul_get_font_list();
889    if(!fontlist[0])
890    {
891#if defined(HAVE_ERRNO_H)
892        errno = EINVAL;
893#endif
894        return NULL;
895    }
896
897    f = cucul_load_font(fontlist[0], 0);
898
899    w = cucul_get_canvas_width(cv) * cucul_get_font_width(f);
900    h = cucul_get_canvas_height(cv) * cucul_get_font_height(f);
901
902    *bytes = w * h * 4 + 18; /* 32 bpp + 18 bytes for the header */
903    cur = data = malloc(*bytes);
904
905    /* ID Length */
906    cur += sprintf(cur, "%c", 0);
907    /* Color Map Type: no colormap */
908    cur += sprintf(cur, "%c", 0);
909    /* Image Type: uncompressed truecolor */
910    cur += sprintf(cur, "%c", 2);
911    /* Color Map Specification: no color map */
912    memset(cur, 0, 5); cur += 5;
913
914    /* Image Specification */
915    cur += sprintf(cur, "%c%c", 0, 0); /* X Origin */
916    cur += sprintf(cur, "%c%c", 0, 0); /* Y Origin */
917    cur += sprintf(cur, "%c%c", w & 0xff, w >> 8); /* Width */
918    cur += sprintf(cur, "%c%c", h & 0xff, h >> 8); /* Height */
919    cur += sprintf(cur, "%c", 32); /* Pixel Depth */
920    cur += sprintf(cur, "%c", 40); /* Image Descriptor */
921
922    /* Image ID: no ID */
923    /* Color Map Data: no colormap */
924
925    /* Image Data */
926    cucul_render_canvas(cv, f, cur, w, h, 4 * w);
927
928    /* Swap bytes. What a waste of time. */
929    for(i = 0; i < w * h * 4; i += 4)
930    {
931        char w;
932        w = cur[i]; cur[i] = cur[i + 3]; cur[i + 3] = w;
933        w = cur[i + 1]; cur[i + 1] = cur[i + 2]; cur[i + 2] = w;
934    }
935
936    cucul_free_font(f);
937
938    return data;
939}
940
Note: See TracBrowser for help on using the repository browser.