LaTeX 教學系列:sty 與 cls 檔

latex

建立自己的 sty 與 cls 檔

Author

Anthony Sung

Published

July 29, 2024

在資訊與網路發達的時代,我們幾乎可以很快速地查詢到我們想要知道的任何資訊,其中包含程式碼的套件、模板等。但是,當我們使用一個程式碼語言一段時間後,可能會有自己使用的心得,或是想要簡化、省略某些複雜的設定,因此萌生自己寫套件將模板分享出去的心態。使用 LaTeX 也會有這樣的一天,因此這篇文章將教會你如何建立自己的套件與模板,能夠將更好、更簡便的內容分享出去。

LaTeX 中有兩個重要的檔案名稱,分別是 .sty 檔與 .cls 檔。LaTeX 的 .sty.cls 文件都是用來定義文件樣式的,但它們的用途和使用方式有些不同。.sty 檔可以比喻作一個工具箱,裡面放著各種可以隨時拿來用的小工具。你想要什麼功能,就把對應的 .sty 文件引入你的文件中。它不會改變整個文件的基本結構,而是提供一些額外的功能或者微調樣式。.cls 檔則更像是一個完整的模板,它定義了整個文件的基本結構和外觀。當你使用 .cls 文件時,你是在選擇一種特定類型的文件,比如說論文、報告或者書信。

特性 .sty 文件 .cls 文件
用途 提供額外功能或微調樣式 定義整個文件的基本結構和外觀
使用方式 \usepackage 指令引入 \documentclass 指令指定
靈活性 可以同時使用多個 通常只使用一個
影響範圍 局部,只影響特定功能 全局,影響整個文件
常見用途 新增特殊功能,如數學符號、圖表等 設定整體格式,如論文、報告模板

在這篇文章中,你會學到:

由於之前的文章內容較著重在應用層面,忽略了如何使用 LaTeX 中較為程式一點的功能,例如 \makeatletter\makeatother 等,或是計數器的概念,本篇文章將會一一介紹。本文也參考官方文件sty和cls文档编写指南的內容進行些許翻譯與個人闡述。

認識 sty 與 cls

一個 .sty 套件含能夠改變文件外觀的所有指令,當建立一份 .sty 檔之後,就將主文件(通常來說都是 .tex 檔)的前言區變乾淨了,僅需使用 \usepackage{sty 檔名} 即可。

任何有關文件外觀調整的內容,都可以放在 ..sty 檔案中。例如想要設定超連結的顏色,就可以使用 \RequirePackage{hyperref},然後使用 \hypersetup{...} 設定超連結顏色即可。但是必須注意到,.sty 檔案畢竟還是設定文件的風格(style),如果只是把 .sty 檔當作前言區的延伸(extension),單純避免文件的前言區太過雜亂,大可不必另開一個新的 .sty 檔。1

你可能會想說:「不是用 \usepackage 嗎?怎麼跑出一個我沒看過的東西呢?」這正是本篇要講述的內容。接著,就讓我們進入到建立 .sty 檔案的環節與其中的設定吧!

基本架構

一個好的 .cls.sty 檔,不只是要讓作者自己在數個月甚至數年之後回頭點開這份文件能夠看懂,更要考慮到如果發布到網路上供其他使用者使用時,也能夠明白這份檔案設定的內容是什麼。用樹狀圖的方式,可以更好的呈現一個類別檔案或套件檔案的基本架構。

MyPackage.sty
├── sty 檔資訊
│
├── 引入需要的套件
│
├── 定義新指令
│
├── 定義新環境
│
├── 設定頁面佈局
│
├── 設定字體和段落樣式
│
└── ...

一份好的 .cls.sty 文件架構,不僅可以助人又助己。

案資訊

如同文件的標題頁一樣,.cls.sty 檔也需要一個資訊區,讓使用者一眼就可以了解這份文件的作者是誰、建立日期、如果有錯誤應該聯繫何人等。通常來說,這些基本資訊都會以註解的方式呈現。以下用 .sty 檔當作範例:

% --------------------------------------------------
% 簡單的 sty 檔案範例
% Simple LaTeX Style File Example
% --------------------------------------------------
% Version       : 1.0
% Author        : 你的名字 <your_email@example.com>
% Repository    : https://github.com/yourusername/Simple-LaTeX-Style
% Document      : https://yourusername.github.io/Simple-LaTeX-Style/
% Last Updated  : YYYY.MM.DD

接著我們就要加入檔案的文件介紹。首先,我們要使用 \NeedsTeXFormat{} 來確保文件使用的是 LaTeX2e 版本。LaTeX2e 是目前最常用的 LaTeX 版本,它在1994年推出,是對原始 LaTeX 系統的一次重大更新。你可以把 LaTeX2e 想像成一個升級版的文字處理軟體,專門用來排版複雜的文件,特別是包含大量數學公式和專業格式的文章。

將版本資訊告訴電腦後,我們還需要定義這份 .cls 以及 .sty 檔的類別名稱與敘述,方便使用者在主文件引入。.cls 使用的方式為 \ProvidesClass{類別名稱}[時間與其他資訊],而 .sty 檔則要用 \ProvidesPackage{文件名稱}[時間與其他資訊]

筆者在此處建議,文件名稱通常不會超過十個字符(不包含副檔名),主要原因是避免文件名稱太冗長,在引入上較為不便。另外,文件名稱也應該盡量避免與現有的 LaTeX 套件名稱衝突,否則在引入時,電腦會無所適從,不曉得目前所要引入的套件是自己撰寫的 .sty 檔還是既有的套件,類別文件也是同理。

引用其他套件

任何作品都是在前人的努力之下堆疊起來的,LaTeX 的 .cls.sty 檔也不例外。在創建類別文件與套件時,我們有時需要使用既有的類別或套件來幫助我們完成設定。

建議引入套件指令

官方文件提供四個建議指令讓我們引入套件:分別是 \LoadClass\LoadClassWithOptions\RequirePackage\RequirePackageWithOptions。以下用表格分別說明:

方法 用途
\LoadClass{cls 檔名} 載入其他類別文件
\LoadClassWithOptions{cls 檔名} 載入其他類別文件並傳遞選項
\RequirePackage{sty 檔名} 載入其他套件
\RequirePackageWithOptions{sty 檔名} 載入其他套件並傳遞選項

你可能會疑惑:「什麼叫傳遞選項?」所謂傳遞選項,指的是將選項從主文件或樣式文件傳遞給類別文件或套件,以便這些類別文件或套件能夠根據這些選項來調整其行為或設定。

假設你有一個自定義的類文件 mypackage.sty,希望載入 hyperref 套件並傳遞 colorlinks=true,linkcolor=blue 這個選項,我們在主文件與 .sty 應該這樣處理。

在主文件中,我們想要引入 mypackage.sty

.sty 檔中,就應該使用 \RequirePackageWithOptions{hyperref},將傳遞選項給 hyperref 套件。上述例子中,colorlinks=truelinkcolor=blue 這兩個選項會被傳遞給 hyperref 套件,從而設定超連結顏色為藍色。

同理,假設你有一個自定義的類文件 myclass.cls,希望根據 article 類並傳遞 a4paper12pt 這兩個選項,在主文件中,我們會在前言區告訴 LaTeX 我們要使用 myclass 這個自定義類別:

在類別文件中就應該使用 \LoadClassWithOptions{article},將傳遞選項給 article 類別。

使用傳遞選項的原因很簡單,就是增加類別和套件的可重用性,因為不同的文件可能需要不同的配置。,此外,在主文件中我們可以集中管理這些選項,避免了在前言區又使用相同的選項

不建議引入套件方式

使用 \input 指令來引入其他文件有多種潛在問題,因此不建議在載入類別文件或套件時使用。這些問題包括難以管理、潛在的錯誤和資源浪費等。

  • 無法追蹤:使用 \input 載入的文件不會列在 \listfiles 列表中,這會使得文件管理變得困難。2

  • 重複載入:如果一個包多次使用 \input 載入,它會每次都重新載入,浪費時間和內存,並可能導致奇怪的行為。

  • 選項處理問題:使用 \input 無法正確處理選項,而使用 \RequirePackage\usepackage 能夠處理套件的選項,避免選項處理錯誤。

  • 錯誤警告:如果一個套件 foo.sty 使用 \input 載入另一個套件 baz.sty,會出現警告,提示載入的套件和實際提供的套件不一致,造成混淆。

定義新指令

其實定義新指令的方式在之前的文章中都有約略提及,但在此處就以更清晰、架構化的方式呈現,並說明一些先前沒有講述的內容。

在 LaTeX 中,定義新指令可以使文件更易於維護。定義新指令的方式分為定義新的指令,即 \newcommand,或是重新定義既有指令,\renewcommand。此小節專門講述如何使用這兩個方式定義新的指令。

newcommand

\newcommand 用於創建新的 LaTeX 指令,它的基本語法為 \newcommand{\指令名稱}[參數數量][第一個參數的默認值]{定義內容},其中:

  • \commandname:新指令的名稱,通常以 \ 開頭。
  • num:為可選參數,定義參數的數量,最多可以是 9 個。如果省略,則默認為 0。
  • default:同樣為可選參數,第一個參數的默認值。如果省略,則無默認值。
  • definition:指令的定義內容。

由於 TeXLive 無法引入自定義的 .sty 檔,因此以下就用一般的 LaTeX 文件當作範例。

renewcommand

\renewcommand 用於修改既有的指令。其語法與 \newcommand 類似:\renewcommand{\新指令名稱}[參數數量][第一個參數的默認值]{定義內容},唯一變動的是定一個參數,必須與原本的參數名稱不同(否則也沒有重新定義的必要)。

一些注意事項

在一些情況下,如移動指令或表格內,需使用 \protect 來防止指令執行錯誤,不過主要的功能還是保護脆弱指令3。假設我們定義了一個自定義指令,用來顯示當前日期:

\newcommand{\todaydate}{\today}

如果我們在移動指令(如章節標題)或表格中直接使用這個指令,可能會導致編譯錯誤或意外行為。

在這個例子中,直接在章節標題和表格中使用 \todaydate 可能會導致編譯錯誤。解決方法是使用 \protect 來保護這些指令,防止它們在不合適的地方展開。

另外,我們可以使用 \providecommand,在指令尚未定義時進行定義,避免覆蓋已存在的指令。例如使用 \providecommand{\newname}{Alice Johnson},如果 \newname 尚未定義,此行程式碼會定義它;如果已經定義,則不會覆蓋現有的定義。簡單來說,這個指令的使用時機就是當你不確定你是否在文件內定義過某個指令時,它會幫助你定義尚未定義過的指令,如果定義過就沒事。

其他定義新指令的方式

\CheckCommand 可以用來檢查某個指令是否已經被定義為特定的內容。如果該指令與期望的定義不匹配,LaTeX 會產生一個錯誤信息。這在編寫 .sty.cls 檔時特別有用,可以確保指令未被無意修改。使用方法為 \CheckCommand{\欲檢查的指令名稱}{期望的指令定義內容}。例如 \CheckCommand{\chapter}{\oldchapter} 可以檢查 \chapter 指令是否被定義為 \oldchapter

\def 則用於定義新的指令或重定義已有的指令。與 LaTeX 的 \newcommand\renewcommand 相比,\def 確實會更加靈活,但也更危險,因為它不會進行任何檢查4,因此容易覆蓋已有指令並引起潛在的衝突和錯誤,所以要避免使用。使用方法是 \def\指令名稱#1#2{定義內容},其中 #1#2 為指令的參數,參數數量不限,由此就可以看出較傳統定義新指令的方式更為靈活。

\let 指令的主要功能是將一個指令名稱重新分配給另一個指令名稱。這樣做的好處是可以保存某個指令的原始定義,然後重新定義這個指令,而在需要時可以恢復到原來的定義。例如想重新定義 \section 指令來新增一些額外的功能,但仍然需要保留原來的 \section 指令,你可以這樣做:

\let\oldsection\section
\renewcommand{\section}[1]{%
  \oldsection{#1} % 使用原來的 \section 指令
  \typeout{Section title: #1} % 輸出章節標題到控制台
}

在這個例子中,我們使用 \let\oldsection\section 保存了原來的 \section 指令到 oldsection 中,然後用 \renewcommand 重新定義了 \section 指令。新的 \section 指令會首先調用原來的 \section 指令,然後輸出章節標題到控制台。

定義新環境

LaTeX 的環境可以想像成是一個特殊的區域或容器,用來放置某種特定格式或功能的內容。有點像是在文件中開闢了一塊特別的地方,告訴 LaTeX:「嘿,這裡面的東西要用特殊方式處理哦!」

同樣地,定義新環境的方式有很多種,與定義新指令大同小異,有 \newenvironment\renewenvironment\def。下面介紹 \newenvironment\renewenvironment 的使用方法,以及為什麼不建議使用 \def 來定義環境。

newenvironment

\newenvironment 用來創建一個新的 LaTeX 環境,它的基本語法為 \newenvironment{環境名稱}[參數數量]{開始程式碼}{結束程式碼}

舉例來說,我們想要建立一個名為 myenv 的環境,將內容顯示為強調的引用,可以這麼做:

renewenvironment

\renewenvironment 用於修改已經存在的 LaTeX 環境。它的語法與 \newenvironment 類似:\newenvironment{新環境名稱}[參數數量]{開始程式碼}{結束程式碼}

假設我們要修改標準的 quote 環境,使其內容顯示為斜體,可以這麼操作:

但是你會發現,這樣的處理方式會報錯。主要原因在於重新定義 quote 環境時,我們又以 quote 環境定義,導致無限迴圈。為了避免這樣的狀況,我們改以下面的方式修改。

我們使用 \list{}{\rightmargin\leftmargin} 設定 quote 環境的左右邊距,用 \item\relax\itshape 設定 quote 環境的項目樣式為斜體。

為什麼不建議使用 \def

如果你有仔細看定義新指令的部分,應該可以很快知道背後的原因,就是因為以 \def 指令定義新環境時,無法檢查是否已經定義過,從而出現覆蓋環境的問題。不過既然都提到了,還是要介紹一下用 \def 定義新環境的方式,使用方式為:

\def\<環境名稱>{開始程式碼}
\def\end<環境名稱>{結束程式碼}

例如以下定義一個名為 foo 的環境:

\def\foo{\begin{quote}}
\def\endfoo{\end{quote}}

但如果需要改變這個環境的行為,必須分別修改 \foo\endfoo,容易出錯並且不直觀。綜上,筆者還是推薦使用 \newenvironment\renewenvironment 來定義和修改環境。

鍵值設定

「鍵」(key)在日常生活中隨處可見,從手機、電腦打字的鍵盤,或是路邊咖啡機購買飲料的按鍵,我們無處不在使用鍵的概念。

那麼,究竟什麼是鍵呢?鍵可以理解為是一種標識或名稱,用來找到與之對應的資訊,而這個對應的資訊就叫做「值」(value)。我們生活中所使用的鍵也是同樣道理——敲擊鍵盤上特定的鍵就會跑出相對應的字;點下咖啡機上的「美式咖啡」就會跑出美式咖啡。

也就是說,只要我們有一組對應關係,而這組對應關係恰好又有輸入與輸出的特性,那麼就可以利用鍵的概念儲存,這個過程稱為鍵值設定:將對應關係以系統化的方式儲存。用更簡單的話說,鍵值設定就是「這個選項是什麼?它的值是多少?」

參數傳遞

在正式講解鍵值設定前,我們要先講參數傳遞。參數傳遞是指將資訊從一個地方傳遞到另一個地方的過程,而在 LaTeX 中,參數傳遞通常是指將值或選項從一個指令或環境傳遞到另一個指令或環境,以便這些指令或環境能夠根據傳遞的參數來執行相應的操作。

參數傳遞的目的

參數傳遞存在的目的也很簡單,第一就是讓使用者不需要在文件中重複新增相同的資訊,第二就是防呆措施,避免使用這個套件的人胡亂修改參數名稱,達到區分內部指令與外部指令的功能

參數傳遞的寫法

誠如上述所言,為了要區分內外部指令,在 LaTeX 中我們使用 @ 符號來區分,後面加上參數名稱。例如,假設我們在類別文件中有一個內部參數 \@mycolor,它的默認值是 black

\newcommand{\@mycolor}{black}

這樣就定義了一個內部參數 \@mycolor,並賦予它一個默認值 black。而為了讓使用者可以在主文件中設定這些參數,我們可以定義一些指令,這些指令會更新內部參數的值。例如,我們可以定義一個指令 \setcolor,用來設定顏色:

\newcommand{\setcolor}[1]{\renewcommand{\@mycolor}{#1}}

當使用者在主文件中使用這個指令時,內部參數 \@mycolor 的值就會被更新為使用者設定的值。例如 \setcolor{blue} 就會將內部參數 \@mycolor 的值變成 blue

LaTeX 中的鍵值設定

LaTeX 中的鍵值設定應用在文件設定選項和參數的部分,以便用更加系統化的方式控制文件的行為和外觀。設定鍵值的方式,我們仰賴三個套件,分別是 kvdefinekeyskvsetkeyskvoptions5。下面講解參考相對應的官方文件,分別是定義鍵值設定鍵值設定選項

首先,我們要引入 kvoptionskvdefinekeyskvsetkeys 套件,使用 \RequirePackage{kvoptions, kvdefinekeys, kvsetkeys} 指令可以一次將三個套件叫進來。

接著使用 \SetupKeyvalOptions 來設定鍵值選項的家族、前綴和處理函數:

\SetupKeyvalOptions{
  family=class 名稱,      % 設定鍵值選項的家族名稱
  prefix=class 名稱@,     % 設定鍵值選項的前綴
  setkeys=\kvsetkeys   % 設定用於設定鍵值的函數
}

前綴就是上面提到的,用於區分內外部指令與不同家族指令的功能;處理函數則是實際設定鍵值配對的函數。當你設定鍵值配對時,該函數負責解析並執行你設定的程式碼。

將上面鍵值選項都設定完成後,就要實際定義鍵值。定義鍵值的方式我們使用 \kv@define@key{家族}{鍵}[默認值]{執行程式碼} 來處理,我們來一一講解一下這四個括號裡面代表的意義,並以下面的程式碼作為範例:

\kv@define@key{myclass}{color}[black]{\def\myclass@color{#1}}
  • 家族:這個參數指定了鍵值對所屬的家族名稱。家族名稱用來組織和區分不同的鍵值配對,避免不同的配對之間出現相互混淆的情況。例如,假設我們有一個家族名稱為 myclass,那麼所有與 myclass 相關的鍵值配對都屬於這個家族。

  • 鍵:此參數指定具體的鍵名。鍵名用來標識每個特定的設定或選項。例如,在上述例子中,color 是鍵名稱,用來設定文件的顏色。

  • 默認值:默認值的意思是,如果沒有為該鍵提供值,則使用這個默認值。此參數是可選填的,可以省略。如果省略,則表示該鍵沒有默認值。

  • 執行程式碼:代表當設定該鍵值需要執行的程式碼。#1 代表鍵的值。在這裡,{\def\myclass@color{#1}} 是當 color 鍵被設定時需要執行的程式碼。它將 #1 的值賦給變量 \myclass@color

根據定義的鍵值配對,我們就需要設定鍵值,讓使用者可以設定鍵值配對。方式很簡單,只要輸入以下程式碼即可:\kvsetkeys{家族}{鍵=值, ...}。我們也可以額外定義一個指令將這些設定應用在文件中。

鍵值設定應用

上面的內容有點抽象,也稍微艱澀一點。為了不要讓這篇文章變得那麼枯燥,我們直接用一個實際例子處理,並分為兩部分討論,一個是 .cls 檔,另一個則是主文件。以下例子在 Overleaf 上可見,請點擊此處查看編譯結果。

類別文件設定

% --------------------------------------------------
% 簡單的 cls 檔案範例
% Simple LaTeX Class File Example
% --------------------------------------------------
% Version       : 1.0
% Author        : 你的名字 <your_email@example.com>
% Repository    : https://github.com/yourusername/Simple-LaTeX-Class
% Document      : https://yourusername.github.io/Simple-LaTeX-Class/
% Last Updated  : YYYY.MM.DD
% --------------------------------------------------

% 告知編譯器當前的 LaTeX 版本
\NeedsTeXFormat{LaTeX2e}

% 定義類別名稱與敘述
\ProvidesClass{myclass}[2024/07/29 v1.0 Example Class with Key-Value Options]

% 載入所需套件
\RequirePackage{kvoptions, kvdefinekeys, kvsetkeys}
\RequirePackage{xcolor}
\RequirePackage{graphicx}

% 設定鍵值選項的家族、前綴和處理函數
\SetupKeyvalOptions{
  family=myclass,
  prefix=myclass@,
  setkeys=\kvsetkeys
}

% 定義具體的鍵值
\kv@define@key{myclass}{color}[black]{\def\myclass@color{#1}}
\kv@define@key{myclass}{fontsize}[10pt]{\def\myclass@fontsize{#1}}

% 設定默認值
\def\myclass@color{black}
\def\myclass@fontsize{10pt}

% 使用者設定指令
\newcommand{\myclasssetup}[1]{%
  \kvsetkeys{myclass}{#1}
}

% 處理所有未在顯式選項列表中的選項
\DeclareOption*{\ProcessKeyvalOptions{myclass}}
\ProcessOptions\relax

% 載入基礎類別文件
\LoadClass{article}

% 應用設定
\newcommand{\applysettings}{%
  \color{\myclass@color}%
  \fontsize{\myclass@fontsize}{\myclass@fontsize}\selectfont
}

% 重新定義 section 指令來應用設定
\AtBeginDocument{%
  \let\originalsection\section
  \renewcommand{\section}[1]{%
    \applysettings%
    \originalsection{#1}%
  }
}

這個 .cls 檔案定義了一個名為 myclass 的 LaTeX 類別,允許使用者通過鍵值對設定文件的顏色和字體大小。它使用了 kvoptionskvdefinekeyskvsetkeys 等套件來處理這些選項,並設定了默認顏色為黑色和字體大小為 10pt。通過 \myclasssetup 指令,使用者可以在文件中靈活地設定這些參數。檔案還重新定義了 \section 指令,使其應用使用者設定的顏色和字體大小。

類別文件的細節

這份類別文件裡面有些細節是上面沒有提到的,以下我們就來一一討論。首先是 \DeclareOption*{\ProcessKeyvalOptions{myclass}}\ProcessOptions\relax,這兩行指令是用來處理 LaTeX 類別文件中的選項,代表選項可以由使用者在文件的開頭利用 \documentclass 指令來設定。例如,當使用者寫 \documentclass[color=red, fontsize=12pt]{myclass} 時,代表 [] 中的選項需要被解析並應用在文件中。

  • \DeclareOption*:這是一個 LaTeX 指令,用來指定一個處理所有不在顯式選項列表中定義的選項的指令。言下之意,當 LaTeX 遇到一個選項(如 color=red),它會首先檢查這個選項是否在已經定義的選項中。如果找不到,它就會使用 \DeclareOption* 定義的處理方法來處理這個選項。
  • \ProcessKeyvalOptions{myclass}:這個指令來自 kvoptions 套件,用於解析和應用屬於 myclass 家族的鍵值配對選項。它會根據之前定義的鍵值配對(如 colorfontsize),解析使用者提供的選項並執行相應的程式碼。
  • \ProcessOptions\relax\ProcessOptions 用來處理所有在 \documentclass 中傳遞的選項,它會依次檢查每個選項,並應用對應的處理方法。如果一個選項沒有在顯式選項列表中定義,則會使用 \DeclareOption* 指定的方法來處理它。而 \relax 則是 LaTeX 原先就有的指令,用來表示「什麼也不做」。在此處代表 \ProcessOptions 完成後不需要執行任何額外的操作。

套用在主文件中

根據這份類別文件,我們使用 \documentclassmyclass.cls 這個類別應用在文件中。

\documentclass[color=blue, fontsize=12pt]{myclass}

\begin{document}

\section{Introduction}
Example text

\section{Another Section}
Example text

\end{document}

進階用法

上面談論的都是在撰寫一份文件的類別文件與套件的基本觀念與指令,建議讀者多家練習,除了能夠看懂別人寫的程式碼外,更要嘗試以自己的方式寫出來。不過這些指令只是冰山一角,還有許多相關的設定,因此額外拉出一小節,專門把這些雜項討論一遍。

計數器

在 LaTeX 中,計數器是一種非常有用的工具,它們就像是文件中的「計數員」。想像一下,你有一個小幫手,負責為你的章節、圖表、頁碼等進行編號,這就是計數器的工作。

那什麼是計數器呢?

計數器本質上是一個可以儲存整數值的變數。LaTeX 使用計數器來追蹤文件中的各種數值,例如章節編號、頁碼、圖表編號等。每當你在文件中使用某個需要編號的元素時,相應的計數器就會自動增加。

LaTeX 的預定義計數器

LaTeX 已經為我們預定義了許多常用的計數器,例如:

以下是用 Markdown 表格呈現的各種 LaTeX 編號類型:

編號類型 LaTeX 指令 說明
頁碼 \pageref{} 頁碼編號
章節編號 \chapter{} bookreport 文件類中使用
小節編號 \section{} 小節編號
子小節編號 \subsection{} 子小節編號
圖片編號 \figureref{} 圖片編號
表格編號 \tableref{} 表格編號
公式編號 \eqref{} 公式編號

這些計數器會在你使用相應的指令(如 \chapter\section 等)時自動增加。

如何使用計數器

使用計數器可以達到諸多用途,包含顯示計數器的值,例如可以用在顯示當前的頁碼,或是修改、增加計數器的值,甚至可以創建新的計數器。

顯示計數器的值

要顯示計數器的當前值,我們可以使用 \the 指令在既有或定義過的計數器前面。例如:

目前是第 \the\page 頁。

這會顯示當前的頁碼。

修改、增加計數器的值

修改計數器的值十分簡單,我們使用 \setcounter{計數器}{值} 指令修改計數器的值為後方指定的數字。例如

\setcounter{page}{1}

可以將頁碼設定為 1。修改計數器的情境包含圖表、標題、頁碼等。而使用 \stepcounter{計數器} 指令可以將計數器的值增加 1。例如

\stepcounter{section}

就可以將小節編號加 1。如果只想暫時增加計數器的值而不影響後續編號,可以使用 \refstepcounter

創建新的計數器

我們可以使用 \newcounter{計數器名稱} 指令創建自定義的計數器。舉例來說,要建立一個 mycounter 的計數器,只要輸入 \newcounter{mycounter} 即可。

計數器的實際應用

以下我們就來看計數器應用在 LaTeX 的例子,可以參考程式碼的寫法,嘗試建立自己的計數器。

計數器範例 1:自定義的習題環境

假設我們想創建一個習題環境,每個習題都有自己的編號。首先我們需要建立計數器,設定為 exercisecount,然後新增一個環境,允許使用者將文字放入其中,稍微排版一下,就可以使用習題環境。

計數器範例 2:帶有子編號的自定義環境

假設我們想創建一個定理環境,它的編號要跟隨小節編號。

條件句

條件語句就像是一個決策系統,幫助我們根據不同的情況做出不同的選擇。想像一下,你正在寫一份報告,根據不同的讀者(比如學生、教授或公司主管),你可能想要顯示不同的內容。這就是條件語句派上用場的時候了!

if 指令:最基本的比較

\if 指令就像是問一個簡單的是非問題。它會比較兩個字符,看它們是不是一樣的。白話來說,就是在玩一個配對遊戲,\if 就是在問「這兩個東西是不是一樣的?」

可以發現到,if 的判斷句寫法如下:

\ifcondition
  % 當條件為真時執行的指令
\else
  % 當條件為假時執行的指令
\fi

注意到在 LaTeX 中,當判斷條件結束後,必須加上 \fi,告訴電腦說判斷句已經結束了,無論是最基本的 \if 還是 \ifx 等,都需要加上 \fi;而 \else 就像是在說「如果前面的條件不成立,那就做這個」。我們也可以透過嵌套的方式建立一個巢狀判斷,一層一層判斷輸入的條件是否為真。

在這個範例中,\checkconditions 指令接受兩個參數,先判斷第一個參數是否為 A,如果是,則進一步判斷第二個參數是否大於 5;如果第一個參數不是 A,則判斷是否為 B;如果都不是,則輸出 “The first parameter is not A or B.”

ifx 指令:更嚴格的比較

\ifx 指令更像是在比較兩個物品的所有細節,而不僅僅是它們的外表。這就像是在比較兩個人,不只看他們的長相,還要看他們的性格、愛好等是否都一樣。

ifnum 指令:數字的比較

\ifnum 指令用來比較數字的大小,就像是在玩「比大小」的遊戲。

上述範例根據分數給出不同的評價,就像老師根據考試成績給出評語。

ifdim 指令:尺寸的比較

\ifdim 指令用於比較長度或尺寸,就像是在比較兩條線段的長短。\ifdim 就是一個標準長度測量儀,可以告訴你哪個更長,或者它們是否一樣長。

ifcase 指令:多重選擇

\ifcase 指令就像是一個多選題,根據不同的數字選擇不同的答案。

計數器的實際應用

既然我們提到了計數器與條件判斷,那麼我們就可以將計數器進行延伸使用。例如,我們可以檢查當前頁碼並做出相應的輸出:

\ifnum\value{page}=1
  這是第一頁
\else
  這不是第一頁
\fi

這段程式碼會根據當前頁碼的值來決定輸出「這是第一頁」或「這不是第一頁」。我們也可以對計數器進行基本的運算,例如,我們可以將章節號和小節號相加並存儲在一個新的計數器中:

\newcounter{total}
\setcounter{total}{\value{chapter}+\value{section}}

我們先新增了一個名為 total 的新計數器,並將其值設定為當前章節號和小節號的和。

有時我們可能需要暫時保存一個計數器的值,然後在某些操作後恢復它。例如,我們可以保存當前頁碼,執行一些可能改變頁碼的操作,然後恢復之前保存的頁碼。

\newcounter{saved}
\setcounter{saved}{\value{page}}  % 保存當前頁碼
% ... 一些可能改變頁碼的操作 ...
\setcounter{page}{\value{saved}}  % 恢復之前保存的頁碼

小結

LaTeX 的條件語句就像是一個聰明的助手,幫助我們根據不同的情況自動調整文件的內容。無論是比較簡單的字符、複雜的定義,還是數字和尺寸,這些指令都能幫我們做出正確的選擇。使用這些條件語句,你可以創建出更加靈活和智能的文件。例如,你可以:

  • 根據文件類型自動調整格式(比如論文、報告或簡報)
  • 根據語言設定更換文件中的某些詞語
  • 根據頁面大小調整圖片或表格的尺寸
  • 根據章節編號自動生成不同的頁眉頁腳

掌握了這些條件語句,你就像擁有了一把神奇的鑰匙,可以打開 LaTeX 世界的無限可能性。不過請記住一點:過度使用條件語句可能會使你的程式碼變得複雜難懂。所以,要適度使用,保持程式碼的簡潔和可讀性。

字符類別碼

在 LaTeX 的世界裡,每個字符都有一個「身份證」,這就是所謂的字符類別碼(Category Code,簡稱 catcode)。想像一下,如果 LaTeX 是一個王國,那麼每個字符就是這個王國的居民,而類別碼就是他們的身份證明,決定了他們在這個王國裡可以做什麼,不可以做什麼。

字符類別碼是什麼?

LaTeX 總共有 16 種不同的類別碼,從 0 到 15。每種類別碼都有特定的含義和用途。例如:

0。反斜線 \ 通常被指定為類別碼 0,它是「逃逸字符」,用來引入指令。 1。左花括號 { 通常被指定為類別碼 1,它是「開始組」的標記。 2。右花括號 } 通常被指定為類別碼 2,它是「結束組」的標記。 11。一般的字母字符被指定為類別碼 11,這些是「普通字符」。 12。其他符號,如 @#$ 等,通常被指定為類別碼 12,這些是「其他字符」。

為什麼要關心字符類別碼?

在日常使用 LaTeX 時,我們可能不太需要關心字符類別碼。但是,當我們開始深入研究 LaTeX,特別是在編寫套件或者需要修改 LaTeX 的內部行為時,了解並善用字符類別碼就變得非常重要了。

特別是 @ 符號,它在 LaTeX 中扮演著特殊的角色。在一般的文件中,@ 的類別碼是 12(其他字符),這意味著它不能用在套件的名稱中。但在套件和類別檔案中,@ 的類別碼被改為 11(普通字符),這樣套件作者就可以使用帶 @ 的套件名稱,從而創建「內部」指令,這些指令通常不會給使用者看到。

因此,了解並且善用字符類別碼可以讓我們更靈活地控制 LaTeX 的行為。以下是一些實際應用的例子:

1. 訪問和修改套件的內部指令:有時我們需要微調某個套件的行為,這可能涉及修改其內部指令。使用 \makeatletter\makeatother 可以讓我們安全地進行這些修改。

2. 建立「私有」指令:在編寫自己的套件時,我們可以使用帶 @ 的指令名來創建「私有」指令,這些指令不會被使用者意外覆蓋或使用。

3. 臨時改變字符的行為:通過修改字符的類別碼,我們可以臨時改變某些字符的行為。例如,將某個特殊字符暫時變為普通字符,或者將普通字符變為活動字符(catcode 13)來定義特殊的行為。

4. 解析特殊格式的輸入:在處理特殊格式的輸入時(如解析 .csv 文件),修改某些字符的類別碼可以簡化解析過程。

三個重要的字符類別碼指令

為了操控字符類別碼,特別是 @ 符號的類別碼,LaTeX 提供了三個重要的指令:

  • \catcode:查看或修改任何字符的類別碼。
  • \makeatletter:將 @ 的類別碼從 12 改為 11,使其成為普通字符。
  • \makeatother:將 @ 的類別碼從 11 改回 12,恢復其特殊字符的身份。

讓我們詳細看看這三個指令的使用方法和範例。

catcode 指令

\catcode 是最靈活的字符類別碼指令,它允許我們查看或修改任何字符的類別碼。語法是 \catcode'\⟨字符⟩=⟨數字⟩。例如:

\catcode'\@=11  % 將 @ 的類別碼設為 11(普通字符)
\catcode'\$=11  % 將 $ 的類別碼設為 11,使其失去數學模式分隔符的功能

% 使用新的類別碼
這裡 $ 不再開始數學模式,而是普通字符。

\catcode'\$=3  % 將 $ 的類別碼恢復為 3(數學模式分隔符)
$x^2 + y^2 = z^2$  % 現在 $ 又可以用來分隔數學模式了

makeatletter 與 makeatother 指令

\makeatletter 是一個快速指令,專門用來將 @ 符號的類別碼改為 11(普通字符)。這使得我們可以在文件中使用或修改包含 @ 的內部指令。而 \makeatother\makeatletter 的配對指令,它將 @ 符號的類別碼恢復為 12(其他字符)。在使用完 \makeatletter 後,我們應該始終使用 \makeatother 來恢復 @ 的原始類別碼,以防止意外修改或使用內部指令。

\makeatletter

% 現在我們可以定義或使用包含 @ 的指令了
\newcommand{\my@command}{這是一個內部指令}

% 使用內部指令
\my@command

% 甚至可以修改現有的內部指令
\let\original@section\section
\renewcommand{\section}[1]{%
  \original@section{修改後的:#1}%
}

\makeatother

注意事項

雖然字符類別碼是一個強大的工具,但使用時需要格外小心:

1. 永遠記得在使用 \makeatletter 後使用 \makeatother,以防止意外修改內部指令。

2. 修改常用字符的類別碼可能會導致意想不到的後果,使用前請仔細考慮。

3. 在套件或類別文件中,@ 的類別碼預設就是 11,不需要使用 \makeatletter

4. 如果你在定義新的指令或環境時使用了 @,記得將定義包裹在 \makeatletter\makeatother 之間。

鉤子與回調

在 LaTeX 中,鉤子(hook)和回調(callback)6是一種強大的機制,允許使用者在特定的時間點執行程式碼。這些機制可以用來在文件的不同階段執行自定義的程式碼,從而實現更加靈活和動態的文件處理。分為四個指令:

指令 說明
\AtBeginDocument 在文件開始時執行程式碼
\AtEndDocument 在文件結束時執行程式碼
\AtEndOfPackage 在套件的末尾執行程式碼
\AtEndOfClass 在類別的末尾執行程式碼

以下就逐一介紹這些指令的功能。

AtBeginDocument 與 AtEndDocument

\AtBeginDocument 指令允許使用者在文件開始時執行特定的程式碼。這個指令非常有用,特別是在需要於文件開始時進行一些初始化設定或定義一些套件時。而 \AtEndDocument 指令則是讓使用者在文件結束時執行特定的程式碼。這個指令特別適合用來新增附錄、參考文獻或者在文件的最後插入一些總結性的內容。

舉例來說,我們想要在文件一開始時加入目錄,但又不想要預設的目錄標題 “Contents”,做法就是跟 LaTeX 編譯器說:在文件一開始時,就將目錄名稱改為「目錄」即可。同時,我們要在文件最後加上附錄章節,可以參考以下程式碼:

AtEndOfPackage 與 AtEndOfClass

這兩個指令顧名思義,就是在套件或是類別文件即將結束時執行程式碼。使用方法為:

% sty 檔
\AtEndOfPackage{%
  \exampleCommand
}

% cls 檔
\AtEndOfClass{%
  \exampleClassCommand
}

假設我們有一個自定義的套件 example.sty,我們希望在套件加載完畢後自動執行一個指令來顯示訊息。

% example.sty - 自定義套件
\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{example}[2024/07/29 v1.0 Example Package]

\newcommand{\exampleCommand}{這是一個例子指令。}

% 在套件結束時執行
\AtEndOfPackage{%
  \exampleCommand % 執行例子指令
  \typeout{Example package loaded.} % 在控制台輸出訊息
}

上傳套件到 CTAN

不曉得還記不記得 CTAN。這個網站就是套件的資料庫,它存放了各種套件的說明文件,鉅細靡遺的將套件使用方式告訴使用者,不至於讓使用者下載並安裝套件後無所適從。

相信看到這邊的你,肯定有能力寫出自己的套件並且上傳。接下來最後一部分就是要教大家如何上傳套件到 CTAN。

步驟 1:準備套件

首先,你需要確保你的套件包含以下文件:

  1. 主要套件文件:例如 mystyle.sty

  2. 文檔文件:例如 READMEREADME.md,用於介紹套件的功能、安裝方法和使用說明。

  3. 範例文件:例如 example.tex,用於展示套件的使用方法。

  4. 說明文件:例如 mystyle.pdf,詳細說明套件的功能和使用方法。這通常是由 .dtx 文件生成的。

  5. 目錄結構:確保你的資料夾結構清晰,例如:

mystyle/
├── README.md
├── mystyle.sty
├── example.tex
└── doc/
    └── mystyle.pdf

步驟 2:壓縮套件

將準備好的套件文件和目錄結構壓縮成一個 .zip.tar.gz 文件。

步驟 3:打開 CTAN 上傳頁面

打開網頁瀏覽器,打開 CTAN 上傳頁面。你需要登錄 CTAN 帳號,如果沒有帳號,可以在該頁面直接註冊一個。

步驟 4:填寫上傳表單

在上傳頁面,需要填寫套件的基本資訊,包含套件名稱、版本、套件用途、開發團隊、許可證等。最後點擊上傳文件,將壓縮檔上傳後即可完成。上傳完畢後,CTAN 團隊會對上傳進行審核。審核過程通常會在幾天內完成,審核通過後,套件將被新增到 CTAN 的資料庫中,並向所有使用者開放下載和使用。

Footnotes

  1. Tony Roberts. (2020, May 25). LaTeX 52 Make your own style file [Video]. YouTube. https://www.youtube.com/watch?v=xNjWAdpAjhg↩︎

  2. 關於 \listfiles 的使用方式,請參見…↩︎

  3. 所謂脆弱指令,指的是在文件裡面使用多次,且可能會受其他指令影響的指令,例如 \caption\section↩︎

  4. 傳統的定義新指令方式,在重複定義新指令時會報錯。↩︎

  5. 原本的鍵值設定是使用 keyval 套件處理,但是 kv 系列套件的出現,改善了原本 keyval 套件的一些問題,並強化功能。↩︎

  6. 看到這個標題不曉得會不會笑出來,筆者思考很久,是否應該維持原文,但思考過後決定還是以比較有趣的方式呈現,否則這篇文章真的太過艱澀、無聊了。↩︎

Reuse

Citation

BibTeX citation:
@online{sung2024,
  author = {Sung, Anthony},
  title = {LaTeX {教學系列:sty} {與} Cls {檔}},
  date = {2024-07-29},
  url = {https://yueswater.com/posts/2024-07-29-latex-sty-cls/},
  langid = {en}
}
For attribution, please cite this work as:
Sung, Anthony. 2024. “LaTeX 教學系列:sty 與 Cls 檔.” July 29, 2024. https://yueswater.com/posts/2024-07-29-latex-sty-cls/.