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