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

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