xenocara/app/xterm/html.c
2019-02-24 11:41:42 +00:00

320 lines
9.3 KiB
C

/* $XTermId: html.c,v 1.13 2018/07/02 18:30:45 tom Exp $ */
/*
* Copyright 2015,2018 Jens Schweikhardt
* Copyright 2018 Thomas E. Dickey
*
* 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 MakeDim(color) \
color = (unsigned short) ((2 * (unsigned) color) / 3)
#define RGBPCT(c) \
((double)c.red / 655.35), \
((double)c.green / 655.35), \
((double)c.blue / 655.35)
#define DUMP_PREFIX "xterm"
#define DUMP_SUFFIX ".xhtml"
#define DEFAULTNAME DUMP_PREFIX DUMP_SUFFIX
#ifdef VMS
#define VMS_HTML_FILE "sys$scratch:" DEFAULTNAME
#endif
static void dumpHtmlHeader(XtermWidget xw, FILE *fp);
static void dumpHtmlScreen(XtermWidget xw, FILE *fp);
static void dumpHtmlLine(XtermWidget xw, int row, FILE *fp);
static void dumpHtmlFooter(XtermWidget, FILE *fp);
static void writeStyle(XtermWidget, FILE *fp);
char *PixelToCSSColor(XtermWidget xw, Pixel p);
void
xtermDumpHtml(XtermWidget xw)
{
FILE *fp;
TRACE(("xtermDumpHtml...\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) {
dumpHtmlHeader(xw, fp);
dumpHtmlScreen(xw, fp);
dumpHtmlFooter(xw, fp);
fclose(fp);
}
TRACE(("...xtermDumpHtml done\n"));
}
static void
dumpHtmlHeader(XtermWidget xw, FILE *fp)
{
fputs("<?xml version='1.0' encoding='UTF-8'?>\n", fp);
fputs("<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Strict//EN'\n", fp);
fputs(" 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd'>\n", fp);
fputs("<html xmlns='http://www.w3.org/1999/xhtml' lang='en' xml:lang='en'>\n", fp);
fputs(" <head>\n", fp);
fprintf(fp, " <meta name='generator' content='%s'/>\n", xtermVersion());
fputs(" <meta http-equiv='Content-Type' content='text/html; charset=UTF-8'/>\n", fp);
fputs(" <link rel='Stylesheet' type='text/css' href='xterm.css'/>\n", fp);
fputs(" <title>Xterm</title>\n", fp);
writeStyle(xw, fp);
fputs(" </head>\n", fp);
fputs(" <body>\n", fp);
fputs(" <div id='vt100'>\n", fp);
fputs(" <pre>", fp);
}
static void
writeStyle(XtermWidget xw, FILE *fp)
{
TScreen *s = TScreenOf(xw);
fputs(" <style type='text/css'>\n", fp);
fputs(" body, pre { margin: 0 }\n", fp);
fputs(" #vt100 {\n", fp);
fputs(" float: left;\n", fp);
fprintf(fp, " font-size: 12pt;\n");
fprintf(fp, " border: %upx solid %s;\n", BorderWidth(xw),
PixelToCSSColor(xw, BorderPixel(xw)));
fprintf(fp, " padding: %dpx;\n", s->border);
fprintf(fp, " background: %s\n", PixelToCSSColor(xw, xw->old_background));
fprintf(fp, " }\n");
fputs(" .ul { text-decoration: underline }\n", fp);
fputs(" .bd { font-weight: bold }\n", fp);
fputs(" .it { font-style: italic }\n", fp);
fputs(" .st { text-decoration: line-through }\n", fp);
fputs(" .lu { text-decoration: line-through underline }\n", fp);
fputs(" </style>\n", fp);
}
static void
dumpHtmlScreen(XtermWidget xw, FILE *fp)
{
TScreen *s = TScreenOf(xw);
int row;
for (row = s->top_marg; row <= s->bot_marg; ++row) {
dumpHtmlLine(xw, row, fp);
}
}
/*
* Note: initial and final space around values of class and style
* attribute are deliberate. They make it easier for XPath
* to test whether a particular name is among the attributes.
* It allows expressions such as
* [contains(@class, ' ul ')]
* instead of the unwieldy
* [contains(concat(' ', @class, ' '), ' ul ')]
* The ev and od (for even and odd rows) values
* avoid empty values when going back to old fg/bg.
*/
static void
dumpHtmlLine(XtermWidget xw, int row, FILE *fp)
{
TScreen *s = TScreenOf(xw);
char attrs[2][sizeof
"<span class=' ev ul bd it st du ' style='color: rgb(100.00%, 100.00%, 100.00%); background: rgb(100.00%, 100.00%, 100.00%)'>"];
int attr_index = 0;
char *attr = &attrs[attr_index][0];
int inx = ROW2INX(s, row);
LineData *ld = getLineData(s, inx);
int col;
if (ld == 0)
return;
for (col = 0; col < MaxCols(s); col++) {
XColor fgcolor, bgcolor;
IChar chr = ld->charData[col];
int slen = 0;
fgcolor.pixel = xw->old_foreground;
bgcolor.pixel = xw->old_background;
#if OPT_ISO_COLORS
if (ld->attribs[col] & FG_COLOR) {
Pixel fg = extract_fg(xw, ld->color[col], ld->attribs[col]);
#if OPT_DIRECT_COLOR
if (ld->attribs[col] & ATR_DIRECT_FG)
fgcolor.pixel = fg;
else
#endif
fgcolor.pixel = s->Acolors[fg].value;
}
if (ld->attribs[col] & BG_COLOR) {
Pixel bg = extract_bg(xw, ld->color[col], ld->attribs[col]);
#if OPT_DIRECT_COLOR
if (ld->attribs[col] & ATR_DIRECT_BG)
bgcolor.pixel = bg;
else
#endif
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) {
MakeDim(fgcolor.red);
MakeDim(fgcolor.green);
MakeDim(fgcolor.blue);
}
#endif
if (ld->attribs[col] & INVERSE) {
XColor tmp = fgcolor;
fgcolor = bgcolor;
bgcolor = tmp;
}
slen = sprintf(attr + slen, "<span class=' %s",
((row % 2) ? "ev" : "od"));
if (ld->attribs[col] & BOLD)
slen += sprintf(attr + slen, " bd");
#if OPT_WIDE_ATTRS
/*
* Handle multiple text-decoration properties.
* Treat ATR_DBL_UNDER the same as UNDERLINE since there is no
* official proper CSS 2.2 way to use double underlining. (E.g.
* using border-bottom does not work for successive lines and
* "text-decoration: underline double" is a browser extension).
*/
if ((ld->attribs[col] & (UNDERLINE | ATR_DBL_UNDER)) &&
(ld->attribs[col] & ATR_STRIKEOUT))
slen += sprintf(attr + slen, " lu");
else if (ld->attribs[col] & (UNDERLINE | ATR_DBL_UNDER))
slen += sprintf(attr + slen, " ul");
else if (ld->attribs[col] & ATR_STRIKEOUT)
slen += sprintf(attr + slen, " st");
if (ld->attribs[col] & ATR_ITALIC)
slen += sprintf(attr + slen, " it");
#else
if (ld->attribs[col] & UNDERLINE)
slen += sprintf(attr + slen, " ul");
#endif
slen += sprintf(attr + slen,
" ' style='color: rgb(%.2f%%, %.2f%%, %.2f%%);",
RGBPCT(fgcolor));
(void) sprintf(attr + slen,
" background: rgb(%.2f%%, %.2f%%, %.2f%%)'>", RGBPCT(bgcolor));
if (col == 0) {
fputs(attr, fp);
attr = &attrs[attr_index ^= 1][0];
} else {
if (strcmp(&attrs[0][0], &attrs[1][0])) {
fputs("</span>", fp);
fputs(attr, fp);
attr = &attrs[attr_index ^= 1][0];
}
}
#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;
case ' ':
fputs("\302\240", fp);
break;
default:
fputc((int) chr, fp);
}
}
fprintf(fp, "</span>\n");
}
static void
dumpHtmlFooter(XtermWidget xw GCC_UNUSED, FILE *fp)
{
fputs("</pre>\n", fp);
fputs(" </div>\n", fp);
fputs(" </body>\n", fp);
fputs("</html>\n", fp);
}
char *
PixelToCSSColor(XtermWidget xw, Pixel p)
{
static char rgb[sizeof "rgb(100.00%, 100.00%, 100.00%)"];
XColor c;
c.pixel = p;
XQueryColor(xw->screen.display, xw->core.colormap, &c);
sprintf(rgb, "rgb(%.2f%%, %.2f%%, %.2f%%)", RGBPCT(c));
return rgb;
}
/* vim: set ts=8 sw=4 et: */