如何将任意元素添加到 Bookdown 中目录的 Table?

How can I add arbitrary elements to the Table of Contents in Bookdown?

我正在通过 bookdown 制作一本书。 我知道可以通过添加属性 {.unlisted .unnumbered} 来省略目录 Table 中的标题,如 Section 4.18 of the R Markdown Cookbook 所示。 但是,如何将任意内容添加到目录的 Table 中? 如果我只需要为 PDF 输出添加它,我可以使用(例如)LaTeX 命令 \addcontentsline,但我还需要它显示在 HTML 内容侧栏中。

例如,如果您从 RStudio 设置一个新的默认 bookdown 项目,它包含文件 01-intro.Rmd。 前几行是

# Introduction {#intro}

You can label chapter and section titles using `{#label}` after them, e.g., we can reference Chapter \@ref(intro). If you do not manually label them, there will be automatic labels anyway, e.g., Chapter \@ref(methods).

Figures and tables with captions will be placed in `figure` and `table` environments, respectively.

我需要能够创建添加到 table 内容的任意 div,如下所示:

# Introduction {#intro}

You can label chapter and section titles using `{#label}` after them, e.g., we can reference Chapter \@ref(intro). If you do not manually label them, there will be automatic labels anyway, e.g., Chapter \@ref(methods).

::: {#arbitrary-header <some other attribute?>}
Figures and tables with captions will be placed in `figure` and `table` environments, respectively.
:::

这将添加句子“Figures and tables with captions will be placed in respectively in figure and table environments.”在目录的 LaTeX Table 和 HTML 输出的侧边栏中。 这个问题的上下文是我需要在另一个自定义 div 中放置一个 header,该自定义 div 对颜色框内的内容进行格式化以使其脱颖而出。 否则,我当然可以在上面的句子之前通过 ## 添加另一个标题。

也许是这个解决方案?

CSS-文件:

  k1 {
   font-family: 'Times New Roman', Times, serif; 
   font-size: 12pt;
   text-align: justify;
  }
  
  #TOC {     
    color:black; 
    background-color: white;     
    position: fixed; 
    top: 0; 
    left: 0; 
    width: 250px;
    padding: 10px; 
    overflow:auto; 
    margin-left: -5px;
 }

  body {
    max-width: 800px;  
    margin-left:300px;
    line-height: 20px;
  }

  div#TOC li {
    list-style:none;
  }
  
  h2#toc-title {
  font-size: 24px;
  color: Red;
  }

Rmd-文件:

---
title: "Arbitrary elements"

author: "Me"
date: "`r Sys.Date()`"
output:
  bookdown::html_document2:
    toc: true
    css: "arb.css"
toc-title: "Contents"    
---

# My question is it: "..."

# Introduction of our story ...

# It was a strange person ...

## The Flame was in his body ...

# <k1> Figures and tables with captions will be placed in `figure` and `table` environments, respectively. </k1> {-} 

## Where is the love, friends?

我们将使用 Lua filter for this, as those are a good way to modify R Markdown behavior. The "Bookdown Cookbook" has an excellent overview 并包含如何使用 Lua 过滤器的说明。

我们这样做的方法是避开正常的 TOC 生成器并在 Lua 中重写它。然后我们将新的 TOC 添加为名为 table-of-contents(和 toc 以实现兼容性)的元值,这足以包含在输出中。

所以首先让我们转储代码来创建一个普通目录:

_ENV = pandoc

local not_empty = function (x) return #x > 0 end
local section_to_toc_item

local function to_toc_item (number, text, id, subcontents)
  if number then
    text = Span({Str(number),Space()} .. text, {class='toc-section-number'})
  end
  local header_link = id == '' and text or Link(text, '#' .. id)
  local subitems = subcontents:map(section_to_toc_item):filter(not_empty)
  return List{Plain{header_link}} ..
      (#subitems == 0 and {} or {BulletList(subitems)})
end

section_to_toc_item = function (div)
  -- bail if this is not a section wrapper
  if div.t ~= 'Div' or not div.content[1] or div.content[1].t ~= 'Header' then
    return {}
  end
  local heading = div.content:remove(1)
  local number = heading.attributes.number
  -- bail if this is not supposed to be included in the toc
  if not number and heading.classes:includes 'unlisted' then
    return {}
  end

  return to_toc_item(number, heading.content, div.identifier, div.content)
end

-- return filter
return {
  { Pandoc = function (doc)
      local sections = utils.make_sections(true, nil, doc.blocks)
      local toc_items = sections:map(section_to_toc_item):filter(not_empty)
      doc.meta['table-of-contents'] = {BulletList(toc_items)}
      doc.meta.toc = doc.meta['table-of-contents']
      return doc
    end
  },
}

代码进行了一些小的简化,但应该为大多数文档生成相同的目录。现在我们可以继续并根据自己的喜好修改代码。例如,要包含带有 class toc-line 的 div,可以通过在函数开头添加以下代码来修改 section_to_toc_item

section_to_toc_item = function (div)
  if div.t == 'Div' and div.classes:includes('toc-line') then
    return to_toc_item(nil, utils.blocks_to_inlines(div.content), div.identifier)
  end
  ⋮
end

根据需要修改代码。

此外,如果您想从正常输出中排除额外的 TOC 行,则必须将它们过滤掉。让脚本 return 第二个过滤器来执行此操作:

return {
  { Pandoc = function (doc) ... end },
  { Div = function (div) return div.classes:includes 'toc-line' and {} or nil end }
}

我们可以使用 R 函数打印彩色框并将标题添加到 TOC 取决于输出格式。对于 gitbook 输出,这很容易使用 HTML 和降价来完成。对于 pdf_book 我们可以使用 LaTeX 环境来处理像 tcolorbox.

这样的彩色框

这是函数(在 .Rmd 文件的代码块中定义):

block_toc <- function(title, level, content, output) {
  if(output == "html") {
    title <- paste(paste(rep("#", level), collapse = ""), title, "{-}")
    cat('<div class = "myblock">', title, '<p>', content, '</p>\n</div>', sep = "\n")
  } else {
    level <- c("part", "chapter", "section")[level]
    cat('\addcontentsline{toc}{', level, '}{', title, '}',
        '\n\begin{mybox}\n\textbf{\noindent ', title, '}\n\medskip\n\n', content,
        '\n\n\end{mybox}', sep = "")
  }
}

根据输出格式,block_toc() 连接并打印块的代码,当然,还确保将标题添加到目录中。您可以使用 level.

设置添加框标题的 TOC 级别

在块中像这样使用 block_toc()

```{r, results='asis', echo=F, eval=T}
block_toc(
    title = "Central Limit Theorem",
    level = 2
    content = "The CLT states that, as $n$ goes to infinity, 
      the sample average $\bar{X}$ converges in distribution 
      to $\mathcal{N}(\mu,\sigma^2/n)$.",
    output = knitr::opts_knit$get("rmarkdown.pandoc.to")
)
```

output = knitr::opts_knit$get("rmarkdown.pandoc.to")会在建书时获取当前输出格式并将其传递给函数


吸引人的盒子的一些样式

添加到 preamble.tex(对于 PDF 输出中的彩色框 - 在 YAML header 中包含文件)。这将为生成蓝框定义一个 tcolorbox 环境。

\usepackage{tcolorbox}
\definecolor{blue}{HTML}{D7DDEF}
\definecolor{darkblue}{HTML}{2B4E70}
\newtcolorbox{mybox}{colback=blue, colframe=darkblue}

添加到 style.css(HTML 彩色框的样式)或包含在 ```{css} 代码块中:

.myblock {
 background-color: #d7ddef;
 border: solid #2b4e70;
 border-radius: 15px;
}
.myblock p, .myblock h2, .myblock h3 {
  padding: 5px 5px 5px 20px;
  margin-top: 0px !important;
}

对于 HTML 输出 (gitbook) 这会产生

对于 LaTeX 输出 (pdf_book) 它看起来像这样

在 TOC 的章节级别有相应的条目。