3
votes

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)
```
2
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 that might be of help.Bryan Hanson
It's certainly related and I'll likely consider it if nothing else avails. Thanks!r2evans

2 Answers

2
votes

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")
```
0
votes

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()
```