Quarto交叉引用:文本更改时保持持久的交叉引用。

5

我正在使用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].

这将产生所需的输出:

enter image description here

问题

问题在于该文档正在被多位作者重新起草。如果将一个章节标题从为什么Stack Overflow如此伟大更改为我为什么喜欢Stack Overflow,它会破坏使用第二种样式(章节标题)的交叉引用。

enter image description here

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

在Quarto交叉引用文档中有各种选项。但是,如果更新了部分标题,我看不到使交叉引用文本更新的方法,只要显式标识符保持不变即可。

这个存在吗?

2个回答

3

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

用法:

  1. 对于章节或子章节标题,使用非常简单。我们只需要使用 \nameref{sec-id},其中 sec-id 是在标题中使用的章节标识符,如 #sec-id

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

  3. 对于使用 markdown 语法添加的图像或 markdown 表格,使用此过滤器有点 hacky。我们需要使用嵌套的 div(使用 pandoc div 语法创建,外部 div 使用 ::::,内部 div 使用 :::)。在第一个 div 中,我们必须添加 link 类、link-idlink-title,在第二个 div 中,我们需要添加另一个类 cell-output-display。然后同样地,使用我们分配给 link-id 的 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}

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

生成输出的第一部分

First part of the generated output


现在的问题是,成为一个Quarto过滤器是否有用? - shafee
1
嗨,Shafee。我暂时没有机会尝试并接受答案,但它看起来很棒 - 谢谢。相比于Lua脚本,使用Quarto过滤器的优势是什么?在yaml中包含过滤器似乎很容易。 - SamR
Quarto过滤器实际上是基于Lua过滤器的。所以我认为并没有太大的差异和优势。如果你查看Quarto文档,你会明白的:)。 - shafee
1
好的,我已经尝试了HTML和PDF两种格式 - 这真是太棒了。如果可以的话,我会点赞两次。首先它对于这两种格式都完美地工作。其次,它让我看到了全新的Lua过滤器世界。也许你应该将其作为Quarto过滤器/扩展(仍不完全清楚区别)发布在他们的网站上,Quarto作为一项技术仍在发展壮大,我相信许多人会从这样的过滤器中受益。此外,如果开发人员看到它,他们可能会考虑将其作为核心功能。 - SamR

3

经过一些实验,这是一种基于 TeX SE答案的LaTeX方法。

我们在头部加载nameref包,然后将Quarto引用从@sec-stack更改为\nameref{sec-stack}。 引用在更改章节标题后仍然存在,例如更改为我为什么喜欢Stack Overflow

---
format:   
  pdf:
    number-sections: true
header-includes:
 \usepackage{nameref}
---

# Why I love Stack Overflow {#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 \nameref{sec-stack}.

但是

缺点是这不是Quarto的原生解决方案,也无法渲染到其他格式,如HTML。不过,它能够正常工作,只需进行一次简单的正则表达式替换即可更改所有引用。我还没有接受这个答案,如果有本地的Quarto答案,我会接受它。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接