四开交叉引用:文本更改时持久交叉引用

Sam*_*amR 6 r cross-reference pandoc r-markdown quarto

我正在使用四开本创建一份长 PDF 报告。我想包括两种格式的交叉引用:

  1. 到章节/图号,例如

正如3.1 节中讨论的

  1. 到部分/图标题,例如

正如我的精彩部分中所讨论的。

可以这样实现:

---
format:   
  pdf:
    number-sections: true
---

# Why Stack Overflow is so great {#sec-stack}

I can use style 1, the section number, with the explicit ID, e.g. as seen in @sec-stack.

I can use style 2, the section title, with a link [Why Stack Overflow is so great].

I can also custom text with [Custom text before the link][Why Stack Overflow is so great].
Run Code Online (Sandbox Code Playgroud)

这会产生所需的输出:

在此输入图像描述

问题

问题是该文件正在由几位作者重新起草。如果章节标题从Why Stack Overflow is so Great更改为Why I love Stack Overflow,则会破坏使用第二种样式(章节标题)的交叉引用。

在此输入图像描述

我正在寻找一种使用显式标识符 引用部分的方法@sec-stack,并让它显示标题而不是部分编号。这将类似于[@sec-stack]而不是@sec-stack

四开交叉引用文档中有多种选项。但是,我看不到一种方法,以便在更新部分标题时交叉引用文本也会更新,前提是显式标识符保持不变。

这存在吗?

sha*_*fee 4

我编写了一个Lua 过滤器 name_crossref.lua来获取交叉引用文本而不是节/图/表号。以下方法适用于 HTML 和 pdf 输出格式。

用法:

  1. 对于章节或小节标题,其使用非常简单。我们只需要使用header 中使用的部分标识符\nameref{sec-id}作为sec-id#sec-id

  2. 对于从代码块生成的图像或表格,我们需要使用一些块选项。我们需要使用link代码块类,需要定义一个 id 以便稍后与 交叉引用图像或表格link-id,并且需要定义一个将用作与 交叉引用文本的标题link-title。然后使用我们分配的 id 来link-id引用生成的表/图像\nameref{id}

  3. 对于使用 Markdown 语法或 Markdown 表添加的图像,使用此过滤器有点麻烦。我们需要使用嵌套 div(使用 pandoc div 语法创建,::::用于外部 div 和:::内部 div)。在第一个 div 中,我们必须添加link类 , link-idlink-title在第二个 div 中我们需要添加另一个类cell-output-display。同样,然后使用我们分配给的 idlink-id来引用生成的表/图像\nameref{id}

---
title: "Cross Referencing the Name"
author: Shafee
format: 
  html: default
  pdf: default
number-sections: true
filters: 
  - name_crossref.lua
---

# Why Quarto is so great {#sec-stack}

`r stringi::stri_rand_lipsum(1)`
See \nameref{sec-stack}.

## How it is so {#how}

`r stringi::stri_rand_lipsum(1)`
See \nameref{how}.

## Images

```{r}
#| classes: link
#| link-id: fig1
#| link-title: My Awesome plot

plot(1:10)
```

`r stringi::stri_rand_lipsum(1)`
see \nameref{fig1}

## Tables

```{r}
#| classes: link
#| link-id: tab1
#| link-title: Mtcars Data

head(mtcars)
```

`r stringi::stri_rand_lipsum(1)`
see \nameref{tab1}


# Markdown Images

:::: {.link link-id="fig2" link-title="Scatter plot of mpg"}
::: {.cell-output-display}

![mpg](test-filename/mpg.png)
:::
::::

`r stringi::stri_rand_lipsum(4)`

see \nameref{fig2}


# Markdown Table

:::: {.link link-id="tab2" link-title="Markdown table"}
::: {.cell-output-display}


| Col1 | Col2 | Col3 |
|------|------|------|
| A    | B    | C    |
| E    | F    | G    |
| A    | G    | G    |

: My Caption

:::
::::

`r stringi::stri_rand_lipsum(4)`

see \nameref{tab2}

Run Code Online (Sandbox Code Playgroud)

name_crossref.lua

local str = pandoc.utils.stringify

function get_header_data(data)
  local get_headers = {
      Header = function(el)
        local id = el.identifier
        local text = str(el.content):gsub("^[%d.]+ ", "")
        table.insert(data, {id = id, text = text})
      end,
      
      Div = function(el)
        if el.attributes["link-id"] then
          local id = el.attributes["link-id"]
          local text = el.attributes["link-title"]
          table.insert(data, {id = id, text = text})
        end
      end
    }
  return get_headers
end

function change_ref(data)
  local change_rawinline = {
    RawInline = function(el)
      for key, value in pairs(data) do
        if el.text:match("\\nameref{(.*)}") == value.id then
          local target =  "#" .. value.id 
          local link = pandoc.Link(value.text, target)
          return link
        end
      end
    end
  }
  return change_rawinline
end

local function add_div_id(div)
  return {
   Div = function(elem)
     if elem.classes:includes("cell-output-display") 
      or elem.classes:includes("cell-output-stdout")then
      elem.identifier = div.attributes["link-id"]
      return elem
     end
  end
  }
end

function Div(el)
  if el.classes:includes('link') then
    return el:walk(add_div_id(el))
  end
end

function Pandoc(doc)
  local header_data = {}
  doc:walk(get_header_data(header_data))
  return doc:walk(change_ref(header_data))
end

Run Code Online (Sandbox Code Playgroud)

生成输出的第一部分

生成输出的第一部分