> For the complete documentation index, see [llms.txt](https://overleaf-pro.ayaka.space/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://overleaf-pro.ayaka.space/latex/zh-tw/shen-ru-wen-zhang/23-how-does-latex-typeset-headers-and-footers.md).

# LaTeX 如何排版頁首與頁尾？

## LaTeX 如何排版頁首和頁尾？

### 簡介

如果您正在尋找有關變更頁首和頁尾的操作指南，請 [請造訪我們的說明頁面](/latex/zh-tw/ge-shi-hua/02-headers-and-footers.md) 因為本文著重說明 TeX 引擎以及 LaTeX 用來產生頁首和頁尾的底層機制。

* **注意（更新）**：本文探討 2022 年 6 月之前使用的 LaTeX 核心標記機制，當時引入了更新且更靈活的 LaTeX 標記處理實作。請參見 [LaTeX 新標記機制概覽](https://www.latex-project.org/help/documentation/ltmarks-doc.pdf) 以了解更多細節。為了向後相容，LaTeX 仍持續支援其舊版標記機制。討論 TeX 引擎行為的本文內容不受這些 LaTeX 變更影響。
* **注意**：在以下討論中會使用「LaTeX」、「TeX」和「TeX 引擎」這些術語，因此了解它們的含義很有幫助——請參見 Overleaf 文章 [名稱中的奧祕：TeX 各種變體指南](/latex/zh-tw/shen-ru-wen-zhang/55-what-s-in-a-name-a-guide-to-the-many-flavours-of-tex.md) 以取得更多資訊。

為了提供有用的背景／上下文，我們先概述 TeX 排版中一些相關的底層領域——包括暫時（內部）內容儲存、分頁與頁面構成。為了簡化討論，我們將版面頁面視為包含文字「正文」以及一些頁首與頁尾。圖形（浮動體）和註腳等頁面項目不在討論範圍內，因為它們涉及一種稱為 *插入*，這遠超出本文範圍。

我們先從基本概念 *節點*：TeX 引擎用來進行暫時（內部）內容儲存的基本構成單元。

### 關於內容儲存的說明：TeX 節點

當底層 TeX 引擎處理您的 LaTeX 程式碼以產生排版成品——例如段落、表格與數學式——時，它需要將該內容暫時儲存在所使用的 TeX 引擎記憶體中。為了儲存排版內容，TeX 引擎會使用一系列所謂的 *節點*，您可以把它想成大小可變的電腦（裝置）記憶體「區塊」，彼此連結形成一個串列（請參見這個 [維基百科關於鏈結資料結構的文章](https://en.wikipedia.org/wiki/Linked_data_structure)).

![TeX 引擎如何使用鏈結節點儲存內容的示意圖](/files/4c790e91eff5d387d6601b12a8450abeb7ebd40b)

為了表示 TeX 排版的基本元素——例如字元、間距、盒子、懲罰、字距微調以及 [*標記*](#why-were-here-understanding-mark-nodes) （僅舉幾例）——TeX 引擎使用不同的 *類型* 類型的節點。某些節點，例如盒子所用的節點，需要儲存比字元節點等較簡單類型更多的資料；因此，有些節點比其他節點更大——需要更多位元組才能將其儲存在記憶體中。閱讀 TeX 相關資料時，您可能會看到像 *字元節點*, *間距節點*, [*標記節點*](#why-were-here-understanding-mark-nodes) 等等：現在您知道，這些只是用來命名分配給儲存代表 TeX 排版元素之資料的記憶體區塊。

#### 我們來到這裡的原因：理解標記節點

在眾多節點類型中，我們需要討論的是一種稱為 *標記節點* ，它由 `\mark` 命令的更多內容：

```latex
\mark{要儲存的內容}
```

該 `\mark` 指令並不會直接產生任何排版內容：其引數 `*要儲存的內容*` 會被儲存到記憶體中供日後使用，並且會建立一個 *標記節點* 來包含以下內容的記憶體位置： *要儲存的內容*。如我們將看到的， `\mark` 指令被設計出來 *專門* 協助產生頁首和頁尾。

由 `\mark` 指令建立的標記節點會「嵌入」到頁面內容中，並在頁面構成的最後階段準備好供使用，當 TeX 引擎準備將頁首和頁尾加入已排版頁面（正文）時即可取用。通常， `*要儲存的內容*` 會包含頁碼與章節或節標題／編號等內容——也就是頁首和頁尾的構成要素。

#### ε-TeX 新增了 \marks 指令

[ε-TeX 第 2 版](https://texdoc.org/serve/e-TeX/0)，是對 Knuth 原始 TeX 的擴充，於 1998 年發表，並提供了新功能與現有功能的增強。如今，ε-TeX 的大多數增強功能已被納入主流 TeX 引擎：pdfTeX、LuaTeX 與 XeTeX——請參見 TeX 引擎的 [**`-etex`** 命令列選項](/latex/zh-tw/geng-duo-zhu-ti/44-tex-engine-command-line-options-for-pdftex-xetex-and-luatex.md) ，用以啟用 ε-TeX 擴充功能。

Knuth 原始的 TeX 軟體提供 \mark 指令，僅建立 1 種「類別」或「型別」的標記節點。您的文件可以包含許多 \mark 指令，但它們都會建立同一「型別」或「類別」的標記節點——沒有內建機制來對它們分組或分類。這項限制已由 ε-TeX 的增強功能移除：pdfTeX、LuaTeX 與 XeTeX 支援的 \marks 指令：

```latex
\marks n {要儲存的內容}
```

其中 `n` 是一個整數（`0` <= `n` < `32768`）用來決定標記節點的 *類別*.

注意：

* 寫成 `\marks 0` （即 `n`=`0`）等同於使用 TeX 原本的 `\mark` 命令
* ε-TeX 也引入了標記變數（[後文再看](#pre-output-checks-looking-for-mark-nodes)）用於每個類別（`n`）的標記： `\firstmarks n`, `\topmarks n` 以及 `\botmarks n`。可選擇的標記變數類別可用於為特定排版工作提供標記資料。
* LuaTeX 提高了 `n` 到 `65535`的最大值，從而使 $$2^{16}$$ 標記類別

### TeX 使用標記的原因

要理解 *為什麼* （以及如何）TeX 引擎使用 `\mark` （或 `\marks`）指令來產生頁首和頁尾，我們需要先了解 TeX 引擎在建立頁面時的一些「特性」——尋找分頁點並將完成的頁面輸出到 PDF 檔案。實際上，這些頁面構成過程相當複雜，但其核心原理／概念可以簡化，以提供足夠的背景說明，幫助我們理解標記。

當 TeX 引擎完成段落、表格或數學式等單一內容項目的排版時，它會呼叫一個內部常式（名為 `build_page(...)`）嘗試將那些新排版的項目加入目前正在記憶體中構建的頁面。當新增內容被加入正在建立的頁面時，TeX 也會檢查這些新加入的內容是否使目前頁面變得「足夠滿」。 *分頁點*——將該頁的內容送去做最後處理（「封裝」）並隨後輸出到 PDF 檔案。

頁面封裝（構成）最後階段的一項關鍵功能稱為 *輸出常式* ，其實質上是使用者定義的一連串指令，用來將頁面內容封裝好，讓它可以準備送往 PDF 檔案。「封裝」的一個例子是加入頁首與頁尾，但輸出常式還有許多用途——例如，請參見 [TUGboat 上的這篇文章](https://tug.org/TUGboat/tb11-1/tb27salomon.pdf)。輸出常式是 TeX 中相當複雜的領域，因此這裡不會深入說明：就目前而言，只要記住頁面構成的最後階段會使用某些使用者定義的指令序列（統稱為一個 *輸出常式*).

但讓我們考慮一下，當 TeX 完成一段很長的文字段落排版，並將其拆成一系列單獨排版的行時，可能會發生什麼事。TeX 會想把那個新排版的段落—— *一些* 這些行中只有 *不會* 會納入其他頁面元素，例如稍後（由輸出常式）加入的頁首和頁尾。

當幾行段落已加入目前頁面後，頁面可能變得「足夠滿」，到了 TeX 建立分頁點的時候。不過，我們會有一些「剩餘」內容，因為只有 *part* 段落中的內容進入了目前頁面：那些「超出段落內容」會先儲存起來，供稍後建立 *下一個* 頁面。下方圖示說明了這個概念：段落的一部分在 *目前的* 頁面上，而另一部分則保留給 *下一個* 頁面。

![跨越分頁點的段落示意圖](/files/595658d206f6d16fcf82c6427d2508c80d744db6)

此段落範例說明，TeX 引擎常常會處理（排版）一些內容片段，這些內容片段無法 *完整地*，被放入目前頁面——因為該內容延伸超過了 *整個段落* ，且不像某些應用程式那樣採取逐行排版段落的方式。換句話說，TeX 會排版 *整個* 段落，不論它是否能放入目前頁面（這會在之後處理）。順帶一提，「完整排版後再加入目前頁面」的原則也適用於其他類型的 TeX 內容，包括從 .tex 檔完整讀入並在嘗試加入目前頁面之前就完全排版的表格。

以我們的段落範例來說，該段落的完整文字， *包括其中的任何指令*，都已完全處理——這表示 TeX 的排版已到達您的 `.tex` 檔案中的某個位置。TeX 引擎的「目前內部狀態」，例如變數值或已儲存的參數設定，會反映出截至目前位置——也就是段落結尾——所處理的內容，包括其中的任何指令。

雖然整個段落都已排版，但其中一部分無法容納於目前頁面，因而將「超出段落內容」儲存供 *下一個* 頁面使用。在產生「超出段落內容」的 LaTeX 程式碼（文字加指令）中，TeX 引擎 *可能* 呼叫了任意數量的指令與巨集，這些指令與巨集改變了 TeX 引擎內部儲存的值與變數——亦即改變了 TeX 的「目前狀態」。

下圖說明了 TeX 一個極為重要的「特性」，它影響 TeX 排版的多個面向。看圖的右側，您會看到由節點組成、目前儲存在記憶體中的所有內容示意：淺灰色區域包含構成 *目前的* 頁面的節點，而深灰色區域則是將要輸出到 *下一個* 頁面的排版內容。這個情境反映了我們的段落範例：深灰色區域代表「超出段落內容」：因為不適合目前頁面而被保留的文字行。

![TeX 找到分頁點時的節點位置示意圖，也顯示保留給下一頁的內容](/files/44bf1eb115b88a074511bcb0af8b72dde23ed1dd)

請記住，一旦 TeX 擁有一個「足夠滿」的頁面（正文），它就會將該內容交給一種稱為 *輸出常式* 的東西，這是一個使用者定義的指令序列。輸出常式除其他功能外，會將頁首和頁尾加入頁面正文，完成頁面的最後定稿，準備將其輸出至 PDF 檔案。

上方圖示表示 TeX 選擇在某個特定節點處進行分頁，例如 `\baselineskip` 排版行之間的間距，並準備使用輸出常式來開始封裝目前頁面——一組內建指令與使用者定義巨集的序列。 *然而*，除了構成目前頁面的內容（儲存在記憶體中）之外，TeX 還已經處理並儲存（在記憶體中） *額外內容*，來自您的 `.tex` 檔案。那些額外內容延伸至 *超過分頁點* 以及 *可能* ，其中可能包含會改變重要頁面相關值或變數的指令，影響 TeX 的「目前內部狀態」——其中包括內部參數值或由巨集儲存的資料。

當 TeX 開始封裝目前頁面時，它的「目前內部狀態」 *不是* 由截至分頁位置所排版的一切所決定，因為 TeX 已經 *超過了該點* ，並已處理額外內容。如果您將檔案中對應分頁發生的位置，以及 TeX 實際到達的 `.tex` 檔案位置加以比較，TeX 這兩個對應的內部狀態——包括儲存的資料與變數值——可能會大相逕庭。TeX 排版的目前狀態通常會領先於分頁發生的位置；也就是說，TeX 的排版活動 *並不同步* 與透過輸出常式輸出已排版頁面的過程。

#### 假設範例：如何得到錯誤的頁首

下圖說明了假設性的指令 `\myheader` 以及 `\myfooter` ，用來儲存所需的頁首和頁尾文字；例如，使用如下基本定義：

```latex
\newtoks\headertoks% 一個用於頁首文字的新 token list 變數
\newtoks\footertoks% 一個用於頁尾文字的新 token list 變數
% 使用 \global 以確保儲存的頁首或頁尾資料
% 在任何地方都可存取，包括
% 輸出常式中的指令
\newcommand{\myheader}[1]{\global\headertoks={#1}}
\newcommand{\myfooter}[1]{\global\footertoks={#1}}
```

其中 `\headertoks` 以及 `\footertoks` 是用來儲存頁首與頁尾文字的 token list。

此外，我們的假設範例也有一個輸出常式，會從目前的 `\myheader` 以及 `\myfooter`。如圖所示，在預定輸往目前頁面的內容中， `\myheader` 以及 `\myfooter` 設定了適當（正確）的值。然而，可能會有 `\myheader` 和／或 `\myfooter` 在跨越分頁點的段落中被呼叫。由於 TeX 必須處理整個段落， `\myheader` 以及 `\myfooter` 會再次被呼叫，以重設儲存在「TeX 內部狀態」中的頁首／頁尾文字，產生不符合目前頁面的值。當輸出常式產生頁首和頁尾時，它們將包含原本要給下一頁的文字。

![若輸出常式僅使用 TeX 巨集來擷取頁首和頁尾文字，則會產生錯誤頁首與頁尾的圖示](/files/c22ef0e36d905bee4f12b5594e8ccfa6250592f3)

#### TeX 的非同步行為：\mark 指令來解圍

TeX 的排版（內容構成）活動 *並不同步* 與透過輸出常式中的指令輸出排版頁面的最後過程不同步。當輪到輸出某一頁時，TeX 的實際內部狀態已經超前於分頁位置可能具有的任何狀態——也就是它在目前正在輸出的頁面上排版最後一段內容的地方。

在 [The TeXbook](https://www.amazon.co.uk/TeXbook-Donald-Knuth/dp/0201134489)，TeX 的創造者 Donald Knuth 將此情況描述為

> …… TeX 的輸出常式落後於其頁面構成活動

因為這種不同步的關係，以「特殊設施」形式存在的 *標記機制*，透過 `\mark` （或 `\marks n`）指令， `\mark` 指令會將標記節點嵌入頁面內容中，儲存可供所在頁面的頁首和頁尾使用之內容的記憶體位置。

那本知名且備受推崇的書的作者 [TeX by Topic](https://www.eijkhout.net/tex/tex-by-topic.html) 將 `\mark` 機制描述為

> ……輸出常式可藉此取得目前已截斷頁面內容資訊的主要機制，尤其是其頂端與底端。

該 `\mark` 指令是一種旨在避開 TeX 分頁與輸出常式演算法非同步特性的機制。

![嵌入頁面正文內容中的標記資料（節點）示意圖](/files/1d938d86593576dd89eb2ea41b731ae34abe83cd)

### 輸出前檢查：尋找標記節點

當 TeX 引擎確定分頁位置後，它會呼叫一個內部常式（稱為 `fire_up()`）它會在輸出常式完成頁面構成並將完成頁寫入 PDF 檔案之前，先進行大量前置處理。這些前置處理步驟中的一個重要部分，是設定三個 *全域標記變數*: `\botmark`, `\topmark` 以及 `\firstmark` ，它們用來提供建構頁首和頁尾的資料。當然，一頁可以包含多個標記節點，但這些會經過篩選以產生三個標記變數，其作用如下：

* **`\botmark`** 是目前分頁點生效的標記；亦即在 *目前的* 頁面上看到的最後一個標記
* **`\topmark`** 是 `\botmark` 來自 *前一個* 頁面上看到的最後一個標記
* **`\firstmark`** 是 *目前的* 頁面上的第一個標記——介於 `\topmark` 以及 `\botmark`

下列示意圖顯示預處理排版頁面內容以決定三個全域標記變數值的基本原理 `\botmark`, `\topmark` 以及 `\firstmark`:

![用於決定三個全域標記變數 \botmark、\topmark 與 \firstmark 值的基本原理示意圖](/files/3b378d3eb0acaf9c752c9cd97e0bbfa7006e6dfc)

下一節將使用上文提供的一些說明，概述 LaTeX 如何排版頁首和頁尾。

### LaTeX 的類別：頁面樣式與頁首／頁尾

當您寫下 `\documentclass{*class*}` 其中 `*class*` 可能是 `article`, `book`, `report` 或 `letter`，LaTeX 會載入一個名為 `*class*.cls`；例如， `book.cls`, `article.cls` 等等。這些 `.cls` 檔案包含 LaTeX 程式碼，用來實作各類別特有的各種指令與功能變體，而這些指令與功能預期會存在於所有文件類別中，並由所有文件類別提供——也就是所有文件類別共享的行為。範例可能包括類別特定的頁面版面邊界，以及某些指令的類別特定版本，例如 `\section`, `\subsection` 等等。

另一個重要的 `.cls` 檔案內容，是標準頁面樣式的類別特定實作，它們定義了使用特定類別與頁面樣式產生的文件之頁首與頁尾。例如：

* **`book.cls`** 以及 **`article.cls`** 都包含頁面樣式的實作： `headings` 以及 `myheadings`
* **`letter.cls`** 包含頁面樣式的定義： `headings`, `empty`, `plain` 以及一個名為 `firstpage`
* **`report.cls`** 包含頁面樣式的定義： `headings` 以及 `myheadings`

頁面樣式的實作通常會依賴於 `twoside` 選項 `\documentclass`——文件是單面或雙面——以便針對左頁（偶數頁）與右頁（奇數頁）適當定義頁首和頁尾。

文件類別可能會依賴 `plain` 或 `empty` LaTeX 其他地方提供的頁面樣式預設定義——這些定義位於 `.cls` 正在使用的檔案之外。

#### 更多關於頁面樣式

當您使用下列指令變更頁面樣式時 `\pagestyle{*somestyle*}` 或 `\thispagestyle{*somestyle*}` LaTeX 預期會找到一個名為 `ps@*somestyle*`——通常由文件類別 `.cls` 檔案，或是某個套件，例如 `fancyhdr`。順帶一提，所謂「內部指令」是指屬於 LaTeX 原始碼的一部分，且其名稱包含一個 `@` 符號，通常因此不會被隨意使用（因為其 [類別碼](/latex/zh-tw/geng-duo-zhu-ti/43-table-of-tex-category-codes.md)).

定義 `\ps@*somestyle*` 負責實作該頁面樣式的功能／行為，其中包括提供以下內容的定義：

* 在頁面構成最後階段使用的指令（用來加入頁首和頁尾，請參見 [本文後文](#commands-for-the-output-routine)):
  * `\@oddhead`
  * `\@oddfoot`
  * `\@evenhead`
  * `\@evenfoot`
* 用於插入與節相關的標記資料，以供產生包含節標題、編號等內容的文件頁首和頁尾之用。例如， `headings` 頁面樣式（在 `book.cls`）中定義了下列產生標記的指令：
  * `\chaptermark`
  * `\sectionmark`

我們將 [更進一步探討產生標記的指令](#mark-commands-and-mark-data)，例如 `\chaptermark`，但僅舉例來說：當您寫下 `\chapter{chapter title}` 加上 `\chaptermark` 指令時，也會執行插入與章節相關的標記資料，以供後續產生頁首。

#### 基於頁面樣式的頁首與頁尾指令剖析

本節探討用來建立頁首和頁尾的一些核心 LaTeX 指令／程序——並借用本文前文提出的說明或概念。

**LaTeX 的頁首與頁尾「模型」**

LaTeX（預設）頁首與頁尾的內容是基於文件節層次的兩層架構：一個「較高層級」的節包含多個「較低層級」的節。對 book 和 article 類別而言，這可對應為：

* `**book**` **類別**：「較高層級」的節由 `\chapter` 指令產生，而「較低層級」的節則透過 `\section`，反映出章節中包含多個節。
* `**article**` **類別** （假設是雙面文件）：「較高層級」的節使用 `\section` 指令產生，而「較低層級」的節則透過 `\subsection`，反映出同一節內出現多個子節。

LaTeX 透過其分節指令套用頁首與頁尾的「模型」，這些分節指令會「注入」標記節點（透過 `\mark` 指令）來儲存與正在建立的文件節相關的資料。例如，每次您寫下 `\section{*某節標題*}` 就會建立一個標記節點，其中包含反映 `*某節標題*` 以及節編號的資料。

LaTeX 使用一套標記系統，其中包含以下形式的資料： `{left}{right}`，最終透過 `\mark` 命令

```latex
\mark{{left}{right}}
```

並包裹在多層巨集之中，這些巨集旨在讓使用者免於接觸底層細節——本文後續章節將探討這些細節。

#### 探索 book 類別

當 `book.cls` 檔案載入時，其最後幾個動作之一是執行 `\pagestyle{headings}` ，將預設頁面樣式設為 `headings`，所以我們將更詳細地看看該頁面樣式。

如 [上文所述](#more-on-page-styles)，的功能／屬性 `headings` 頁面樣式，由 `book` 類別所實作，將由（內部）指令 `\ps@headings` 所定義，該指令位於檔案中 `book.cls`。精確定義為 `\ps@headings` 取決於文件是單面還是雙面（預設為 `book` 類別）。

**「標記命令」與「標記資料」**

對於由 `book` 類別產生的雙面文件， `\ps@headings` 會建立（定義）兩個「標記命令」： `\chaptermark` 以及 `\sectionmark` 它們會產生如下形式的「標記資料」 `{left}{right}`，供頁首與頁尾使用。這些標記命令反映了 LaTeX 的頁首與頁尾模型——基於文件章節的階層：

* 「較高層級」的章節命令（這裡是 `\chapter`）用來為左頁（偶數頁）的頁首提供「標記資料」。當你要新增一章時，只要寫入 `\chapter{*chapter title*}`, `\chaptermark` 就會呼叫以建立反映該新章節的「標記資料」——其中包含其 `*chapter title*` 與編號。
* 「較低層級」的章節命令（這裡是 `\section`）用來為右頁（奇數頁）的頁首提供「標記資料」。當你寫入 `\section{*section title*}` 加上 `\sectionmark` 時，會呼叫該命令以建立反映新節的「標記資料」——其中包含其 `*section title*` 與編號。前一個 `\chapter` 所提供的任何標記資料，不會受到之後任何 \section 命令的影響。

**\markboth 和 \markright**

LaTeX 的特定章節標記命令，例如 `\chaptermark` 以及 `\sectionmark`，會使用另外兩個命令，稱為 `\markboth` 以及 `\markright` ，它們提供頁首與頁尾實際使用的標記資料。 `\markboth` 和／或 `\markright` 可以包含用來美化頁首或頁尾文字的命令，例如將其轉為大寫。

對於使用 `book` 類別預設值（雙面、 `headings` 頁面樣式）產生的文件，產生標記的命令運作如下：

* `\chaptermark` 使用 `\markboth`
* `\sectionmark` 產生標記資料 `\markright`

`\markboth` 以及 `\markright` 是巨集，最終會使用原始（內建、低階）的 `\mark` 命令，實際插入由章節命令內部產生的標記。

`\markboth` 形式如下

* **`\markboth{left}{right}`**: 使用 `\mark{{left}{right}}` 插入一個標記（節點），因而形成一個包含一對大括號值的標記節點 `{left}{right}`.

`\markright` 形式如下

* **`\markright{newright}`**: 使用 `\mark{{currentleft}{newright}}`；也就是說，它會變更 `的目前值` 到 `newright` ，但 `的目前值` （即 `currentleft`）保持不變，因而形成一個包含一對大括號值的標記節點 `{currentleft}{newright}`.

的行為 `\markboth` 以及 `\markright` 支援兩層級的章節階層，例如每一章中包含多個節的文件。

以下以（雙面） `book` 文件類別（使用 headings 頁面樣式）為例：

* `\chaptermark` 使用 `\markboth` 來設定：
  * 一個 `{left}` 欄位，將 CHAPTER. 以大寫排版。
  * 一個 {right} 欄位，為空白（`{}`)
* `\sectionmark` 使用 `\markright` 以設定 `{right}` 標記欄位以進行排版，也同樣以大寫。

要理解為什麼 `\sectionmark` 使用 `\markright` 我們可以觀察到，每一個新的 `\section` 命令都應該：

* 插入一個包含此 `\section` 資料的新標記（例如其標題與編號）。
* 但是 *不是* 影響目前 `\chapter` 中的標記資料值 `\section` 命令出現的位置

基於這些原因， `\section` 命令會呼叫 `\sectionmark` ，它使用 `\markright` 來建立一個 `\section`-相關的標記：它會更新 `{right}` 標記資料欄位，但不會影響目前的 `{left}` 標記資料欄位值，而該值是由目前的 `\chapter`.

注意：LaTeX 原始碼包含下列提醒：

> 這些標記命令對於右標記「編號在」左標記之內的情況運作得相當好——例如，左標記會被 \chapter 命令改變，而右標記會被 \section 命令改變。不過，如果同一頁上出現 2 個 \markboth，結果就會有些異常。

**輸出例程的命令**

該 `\ps@headings` 命令實作了下列命令的定義

* `\@oddhead`
* `\@oddfoot`
* `\@evenhead`
* `\@evenfoot`

這些命令由輸出例程在頁面組版最後階段用來加入頁首與頁尾。例如， `book.cls` 將 `\@oddfoot` 以及 `\@evenfoot` 設定為 `\@empty`的值，而其定義為 `\def\@empty{}`，因此 `\ps@headings` 會在左頁與右頁產生空白頁尾：

```latex
\let\@oddfoot\@empty
\let\@evenfoot\@empty
```

頁首定義如下：

```latex
\def\@evenhead{\thepage\hfil\slshape\leftmark}%
\def\@oddhead{{\slshape\rightmark}\hfil\thepage}%
```

其中

* `\leftmark` 會擷取 `{left}` 值自一個 `{left}{right}` 標記對
* `\rightmark` 會擷取 `{right}` 值自一個 `{left}{right}` 標記對
* \thepage 會輸出目前頁碼

從本質上說，這些 `\@evenhead` 以及 `\@oddhead` 會產生以下結果：

* `\@evenhead`：頁碼出現在頁首左側，而 `\leftmark`的內容，並以字型命令 `\slshape`設定樣式後，輸出在頁首右側——中間的空白由高度可伸縮的 `\hfil` 膠。
* `\@oddhead`： `\rightmark`的內容，並以字型命令 `\slshape`的內容出現在頁首左側，而目前頁碼輸出在頁首右側——同樣地，中間的空白由 `\hfil` 膠。

來提供。 *然而，還有一個關鍵問題：* `{left}{right}` 使用了哪個 `\leftmark` 以及 `\rightmark`標記對：換句話說，它們是從哪裡取得標記資料值的？

如 [上文所述](#filtering) 但是 [如下所述](#explainfiltering)，在最終頁面組版階段，文件頁面中包含的任何標記都會被「過濾」並用來設定三個全域標記變數的值： `\botmark`, `\topmark` 以及 `\firstmark`。由於 [LaTeX 標記的結構](#latexs-model-of-headers-and-footers)，這 3 個標記變數中的每一個最終都會包含 `{left}{right}` 值，對應某一對值 `{left}` 以及 `{right}` 並且正是這些值提供了實際的 `{left}{right}` 標記對（欄位），供 `\leftmark` 以及 `\rightmark`:

* `\leftmark` 會擷取 `的目前值` 從 `{left}{right}` 所提供的標記對（欄位）中的值 `\botmark`
* `\rightmark` 會擷取 `的目前值` 從 `{left}{right}` 所提供的標記對 `\firstmark`

注意 `\markboth` 以及 `\markright` 可以包含用來美化頁首與頁尾文字的命令——例如轉成大寫。

### 使用較低階 LaTeX 命令的範例

以下範例示範透過重新定義本文中討論的某些低階（內部）LaTeX 命令（即包含 @ 符號的那些）來變更頁面樣式或頁首與頁尾。我們並不主張用這種方式來變更頁首與頁尾，但對於需要的人（例如套件作者）而言，這是可行的。變更頁首與頁尾的首選解決方案是使用 [`fancyhdr` 套件](https://ctan.org/pkg/fancyhdr?lang=en)，這在一篇 [Overleaf 說明文章](/latex/zh-tw/ge-shi-hua/02-headers-and-footers.md).

#### 定義一個最小頁面樣式

以下範例定義了一個新、極簡的頁面樣式，稱為 `demostyle` ，它使用靜態文字來定義頁首與頁尾，且不依賴章節命令（`\chapter`, `\section` 等）來設定頁首與頁尾內容。

範例一開始會將 `@` \`@\` `\ps@demostyle` 命令透過重新定義（在輸出例程中）用來產生頁首與頁尾的命令，實作我們的最小頁面樣式： `\@oddhead`, `\@oddfoot`, `\@evenhead` 以及 `\@evenfoot`。注意 `twoside` 選項在我們的 `\documentclass` 宣告中——它使用 `article` 類別。

```latex
\documentclass[twoside]{article}
\catcode`@=11
\newcommand{\ps@demostyle}{%
\renewcommand\@oddfoot{\hfil 奇數頁頁尾\hfil}%
\renewcommand\@evenfoot{\hfil 偶數頁頁尾\hfil}%
\renewcommand\@evenhead{\thepage\hfil 偶數頁頁首}%
\renewcommand\@oddhead{奇數頁頁首\hfil\thepage}}
\catcode`@=12
\title{示範頁面樣式}
\author{Overleaf}
\date{2022 年 8 月}
\begin{document}
\pagestyle{demostyle}
\maketitle
\newpage
\section{簡介}
\newpage
\section{更多內容}
\end{document}
```

[在 Overleaf 中開啟此範例](https://www.overleaf.com/docs?engine=pdflatex\&snip_name=Creating+a+new+page+style\&snip=%5Cdocumentclass%5Btwoside%5D%7Barticle%7D%0A%5Ccatcode%60%40%3D11%0A%5Cnewcommand%7B%5Cps%40demostyle%7D%7B%25%0A%5Crenewcommand%5C%40oddfoot%7B%5Chfil+The+odd-page+footer%5Chfil%7D%25%0A%5Crenewcommand%5C%40evenfoot%7B%5Chfil+The+even-page+footer%5Chfil%7D%25%0A%5Crenewcommand%5C%40evenhead%7B%5Cthepage%5Chfil+The+even-page+header%7D%25%0A%5Crenewcommand%5C%40oddhead%7BThe+odd-page+header%5Chfil%5Cthepage%7D%7D%0A%5Ccatcode%60%40%3D12%0A%5Ctitle%7BDemonstrating+a+page+style%7D%0A%5Cauthor%7BOverleaf%7D%0A%5Cdate%7BAugust+2022%7D%0A%5Cbegin%7Bdocument%7D%0A%5Cpagestyle%7Bdemostyle%7D%0A%5Cmaketitle%0A%5Cnewpage%0A%5Csection%7BIntroduction%7D%0A%5Cnewpage%0A%5Csection%7BMore+material%7D%0A%5Cend%7Bdocument%7D)

#### 變更 book 類別的頁首

以下範例透過重新定義 `\@oddhead` 以及 `\@evenhead` 來變更 book 類別的頁首，使左頁與右頁頁首包含目前頁碼與章節標題。請注意第二個 `\chapter` 命令使用了可選的章節短標題 `[The short title]` ，現在由它提供頁首文字，而不是長標題。

`\section` 命令對頁首內容沒有影響，因為我們的頁首重新定義只使用 `\leftmark` ，它會取得目前章節的詳細資訊。若要存取目前節的詳細資訊，我們需要在 `\rightmark` 的定義中使用 `\@oddhead` 和／或 `\evenhead`.

```latex
\documentclass{book}
% 注意：'@' 的類別碼暫時改為 11
% 以允許在命令名稱中使用它
\catcode `@=11
% 讓 \@oddfoot 與 \evenfoot 等同於 \@empty
% 使它們成為空白（空）
\let\@oddfoot\empty\let\evenfoot\empty
% 重新定義 \@oddhead 與 \@evenhead
\renewcommand{\@oddhead}{\leftmark\hfil\thepage}
\renewcommand{\@evenhead}{\thepage\hfil\leftmark}
\catcode `@=12
% 使用一個方便的小頁面尺寸
\usepackage[paperheight=16cm,paperwidth=12cm,textwidth=10cm]{geometry}
\title{\TeX{} 使用者的回憶錄}
\author{Overleaf}
\begin{document}
\frontmatter
\maketitle
這是使用羅馬數字的前置內容。
\mainmatter
\chapter{我該從哪裡開始？}
第 1 章：一個短章。
\newpage
\section{一開始...}
一個節。
\chapter[短標題]{一個標題很長、因此不適合用於頁首的章節}
\newpage
\section{另一個節}
內容很少。
\newpage
\section{什麼，另一個節？}
內容也很少。
\end{document}
```

[在 Overleaf 中開啟此範例](https://www.overleaf.com/docs?engine=pdflatex\&snip_name=Changing+headers+for+the+book+class\&snip=%5Cdocumentclass%7Bbook%7D%0A%25+NB%3A+category+code+of+%27%40%27+temporarily+changed+to+11%0A%25+to+enable+its+use+in+command+names%0A%5Ccatcode+%60%40%3D11%0A%25+Let+%5C%40oddfoot+and+%5Cevenfoot+be+equivalent+to+%5C%40empty%0A%25+to+make+them+blank+%28empty%29%0A%5Clet%5C%40oddfoot%5Cempty%5Clet%5Cevenfoot%5Cempty%0A%25+Redefine+%5C%40oddhead+and+%5C%40evenhead%0A%5Crenewcommand%7B%5C%40oddhead%7D%7B%5Cleftmark%5Chfil%5Cthepage%7D%0A%5Crenewcommand%7B%5C%40evenhead%7D%7B%5Cthepage%5Chfil%5Cleftmark%7D%0A%5Ccatcode+%60%40%3D12%0A%25+Use+a+conveniently+small+page+size%0A%5Cusepackage%5Bpaperheight%3D16cm%2Cpaperwidth%3D12cm%2Ctextwidth%3D10cm%5D%7Bgeometry%7D%0A%5Ctitle%7BMemoirs+of+a+%5CTeX%7B%7D+user%7D%0A%5Cauthor%7BOverleaf%7D%0A%5Cbegin%7Bdocument%7D%0A%5Cfrontmatter%0A%5Cmaketitle%0AThis+is+frontmatter+which+uses+Roman+numerals.%0A%5Cmainmatter%0A%5Cchapter%7BWhere+do+I+start%3F%7D%0AChapter+1%3A+A+short+chapter.%0A%5Cnewpage%0A%5Csection%7BIn+the+beginning...%7D%0AA+section.%0A%5Cchapter%5BThe+short+title%5D%7BA+chapter+with+a+very+long+title%2C+making+it+unsuitable+for+headers%7D%0A%5Cnewpage%0A%5Csection%7BAnother+section%7D%0AWith+little+content.%0A%5Cnewpage%0A%5Csection%7BWhat%2C+another+section%3F%7D%0AAlso+with+little+content.%0A%5Cend%7Bdocument%7D)

#### 使用 ε-TeX 擴充的標記命令

該 `book` 類別使用以下的 `\@evenhead` 以及 `\@oddhead` 來產生頁首：

```latex
\def\@evenhead{\thepage\hfil\slshape\leftmark}
\def\@oddhead{{\slshape\rightmark}\hfil\thepage}
```

其中命令 `\leftmark` 以及 `\rightmark` 被定義為

```latex
\def\leftmark{\expandafter\@leftmark\botmark\@empty\@empty}
\def\rightmark{\expandafter\@rightmark\firstmark\@empty\@empty}
```

使用 ε-TeX 的擴充命令，以及 ε-TeX 的標記類別 0 與原始 TeX 命令之間的等價關係，我們可以：

* 將 `\leftmark` 來替換 `\botmark` 改寫成與 ε-TeX 的 `\botmarks0` 等價，並將
* 將 `\rightmark` 來替換 `\firstmark` 改寫成與 ε-TeX 的 `\firstmarks0` 視為等價

這些重新定義會產生

```latex
\renewcommand{\leftmark}{\expandafter\@leftmark\botmarks0\relax\@empty\@empty}
\renewcommand{\rightmark}{\expandafter\@rightmark\firstmarks0\relax\@empty\@empty}
```

請注意使用 `\relax` 來終止 TeX 對更多數字的搜尋——你也可以用空白 `\relax` 來作為終止符。

以下範例輸出的結果與 `\leftmark` 以及 `\rightmark`.

```latex
\documentclass{book}
% 重新定義 \leftmark 和 \rightmark 以使用
% 分別使用 \botmarks0 和 \firstmarks0
% 注意：'@' 的類別碼暫時改為 11
% 以允許在命令名稱中使用它
\catcode `@=11
\renewcommand{\leftmark}{\expandafter\@leftmark\botmarks0\relax\@empty\@empty}
\renewcommand{\rightmark}{\expandafter\@rightmark\firstmarks0\relax\@empty\@empty}
\catcode `@=12
% 使用一個方便的小頁面尺寸
\usepackage[paperheight=16cm,paperwidth=12cm,textwidth=10cm]{geometry}
\title{\TeX{} 使用者的回憶錄}
\author{Overleaf}
\begin{document}
\frontmatter
\maketitle
這是使用羅馬數字的前置內容。
\mainmatter
\chapter{我該從哪裡開始？}
第 1 章：一個短章。
\newpage
\section{一開始...}
一個節。
\newpage
另一個節。
\end{document}
```

[在 Overleaf 中開啟此範例](https://www.overleaf.com/docs?engine=pdflatex\&snip_name=Using+%CE%B5-TeX%E2%80%99s+extended+marks+command\&snip=%5Cdocumentclass%7Bbook%7D%0A%25+Redefine+%5Cleftmark+and+%5Crightmark+to+use+%0A%25+%5Cbotmarks0+and+%5Cfirstmarks0+respectively%0A%25+NB%3A+category+code+of+%27%40%27+temporarily+changed+to+11%0A%25+to+enable+its+use+in+command+names%0A%5Ccatcode+%60%40%3D11%0A%5Crenewcommand%7B%5Cleftmark%7D%7B%5Cexpandafter%5C%40leftmark%5Cbotmarks0%5Crelax%5C%40empty%5C%40empty%7D%0A%5Crenewcommand%7B%5Crightmark%7D%7B%5Cexpandafter%5C%40rightmark%5Cfirstmarks0%5Crelax%5C%40empty%5C%40empty%7D%0A%5Ccatcode+%60%40%3D12%0A%25+Use+a+conveniently+small+page+size%0A%5Cusepackage%5Bpaperheight%3D16cm%2Cpaperwidth%3D12cm%2Ctextwidth%3D10cm%5D%7Bgeometry%7D%0A%5Ctitle%7BMemoirs+of+a+%5CTeX%7B%7D+user%7D%0A%5Cauthor%7BOverleaf%7D%0A%5Cbegin%7Bdocument%7D%0A%5Cfrontmatter%0A%5Cmaketitle%0AThis+is+frontmatter+which+uses+Roman+numerals.%0A%5Cmainmatter%0A%5Cchapter%7BWhere+do+I+start%3F%7D%0AChapter+1%3A+A+short+chapter.%0A%5Cnewpage%0A%5Csection%7BIn+the+beginning...%7D%0AA+section.%0A%5Cnewpage%0AAnother+section.%0A%5Cend%7Bdocument%7D)

**關於 ε-TeX 的註記**

如 [我們前面提到](#etex)，ε-TeX 引入了 `\marks` 命令的更多內容：

```latex
\marks n {要儲存的內容}
```

，它擴充了原始的 `\mark` Knuth 的 TeX 引擎功能，且可在三個主流 TeX 引擎中使用：pdfTeX、LuaTeX 和 XeTeX。

ε-TeX 的實作也引入了新的全域標記變數 `\firstmarks n`, `\botmarks n` 以及 `\topmarks n`：每個類別各一個 `n`。這些以類別為基礎的標記變數是依照上述機制確定的 [，並且都可用於產生頁首與頁尾。](#pre-output-checks-looking-for-mark-nodes)你可以在

的重新定義中使用任一 $$2^{15}$$ = 32768 個標記類別，範圍從 0 到 32767。 `\leftmark` 或 `\rightmark`。這假設你已為所選的標記類別提供（插入）適當的標記資料， `n`，透過 `\marks n{{left}{right}}`.

請注意，LuaTeX 提供 $$2^{16}$$ = 65536 個標記類別， `n` 範圍從 0 到 65535。

**使用標記類別 10 的範例**

此範例示範使用標記類別 10（隨機選擇）來建立文件頁首。這份範例文件排版了 6 頁，其中包含本文末尾的 [已完成範例](#a-worked-example-to-show-how-tex-engines-determine-values-for-botmark-topmark-and-firstmark)。研究該範例將可看出為什麼 TeX 引擎為每個頁首選擇了特定的標記， $$\alpha$$, $$\beta$$, $$\gamma$$ 以及 $$\delta$$。

我們先定義一個方便的巨集， `\domark`，它使用 `\marks 10` 來插入符合 LaTeX `{left}{right}` 慣例的標記資料：

```latex
\newcommand{\domark}[2]{\marks 10{{#1}{#2}}}
```

透過重新定義 `\leftmark` 以及 `\rightmark`，讓 10 類別的標記可供 LaTeX 的頁首與頁尾機制使用——並額外加入一些資訊：

```latex
\renewcommand{\leftmark}{\expandafter\@leftmark\botmarks10 \@empty\@empty{}（經由 \texttt{\string\botmarks10}）}
\renewcommand{\rightmark}{\expandafter\@rightmark\firstmarks10 \@empty\@empty{}（經由 \texttt{\string\firstmarks10}）}
```

在插入若干（10 類別）標記之後，TeX 引擎（這裡是 pdfTeX）會乖乖地為 10 類別建立全域標記變數——`\botmarks10`, `\topmarks10` 以及 `\firstsmarks10` 然而，還有一個關鍵問題： `\leftmark` 以及 `\rightmark` 用來產生頁首。

```latex
\documentclass{book}
% 一個用於搭配 10 類別標記的簡短命令
% 並遵循 LaTeX 的標記結構 {left}{right}
\newcommand{\domark}[2]{\marks 10{{#1}{#2}}}
% 重新定義 \leftmark 和 \rightmark 以使用
% 分別對應 \botmarks10 和 \firstmarks10
\catcode`@=11
\renewcommand{\leftmark}{\expandafter\@leftmark\botmarks10 \@empty\@empty{}（經由 \texttt{\string\botmarks10}）}
\renewcommand{\rightmark}{\expandafter\@rightmark\firstmarks10 \@empty\@empty{}（經由 \texttt{\string\firstmarks10}）}
\catcode`@=12
\title{展示 \(\varepsilon\)-\TeX{} 的增強標記}
\author{Overleaf}
\date{2022 年 8 月}
\begin{document}

第 1 頁：未新增標記，因此所有標記變數仍處於初始化狀態：空白（NULL）。

\newpage
第 2 頁：透過

\verb|\domark{$\alpha$-left}{$\alpha$-right}|\domark{$\alpha$-left}{$\alpha$-right}

\newpage
第 3 頁：此頁未新增任何標記。

\newpage
第 4 頁：先加入 $\beta$ 標記，再加入 $\gamma$ 標記，透過

\verb|\domark{$\beta$-left}{$\beta$-right}|

\verb|\domark{$\gamma$-left}{$\gamma$-right}|。
\domark{$\beta$-left}{$\beta$-right}
\domark{$\gamma$-left}{$\gamma$-right}

\newpage
第 5 頁：透過

\verb|\domark{$\delta$-left}{$\delta$-right}|
\domark{$\delta$-left}{$\delta$-right}

\newpage
第 6 頁：此頁未新增任何標記。
\end{document}
```

[在 Overleaf 中開啟此範例](https://www.overleaf.com/docs?engine=pdflatex\&snip_name=Using+e-TeX+global+marks+variables\&snip=%5Cdocumentclass%7Bbook%7D%0A%25+A+short+command+to+use+marks+with+class+10%0A%25+and+following+LaTeX%27s+mark+structure+%7Bleft%7D%7Bright%7D%0A%5Cnewcommand%7B%5Cdomark%7D%5B2%5D%7B%5Cmarks+10%7B%7B%231%7D%7B%232%7D%7D%7D%0A%25+Redefine+%5Cleftmark+and+%5Crightmark+to+use+%0A%25+%5Cbotmarks10+and+%5Cfirstmarks10+respectively%0A%5Ccatcode%60%40%3D11%0A%5Crenewcommand%7B%5Cleftmark%7D%7B%5Cexpandafter%5C%40leftmark%5Cbotmarks10+%5C%40empty%5C%40empty%7B%7D+%28via+%5Ctexttt%7B%5Cstring%5Cbotmarks10%7D%29%7D%0A%5Crenewcommand%7B%5Crightmark%7D%7B%5Cexpandafter%5C%40rightmark%5Cfirstmarks10+%5C%40empty%5C%40empty%7B%7D+%28via+%5Ctexttt%7B%5Cstring%5Cfirstmarks10%7D%29%7D%0A%5Ccatcode%60%40%3D12%0A%5Ctitle%7BDemonstrating+%5C%28%5Cvarepsilon%5C%29-%5CTeX%E2%80%99s+enhanced+marks%7D%0A%5Cauthor%7BOverleaf%7D%0A%5Cdate%7BAugust+2022%7D%0A%5Cbegin%7Bdocument%7D%0A%0APage+1%3A+No+marks+added+so+all+mark+variables+remain+in+their+initialized+state%3A+empty+%28NULL%29.%0A%0A%5Cnewpage%0APage+2%3A+%24%5Calpha%24-mark+added+to+this+page+via%0A%0A%5Cverb%7C%5Cdomark%7B%24%5Calpha%24-left%7D%7B%24%5Calpha%24-right%7D%7C%5Cdomark%7B%24%5Calpha%24-left%7D%7B%24%5Calpha%24-right%7D%0A%0A%5Cnewpage%0APage+3%3A+No+new+marks+added+to+this+page.%0A%0A%5Cnewpage%0APage+4%3A+%24%5Cbeta%24-mark+followed+by+%24%5Cgamma%24-mark+added+to+this+page+via%0A%0A%5Cverb%7C%5Cdomark%7B%24%5Cbeta%24-left%7D%7B%24%5Cbeta%24-right%7D%7C%0A%0A%5Cverb%7C%5Cdomark%7B%24%5Cgamma%24-left%7D%7B%24%5Cgamma%24-right%7D%7C.%0A%5Cdomark%7B%24%5Cbeta%24-left%7D%7B%24%5Cbeta%24-right%7D%0A%5Cdomark%7B%24%5Cgamma%24-left%7D%7B%24%5Cgamma%24-right%7D%0A%0A%5Cnewpage%0APage+5%3A+%24%5Cdelta%24-mark+added+to+this+page+via%0A%0A%5Cverb%7C%5Cdomark%7B%24%5Cdelta%24-left%7D%7B%24%5Cdelta%24-right%7D%7C%0A%5Cdomark%7B%24%5Cdelta%24-left%7D%7B%24%5Cdelta%24-right%7D%0A%0A%5Cnewpage%0APage+6%3A+No+marks+added+to+this+page.%0A%5Cend%7Bdocument%7D)

下列圖形顯示此範例所產生的頁首：

![顯示使用 ε-TeX 擴充標記命令的 LaTeX 程式碼輸出之圖形](/files/ab88d519763d16c22dd8c1fe1aa82bf43733a1fb)

* **注意**：第 1 頁的頁首是在 class 10 的三個標記變數——`\botmarks10`, `\topmarks10` 以及 `\firstsmarks10`——皆為空白時建立的。最後的結果是 `\rightmark` （針對第 1 頁）只插入最後一段頁首片段： `（透過 \texttt{\string\firstmarks10}）`，如上圖所示。

## 一個完成的範例，說明 TeX 引擎如何決定 \botmark、\topmark 與 \firstmark 的值

![Db.gif](/files/9a66e4a34af5bd3948cb7c3722d59c90576d9aa4)![Db.gif](/files/9a66e4a34af5bd3948cb7c3722d59c90576d9aa4) 模仿 Knuth 使用雙重危險彎折符號似乎很合適（圖片來源於 [這個網站](http://www.truetex.com/db.htm)），因為這部分內容相當低階、且有點「窺視內部」——雖然我們希望它能引起勇於探索的讀者興趣，幫助理解更多細節。

以下討論主要基於 [The TeXbook](https://www.amazon.co.uk/TeXbook-Donald-Knuth/dp/0201134489)第 258 頁最後一個範例。在這裡，我們已對該範例進行編輯與重新排版，供無法取得 The TeXbook 的讀者使用：

![來自 The TeXbook 第 258 頁範例的編輯與重新排版本](/files/54dab669281f2b8c543d6f88b649a31d14c85665)

此圖中呈現的表格是根據 Knuth 提供的表格重製而來，但和原始版本一樣，並未說明其推導方式。這裡我們將補充說明，以提供更多細節，展示這些 `\botmark`, `\topmark` 以及 `\firstmark` 值是如何決定的——取得這些細節需要探究 TeX 引擎的原始碼！

設定 `\botmark`, `\topmark` 以及 `\firstmark` 值的過程，可用以下圖表及後續說明中的 3 段簡短偽程式碼來概括：

1. 內容前檢查
2. 內容迴圈
3. 內容後檢查

注意：

* 步驟 1「內容前檢查」與步驟 3「內容後檢查」的測試會在每一頁執行一次。
* 步驟 2「內容迴圈」涉及 TeX 掃描頁面內容，搜尋標記節點，並使用它們設定如圖所示的標記變數值。

![顯示 TeX 引擎用來設定三個全域標記變數值之程式碼的偽程式碼版本的圖片](/files/bd8676abb0edb82269ce2ee33b2067260d760b92)

每當 TeX 找到合適的分頁點時，就會將該頁內容送去做最終處理，對文件中的每一頁執行這三個標記處理步驟。標示為 1、2 與 3 的處理步驟會在本節最後的圖表中引用。

請注意：

* 在第一頁處理之前，所有三個全域標記變數 `\botmark`, `\topmark` 以及 `\firstmark` 都已初始化為空白（NULL）。
* 的最終值 `\botmark`, `\topmark` 以及 `\firstmark` 在……結尾時決定的 *前一個* 頁面會成為用於處理……的（輸入）值 *目前的* 頁面——也就是正在處理的那一頁。

以下程式碼片段是 ***偽程式碼*** 根據實際 TeX 引擎原始碼歸納（摘要）而來。其目的是提供簡潔的程式碼摘要，以幫助說明所涉及的關鍵原則。

**步驟 1：內容前檢查** 在 TeX 檢視目前頁面的實際內容之前，它會先檢查目前的 `\botmark` 值——亦即由……產生的 *前一個* 頁面——是否為空（NULL）：

```latex
if(\botmark != NULL)
{
   \topmark = \botmark;
   \firstmark= NULL;
}
```

在此測試中，如果 `\botmark` （來自 *前一個* 頁面）的……是 *不是* 空的：

* `\topmark` 對於 *目前的* 頁面會被設為 *前一個* 頁面的 `\botmark` 值
* `\firstmark`，對於 *目前的* 頁面（正在處理的），會設為空（NULL）：以假設此頁沒有任何 `\mark` 節點。

請注意，此測試會 *一次* 針對每一個正在處理的頁面執行。

**步驟 2：內容迴圈** 接著，TeX 會遍歷頁面內容；該迴圈的一部分包含尋找 `\mark` 節點：

```latex
if(type(node)==mark_node)
{
   if(\firstmark==NULL)
   {
      \firstmark=node(text);
   }
   \botmark= node(text);
}
```

此測試會套用到目前頁面內的每一個標記節點。前一個測試（上面的步驟 1）可能已將 `\firstmark` 設為空，因此在目前頁面中找到的第一個標記會成為 `\firstmark` 以及 `\botmark`。 *後續的* `\mark` 在執行此迴圈時偵測到的節點不會改變 `\firstmark`的值，因為它已不再是空的（NULL），但 `\botmark` 會更新——因為這正是 `\botmark`的目的：用來儲存最後看到的 `\mark` 此頁面的節點。

**步驟 3：內容後檢查** 一旦遍歷完頁面內容，TeX 會套用以下最後一個測試，而這個測試同樣每頁只會執行一次：

```latex
if((\topmark != NULL)&&(\firstmark==NULL))
{
	\firstmark= \topmark;
}
```

* 如果 `\topmark` 不是空的（NULL），我們就知道，根據步驟 1， `\botmark` 前一頁的……不是空的（NULL）。
* 如果 `\firstmark` 仍然是空的，那麼我們就還沒有看到任何 `\mark` 節點，因為步驟 2 中的測試沒有被觸發；因此， `\firstmark` 會被設為 `\topmark` 這也同時是 `\botmark` ，來自前一頁的值。

在我們的範例中，依序將這三個步驟套用到全部 6 個頁面之後， `\botmark`, `\topmark` 以及 `\firstmark` 的值會透過下方圖示所示的轉換推導出，其中：

* 淺藍色圓角矩形表示各頁的最終標記變數值
* 淺灰色圓角方塊表示處理過程中產生的中間值
* 帶有標籤的箭頭表示依據上方圖示中的步驟 1、2 或 3 所造成的標記變數值變化——或表示該值未被任何步驟變更；標示為「未變更」。

以下圖示說明這三段偽程式碼如何產生 `\botmark`, `\topmark` 以及 `\firstmark` ，列於《The TeXbook》第 258 頁所載的表格中。

![一個配合《The TeXbook》第 258 頁範例的轉換圖，顯示全域標記變數被指派其值時的內部轉換](/files/2bcce2996cb72a322b9122828bf48496ff6bc2de)


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://overleaf-pro.ayaka.space/latex/zh-tw/shen-ru-wen-zhang/23-how-does-latex-typeset-headers-and-footers.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
