Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@

## BUG FIXES

- Figure captions are no longer dropped when a single chunk produces multiple captioned figures for Markdown-based output (e.g., HTML or PDF via Pandoc). Previously, consecutive images were emitted in one paragraph (`![cap1](a) ![cap2](b)`), which Pandoc treats as inline images and renders without captions. knitr now inserts a blank line after each captioned figure except the last, so Pandoc emits a separate figure (with caption) for each image (thanks, @atusy, #2032, #1524, #1760).

- knitr now emits a warning when a code chunk opens a new graphics device (e.g., via `dev.new()`), because plots drawn on such devices cannot be captured by knitr and will silently fail to render in the output (thanks, @Higgs32584, #2355).

- `include_graphics()` now converts absolute paths to paths relative to the output directory of the rendered document (communicated by **rmarkdown** >= 2.32 via `opts_knit$get('rmarkdown.output_dir')`) instead of knitr's working directory (the input directory), which fixes broken image paths when the input and output directories differ (e.g., rendering to a different `output_dir`). The existence check (when `error = TRUE`) also uses the original path so that it no longer reports false negatives when `root.dir` differs from the output directory (thanks, @naikymen @jameelalsalam @cderv, #2171, r-lib/pkgdown#2334).
Expand Down
17 changes: 15 additions & 2 deletions R/hooks-md.R
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ hook_plot_md_base = function(x, options) {
res = sprintf('![%s](%s)', cap, x2)
if (!is.null(lnk) && !is.na(lnk)) res = sprintf('[%s](%s)', res, lnk)
res = paste0(res, if (nocap) '<!-- -->' else '', if (is_latex_output()) ' ' else '')
return(res)
return(sep_captioned_fig(res, cap, options))
}
add_link = function(x) {
if (is.null(lnk) || is.na(lnk)) return(x)
Expand Down Expand Up @@ -131,7 +131,20 @@ hook_plot_md_pandoc = function(x, options) {
)
if (at != '') at = paste0('{', at, '}')

sprintf('![%s](%s%s)%s', cap, base, .upload.url(x), at)
sep_captioned_fig(sprintf('![%s](%s%s)%s', cap, base, .upload.url(x), at), cap, options)
}

# When a chunk generates multiple figures that carry captions, the plot hook is
# called once per figure and the bare `![cap](path)` images would otherwise be
# concatenated into a single paragraph, e.g. `![cap1](a) ![cap2](b)`. Pandoc
# treats several images in one paragraph as inline images and drops the captions
# (no figure environment / <figcaption>). Append a blank line after each
# captioned figure except the last so that Pandoc emits a figure per image.
# See https://github.com/yihui/knitr/issues/2032 and #1524.
sep_captioned_fig = function(res, cap, options) {
if (cap != '' && (options$fig.cur %n% 1L) < (options$fig.num %n% 1L))
res = paste0(res, '\n\n')
res
}

css_align = function(align) {
Expand Down
11 changes: 11 additions & 0 deletions tests/testit/test-hooks-md.R
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,17 @@ assert("Include a plot by pandoc md", {
sprintf("![%s](1.png){width=%s %s}", cap, w, ex))
})

assert("captioned figures in a multi-figure chunk are separated by a blank line (#2032)", {
fig = function(cur, num, cap = NULL) opt(cap = cap, fig.cur = cur, fig.num = num)
# a captioned figure that is not the last one gets a trailing blank line
(hook_plot_md_pandoc(x, fig(1, 2, cap)) %==% sprintf("![%s](1.png)\n\n", cap))
# the last figure and single-figure chunks do not
(hook_plot_md_pandoc(x, fig(2, 2, cap)) %==% sprintf("![%s](1.png)", cap))
(hook_plot_md_pandoc(x, fig(1, 1, cap)) %==% sprintf("![%s](1.png)", cap))
# figures without a caption are left inline (Pandoc keeps them as inline images)
(hook_plot_md_pandoc(x, fig(1, 2)) %==% "![](1.png)")
})

assert('empty alt text is preserved and NA alt is discarded', {
(hook_plot_md(x, opts_chunk$merge(list(fig.alt = ''))) %==% '<img src="1.png" alt="" />')
(hook_plot_md(x, opts_chunk$merge(list(fig.alt = NA, out.width = '100'))) %==% '<img src="1.png" width="100" />')
Expand Down
Loading