Repository: vega/vl-convert (language: python)

Issue:
vl-convert fails with fractional font sizes
The following spec, with `"fontSize": 20.5`, works just fine in Vega Editor:

```
{
  "$schema": "https://vega.github.io/schema/vega-lite/v5.json",
  "data": {
    "values": [
      {"text": "Some text"}
    ]
  },
  "mark": {"type": "text", "fontSize": 20.5},
  "encoding": {
    "text": {
      "type": "nominal",
      "field": "text"
    }
  }
}
```

...but crashes in `vl-convert` 0.10.3:

```
% vl-convert vl2svg --input fractional-fontSize.json --output fractional-fontSize.svg  
ERROR Error: Failed to deserialize text info: invalid type: floating point `20.5`, expected i32 at line 3 column 14
    at Object.sg.textMetrics.width (<anon>:17:45)
    at Ve (https://cdn.skypack.dev/-/vega-scenegraph@v4.10.2-W1dltMWN7mO3TARazJA1/dist=es2020,mode=imports,min/optimized/vega-scenegraph.js:1:30235)
    at J (https://cdn.skypack.dev/-/vega-view-transforms@v4.5.9-LiB26zIbxiHZW70fnrDI/dist=es2020,mode=imports,min/optimized/vega-view-transforms.js:1:1588)
    at https://cdn.skypack.dev/-/vega-view-transforms@v4.5.9-LiB26zIbxiHZW70fnrDI/dist=es2020,mode=imports,min/optimized/vega-view-transforms.js:1:1278
    at Array.forEach (<anonymous>)
    at Mt.transform (https://cdn.skypack.dev/-/vega-view-transforms@v4.5.9-LiB26zIbxiHZW70fnrDI/dist=es2020,mode=imports,min/optimized/vega-view-transforms.js:1:1259)
    at Mt.evaluate (https://cdn.skypack.dev/-/vega-dataflow@v5.7.5-asKYS4gpPLMPf64pSozt/dist=es2020,mode=imports,min/optimized/vega-dataflow.js:1:15429)
    at Mt.run (https://cdn.skypack.dev/-/vega-dataflow@v5.7.5-asKYS4gpPLMPf64pSozt/dist=es2020,mode=imports,min/optimized/vega-dataflow.js:1:15286)
    at ne.Gt [as evaluate] (https://cdn.skypack.dev/-/vega-dataflow@v5.7.5-asKYS4gpPLMPf64pSozt/dist=es2020,mode=imports,min/optimized/vega-dataflow.js:1:12073)
    at ne.evaluate (https://cdn.skypack.dev/-/vega-view@v5.11.1-FQ9r1BvJOMHegkomXDyj/dist=es2020,mode=imports,min/optimized/vega-view.js:2:1621)
```

I didn't specifically test this example with altair_saver, but I discovered it when testing the Vega-Altair 4.x → 5.x upgrade on an old project that uses such fractional font sizes and altair_saver.


Discussion / hints:
Thanks for the report! Looks like we need to change `size` to a float in https://github.com/vega/vl-convert/blob/74a0af5cacc3212319198d699d8610b704cc45ab/vl-convert-rs/src/text.rs#L107

Fix patch:
diff --git a/vl-convert-rs/src/text.rs b/vl-convert-rs/src/text.rs
index 4dee7ac6..fd01780e 100644
--- a/vl-convert-rs/src/text.rs
+++ b/vl-convert-rs/src/text.rs
@@ -104,7 +104,7 @@ struct TextInfo {
     variant: Option<String>,
     weight: Option<String>,
     family: Option<String>,
-    size: i32,
+    size: f64,
     text: Value,
 }
 


Test patch:
diff --git a/vl-convert-rs/tests/test_specs.rs b/vl-convert-rs/tests/test_specs.rs
index 2872903a..aeab40be 100644
--- a/vl-convert-rs/tests/test_specs.rs
+++ b/vl-convert-rs/tests/test_specs.rs
@@ -265,7 +265,8 @@ mod test_svg {
             "stacked_bar_h",
             "bar_chart_trellis_compact",
             "line_with_log_scale",
-            "numeric_font_weight"
+            "numeric_font_weight",
+            "float_font_size",
         )]
         name: &str,
     ) {
@@ -309,6 +310,7 @@ mod test_png_no_theme {
         case("line_with_log_scale", 2.0),
         case("remote_images", 1.0),
         case("maptile_background", 1.0),
+        case("float_font_size", 1.0),
     )]
     fn test(
         name: &str,
diff --git a/vl-convert-rs/tests/vl-specs/expected/v5_8/float_font_size.png b/vl-convert-rs/tests/vl-specs/expected/v5_8/float_font_size.png
new file mode 100644
index 00000000..18155be5
Binary files /dev/null and b/vl-convert-rs/tests/vl-specs/expected/v5_8/float_font_size.png differ
diff --git a/vl-convert-rs/tests/vl-specs/expected/v5_8/float_font_size.svg b/vl-convert-rs/tests/vl-specs/expected/v5_8/float_font_size.svg
new file mode 100644
index 00000000..cb67b46d
--- /dev/null
+++ b/vl-convert-rs/tests/vl-specs/expected/v5_8/float_font_size.svg
@@ -0,0 +1,1 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" class="marks" width="229" height="264" viewBox="0 0 229 264"><rect width="229" height="264" fill="white"/><g fill="none" stroke-miterlimit="10" transform="translate(44,27)"><g class="mark-group role-frame root" role="graphics-object" aria-roledescription="group mark container"><g transform="translate(0,0)"><path class="background" aria-hidden="true" d="M0.5,0.5h180v200h-180Z" stroke="#ddd"/><g><g class="mark-group role-axis" aria-hidden="true"><g transform="translate(0.5,0.5)"><path class="background" aria-hidden="true" d="M0,0h0v0h0Z" pointer-events="none"/><g><g class="mark-rule role-axis-grid" pointer-events="none"><line transform="translate(0,200)" x2="180" y2="0" stroke="#ddd" stroke-width="1" opacity="1"/><line transform="translate(0,160)" x2="180" y2="0" stroke="#ddd" stroke-width="1" opacity="1"/><line transform="translate(0,120)" x2="180" y2="0" stroke="#ddd" stroke-width="1" opacity="1"/><line transform="translate(0,80)" x2="180" y2="0" stroke="#ddd" stroke-width="1" opacity="1"/><line transform="translate(0,40)" x2="180" y2="0" stroke="#ddd" stroke-width="1" opacity="1"/><line transform="translate(0,0)" x2="180" y2="0" stroke="#ddd" stroke-width="1" opacity="1"/></g></g><path class="foreground" aria-hidden="true" d="" pointer-events="none" display="none"/></g></g><g class="mark-group role-axis" role="graphics-symbol" aria-roledescription="axis" aria-label="X-axis titled 'a' for a discrete scale with 9 values: A, B, C, D, E, ending with I"><g transform="translate(0.5,200.5)"><path class="background" aria-hidden="true" d="M0,0h0v0h0Z" pointer-events="none"/><g><g class="mark-rule role-axis-tick" pointer-events="none"><line transform="translate(10,0)" x2="0" y2="5" stroke="#888" stroke-width="1" opacity="1"/><line transform="translate(30,0)" x2="0" y2="5" stroke="#888" stroke-width="1" opacity="1"/><line transform="translate(50,0)" x2="0" y2="5" stroke="#888" stroke-width="1" opacity="1"/><line transform="translate(70,0)" x2="0" y2="5" stroke="#888" stroke-width="1" opacity="1"/><line transform="translate(90,0)" x2="0" y2="5" stroke="#888" stroke-width="1" opacity="1"/><line transform="translate(110,0)" x2="0" y2="5" stroke="#888" stroke-width="1" opacity="1"/><line transform="translate(130,0)" x2="0" y2="5" stroke="#888" stroke-width="1" opacity="1"/><line transform="translate(150,0)" x2="0" y2="5" stroke="#888" stroke-width="1" opacity="1"/><line transform="translate(170,0)" x2="0" y2="5" stroke="#888" stroke-width="1" opacity="1"/></g><g class="mark-text role-axis-label" pointer-events="none"><text text-anchor="middle" transform="translate(9.5,15)" font-family="sans-serif" font-size="10px" fill="#000" opacity="1">A</text><text text-anchor="middle" transform="translate(29.5,15)" font-family="sans-serif" font-size="10px" fill="#000" opacity="1">B</text><text text-anchor="middle" transform="translate(49.5,15)" font-family="sans-serif" font-size="10px" fill="#000" opacity="1">C</text><text text-anchor="middle" transform="translate(69.5,15)" font-family="sans-serif" font-size="10px" fill="#000" opacity="1">D</text><text text-anchor="middle" transform="translate(89.5,15)" font-family="sans-serif" font-size="10px" fill="#000" opacity="1">E</text><text text-anchor="middle" transform="translate(109.5,15)" font-family="sans-serif" font-size="10px" fill="#000" opacity="1">F</text><text text-anchor="middle" transform="translate(129.5,15)" font-family="sans-serif" font-size="10px" fill="#000" opacity="1">G</text><text text-anchor="middle" transform="translate(149.5,15)" font-family="sans-serif" font-size="10px" fill="#000" opacity="1">H</text><text text-anchor="middle" transform="translate(169.5,15)" font-family="sans-serif" font-size="10px" fill="#000" opacity="1">I</text></g><g class="mark-rule role-axis-domain" pointer-events="none"><line transform="translate(0,0)" x2="180" y2="0" stroke="#888" stroke-width="1" opacity="1"/></g><g class="mark-text role-axis-title" pointer-events="none"><text text-anchor="middle" transform="translate(90,30)" font-family="sans-serif" font-size="11px" font-weight="bold" fill="#000" opacity="1">a</text></g></g><path class="foreground" aria-hidden="true" d="" pointer-events="none" display="none"/></g></g><g class="mark-group role-axis" role="graphics-symbol" aria-roledescription="axis" aria-label="Y-axis titled 'b' for a linear scale with values from 0 to 100"><g transform="translate(0.5,0.5)"><path class="background" aria-hidden="true" d="M0,0h0v0h0Z" pointer-events="none"/><g><g class="mark-rule role-axis-tick" pointer-events="none"><line transform="translate(0,200)" x2="-5" y2="0" stroke="#888" stroke-width="1" opacity="1"/><line transform="translate(0,160)" x2="-5" y2="0" stroke="#888" stroke-width="1" opacity="1"/><line transform="translate(0,120)" x2="-5" y2="0" stroke="#888" stroke-width="1" opacity="1"/><line transform="translate(0,80)" x2="-5" y2="0" stroke="#888" stroke-width="1" opacity="1"/><line transform="translate(0,40)" x2="-5" y2="0" stroke="#888" stroke-width="1" opacity="1"/><line transform="translate(0,0)" x2="-5" y2="0" stroke="#888" stroke-width="1" opacity="1"/></g><g class="mark-text role-axis-label" pointer-events="none"><text text-anchor="end" transform="translate(-7,203)" font-family="sans-serif" font-size="10px" fill="#000" opacity="1">0</text><text text-anchor="end" transform="translate(-7,163)" font-family="sans-serif" font-size="10px" fill="#000" opacity="1">20</text><text text-anchor="end" transform="translate(-7,123)" font-family="sans-serif" font-size="10px" fill="#000" opacity="1">40</text><text text-anchor="end" transform="translate(-7,83)" font-family="sans-serif" font-size="10px" fill="#000" opacity="1">60</text><text text-anchor="end" transform="translate(-7,42.99999999999999)" font-family="sans-serif" font-size="10px" fill="#000" opacity="1">80</text><text text-anchor="end" transform="translate(-7,3)" font-family="sans-serif" font-size="10px" fill="#000" opacity="1">100</text></g><g class="mark-rule role-axis-domain" pointer-events="none"><line transform="translate(0,200)" x2="0" y2="-200" stroke="#888" stroke-width="1" opacity="1"/></g><g class="mark-text role-axis-title" pointer-events="none"><text text-anchor="middle" transform="translate(-27.6845703125,100) rotate(-90) translate(0,-2)" font-family="sans-serif" font-size="11px" font-weight="bold" fill="#000" opacity="1">b</text></g></g><path class="foreground" aria-hidden="true" d="" pointer-events="none" display="none"/></g></g><g class="mark-rect role-mark marks" role="graphics-object" aria-roledescription="rect mark container"><path aria-label="a: A; b: 28" role="graphics-symbol" aria-roledescription="bar" d="M1,144h18v56h-18Z" fill="#4c78a8"/><path aria-label="a: B; b: 55" role="graphics-symbol" aria-roledescription="bar" d="M21,89.99999999999999h18v110.00000000000001h-18Z" fill="#4c78a8"/><path aria-label="a: C; b: 43" role="graphics-symbol" aria-roledescription="bar" d="M41,114.00000000000001h18v85.99999999999999h-18Z" fill="#4c78a8"/><path aria-label="a: D; b: 91" role="graphics-symbol" aria-roledescription="bar" d="M61,17.999999999999993h18v182h-18Z" fill="#4c78a8"/><path aria-label="a: E; b: 81" role="graphics-symbol" aria-roledescription="bar" d="M81,37.999999999999986h18v162h-18Z" fill="#4c78a8"/><path aria-label="a: F; b: 53" role="graphics-symbol" aria-roledescription="bar" d="M101,94h18v106h-18Z" fill="#4c78a8"/><path aria-label="a: G; b: 19" role="graphics-symbol" aria-roledescription="bar" d="M121,162h18v38h-18Z" fill="#4c78a8"/><path aria-label="a: H; b: 87" role="graphics-symbol" aria-roledescription="bar" d="M141,26h18v174h-18Z" fill="#4c78a8"/><path aria-label="a: I; b: 52" role="graphics-symbol" aria-roledescription="bar" d="M161,96h18v104h-18Z" fill="#4c78a8"/></g><g class="mark-group role-title"><g transform="translate(90,-21.75)"><path class="background" aria-hidden="true" d="M0,0h0v0h0Z" pointer-events="none"/><g><g class="mark-text role-title-text" role="graphics-symbol" aria-roledescription="title" aria-label="Title text 'Some title'" pointer-events="none"><text text-anchor="middle" transform="translate(0,10)" font-family="sans-serif" font-size="12.75px" font-weight="bold" fill="#000" opacity="1">Some title</text></g></g><path class="foreground" aria-hidden="true" d="" pointer-events="none" display="none"/></g></g></g><path class="foreground" aria-hidden="true" d="" display="none"/></g></g></g></svg>
\ No newline at end of file
diff --git a/vl-convert-rs/tests/vl-specs/float_font_size.vl.json b/vl-convert-rs/tests/vl-specs/float_font_size.vl.json
new file mode 100644
index 00000000..f82d2d52
--- /dev/null
+++ b/vl-convert-rs/tests/vl-specs/float_font_size.vl.json
@@ -0,0 +1,17 @@
+{
+  "$schema": "https://vega.github.io/schema/vega-lite/v5.json",
+  "title": "Some title",
+  "config": {"title": {"fontSize": 12.75}},
+  "data": {
+    "values": [
+      {"a": "A", "b": 28}, {"a": "B", "b": 55}, {"a": "C", "b": 43},
+      {"a": "D", "b": 91}, {"a": "E", "b": 81}, {"a": "F", "b": 53},
+      {"a": "G", "b": 19}, {"a": "H", "b": 87}, {"a": "I", "b": 52}
+    ]
+  },
+  "mark": "bar",
+  "encoding": {
+    "x": {"field": "a", "type": "nominal", "axis": {"labelAngle": 0}},
+    "y": {"field": "b", "type": "quantitative"}
+  }
+}