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

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