LaTex 中圓圈序號及一些特殊字元的輸入

眾所周知,LATEX 提供了 \textcircled 命令用以給字元加圈,但效果卻不怎麼好:

\textcircled

實際上,加圈並不是一個平凡的變換,它會涉及到圈內字元形狀的微調,而這是幾乎無法在 TEX 宏層面解決的。因此,要得到比較好的效果,最好能使用預先設計的字元形(glyph)。


傳統方案

pifont 宏包提供了一系列雜錦符號(dingbats),其中就有帶圈數字。pifont 屬於 psnfss 宏集,它封裝了一系列 PostScript 字體,包含著名的 Helvetica、Times、Courier 等。pifont 使用的是 Zapf Dingbats 字體。

使用 \ding{<number>} 可以很方便地使用帶圈數字(共有四種),當然也有其他符號。具體數字可參見下圖:

pifont

在主流的 TEX 引擎下,pifont 宏包都可以使用。


Unicode

數字 0–50 的帶圈版本都分配了對應的 Unicode 碼位,因而在現代 TEX 引擎(X⁠E⁠TEX 和 Lua­TEX,若無特殊說明以下僅討論這兩者)中,配合合適的字體,理論上可以直接輸入這些符號。具體見下表:[1]

24EA 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469
246A 246B 246C 246D 246E 246F 2470 2471 2472 2473
3251 3252 3253 3254 3255 3256 3257 3258 3259 325A
325B 325C 325D 325E 325F 32B1 32B2 32B3 32B4 32B5
32B6 32B7 32B8 32B9 32BA 32BB 32BC 32BD 32BE 32BF

Zapf Dingbats 中的其他幾種樣式也分配有碼位:

  • 反白(negative circled digits)

    24FF 2776 2777 2778 2779 277A 277B 277C 277D 277E 277F
    24EB 24EC 24ED 24EE 24EF 24F0 24F1 24F2 24F3 24F4
  • 無襯線(circled sans-serif digits)

    🄋
    1F10B 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789
  • 無襯線反白(negative circled sans-serif digits)

    🄌
    1F10C 278A 278B 278C 278D 278E 278F 2790 2791 2792 2793

此外,還額外增加了一些樣式:

  • 雙線(double circled digits)

    24F5 24F6 24F7 24F8 24F9 24FA 24FB 24FC 24FD 24FE
  • 加框(circled numbers on black square)

    3248 3249 324A 324B 324C 324D 324E 324F
  • 帶圓括弧(parenthesized digits)

    2474 2475 2476 2477 2478 2479 247A 247B 247C 247D
    247E 247F 2480 2481 2482 2483 2484 2485 2486 2487
  • 帶點(digits with full stop)

    🄀
    1F100 2488 2489 248A 248B 248C 248D 248E 248F 2490 2491
    2492 2493 2494 2495 2496 2497 2498 2499 249A 249B
  • 帶逗號(digits with comma)

    🄁 🄂 🄃 🄄 🄅 🄆 🄇 🄈 🄉 🄊
    1F101 1F102 1F103 1F104 1F105 1F106 1F107 1F108 1F109 1F10A
  • 這些姑且也算上吧:

    3220 3221 3222 3223 3224 3225 3226 3227 3228 3229
    3280 3281 3282 3283 3284 3285 3286 3287 3288 3289
    🈩 🈔 🈪
    1F229 1F214 1F22A
    🉂 🉁
    1F242 1F241

這些符號分散在以下幾個 Unicode 區塊(block)中:

  • Enclosed Alphanumerics (U+2460–U+24FF)
    • 帶圈 0–20(以及 a–z、A–Z)
    • 反白 0、11–20
    • 雙線 1–10
    • 帶圓括弧 1–20
    • 帶點 1–20
  • Dingbats (U+2700–U+27BF)
    • 反白 1–10
    • 無襯線 1–10
    • 無襯線反白 1–10
  • Enclosed CJK Letters and Months (U+3200–U+32FF)
    • 帶圈 21–50
    • 加框 10–80(僅限整十)
    • 帶圈
    • 帶圓括弧
  • Enclosed Alphanumeric Supplement (U+1F100–U+1F1FF)
    • 帶逗號 0–9
    • 無襯線、無襯線反白以及帶點的 0
  • Enclosed Ideographic Supplement (U+1F200–U+1F2FF)
    • 帶方框
    • 帶六角括弧

直接輸入,或者利用碼位,都能在 LATEX 中使用以上這些帶圈數字(注意不同方法對大小寫的要求有差異):

\documentclass{article}
\usepackage{fontspec}
\setmainfont{Source Han Serif SC}

\begin{document}
① ② ③ ④ ⑤
\symbol{"2776} \symbol{"2777} \symbol{"2778} \symbol{"2779} \symbol{"277A}
\char"3248\    \char"3249\    \char"324A\    \char"324B\    \char"324C\
^^^^3280       ^^^^3281       ^^^^3282       ^^^^3283       ^^^^3284
^^^^^1f229     ^^^^^1f214     ^^^^^1f22a
\end{document}

使用 X⁠ELATEX 或 Lua­LATEX 編譯,效果如下:

textcircled-fontspec


xunicode-addon 宏包

在實際使用中,無論是依靠碼位,還是藉由輸入法直接錄入這些特殊字元,都不是很方便。在 xunicode-addon 宏包(從屬於 xeCJK)中,\textcircled 等一系列命令被重新定義,從而能夠顯示 Unicode 所分配的帶圈數字(和字母等)。舉例如下:

\documentclass{article}
\usepackage{fontspec,xunicode-addon}
\setmainfont{Source Han Serif SC}

\begin{document}
\textcircled{1}
\textcircled{25}
\textcircled{a}
\textcircled{Z}
\end{document}

利用 LATEX3 語法也可以迅速寫出如下循環而不傷身體:

\ExplSyntaxOn
\cs_set:Npn \TESTi
  {
    \int_step_inline:nnn {  0 } { 25 } { \textcircled{##1} ~ } \par
    \int_step_inline:nnn { 26 } { 50 } { \textcircled{##1} ~ } \par
  }
\cs_set:Npn \TESTii
  { \tl_map_inline:nn { abcdefghijklmnopqrstuvwxyz } { \textcircled{##1} ~ } \par }
\cs_set:Npn \TESTiii
  { \tl_map_inline:nn { ABCDEFGHIJKLMNOPQRSTUVWXYZ } { \textcircled{##1} ~ } \par }
\ExplSyntaxOff

\TESTi
\TESTii
\TESTiii

textcircled-xunicode-addon

當然,其他樣式的帶圈數字並沒有提供快捷的輸入方式。


ctex 宏集中使用

以上的案例都是在標準文檔類 article 中搭配 fontspec 宏包完成的。如果切換成 ctex 宏集,則需要額外做一些調整。

對於中文文檔,我們通常需要為中西文(「西文」主要指 Latin script)分別設置字體。上面已經提到過,帶圈數字分散在了幾個 Unicode 區塊中。xeCJK 將其中的 Enclosed CJK Letters and Months 和 Enclosed Ideographic Supplement 設置為了 CJK 字元類,使用中文字體;其餘則為 Default 字元類,使用西文字體。

Lua­TEX 下的情況類似,但稍顯複雜。首先是 luatexja 作出了 ALchar 和 JAchar 的劃分,大致相當於西文和日文(AL=ALphabetic,JA=JApanese);同時又預定義了一些字元範圍。默認設置中,上文所列的所有帶圈數字均會使用日文字體。其後,ctex 宏集為了適應中文排版的需求又做了一些修改。結果是,Enclosed Alphanumerics 被設置為了 ALchar,即使用西文字體。

總而言之,在 ctex 宏集的默認配置下:

Unicode 區塊 X⁠ELATEX Lua­LATEX
Enclosed Alphanumerics 西文 西文
Dingbats 西文 西文
Enclosed CJK Letters and Months 中文 中文
Enclosed Alphanumeric Supplement 西文 西文
Enclosed Ideographic Supplement 中文 西文

在 X⁠ELATEX 下,可以做如下修改:

% 使用中文字體
\xeCJKDeclareCharClass{CJK}{%
  "24EA,        % ⓪
  "2460->"2473, % ①–⑳
  "3251->"32BF, % ㉑–㊿
  "24FF,        % ⓿
  "2776->"277F, % ❶–❿
  "24EB->"24F4  % ⓫–⓴
}
\setCJKmainfont{Source Han Serif SC}

% 或使用西文字體
% \xeCJKDeclareCharClass{Default}{%
%   "24EA, "2460->"2473, "3251->"32BF,
%   "24FF, "2776->"277F, "24EB->"24F4}
% \setmainfont{Garamond-Math.otf}

在 Lua­LATEX 下,也完全類似:

% 使用中文字體
\ltjdefcharrange{6}{%
  "24EA, "2460-"2473, "3251-"32BF,
  "24FF, "2776-"277F, "24EB-"24F4}
\setCJKmainfont{Source Han Serif SC}

% 或使用西文字體
% \ltjdefcharrange{3}{%
%   "24EA, "2460-"2473, "3251-"32BF,
%   "24FF, "2776-"277F, "24EB-"24F4}
% \setmainfont{Garamond-Math.otf}

這裡的 63 原先分別對應日文字元和西文標點、符號。還需注意範圍的寫法與 xeCJK 中不同。

配合 xunicode-addon 宏包,在 ctex 宏集中也同樣可以使用 \textcircled 命令輸入預定義的帶圈數字。但需注意,\textcircled 會預先檢查字元是否存在,且僅在西文字體中進行。所以如需使用中文字體進行顯示,就要「指鹿為馬」:[2]

% XeLaTeX 下需要把全體帶圈數字都設置成 Default 類
% LuaLaTeX 下無須額外設置
\xeCJKDeclareCharClass{Default}{"24EA, "2460->"2473, "3251->"32BF}

% 將中文字體聲明為(西文)字體族
\newfontfamily\EnclosedNumbers{Source Han Serif SC}

% 放置鉤子,只讓帶圈字元才需更換字體
\AtBeginUTFCommand[\textcircled]{\begingroup\EnclosedNumbers}
\AtEndUTFCommand[\textcircled]{\endgroup}

對於字體中沒有的帶圈數字,\textcircled 也能夠自動生成(由圓圈和相應的數字拼合)。選擇合適的字體之後,便可做一些比較暴力的嘗試:

textcircled-matrix

即使是三位數,效果也尚能接受。

OpenType 的 nalt 特性

在 OpenType 中,有一項名為 nalt(Alternate Annotation Forms)的 GSUB 特性,它的作用是把特定的字元形替換成符號標註形式(notational forms)。不少日文字體都包含這一特性,我們可以利用 fontspec 宏包提供的相關選項調用。舉例如下:

\documentclass{article}
\usepackage{fontspec}
\setmainfont{ipaexm.ttf}  % IPAex 明朝,TeX Live 自帶

\begin{document}
{\addfontfeature{Annotation=0}123456789}
{\addfontfeature{Annotation=1}123456789}
{\addfontfeature{Annotation=2}123456789}
\end{document}

textcircled-nalt

需要注意的是,Annotation=X 中的某個 X 具體對應何種樣式,這是由字體設計者決定的。此外,在一些字體中,部分假名、漢字也有類似的標註形式,可以用相同方法使用:

\documentclass{ctexart}
\setCJKmainfont{Hiragino Mincho Pro W3}

\begin{document}
{\addCJKfontfeature{Annotation=0}あア}
{\addCJKfontfeature{Annotation=1}かカ}
{\addCJKfontfeature{Annotation=2}さサ}
{\addCJKfontfeature{Annotation=3}たタ}
{\addCJKfontfeature{Annotation=4}なナ}
{\addCJKfontfeature{Annotation=5}はハ}
{\addCJKfontfeature{Annotation=6}まマ}
\end{document}

textcircled-nalt-kana

這裡我們用 \addCJKfontfeature 代替了 \addfontfeature。此處作為演示的字體是 macOS 自帶的ヒラギノ明朝,在 Windows/Linux 上可換用其他字體。

Adobe-Japan1-7

Adobe-Japan1-7 字符集定義了更多的帶圈數字,很多樣式都支援 0–100 的數字範圍。但由於 Unicode 沒有為它們分配碼位,我們必須用 CID(Character IDentifier)來指定。[3]

由於 CID 到具體字元的映照比較複雜,因而這裡我們提供了一個宏包 textcircle-cid,用來通過 CID 調用帶圈數字。textcircle-cid 宏包提供了下面一組命令:

  • \CIDtextcircled
  • \CIDtextblackcircled
  • \CIDtextboxed
  • \CIDtextblackboxed
  • \CIDtextrboxed
  • \CIDtextblackrboxed

支援的數字範圍是 0–100 和 00–09。X⁠E⁠TEX、Lua­TEX 和 up­TEX 這三種 Unicode 引擎均可使用,但需要配合其他宏包及命令以實現正確的字體調用:

  • X⁠ELATEX 下需要通過 \setmainfont 等命令設置字體

  • Lua­LATEX 下需要通過 \setmainjfont 等命令設置(日文)字體

  • up­LATEX 下需要調用 pxchfon 宏包,並且使用 \setminchofont 等命令設置字體,具體可以參考以下示例:

    % test-uptex.tex
    \documentclass{ujarticle}
    \usepackage{pxchfon,textcircle-cid}
    \setminchofont{KozMinPr6N-Regular.otf}
    \setgothicfont{KozGoPr6N-Regular.otf}
    
    \def\TEST{%
      \CIDtextcircled{0}
      \CIDtextblackcircled{1}
      \CIDtextboxed{00}
      \CIDtextblackboxed{10}
      \CIDtextrboxed{50}
      \CIDtextblackrboxed{100}}
    
    \begin{document}
    \textmc{\TEST} \par
    \textgt{\TEST}
    \end{document}
    

    注意 up­TEX 不直接生成 PDF,因此編譯時可採取如下方式:

    uplatex test-uptex && dvipdfmx test-uptex
    

    在上面的示例中,我們使用的字體是 Adobe 的小塚明朝和小塚ゴシック。事實上,只有遵從 Adobe-Japan1 的字體,才能利用 CID 正確地調用相應的字元。

字體的選擇

上文我們多次提及,帶圈數字的具體使用與字體密切相關。下面我們整理了 TEX Live 自帶的、可使用帶圈數字的字體,以及對應的數字範圍:

字體 帶圈 反白 無襯線 無襯線反白
Baekmuk Batang/Dotum/Gulim/Headline 1–15
Carlito 0–20 0–20
DejaVuSans 1–10 1–10 1–10 1–10
FreeMono, FreeSans 1–10
FreeSerif 1–10 1–10 1–10 1–10
Garamond Math 0–50 0–20
IPAGothic, IPAMincho 1–50 1–20
Junicode 0–20 0–20
Lato 0–20 0–20
Libertinus Serif/Sans/Math, Linux Libertine, Linux Biolinum 0–20 0–20
Libertinus Keyboard, Linux Biolinum Keyboard 1–10
STIX, STIX Math 0–9 1–10 1–10
STIX Two Math 0–20 0–20 1–10 1–10
UnBatang, UnDinaru, UnDotum, UnGraphic, UnGungseo, UnJamoBatang, UnJamoDotum, UnJamoNovel, UnJamoSora, UnPen, UnPenheulim, UnPilgi, UnPilgia, UnShinmun, UnVada, UnYetgul 0–20
XITS, XITS Math 0–9 1–10 1–10
文鼎PL簡報宋、文鼎PL簡中楷 1–10

以下是其他一些常見中、日文字體,其中很多是作業系統自帶的:

字體 帶圈 反白 無襯線 無襯線反白
思源宋體、思源黑體 0–50 0–20 0–10 0–10
微軟雅黑、微軟正黑 1–10
蘋方 0–50 0–20 0–10 1–10
方正書宋、方正黑體、方正楷體、方正仿宋、等線 1–10
宋體、黑體、楷體、仿宋(中易) 1–10
更紗黑體 (Sarasa Gothic) 0–50 0–20 0–10 0–10
小塚明朝 (Kozuka Mincho)、小塚ゴシック (Kozuka Gothic) 0–100 0–100
游明朝 (Yu Mincho)、游ゴシック (Yu Gothic) 0–100 0–100
メイリオ (Meiryo) 0–50 1–20 0–10 1–10

這裡我們用了 Python 腳本 check-circled-number.py 來讀取字體資訊,它還依賴 FontForge。注意由於字體版本不同,不保證表中所列結果與實際情況完全一致。

注釋

  1. ^在本頁面的 CSS 中,帶圈數字將優先使用思源宋體(Source Han Serif)顯示,但具體結果仍然取決於字體的安裝情況以及瀏覽器的渲染方式。
  2. ^感謝 @qinglee 的指導!見 CTeX-org/ctex-kit #399
  3. ^感謝 @clerkma 的指導!見 CTeX-org/forum #20