一些示例展示 LaTeX 如何处理章节/部分标题中的宏

一些示例展示 LaTeX 如何处理章节/部分标题中的宏

一些示例展示 LaTeX 如何处理章节/部分标题中的宏

示例 1

一个非常基本的 LaTeX 章节和参考过程如下

\documentclass{book}
\usepackage{hyperref}

\begin{document}
    \frontmatter
    \tableofcontents
    \mainmatter
    \chapter{Chap}\label{expansion_test}
    \nameref{expansion_test}
\end{document}

.aux文件中将出现以下行

\newlabel{expansion_test}{{1}{1}{Chap}{chapter.1}{}}

并在.toc文件中

\contentsline {chapter}{\numberline {1}Chap}{1}{chapter.1}%

现在一切正常。

示例 2

但是,当人们尝试使用一些复杂的宏来代替纯文本章节标题时,例如

\documentclass{book}
\usepackage{hyperref}
\newcommand{\expansiontest}[1][]{
    expansiontest~#1
}

\begin{document}
    \frontmatter
    \tableofcontents
    \mainmatter
    \chapter{\expansiontest[Chap]}\label{expansion_test}
    \nameref{expansion_test}
\end{document}

然后会发现上面两行的标题/名称部分现在将是未扩展的宏,例如

\newlabel{expansion_test}{{1}{1}{\expansiontest [Chap]}{chapter.1}{}}
\contentsline {chapter}{\numberline {1}\expansiontest [Chap]}{1}{chapter.1}%

在这个例子中,一切仍然很好。

示例 3

但是当人们试图在这个复杂的宏中包含一些用户定义的计数器时,事情就会变得非常糟糕。

\documentclass{book}
\usepackage{hyperref}
\newcounter{SomeCounter}[chapter]
\renewcommand{\theSomeCounter}{\thechapter.\arabic{SomeCounter}.}
\newcommand{\expansiontest}[1][]{
    \theSomeCounter~expansiontest~#1
}

\begin{document}
    \frontmatter
    \tableofcontents
    \mainmatter
    \chapter{\expansiontest[Chap]}\label{expansion_test}
    \refstepcounter{SomeCounter}
    \nameref{expansion_test}
\end{document}

此宏不会像以前一样在.aux.toc文件中展开。但这现在会引起一些麻烦。

  • 在章节标题中,你会发现计数器显示为1.0因为它位于第 1 章并且没有\refstepcounter{SomeCounter}预先存在的命令。这就是所需的显示!

  • 由于目录位于第 0 章,并且\refstepcounter{SomeCounter}目录之前也没有任何命令,因此在目录中,计数器显示为0.0

  • 作为最后的参考,可以发现计数器显示为1.1因为我们\refstepcounter{SomeCounter}在此之前已经实现了\nameref

示例 4

\refstepcounter{SomeCounter}如果在宏的定义中进一步包括该行\expansiontest就可以实现一些自动化。

\documentclass{book}
\usepackage{hyperref}
\newcounter{SomeCounter}[chapter]
\renewcommand{\theSomeCounter}{\thechapter.\arabic{SomeCounter}.}
\newcommand{\expansiontest}[1][]{
    \refstepcounter{SomeCounter}
    \theSomeCounter~expansiontest~#1
}

\begin{document}
    \frontmatter
    \tableofcontents
    \mainmatter
    \chapter{\expansiontest[Chap]}\label{expansion_test}
    \nameref{expansion_test}
\end{document}

你会发现计数器显示为

  • 在章节标题中,1.1

  • 在目录中,0.1

  • 在最后的参考中,1.2

我的问题

  1. 那么,在写入这些各种辅助文件时,LaTeX 停止扩展宏的确切行是什么?在示例 2 中,如果将可选参数更改为强制参数,则仍会在和.aux文件中找到扩展的宏.toc。但如果进一步包含斜体格式的宏,如

    \newcommand{\expansiontest}[1]{
        \textit{expansiontest~#1}
    }
    

    然后,.toc我们仍然会在文件中找到扩展的命令,但是现在.aux我们会找到未扩展的宏。

  2. 我研究这个问题的中期目标是,当章节标题以宏表示时,在目录和参考文献中获得与原始章节标题相同的显示。这个问题并非用户定义计数器的使用所独有。人们还可以\expansiontest在任何地方重新定义宏以查找不一致之处。那么,我们能做到这一点吗?我们如何做到这一点?

  3. 我的最终目标是定义一个具有以下功能的新计数器和宏。

    • 每次调用时,这个新计数器增加一。
    • 然后显示当前计数器,后跟该宏的参数的一些文本。
    • 还可能有一些用户定义的格式,如斜体、特定颜色、盒装等。
    • 此新宏在章节/部分标题中使用时可以在目录中正确显示计数器并参考。
    • 此宏也将在章节/节标题之外使用,即在普通文本中。并且章节/节标题内部和外部的计数器必须是同一个。我不希望定义两个独立的计数器。

    那么,如何实现这些目标?

    我目前的解决方案如下。但这会导致此章节标题问题。

    \newcounter{SomeCounter}[chapter]
    \renewcommand{\theSomeCounter}{\thechapter.\arabic{SomeCounter}.}
    \newcommand{\SomeCounterName}{A name}
    \newcommand{\SomeMacro}[1][]{%
        \refstepcounter{SomeCounter}%
        \ifthenelse{\isempty{#1}}{\let\@currentlabelname\@currentlabel}{\NR@gettitle{#1}}%
        {\bfseries\defclr{\setlength{\fboxrule}{0.25ex}\setlength{\fboxsep}{0.5ex}\fbox{\SomeCounterName~\theSomeCounter{}}}\ifthenelse{\isempty{#1}}{}{~(#1)}}%
    }
    

答案1

我认为,关于当将宏写入这些不同的辅助文件时 LaTeX 停止扩展宏的“确切点”的问题很难回答。

将标记写入外部文本文件时,LaTeX 2ε 内核提供并使用各种机制来防止或计时某些宏的扩展。

是否在宏中使用/应用/考虑这些机制取决于编写宏的程序员。

时间控制是必要的,因为例如某些信息(尤其是页码)只有在积累并排版出足够的页面材料并且输出例程弹出页面时才可用,从而调整页面计数器的值。

因此,如果您还希望将页码写入辅助文件(例如交叉引用标签的情况),则需要延迟写入,直到页面被转储到 .dvi 或 .pdf 文件。

因此,表示要写入内容的标记是立即扩展还是在页面弹出时扩展会有所不同,因为在这两个时间点之间存在一个时间跨度,在此期间例如可以重新定义控制序列/可以获得不同的含义,这反过来会影响扩展的结果,从而影响写入的内容。

例如,在将信息从一个辅助文件传输到另一个辅助文件时,必须防止扩展,而不应更改在标记化后形成代表该信息的标记的字符集。例如,像 等这样的分段命令\chapter\section内部使用\addcontentsline/\addtocontents让 TeX 向 .aux 文件写入指令。该指令是\@writefile让 TeX 将内容写入另一个辅助文件(例如 .toc 文件(目录)或 .lot 文件(表格列表)或 .lof 文件(图片列表))的指令。\@writefile在 LaTeX 运行结束时处理 .aux 文件时执行指令,同时由于像\tableofcontents\listoftables或 这样的命令\listoffigures在内部使用\@starttoc,相应的\write句柄被分配,相应的输出文件被分配给这些\write句柄并打开以进行写入。所以在这里你有将信息从 .aux 文件传输到 .toc/.lot/.lof 文件的场景。

LaTeX 2ε 内核以及时间扩展控制和预防机制已随着时间的推移而不断发展。并非所有较旧的软件包都使用后来添加的机制。

解释目前 LaTeX ε 内核中最重要的扩展控制机制可能有助于获得概述。

但您需要大致了解 TeX 程序的工作原理。这些不是 LaTeX 2ε 内核特有的内容,而是 TeXbook 等书中解释的内容,而 TeXbook 是在 TeX 程序诞生时 LaTeX 格式尚不存在的时代诞生的。

在 TeXbook 中,Donald E. Knuth 将 TeX 的工作方式与有眼睛和消化道的野兽的消化过程的方式进行了类比。

眼睛逐行查看 .tex-input-file。查看完一行 .tex-input 后​​,移动角色一行 .tex-input 放入口中。
(这里的“看并动”是指

  • 将 .tex-input-file 行中看到的字符复制到 TeX 管理的某个内存区域,
  • 从计算机平台的字符编码转换为 TeX 引擎的内部字符编码,
  • 删除行右端的所有空格字符,
  • 在行的右端附加一个字符,该字符在 TeX 引擎的内部字符编码方案中的代码点号等于整数参数的值\endlinechar。通常该值为 13,而 13 表示 TeX 引擎的内部字符编码方案中的返回字符。对于传统的 TeX 引擎,内部字符编码方案是 ASCII。对于基于 XeTeX 或 LuaTeX 的 TeX 引擎,内部字符编码方案是 unicode/utf-8,其中 ASCII 是其严格子集。)

嘴巴把这些输入字符作为一组产生标记的指令(控制序列标记、字符标记)并将这些代币送入食道。 (嘴巴将眼睛产生的“输入字符流”分成小块,并根据这些小块产生标记(控制序列标记、字符标记),然后将这些标记送入食道,这意味着在食道中你有一个“标记流”/一个“标记流”。)

在可扩展令牌的喉咙扩张中例如宏,发生。这意味着可扩展标记会从标记流中移除,而替换标记(如果有)会插入到标记流中。这种情况(在某种反流过程中)会一直发生,直到标记流中没有剩余的可扩展标记。食道中可扩展标记扩展后产生的标记会被送入 TeX 的胃中。因此,通常只有不可扩展标记才能到达 TeX 的胃中。

我在这里写“通常”是因为在特殊情况下,扩张被抑制,以便可扩展的令牌可以到达胃:
例如,当胃从食道请求属于⟨参数文本⟩或者⟨平衡文本⟩赋值时\def,扩展被抑制。随着⟨平衡文本⟩赋值扩展的\edef不被抑制。例如,属于⟨平衡文本⟩标记寄存器赋值扩展被抑制。(但标记寄存器赋值扩展不会被抑制,直到{找到⟨平衡文本⟩,随后是⟨右括号⟩

扩展阶段发生在 TeX 的管道中,其作用仅仅是用其他标记替换标记流中的标记。仅此而已。

胃和胃后面的消化器官负责处理不可膨胀的物质。

处理其他事物之下的不可扩展标记包含以下操作

  • 创建和放置框、将段落分成行、在页面上分配行以及进行其他排版工作,例如向页面添加页眉和/或页脚(例如页码和章节标题)以及将页面弹出到 .dvi 或 .pdf 输出文件。

  • 向 .dvi 或 .pdf 输出文件写入特殊指令,例如,用于设置 pdf 文件中的书签或超链接等“基础设施”和命名目的地/目标/锚点。

  • 将消息写入屏幕,将内容写入外部文本文件,例如 .aux-file、.toc-file、.log-file/terminal/screen。

    就像\def,哪个—如果 存在 已采取 为了 某物 携带的 出去—触发 TeX 的喉咙抑制属于分配的后续标记的扩展,\write这也是一件有趣的事情:

    工作方式\write取决于令牌\write前面是否有令牌\immediate
    如果\write前面有\immediate,则令牌会立即写入文件,从而立即扩展可扩展令牌。
    如果\write前面没有\immediate,则令牌会被保存而不扩展,直到下次将页面送出/弹出到 .dvi 文件或 .pdf 文件。在送出/弹出相关页面时,会写入并扩展可扩展令牌。
    如果\write前面没有\immediate,则在保存令牌的时刻和由于送出/弹出页面而扩展和写入令牌的时刻之间存在时间跨度。在此时间跨度内,可以重新定义要写入的令牌,从而在写入时获得不同的结果。

  • 执行任务。例如,

    • 为寄存器或 TeX 参数分配值/内容。
      寄存器可以是计数寄存器、标记寄存器等。TeX
      参数调整 TeX 的某些行为。例如,\escapechar是 TeX 参数。写入屏幕/外部文件时,TeX 已在处理标记。的值\escapechar在写入控制序列标记或通过\string或传递\detokenize控制序列标记的“文本表示”时起着重要作用——控制序列标记的“文本表示”由类别 12(其他)的显式字符标记组成,但空格属于类别 10(空格):TeX 参数的值\escapechar表示字符标记的字符代码 [= TeX 内部字符编码方案中相应字符的代码点编号],该字符标记将被添加到表示所讨论的控制序列标记名称的字符标记序列的前面。通常的值为\escapechar92,而在 unicode 和 ASCII 中,代码点编号 92 表示反斜杠字符\

    • \def/ \edef/ \gdef/而言的宏定义\xdef。/
      \def/ \edef/为\gdef控制\xdef序列标记分配替换规则。它们“指示”TeX 从此在管道/宏扩展阶段(在扩展不受抑制的情况下)从标记流中删除相关的控制序列标记和根据分配的参数文本的后续标记,并在遇到分配了替换规则的控制序列标记时根据分配的定义文本将一些标记集/序列插入到标记流中。/ / /\def关注\edefTeX管道的行为/关注扩展阶段的行为。\gdef\xdef

    • \let或的赋值\futurelet。-
      赋值\let不是替换规则。-赋值\let并不只关注改变 TeX 食道的行为。-赋值\let也可以影响食道后面的消化器官的行为:\let\BBB=\AAA“告诉”TeX:“当遇到 时,以要执行的\BBB方式执行 — 例如,不是在将/应用于时,不是在作为分隔宏参数的分隔符移除时,不是在未展开写入屏幕/文本文件时,不是在未展开的情况下收集宏赋值定义文本的标记时 — 表现得像你遇到 时的行为一样(而 的含义仍然没有改变)。”执行后触发在执行 -赋值 时将通过执行 触发的操作。如果你说那么可以用来指示 TeX 构造一个框,其内容将在受限水平模式下排版。这不是在食道中发生的事情,而是在胃中发生的事情。如果您随后说,那么仍然可以用于此目的,但不能再用于此目的,因为现在已经分配了一个替换规则/因为已经成为一个宏,它在喉咙扩展阶段从标记流中移除并由代表短语的字符标记替换。\BBB\string\detokenize\BBB\BBB\BBB\AAA\AAA\let\BBB=\AAA\BBB\let\AAA\let\BBB=\hbox\BBB\def\hbox{You should not do this}\BBB\hbox\hbox\hboxYou should not do this

      \futurelet⟨single control sequence that shall obtain a new meaning⟩
                ⟨single token to process when assignment is done⟩
                ⟨single token whose current meaning is to be copied⟩
      就好像
      \let⟨single control sequence that shall obtain a new meaning⟩=
          ⟨single token whose current meaning is to be copied⟩
          ⟨single token to process when assignment is done⟩
          ⟨single token whose current meaning is to be copied⟩
      或者类似
      \afterassignment⟨single token to process when assignment is done⟩
      \let⟨single control sequence that shall obtain a new meaning⟩=
          ⟨token whose current meaning is to be copied⟩
          ⟨token whose current meaning is to be copied⟩

 

LaTeX 命令(例如\newcommand/ \renewcommand/ \DeclareRobustCommand/\ProvideCommand或 ) \NewDocumentCommand只是一些棘手的宏,在扩展过程中会“分解”为指令,以 TeX 的\def-primitive 形式执行宏定义。如果有可选参数,这些指令会将指令放入待定义的控制序列的替换文本中,以“向前查看”标记流中的下一个标记,以找出是否存在[/a 左方括号。
只有在调用 或 时,才能以可靠的方式使用“老式”TeX 引擎向前查看标记流中的下一个标记,\let而 或\futurelet又是赋值。(许多事情可以通过仅借助于扩展方法检查宏参数来完成,但是从标记流中抓取未限定的宏参数的方式使其无法仅通过扩展方法可靠地产生标记流中的下一个任意标记是空格标记或花括号/的情况。{1}2\NewExpandableDocumentCommand的情况,以前是通过包提供的解析如今是 LaTeX 2ε 内核的一部分,它试图仅通过基于扩展的方法检测可选参数,但 xparse-manual 表明,例如,它与 的\test[optional]{mandatory}处理方式相同\test{[}optional]{mandatory}。使用 LuaTeX,您可以通过 Lua 例程实现可扩展的前瞻,但当 LaTeX 和宏\newcommand被发明时,还没有 LuaTeX。)
用/ / /[定义的 命令来检测是否存在 oa 的下一个标记是通过 来实现的,这意味着处理通过 /etc 定义的可选参数的扩展宏也会产生用于执行分配的标记,这些分配是检测是否存在可选参数的前瞻所需的。这些分配标记不会在食道中执行。它们进入胃中。这反过来意味着,在所有工作都必须在食道中执行的情况下,这种宏不起作用。即,这种宏在纯扩展上下文中不起作用。\newcommand\renewcommand\DeclareRobustCommand\ProvideCommand\futurelet\newcommand

\edef//\xdef诸如此类\write的都是纯粹的扩展上下文:

如果你定义

\def\innermacro{X}%
\def\something{Y}%
\def\macro{\def\innermacro{\something}}%

并说

\newwrite\myfile
\immediate\openout\myfile foobar.txt %
\immediate\write\myfile{\macro}%
\immediate\closeout\myfile

,然后在实际写入 foobar.txt 之前内容会被扩展,而这反过来\immediate会立即发生。

关键/微妙的一点是:

\def到达胃部为了 存在 携带的 出去,TeX“告诉”食道抑制扩张,同时将构成作业其他标记/组成部分的标记发送到胃部。

但是——如上所述——在实际编写内容时会发生扩展,并且扩展会\macro产生标记\def\innermacro{\something}。而标记\def反过来又是不可扩展的,并且 情况 不是 经过考虑的 A 令牌 携带的 出去 经过考虑的 A 令牌 书面。因此,扩展不会受到后续标记的抑制。因此,也会扩展,得到。反过来,\innermacro也是不可扩展的,并且也写为 。然后,不可扩展的,写为 。然后扩展,得到,反过来,是不可扩展的,并且写为 。然后,不可扩展的,写为 。总之,会产生写入文件序列,这可能不是预期的。类似地,如果你说,这就像在说:扩展后的是不可扩展的,并且不被识别为应执行的标记,因此它会进入 -assignment 的替换文本,而不会导致 TeX 抑制后续标记的扩展。XX{\somethingY}\immediate\write\myfile{\macro}\def X{Y}\edef\foobar{\macro}\def\foobar{\def X{Y}}\def\macro\edef

\protect这就是为什么在 LaTeX 2ε 中发明了“老式”的扩展预防机制的原因:

您可以说\def\protect{\noexpand\protect\noexpand}...\immediate\write\myfile{\protect\macro},这将产生写入序列\protect\macro
当文件在\let\protect=\relax生效期间读回时,与胃的-no-op-primitive\protect相同,并像往常一样扩展。 当您说并让 TeX 写入屏幕时,会被字符串化,您会在屏幕上看到写入的字符序列。 使用 LaTeX 2ε 内核,宏会扩展为。使用 LaTeX 2ε 内核,宏等于。\relax\macro
\let\protect=\string\protect\macro\macro\macro
\@unexpandable@protect\noexpand\protect\noexpand
\@typeset@protect\let\relax

\protect通过在执行之前将标记\let设置为等于以及在执行之后将标记设置为等于/来临时(重新)定义标记,这是使用 LaTeX 2ε和后台完成的。\@unexpandable@protect\edef\let\protect\@typeset@protect\relax\edef\protected@edef\protected@write

\protected@write就像执行\protected@edef并将结果作为延迟(非\immediate)的参数传递一样\write

使用替换文本/参数,\protected@edef可以手动\protected@write添加标记。 使用根据(在任何情况下)或/ / (仅在应处理可选参数的情况下)定义的宏标记,可以在后台自动添加标记。\protect
\DeclareRobustCommand\newcommand\renewcommand\providecommand\protect

除了“老式”机制之外,\protect还有一个较新的东西:

\protected较新/扩展的 TeX 引擎附带了用于 \def/ \edef/ \gdef/赋值的内置前缀\xdef
就像您可以说\global\def...表示赋值不应局限于局部范围,而应应用于所有上级范围,就像您可以说 表示\long\def...待定义的宏的参数可能包含标记\par,就像您可以说 \outer\def...表示待定义的控制序列既不能在宏的参数中使用,也不能在赋值的替换文本或参数文本中使用\def,前缀可用于表示当直接由或或或或或或或 或通过向前查看 或或的对齐\protected触发扩展时,不能扩展待定义的控制序列。但是对于或或等,使用前缀定义的宏的扩展仍然可以由/ / / / /之类的东西触发。前缀、和可以组合使用。\write\edef\xdef\message\errmessage\special\mark\marks\noalign\omit\directlua\write\edef\xdef\protected\if\ifcat\ifnum\number\romannumeral\expandafter\long\global\outer\protected

还有另外两种非常相似的微妙的防止扩张的机制:

  • 当通过 /之类的东西写入或在/\write内扩展时,将传递形成寄存器的值/内容的标记,但不会扩展它们。\edef\xdef\the⟨register⟩
  • 当通过 写入\write或在 中扩展被包裹的\edef/某物时,则会传递但不会扩展。\xdef\unexpanded{...}

请注意,在/ -赋值扩展期间以及在写入文件时以及在执行 . 时,类别 6(参数)的哈希值\unexpanded会加倍。(就像在写入文件和应用于该文件之间包装标记,但计算机平台的字符编码方案和 TeX 的内部字符编码方案之间的转换,并在此应用 .tcx 文件进行字符翻译,表示何时以 -notation 形式编写字符被省略,这是一件好事。)\the\edef\xdef\scantokens\scantokens\unexpanded{...}\input^^

标准类的分段命令(文章报告) 用于\protected@edef将章节标题保存在临时宏中,以便在创建页眉/页脚时使用。

标准类的分段命令使用\addcontentsline/\addtocontents来处理 .toc 文件的条目。\addtocontents/\addcontentsline反过来在内部用于将 -entry\protected@write写入\@writefile.aux 文件。 \@writefile通过将内容放入临时标记寄存器并写入其\the-expansion 来防止扩展。

标准类的分段命令用于\protected@edef获取交叉引用信息并将其保存到命令使用的未受保护的临时宏中\label

加载时超链接或者名称引用\nameref,通过来自包的宏,可以 获取并保存作为分段命令参数的分段标题,以便进行交叉引用获取标题字符串. 包中的宏获取标题字符串\label从分段命令的参数中删除不需要的标记,例如 -command 的实例,因为这是不可取的,例如,在重复引用相同的分段标题时,尝试一次又一次地放置相同的交叉引用标签。
因此存在两种可能性:要么通过 完全展开构成分段标题的参数的标记\protected@edef。要么根本不展开它们,并且保护 -command 提供的用于暂存宏的顶层扩展,\label以免进一步扩展。默认设置是根本不展开构成分段标题的标记,并且保护 -command 的暂存宏的顶层扩展,以免进一步扩展。如果在加载时\label指定该选项expanded获取标题字符串,然后使用 来扩展事物\protected@edef

LaTeX 2ε 内核的机制\label反过来用于将条目\protected@write写入\newlabel{}{...}.aux 文件。

因为不希望重复某些内容,所以需要从分段命令的参数中删除这些内容,这种情况不仅发生在\nameref-references 的基础结构中。
分段命令的参数被称为“移动参数”,因为它们不仅出现在正文中排版节标题的位置,还出现在许多其他地方:

  • 在目录中,
  • 在书签中,
  • 做的时候\nameref
  • 在页眉中是否\pagestyle{headings}有效。

书签需要特别注意,因为它们属于 .pdf 文件的“基础设施”,因此需要使用符合 pdf 标准并能被 pdf 查看器理解的字符编码进行编码。\hyperref并且包bookmarks在后台进行转换。

页眉需要特别注意,因为某些页面样式会执行类似\uppercase/ 的操作\lowercase,然后这些操作也会应用于属于交叉引用标签名称的字符标记。因此,如果您在节段命令的参数内执行“无超链接”交叉引用,则表示交叉引用标签的字符可能会在执行交叉引用命令之前通过大写/小写进行转换,从而导致尝试访问未定义的交叉引用标签的数据。

\nameref\@currentlabelname-references 需要特别注意,因为保存了 sectioning-title 并且被 使用的scratch-macro\label是在文档正文中实际排版 section-heading 之前(重新)定义的。

说了这么多,一种方法可能是\expansiontest只处理非可选参数的命令,因此可以在纯扩展上下文中完全扩展。纯扩展上下文由\protect不等于\@typeset@protect/ 的标记的情况表示\relax。如果在正常排版模式下,没有为 pdf 书签创建名称字符串,即,当没有准备或\protected@write移动\protected@edef参数的移动时,命令将通过相关计数器\refstepcounter并放置交叉引用标签,以便在执行此实例时可以在整个文档中检索计数器的值\expansiontest。在任何情况下 - 即使在准备移动参数的移动时,命令也应创建对该交叉引用标签的“无超链接”交叉引用并放置其他参数。这意味着对于每个实例,\expansiontest您都需要提供一个表示交叉引用标签唯一名称的参数(然后在内部与移动参数一起使用)。如果页面样式有效,其中构成分段命令参数的标记被转换,例如使用类似\uppercase或 的东西\lowercase,那么交叉引用标签的唯一名称应该由不受其影响的标记形成。例如,如果进行了大写,则可以使用大写字母和数字。还建议坚持使用 ASCII 字符,因为这可能会最大限度地减少在 8 位引擎上使用包 inputenc 进行某些字符编码时遇到麻烦的可能性,方法是使一些非 ASCII/一些高 128 字节/字符处于活动状态:

\documentclass{book}
\usepackage[expand]{gettitlestring} % have \nameref-titlephrases expanded by means of `\protected@edef`
                                    % so that the instuction for stepping counter and placing label are
                                    % removed while preparing \@currentlabelname.
\usepackage{hyperref}
\newcounter{SomeCounter}[chapter]
\renewcommand{\theSomeCounter}{\thechapter.\arabic{SomeCounter}.}
\makeatletter
\newcommand{\expansiontest}[2]{%
    \ifx\protect\@typeset@protect
       \texorpdfstring{%
         % A unique cross-referencing-label needs to be placed for each counter stepped here.
         \refstepcounter{SomeCounter}%
         \NR@gettitle{\ref*{#1}~expansiontest~#2}%
         \label{#1}%
        }{}%
    \fi
    \ref*{#1}~expansiontest~#2%
}%
\makeatother

\begin{document}
    \frontmatter
    \tableofcontents
    \mainmatter
    \pagestyle{headings}
    \chapter{some chapter}\label{some chapter}
    \chapter{\label{expansion_test}\expansiontest{EXPANSIONTEST(SOMECOUNTER)(LABEL1)}{Chap}}

    Some text.

    \bigskip

    \noindent
    \expansiontest{EXPANSIONTEST(SOMECOUNTER)(LABEL2)}{Not in chap}

    \bigskip

    \noindent
    Cross-referencing:

    \noindent
    \verb|\nameref{expansion_test}|: \nameref{expansion_test}

    \noindent
    \verb|\ref{expansion_test}|: \ref{expansion_test}

    \noindent
    \verb|\nameref{EXPANSIONTEST(SOMECOUNTER)(LABEL1)}|:
    \nameref{EXPANSIONTEST(SOMECOUNTER)(LABEL1)}

    \noindent
    \verb|\ref{EXPANSIONTEST(SOMECOUNTER)(LABEL1)}|:
    \ref{EXPANSIONTEST(SOMECOUNTER)(LABEL1)}

    \noindent
    \verb|\nameref{EXPANSIONTEST(SOMECOUNTER)(LABEL2)}|:
    \nameref{EXPANSIONTEST(SOMECOUNTER)(LABEL2)}

    \noindent
    \verb|\ref{EXPANSIONTEST(SOMECOUNTER)(LABEL2)}|:
    \ref{EXPANSIONTEST(SOMECOUNTER)(LABEL2)}

    \noindent
    \verb|\nameref{some chapter}|: \nameref{some chapter}

    \noindent
    \verb|\ref{some chapter}|: \ref{some chapter}

    % Some pages so you can see the page headers
    \newpage\null\newpage
\end{document}

在此处输入图片描述

相关内容