diff --git a/NEWS.md b/NEWS.md index 2bf24ce42d..9c1449882b 100644 --- a/NEWS.md +++ b/NEWS.md @@ -4,6 +4,8 @@ - The chunk option `collapse = TRUE` now works as expected when the chunk option `attr.*` or `class.*` is provided. By this change, The chunk option `collapse = TRUE` forces `attr.*` and `class.*` be `NULL` except for the chunk options `attr.source` and `class.source` (thanks, @aosavi @cderv @atusy, #1902 #1906). +- Captions disappeared when a chunk generates multiple figures continuously (thanks, @atusy, #1760) + # CHANGES IN knitr VERSION 1.30 ## NEW FEATURES diff --git a/R/hooks-md.R b/R/hooks-md.R index 3a45fc3090..0fbc360ab1 100644 --- a/R/hooks-md.R +++ b/R/hooks-md.R @@ -3,25 +3,26 @@ hook_plot_md = function(x, options) { # if not using R Markdown v2 or output is HTML, just return v1 output if (is.null(to <- pandoc_to()) || is_html_output(to)) - return(hook_plot_md_base(x, options)) + return(append_blank_lines(hook_plot_md_base(x, options), options)) if ((options$fig.show == 'animate' || is_tikz_dev(options)) && is_latex_output()) return(hook_plot_tex(x, options)) + office_output = to %in% c('docx', 'pptx', 'rtf', 'odt') if (need_special_plot_hook(options)) { if (is_latex_output()) { # Pandoc < 1.13 does not support \caption[]{} so suppress short caption if (is.null(options$fig.scap)) options$fig.scap = NA - return(hook_plot_tex(x, options)) + return(append_blank_lines(hook_plot_tex(x, options), options)) } if (office_output) { if (options$fig.align != 'default') { warning('Chunk options fig.align is not supported for ', to, ' output') options$fig.align = 'default' } - return(hook_plot_md_pandoc(x, options)) + return(append_blank_lines(hook_plot_md_pandoc(x, options), options)) } } - hook_plot_md_base(x, options) + append_blank_lines(hook_plot_md_base(x, options), options) } # decide if the markdown plot hook is not enough and needs special hooks like @@ -114,6 +115,16 @@ css_text_align = function(align) { if (align == 'default') '' else sprintf(' style="text-align: %s"', align) } +append_blank_lines = function(x, options) { + if ( + (!identical(options$echo, FALSE) && options$fig.show != 'hold') || + is.null(options$fig.cap) || options$fig.num == options$fig.cur + ) { + return(x) + } + paste0(x, "\n\n") +} + # turn a class string "a b" to c(".a", ".b") for Pandoc fenced code blocks block_class = function(x) { if (length(x) > 0) gsub('^[.]*', '.', unlist(strsplit(x, '\\s+'))) diff --git a/tests/testit/test-hooks-md.R b/tests/testit/test-hooks-md.R index 3e186b010a..afbff9570f 100644 --- a/tests/testit/test-hooks-md.R +++ b/tests/testit/test-hooks-md.R @@ -67,8 +67,15 @@ x = "1.png" w = h = 1 ex = "style='margin: 0;'" cap = "foo" -opt <- function(w = NULL, h =NULL, ex = NULL, cap = NULL, show = 'asis', ...) { - list(out.width = w, out.height = h, out.extra = ex, fig.cap = cap, fig.show = show, ...) +align = 'left' +link = 'https://example.com' +opt <- function( + w = NULL, h =NULL, ex = NULL, align = 'default', cap = NULL, show = 'asis', ... +) { + list( + out.width = w, out.height = h, out.extra = ex, + fig.align = align, fig.cap = cap, fig.show = show,... + ) } assert("Include a plot by pandoc md", { @@ -80,3 +87,67 @@ assert("Include a plot by pandoc md", { (hook_plot_md_pandoc(x, opt(w = w, cap = cap, ex = ex)) %==% sprintf("![%s](1.png){width=%s %s}", cap, w, ex)) }) + +hook = hook_plot_md_base +assert("Include a plot in variety of formats with hook_plot_md_base", { + # width, height, and extra are null, and align is default + (hook(x, opt()) %==% "![](1.png)") + (hook(x, opt(cap = cap)) %==% sprintf("![%s](1.png)", cap)) + (hook(x, opt(fig.link = link)) %==% sprintf("[![](1.png)](%s)", link)) + opts_knit$set(rmarkdown.pandoc.to = 'latex') + (hook(x, opt(cap = '')) %==% "![](1.png) ") + opts_knit$set(rmarkdown.pandoc.to = 'html') + (hook(x, opt(cap = '')) %==% "![](1.png)") + # html output with caption or width + (hook(x, opt(align = align, cap = cap)) %==% + sprintf('
\n%s\n

%s

\n
', align, cap, cap)) + ## add link + (hook(x, opt(w = w, cap = cap, fig.link = link)) %==% + sprintf('
\n%s\n

%s

\n
', link, cap, w, cap)) + ## fig.caption is TRUE + (hook(x, opt(w = w, cap = cap, fig.topcaption = TRUE)) %==% + sprintf('
\n

%s

%s
', cap, cap, w)) + ## plot1 is FALSE + (hook(x, opt(w = w, cap = cap, show = 'hold', fig.cur = 2, fig.num = 2)) %==% + sprintf('%s\n

%s

\n', cap, w, cap)) + ## plot2 is FALSE + (hook(x, opt(w = w, cap = cap, show = 'hold', fig.cur = 1, fig.num = 2)) %==% + sprintf('
\n%s', cap, w, cap)) + # else + opts_knit$restore() + (hook(x, opt(align = 'center')) %==% '') +}) + +hook <- hook_plot_md +assert('Check if hook_plot_md passes arguments to sub-functions', { + opts_knit$set(rmarkdown.pandoc.to = 'html') + (hook(x, opt()) %==% hook_plot_md_base(x, opt())) + + opts_knit$set(rmarkdown.pandoc.to = 'latex') + o = opt(w = w) + (hook(x, o) %==% hook_plot_tex(x, o)) + + opts_knit$set(rmarkdown.pandoc.to = 'docx') + (hook(x, o) %==% hook_plot_md_pandoc(x, o)) + + opts_knit$set(rmarkdown.pandoc.to = 'markdown') + (hook(x, opt()) %==% hook_plot_md_base(x, opt())) + (hook(x, o) %==% hook_plot_md_base(x, o)) +}) + +assert('Conditionally append blank lines to figures so that figure captions are displayed correctly', { + # Append when echo is FALSE or fig.show is hold and the current figure is not the last one + (append_blank_lines('a', list(echo = FALSE, fig.cap = cap, fig.num = 3, fig.cur = 1)) %==% 'a\n\n') + (append_blank_lines('a', list(fig.show = 'hold', fig.cap = cap, fig.num = 3, fig.cur = 1)) %==% 'a\n\n') + + # Do not append when + ## echo is not FALSE + (append_blank_lines('', list(echo = TRUE)) %==% '') + ## fig.show is not hold + (append_blank_lines('', list(echo = FALSE, fig.show = 'asis')) %==% '') + ## fig.cap is NULL + (append_blank_lines('', list(echo = FALSE, fig.cap = NULL)) %==% '') + ## fig.num is equal to fig.cur + (append_blank_lines('', list(echo = FALSE, fig.cap = cap, fig.num = 1, fig.cur = 1)) %==% '') +}) +