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 \" \">]>\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("&", fp); 2672e4f8982Smrg break; 2682e4f8982Smrg case '<': 2692e4f8982Smrg fputs("<", fp); 2702e4f8982Smrg break; 2712e4f8982Smrg case '>': 2722e4f8982Smrg fputs(">", 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