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

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