Repository: Textualize/rich
Pull request #2288 (2022-05-23 08:43:43)

Firefox fixes for SVG


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index bbd0167c..19033811 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,16 @@ 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.2] - 2022-05-23
+
+### Fixed
+
+- Fix for SVG on Firefox
+
+### Changed
+
+- Removed excess margin from SVG, tweaked cell sizes to better render block characters
+
 ## [12.4.1] - 2022-05-08
 
 ### Fixed
diff --git a/pyproject.toml b/pyproject.toml
index 71f4e9ac..eca3f240 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.1"
+version = "12.4.2"
 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 32aa71c9..d469bac2 100644
--- a/rich/_export_format.py
+++ b/rich/_export_format.py
@@ -50,7 +50,6 @@ CONSOLE_SVG_FORMAT = """\
 
     .{unique_id}-title {{
         font-size: 18px;
-
         font-weight: bold;
         font-family: arial;
     }}
@@ -60,7 +59,9 @@ CONSOLE_SVG_FORMAT = """\
     {chrome}
     <g transform="translate({terminal_x}, {terminal_y})">
     {backgrounds}
-    <text alignment-baseline="baseline" class="{unique_id}-matrix" font-variant="east-asian-width-values">{matrix}</text>
+    <g class="{unique_id}-matrix">
+    {matrix}
+    </g>
     </g>
 </svg>
 """
diff --git a/rich/console.py b/rich/console.py
index 2159d3c1..2f42b71a 100644
--- a/rich/console.py
+++ b/rich/console.py
@@ -2282,18 +2282,18 @@ class Console:
 
         width = self.width
         char_height = 20
-        char_width = char_height * 0.62
-        line_height = char_height * 1.32
+        char_width = char_height * 0.61
+        line_height = char_height * 1.22
 
-        margin_top = 20
-        margin_right = 16
-        margin_bottom = 20
-        margin_left = 16
+        margin_top = 1
+        margin_right = 1
+        margin_bottom = 1
+        margin_left = 1
 
         padding_top = 40
-        padding_right = 12
+        padding_right = 8
         padding_bottom = 12
-        padding_left = 12
+        padding_left = 8
 
         padding_width = padding_left + padding_right
         padding_height = padding_top + padding_bottom
@@ -2385,16 +2385,18 @@ class Console:
                             height=line_height + 1,
                         )
                     )
-                text_group.append(
-                    make_tag(
-                        "tspan",
-                        escape_text(text),
-                        _class=f"{unique_id}-{class_name}",
-                        x=x * char_width,
-                        y=y * line_height + char_height,
-                        textLength=char_width * len(text),
+
+                if text != " " * len(text):
+                    text_group.append(
+                        make_tag(
+                            "text",
+                            escape_text(text),
+                            _class=f"{unique_id}-{class_name}",
+                            x=x * char_width,
+                            y=y * line_height + char_height,
+                            textLength=char_width * len(text),
+                        )
                     )
-                )
                 x += cell_len(text)
 
         styles = "\n".join(
@@ -2421,7 +2423,7 @@ class Console:
         if title:
             chrome += make_tag(
                 "text",
-                title,
+                escape_text(title),
                 _class=f"{unique_id}-title",
                 fill=title_color,
                 text_anchor="middle",
@@ -2429,9 +2431,11 @@ class Console:
                 y=margin_top + char_height + 6,
             )
         chrome += f"""
-            <circle cx="40" cy="40" r="7" fill="#ff5f57"/>
-            <circle cx="62" cy="40" r="7" fill="#febc2e"/>
-            <circle cx="84" cy="40" r="7" fill="#28c840"/>
+            <g transform="translate(30,24)">
+            <circle cx="0" cy="0" r="7" fill="#ff5f57"/>
+            <circle cx="22" cy="0" r="7" fill="#febc2e"/>
+            <circle cx="44" cy="0" r="7" fill="#28c840"/>
+            </g>
         """
 
         svg = code_format.format(


Test patch:
diff --git a/tests/test_console.py b/tests/test_console.py
index 007cd281..35f3cbf3 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 1296 118.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: 26.400000000000002px;\n        font-variant-east-asian: full-width;\n    }\n\n    .terminal-614794459-title {\n        font-size: 18px;\n\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    <rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1" x="16" y="20" width="1264" height="78.4" rx="12"/><text class="terminal-614794459-title" fill="#c5c8c6" text-anchor="middle" x="632" y="46">Rich</text>\n            <circle cx="40" cy="40" r="7" fill="#ff5f57"/>\n            <circle cx="62" cy="40" r="7" fill="#febc2e"/>\n            <circle cx="84" cy="40" r="7" fill="#28c840"/>\n        \n    <g transform="translate(28, 60)">\n    <rect fill="#cc555a" x="0" y="0" width="38.2" height="27.4"/>\n    <text alignment-baseline="baseline" class="terminal-614794459-matrix" font-variant="east-asian-width-values"><tspan class="terminal-614794459-r1" x="0" y="20" textLength="37.2">foo</tspan><tspan class="terminal-614794459-r2" x="37.2" y="20" textLength="12.4">&#160;</tspan><tspan class="terminal-614794459-r2" x="49.6" y="20" textLength="62">Click</tspan><tspan class="terminal-614794459-r2" x="111.6" y="20" textLength="1128.4">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</tspan><tspan class="terminal-614794459-r2" x="1240" y="20" textLength="12.4">\n</tspan></text>\n    </g>\n</svg>\n'
+EXPECTED_SVG = '<svg class="rich-terminal" viewBox="0 0 1238 78.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    <rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1" x="1" y="1" width="1236" height="76.4" rx="12"/><text class="terminal-614794459-title" fill="#c5c8c6" text-anchor="middle" x="618" y="27">Rich</text>\n            <g transform="translate(30,24)">\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)">\n    <rect fill="#cc555a" x="0" y="0" width="37.6" height="25.4"/>\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'
 
 
 def test_export_svg():