4

How can I programmatically set a figure caption in a knitr hook?

I'd like to set the figure caption, if not explicitly defined, to the chunk label. I've read the knitr docs on options, options, and hooks, and though I think I understand the mechanisms at play, I can't get it to work.

My use-case that perhaps justifies this behavior: my work-flow recently adapted to start my data and visualization exploration in Rmd files. I'll use chunks for cleaning, subsetting, etc, and then a sample chunk for each visualization. This is quick and dirty, meaning minimal markdown. When I look over the report (typically rendered into PDF), I'll look at a figure and want to go straight to the source for it. Though text before/after the figure can provide insight, due to LaTeX figure rules it is not a sure thing. Counting figure numbers is feasible, but not "easy" (and becomes problematic with many figures). Captions are always with the figure, so it'd be great if I can default to filling the caption with the chunk label. (Yes, it's a little lazy of me.)

The MWE is below.

The hook code ran just fine; the returned strings in the hook appeared correctly. However, the figure caption did not change. Exception: when there is a chunk with an undefined fig.cap, all subsequent chunks have their caption set to the first un-captioned chunk name; this doesn't surprise me due to the global nature of opts_chunk, so that's out.

I suspect it might be related to "output hooks" vice "chunk hooks," but this really is a per-chunk thing and I do not want to modify the plot, just set the caption.

MWE:

---
title: "Document Title"
author: "My Name"
output:
  pdf_document:
    fig_caption: yes
---

# Header

```{r setup}
knit_hooks$set(autocap = function(before, options, envir) {
    if (before) {
        if (is.null(options$fig.cap)) {
            options$fig.cap <- options$label
            knitr::opts_current$set(fig.cap = options$label)
            knitr::opts_chunk$set(fig.cap = options$label)   # wrong!
            paste('Set: `', options$label, '`, `',
                  knitr::opts_current$get('fig.cap'), '`', sep = '')
        } else {
            paste('Kept: `', options$fig.cap, '`', sep = '')
        }
    }
})
opts_chunk$set(autocap = TRUE)
```

## No Plot

```{r textOnly}
1+1
```

## Caption Already Set

```{r someplot, fig.cap='someplot caption'}
plot(0)
```

## Caption Not Set

```{r anotherPlot}
plot(1)
```
Daniel Widdis
  • 8,424
  • 13
  • 41
  • 63
r2evans
  • 141,215
  • 6
  • 77
  • 149
  • I came here from your question on the `knitr` list. Just a couple of options that might serve as alternatives/extensions to what you need. Take a look at the `kfigr` package which handles figuring numbering in markdown. And also I posted a related question on [tex](http://tex.stackexchange.com/q/238710/6580) that might be of help. – Bryan Hanson Jul 17 '15 at 13:21
  • It's certainly related and I'll likely consider it if nothing else avails. Thanks! – r2evans Jul 17 '15 at 15:16

2 Answers2

3

Is it ok like this ? I simply modify the knitr internal function .img.cap function which can be found here.

```{r}
.img.cap = function(options) {
  if(is.null(options$fig.cap)) options$label else options$fig.cap
}
assignInNamespace(".img.cap", .img.cap, ns="knitr")
```
Stéphane Laurent
  • 75,186
  • 15
  • 119
  • 225
  • Yes, thank you! I'm not particular thrilled that it's modifying `knitr` internals, but I haven't found any other solution, and it's intended for development and not deployment so a small hack is acceptable. Great! – r2evans Aug 13 '15 at 15:45
1

Does it help ?

```{r}
library(knitr)
knit_hooks$set(htmlcap = function(before, options, envir) {
  if(!before) {
    caption <- ifelse(is.character(options$htmlcap), options$htmlcap, options$label)
    paste('<p class="caption">', caption, "</p>", sep="")
  }
})
```

```{r Hello, htmlcap=TRUE}
library(ggplot2)
ggplot(diamonds,aes(price,carat)) + geom_point()
```

```{r, htmlcap="Hello again"}
ggplot(diamonds,aes(price,carat)) + geom_point()
```
Stéphane Laurent
  • 75,186
  • 15
  • 119
  • 225
  • Thanks, this meets some of the *intent* (of adding the chunk label as text immediately after the image). I'm really trying to get it as the figure caption itself, though. If I don't find something better, I'll accept this answer. Thanks for the suggestion! (My next task will be to make a default behavior such that figure chunks with no captions will have it automatically labeled.) – r2evans Jul 19 '15 at 20:59