rmarkdown icon indicating copy to clipboard operation
rmarkdown copied to clipboard

Knitting to pdf crashes with `out.width=` specification in the code chunk

Open oolonek opened this issue 2 years ago • 8 comments

---
title: 'Min example'
date: "`r format(Sys.time(), '%d %B, %Y')`"
output:
  bookdown::html_document2:
    df_print: paged
    toc: TRUE
  bookdown::word_document2: default
  bookdown::pdf_document2:
    toc: no
    fig_caption: yes
    number_sections: true
    keep_tex: yes
always_allow_html: yes
latex_engine: pdflatex
---

```{r message=FALSE, warning = FALSE}
library(knitr)
library(rmarkdown)
library(plotly)
```

```{r, echo=FALSE, fig.cap="plotly_test", fig.topcaption=TRUE, warning=F, out.width="100%", out.height="100%"}
library(plotly)
p <- plot_ly(economics, x = ~date, y = ~unemploy / pop)
p
```


```{r, echo=TRUE}
xfun::session_info('rmarkdown')
```

output from xfun::session_info('bookdown')

> xfun::session_info('rmarkdown')
R version 4.2.2 (2022-10-31)
Platform: x86_64-apple-darwin17.0 (64-bit)
Running under: macOS Big Sur ... 10.16

Locale: en_US.UTF-8 / en_US.UTF-8 / en_US.UTF-8 / C / en_US.UTF-8 / en_US.UTF-8

Package version:
  base64enc_0.1.3 bslib_0.4.2     cachem_1.0.7    cli_3.6.0      
  digest_0.6.31   ellipsis_0.3.2  evaluate_0.20   fastmap_1.1.1  
  fs_1.6.1        glue_1.6.2      graphics_4.2.2  grDevices_4.2.2
  highr_0.10      htmltools_0.5.4 jquerylib_0.1.4 jsonlite_1.8.4 
  knitr_1.42      lifecycle_1.0.3 magrittr_2.0.3  memoise_2.0.1  
  methods_4.2.2   mime_0.12       R6_2.5.1        rappdirs_0.3.3 
  rlang_1.0.6     rmarkdown_2.20  sass_0.4.5      stats_4.2.2    
  stringi_1.7.12  stringr_1.5.0   tinytex_0.44    tools_4.2.2    
  utils_4.2.2     vctrs_0.5.2     xfun_0.37       yaml_2.3.7     

Pandoc version: 3.1

When running the previous minimal example with rmarkdown::render('min_ex.rmd', output_dir = '../docs/report/', output_format = 'bookdown::pdf_document2', knit_root_dir = getwd()) I get the following error :

processing file: min_ex.rmd
  |..............................                      |  57% (unnamed-chunk-2)Quitting from lines 45-48 (min_ex.rmd) 
Error in s$close() : attempt to apply non-function
  

If I change the r chunk specification from ```{r, echo=FALSE, fig.cap="plotly_test", fig.topcaption=TRUE, warning=F, out.width="100%", out.height="100%"}``` to ```{r, echo=FALSE, fig.cap="plotly_test", fig.topcaption=TRUE, warning=F}``` then the output is correctly knitted.

No problem when knitting to html.

oolonek avatar Mar 03 '23 11:03 oolonek

Thanks I can reproduce and the traceback of the error is this

Error in `s$close()`:
! tentative d'appliquer un objet qui n'est pas une fonction
Backtrace:
  1. rmarkdown::render("C:/Users/chris/Documents/test.Rmd", encoding = "UTF-8")
  2. knitr::knit(knit_input, knit_output, envir = envir, quiet = quiet)
  3. knitr:::process_file(text, output)
  6. knitr:::process_group.block(group)
  7. knitr:::call_block(x)
     ...
 27. chromote:::synchronize(p, loop = private$child_loop)
 31. base::tryCatch(...)
 32. base (local) tryCatchList(expr, classes, parentenv, handlers)
 33. base (local) tryCatchOne(expr, names, parentenv, handlers[[1L]])
 34. base (local) doTryCatch(return(expr), name, parentenv, handler)
Exécution arrêtée

This seems to be an issue with chromote as it is used by webshot2 which is called by knitr to take a screenshot of the HTMLWIDGET for including into the PDF.

i'll see if something has changed there or how it interfere with the options

cderv avatar Mar 03 '23 13:03 cderv

Simplified example showing this is not bookdown related

---
title: "Min example"
date: "`r format(Sys.time(), '%d %B, %Y')`"
output:
  pdf_document: default
  html_document:
    df_print: paged
---

```{r message=FALSE, warning = FALSE}
library(plotly)
```

```{r}
#| out.height="100%"
library(plotly)
p <- plot_ly(economics, x = ~date, y = ~unemploy / pop)
p
```

Full error

Error in `s$close()`:
! attempt to apply non-function
---
Backtrace:
     ▆
  1. └─rmarkdown::render("test.Rmd")
  2.   └─knitr::knit(knit_input, knit_output, envir = envir, quiet = quiet)
  3.     └─knitr:::process_file(text, output)
  4.       ├─base::withCallingHandlers(...)
  5.       ├─base::withCallingHandlers(...)
  6.       ├─knitr:::process_group(group)
  7.       └─knitr:::process_group.block(group)
  8.         └─knitr:::call_block(x)
  9.           └─knitr:::block_exec(params)
 10.             └─knitr:::eng_r(options)
 11.               ├─knitr:::in_input_dir(...)
 12.               │ └─knitr:::in_dir(input_dir(), expr)
 13.               └─knitr (local) evaluate(...)
 14.                 └─evaluate::evaluate(...)
 15.                   └─evaluate:::evaluate_call(...)
 16.                     ├─base (local) handle(...)
 17.                     ├─base::withCallingHandlers(...)
 18.                     ├─base::withVisible(value_fun(ev$value, ev$visible))
 19.                     └─knitr (local) value_fun(ev$value, ev$visible)
 20.                       └─knitr (local) fun(x, options = options)
 21.                         ├─base::withVisible(knit_print(x, ...))
 22.                         └─knitr::knit_print(x, ...)
 23.                           └─knitr:::html_screenshot(x)
 24.                             ├─knitr:::in_dir(...)
 25.                             ├─base::do.call(...)
 26.                             └─webshot2 (local) `<fn>`(...)
 27.                               └─cm$wait_for(p)
 28.                                 └─chromote:::synchronize(p, loop = private$child_loop)
 29.                                   ├─promises::with_promise_domain(...)
 30.                                   │ └─domain$wrapSync(expr)
 31.                                   │   └─base::force(expr)
 32.                                   └─base::tryCatch(...)
 33.                                     └─base (local) tryCatchList(expr, classes, parentenv, handlers)
 34.                                       └─base (local) tryCatchOne(expr, names, parentenv, handlers[[1L]])
 35.                                         └─base (local) doTryCatch(return(expr), name, parentenv, handler)

cderv avatar Mar 03 '23 13:03 cderv

So this is indeed an issue with webshot2 and chromote as it does not seem they accept width and height in %.

Currently when provided as knitr option on the chunk, we pass out.width and out.height (user specified or internally computed) as value to webshot2::webshot() - see

Here is a minimal reprex:

> webshot2::webshot("https://github.com/rstudio/shiny", vwidth = 468, vheight = "100%")
Error in s$close() : attempt to apply non-function

I don't think it can take % really, but they should at least error nicely if this is the case.

On our side, I wonder if we can pass some pixel value when a % value is passed to out.(witdh|height) options. @yihui in case you have an idea...

thanks for the report @oolonek. As a workaround, it seems older webshot 📦 (which is using deprecated phantomjs tool) accepts % so you can default to this in the meantime setting this in setup chunk

knitr::opts_chunk$set(webshot = "webshot")

or webshot = "webshot" as chunk option.

Hope it helps

cderv avatar Mar 03 '23 13:03 cderv

Thanks for the workaround @cderv ! Will watch here for the longer term solution.

oolonek avatar Mar 03 '23 15:03 oolonek

Thanks @cderv. I run into the same issue

  • with Quarto
  • with any other htmlwidget (leaflet, ggiraph, ...)
  • by setting fig-asp (instead of out-width in percentage)

See reprex below

---
title: Test
format:
  pdf: default
fig-width: 6
fig-asp: 0.618
---

## Plots

```{r}
library(leaflet)
leaflet() %>%
  addTiles()
```

Workaround with webshot = "webshot" works also in this case.

retodomax avatar Mar 20 '24 08:03 retodomax

Thanks for the report. This is still an issue with webshot2 I believe that does not handle the resulting dimensions.

It seems not to handle the decimal value

webshot2::webshot("https://github.com/rstudio/shiny", vwidth = 6*72, vheight = 6*72*0.618)
#> Error in s$close(): tentative d'appliquer un objet qui n'est pas une fonction

(6 is your fig.width, 72 is the default DPI 0.618 is the aspect ratio -> this is used to calculate the height))

As I said above, maybe we can do something in knitr to pass transformed argument, but we would need to round the number here.

It seems an issue in webshot2 really. 🤔

cderv avatar Mar 20 '24 09:03 cderv