diff --git a/news/changelog-1.3.md b/news/changelog-1.3.md index b31b0f88e5c..9c0316f1d6c 100644 --- a/news/changelog-1.3.md +++ b/news/changelog-1.3.md @@ -101,6 +101,10 @@ - Improve the performance of extremely large documents with margin elements by improving the efficiency of positioning the elements. +## Docx Format + +- Ensure that the figure caption and the figure itself is laid out as consecutive paragraphs. ([#4004](https://github.com/quarto-dev/quarto-cli/issues/4004)) + ## Listings - Listings now support `template-params`, which will be passed to custom EJS templates in a variable called `templateParams` when a listing is rendered. diff --git a/src/resources/filters/layout/wp.lua b/src/resources/filters/layout/wp.lua index e54637378be..3bd2b93b706 100644 --- a/src/resources/filters/layout/wp.lua +++ b/src/resources/filters/layout/wp.lua @@ -8,46 +8,40 @@ function tableWpPanel(divEl, layout, caption) }) end - function wpDivFigure(div) - - -- options - options = { - pageWidth = wpPageWidth(), - } - -- determine divCaption handler (always left-align) - local divCaption = nil - if _quarto.format.isDocxOutput() then - divCaption = docxDivCaption - elseif _quarto.format.isOdtOutput() then - divCaption = odtDivCaption - end - if divCaption then - options.divCaption = function(el, align) return divCaption(el, "left") end - end - - -- get alignment local align = figAlignAttribute(div) + local capLoc = capLocation("fig", "bottom") + + local captionPara = div.content[2]:clone() + local figurePara = div.content[1]:clone() + + -- Switch to modern alignment directives for OOXML + local wordAligns = { + left = "start", + right = "end", + center = "center" + } + + -- Generate a raw OOXML string that sets paragraph properties + local docxAlign = "" - -- create the row/cell for the figure - local row = pandoc.List() - local cell = div:clone() - transferImageWidthToCell(div, cell) - row:insert(tableCellContent(cell, align, options)) - - -- make the table - local figureTable = pandoc.SimpleTable( - pandoc.List(), -- caption - { layoutTableAlign(align) }, - { 1 }, -- full width - pandoc.List(), -- no headers - { row } -- figure - ) - - -- return it - return pandoc.utils.from_simple_table(figureTable) - + captionPara.content:insert(1, pandoc.RawInline("openxml", docxAlign)) + + if capLoc == "top" then + + return pandoc.Div({ + captionPara, + figurePara + }) + + else + -- "bottom" or default + return pandoc.Div({ + figurePara, + captionPara + }) + end end function wpPageWidth() diff --git a/src/resources/filters/quarto-pre/options.lua b/src/resources/filters/quarto-pre/options.lua index 0a5a986bbe5..b4fd252a79d 100644 --- a/src/resources/filters/quarto-pre/options.lua +++ b/src/resources/filters/quarto-pre/options.lua @@ -30,6 +30,15 @@ function var(name, def) end end +function capLocation(scope, default) + local loc = option(scope .. '-cap-location', option('cap-location', nil)) + if loc ~= nil then + return inlinesToString(loc) + else + return default + end +end + function parseOption(name, options, def) local keys = split(name, ".") diff --git a/tests/docs/smoke-all/2023/03/01/4004.qmd b/tests/docs/smoke-all/2023/03/01/4004.qmd new file mode 100644 index 00000000000..880295753d7 --- /dev/null +++ b/tests/docs/smoke-all/2023/03/01/4004.qmd @@ -0,0 +1,48 @@ +--- +title: "issue 4004" +format: + docx: + fig-cap-location: top +_quarto: + tests: + docx: + ensureDocxRegexMatches: + - [ + Figure 1.*?.*?, + Figure 2.*?.*?, + Figure 3.*?.*? + ] +--- + +Lets's see @fig-01. And we'll use different alignments. + +```{r} +#| label: fig-01 +#| fig-cap: The figure caption. +#| fig-align: left +plot(1:10) +``` + +```{r} +#| label: fig-02 +#| fig-cap: The figure caption. +#| fig-align: right +plot(1:10) +``` + +```{r} +#| label: fig-03 +#| fig-cap: The figure caption. +#| fig-align: center +plot(1:10) +``` + +Compare with tables (caption implementation not changed for this issue) + +```{r} +#| label: tbl-01 +#| tbl-cap: The table caption. +library(knitr) + +kable(mtcars[1:2, 1:2]) +```