jimhester / knitrBootstrap

A framework to create bootstrap styled HTML reports from knitr Rmarkdown.
Other
274 stars 61 forks source link

Display figure captions #179

Closed humburg closed 9 years ago

humburg commented 9 years ago

Using PR #68 I'm trying to get figure captions to display. With _rmarkdown::htmldocument setting the option fig_caption: true ensures that captions are displayed below figures in the output. However, when using _knitrBootstrap::bootstrapdocument this option seems to be ignored. Here is an example Rmd file:


---
title: Caption test 
output:
  knitrBootstrap::bootstrap_document:
    fig_caption: true 

---

Some text.

```{r fig, fig.cap="**Figure:** caption"}
plot(1:10)

Other text.


The relevant part of the HTML output looks like this (regardless whether _fig_caption_ is _true_ or _false_):

``` html
<p>Some text.</p>
<div class="row">
  <div class="panel panel-primary">
    <button class="btn btn-default btn-xs btn-primary">
      <span class="glyphicon glyphicon-chevron-left"></span>
    </button>
    <pre >
      <code class="source r">plot(1:10)</code>
    </pre>
  </div>
  <div class="row">
    <div class="col-md-offset-3 col-md-6">
      <a href="#" class="thumbnail"><img src="test_files/figure-html/fig-1.png" alt="Figure: caption" /> </a>
    </div>
  </div>
</div>
<p>Other text.</p>

As you can see the caption is added as alt text, which is great, but I'd like to have this included as text in the div that contains the figure as well. Is there a way to achieve this with knitrBootstrap? For comparison the output produced by _rmarkdown::htmldocument is below.

<p>Some text.</p>
<pre class="r">
  <code>plot(1:10)</code>
</pre>
<div class="figure">
  <img src="test_files/figure-html/fig-1.png" alt="Figure: caption" />
  <p class="caption"><strong>Figure:</strong> caption</p>
</div>
<p>Other text.</p>
crsh commented 9 years ago

I agree this would be very nice to have.

humburg commented 9 years ago

I had a closer look at the code for this and ran some tests. I think I have a better understanding of the underlying issues now but I'm unsure how to best resolve them.

As I see it the problem is the way the output generated by knitrBootstrap is interpreted by pandoc. To get a figure with caption the corresponding markdown has to appear in a paragraph by itself (see pandoc documentation) but knitrBootstrap generates additional HTML in the same paragraph. As a result pandoc produces an inline figure instead.

Of course it is pretty straightforward to just add some additional line breaks around the ![]() element to ensure the figure caption is generated. Unfortunately this doesn't play nice with the thumbnail code because pandoc ends up wrapping <a href="#" class="thumbnail"> in a p element, which breaks whole thing. Adding the extra line breaks works fine when using bootstrap.thumbnail=FALSE.

It would be nice to have both though. One option would be to just generate the HTML for the figure directly to bypass any pandoc related issues but that isn't very elegant. It will be harder to maintain if pandoc's behaviour (and expectations of what the output should be) changes in the future (not sure how likely that is). The other problem is that it will make the generated markdown file useless for conversion to other formats but I think that is already the case because of the way code blocks are handled so this may be acceptable.

@jimhester, I'd be happy to contribute a pull request that addresses this in some way, either along the lines discussed above or some better solution if one is suggested. It would be good if we could agree on what approach should be implemented beforehand though. Based on the discussion above my proposal would be to basically implement both solutions, using the (knitr generated) markdown with extra line breaks if no thumbnails are used and generate the HTML directly otherwise.

jimhester commented 9 years ago

@humburg I haven't looked at this as recently as you, but I think what you propose is correct.

Embedded html is part of the markdown "spec", so generating the html in there will technically be fine.

I think generating the HTML directly when bootstrap.thumbnail = TRUE and relying on pandoc's caption generation when bootstrap.thumbnail = FALSE is the way to go here.

bootstrap.thumbnail = TRUE implies we are doing our own thing with the thumbnails so if the behavior ends up deviating from pandoc in the future that should be ok, (but we can always change it to match if needed).

You can take a look at knitr:::hook_plot_md(), knitr:::hook_plot_md_base() and knitr:::hook_plot_html() if you need some examples of doing the full HTML generation.

@humburg I also granted you write permissions for this repo, feel free to continue to send pull requests if you'd like (and I can review them), but you can also commit directly to it as well. Thank you for your interest in this project!

humburg commented 9 years ago

@jimhester Thanks, I've created a branch to work on this. I think this basically works as long as there is only one plot per chunk. For multiple plots it is following the logic used by knitr to not ad captions if the plots are held until the end of the chunk but to add them otherwise. Not sure whether that is the best behaviour but at least it is consistent.

I've created a Gist with a simple file for testing here: https://gist.github.com/humburg/f39efe7597d4004a92a3

humburg commented 9 years ago

I merged the changes into #68. Captions should now be available for individual figures whether they use the thumbnails or not. When using thumbnails the popup view of the image will also have the caption. Chunks that produce multiple figures with fig.show="hold" won't get any captions.

jimhester commented 9 years ago

Looks good! Thanks for working on this!