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

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