Skip to content

Commit bb00fc9

Browse files
committed
svg update
1 parent 3aeeb8f commit bb00fc9

File tree

1 file changed

+137
-0
lines changed

1 file changed

+137
-0
lines changed

rich/console.py

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
CONSOLE_SVG_FORMAT,
5252
)
5353
from ._log_render import FormatTimeCallable, LogRender
54+
from ._loop import loop_last
5455
from .align import Align, AlignMethod
5556
from .color import ColorSystem
5657
from .control import Control
@@ -2364,6 +2365,142 @@ def export_svg(
23642365

23652366
return rendered_code
23662367

2368+
def export_svg(
2369+
self,
2370+
*,
2371+
title: str = "Rich",
2372+
theme: Optional[TerminalTheme] = None,
2373+
clear: bool = True,
2374+
code_format: str = CONSOLE_SVG_FORMAT,
2375+
id: str | None = None,
2376+
) -> str:
2377+
2378+
code_format = """
2379+
<svg id="{unique_id}" viewBox="0 0 {width} {height}" xmlns="http://www.w3.org/2000/svg">
2380+
<style>
2381+
text {{
2382+
font-family: Fira Code, monospace;
2383+
font-size: {char_height}px;
2384+
font-variant: east-asian-width-values;
2385+
line-height: {line_height}px;
2386+
dominant-baseline: text-before-edge;
2387+
}}
2388+
{styles}
2389+
</style>
2390+
<g transform="translate({terminal_x}, {terminal_y})">
2391+
{backgrounds}
2392+
<text>{matrix}</text>
2393+
</g>
2394+
</svg>
2395+
"""
2396+
from rich.cells import cell_len
2397+
2398+
style_cache: dict[Style, str] = {}
2399+
2400+
def get_svg_style(style: Style) -> str:
2401+
if style in style_cache:
2402+
return style_cache[style]
2403+
css_rules = []
2404+
if style.color is not None:
2405+
r, g, b = style.color.get_truecolor(_theme)
2406+
css_rules.append(f"fill: #{r:02X}{g:02x}{b:02X}")
2407+
2408+
if style.bold:
2409+
css_rules.append("font-weight: bold")
2410+
if style.italic:
2411+
css_rules.append("font-style: italic;")
2412+
if style.underline:
2413+
css_rules.append("text-decoration: underline;")
2414+
2415+
css = ";".join(css_rules)
2416+
style_cache[style] = css
2417+
return css
2418+
2419+
_theme = theme or SVG_EXPORT_THEME
2420+
2421+
width = 0
2422+
char_height = 20
2423+
char_width = char_height * 0.62
2424+
line_height = char_height * 1.15
2425+
2426+
padding_top = 20
2427+
padding_left = 10
2428+
padding_right = 10
2429+
padding_bottom = 20
2430+
2431+
text_backgrounds = []
2432+
text_group: list[str] = []
2433+
classes: dict[str, int] = {}
2434+
style_no = 1
2435+
2436+
with self._record_buffer_lock:
2437+
x = 0
2438+
y = 1
2439+
segments = list(
2440+
Segment.filter_control(
2441+
Segment.simplify(self._record_buffer),
2442+
)
2443+
)
2444+
2445+
if id is None:
2446+
unique_id = "terminal-" + str(
2447+
zlib.adler32(
2448+
("".join(segment.text for segment in segments)).encode(
2449+
"utf-8", "ignore"
2450+
)
2451+
+ title.encode("utf-8", "ignore")
2452+
)
2453+
)
2454+
else:
2455+
unique_id = id
2456+
for last, line in loop_last(Segment.split_lines(segments)):
2457+
x = 0
2458+
for text, style, _control in line:
2459+
style = style or Style()
2460+
rules = get_svg_style(style)
2461+
if rules not in classes:
2462+
classes[rules] = style_no
2463+
style_no += 1
2464+
class_name = f"r{classes[rules]}"
2465+
2466+
for character in text:
2467+
if style.bgcolor is not None:
2468+
r, g, b = style.bgcolor.get_truecolor(_theme)
2469+
text_backgrounds.append(
2470+
f"""<rect fill="#{r:02X}{g:02x}{b:02X}" x="{x * char_width}" y="{y * line_height}" width="{char_width}" height="{line_height}"></rect> """
2471+
)
2472+
text_group.append(
2473+
f"""<tspan class="#{unique_id} {class_name}" x="{x * char_width}" y="{y * line_height}" textLength="{char_width}px">{character}</tspan>"""
2474+
)
2475+
x += cell_len(character)
2476+
if not last:
2477+
text_group.append(
2478+
f"""<tspan x="{x * char_width}" y="{y * line_height}" textLength="{char_width}px">\n</tspan>"""
2479+
)
2480+
width = max(x, width)
2481+
y += 1
2482+
2483+
styles = "\n".join(
2484+
f"#{unique_id} .r{rule_no} {{ {css} }}" for css, rule_no in classes.items()
2485+
)
2486+
backgrounds = "".join(text_backgrounds)
2487+
matrix = "".join(text_group)
2488+
2489+
svg = code_format.format(
2490+
unique_id=unique_id,
2491+
char_width=char_width,
2492+
char_height=char_height,
2493+
line_height=line_height,
2494+
width=width * char_width,
2495+
height=y * line_height,
2496+
terminal_x=0,
2497+
terminal_y=0,
2498+
styles=styles,
2499+
backgrounds=backgrounds,
2500+
matrix=matrix,
2501+
)
2502+
return svg
2503+
23672504
def save_svg(
23682505
self,
23692506
path: str,

0 commit comments

Comments
 (0)