Skip to content

Instantly share code, notes, and snippets.

@kLiHz
Last active November 1, 2021 11:59
Show Gist options
  • Save kLiHz/99cd65c5f2901da5e4e58b37cdced7e5 to your computer and use it in GitHub Desktop.
Save kLiHz/99cd65c5f2901da5e4e58b37cdced7e5 to your computer and use it in GitHub Desktop.
Terminal Output to SVG

Terminal Output (in HTML <p>s) to SVG

The reason I built this was that I was unable to find a tool to convert Windows Terminal's output to SVG (not a animated SVG, just a screenshot).

Since one can copy Windows Terminal's output as HTML code, I tried to find some tools to convert HTML to SVG. But I can't find any either.

By the way, Asciinema can record CLI output in its own format, and it can save its recordings as 'raw' format, in which I guess are some escape sequences. Maybe one can choose to convert those sequence to SVG.

Update: I found something that might be seful:

Anyway, I wrote a stupid Python script, which may convert the HTML code copied from Windows Terminal, to a SVG format.

One may install Beautiful Soup and lxml to run the script.

The input should be like:

<p>First Line</p>
<p>Second line <span style="color: rgb(122, 122, 122);">with some styled text in it.</span> </p>

And the output should be like:

<svg width="720" height="120" xmlns="http://www.w3.org/2000/svg">
	<style>text { font-family: Consolas,"Courier New",monospace; font-weight: 400; font-size: 20px; fill: #CCCCCC; }</style>
	<rect x="0" y="0" width="720" height="120" fill="#222A35"/>
	<text x="25" y="50">First Line</text>
	<text x="25" y="75">Second line <tspan style="fill: rgb(122, 122, 122);">with some styled text in it.</tspan> </text>
</svg>

out.svg

<p>First Line</p>
<p>Second line <span style="color: rgb(122, 122, 122);">with some styled text in it.</span> </p>
from bs4 import BeautifulSoup
html_doc = ""
with open('html.in') as in_file:
html_doc = in_file.read()
html_doc = '<div class="terminal">' + html_doc + '</div>'
soup = BeautifulSoup(html_doc, 'html.parser')
div = soup.find('div', {'class': 'terminal'})
elements = []
i = 0
maxlen = 0
for p in div.findChildren('p', recursive=False):
# For each '<p>' tag in '<div>'
# Each '<p>' makes up a 'line', thus becomes a '<text>' element
line = '\t<text x="{}" y="{}">'.format(25, 50 + i * 25)
for child in p.contents:
if child.name == 'span':
# make '<tspan>' from '<span>'
contents = str(child.contents[0])
if contents.find(' ') != -1:
style_attr += ' white-space: pre;'
style_attr = child.get('style').replace('color', 'fill')
tspan = '<tspan style="{}">{}</tspan>'.format(style_attr, contents)
line += tspan
else:
line += str(child.string)
maxlen = max(maxlen, len(BeautifulSoup(line, 'lxml').get_text()))
line += '</text>\n'
i += 1
elements.append(line)
width = max(720, maxlen * 12.5)
height = 50 + i * 25 + 20
bg_rect = '\t<rect x="0" y="0" width="{}" height="{}" fill="#222A35"/>\n'.format(width, height)
elements.insert(0, bg_rect)
svg = '<svg width="{}" height="{}" xmlns="http://www.w3.org/2000/svg">\n{}</svg>\n'
stylesheet = \
'\t<style>' \
'text { ' \
'font-family: Consolas,"Courier New",monospace; ' \
'font-weight: 400; ' \
'font-size: 20px; ' \
'fill: #CCCCCC; ' \
'}</style>\n' \
svg = svg.format(width, height, stylesheet + "".join(elements))
with open('out.svg', 'w') as out_file:
out_file.write(svg)
Display the source blob
Display the rendered blob
Raw
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment