Repository: Textualize/rich
Pull request #2290 (2022-05-24 06:30:37)

More svg tweaks
## Type of changes

- [ ] Bug fix
- [ ] New feature
- [ ] Documentation / docstrings
- [ ] Tests
- [ ] Other

## Checklist

- [ ] I've run the latest [black](https://github.com/psf/black) with default args on new code.
- [ ] I've updated CHANGELOG.md and CONTRIBUTORS.md where appropriate.
- [ ] I've added tests for new code.
- [ ] I accept that @willmcgugan may be pedantic in the code review.

## Description

Please describe your changes here. If this fixes a bug, please link to the issue, if possible.



Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 18e5acba..52db0457 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file.
 The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
 and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
+## [12.4.4] - 2022-05-24
+
+### Changed
+
+- Added clipping per line to SVG output to avoid box characters overlapping
+- Optimized SVG output
+
 ## [12.4.3] - 2022-05-23
 
 ### Changed
diff --git a/pyproject.toml b/pyproject.toml
index e922561c..1b16acf9 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -2,7 +2,7 @@
 name = "rich"
 homepage = "https://github.com/willmcgugan/rich"
 documentation = "https://rich.readthedocs.io/en/latest/"
-version = "12.4.3"
+version = "12.4.4"
 description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal"
 authors = ["Will McGugan <willmcgugan@gmail.com>"]
 license = "MIT"
diff --git a/rich/_export_format.py b/rich/_export_format.py
index 028e8336..b79c1306 100644
--- a/rich/_export_format.py
+++ b/rich/_export_format.py
@@ -61,6 +61,7 @@ CONSOLE_SVG_FORMAT = """\
     <clipPath id="{unique_id}-clip-terminal">
       <rect x="0" y="0" width="{terminal_width}" height="{terminal_height}" />
     </clipPath>
+    {lines}
     </defs>
 
     {chrome}
diff --git a/rich/console.py b/rich/console.py
index 9e2b5fe3..eba27611 100644
--- a/rich/console.py
+++ b/rich/console.py
@@ -2330,11 +2330,7 @@ class Console:
             )
 
         with self._record_buffer_lock:
-            segments = list(
-                Segment.filter_control(
-                    Segment.simplify(self._record_buffer),
-                )
-            )
+            segments = list(Segment.filter_control(self._record_buffer))
             if clear:
                 self._record_buffer.clear()
 
@@ -2383,6 +2379,7 @@ class Console:
                             y=y * line_height + 1.5,
                             width=char_width * text_length,
                             height=line_height + 0.25,
+                            shape_rendering="crispEdges",
                         )
                     )
 
@@ -2395,10 +2392,19 @@ class Console:
                             x=x * char_width,
                             y=y * line_height + char_height,
                             textLength=char_width * len(text),
+                            clip_path=f"url(#{unique_id}-line-{y})",
                         )
                     )
                 x += cell_len(text)
 
+        line_offsets = [line_no * line_height + 1.5 for line_no in range(y)]
+        lines = "\n".join(
+            f"""<clipPath id="{unique_id}-line-{line_no}">
+    {make_tag("rect", x=0, y=offset, width=char_width * width, height=line_height + 0.25)}
+            </clipPath>"""
+            for line_no, offset in enumerate(line_offsets)
+        )
+
         styles = "\n".join(
             f".{unique_id}-r{rule_no} {{ {css} }}" for css, rule_no in classes.items()
         )
@@ -2443,8 +2449,8 @@ class Console:
             char_width=char_width,
             char_height=char_height,
             line_height=line_height,
-            terminal_width=char_width * width,
-            terminal_height=(y + 1) * line_height,
+            terminal_width=char_width * width - 1,
+            terminal_height=(y + 1) * line_height - 1,
             width=terminal_width + margin_width,
             height=terminal_height + margin_height,
             terminal_x=margin_left + padding_left,
@@ -2453,6 +2459,7 @@ class Console:
             chrome=chrome,
             backgrounds=backgrounds,
             matrix=matrix,
+            lines=lines,
         )
         return svg
 


Test patch:
diff --git a/tests/test_console.py b/tests/test_console.py
index e09b2385..828d3775 100644
--- a/tests/test_console.py
+++ b/tests/test_console.py
@@ -494,7 +494,7 @@ def test_export_html_inline():
     assert html == expected
 
 
-EXPECTED_SVG = '<svg class="rich-terminal" viewBox="0 0 1238 74.4" xmlns="http://www.w3.org/2000/svg">\n    <!-- Generated with Rich https://www.textualize.io -->\n    <style>\n\n    @font-face {\n        font-family: "Fira Code";\n        src: local("FiraCode-Regular"),\n                url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff2/FiraCode-Regular.woff2") format("woff2"),\n                url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff/FiraCode-Regular.woff") format("woff");\n        font-style: normal;\n        font-weight: 400;\n    }\n    @font-face {\n        font-family: "Fira Code";\n        src: local("FiraCode-Bold"),\n                url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff2/FiraCode-Bold.woff2") format("woff2"),\n                url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff/FiraCode-Bold.woff") format("woff");\n        font-style: bold;\n        font-weight: 700;\n    }\n\n    .terminal-614794459-matrix {\n        font-family: Fira Code, monospace;\n        font-size: 20px;\n        line-height: 24.4px;\n        font-variant-east-asian: full-width;\n    }\n\n    .terminal-614794459-title {\n        font-size: 18px;\n        font-weight: bold;\n        font-family: arial;\n    }\n\n    .terminal-614794459-r1 { fill: #608ab1;font-weight: bold }\n.terminal-614794459-r2 { fill: #c5c8c6 }\n    </style>\n\n    <defs>\n    <clipPath id="terminal-614794459-clip-terminal">\n      <rect x="0" y="0" width="1220.0" height="24.4" />\n    </clipPath>\n    </defs>\n\n    <rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1" x="1" y="1" width="1236" height="72.4" rx="8"/><text class="terminal-614794459-title" fill="#c5c8c6" text-anchor="middle" x="618" y="27">Rich</text>\n            <g transform="translate(26,22)">\n            <circle cx="0" cy="0" r="7" fill="#ff5f57"/>\n            <circle cx="22" cy="0" r="7" fill="#febc2e"/>\n            <circle cx="44" cy="0" r="7" fill="#28c840"/>\n            </g>\n        \n    <g transform="translate(9, 41)" clip-path="url(#terminal-614794459-clip-terminal)">\n    <rect fill="#cc555a" x="0" y="1.5" width="36.6" height="24.65"/>\n    <g class="terminal-614794459-matrix">\n    <text class="terminal-614794459-r1" x="0" y="20" textLength="36.6">foo</text><text class="terminal-614794459-r2" x="48.8" y="20" textLength="61">Click</text><text class="terminal-614794459-r2" x="1220" y="20" textLength="12.2">\n</text>\n    </g>\n    </g>\n</svg>\n'
+EXPECTED_SVG = '<svg class="rich-terminal" viewBox="0 0 1238 74.4" xmlns="http://www.w3.org/2000/svg">\n    <!-- Generated with Rich https://www.textualize.io -->\n    <style>\n\n    @font-face {\n        font-family: "Fira Code";\n        src: local("FiraCode-Regular"),\n                url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff2/FiraCode-Regular.woff2") format("woff2"),\n                url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff/FiraCode-Regular.woff") format("woff");\n        font-style: normal;\n        font-weight: 400;\n    }\n    @font-face {\n        font-family: "Fira Code";\n        src: local("FiraCode-Bold"),\n                url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff2/FiraCode-Bold.woff2") format("woff2"),\n                url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff/FiraCode-Bold.woff") format("woff");\n        font-style: bold;\n        font-weight: 700;\n    }\n\n    .terminal-614794459-matrix {\n        font-family: Fira Code, monospace;\n        font-size: 20px;\n        line-height: 24.4px;\n        font-variant-east-asian: full-width;\n    }\n\n    .terminal-614794459-title {\n        font-size: 18px;\n        font-weight: bold;\n        font-family: arial;\n    }\n\n    .terminal-614794459-r1 { fill: #608ab1;font-weight: bold }\n.terminal-614794459-r2 { fill: #c5c8c6 }\n    </style>\n\n    <defs>\n    <clipPath id="terminal-614794459-clip-terminal">\n      <rect x="0" y="0" width="1219.0" height="23.4" />\n    </clipPath>\n    \n    </defs>\n\n    <rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1" x="1" y="1" width="1236" height="72.4" rx="8"/><text class="terminal-614794459-title" fill="#c5c8c6" text-anchor="middle" x="618" y="27">Rich</text>\n            <g transform="translate(26,22)">\n            <circle cx="0" cy="0" r="7" fill="#ff5f57"/>\n            <circle cx="22" cy="0" r="7" fill="#febc2e"/>\n            <circle cx="44" cy="0" r="7" fill="#28c840"/>\n            </g>\n        \n    <g transform="translate(9, 41)" clip-path="url(#terminal-614794459-clip-terminal)">\n    <rect fill="#cc555a" x="0" y="1.5" width="36.6" height="24.65" shape-rendering="crispEdges"/>\n    <g class="terminal-614794459-matrix">\n    <text class="terminal-614794459-r1" x="0" y="20" textLength="36.6" clip-path="url(#terminal-614794459-line-0)">foo</text><text class="terminal-614794459-r2" x="48.8" y="20" textLength="61" clip-path="url(#terminal-614794459-line-0)">Click</text><text class="terminal-614794459-r2" x="1220" y="20" textLength="12.2" clip-path="url(#terminal-614794459-line-0)">\n</text>\n    </g>\n    </g>\n</svg>\n'
 
 
 def test_export_svg():