Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

vjust is not respected on linux #55

Closed
albert-ying opened this issue Jan 13, 2022 · 15 comments
Closed

vjust is not respected on linux #55

albert-ying opened this issue Jan 13, 2022 · 15 comments

Comments

@albert-ying
Copy link

Hi, thank you for this amazing package! I noticed that for some reason the geomtextpath doesn't respond to vjust value on my linux machine. This bug doesn't appear on my macOS machine tho

Here is the code I copyed from a previous issue

library(ggplot2)
library(geomtextpath)
library(patchwork)

df <- data.frame(
  x = seq(-0.5, 1.5, length.out = 10),                                       
  y = 1,
  label = "geom_textpath"
)

plot_vjust <- function(vjust) {
  ggplot(df, aes(x, y)) +
    geom_textpath(aes(label = label), vjust = vjust) +                       
    annotate(x = 2, y = 1, label = "geom_text", geom = "text", vjust = vjust) +
    ggtitle(paste0("vjust = ", vjust)) +                                     
    scale_x_continuous(limits = c(-1, 3))                                    
}

p = plot_vjust(-1) + plot_vjust(0) + plot_vjust(1) + plot_vjust(2) + plot_vjust(10) + plot_vjust(15)

And this is my output on Linux machine: you can see the geom_textpath does not respond to vjust value like geom_text

image

Here is my sessionInfo

R version 4.1.1 (2021-08-10)
Platform: x86_64-pc-linux-gnu (64-bit)
Running under: Ubuntu 16.04.6 LTS

Matrix products: default
BLAS/LAPACK: /usr/lib/libopenblasp-r0.2.18.so

locale:
 [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C               LC_TIME=en_US.UTF-8       
  [4] LC_COLLATE=en_US.UTF-8     LC_MONETARY=en_US.UTF-8    LC_MESSAGES=en_US.UTF-8   
   [7] LC_PAPER=en_US.UTF-8       LC_NAME=C                  LC_ADDRESS=C              
   [10] LC_TELEPHONE=C             LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C       

   attached base packages:
   [1] stats     graphics  grDevices utils     datasets  methods   base     

   other attached packages:
   [1] patchwork_1.1.1    geomtextpath_0.1.0 ggplot2_3.3.5     

   loaded via a namespace (and not attached):
    [1] Rcpp_1.0.7             magrittr_2.0.1         tidyselect_1.1.1      
     [4] munsell_0.5.0          colorspace_2.0-2       R6_2.5.1              
      [7] ragg_1.2.1             rlang_0.4.12           fansi_0.5.0           
      [10] dplyr_1.0.7            tools_4.1.1            grid_4.1.1            
      [13] gtable_0.3.0           utf8_1.2.2             DBI_1.1.2             
      [16] withr_2.4.3            systemfonts_1.0.3.9000 ellipsis_0.3.2        
      [19] digest_0.6.28          assertthat_0.2.1       tibble_3.1.5          
      [22] lifecycle_1.0.1        crayon_1.4.2           textshaping_0.3.6     
      [25] farver_2.1.0           purrr_0.3.4            vctrs_0.3.8           
      [28] glue_1.4.2             labeling_0.4.2         compiler_4.1.1        
      [31] pillar_1.6.4           generics_0.1.0         scales_1.1.1          
      [34] pkgconfig_2.0.3    

Thank you!

@AllanCameron
Copy link
Owner

Hi @albert-ying. Thanks for using the package, and bringing this to our attention. It's not clear to me why this bug would show up on one system and not another. I'm assuming you have tried a new R session and the latest development version of geomtextpath?

If so, would you mind checking the behaviour of a slightly modified version of the code so that we can try to track this down? If you modify the function to

plot_vjust <- function(vjust) {
  ggplot(df, aes(x, y)) +
    geom_textpath(aes(label = label, vjust = vjust)) +                       
    geom_text(x = 2, y = 1, label = "geom_text", vjust = vjust) +
    ggtitle(paste0("vjust = ", vjust)) +                                     
    scale_x_continuous(limits = c(-1, 3)) + 
    scale_vjust_identity()                                  
}

Do you still get the same behaviour?

@albert-ying
Copy link
Author

albert-ying commented Jan 13, 2022

I'm assuming you have tried a new R session and the latest development version of geomtextpath

Yes.

I tried your modified code, it gives me the same result.

I noticed that when I save the plot as PDF, it gives the weird-looking font

image

Might be related to the rendering of the text/font?

update

The font looks normal after I specify the family, but the vjust still doesn't work. Also doesn't relate to the rich argument.

    geom_textpath(aes(label = label, vjust = vjust), rich = T, family = "Helvetica")

image

@AllanCameron
Copy link
Owner

Thanks for trying that Albert. It looks to me as though the spacing is being increased between letters in the pdf version until you specify "family". Ultimately, both versions are drawn with the same font using grid::textGrob - I'm guessing that the pdf device has a different fallback font when the "family" is left blank. We'll need to investigate this further, along with the vjust issue.

Thanks again

@albert-ying
Copy link
Author

Not just vjust, \n doesn't work here either, which is strange.

Thank you for looking at it!

library(ggplot2)
library(geomtextpath)
library(patchwork)
df <- data.frame(
  x = seq(-0.5, 1.5, length.out = 10),
  y = 1,
  label = "geom_textpath \n 2nd row"
)
plot_vjust <- function(vjust) {
  ggplot(df, aes(x, y)) +
    geom_textpath(aes(label = label, vjust = vjust), family = "Helvetica") +                       
    geom_text(x = 2, y = 1, label = "geom_text \n 2nd row", vjust = vjust) +
    ggtitle(paste0("vjust = ", vjust)) +                                     
    scale_x_continuous(limits = c(-1, 3)) + 
    scale_vjust_identity()
}

p = plot_vjust(-1) + plot_vjust(0) + plot_vjust(1) + plot_vjust(2) + plot_vjust(10) + plot_vjust(15)

image

@teunbrand
Copy link
Collaborator

teunbrand commented Jan 13, 2022

Strange, I get the following on Linux, where the textpath does seem responsive to vjust. My R version is 4.0.5 and linux distro is Ubuntu 20.04.3.

library(ggplot2)
library(geomtextpath)
library(patchwork)

df <- data.frame(
  x = seq(-0.5, 1.5, length.out = 10),                                       
  y = 1,
  label = "geom_textpath"
)

plot_vjust <- function(vjust) {
  ggplot(df, aes(x, y)) +
    geom_textpath(aes(label = label), vjust = vjust) +                       
    annotate(x = 2, y = 1, label = "geom_text", geom = "text", vjust = vjust) +
    ggtitle(paste0("vjust = ", vjust)) +                                     
    scale_x_continuous(limits = c(-1, 3))                                    
}

p = plot_vjust(-1) + plot_vjust(0) + plot_vjust(1) + plot_vjust(2) + plot_vjust(10) + plot_vjust(15)
p

Created on 2022-01-13 by the reprex package (v2.0.0)

But something seems off about vjust anyhow, it doesn't quite follow the vanilla text offsets. The text spacing looks weird as well. Update: text spacing issue seems device dependent (doesn't appear when using ragg).

@albert-ying
Copy link
Author

Hmm I'm on R 4.1.1, Ubuntuk 16.04.6.

Is there any system-related dependency that might be related to this?

@teunbrand
Copy link
Collaborator

teunbrand commented Jan 13, 2022

I'm not aware that we'd have such dependencies, we don't have any compiled code in geomtextpath.
I'm sorry we can't debug this ourselves, we don't have the same platform setup as you do. Would you mind checking for me what the following code returns in your case?

geomtextpath:::measure_label("AB\nCD")[[1]]
#>   glyph       ymin        xmin       xmid      xmax substring y_id
#> 1     A  0.1116536 0.009331597 0.06488715 0.1204427         1    2
#> 2     B  0.1116536 0.120442708 0.17599826 0.2315538         1    2
#> 3     C -0.1116536 0.000000000 0.06011285 0.1202257         1    3
#> 4     D -0.1116536 0.120442708 0.18055556 0.2406684         1    3

Created on 2022-01-13 by the reprex package (v2.0.1)

The exact values can vary slightly due to the fonts available (this was Arial on windows), but I'm interested to see if the ymin column varies, in which case we can rule out that textshaping is giving weird results.

@AllanCameron
Copy link
Owner

AllanCameron commented Jan 13, 2022

I have tracked down part of the problem. You'll notice that the geomtextpath position is wrong when vjust == 1. This is actually due to a bug in one of the dependencies (textshaping), which gives the wrong y position when vjust is exactly 1:

textshaping::shape_text('hello', vjust = 1)
#> $shape
#> # A tibble: 5 x 7
#>   glyph index metric_id string_id x_offset y_offset x_midpoint
#>   <int> <int>     <int>     <int>    <dbl>    <dbl>      <dbl>
#> 1     0    75         1         1     0           0       3.33
#> 2     1    72         1         1     6.67        0       3.33
#> 3     2    79         1         1    13.3         0       1.33
#> 4     3    79         1         1    16.0         0       1.33
#> 5     4    82         1         1    18.7         0       3.33
#> 
#> $metrics
#> # A tibble: 1 x 11
#>   string width height left_bearing right_bearing top_bearing bottom_bearing
#>   <chr>  <dbl>  <dbl>        <dbl>         <dbl>       <dbl>          <dbl>
#> 1 hello   25.4   24.3        0.797         0.438        2.27           2.41
#> # ... with 4 more variables: left_border <dbl>, top_border <dbl>, pen_x <dbl>,
#> #   pen_y <dbl>
textshaping::shape_text('hello', vjust = 1 + .Machine$double.eps)
#> $shape
#> # A tibble: 5 x 7
#>   glyph index metric_id string_id x_offset y_offset x_midpoint
#>   <int> <int>     <int>     <int>    <dbl>    <dbl>      <dbl>
#> 1     0    75         1         1     0       -10.9       3.33
#> 2     1    72         1         1     6.67    -10.9       3.33
#> 3     2    79         1         1    13.3     -10.9       1.33
#> 4     3    79         1         1    16.0     -10.9       1.33
#> 5     4    82         1         1    18.7     -10.9       3.33
#> 
#> $metrics
#> # A tibble: 1 x 11
#>   string width height left_bearing right_bearing top_bearing bottom_bearing
#>   <chr>  <dbl>  <dbl>        <dbl>         <dbl>       <dbl>          <dbl>
#> 1 hello   25.4   24.3        0.797         0.438        2.27           2.41
#> # ... with 4 more variables: left_border <dbl>, top_border <dbl>, pen_x <dbl>,
#> #   pen_y <dbl>
textshaping::shape_text('hello', vjust = 1 - .Machine$double.eps)
#> $shape
#> # A tibble: 5 x 7
#>   glyph index metric_id string_id x_offset y_offset x_midpoint
#>   <int> <int>     <int>     <int>    <dbl>    <dbl>      <dbl>
#> 1     0    75         1         1     0       -10.8       3.33
#> 2     1    72         1         1     6.67    -10.8       3.33
#> 3     2    79         1         1    13.3     -10.8       1.33
#> 4     3    79         1         1    16.0     -10.8       1.33
#> 5     4    82         1         1    18.7     -10.8       3.33
#> 
#> $metrics
#> # A tibble: 1 x 11
#>   string width height left_bearing right_bearing top_bearing bottom_bearing
#>   <chr>  <dbl>  <dbl>        <dbl>         <dbl>       <dbl>          <dbl>
#> 1 hello   25.4   24.3        0.797         0.438        2.27           2.41
#> # ... with 4 more variables: left_border <dbl>, top_border <dbl>, pen_x <dbl>,
#> #   pen_y <dbl>

I'm guessing that the bug is further-reaching on some systems?

Created on 2022-01-13 by the reprex package (v2.0.1)

@teunbrand
Copy link
Collaborator

teunbrand commented Jan 13, 2022

Yeah I defensively coded against that bug previously:

# Remedy for https://github.com/r-lib/systemfonts/issues/85
vjust[vjust == 1] <- 1 + .Machine$double.eps

But this I forgot to catch that bug when I refactored the code at some point. I've already included it in my local version, so it'll be fixed after the next PR. AFAIK, it shouldn't affect any other vjust values.

@albert-ying
Copy link
Author

Here is my output, the ymin indeed does not vary

geomtextpath:::measure_label("AB\nCD")[[1]]
  glyph       ymin        xmin       xmid      xmax substring y_id
1     A 0.07736545 0.008029514 0.06488715 0.1217448         1    2
2     B 0.07736545 0.121961806 0.17903646 0.2361111         1    2
3     C 0.07736545 0.000000000 0.05815972 0.1163194         1    2
4     D 0.07736545 0.116319444 0.18033854 0.2443576         1    2

@teunbrand
Copy link
Collaborator

Thanks! That narrows it down to a particular part of the code.

@teunbrand teunbrand mentioned this issue Jan 13, 2022
@AllanCameron
Copy link
Owner

AllanCameron commented Jan 14, 2022

Is this perhaps a problem with textshaping::shape_text not respecting vjust on some machines?

@albert-ying -In a fresh R session with your normal default settings, the following function will return the value of the y offset (in pixels) for different values of vjust, given default font values:

 f <- function(x) textshaping::shape_text('x', vjust = x)$shape$y_offset

For example, I get

f(0)
#> [1] 0

f(0.5)
#> [1] -5.421875

The symptoms you describe suggest that there is a problem with textshaping::shape_text (or the arguments passed to it) that would result in this function returning 0 (or some other low, but fixed, number) for any input.

If I am right about this, then it suggests that on your system, every glyph height is somehow being measured as 0. This would happen, for example, if you passed a font size of 0, or if your device height was 0. We know that the font size is not 0, since the x measurements are correct. The most likely cause I can see for this is that the device height is somehow being registered as 0 in the textshaping::shape_text function, but only if the f() function above returns 0 for all numeric inputs.

@albert-ying
Copy link
Author

Yes, you are right! @AllanCameron

[ins] r$>  f <- function(x) textshaping::shape_text('x', vjust = x)$shape$y_offset

[ins] r$> f(0)
[1] 0

[ins] r$> f(0.5)
[1] 0

@AllanCameron
Copy link
Owner

Thanks for trying that @albert-ying

From looking at the shape_text source code, it is really just an R wrapper round HarfBuzz, which is written in c++. I can't see anything in the shape_text source that should be affected by the system you are using, so my guess would be that there is some issue with HarfBuzz, which builds differently according to the system on which it is compiled. You will already have this installed on your computer, but I think that if you were able to update this package, maybe here, this may fix your problem.

@albert-ying
Copy link
Author

Thank you for helping and figuring out the root cause! Although I failed to link the new version of HarfBuzz to shape_text... But it is not the problem of geomtextpath anymore, I think. Still nice to know what is happing under the hood, thank you so much!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants