Custom font not rendering when downloading ggplot while Shiny app is online

433 views Asked by At

I am developing this Shiny app and regularly updating it online. Naturally , I work on it locally and upload it every once in a while to check that everything is running smoothly. But I've hit this issue and I cannot seem to get past it.

I am using a custom (Google) font called Source Code Pro on the app, so that the entire app uses this font, graphs included. (For the sake of simplicity I deliberately haven't changed the font of the download button in the minimum reproducible example.) The font is sourced using the showtext package and by running font_add_google("Source Code Pro").

The graphs are produced with ggplot2, then I use ggplotly(...) to produce a plotly graph and finally plotlyOutput and renderPlotly do the rest. The graph itself is produced via a user-defined function called returnChart which returns a ggplot using as inputs the data (sample_data) and some colors (color_ids), and an additional parameter which indicates whether the output will be downloaded or not (download = F is default). This adds a legend to the graph (a legend is not needed in the graph shown on the app due to the plotly tooltip, but this naturally does not work when downloading the graph as a PNG).

When running the app locally and clicking the download button, the downloaded graph's font is Source Code Pro, but this is not the case when the app is run on the shinyapps.io servers. (Originally, it would also fail locally but using the Cairo package fixed that). I have looked into this solution but it does not seem to fix the issue. In any case, I don't think the issue lies with Ubuntu since the custom fonts do work for the plotly plots shown on the app.

Any ideas regarding this are greatly appreciated.

The online app can be found here.

The code runs in the same file in the exact same order as below, but I've split it for ease of reading.

Loading packages, initializing data:

library(ggplot2)
library(tidyverse)
library(showtext)
library(Cairo)
library(shiny)
library(plotly)
library(viridis)
library(PerformanceAnalytics)
options(shiny.usecairo=T)
font_add_google("Source Code Pro")

sample_return1 = c(0.0866, 0.2254, 0.0079, -0.0006, 0.0216,
                  -0.0210, -0.0992, 0.0219, 0.0430, -0.0388,
                  0.2031, 0.0254,-0.0293, 0.0227, 0.0809,
                  0.0129, 0.0024, 0.0361, 0.0047, 0.0292,
                  0.0228,-0.0067, 0.0056, 0.0116,-0.0087,
                  0.0662, 0.1083, 0.0269, 0.0479,-0.0294,
                  -0.0329, 0.0104,-0.1716, 0.1194, 0.0152,
                  0.0389,-0.0026,-0.0381, 0.0568,-0.0873,
                  0.0797, 0.0141, 0.0112, 0.0145,-0.0278,
                  0.0395,-0.0164, 0.1176,-0.0232, 0.0236,
                  0.0306, 0.0210, 0.0333,-0.1140,-0.0780,
                  -0.0490, 0.0270, 0.0200, 0.1140, 0.1110)

sample_return2 = c(0.0336, 0.0891,-0.0096, 0.0211, 0.0483,
                   -0.0869, 0.0575, 0.1442,-0.0911,-0.0413,
                   -0.1383, 0.1542, 0.1088,-0.0886, 0.1515,
                   -0.1050, 0.0244, 0.2322, 0.0269,-0.0011,
                   0.0065, 0.1001, 0.0962,-0.0750, 0.0594, 
                   0.0416, 0.0344,-0.0705,-0.0063,-0.1437,
                   -0.0054, 0.0131,-0.0147, 0.0331, 0.0303,
                   -0.0237,-0.0301, 0.0095,-0.0048, 0.0012, 
                   0.0141, 0.0285, 0.0106,-0.0319, 0.0722, 
                   0.0161,-0.0082,-0.0043, 0.0068,-0.0189,
                   -0.0065,-0.0400, 0.0136, 0.0125,-0.0076,
                   -0.0380, 0.0189, 0.0309,-0.0065, 0.0223,
                   0.0084,-0.0049,-0.0337, 0.0137, 0.0115,
                   -0.0093,-0.0025, 0.0025, 0.0042)




sample_dates1 = as.Date(c("2015-09-30", "2015-10-31", "2015-11-30", 
                         "2015-12-31", "2016-01-31", "2016-02-29", 
                         "2016-03-31", "2016-04-30", "2016-05-31", 
                         "2016-06-30", "2016-07-31", "2016-08-31", 
                         "2016-09-30", "2016-10-31", "2016-11-30", 
                         "2016-12-31", "2017-01-31", "2017-02-28", 
                         "2017-03-31", "2017-04-30", "2017-05-31",
                         "2017-06-30", "2017-07-31", "2017-08-31", 
                         "2017-09-30", "2017-10-31", "2017-11-30", 
                         "2017-12-31", "2018-01-31", "2018-02-28", 
                         "2018-03-31", "2018-04-30", "2018-05-31", 
                         "2018-06-30", "2018-07-31", "2018-08-31", 
                         "2018-09-30", "2018-10-31", "2018-11-30", 
                         "2018-12-31", "2019-01-31", "2019-02-28",
                         "2019-03-31", "2019-04-30", "2019-05-31", 
                         "2019-06-30", "2019-07-31", "2019-08-31", 
                         "2019-09-30", "2019-10-31", "2019-11-30", 
                         "2019-12-31", "2020-01-31", "2020-02-29", 
                         "2020-03-31", "2020-04-30", "2020-05-31", 
                         "2020-06-30", "2020-07-31", "2020-08-31"))

sample_dates2 = as.Date(c( "2015-01-31", "2015-02-28", "2015-03-31", 
                           "2015-04-30", "2015-05-31", "2015-06-30", 
                           "2015-07-31", "2015-08-31", "2015-09-30", 
                           "2015-10-31", "2015-11-30", "2015-12-31", 
                           "2016-01-31", "2016-02-29", "2016-03-31", 
                           "2016-04-30", "2016-05-31", "2016-06-30", 
                           "2016-07-31", "2016-08-31", "2016-09-30",
                           "2016-10-31", "2016-11-30", "2016-12-31", 
                           "2017-01-31", "2017-02-28", "2017-03-31", 
                           "2017-04-30", "2017-05-31", "2017-06-30", 
                           "2017-07-31", "2017-08-31", "2017-09-30", 
                           "2017-10-31", "2017-11-30", "2017-12-31", 
                           "2018-01-31", "2018-02-28", "2018-03-31", 
                           "2018-04-30", "2018-05-31", "2018-06-30",
                           "2018-07-31", "2018-08-31", "2018-09-30", 
                           "2018-10-31", "2018-11-30", "2018-12-31", 
                           "2019-01-31", "2019-02-28", "2019-03-31", 
                           "2019-04-30", "2019-05-31", "2019-06-30", 
                           "2019-07-31", "2019-08-31", "2019-09-30", 
                           "2019-10-31", "2019-11-30", "2019-12-31", 
                           "2020-01-31", "2020-02-29", "2020-03-31",
                           "2020-04-30", "2020-05-31", "2020-06-30", 
                           "2020-07-31", "2020-08-31", "2020-09-30"))

sample_name = c(rep("Quantamental", length(sample_return1)), 
                    rep("St. Gallen", length(sample_return2)))

sample_data = data.frame(Date = c(sample_dates1, sample_dates2),
                         Return = c(sample_return1, sample_return2),
                         Program = sample_name)

color_ids = viridis(2)

The returnChart function:

returnChart <- function(sample_data, color_ids, download = F){

p <- ggplot(data = sample_data,
            aes(x = Date, y = Return, color = Program, group = Program,
                text = paste0("Program: ", Program, "\n",
                              "Return: ", round(100*Return, 1), "%\n",
                              "Date: ", as.character(as.yearmon(Date)), "\n"))) +
  geom_line() +
  theme_bw() +
  scale_y_continuous(labels=scales::percent) +
  scale_color_manual(name = "Program", values = color_ids) +
  theme(axis.title.x = element_text(family = "Source Code Pro"),
        axis.title.y = element_text(family = "Source Code Pro"),
        axis.text.x = element_text(family = "Source Code Pro"),
        axis.text.y = element_text(family = "Source Code Pro"))

if (download == T){
  p <- p + 
    theme(legend.position = "bottom",
          legend.title = element_text(size = 8, family = "Source Code Pro"),
          legend.text = element_text(size = 6, family = "Source Code Pro")) +
    guides(color=guide_legend(nrow=ceiling(length(unique(sample_data$Program))/2),
                              byrow=TRUE, override.aes = list(size = 0.5)))
}

return(p)

}

The Shiny app:

ui <- shinyUI(
  mainPanel(
    plotlyOutput("returnPlot"),
    downloadButton("downloadReturn", "Download")
  )
)

server <- shinyServer(function(input, output, session){
  
  output$returnPlot <- renderPlotly({
      
      p <- returnChart(sample_data, color_ids)
      
      p <- p +
        theme(legend.position = "none") +
        theme(legend.title = element_blank())
      
      ggplotly(p, tooltip = c("text")) %>%
        config(modeBarButtonsToRemove = list("toImage"))
      
      
  })
  
  output$downloadReturn <- downloadHandler(
    filename = function(){
      "Return_Graph.png"
    },
    content = function(file) {
      
      png(file, width = 16, height = 9, units = 'cm', res = 300, 
          type = "cairo")
      
      print(returnChart(sample_data, color_ids, download = T))
      
      dev.off()
    },
    contentType = 'image/png'
  )
  
})


shinyApp(server = server, ui = ui)
0

There are 0 answers