r-lib / svglite

A lightweight svg graphics device for R
https://svglite.r-lib.org
181 stars 39 forks source link

svglite does not encode tabular and proportional numbers correctly (faulty calculation of string widths?) #175

Open psoldath opened 3 months ago

psoldath commented 3 months ago

{svglite} does not encode tabular and proportional numbers correctly. Tabular figures of a font that uses proportional numbers as standard gets narrowed when fix_text_size = TRUE and somewhat squeezed together when fix_text_size = FALSE. In return, proportional figures of a font that uses tabular numbers as standard gets widened when fix_text_size = TRUE and somewhat spread out when fix_text_size = FALSE.

In the following "reprex" I load the standard sans font on my system (Helvetica; macOS Sonoma). Helvetica uses tabular figures as the standard format of figures, so I change it to proportional figures and compare the {svglite} outputs with that of {ragg}. In the outputs, we see that {ragg} encodes the proportional numbers correctly, while {svglite} does not. In these examples, I use geom_label() to plot text strings, but the problem is apparent in all other text elements as well, such as axis text etc.

library(tidyverse)
library(systemfonts)
library(svglite)
library(ragg) # for comparison

sans <- match_fonts("sans")$path
sans <- system_fonts()$family[system_fonts()$path == sans][1]

register_variant(
  "sans_proportional", 
  sans, 
  features = font_feature(numbers = "proportional")
)

p <- mpg |> 
  ggplot() +
  geom_label(aes(x = 0, y = 1), 
             label = "1111111111", 
             family = sans) + # upper text in standard tabular numbers
  geom_label(aes(x = 0, y = 0), 
             label = "1111111111",
             family = "sans_proportional") + # lower text in specified proportional numbers gets widened
  theme_void()

ggsave("p.svg", p, device = svglite)
ggsave("p_nofix.svg", p, device = svglite, fix_text_size = F)

p <- p +
  theme(panel.background = element_rect(fill = "grey95"))

ggsave("p.png", p, device = agg_png)

p.svg: proportional numbers are widened (to fill out a wrong string width that is too wide?)

p

p_nofix.svg: the label box is the same width as for the tabular numbers (again wrong string width?)

p_nofix

p.png is encoded correctly p

...And for the opposite case, when a font that uses proportional figures as standard format is specified to use tabular figures:

library(tidyverse)
library(systemfonts)
library(svglite)
library(ragg) # for comparison

# Using Helvetica Now as an example of font that uses proportional figures as standard format
Helvetica_path <- system_fonts() |> 
  filter(
    family == "Helvetica Now Text",
    style == "Regular"
  )

register_font(
  "Helvetica_regular",
  Helvetica_path
)

register_variant(
  "Helvetica_tabular",
  "Helvetica Now Text",
  features = font_feature(numbers = "tabular")
)

q <- mpg |> 
  ggplot() +
  geom_label(aes(x = 0, y = 1), 
             label = "1111111111",
             family = "Helvetica_regular") +
  geom_label(aes(x = 0, y = 0), 
             label = "1111111111",
             family = "Helvetica_tabular") +
  theme_void()

ggsave("q.svg", q, device = svglite)
ggsave("q_nofix.svg", q, device = svglite, fix_text_size = F)

q <- q +
  theme(panel.background = element_rect(fill = "grey95"))

ggsave("q.png", q, device = agg_png)

q.svg: tabular numbers are narrowed (to keep them within a too narrow label box?)

q

q_nofix.svg: tabular numbers extends outside the label box (too narrow label box due to wrong string width?)

q_nofix

q.png is encoded correctly q

thomasp85 commented 2 days ago

Thanks for this - this is an oversight on my part when I added the possibility of registering font features. Will fix in the upcoming release