svg.c revision a5ae21e4
1/* $XTermId: svg.c,v 1.21 2021/09/19 18:22:57 tom Exp $ */ 2 3/* 4 * Copyright 2017-2020,2021 Thomas E. Dickey 5 * Copyright 2015-2016,2017 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 44#define CELLW 10 45#define CELLH 20 46 47static void dumpSvgHeader(XtermWidget xw, FILE *fp); 48static void dumpSvgScreen(XtermWidget xw, FILE *fp); 49static void dumpSvgLine(XtermWidget xw, int row, FILE *fp); 50static void dumpSvgFooter(XtermWidget, FILE *fp); 51 52static int rows = 0; 53static int cols = 0; 54static Dimension bw = 0; /* borderWidth */ 55static int ib = 0; /* internalBorder */ 56 57void 58xtermDumpSvg(XtermWidget xw) 59{ 60 char *saveLocale; 61 FILE *fp; 62 63 TRACE(("xtermDumpSvg...\n")); 64 saveLocale = xtermSetLocale(LC_NUMERIC, "C"); 65 fp = create_printfile(xw, ".svg"); 66 if (fp != 0) { 67 dumpSvgHeader(xw, fp); 68 dumpSvgScreen(xw, fp); 69 dumpSvgFooter(xw, fp); 70 fclose(fp); 71 } 72 xtermResetLocale(LC_NUMERIC, saveLocale); 73 TRACE(("...xtermDumpSvg done\n")); 74} 75 76static void 77dumpSvgHeader(XtermWidget xw, FILE *fp) 78{ 79 TScreen *s = TScreenOf(xw); 80 81 rows = s->bot_marg - s->top_marg + 1; 82 cols = MaxCols(s); 83 bw = BorderWidth(xw); 84 ib = s->border; 85 86 fputs("<?xml version='1.0' encoding='UTF-8'?>\n", fp); 87 fputs("<!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN'\n", fp); 88 fputs(" 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'>\n", fp); 89 fputs("<svg xmlns='http://www.w3.org/2000/svg'\n", fp); 90 fputs(" version='1.1' baseProfile='full'\n", fp); 91 fprintf(fp, " viewBox='0 0 %d %d'>\n", 2 * (bw + ib) + cols * CELLW, 2 * 92 (bw + ib) + 93 rows * CELLH); 94 fprintf(fp, " <desc>%s Screen Dump</desc>\n", xtermVersion()); 95 fprintf(fp, 96 " <g font-size='%.2f' font-family='monospace, monospace'>\n", 97 0.80 * CELLH); 98 xevents(xw); 99} 100 101static void 102dumpSvgScreen(XtermWidget xw, FILE *fp) 103{ 104 TScreen *s = TScreenOf(xw); 105 int row; 106 107 fprintf(fp, " <rect x='0' y='0' width='%u' height='%u' fill='%s'/>\n", 108 cols * CELLW + 2 * (bw + ib), rows * CELLH + 2 * (bw + ib), 109 PixelToCSSColor(xw, xw->core.border_pixel)); 110 fprintf(fp, " <rect x='%u' y='%u' width='%u' height='%u' fill='%s'/>\n", 111 bw, bw, 112 MaxCols(s) * CELLW + 2 * ib, 113 (unsigned) (rows * CELLH + 2 * ib), 114 PixelToCSSColor(xw, xw->old_background)); 115 116 for (row = s->top_marg; row <= s->bot_marg; ++row) { 117 fprintf(fp, " <!-- Row %d -->\n", row); 118 dumpSvgLine(xw, row, fp); 119 } 120} 121 122static void 123dumpSvgLine(XtermWidget xw, int row, FILE *fp) 124{ 125 TScreen *s = TScreenOf(xw); 126 int inx = ROW2INX(s, row); 127 LineData *ld = getLineData(s, inx); 128 int col, sal, i; /* sal: same attribute length */ 129 130 if (ld == 0) 131 return; 132 133 for (col = 0; col < MaxCols(s); col += sal) { 134 XColor fgcolor, bgcolor; 135 136 /* Count how many consecutive cells have the same color & attributes. */ 137 for (sal = 1; col + sal < MaxCols(s); ++sal) { 138#if OPT_ISO_COLORS 139 if (!isSameCColor(ld->color[col], ld->color[col + sal])) 140 break; 141#endif 142 if (ld->attribs[col] != ld->attribs[col + sal]) 143 break; 144 } 145 146 fgcolor.pixel = xw->old_foreground; 147 bgcolor.pixel = xw->old_background; 148#if OPT_ISO_COLORS 149 if (ld->attribs[col] & FG_COLOR) { 150 Pixel fg = extract_fg(xw, ld->color[col], ld->attribs[col]); 151#if OPT_DIRECT_COLOR 152 if (ld->attribs[col] & ATR_DIRECT_FG) 153 fgcolor.pixel = fg; 154 else 155#endif 156 fgcolor.pixel = s->Acolors[fg].value; 157 } 158 if (ld->attribs[col] & BG_COLOR) { 159 Pixel bg = extract_bg(xw, ld->color[col], ld->attribs[col]); 160#if OPT_DIRECT_COLOR 161 if (ld->attribs[col] & ATR_DIRECT_BG) 162 bgcolor.pixel = bg; 163 else 164#endif 165 bgcolor.pixel = s->Acolors[bg].value; 166 } 167#endif 168 169 (void) QueryOneColor(xw, &fgcolor); 170 (void) QueryOneColor(xw, &bgcolor); 171 xevents(xw); 172 173 if (ld->attribs[col] & BLINK) { 174 /* White on red. */ 175 fgcolor.red = fgcolor.green = fgcolor.blue = MAX_U_COLOR; 176 bgcolor.red = MAX_U_COLOR; 177 bgcolor.green = bgcolor.blue = 0u; 178 } 179#if OPT_WIDE_ATTRS 180 if (ld->attribs[col] & ATR_FAINT) { 181 MakeDim(fgcolor.red); 182 MakeDim(fgcolor.green); 183 MakeDim(fgcolor.blue); 184 } 185#endif 186 if (ld->attribs[col] & INVERSE) { 187 XColor tmp = fgcolor; 188 fgcolor = bgcolor; 189 bgcolor = tmp; 190 } 191 192 /* Draw the background rectangle. */ 193 fprintf(fp, " <rect x='%d' y='%d' ", bw + ib + col * CELLW, bw + ib 194 + row * CELLH); 195 fprintf(fp, "height='%d' width='%d' ", CELLH, sal * CELLW); 196 fprintf(fp, "fill='rgb(%.2f%%, %.2f%%, %.2f%%)'/>\n", RGBPCT(bgcolor)); 197 198 /* Now the <text>. */ 199 /* 200 * SVG: Rendering text strings into a given rectangle is a challenge. 201 * Some renderers accept and do the right thing with the 'textLength' 202 * attribute, while others ignore it. The only predictable way to place 203 * (even monospaced) text properly is to do it character by character. 204 */ 205 206 fprintf(fp, " <g"); 207 if (ld->attribs[col] & BOLD) 208 fprintf(fp, " font-weight='bold'"); 209#if OPT_WIDE_ATTRS 210 if (ld->attribs[col] & ATR_ITALIC) 211 fprintf(fp, " font-style='italic'"); 212#endif 213 fprintf(fp, " fill='rgb(%.2f%%, %.2f%%, %.2f%%)'>\n", RGBPCT(fgcolor)); 214 215 for (i = 0; i < sal; ++i) { 216 IChar chr = ld->charData[col + i]; 217 218 if (chr == ' ') 219 continue; 220 fprintf(fp, " <text x='%d' y='%d'>", bw + ib + (col + i) * 221 CELLW, bw + ib + row * CELLH + (CELLH * 3) / 4); 222#if OPT_WIDE_CHARS 223 if (chr > 127) { 224 /* Ignore hidden characters. */ 225 if (chr != HIDDEN_CHAR) { 226 Char temp[10]; 227 *convertToUTF8(temp, chr) = 0; 228 fputs((char *) temp, fp); 229 } 230 } else 231#endif 232 switch (chr) { 233 case 0: 234 fputc(' ', fp); 235 break; 236 case '&': 237 fputs("&", fp); 238 break; 239 case '<': 240 fputs("<", fp); 241 break; 242 case '>': 243 fputs(">", fp); 244 break; 245 default: 246 fputc((int) chr, fp); 247 } 248 fprintf(fp, "</text>\n"); 249 xevents(xw); 250 } 251 fprintf(fp, " </g>\n"); 252 xevents(xw); 253 254#define HLINE(x) \ 255 fprintf(fp, " <line x1='%d' y1='%d' " \ 256 "x2='%d' y2='%d' " \ 257 "stroke='rgb(%.2f%%, %.2f%%, %.2f%%)'/>\n", \ 258 bw + ib + col * CELLW, bw + ib + row * CELLH + CELLH - (x), \ 259 bw + ib + (col + sal) * CELLW, bw + ib + row * CELLH + CELLH - (x), \ 260 RGBPCT(fgcolor)) 261 262 /* Now the line attributes. */ 263 if (ld->attribs[col] & UNDERLINE) { 264 HLINE(4); 265 } 266#if OPT_WIDE_ATTRS 267 if (ld->attribs[col] & ATR_STRIKEOUT) { 268 HLINE(9); 269 } 270 if (ld->attribs[col] & ATR_DBL_UNDER) { 271 HLINE(3); 272 HLINE(1); 273 } 274#endif 275 xevents(xw); 276 } 277 xevents(xw); 278} 279 280static void 281dumpSvgFooter(XtermWidget xw, FILE *fp) 282{ 283 fputs(" </g>\n</svg>\n", fp); 284 xevents(xw); 285} 286