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
<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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment