【问题标题】:How can I add arbitrary elements to the Table of Contents in Bookdown?如何在 Bookdown 的目录中添加任意元素?
【发布时间】:2022-01-08 01:13:09
【问题描述】:

我正在通过bookdown 制作一本书。 我知道可以通过添加属性{.unlisted .unnumbered} 来省略目录中的标题,如Section 4.18 of the R Markdown Cookbook 所示。 但是,如何将任意内容添加到目录中? 如果我只需要为 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.

我需要能够创建添加到目录中的任意divs,如下所示:

# 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.
:::

这将添加句子“带有标题的图形和表格将分别放置在figuretable 环境中。”在 LaTeX 目录和 HTML 输出的侧边栏中。 这个问题的背景是我需要在另一个自定义div 中放置一个标题,该标题对颜色框中的内容进行格式化以使其脱颖而出。 否则,我当然可以在上面的句子之前通过##添加另一个标题。

【问题讨论】:

    标签: r r-markdown pandoc bookdown


    【解决方案1】:

    我们可以使用 R 函数打印一个彩色框并将标题添加到 TOC取决于输出格式。对于gitbook 输出,这很容易使用HTML 和markdown 完成。对于pdf_book,我们可以为tcolorbox 之类的彩色框使用LaTeX 环境。

    这是函数(在 .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() 连接并打印块的代码,当然,还确保将标题添加到 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 标头中包含文件)。这将定义一个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),它看起来像这样

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

    【讨论】:

    • 这看起来很棒,我认为应该很适合我的目的!我将在今天晚些时候对其进行测试,如果是,请接受答案并奖励赏金。谢谢!
    【解决方案2】:

    也许这个解决方案?

    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?
    

    【讨论】:

      【解决方案3】:

      我们将为此使用Lua filter,因为这是修改 R Markdown 行为的好方法。 “Bookdown Cookbook”有一个 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
        },
      }
      

      代码做了一些小的简化,但应该为大多数文档生成相同的目录。现在我们可以继续并根据自己的喜好修改代码。例如,要包含类 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 {
        { Pandoc = function (doc) ... end },
        { Div = function (div) return div.classes:includes 'toc-line' and {} or nil end }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-01-09
        • 2012-08-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-01-24
        • 2017-12-12
        • 1970-01-01
        相关资源
        最近更新 更多