html.c revision ad37e533
1/* $XTermId: html.c,v 1.21 2021/02/25 23:19:16 tom Exp $ */ 2 3/* 4 * Copyright 2018-2020,2021 Thomas E. Dickey 5 * Copyright 2015,2018 Jens Schweikhardt 6 * 7 * All Rights Reserved 8 * 9 * Permission is hereby granted, free of charge, to any person obtaining a copy 10 * of this software and associated documentation files (the "Software"), to 11 * deal in the Software without restriction, including without limitation the 12 * rights to use, copy, modify, merge, publish, distribute, sublicense, 13 * and/or sell copies of the Software, and to permit persons to whom the 14 * Software is furnished to do so, subject to the following conditions: 15 * 16 * The above copyright notice and this permission notice shall be included in 17 * all copies or substantial portions of the Software. 18 * 19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 22 * THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 23 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 24 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 25 * OTHER DEALINGS IN THE SOFTWARE. 26 * 27 * Except as contained in this notice, the name(s) of the above copyright 28 * holders shall not be used in advertising or otherwise to promote the sale, 29 * use or other dealings in this Software without prior written 30 * authorization. 31 */ 32 33#include <xterm.h> 34#include <version.h> 35 36#define MakeDim(color) \ 37 color = (unsigned short) ((2 * (unsigned) color) / 3) 38 39#define RGBPCT(c) \ 40 ((double)c.red / 655.35), \ 41 ((double)c.green / 655.35), \ 42 ((double)c.blue / 655.35) 43 44static void dumpHtmlHeader(XtermWidget xw, FILE *fp); 45static void dumpHtmlScreen(XtermWidget xw, FILE *fp); 46static void dumpHtmlLine(XtermWidget xw, int row, FILE *fp); 47static void dumpHtmlFooter(XtermWidget, FILE *fp); 48static void writeStyle(XtermWidget, FILE *fp); 49 50void 51xtermDumpHtml(XtermWidget xw) 52{ 53 char *saveLocale; 54 FILE *fp; 55 56 TRACE(("xtermDumpHtml...\n")); 57 saveLocale = xtermSetLocale(LC_NUMERIC, "C"); 58 fp = create_printfile(xw, ".xhtml"); 59 if (fp != 0) { 60 dumpHtmlHeader(xw, fp); 61 dumpHtmlScreen(xw, fp); 62 dumpHtmlFooter(xw, fp); 63 fclose(fp); 64 } 65 xtermResetLocale(LC_NUMERIC, saveLocale); 66 TRACE(("...xtermDumpHtml done\n")); 67} 68 69static void 70dumpHtmlHeader(XtermWidget xw, FILE *fp) 71{ 72 fputs("<?xml version='1.0' encoding='UTF-8'?>\n", fp); 73 fputs("<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Strict//EN'\n", fp); 74 fputs(" 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd'>\n", fp); 75 fputs("<html xmlns='http://www.w3.org/1999/xhtml' lang='en' xml:lang='en'>\n", fp); 76 fputs(" <head>\n", fp); 77 fprintf(fp, " <meta name='generator' content='%s'/>\n", xtermVersion()); 78 fputs(" <meta http-equiv='Content-Type' content='text/html; charset=UTF-8'/>\n", fp); 79 fputs(" <link rel='Stylesheet' type='text/css' href='xterm.css'/>\n", fp); 80 fputs(" <title>Xterm</title>\n", fp); 81 writeStyle(xw, fp); 82 fputs(" </head>\n", fp); 83 fputs(" <body>\n", fp); 84 fputs(" <div id='vt100'>\n", fp); 85 fputs(" <pre>", fp); 86 xevents(xw); 87} 88 89static void 90writeStyle(XtermWidget xw, FILE *fp) 91{ 92 TScreen *s = TScreenOf(xw); 93 94 fputs(" <style type='text/css'>\n", fp); 95 fputs(" body, pre { margin: 0 }\n", fp); 96 fputs(" #vt100 {\n", fp); 97 fputs(" float: left;\n", fp); 98 fprintf(fp, " font-size: 12pt;\n"); 99 fprintf(fp, " border: %upx solid %s;\n", BorderWidth(xw), 100 PixelToCSSColor(xw, BorderPixel(xw))); 101 fprintf(fp, " padding: %dpx;\n", s->border); 102 fprintf(fp, " background: %s\n", PixelToCSSColor(xw, xw->old_background)); 103 fprintf(fp, " }\n"); 104 fputs(" .ul { text-decoration: underline }\n", fp); 105 fputs(" .bd { font-weight: bold }\n", fp); 106 fputs(" .it { font-style: italic }\n", fp); 107 fputs(" .st { text-decoration: line-through }\n", fp); 108 fputs(" .lu { text-decoration: line-through underline }\n", fp); 109 fputs(" </style>\n", fp); 110 xevents(xw); 111} 112 113static void 114dumpHtmlScreen(XtermWidget xw, FILE *fp) 115{ 116 TScreen *s = TScreenOf(xw); 117 int row; 118 119 for (row = s->top_marg; row <= s->bot_marg; ++row) { 120 dumpHtmlLine(xw, row, fp); 121 } 122} 123 124/* 125 * Note: initial and final space around values of class and style 126 * attribute are deliberate. They make it easier for XPath 127 * to test whether a particular name is among the attributes. 128 * It allows expressions such as 129 * [contains(@class, ' ul ')] 130 * instead of the unwieldy 131 * [contains(concat(' ', @class, ' '), ' ul ')] 132 * The ev and od (for even and odd rows) values 133 * avoid empty values when going back to old fg/bg. 134 */ 135static void 136dumpHtmlLine(XtermWidget xw, int row, FILE *fp) 137{ 138 TScreen *s = TScreenOf(xw); 139 char attrs[2][sizeof 140 "<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%)'>"]; 141 int attr_index = 0; 142 char *attr = &attrs[attr_index][0]; 143 int inx = ROW2INX(s, row); 144 LineData *ld = getLineData(s, inx); 145 int col; 146 147 if (ld == 0) 148 return; 149 150 for (col = 0; col < MaxCols(s); col++) { 151 XColor fgcolor, bgcolor; 152 IChar chr = ld->charData[col]; 153 int slen = 0; 154 155 fgcolor.pixel = xw->old_foreground; 156 bgcolor.pixel = xw->old_background; 157#if OPT_ISO_COLORS 158 if (ld->attribs[col] & FG_COLOR) { 159 Pixel fg = extract_fg(xw, ld->color[col], ld->attribs[col]); 160#if OPT_DIRECT_COLOR 161 if (ld->attribs[col] & ATR_DIRECT_FG) 162 fgcolor.pixel = fg; 163 else 164#endif 165 fgcolor.pixel = s->Acolors[fg].value; 166 } 167 if (ld->attribs[col] & BG_COLOR) { 168 Pixel bg = extract_bg(xw, ld->color[col], ld->attribs[col]); 169#if OPT_DIRECT_COLOR 170 if (ld->attribs[col] & ATR_DIRECT_BG) 171 bgcolor.pixel = bg; 172 else 173#endif 174 bgcolor.pixel = s->Acolors[bg].value; 175 } 176#endif 177 178 XQueryColor(xw->screen.display, xw->core.colormap, &fgcolor); 179 XQueryColor(xw->screen.display, xw->core.colormap, &bgcolor); 180 xevents(xw); 181 182 if (ld->attribs[col] & BLINK) { 183 /* White on red. */ 184 fgcolor.red = fgcolor.green = fgcolor.blue = MAX_U_COLOR; 185 bgcolor.red = MAX_U_COLOR; 186 bgcolor.green = bgcolor.blue = 0u; 187 } 188#if OPT_WIDE_ATTRS 189 if (ld->attribs[col] & ATR_FAINT) { 190 MakeDim(fgcolor.red); 191 MakeDim(fgcolor.green); 192 MakeDim(fgcolor.blue); 193 } 194#endif 195 if (ld->attribs[col] & INVERSE) { 196 XColor tmp = fgcolor; 197 fgcolor = bgcolor; 198 bgcolor = tmp; 199 } 200 201 slen = sprintf(attr + slen, "<span class=' %s", 202 ((row % 2) ? "ev" : "od")); 203 if (ld->attribs[col] & BOLD) 204 slen += sprintf(attr + slen, " bd"); 205#if OPT_WIDE_ATTRS 206 /* 207 * Handle multiple text-decoration properties. 208 * Treat ATR_DBL_UNDER the same as UNDERLINE since there is no 209 * official proper CSS 2.2 way to use double underlining. (E.g. 210 * using border-bottom does not work for successive lines and 211 * "text-decoration: underline double" is a browser extension). 212 */ 213 if ((ld->attribs[col] & (UNDERLINE | ATR_DBL_UNDER)) && 214 (ld->attribs[col] & ATR_STRIKEOUT)) 215 slen += sprintf(attr + slen, " lu"); 216 else if (ld->attribs[col] & (UNDERLINE | ATR_DBL_UNDER)) 217 slen += sprintf(attr + slen, " ul"); 218 else if (ld->attribs[col] & ATR_STRIKEOUT) 219 slen += sprintf(attr + slen, " st"); 220 221 if (ld->attribs[col] & ATR_ITALIC) 222 slen += sprintf(attr + slen, " it"); 223#else 224 if (ld->attribs[col] & UNDERLINE) 225 slen += sprintf(attr + slen, " ul"); 226#endif 227 slen += sprintf(attr + slen, 228 " ' style='color: rgb(%.2f%%, %.2f%%, %.2f%%);", 229 RGBPCT(fgcolor)); 230 (void) sprintf(attr + slen, 231 " background: rgb(%.2f%%, %.2f%%, %.2f%%)'>", RGBPCT(bgcolor)); 232 if (col == 0) { 233 fputs(attr, fp); 234 attr = &attrs[attr_index ^= 1][0]; 235 } else { 236 if (strcmp(&attrs[0][0], &attrs[1][0])) { 237 fputs("</span>", fp); 238 fputs(attr, fp); 239 attr = &attrs[attr_index ^= 1][0]; 240 } 241 } 242 243#if OPT_WIDE_CHARS 244 if (chr > 127) { 245 /* Ignore hidden characters. */ 246 if (chr != HIDDEN_CHAR) { 247 Char temp[10]; 248 *convertToUTF8(temp, chr) = 0; 249 fputs((char *) temp, fp); 250 } 251 } else 252#endif 253 switch (chr) { 254 case 0: 255 fputc(' ', fp); 256 break; 257 case '&': 258 fputs("&", fp); 259 break; 260 case '<': 261 fputs("<", fp); 262 break; 263 case '>': 264 fputs(">", fp); 265 break; 266 case ' ': 267 fputs("\302\240", fp); 268 break; 269 default: 270 fputc((int) chr, fp); 271 } 272 xevents(xw); 273 } 274 fprintf(fp, "</span>\n"); 275 xevents(xw); 276} 277 278static void 279dumpHtmlFooter(XtermWidget xw, FILE *fp) 280{ 281 fputs("</pre>\n", fp); 282 fputs(" </div>\n", fp); 283 fputs(" </body>\n", fp); 284 fputs("</html>\n", fp); 285 xevents(xw); 286} 287 288char * 289PixelToCSSColor(XtermWidget xw, Pixel p) 290{ 291 static char rgb[sizeof "rgb(100.00%, 100.00%, 100.00%)"]; 292 XColor c; 293 294 c.pixel = p; 295 XQueryColor(xw->screen.display, xw->core.colormap, &c); 296 sprintf(rgb, "rgb(%.2f%%, %.2f%%, %.2f%%)", RGBPCT(c)); 297 return rgb; 298} 299