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

Last change on this file since 2043 was 2043, checked in by Sam Hocevar, 13 years ago
  • Added as many "const" qualifiers as possible to the public API.
  • 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 2043 2007-11-24 11:08:21Z 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", VERSION);
382    cur += sprintf(cur, "</head><body>\n");
383
384    cur += sprintf(cur, "<div cellpadding='0' cellspacing='0' style='%s'>\n",
385                        "font-family: monospace, fixed; font-weight: bold;");
386
387    for(y = 0; y < cv->height; y++)
388    {
389        uint32_t *lineattr = cv->attrs + y * cv->width;
390        uint32_t *linechar = cv->chars + y * cv->width;
391
392        for(x = 0; x < cv->width; x += len)
393        {
394            cur += sprintf(cur, "<span style=\"");
395            if(cucul_attr_to_ansi_fg(lineattr[x]) < 0x10)
396                cur += sprintf(cur, ";color:#%.03x",
397                               _cucul_attr_to_rgb12fg(lineattr[x]));
398            if(cucul_attr_to_ansi_bg(lineattr[x]) < 0x10)
399                cur += sprintf(cur, ";background-color:#%.03x",
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 %lu bytes, realloc %lu",
436          (unsigned long int)*bytes, (unsigned long int)(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 const *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, needfont;
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");
483
484            if(cucul_attr_to_ansi_fg(lineattr[x]) < 0x10)
485                cur += sprintf(cur, " bgcolor=#%.06lx", (unsigned long int)
486                               _cucul_attr_to_rgb24bg(lineattr[x]));
487
488            if(len > 1)
489                cur += sprintf(cur, " colspan=%d", len);
490
491            cur += sprintf(cur, ">");
492
493            needfont = cucul_attr_to_ansi_bg(lineattr[x]) < 0x10;
494
495            if(needfont)
496                cur += sprintf(cur, "<font color=#%.06lx>", (unsigned long int)
497                               _cucul_attr_to_rgb24fg(lineattr[x]));
498
499            if(lineattr[x] & CUCUL_BOLD)
500                cur += sprintf(cur, "<b>");
501            if(lineattr[x] & CUCUL_ITALICS)
502                cur += sprintf(cur, "<i>");
503            if(lineattr[x] & CUCUL_UNDERLINE)
504                cur += sprintf(cur, "<u>");
505            if(lineattr[x] & CUCUL_BLINK)
506                cur += sprintf(cur, "<blink>");
507
508            for(i = 0; i < len; i++)
509            {
510                if(linechar[x + i] == CUCUL_MAGIC_FULLWIDTH)
511                    ;
512                else if(linechar[x + i] <= 0x00000020)
513                    cur += sprintf(cur, "&nbsp;");
514                else if(linechar[x + i] < 0x00000080)
515                    cur += sprintf(cur, "%c", (unsigned char)linechar[x + i]);
516                else
517                    cur += sprintf(cur, "&#%i;", (unsigned int)linechar[x + i]);
518            }
519
520            if(lineattr[x] & CUCUL_BLINK)
521                cur += sprintf(cur, "</blink>");
522            if(lineattr[x] & CUCUL_UNDERLINE)
523                cur += sprintf(cur, "</u>");
524            if(lineattr[x] & CUCUL_ITALICS)
525                cur += sprintf(cur, "</i>");
526            if(lineattr[x] & CUCUL_BOLD)
527                cur += sprintf(cur, "</b>");
528
529            if(needfont)
530                cur += sprintf(cur, "</font>");
531            cur += sprintf(cur, "</td>");
532        }
533        cur += sprintf(cur, "</tr>\n");
534    }
535
536    /* Footer */
537    cur += sprintf(cur, "</table>\n");
538
539    /* Crop to really used size */
540    debug("html3 export: alloc %lu bytes, realloc %lu",
541          (unsigned long int)*bytes, (unsigned long int)(cur - data));
542    *bytes = (uintptr_t)(cur - data);
543    data = realloc(data, *bytes);
544
545    return data;
546}
547
548/* Export a text file with IRC colours */
549static void *export_irc(cucul_canvas_t const *cv, unsigned long int *bytes)
550{
551    static uint8_t const palette[] =
552    {
553        1, 2, 3, 10, 5, 6, 7, 15, /* Dark */
554        14, 12, 9, 11, 4, 13, 8, 0, /* Light */
555    };
556
557    char *data, *cur;
558    unsigned int x, y;
559
560    /* 14 bytes assumed for max length per pixel. Worst case scenario:
561     * ^Cxx,yy   6 bytes
562     * ^B^B      2 bytes
563     * ch        6 bytes
564     * 3 bytes for max length per line. Worst case scenario:
565     * <spc>     1 byte (for empty lines)
566     * \r\n      2 bytes
567     * In real life, the average bytes per pixel value will be around 5.
568     */
569
570    *bytes = 2 + cv->height * (3 + cv->width * 14);
571    cur = data = malloc(*bytes);
572
573    for(y = 0; y < cv->height; y++)
574    {
575        uint32_t *lineattr = cv->attrs + y * cv->width;
576        uint32_t *linechar = cv->chars + y * cv->width;
577
578        uint8_t prevfg = 0x10;
579        uint8_t prevbg = 0x10;
580
581        for(x = 0; x < cv->width; x++)
582        {
583            uint32_t attr = lineattr[x];
584            uint32_t ch = linechar[x];
585            uint8_t ansifg, ansibg, fg, bg;
586
587            if(ch == CUCUL_MAGIC_FULLWIDTH)
588                continue;
589
590            ansifg = cucul_attr_to_ansi_fg(attr);
591            ansibg = cucul_attr_to_ansi_bg(attr);
592
593            fg = ansifg < 0x10 ? palette[ansifg] : 0x10;
594            bg = ansibg < 0x10 ? palette[ansibg] : 0x10;
595
596            /* TODO: optimise series of same fg / same bg
597             *       don't change fg value if ch == ' '
598             *       make sure the \x03,%d trick works everywhere */
599            if(bg != prevbg || fg != prevfg)
600            {
601                int need_escape = 0;
602
603                if(bg == 0x10)
604                {
605                    if(fg == 0x10)
606                        cur += sprintf(cur, "\x0f");
607                    else
608                    {
609                        if(prevbg == 0x10)
610                            cur += sprintf(cur, "\x03%d", fg);
611                        else
612                            cur += sprintf(cur, "\x0f\x03%d", fg);
613
614                        if(ch == (uint32_t)',')
615                            need_escape = 1;
616                    }
617                }
618                else
619                {
620                    if(fg == 0x10)
621                        cur += sprintf(cur, "\x0f\x03,%d", bg);
622                    else
623                        cur += sprintf(cur, "\x03%d,%d", fg, bg);
624                }
625
626                if(ch >= (uint32_t)'0' && ch <= (uint32_t)'9')
627                    need_escape = 1;
628
629                if(need_escape)
630                    cur += sprintf(cur, "\x02\x02");
631            }
632
633            cur += cucul_utf32_to_utf8(cur, ch);
634            prevfg = fg;
635            prevbg = bg;
636        }
637
638        /* TODO: do the same the day we optimise whole lines above */
639        if(!cv->width)
640            *cur++ = ' ';
641
642        *cur++ = '\r';
643        *cur++ = '\n';
644    }
645
646    /* Crop to really used size */
647    debug("IRC export: alloc %lu bytes, realloc %lu",
648          (unsigned long int)*bytes, (unsigned long int)(cur - data));
649    *bytes = (uintptr_t)(cur - data);
650    data = realloc(data, *bytes);
651
652    return data;
653}
654
655/* Export a PostScript document. */
656static void *export_ps(cucul_canvas_t const *cv, unsigned long int *bytes)
657{
658    static char const *ps_header =
659        "%!\n"
660        "%% libcaca PDF export\n"
661        "%%LanguageLevel: 2\n"
662        "%%Pages: 1\n"
663        "%%DocumentData: Clean7Bit\n"
664        "/csquare {\n"
665        "  newpath\n"
666        "  0 0 moveto\n"
667        "  0 1 rlineto\n"
668        "  1 0 rlineto\n"
669        "  0 -1 rlineto\n"
670        "  closepath\n"
671        "  setrgbcolor\n"
672        "  fill\n"
673        "} def\n"
674        "/S {\n"
675        "  Show\n"
676        "} bind def\n"
677        "/Courier-Bold findfont\n"
678        "8 scalefont\n"
679        "setfont\n"
680        "gsave\n"
681        "6 10 scale\n";
682
683    char *data, *cur;
684    unsigned int x, y;
685
686    /* 200 is arbitrary but should be ok */
687    *bytes = strlen(ps_header) + 100 + cv->height * (32 + cv->width * 200);
688    cur = data = malloc(*bytes);
689
690    /* Header */
691    cur += sprintf(cur, "%s", ps_header);
692    cur += sprintf(cur, "0 %d translate\n", cv->height);
693
694    /* Background, drawn using csquare macro defined in header */
695    for(y = cv->height; y--; )
696    {
697        uint32_t *lineattr = cv->attrs + y * cv->width;
698
699        for(x = 0; x < cv->width; x++)
700        {
701            uint8_t argb[8];
702            _cucul_attr_to_argb4(*lineattr++, argb);
703            cur += sprintf(cur, "1 0 translate\n %f %f %f csquare\n",
704                           (float)argb[1] * (1.0 / 0xf),
705                           (float)argb[2] * (1.0 / 0xf),
706                           (float)argb[3] * (1.0 / 0xf));
707        }
708
709        /* Return to beginning of the line, and jump to the next one */
710        cur += sprintf(cur, "-%d 1 translate\n", cv->width);
711    }
712
713    cur += sprintf(cur, "grestore\n"); /* Restore transformation matrix */
714    cur += sprintf(cur, "0 %d translate\n", cv->height*10);
715
716    for(y = cv->height; y--; )
717    {
718        uint32_t *lineattr = cv->attrs + (cv->height - y - 1) * cv->width;
719        uint32_t *linechar = cv->chars + (cv->height - y - 1) * cv->width;
720
721        for(x = 0; x < cv->width; x++)
722        {
723            uint8_t argb[8];
724            uint32_t ch = *linechar++;
725
726            _cucul_attr_to_argb4(*lineattr++, argb);
727
728            cur += sprintf(cur, "newpath\n");
729            cur += sprintf(cur, "%d %d moveto\n", (x + 1) * 6, y * 10 + 2);
730            cur += sprintf(cur, "%f %f %f setrgbcolor\n",
731                           (float)argb[5] * (1.0 / 0xf),
732                           (float)argb[6] * (1.0 / 0xf),
733                           (float)argb[7] * (1.0 / 0xf));
734
735            if(ch < 0x00000020)
736                cur += sprintf(cur, "(?) show\n");
737            else if(ch >= 0x00000080)
738                cur += sprintf(cur, "(?) show\n");
739            else switch((uint8_t)(ch & 0x7f))
740            {
741                case '\\':
742                case '(':
743                case ')':
744                    cur += sprintf(cur, "(\\%c) show\n", (unsigned char)ch);
745                    break;
746                default:
747                    cur += sprintf(cur, "(%c) show\n", (unsigned char)ch);
748                    break;
749            }
750        }
751    }
752
753    cur += sprintf(cur, "showpage\n");
754
755    /* Crop to really used size */
756    debug("PS export: alloc %lu bytes, realloc %lu",
757          (unsigned long int)*bytes, (unsigned long int)(cur - data));
758    *bytes = (uintptr_t)(cur - data);
759    data = realloc(data, *bytes);
760
761    return data;
762}
763
764/* Export an SVG vector image */
765static void *export_svg(cucul_canvas_t const *cv, unsigned long int *bytes)
766{
767    static char const svg_header[] =
768        "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
769        "<svg width=\"%d\" height=\"%d\" viewBox=\"0 0 %d %d\""
770        " xmlns=\"http://www.w3.org/2000/svg\""
771        " xmlns:xlink=\"http://www.w3.org/1999/xlink\""
772        " xml:space=\"preserve\" version=\"1.1\"  baseProfile=\"full\">\n";
773
774    char *data, *cur;
775    unsigned int x, y;
776
777    /* 200 is arbitrary but should be ok */
778    *bytes = strlen(svg_header) + 128 + cv->width * cv->height * 200;
779    cur = data = malloc(*bytes);
780
781    /* Header */
782    cur += sprintf(cur, svg_header, cv->width * 6, cv->height * 10,
783                                    cv->width * 6, cv->height * 10);
784
785    cur += sprintf(cur, " <g id=\"mainlayer\" font-size=\"10\""
786                        " style=\"font-family: monospace\">\n");
787
788    /* Background */
789    for(y = 0; y < cv->height; y++)
790    {
791        uint32_t *lineattr = cv->attrs + y * cv->width;
792
793        for(x = 0; x < cv->width; x++)
794        {
795            cur += sprintf(cur, "<rect style=\"fill:#%.03x\" x=\"%d\" y=\"%d\""
796                                " width=\"6\" height=\"10\"/>\n",
797                                _cucul_attr_to_rgb12bg(*lineattr++),
798                                x * 6, y * 10);
799        }
800    }
801
802    /* Text */
803    for(y = 0; y < cv->height; y++)
804    {
805        uint32_t *lineattr = cv->attrs + y * cv->width;
806        uint32_t *linechar = cv->chars + y * cv->width;
807
808        for(x = 0; x < cv->width; x++)
809        {
810            uint32_t ch = *linechar++;
811
812            if(ch == ' ' || ch == CUCUL_MAGIC_FULLWIDTH)
813            {
814                lineattr++;
815                continue;
816            }
817
818            cur += sprintf(cur, "<text style=\"fill:#%.03x\" "
819                                "x=\"%d\" y=\"%d\">",
820                                _cucul_attr_to_rgb12fg(*lineattr++),
821                                x * 6, (y * 10) + 8);
822
823            if(ch < 0x00000020)
824                *cur++ = '?';
825            else if(ch > 0x0000007f)
826                cur += cucul_utf32_to_utf8(cur, ch);
827            else switch((uint8_t)ch)
828            {
829                case '>': cur += sprintf(cur, "&gt;"); break;
830                case '<': cur += sprintf(cur, "&lt;"); break;
831                case '&': cur += sprintf(cur, "&amp;"); break;
832                default: *cur++ = ch; break;
833            }
834            cur += sprintf(cur, "</text>\n");
835        }
836    }
837
838    cur += sprintf(cur, " </g>\n");
839    cur += sprintf(cur, "</svg>\n");
840
841    /* Crop to really used size */
842    debug("SVG export: alloc %lu bytes, realloc %lu",
843          (unsigned long int)*bytes, (unsigned long int)(cur - data));
844    *bytes = (uintptr_t)(cur - data);
845    data = realloc(data, *bytes);
846
847    return data;
848}
849
850/* Export a TGA image */
851static void *export_tga(cucul_canvas_t const *cv, unsigned long int *bytes)
852{
853    char const * const *fontlist;
854    char *data, *cur;
855    cucul_font_t *f;
856    unsigned int i, w, h;
857
858    fontlist = cucul_get_font_list();
859    if(!fontlist[0])
860    {
861        seterrno(EINVAL);
862        return NULL;
863    }
864
865    f = cucul_load_font(fontlist[0], 0);
866
867    w = cucul_get_canvas_width(cv) * cucul_get_font_width(f);
868    h = cucul_get_canvas_height(cv) * cucul_get_font_height(f);
869
870    *bytes = w * h * 4 + 18; /* 32 bpp + 18 bytes for the header */
871    cur = data = malloc(*bytes);
872
873    /* ID Length */
874    cur += sprintf(cur, "%c", 0);
875    /* Color Map Type: no colormap */
876    cur += sprintf(cur, "%c", 0);
877    /* Image Type: uncompressed truecolor */
878    cur += sprintf(cur, "%c", 2);
879    /* Color Map Specification: no color map */
880    memset(cur, 0, 5); cur += 5;
881
882    /* Image Specification */
883    cur += sprintf(cur, "%c%c", 0, 0); /* X Origin */
884    cur += sprintf(cur, "%c%c", 0, 0); /* Y Origin */
885    cur += sprintf(cur, "%c%c", w & 0xff, w >> 8); /* Width */
886    cur += sprintf(cur, "%c%c", h & 0xff, h >> 8); /* Height */
887    cur += sprintf(cur, "%c", 32); /* Pixel Depth */
888    cur += sprintf(cur, "%c", 40); /* Image Descriptor */
889
890    /* Image ID: no ID */
891    /* Color Map Data: no colormap */
892
893    /* Image Data */
894    cucul_render_canvas(cv, f, cur, w, h, 4 * w);
895
896    /* Swap bytes. What a waste of time. */
897    for(i = 0; i < w * h * 4; i += 4)
898    {
899        char c;
900        c = cur[i]; cur[i] = cur[i + 3]; cur[i + 3] = c;
901        c = cur[i + 1]; cur[i + 1] = cur[i + 2]; cur[i + 2] = c;
902    }
903
904    cucul_free_font(f);
905
906    return data;
907}
908
Note: See TracBrowser for help on using the repository browser.