9

I have some R markdown that includes the following code:

```{r huff51, fig.show='hold', fig.cap='Design decisions connecting research purpose and outcomes [@huff_2009_designingresearchpublication p. 86].', echo=FALSE}

knitr::include_graphics('images/Huff-2009-fig5.1.svg')
```

When using bookdown to produce HTML output everything works as expected.

When using bookdown to produce PDF output I get an error saying ! LaTeX Error: Unknown graphics extension: .svg.

This is understandable as knitr uses Latex's \includegraphics{images/Huff-2009-fig5.1.svg} to include the image. So, it's not a bug per se.

Is there a better way to include the SVG image so I don't need to pre-process it into, say, a PDF or PNG?

Yihui Xie
  • 28,913
  • 23
  • 193
  • 419
Peter Smith
  • 143
  • 9

3 Answers3

4

An update to Yihui Xie 's answer in '22. The package you want is now rsvg and the code looks like:

show_fig <- function(f)
  {if (knitr::is_latex_output())
  {
    output = xfun::with_ext(f, 'pdf')
    rsvg::rsvg_pdf(xfun::with_ext(f,'svg'), file=output)
  } else {
    output = xfun::with_ext(f, 'svg')
  }
  knitr::include_graphics(output)
}

Then you can add inline code to your text with

`r show_fig("image_file_name_no_extension")`

knitr v 1.39, rsvg v 2.3.1

PeterK
  • 1,185
  • 1
  • 9
  • 23
  • Thanks! This is definitely a better answer than mine. I hope the OP could accept this one instead of mine. – Yihui Xie Aug 22 '22 at 17:14
  • This only kind of worked for me. It showed the SVG without any of the text in it: axes, title. – bshor Nov 23 '22 at 17:54
3

You can create a helper function to convert SVG to PDF. For example, if you have the system package rsvg-convert installed, you may use this function to include SVG graphics:

include_svg = function(path) {
  if (knitr::is_latex_output()) {
    output = xfun::with_ext(path, 'pdf')
    # you can compare the timestamp of pdf against svg to avoid conversion if necessary
    system2('rsvg-convert', c('-f', 'pdf', '-a', '-o', shQuote(c(output, path))))
  } else {
    output = path
  }
  knitr::include_graphics(output)
}

You may also consider R packages like magick (which is based on ImageMagick) to convert SVG to PDF.

Yihui Xie
  • 28,913
  • 23
  • 193
  • 419
  • 3
    To avoid converting files each time, I took the `rsvg-convert` out of the R code and into a `Makefile`, which I then invoke through a `bash` block in the RMarkdown. `Make` will only re-build if the `svg`s have changed since the `pdf`s were last built, so it's a nice way to speed the knitting execution time. – dsz Jun 10 '21 at 03:11
  • @dsz nice - do you also launch the knitting from make? – mitchus Aug 19 '22 at 07:07
  • 1
    @mitchus - no, I wasn't. And this was a long time ago! If you're considering doing this now (2022) you may want to take a look at the `targets` package, which is a native R alternative for make. – dsz Aug 22 '22 at 11:08
  • @dsz - `targets` looks interesting. Does it play nicely with external tools? For example, launch a latex/tikz generation of a figure to both svg and pdf? – mitchus Aug 23 '22 at 08:00
  • @mitchus - haven't investigated that far – dsz Aug 23 '22 at 13:12
0

For bookdown, I really don't like having PDF files on my websites. So I use this code:

if (knitr::is_html_output()) {
  structure("images/01-02.svg", class = c("knit_image_paths", "knit_asis"))
} else {
  # do something for PDF, e.g. an actual PDF file if you have one,
  # or even use Yihui's code in the other answer
  knitr::include_graphics("images/01-02.pdf")
}

It uses the SVG file for websites (i.e., HTML output).

It works perfectly for generating everything: website, gitbook, pdfbook and epub.

To prevent adding this code to every chunk in your bookdown project, add this to index.Rmd:

insert_graphic <- function(path, ...) {
  if (knitr::is_html_output() && grepl("[.]svg$", basename(path), ignore.case = TRUE)) {
    structure(path, class = c("knit_image_paths", "knit_asis"))
  } else {
    knitr::include_graphics(path, ...)
  }
}
MS Berends
  • 4,489
  • 1
  • 40
  • 53