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

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