html.c revision 913cc679
1913cc679Smrg/* $XTermId: html.c,v 1.6 2017/05/30 09:14:55 tom Exp $ */
22e4f8982Smrg
32e4f8982Smrg/*
42e4f8982Smrg * Copyright 2015 Jens Schweikhardt
52e4f8982Smrg *
62e4f8982Smrg * All Rights Reserved
72e4f8982Smrg *
82e4f8982Smrg * Permission is hereby granted, free of charge, to any person obtaining a copy
92e4f8982Smrg * of this software and associated documentation files (the "Software"), to
102e4f8982Smrg * deal in the Software without restriction, including without limitation the
112e4f8982Smrg * rights to use, copy, modify, merge, publish, distribute, sublicense,
122e4f8982Smrg * and/or sell copies of the Software, and to permit persons to whom the
132e4f8982Smrg * Software is furnished to do so, subject to the following conditions:
142e4f8982Smrg *
152e4f8982Smrg * The above copyright notice and this permission notice shall be included in
162e4f8982Smrg * all copies or substantial portions of the Software.
172e4f8982Smrg *
182e4f8982Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
192e4f8982Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
202e4f8982Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
212e4f8982Smrg * THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
222e4f8982Smrg * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
232e4f8982Smrg * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
242e4f8982Smrg * OTHER DEALINGS IN THE SOFTWARE.
252e4f8982Smrg *
262e4f8982Smrg * Except as contained in this notice, the name(s) of the above copyright
272e4f8982Smrg * holders shall not be used in advertising or otherwise to promote the sale,
282e4f8982Smrg * use or other dealings in this Software without prior written
292e4f8982Smrg * authorization.
302e4f8982Smrg */
312e4f8982Smrg
322e4f8982Smrg#include <xterm.h>
332e4f8982Smrg#include <version.h>
342e4f8982Smrg
352e4f8982Smrg#define NO_COLOR	((unsigned)-1)
362e4f8982Smrg#define RGBPCT(c) c.red / 655.35, c.green / 655.35, c.blue / 655.35
372e4f8982Smrg
382e4f8982Smrg#define DUMP_PREFIX "xterm"
392e4f8982Smrg#define DUMP_SUFFIX ".xhtml"
402e4f8982Smrg#define DEFAULTNAME DUMP_PREFIX DUMP_SUFFIX
412e4f8982Smrg
422e4f8982Smrg#ifdef VMS
432e4f8982Smrg#define VMS_HTML_FILE "sys$scratch:" DEFAULTNAME
442e4f8982Smrg#endif
452e4f8982Smrg
462e4f8982Smrgstatic void dumpHtmlHeader(XtermWidget xw, FILE *fp);
472e4f8982Smrgstatic void dumpHtmlScreen(XtermWidget xw, FILE *fp);
482e4f8982Smrgstatic void dumpHtmlLine(XtermWidget xw, int row, FILE *fp);
492e4f8982Smrgstatic void dumpHtmlFooter(XtermWidget, FILE *fp);
502e4f8982Smrgstatic void writeStyle(XtermWidget, FILE *fp);
512e4f8982Smrgchar *PixelToCSSColor(XtermWidget xw, Pixel p);
522e4f8982Smrg
532e4f8982Smrgvoid
542e4f8982SmrgxtermDumpHtml(XtermWidget xw)
552e4f8982Smrg{
562e4f8982Smrg    FILE *fp;
572e4f8982Smrg
582e4f8982Smrg    TRACE(("xtermDumpHtml...\n"));
592e4f8982Smrg#ifdef VMS
602e4f8982Smrg    fp = fopen(VMS_HTML_FILE, "wb");
612e4f8982Smrg#elif defined(HAVE_STRFTIME)
622e4f8982Smrg    {
632e4f8982Smrg	char fname[sizeof(DEFAULTNAME) + LEN_TIMESTAMP];
642e4f8982Smrg	time_t now;
652e4f8982Smrg	struct tm *ltm;
662e4f8982Smrg
672e4f8982Smrg	now = time((time_t *) 0);
682e4f8982Smrg	ltm = localtime(&now);
692e4f8982Smrg
702e4f8982Smrg	if (strftime(fname, sizeof fname,
712e4f8982Smrg		     DUMP_PREFIX FMT_TIMESTAMP DUMP_SUFFIX, ltm) > 0) {
722e4f8982Smrg	    fp = fopen(fname, "wb");
732e4f8982Smrg	} else {
742e4f8982Smrg	    fp = fopen(DEFAULTNAME, "wb");
752e4f8982Smrg	}
762e4f8982Smrg    }
772e4f8982Smrg#else
782e4f8982Smrg    fp = fopen(DEFAULTNAME, "wb");
792e4f8982Smrg#endif
802e4f8982Smrg
812e4f8982Smrg    if (fp != 0) {
822e4f8982Smrg	dumpHtmlHeader(xw, fp);
832e4f8982Smrg	dumpHtmlScreen(xw, fp);
842e4f8982Smrg	dumpHtmlFooter(xw, fp);
852e4f8982Smrg	fclose(fp);
862e4f8982Smrg    }
872e4f8982Smrg    TRACE(("...xtermDumpHtml done\n"));
882e4f8982Smrg}
892e4f8982Smrg
902e4f8982Smrgstatic void
912e4f8982SmrgdumpHtmlHeader(XtermWidget xw, FILE *fp)
922e4f8982Smrg{
932e4f8982Smrg    fputs("<?xml version='1.0' encoding='UTF-8'?>\n", fp);
942e4f8982Smrg    fputs("<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Strict//EN'\n", fp);
952e4f8982Smrg    fputs("  'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd'\n", fp);
962e4f8982Smrg    fputs("  [<!ENTITY s \"&#160;\">]>\n", fp);
972e4f8982Smrg    fputs("<html xmlns='http://www.w3.org/1999/xhtml' lang='en' xml:lang='en'>\n", fp);
982e4f8982Smrg    fputs(" <head>\n", fp);
992e4f8982Smrg    fprintf(fp, "  <meta name='generator' content='%s'/>\n", xtermVersion());
1002e4f8982Smrg    fputs("  <meta http-equiv='Content-Type' content='text/html; charset=UTF-8'/>\n", fp);
1012e4f8982Smrg    fputs("  <link rel='Stylesheet' type='text/css' href='xterm.css'/>\n", fp);
1022e4f8982Smrg    fputs("  <title>Xterm</title>\n", fp);
1032e4f8982Smrg    writeStyle(xw, fp);
1042e4f8982Smrg    fputs(" </head>\n", fp);
1052e4f8982Smrg    fputs(" <body>\n", fp);
1062e4f8982Smrg    fputs("  <div id='vt100'>\n", fp);
1072e4f8982Smrg    fputs("   <pre>", fp);
1082e4f8982Smrg}
1092e4f8982Smrg
1102e4f8982Smrgstatic void
1112e4f8982SmrgwriteStyle(XtermWidget xw, FILE *fp)
1122e4f8982Smrg{
1132e4f8982Smrg    TScreen *s = TScreenOf(xw);
1142e4f8982Smrg
1152e4f8982Smrg    fputs("  <style type='text/css'>\n", fp);
1162e4f8982Smrg    fputs("  body, pre { margin: 0 }\n", fp);
1172e4f8982Smrg    fputs("  #vt100 {\n", fp);
1182e4f8982Smrg    fputs("    float: left;\n", fp);
1192e4f8982Smrg    fprintf(fp, "    font-size: 12pt;\n");
1202e4f8982Smrg    fprintf(fp, "    border: %upx solid %s;\n", BorderWidth(xw),
1212e4f8982Smrg	    PixelToCSSColor(xw, BorderPixel(xw)));
1222e4f8982Smrg    fprintf(fp, "    padding: %dpx;\n", s->border);
1232e4f8982Smrg    fprintf(fp, "    background: %s\n", PixelToCSSColor(xw, xw->old_background));
1242e4f8982Smrg    fprintf(fp, "  }\n");
1252e4f8982Smrg    fputs("  .ul { text-decoration: underline }\n", fp);
1262e4f8982Smrg    fputs("  .bd { font-weight: bold }\n", fp);
1272e4f8982Smrg    fputs("  .it { font-style: italic }\n", fp);
1282e4f8982Smrg    fputs("  .st { text-decoration: line-through }\n", fp);
1292e4f8982Smrg    fputs("  .lu { text-decoration: line-through underline }\n", fp);
1302e4f8982Smrg    fputs("  </style>\n", fp);
1312e4f8982Smrg}
1322e4f8982Smrg
1332e4f8982Smrgstatic void
1342e4f8982SmrgdumpHtmlScreen(XtermWidget xw, FILE *fp)
1352e4f8982Smrg{
1362e4f8982Smrg    TScreen *s = TScreenOf(xw);
1372e4f8982Smrg    int row;
1382e4f8982Smrg
1392e4f8982Smrg    for (row = s->top_marg; row <= s->bot_marg; ++row) {
1402e4f8982Smrg	dumpHtmlLine(xw, row, fp);
1412e4f8982Smrg    }
1422e4f8982Smrg}
1432e4f8982Smrg
1442e4f8982Smrg/*
1452e4f8982Smrg * Note: initial and final space around values of class and style
1462e4f8982Smrg *       attribute are deliberate. They make it easier for XPath
1472e4f8982Smrg *       to test whether a particular name is among the attributes.
1482e4f8982Smrg *       It allows expressions such as
1492e4f8982Smrg *           [contains(@class, ' ul ')]
1502e4f8982Smrg *       instead of the unwieldy
1512e4f8982Smrg *           [contains(concat(' ', @class, ' '), ' ul ')]
1522e4f8982Smrg *       The ev and od (for even and odd rows) values
1532e4f8982Smrg *       avoid empty values when going back to old fg/bg.
1542e4f8982Smrg */
1552e4f8982Smrgstatic void
1562e4f8982SmrgdumpHtmlLine(XtermWidget xw, int row, FILE *fp)
1572e4f8982Smrg{
1582e4f8982Smrg    TScreen *s = TScreenOf(xw);
1592e4f8982Smrg    char attrs[2][sizeof
1602e4f8982Smrg		  "<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%)'>"];
1612e4f8982Smrg    int attr_index = 0;
1622e4f8982Smrg    char *attr = &attrs[attr_index][0];
1632e4f8982Smrg    int inx = ROW2INX(s, row);
1642e4f8982Smrg    LineData *ld = getLineData(s, inx);
1652e4f8982Smrg    int col;
1662e4f8982Smrg
1672e4f8982Smrg    if (ld == 0)
1682e4f8982Smrg	return;
1692e4f8982Smrg
1702e4f8982Smrg    for (col = 0; col < MaxCols(s); col++) {
1712e4f8982Smrg	XColor fgcolor, bgcolor;
1722e4f8982Smrg	IChar chr = ld->charData[col];
1732e4f8982Smrg	int slen = 0;
1742e4f8982Smrg
1752e4f8982Smrg	fgcolor.pixel = xw->old_foreground;
1762e4f8982Smrg	bgcolor.pixel = xw->old_background;
1772e4f8982Smrg#if OPT_ISO_COLORS
1782e4f8982Smrg	if (ld->attribs[col] & FG_COLOR) {
1792e4f8982Smrg	    unsigned fg = extract_fg(xw, ld->color[col], ld->attribs[col]);
1802e4f8982Smrg	    fgcolor.pixel = s->Acolors[fg].value;
1812e4f8982Smrg	}
1822e4f8982Smrg	if (ld->attribs[col] & BG_COLOR) {
1832e4f8982Smrg	    unsigned bg = extract_bg(xw, ld->color[col], ld->attribs[col]);
1842e4f8982Smrg	    bgcolor.pixel = s->Acolors[bg].value;
1852e4f8982Smrg	}
1862e4f8982Smrg#endif
1872e4f8982Smrg
1882e4f8982Smrg	XQueryColor(xw->screen.display, xw->core.colormap, &fgcolor);
1892e4f8982Smrg	XQueryColor(xw->screen.display, xw->core.colormap, &bgcolor);
1902e4f8982Smrg	if (ld->attribs[col] & BLINK) {
1912e4f8982Smrg	    /* White on red. */
1922e4f8982Smrg	    fgcolor.red = fgcolor.green = fgcolor.blue = 65535u;
1932e4f8982Smrg	    bgcolor.red = 65535u;
1942e4f8982Smrg	    bgcolor.green = bgcolor.blue = 0u;
1952e4f8982Smrg	}
1962e4f8982Smrg#if OPT_WIDE_ATTRS
1972e4f8982Smrg	if (ld->attribs[col] & ATR_FAINT) {
1982e4f8982Smrg	    fgcolor.red = (unsigned short) ((2 * fgcolor.red) / 3);
1992e4f8982Smrg	    fgcolor.green = (unsigned short) ((2 * fgcolor.green) / 3);
2002e4f8982Smrg	    fgcolor.blue = (unsigned short) ((2 * fgcolor.blue) / 3);
2012e4f8982Smrg	}
2022e4f8982Smrg#endif
2032e4f8982Smrg	if (ld->attribs[col] & INVERSE) {
2042e4f8982Smrg	    XColor tmp = fgcolor;
2052e4f8982Smrg	    fgcolor = bgcolor;
2062e4f8982Smrg	    bgcolor = tmp;
2072e4f8982Smrg	}
2082e4f8982Smrg
2092e4f8982Smrg	slen = sprintf(attr + slen, "<span class=' %s",
2102e4f8982Smrg		       ((row % 2) ? "ev" : "od"));
2112e4f8982Smrg	if (ld->attribs[col] & BOLD)
2122e4f8982Smrg	    slen += sprintf(attr + slen, " bd");
2132e4f8982Smrg#if OPT_WIDE_ATTRS
2142e4f8982Smrg	/*
2152e4f8982Smrg	 * Handle multiple text-decoration properties.
2162e4f8982Smrg	 * Treat ATR_DBL_UNDER the same as UNDERLINE since there is no
2172e4f8982Smrg	 * official proper CSS 2.2 way to use double underlining. (E.g.
2182e4f8982Smrg	 * using border-bottom does not work for successive lines and
2192e4f8982Smrg	 * "text-decoration: underline double" is a browser extension).
2202e4f8982Smrg	 */
2212e4f8982Smrg	if ((ld->attribs[col] & (UNDERLINE | ATR_DBL_UNDER)) &&
2222e4f8982Smrg	    (ld->attribs[col] & ATR_STRIKEOUT))
2232e4f8982Smrg	    slen += sprintf(attr + slen, " lu");
2242e4f8982Smrg	else if (ld->attribs[col] & (UNDERLINE | ATR_DBL_UNDER))
2252e4f8982Smrg	    slen += sprintf(attr + slen, " ul");
2262e4f8982Smrg	else if (ld->attribs[col] & ATR_STRIKEOUT)
2272e4f8982Smrg	    slen += sprintf(attr + slen, " st");
2282e4f8982Smrg
2292e4f8982Smrg	if (ld->attribs[col] & ATR_ITALIC)
2302e4f8982Smrg	    slen += sprintf(attr + slen, " it");
2312e4f8982Smrg#else
2322e4f8982Smrg	if (ld->attribs[col] & UNDERLINE)
2332e4f8982Smrg	    slen += sprintf(attr + slen, " ul");
2342e4f8982Smrg#endif
2352e4f8982Smrg	slen += sprintf(attr + slen,
2362e4f8982Smrg			" ' style='color: rgb(%.2f%%, %.2f%%, %.2f%%);",
2372e4f8982Smrg			RGBPCT(fgcolor));
238913cc679Smrg	(void) sprintf(attr + slen,
239913cc679Smrg		       " background: rgb(%.2f%%, %.2f%%, %.2f%%)'>", RGBPCT(bgcolor));
2402e4f8982Smrg	if (col == 0) {
2412e4f8982Smrg	    fputs(attr, fp);
2422e4f8982Smrg	    attr = &attrs[attr_index ^= 1][0];
2432e4f8982Smrg	} else {
2442e4f8982Smrg	    if (strcmp(&attrs[0][0], &attrs[1][0])) {
2452e4f8982Smrg		fputs("</span>", fp);
2462e4f8982Smrg		fputs(attr, fp);
2472e4f8982Smrg		attr = &attrs[attr_index ^= 1][0];
2482e4f8982Smrg	    }
2492e4f8982Smrg	}
2502e4f8982Smrg
2512e4f8982Smrg#if OPT_WIDE_CHARS
2522e4f8982Smrg	if (chr > 127) {
2532e4f8982Smrg	    /* Ignore hidden characters. */
2542e4f8982Smrg	    if (chr != HIDDEN_CHAR) {
2552e4f8982Smrg		Char temp[10];
2562e4f8982Smrg		*convertToUTF8(temp, chr) = 0;
2572e4f8982Smrg		fputs((char *) temp, fp);
2582e4f8982Smrg	    }
2592e4f8982Smrg	} else
2602e4f8982Smrg#endif
2612e4f8982Smrg	    switch (chr) {
2622e4f8982Smrg	    case 0:
2632e4f8982Smrg		/* This sometimes happens when resizing... ignore. */
2642e4f8982Smrg		break;
2652e4f8982Smrg	    case '&':
2662e4f8982Smrg		fputs("&amp;", fp);
2672e4f8982Smrg		break;
2682e4f8982Smrg	    case '<':
2692e4f8982Smrg		fputs("&lt;", fp);
2702e4f8982Smrg		break;
2712e4f8982Smrg	    case '>':
2722e4f8982Smrg		fputs("&gt;", fp);
2732e4f8982Smrg		break;
2742e4f8982Smrg	    case ' ':
2752e4f8982Smrg		fputs("&s;", fp);
2762e4f8982Smrg		break;
2772e4f8982Smrg	    default:
2782e4f8982Smrg		fputc((int) chr, fp);
2792e4f8982Smrg	    }
2802e4f8982Smrg    }
2812e4f8982Smrg    fprintf(fp, "</span>\n");
2822e4f8982Smrg}
2832e4f8982Smrg
2842e4f8982Smrgstatic void
2852e4f8982SmrgdumpHtmlFooter(XtermWidget xw GCC_UNUSED, FILE *fp)
2862e4f8982Smrg{
2872e4f8982Smrg    fputs("</pre>\n", fp);
2882e4f8982Smrg    fputs("  </div>\n", fp);
2892e4f8982Smrg    fputs(" </body>\n", fp);
2902e4f8982Smrg    fputs("</html>\n", fp);
2912e4f8982Smrg}
2922e4f8982Smrg
2932e4f8982Smrgchar *
2942e4f8982SmrgPixelToCSSColor(XtermWidget xw, Pixel p)
2952e4f8982Smrg{
2962e4f8982Smrg    static char rgb[sizeof "rgb(100.00%, 100.00%, 100.00%)"];
2972e4f8982Smrg    XColor c;
2982e4f8982Smrg
2992e4f8982Smrg    c.pixel = p;
3002e4f8982Smrg    XQueryColor(xw->screen.display, xw->core.colormap, &c);
3012e4f8982Smrg    sprintf(rgb, "rgb(%.2f%%, %.2f%%, %.2f%%)", RGBPCT(c));
3022e4f8982Smrg    return rgb;
3032e4f8982Smrg}
3042e4f8982Smrg/* vim: set ts=8 sw=4 et: */
305