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

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