xenocara/app/xterm/svg.c

291 lines
8.2 KiB
C

/* $XTermId: svg.c,v 1.6 2016/05/22 19:09:17 tom Exp $ */
/*
* Copyright 2015,2016 Jens Schweikhardt
*
* All Rights Reserved
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Except as contained in this notice, the name(s) of the above copyright
* holders shall not be used in advertising or otherwise to promote the sale,
* use or other dealings in this Software without prior written
* authorization.
*/
#include <xterm.h>
#include <version.h>
#define NO_COLOR ((unsigned)-1)
#define RGBPCT(c) c.red / 655.35, c.green / 655.35, c.blue / 655.35
#define CELLW 10
#define CELLH 20
#define DUMP_PREFIX "xterm"
#define DUMP_SUFFIX ".svg"
#define DEFAULTNAME DUMP_PREFIX DUMP_SUFFIX
#ifdef VMS
#define VMS_SVG_FILE "sys$scratch:" DEFAULTNAME
#endif
extern char *PixelToCSSColor(XtermWidget xw, Pixel p); /* in html.c */
static void dumpSvgHeader(XtermWidget xw, FILE *fp);
static void dumpSvgScreen(XtermWidget xw, FILE *fp);
static void dumpSvgLine(XtermWidget xw, int row, FILE *fp);
static void dumpSvgFooter(XtermWidget, FILE *fp);
static int rows = 0;
static int cols = 0;
static Dimension bw = 0; /* borderWidth */
static int ib = 0; /* internalBorder */
void
xtermDumpSvg(XtermWidget xw)
{
FILE *fp;
TRACE(("xtermDumpSvg...\n"));
#ifdef VMS
fp = fopen(VMS_HTML_FILE, "wb");
#elif defined(HAVE_STRFTIME)
{
char fname[sizeof(DEFAULTNAME) + LEN_TIMESTAMP];
time_t now;
struct tm *ltm;
now = time((time_t *) 0);
ltm = localtime(&now);
if (strftime(fname, sizeof fname,
DUMP_PREFIX FMT_TIMESTAMP DUMP_SUFFIX, ltm) > 0) {
fp = fopen(fname, "wb");
} else {
fp = fopen(DEFAULTNAME, "wb");
}
}
#else
fp = fopen(DEFAULTNAME, "wb");
#endif
if (fp != 0) {
dumpSvgHeader(xw, fp);
dumpSvgScreen(xw, fp);
dumpSvgFooter(xw, fp);
fclose(fp);
}
TRACE(("...xtermDumpSvg done\n"));
}
static void
dumpSvgHeader(XtermWidget xw, FILE *fp)
{
TScreen *s = TScreenOf(xw);
rows = s->bot_marg - s->top_marg + 1;
cols = MaxCols(s);
bw = BorderWidth(xw);
ib = s->border;
fputs("<?xml version='1.0' encoding='UTF-8'?>\n", fp);
fputs("<!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN'\n", fp);
fputs(" 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'>\n", fp);
fputs("<svg xmlns='http://www.w3.org/2000/svg'\n", fp);
fputs(" version='1.1' baseProfile='full'\n", fp);
fprintf(fp, " viewBox='0 0 %d %d'>\n", 2 * (bw + ib) + cols * CELLW, 2 *
(bw + ib) +
rows * CELLH);
fprintf(fp, " <desc>%s Screen Dump</desc>\n", xtermVersion());
fprintf(fp,
" <g font-size='%.2f' font-family='monospace, monospace'>\n",
0.80 * CELLH);
}
static void
dumpSvgScreen(XtermWidget xw, FILE *fp)
{
TScreen *s = TScreenOf(xw);
int row;
fprintf(fp, " <rect x='0' y='0' width='%u' height='%u' fill='%s'/>\n",
cols * CELLW + 2 * (bw + ib), rows * CELLH + 2 * (bw + ib),
PixelToCSSColor(xw, xw->core.border_pixel));
fprintf(fp, " <rect x='%u' y='%u' width='%u' height='%u' fill='%s'/>\n",
bw, bw,
MaxCols(s) * CELLW + 2 * ib,
(unsigned) (rows * CELLH + 2 * ib),
PixelToCSSColor(xw, xw->old_background));
for (row = s->top_marg; row <= s->bot_marg; ++row) {
fprintf(fp, " <!-- Row %d -->\n", row);
dumpSvgLine(xw, row, fp);
}
}
static void
dumpSvgLine(XtermWidget xw, int row, FILE *fp)
{
TScreen *s = TScreenOf(xw);
int inx = ROW2INX(s, row);
LineData *ld = getLineData(s, inx);
int col, sal, i; /* sal: same attribute length */
if (ld == 0)
return;
for (col = 0; col < MaxCols(s); col += sal) {
XColor fgcolor, bgcolor;
/* Count how many consecutive cells have the same color & attributes. */
for (sal = 1; col + sal < MaxCols(s); ++sal) {
#if OPT_ISO_COLORS
if (ld->color[col] != ld->color[col + sal])
break;
#endif
if (ld->attribs[col] != ld->attribs[col + sal])
break;
}
fgcolor.pixel = xw->old_foreground;
bgcolor.pixel = xw->old_background;
#if OPT_ISO_COLORS
if (ld->attribs[col] & FG_COLOR) {
unsigned fg = extract_fg(xw, ld->color[col], ld->attribs[col]);
fgcolor.pixel = s->Acolors[fg].value;
}
if (ld->attribs[col] & BG_COLOR) {
unsigned bg = extract_bg(xw, ld->color[col], ld->attribs[col]);
bgcolor.pixel = s->Acolors[bg].value;
}
#endif
XQueryColor(xw->screen.display, xw->core.colormap, &fgcolor);
XQueryColor(xw->screen.display, xw->core.colormap, &bgcolor);
if (ld->attribs[col] & BLINK) {
/* White on red. */
fgcolor.red = fgcolor.green = fgcolor.blue = 65535u;
bgcolor.red = 65535u;
bgcolor.green = bgcolor.blue = 0u;
}
#if OPT_WIDE_ATTRS
if (ld->attribs[col] & ATR_FAINT) {
fgcolor.red = (unsigned short) ((2 * fgcolor.red) / 3);
fgcolor.green = (unsigned short) ((2 * fgcolor.green) / 3);
fgcolor.blue = (unsigned short) ((2 * fgcolor.blue) / 3);
}
#endif
if (ld->attribs[col] & INVERSE) {
XColor tmp = fgcolor;
fgcolor = bgcolor;
bgcolor = tmp;
}
/* Draw the background rectangle. */
fprintf(fp, " <rect x='%d' y='%d' ", bw + ib + col * CELLW, bw + ib
+ row * CELLH);
fprintf(fp, "height='%d' width='%d' ", CELLH, sal * CELLW);
fprintf(fp, "fill='rgb(%.2f%%, %.2f%%, %.2f%%)'/>\n", RGBPCT(bgcolor));
/* Now the <text>. */
/*
* SVG: Rendering text strings into a given rectangle is a challenge.
* Some renderers accept and do the right thing with the 'textLength'
* attribute, while others ignore it. The only predictable way to place
* (even monospaced) text properly is to do it character by character.
*/
fprintf(fp, " <g");
if (ld->attribs[col] & BOLD)
fprintf(fp, " font-weight='bold'");
#if OPT_WIDE_ATTRS
if (ld->attribs[col] & ATR_ITALIC)
fprintf(fp, " font-style='italic'");
#endif
fprintf(fp, " fill='rgb(%.2f%%, %.2f%%, %.2f%%)'>\n", RGBPCT(fgcolor));
for (i = 0; i < sal; ++i) {
IChar chr = ld->charData[col + i];
if (chr == ' ')
continue;
fprintf(fp, " <text x='%d' y='%d'>", bw + ib + (col + i) *
CELLW, bw + ib + row * CELLH + (CELLH * 3) / 4);
#if OPT_WIDE_CHARS
if (chr > 127) {
/* Ignore hidden characters. */
if (chr != HIDDEN_CHAR) {
Char temp[10];
*convertToUTF8(temp, chr) = 0;
fputs((char *) temp, fp);
}
} else
#endif
switch (chr) {
case 0:
/* This sometimes happens when resizing... ignore. */
break;
case '&':
fputs("&amp;", fp);
break;
case '<':
fputs("&lt;", fp);
break;
case '>':
fputs("&gt;", fp);
break;
default:
fputc((int) chr, fp);
}
fprintf(fp, "</text>\n");
}
fprintf(fp, " </g>\n");
#define HLINE(x) \
fprintf(fp, " <line x1='%d' y1='%d' " \
"x2='%d' y2='%d' " \
"stroke='rgb(%.2f%%, %.2f%%, %.2f%%)'/>\n", \
bw + ib + col * CELLW, bw + ib + row * CELLH + CELLH - (x), \
bw + ib + (col + sal) * CELLW, bw + ib + row * CELLH + CELLH - (x), \
RGBPCT(fgcolor))
/* Now the line attributes. */
if (ld->attribs[col] & UNDERLINE) {
HLINE(4);
}
#if OPT_WIDE_ATTRS
if (ld->attribs[col] & ATR_STRIKEOUT) {
HLINE(9);
}
if (ld->attribs[col] & ATR_DBL_UNDER) {
HLINE(3);
HLINE(1);
}
#endif
}
}
static void
dumpSvgFooter(XtermWidget xw GCC_UNUSED, FILE *fp)
{
fputs(" </g>\n</svg>\n", fp);
}
/* vim: set ts=8 sw=4 et: */