15104ee6eSmrg/* $XTermId: html.c,v 1.24 2024/12/01 20:27:00 tom Exp $ */ 22e4f8982Smrg 32e4f8982Smrg/* 45104ee6eSmrg * Copyright 2018-2021,2024 Thomas E. Dickey 5f2e35a3aSmrg * Copyright 2015,2018 Jens Schweikhardt 62e4f8982Smrg * 72e4f8982Smrg * All Rights Reserved 82e4f8982Smrg * 92e4f8982Smrg * Permission is hereby granted, free of charge, to any person obtaining a copy 102e4f8982Smrg * of this software and associated documentation files (the "Software"), to 112e4f8982Smrg * deal in the Software without restriction, including without limitation the 122e4f8982Smrg * rights to use, copy, modify, merge, publish, distribute, sublicense, 132e4f8982Smrg * and/or sell copies of the Software, and to permit persons to whom the 142e4f8982Smrg * Software is furnished to do so, subject to the following conditions: 152e4f8982Smrg * 162e4f8982Smrg * The above copyright notice and this permission notice shall be included in 172e4f8982Smrg * all copies or substantial portions of the Software. 182e4f8982Smrg * 192e4f8982Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 202e4f8982Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 212e4f8982Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 222e4f8982Smrg * THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 232e4f8982Smrg * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 242e4f8982Smrg * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 252e4f8982Smrg * OTHER DEALINGS IN THE SOFTWARE. 262e4f8982Smrg * 272e4f8982Smrg * Except as contained in this notice, the name(s) of the above copyright 282e4f8982Smrg * holders shall not be used in advertising or otherwise to promote the sale, 292e4f8982Smrg * use or other dealings in this Software without prior written 302e4f8982Smrg * authorization. 312e4f8982Smrg */ 322e4f8982Smrg 332e4f8982Smrg#include <xterm.h> 342e4f8982Smrg#include <version.h> 352e4f8982Smrg 36f2e35a3aSmrg#define MakeDim(color) \ 37f2e35a3aSmrg color = (unsigned short) ((2 * (unsigned) color) / 3) 382e4f8982Smrg 39f2e35a3aSmrg#define RGBPCT(c) \ 40f2e35a3aSmrg ((double)c.red / 655.35), \ 41f2e35a3aSmrg ((double)c.green / 655.35), \ 42f2e35a3aSmrg ((double)c.blue / 655.35) 432e4f8982Smrg 442e4f8982Smrgstatic void dumpHtmlHeader(XtermWidget xw, FILE *fp); 452e4f8982Smrgstatic void dumpHtmlScreen(XtermWidget xw, FILE *fp); 462e4f8982Smrgstatic void dumpHtmlLine(XtermWidget xw, int row, FILE *fp); 472e4f8982Smrgstatic void dumpHtmlFooter(XtermWidget, FILE *fp); 482e4f8982Smrgstatic void writeStyle(XtermWidget, FILE *fp); 492e4f8982Smrg 502e4f8982Smrgvoid 512e4f8982SmrgxtermDumpHtml(XtermWidget xw) 522e4f8982Smrg{ 53f2e35a3aSmrg char *saveLocale; 542e4f8982Smrg FILE *fp; 552e4f8982Smrg 562e4f8982Smrg TRACE(("xtermDumpHtml...\n")); 57f2e35a3aSmrg saveLocale = xtermSetLocale(LC_NUMERIC, "C"); 58f2e35a3aSmrg fp = create_printfile(xw, ".xhtml"); 595104ee6eSmrg if (fp != NULL) { 602e4f8982Smrg dumpHtmlHeader(xw, fp); 612e4f8982Smrg dumpHtmlScreen(xw, fp); 622e4f8982Smrg dumpHtmlFooter(xw, fp); 632e4f8982Smrg fclose(fp); 642e4f8982Smrg } 65f2e35a3aSmrg xtermResetLocale(LC_NUMERIC, saveLocale); 662e4f8982Smrg TRACE(("...xtermDumpHtml done\n")); 672e4f8982Smrg} 682e4f8982Smrg 692e4f8982Smrgstatic void 702e4f8982SmrgdumpHtmlHeader(XtermWidget xw, FILE *fp) 712e4f8982Smrg{ 722e4f8982Smrg fputs("<?xml version='1.0' encoding='UTF-8'?>\n", fp); 732e4f8982Smrg fputs("<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Strict//EN'\n", fp); 74f2e35a3aSmrg fputs(" 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd'>\n", fp); 752e4f8982Smrg fputs("<html xmlns='http://www.w3.org/1999/xhtml' lang='en' xml:lang='en'>\n", fp); 762e4f8982Smrg fputs(" <head>\n", fp); 772e4f8982Smrg fprintf(fp, " <meta name='generator' content='%s'/>\n", xtermVersion()); 782e4f8982Smrg fputs(" <meta http-equiv='Content-Type' content='text/html; charset=UTF-8'/>\n", fp); 792e4f8982Smrg fputs(" <link rel='Stylesheet' type='text/css' href='xterm.css'/>\n", fp); 802e4f8982Smrg fputs(" <title>Xterm</title>\n", fp); 812e4f8982Smrg writeStyle(xw, fp); 822e4f8982Smrg fputs(" </head>\n", fp); 832e4f8982Smrg fputs(" <body>\n", fp); 842e4f8982Smrg fputs(" <div id='vt100'>\n", fp); 852e4f8982Smrg fputs(" <pre>", fp); 86f2e35a3aSmrg xevents(xw); 872e4f8982Smrg} 882e4f8982Smrg 892e4f8982Smrgstatic void 902e4f8982SmrgwriteStyle(XtermWidget xw, FILE *fp) 912e4f8982Smrg{ 922e4f8982Smrg TScreen *s = TScreenOf(xw); 932e4f8982Smrg 942e4f8982Smrg fputs(" <style type='text/css'>\n", fp); 952e4f8982Smrg fputs(" body, pre { margin: 0 }\n", fp); 962e4f8982Smrg fputs(" #vt100 {\n", fp); 972e4f8982Smrg fputs(" float: left;\n", fp); 982e4f8982Smrg fprintf(fp, " font-size: 12pt;\n"); 992e4f8982Smrg fprintf(fp, " border: %upx solid %s;\n", BorderWidth(xw), 1002e4f8982Smrg PixelToCSSColor(xw, BorderPixel(xw))); 1012e4f8982Smrg fprintf(fp, " padding: %dpx;\n", s->border); 1022e4f8982Smrg fprintf(fp, " background: %s\n", PixelToCSSColor(xw, xw->old_background)); 1032e4f8982Smrg fprintf(fp, " }\n"); 1042e4f8982Smrg fputs(" .ul { text-decoration: underline }\n", fp); 1052e4f8982Smrg fputs(" .bd { font-weight: bold }\n", fp); 1062e4f8982Smrg fputs(" .it { font-style: italic }\n", fp); 1072e4f8982Smrg fputs(" .st { text-decoration: line-through }\n", fp); 1082e4f8982Smrg fputs(" .lu { text-decoration: line-through underline }\n", fp); 1092e4f8982Smrg fputs(" </style>\n", fp); 110f2e35a3aSmrg xevents(xw); 1112e4f8982Smrg} 1122e4f8982Smrg 1132e4f8982Smrgstatic void 1142e4f8982SmrgdumpHtmlScreen(XtermWidget xw, FILE *fp) 1152e4f8982Smrg{ 1162e4f8982Smrg TScreen *s = TScreenOf(xw); 1172e4f8982Smrg int row; 1182e4f8982Smrg 1192e4f8982Smrg for (row = s->top_marg; row <= s->bot_marg; ++row) { 1202e4f8982Smrg dumpHtmlLine(xw, row, fp); 1212e4f8982Smrg } 1222e4f8982Smrg} 1232e4f8982Smrg 1242e4f8982Smrg/* 1252e4f8982Smrg * Note: initial and final space around values of class and style 1262e4f8982Smrg * attribute are deliberate. They make it easier for XPath 1272e4f8982Smrg * to test whether a particular name is among the attributes. 1282e4f8982Smrg * It allows expressions such as 1292e4f8982Smrg * [contains(@class, ' ul ')] 1302e4f8982Smrg * instead of the unwieldy 1312e4f8982Smrg * [contains(concat(' ', @class, ' '), ' ul ')] 1322e4f8982Smrg * The ev and od (for even and odd rows) values 1332e4f8982Smrg * avoid empty values when going back to old fg/bg. 1342e4f8982Smrg */ 1352e4f8982Smrgstatic void 1362e4f8982SmrgdumpHtmlLine(XtermWidget xw, int row, FILE *fp) 1372e4f8982Smrg{ 1382e4f8982Smrg TScreen *s = TScreenOf(xw); 1392e4f8982Smrg char attrs[2][sizeof 1402e4f8982Smrg "<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%)'>"]; 1412e4f8982Smrg int attr_index = 0; 1422e4f8982Smrg char *attr = &attrs[attr_index][0]; 1432e4f8982Smrg int inx = ROW2INX(s, row); 1442e4f8982Smrg LineData *ld = getLineData(s, inx); 1452e4f8982Smrg int col; 1462e4f8982Smrg 1475104ee6eSmrg if (ld == NULL) 1482e4f8982Smrg return; 1492e4f8982Smrg 1502e4f8982Smrg for (col = 0; col < MaxCols(s); col++) { 1512e4f8982Smrg XColor fgcolor, bgcolor; 1522e4f8982Smrg IChar chr = ld->charData[col]; 1532e4f8982Smrg int slen = 0; 1542e4f8982Smrg 1552e4f8982Smrg fgcolor.pixel = xw->old_foreground; 1562e4f8982Smrg bgcolor.pixel = xw->old_background; 1572e4f8982Smrg#if OPT_ISO_COLORS 1582e4f8982Smrg if (ld->attribs[col] & FG_COLOR) { 159f2e35a3aSmrg Pixel fg = extract_fg(xw, ld->color[col], ld->attribs[col]); 160f2e35a3aSmrg#if OPT_DIRECT_COLOR 161f2e35a3aSmrg if (ld->attribs[col] & ATR_DIRECT_FG) 162f2e35a3aSmrg fgcolor.pixel = fg; 163f2e35a3aSmrg else 164f2e35a3aSmrg#endif 165f2e35a3aSmrg fgcolor.pixel = s->Acolors[fg].value; 1662e4f8982Smrg } 1672e4f8982Smrg if (ld->attribs[col] & BG_COLOR) { 168f2e35a3aSmrg Pixel bg = extract_bg(xw, ld->color[col], ld->attribs[col]); 169f2e35a3aSmrg#if OPT_DIRECT_COLOR 170f2e35a3aSmrg if (ld->attribs[col] & ATR_DIRECT_BG) 171f2e35a3aSmrg bgcolor.pixel = bg; 172f2e35a3aSmrg else 173f2e35a3aSmrg#endif 174f2e35a3aSmrg bgcolor.pixel = s->Acolors[bg].value; 1752e4f8982Smrg } 1762e4f8982Smrg#endif 1772e4f8982Smrg 178a5ae21e4Smrg (void) QueryOneColor(xw, &fgcolor); 179a5ae21e4Smrg (void) QueryOneColor(xw, &bgcolor); 180f2e35a3aSmrg xevents(xw); 181f2e35a3aSmrg 1822e4f8982Smrg if (ld->attribs[col] & BLINK) { 1832e4f8982Smrg /* White on red. */ 184ad37e533Smrg fgcolor.red = fgcolor.green = fgcolor.blue = MAX_U_COLOR; 185ad37e533Smrg bgcolor.red = MAX_U_COLOR; 1862e4f8982Smrg bgcolor.green = bgcolor.blue = 0u; 1872e4f8982Smrg } 1882e4f8982Smrg#if OPT_WIDE_ATTRS 1892e4f8982Smrg if (ld->attribs[col] & ATR_FAINT) { 190f2e35a3aSmrg MakeDim(fgcolor.red); 191f2e35a3aSmrg MakeDim(fgcolor.green); 192f2e35a3aSmrg MakeDim(fgcolor.blue); 1932e4f8982Smrg } 1942e4f8982Smrg#endif 1952e4f8982Smrg if (ld->attribs[col] & INVERSE) { 1962e4f8982Smrg XColor tmp = fgcolor; 1972e4f8982Smrg fgcolor = bgcolor; 1982e4f8982Smrg bgcolor = tmp; 1992e4f8982Smrg } 2002e4f8982Smrg 2012e4f8982Smrg slen = sprintf(attr + slen, "<span class=' %s", 2022e4f8982Smrg ((row % 2) ? "ev" : "od")); 2032e4f8982Smrg if (ld->attribs[col] & BOLD) 2042e4f8982Smrg slen += sprintf(attr + slen, " bd"); 2052e4f8982Smrg#if OPT_WIDE_ATTRS 2062e4f8982Smrg /* 2072e4f8982Smrg * Handle multiple text-decoration properties. 2082e4f8982Smrg * Treat ATR_DBL_UNDER the same as UNDERLINE since there is no 2092e4f8982Smrg * official proper CSS 2.2 way to use double underlining. (E.g. 2102e4f8982Smrg * using border-bottom does not work for successive lines and 2112e4f8982Smrg * "text-decoration: underline double" is a browser extension). 2122e4f8982Smrg */ 2132e4f8982Smrg if ((ld->attribs[col] & (UNDERLINE | ATR_DBL_UNDER)) && 2142e4f8982Smrg (ld->attribs[col] & ATR_STRIKEOUT)) 2152e4f8982Smrg slen += sprintf(attr + slen, " lu"); 2162e4f8982Smrg else if (ld->attribs[col] & (UNDERLINE | ATR_DBL_UNDER)) 2172e4f8982Smrg slen += sprintf(attr + slen, " ul"); 2182e4f8982Smrg else if (ld->attribs[col] & ATR_STRIKEOUT) 2192e4f8982Smrg slen += sprintf(attr + slen, " st"); 2202e4f8982Smrg 2212e4f8982Smrg if (ld->attribs[col] & ATR_ITALIC) 2222e4f8982Smrg slen += sprintf(attr + slen, " it"); 2232e4f8982Smrg#else 2242e4f8982Smrg if (ld->attribs[col] & UNDERLINE) 2252e4f8982Smrg slen += sprintf(attr + slen, " ul"); 2262e4f8982Smrg#endif 2272e4f8982Smrg slen += sprintf(attr + slen, 2282e4f8982Smrg " ' style='color: rgb(%.2f%%, %.2f%%, %.2f%%);", 2292e4f8982Smrg RGBPCT(fgcolor)); 230913cc679Smrg (void) sprintf(attr + slen, 231913cc679Smrg " background: rgb(%.2f%%, %.2f%%, %.2f%%)'>", RGBPCT(bgcolor)); 2322e4f8982Smrg if (col == 0) { 2332e4f8982Smrg fputs(attr, fp); 2342e4f8982Smrg attr = &attrs[attr_index ^= 1][0]; 2352e4f8982Smrg } else { 2362e4f8982Smrg if (strcmp(&attrs[0][0], &attrs[1][0])) { 2372e4f8982Smrg fputs("</span>", fp); 2382e4f8982Smrg fputs(attr, fp); 2392e4f8982Smrg attr = &attrs[attr_index ^= 1][0]; 2402e4f8982Smrg } 2412e4f8982Smrg } 2422e4f8982Smrg 2432e4f8982Smrg#if OPT_WIDE_CHARS 2442e4f8982Smrg if (chr > 127) { 2452e4f8982Smrg /* Ignore hidden characters. */ 2462e4f8982Smrg if (chr != HIDDEN_CHAR) { 2472e4f8982Smrg Char temp[10]; 2482e4f8982Smrg *convertToUTF8(temp, chr) = 0; 2492e4f8982Smrg fputs((char *) temp, fp); 2502e4f8982Smrg } 2512e4f8982Smrg } else 2522e4f8982Smrg#endif 2532e4f8982Smrg switch (chr) { 2542e4f8982Smrg case 0: 255f2e35a3aSmrg fputc(' ', fp); 2562e4f8982Smrg break; 2572e4f8982Smrg case '&': 2582e4f8982Smrg fputs("&", fp); 2592e4f8982Smrg break; 2602e4f8982Smrg case '<': 2612e4f8982Smrg fputs("<", fp); 2622e4f8982Smrg break; 2632e4f8982Smrg case '>': 2642e4f8982Smrg fputs(">", fp); 2652e4f8982Smrg break; 2662e4f8982Smrg case ' ': 267f2e35a3aSmrg fputs("\302\240", fp); 2682e4f8982Smrg break; 2692e4f8982Smrg default: 2702e4f8982Smrg fputc((int) chr, fp); 2712e4f8982Smrg } 272f2e35a3aSmrg xevents(xw); 2732e4f8982Smrg } 2742e4f8982Smrg fprintf(fp, "</span>\n"); 275f2e35a3aSmrg xevents(xw); 2762e4f8982Smrg} 2772e4f8982Smrg 2782e4f8982Smrgstatic void 279f2e35a3aSmrgdumpHtmlFooter(XtermWidget xw, FILE *fp) 2802e4f8982Smrg{ 2812e4f8982Smrg fputs("</pre>\n", fp); 2822e4f8982Smrg fputs(" </div>\n", fp); 2832e4f8982Smrg fputs(" </body>\n", fp); 2842e4f8982Smrg fputs("</html>\n", fp); 285f2e35a3aSmrg xevents(xw); 2862e4f8982Smrg} 2872e4f8982Smrg 2882e4f8982Smrgchar * 2892e4f8982SmrgPixelToCSSColor(XtermWidget xw, Pixel p) 2902e4f8982Smrg{ 2912e4f8982Smrg static char rgb[sizeof "rgb(100.00%, 100.00%, 100.00%)"]; 2922e4f8982Smrg XColor c; 2932e4f8982Smrg 294a5ae21e4Smrg (void) xw; 2952e4f8982Smrg c.pixel = p; 296a5ae21e4Smrg (void) QueryOneColor(xw, &c); 2972e4f8982Smrg sprintf(rgb, "rgb(%.2f%%, %.2f%%, %.2f%%)", RGBPCT(c)); 2982e4f8982Smrg return rgb; 2992e4f8982Smrg} 300