% \iffalse meta-comment
% !TEX program  = XeLaTeX
%<*internal>
\iffalse
%</internal>
%<*readme>

xeCJK is a package written for XeLaTeX which allows users to typeset
CJK scripts easily.


 - different default fonts for CJK and other characters;
 - spaces automatically ignored between CJK characters;
 - special effects on full-width CJK punctuation; and
 - automatic adjustment of the space between CJK and other characters.


This package is licensed in LPPL.

This package consists of the file  xeCJK.dtx,
                                   full-stop.map,
                                   fullwidth-stop.map,
                                   han-simp.map,
                                   han-trad.map,
             and the derived files xeCJK.pdf,
                                   xeCJK.sty,
                                   xeCJK.cfg,
                                   xeCJK.ins,
                                   xeCJKfntef.sty,
                                   xeCJK-listings.sty,
                                   xunicode-addon.sty,
                                   xeCJK-example-autofake.tex,
                                   xeCJK-example-fallback.tex,
                                   xeCJK-example-subCJKblock.tex,
                                   xeCJK-example-CJKecglue.tex,
                                   xeCJK-example-checksingle.tex,
                                   xeCJK-example-CJKfntef.tex,
                                   xeCJK-example-punctstyle.tex,
                                   xeCJK-example-verbatim.tex,
                                   xeCJK-example-IVS.tex,
                                   xeCJK-example-listings.tex,
                                   full-stop.tec,
                                   fullwidth-stop.tec,
                                   han-simp.tec,
                                   han-trad.tec, and
                                   README.txt (this file).

If you are interested in the process of development you may observe

    http://code.google.com/p/ctex-kit/updates/list

    Author:
            Wenchang Sun    <sunwch@nankai.edu.cn>
    Current Maintainers:
            Leo Liu         <leoliu.pku@gmail.com>
            Qing Lee        <sobenlee@gmail.com>


Installation
------------
The package is supplied in dtx format and as a pre-extracted zip file,
xecjk.tds.zip. The later is most convenient for most users: simply
unzip this in your local texmf directory and run texhash to update the
database of file locations. If you want to unpack the dtx yourself,
running "xetex xeCJK.dtx" will extract the package whereas
"xelatex xeCJK.dtx" will typeset the documentation.

The package requires LaTeX3 support as provided in the l3kernel and
l3packages bundles and the fontspec package. All of these are available
on CTAN as ready-to-install zip files. Suitable versions are available
in the latest version of MiKTeX and TeX Live (updating the relevant
packages online may be necessary).

To compile the documentation without error, you will need some specific
CJK scripts fonts (TrueType or OpenType).
%</readme>
%<*internal>
\fi
\begingroup
  \edef\tempa{\fmtname}
  \edef\tempb{plain}
\expandafter\endgroup
\ifx\tempa\tempb
\csname fi\endcsname
%</internal>
%<*install>

\input l3docstrip.tex
\keepsilent
\askforoverwritefalse
\preamble

$Id: xeCJK.dtx 477 2013-07-25 22:15:20Z sobenlee $
$URL: https://ctex-kit.googlecode.com/svn/trunk/xeCJK/xeCJK.dtx $
-----------------------------------------------------------------
   Author:
            Wenchang Sun    <sunwch@nankai.edu.cn>
   Current Maintainers:
            Leo Liu         <leoliu.pku@gmail.com>
            Qing Lee        <sobenlee@gmail.com>

   Copyright (C) 2007--2013 Wenchang Sun
             (C) 2009--2013 Leo Liu
             (C) 2012--2013 Qing Lee

   This file may be distributed and/or modified under the
   conditions of the LaTeX Project Public License, either version 1.3
   of this license or (at your option) any later version.
   The latest version of this license is in
      http://www.latex-project.org/lppl.txt
   and version 1.3 or later is part of all distributions of LaTeX
   version 2005/12/01 or later.

   This work has the LPPL maintenance status "maintained".
   The Current Maintainer of this work are Leo Liu and Qing Lee.
-----------------------------------------------------------------

\endpreamble
\postamble

   This package consists of the file  xeCJK.dtx,
                                      full-stop.map,
                                      fullwidth-stop.map,
                                      han-simp.map,
                                      han-trad.map,
                and the derived files xeCJK.pdf,
                                      xeCJK.sty,
                                      xeCJK.cfg,
                                      xeCJK.ins,
                                      xeCJKfntef.sty,
                                      xeCJK-listings.sty,
                                      xunicode-addon.sty,
                                      xeCJK-example-autofake.tex,
                                      xeCJK-example-fallback.tex,
                                      xeCJK-example-subCJKblock.tex,
                                      xeCJK-example-CJKecglue.tex,
                                      xeCJK-example-checksingle.tex,
                                      xeCJK-example-CJKfntef.tex,
                                      xeCJK-example-punctstyle.tex,
                                      xeCJK-example-verbatim.tex,
                                      xeCJK-example-IVS.tex,
                                      xeCJK-example-listings.tex,
                                      full-stop.tec,
                                      fullwidth-stop.tec,
                                      han-simp.tec,
                                      han-trad.tec, and
                                      README.txt.
\endpostamble
\declarepreamble\emptypreamble
\endpreamble
\declarepostamble\emptypostamble
\endpostamble

\generate{
  \usedir{source/xelatex/xecjk}
  \file{\jobname.ins}{\from{\jobname.dtx}{install}}
  \usedir{tex/xelatex/xecjk}
  \file{\jobname.sty}{\from{\jobname.dtx}{package}}
  \file{xeCJKfntef.sty}{\from{\jobname.dtx}{fntef}}
  \file{xeCJK-listings.sty}{\from{\jobname.dtx}{listings}}
  \file{xunicode-addon.sty}{\from{\jobname.dtx}{xunicode}}
  \usepreamble\emptypreamble
  \usepostamble\emptypostamble
  \usedir{tex/xelatex/xecjk/config}
  \file{\jobname.cfg}{\from{\jobname.dtx}{config}}
  \usedir{doc/xelatex/xecjk/example}
  \file{\jobname-example-autofake.tex}{\from{\jobname.dtx}{ex-autofake}}
  \file{\jobname-example-fallback.tex}{\from{\jobname.dtx}{ex-fallback}}
  \file{\jobname-example-subCJKblock.tex}{\from{\jobname.dtx}{ex-block}}
  \file{\jobname-example-CJKecglue.tex}{\from{\jobname.dtx}{ex-ecglue}}
  \file{\jobname-example-checksingle.tex}{\from{\jobname.dtx}{ex-single}}
  \file{\jobname-example-CJKfntef.tex}{\from{\jobname.dtx}{ex-fntef}}
  \file{\jobname-example-punctstyle.tex}{\from{\jobname.dtx}{ex-punctstyle}}
  \file{\jobname-example-verbatim.tex}{\from{\jobname.dtx}{ex-verb}}
  \file{\jobname-example-IVS.tex}{\from{\jobname.dtx}{ex-IVS}}
  \file{\jobname-example-listings.tex}{\from{\jobname.dtx}{ex-listings}}
  \nopreamble\nopostamble
  \usedir{doc/xelatex/xecjk}
  \file{README.txt}{\from{\jobname.dtx}{readme}}
}

\endbatchfile
%</install>
%<*internal>
\fi
%</internal>
%<*driver|package|config|fntef|listings|xunicode>
%<*!config>
\NeedsTeXFormat{LaTeX2e}
\RequirePackage{expl3}
%</!config>
\GetIdInfo$Id: xeCJK.dtx 477 2013-07-25 22:15:20Z sobenlee $
%<*driver|package>
  {Typesetting CJK scripts with XeLaTeX}
%</driver|package>
%<config>  {Configuration file for xeCJK package}
%<fntef>  {xeCJK patch file for ulem/CJKfntef}
%<listings>  {xeCJK patch file for listings}
%<xunicode>  {addon file for xunicode}
%<*driver|config>
\ProvidesExplFile
%</driver|config>
%<*driver>
  {\ExplFileName.\ExplFileExtension}
%</driver>
%<config>  {\ExplFileName.cfg}
%<package|fntef|listings|xunicode>\ProvidesExplPackage
%<package>  {\ExplFileName}
%<fntef>  {xeCJKfntef}
%<listings>  {xeCJK-listings}
%<xunicode>  {xunicode-addon}
  {\ExplFileDate}{3.2.5}{\ExplFileDescription}
%<*driver>
\def\xeCJKversion{3.2.5}
\let\xeCJKdate\ExplFileDate
\let\xeCJKrevnum\ExplFileVersion
\ExplSyntaxOff
%</driver>
%</driver|package|config|fntef|listings|xunicode>
%
%<*driver>
\expandafter\let\csname ver@thumbpdf.sty\endcsname\fmtversion
\documentclass[numbered,a4paper,full]{l3doc}
\hypersetup{pdfstartview=FitH}
\usepackage{xeCJK}
\usepackage{xeCJKfntef}
\usepackage{geometry}
\usepackage[toc]{multitoc}
\fvset{formatcom=\xeCJKVerbAddon}
\linespread{1.1}
\setlist{noitemsep,topsep=1ex}
\newlist{optiondesc}{description}{3}
\setlist[optiondesc]{font=\mdseries\ttfamily,align=right,labelsep=.5em,leftmargin=4.5em}
\def\PSKeyVal#1#2{\item[#1]\makebox[4em][l]{\meta{#2}}\ignorespaces}
\setmainfont[Ligatures=TeX]{TeX Gyre Pagella}
\setmonofont{CMU Typewriter Text}
\setCJKmainfont[BoldFont=Adobe Heiti Std,ItalicFont=Adobe Kaiti Std]{Adobe Song Std}
\setCJKmonofont{Adobe Kaiti Std}
\xeCJKDeclareSubCJKBlock{HKMD}  { "FF65 }
\xeCJKDeclareSubCJKBlock{Ext-B} { "20000 -> "2A6DF }
\xeCJKDeclareSubCJKBlock{Hangul}{ "1100 -> "11FF, "3130 -> "318F, "A960 -> "A97F, "AC00 -> "D7AF }
\setCJKmainfont[HKMD]{Microsoft YaHei}
\setCJKmainfont[Ext-B]{SimSun-ExtB}
\setCJKmainfont[Hangul]{Adobe Myungjo Std}
\xeCJKsetup{PunctStyle=kaiming,KaiMingPunct+={：；}}
\def\MacroFont{\small\linespread{1}\normalfont\ttfamily}
\def\XeTeX{\hologo{XeTeX}}
\def\XeLaTeX{\hologo{XeLaTeX}}
\def\LaTeX{\hologo{LaTeX}}
\def\LaTeXe{\hologo{LaTeX2e}}
\parindent=2em
\AtBeginDocument{\DeleteShortVerb{\"}\MakeShortVerb{\|}}
\def\contentsname{目录}
\DeclareUrlCommand\email{\def\UrlLeft##1\UrlRight{\href{mailto:##1}{##1}}}
\def\ctexkit{\href{http://code.google.com/p/ctex-kit/}{\texttt{ctex-kit}}}
\def\ctexkitrev#1{%
  \href{http://code.google.com/p/ctex-kit/source/detail?r=#1}
       {\texttt{ctex-kit} rev#1}}
\ExplSyntaxOn
\cs_new_nopar:Npn \package #1
  {
    \href
      {
        http://mirrors.ctan.org/help/Catalogue/entries/
        \tl_expandable_lowercase:n {#1} .html
      }
      { \pkg {#1} }
  }
\NewDocumentCommand \PrintPunctList { O{7} m m }
  {
    \par
    \begingroup
    \CJKfontspec{Microsoft~YaHei}
    \tl_clear:N \l_tmpa_tl
    \int_zero:N \l_tmpa_int
    \int_set:Nn \l_tmpb_int { \clist_count:c { c__xeCJK_#2_chars_clist } }
    \clist_map_inline:cn { c__xeCJK_#2_chars_clist }
      {
        \int_incr:N \l_tmpa_int
        \tl_put_right:Nx \l_tmpa_tl
          {
            \use_none:n ##1 & \tex_char:D ##1 \scan_stop:
            \int_compare:nNnF \l_tmpa_int = \l_tmpb_int
              {
                \int_compare:nNnTF { \int_mod:nn \l_tmpa_int {#1} } = \c_zero
                    { \exp_not:N \\ \scan_stop: } { & }
              }
          }
      }
    \noindent\hfill\linespread{1}\selectfont
    \begin{tabular}{|*{#1}{>{\footnotesize\ttfamily U+}c|c|}}
      \tl_use:N \l_tmpa_tl
    \end{tabular}\hfill\null
    \endgroup
    \par
  }
\ExplSyntaxOff
\geometry{includemp,hmargin={0mm,15mm},vmargin=15mm,footskip=7mm}
\makeatletter
\preto\IndexParms{\linespread{1}}
\appto\GlossaryParms{%
  \def\@idxitem{\par\hangindent 2em}%
  \def\subitem{\@idxitem\hspace*{1em}}%
  \def\subsubitem{\@idxitem\hspace*{2em}}}
\patchcmd\changes@{\space}{\lbrack}{}{}
\patchcmd\@wrglossary{hdpindex}{hdclindex{\the\c@HD@hypercount}}{}{}
\let\orig@meta\meta
\def\meta#1{%
  \orig@meta{\mbox{\itshape\xeCJKsetup{PunctStyle=plain,CJKecglue}#1}}}
\def\TF{true\orvar{}false}
\def\TTF{\defaultvar{true}\orvar{}false}
\def\TFF{true\orvar\defaultvar{false}}
\def\orvar{\textup{\textbar}}
\def\defaultvar#1{\textbf{\textup{#1}}}
\@addtoreset{CodelineNo}{section}
\def\gobblelineno{%
  \addtocounter{CodelineNo}{\m@ne}\let\theCodelineNo\relax}
\def\tokslink#1{\hyperlink{#1}{\ding{51}}}
\makeatother
\def\indexname{代码索引}
\def\glossaryname{版本历史}
\GlossaryPrologue{%
  \addtocounter{secnumdepth}{-10}%
  \section{\glossaryname}}
\IndexPrologue{%
  \section{\indexname}%
  斜体的数字表示对应项说明所在的页码，下划线的数字表示定义所在的代码行号，而直立体的
  数字表示对应项使用时所在的行号。}
\begin{document}
  \DocInput{\jobname.dtx}
  \newgeometry{margin=15mm,footskip=7mm}
  \PrintChanges
  \PrintIndex
\end{document}
%</driver>
% \fi
%
% \CheckSum{5213}
% \changes{v3.1.0}{2012/11/13}{放弃对 \cs{outer} 宏的特殊处理。}
% \changes{v3.1.1}{2012/12/07}{不再依赖 \pkg{xpatch} 宏包。}
% \changes{v3.2.2}{2013/06/01}{修正某些重音不能正确显示的问题。}
% \changes{v3.2.3}{2013/06/07}{提供四个 TECkit 映射文件用于句号转换和简繁互换。}
% \changes{v3.2.4}{2013/07/02}{遵循 \hologo{LaTeX3} 变量需要预先声明的原则。}
%
%
% \title{\bfseries\pkg{xeCJK} 宏包}
% \author{\href{http://www.ctex.org}{ctex.org}}
% \date{\xeCJKdate\qquad\xeCJKversion\thanks{\ctexkitrev{\xeCJKrevnum}.}}
% \maketitle
%
% \tableofcontents
% \vspace{\baselineskip}
%
% \begin{documentation}
%
% \section{简介}
%
% \pkg{xeCJK} 是一个 \XeLaTeX 宏包，用于排版中日韩（CJK）文字。主要功能：
% \begin{enumerate}[labelindent=\parindent,leftmargin=*]
% \item 分别设置 CJK 和英文字体；
% \item 自动忽略 CJK 文字间的空格而保留其它空格，允许在非标点汉字和英文
% 字母 (a -- z, A -- Z) 间断行；
% \item 提供多种标点处理方式： 全角式、半角式、开明式、行末半角式和 CCT 式；
% \item 自动调整中英文间空白。
% \end{enumerate}
%
% \pkg{xeCJK} 使用了 \XeTeX 的一些最新特性，需要 \XeTeX{} |0.9995.0 [2009/06/29]| 以
% 后的版本。\pkg{xeCJK} 依赖 \hologo{LaTeX3} 项目的宏包套件 \package{l3kernel} 和
% \package{l3packages}。\pkg{xeCJK} 还需要通过 \package{fontspec} 宏包来调用系统字
% 体。将在 \ref{subsec:opts}~节介绍的功能选项 |indentfirst| 选项需要
% \package{indentfirst} 宏包的支持。\pkg{xeCJK} 会自动根据需要载入这些宏包。
%
% \pkg{xeCJK} 的原始作者是孙文昌，2009 年 5 月起宏包被收入 \ctexkit\ 项目进行
% 维护，目前主要维护者是刘海洋\footnote{\email{leoliu.pku@gmail.com}} 和
% 李清\footnote{\email{sobenlee@gmail.com}}。
%
% \section{基本用法}
%
% 与其他 \LaTeX{} 宏包一样，引入 \pkg{xeCJK} 宏包只要在导言区使用
% \begin{verbatim}
%     \usepackage{xeCJK}
% \end{verbatim}
% 在引入 \pkg{xeCJK} 宏包之后，只要设置 CJK 文字的字体，就可以在文档中使用中日
% 韩文字了。
%
% 可以在各种文档类中使用 \pkg{xeCJK} 宏包，最简单的示例是：
% \begin{verbatim}
% \documentclass{article}
% \usepackage{xeCJK}
% \setCJKmainfont{SimSun}
%
% \begin{document}
% 中文 \LaTeX 示例。
% \end{document}
% \end{verbatim}
% 上述示例设置了中文字体 SimSun（宋体）。运行此示例要求系统安装了设置的字体，
% 源文件用 UTF-8 编码保存，使用 \XeLaTeX{} 编译。
%
% \pkg{xeCJK} 只提供了字体和标点控制等基本 CJK 语言支持。对于中文文档，可以使
% 用更为高层的 \package{ctex} 宏包或文档类，它将自动调用 \pkg{xeCJK} 并设置好中文
% 字体，同时提供了进一步的本地化支持。详细内容参看 \package{ctex} 宏包套件的说明。
%
% \pkg{xeCJK} 提供了大量选项，可以在宏包调用时作为宏包选项或用 \cs{xeCJKsetup}
% 命令进行设置，详见 \ref{subsec:opts}~节。除了 \cs{setCJKmainfont} 命令，
% \pkg{xeCJK} 还提供了许多其他命令设置和选择中文字体，详见
% \ref{subsec:fontset}~节。其他更详细的功能也都将在下面详细说明。在本文档所在的
% 文件夹的 |example| 目录下面也有一些例子可以参考。
%
% \section{用户手册}
%
% \subsection{宏包选项}
% \label{subsec:opts}
%
% \pkg{xeCJK} 以 \meta{key}|=|\meta{var} 的形式提供宏包选项，你可以在调用宏包
% 的时候直接设置这些选项，也可以在调用宏包之后使用 \cs{xeCJKsetup} 来设置这些选
% 项。\pkg{xeCJK} 内部调用 \pkg{fontspec} 宏包，可以在调用 \pkg{xeCJK} 的时候，
% 使用它的宏包选项。\pkg{xeCJK} 会将 \pkg{fontspec} 的选项传递给它。
%
% \begin{function}{\xeCJKsetup}
%   \begin{syntax}
%     \cs{xeCJKsetup} \{\meta{key_1}=\meta{var_1}, \meta{key_2}=\meta{var_2}, ...\}
%   \end{syntax}
%   其中 \meta{key_1}, \meta{key_2} 是设置选项，而 \meta{val_1}, \meta{val_2} 则是对应选项的
%   设置内容。多个选项可以在一个语句中完成设置。例如
%   \begin{verbatim}
%     \usepackage[PunctStyle=kaiming]{xeCJK}
%   \end{verbatim}
%   等价于
%   \begin{verbatim}
%     \usepackage{xeCJK}
%     ......
%     \xeCJKsetup{PunctStyle=kaiming}
%   \end{verbatim}
% \end{function}
%
% 有些选项或命令后面带有 \hypertarget{expstar}{\hyperlink{expstar}{$\star$}} 号，这表示这
% 个选项或命令只能在导言区中使用，而 \hypertarget{rexpstar}{\hyperlink{rexpstar}{\ding{73}}}
% 号则表示这个选项或命令只能在导言区使用，并且只影响随后定义的 CJK 字体。其余不带特殊标记的
% 选项或命令，如果没有特别说明，可以在导言区或正文中使用。%
% 使用粗体来表示 \pkg{xeCJK} 的默认设置。
%
% \begin{function}[EXP,added=2012-11-22]{LocalConfig}
%   \begin{syntax}
%     LocalConfig = \marg{\TTF\orvar{}name}
%   \end{syntax}
%   是否使用本地配置文件 \texttt{xeCJK-\meta{name}.cfg}。\meta{name} 可以是不包含
%   空格的任意使文件名合法的字符串。如果设置为 |true|，则使用的是 \texttt{xeCJK.cfg}；
%   设置为 |false| 则不载入配置文件。可以把将要在下文介绍到的对 \pkg{xeCJK} 的一些
%   设置（例如设置常用 CJK 字体、修改字符范围和定义新的标点输出格式等）保存到文件
%   \texttt{xeCJK-\meta{name}.cfg}。然后把这个文件放在本地的 |TDS| 目录下的适当
%   位置。使用 \TeX~Live 的用户，可以新建下列目录，然后再把
%   \texttt{xeCJK-\meta{name}.cfg} 放在里面：
%   \begin{verbatim}
%     texlive/texmf-local/tex/xelatex/xecjk
%   \end{verbatim}
%   最后还需要在命令行下执行 |mktexlsr|，刷新文件名数据库以便 \TeX 系统能够找到它。
% \end{function}
%
% \begin{function}[EXP,updated=2012-11-22]{indentfirst}
%   \begin{syntax}
%     indentfirst = \meta{\TTF}
%   \end{syntax}
%   是否使用 \pkg{indentfirst} 宏包，使得跟在章节标题后面的第一段首行也缩进。\bigskip
% \end{function}
%
% 请注意，\pkg{xeCJK} 宏包中只有上述 |LocalConfig|、和 |indentfirst| 这两个选项需要
% 在调用 \pkg{xeCJK} 时设置，而不能通过 \cs{xeCJKsetup} 来设置。
%
% \begin{function}{xeCJKactive}
%   \begin{syntax}
%     xeCJKactive = \meta{\TTF}
%   \end{syntax}
%   打开/关闭对中文的特殊处理。事实上，这个选项会打开/关闭 \XeTeX 的整个字符类机制，依赖
%   这个机制的宏包都会受到影响。
% \end{function}
%
% \begin{function}{CJKspace}
%   \begin{syntax}
%     CJKspace = \meta{\TFF}
%   \end{syntax}
%   缺省状态下，\pkg{xeCJK} 会忽略 CJK 文字之间的空格，使用这一选项来保留它们之间的空格。
% \end{function}
%
% \begin{function}[EXP]{CJKmath}
%   \begin{syntax}
%     CJKmath = \meta{\TFF}
%   \end{syntax}
%   是否支持在数学环境中直接输入 CJK 字符。使用这个选项后，可以直接在数学环境中
%   输出 CJK 字符。
% \end{function}
%
% \begin{function}{CJKglue}
%   \begin{syntax}
%     CJKglue = \{\cs{hskip} 0pt plus 0.08\cs{baselineskip}\}
%   \end{syntax}
%   设置 CJK 文字之间插入的 |glue|，上边是 \pkg{xeCJK} 的默认值。一般来说，除非有
%   特殊需要（例如，改变文字间距等），否则不需要设置这个选项，使用默认值即可。如果要设置
%   这个选项，为了行末的对齐，设置的 |glue| 最好有一定的弹性。
% \end{function}
%
% \begin{function}{CJKecglue}
%   \begin{syntax}
%     CJKecglue = \Arg{glue}
%   \end{syntax}
%   设置 CJK 文字与西文、CJK 文字与行内数学公式之间的间距，默认值是一个空格。使用这个
%   选项设置的 |glue| 最好也要用一定的弹性。请注意，这里设置的 |glue| 只影响
%   \pkg{xeCJK} 根据需要自动添加的空白，源文件中直接输入的 CJK 文字与西文之间的空格不
%   受影响（直接输出）。有时候 \pkg{xeCJK} 可能不能正确地调整间距，需要手动加空格。
% \end{function}
%
% \begin{function}{xCJKecglue}
%   \begin{syntax}
%     xCJKecglue = \marg{\TFF\orvar{}glue}
%   \end{syntax}
%   缺省状态下，\pkg{xeCJK} 不对源文件中直接输入的 CJK 文字与西文之间的空格进行调整，如
%   果需要调整，请使用这个选项。如果使用这个选项，将使用 |CJKecglue| 替换源文件中直接输
%   入的 CJK 文字与西文之间的空格。
% \end{function}
%
% \begin{function}[updated=2013-06-26]{CheckSingle}
%   \begin{syntax}
%     CheckSingle = \meta{\TFF}
%   \end{syntax}
%   是否避免单个 CJK 文字单独占一个段落的最后一行。需要说明的是，这个选项只有在
%   段末的最后一个字是 CJK 文字或者标点符号，并且倒数第二和第三个字都是文字才能
%   正确处理处理孤字的问题。如果这倒数三个字有作为控制序列的参数的情况，那么一般
%   来说也不能正确处理。
% \end{function}
%
% \begin{function}[added=2012-12-06]{PlainEquation}
%   \begin{syntax}
%     PlainEquation = \meta{\TFF}
%   \end{syntax}
%   如果使用了 |$$...$$| 的形式来输入行间数学公式，就需要启用本选项，以便
%   |CheckSingle| 选项能够正确识别。推荐使用 |\[...\]| 的形式来输入行间数学公式。
% \end{function}
%
% \begin{function}[added=2012-12-04]{NewLineCS,NewLineCS+,NewLineCS-}
%   \begin{syntax}
%     NewLineCS = \{ \cs{par} \cs{[} \}
%   \end{syntax}
%   设置造成断行的控制序列，以便 |CheckSingle| 选项能够正确识别。
%   以上是 \pkg{xeCJK} 的初始设置。
% \end{function}
%
% \begin{function}[added=2012-12-04]{EnvCS,EnvCS+,EnvCS-}
%   \begin{syntax}
%     EnvCS = \{ \cs{begin} \cs{end} \}
%   \end{syntax}
%   设置 \LaTeX 环境开始和结束的控制序列，以便 |CheckSingle| 选项能够正确识别。
%   以上是 \pkg{xeCJK} 的初始设置。
% \end{function}
%
% \begin{function}[updated=2012-12-06]{InlineEnv,InlineEnv+,InlineEnv-}
%   \begin{syntax}
%     InlineEnv = \{\meta{env_1}, \meta{env_2}, \meta{env_3}, ...\}
%   \end{syntax}
%   在使用 |CheckSingle| 选项的时候，\pkg{xeCJK} 会将 CJK 文字后接着的 \LaTeX 环境的
%   开始 |\begin{...}| 和结束 |\end{...}| 视为断行的地方，如果有某些特殊
%   的 \LaTeX 环境没有造成断行，可以使用这个选项来声明它，以便 |CheckSingle| 能正确识别。
% \end{function}
%
% \begin{function}{AutoFallBack}
%   \begin{syntax}
%     AutoFallBack = \meta{\TFF}
%   \end{syntax}
%   当文档中有个别生僻字时，可以使用这个选项，自动使用预先设置好的后备字体来输出这些生僻
%   字。后备字体的设置方法将在 \ref{subsec:fontset} 节中介绍。
% \end{function}
%
% \begin{function}[rEXP]{AutoFakeBold}
%   \begin{syntax}
%     AutoFakeBold = \marg{\TFF\orvar{}数字}
%   \end{syntax}
%   全局设定当没有声明对应的粗体时，是否使用\textbf{\CJKfontspec[AutoFakeBold]{Adobe Song Std}伪粗体}；
%   当输入的是数字时，将使用伪粗体，并将使用输入的数字作为伪粗体的默认粗细程度。
% \end{function}
%
% \begin{function}[rEXP]{AutoFakeSlant}
%   \begin{syntax}
%     AutoFakeSlant = \marg{\TFF\orvar{}数字}
%   \end{syntax}
%   全局设定当没有声明对应的斜体时，是否使用\textit{\CJKfontspec[AutoFakeSlant]{Adobe Song Std}伪斜体}；
%   当输入的是数字时，将使用伪斜体，并将使用输入的数字作为伪斜体的默认倾斜程度。
% \end{function}
%
% \begin{function}[rEXP]{EmboldenFactor}
%   \begin{syntax}
%     EmboldenFactor = \marg{数字\orvar\defaultvar{4}}
%   \end{syntax}
%   设置伪粗体的默认粗细程度。
% \end{function}
%
% \begin{function}[rEXP]{SlantFactor}
%   \begin{syntax}
%     SlantFactor = \marg{数字\orvar\defaultvar{0.167}}
%   \end{syntax}
%   设置伪斜体的粗细程度，范围是 $-0.999 \sim 0.999$。
% \end{function}
%
% \begin{function}[updated=2012-11-10]{PunctStyle}
%   \begin{syntax}
%     PunctStyle = \marg{\defaultvar{quanjiao}\orvar{}banjiao\orvar{}kaiming\orvar{}hangmobanjiao\orvar{}CCT\orvar{}plain\orvar{}...}
%   \end{syntax}
%   设置标点处理格式。\pkg{xeCJK} 中预先定义好的格式为
%   \begin{description}[font=\mdseries\ttfamily,align=right,labelsep=1em,labelindent=2em,leftmargin=*]
%     \item[quanjiao] 全角式：所有标点占一个汉字宽度，相邻两个标点占 1.5 汉字宽度；
%     \item[banjiao]  半角式：所有标点占半个汉字宽度；
%     \item[kaiming]  开明式：句末点号用全角，其他半角；
%     \item[hangmobanjiao] 行末半角式：所有标点占一个汉字宽度，行首行末对齐；
%     \item[CCT]   CCT 格式：所有标点符号的宽度略小于一个汉字宽度；
%     \item[plain] 原样（不调整标点间距）。
%   \end{description}
%   可以使用 \ref{subsec:punctstyle} 中介绍的 \cs{xeCJKDeclarePunctStyle} 定义新的标点
%   格式。
% \end{function}
%
% \begin{function}[EXP]{KaiMingPunct,KaiMingPunct+,KaiMingPunct-}
%   \begin{syntax}
%     KaiMingPunct = \marg{\defaultvar{ ． 。？ ！}}
%   \end{syntax}
%   设置开明（|kaiming|）标点处理格式时的句末点号，|KaiMingPunct| 后带的 |+| 与 |-|
%   分别表示从已有的开明句末点号中增加或减少标点。
% \end{function}
%
% \begin{function}[EXP]{LongPunct,LongPunct+,LongPunct-}
%   \begin{syntax}
%     LongPunct = \marg{\defaultvar{ — ― ─ ‥ … }}
%   \end{syntax}
%   设置长标点，例如破折号“——”与省略号“……”，允许在长标点前后
%   断行，但是禁止在它们之间断行。
% \end{function}
%
% \begin{function}[EXP]{MiddlePunct,MiddlePunct+,MiddlePunct-}
%   \begin{syntax}
%     MiddlePunct = \marg{\defaultvar{ — ― ─ · · ･ }}
%   \end{syntax}
%   设置居中显示的标点，例如间隔号“\textbf{·}”。对于在 CJK 文字之间的居中标点，
%   \pkg{xeCJK} 会根据不同的标点处理格式，调整居中标点与前后文字之间的空白，保证
%   其确实居中。对于行末出现的居中标点，允许在其后面断行，但禁止在它前面断行。
% \end{function}
%
% \begin{function}[EXP]{PunctWidth}
%   \begin{syntax}
%     PunctWidth = \Arg{length}
%   \end{syntax}
%   缺省状态下，\pkg{xeCJK} 会根据所选择的标点处理格式自动计算标点所占的宽度，如果对缺
%   省设置不满意，可以通过这一选项来改变它。为了使得标点所占的宽度能够适应字体大小的变化，
%   这里设置的 |length| 的单位最好用 |em| 等相对距离单位，而不建议使用诸如 |pt| 之类的
%   绝对距离单位。这里的设置可用于除了 |plain| 以外的所有标点处理格式。同时，这里的
%   设置对所有的 CJK 标点都生效，如果只要设置部分标点，请使用 \ref{subsec:punct}~节的
%   \cs{xeCJKsetwidth}。
% \end{function}
%
% \begin{function}{AllowBreakBetweenPuncts}
%   \begin{syntax}
%     AllowBreakBetweenPuncts = \meta{\TFF}
%   \end{syntax}
%   缺省状态下，\pkg{xeCJK} 禁止在相邻 CJK 右标点和 CJK 左标点之间换行，可以使用
%   这一选项改变这一设置。
% \end{function}
%
% \begin{function}[added=2012-12-02]{CheckFullRight}
%   \begin{syntax}
%     CheckFullRight = \meta{\TFF}
%   \end{syntax}
%   某些控制序列要求不能在它的前面断行。但是在缺省状态下，单个全角右标点的后面总是
%   可以断行的。因此当这些控制序列出现在全角右标点后面时，可能会出现意料之外的断行。
%   此时可以使用这个选项来避免这个情况。
% \end{function}
%
% \begin{function}[added=2012-12-02]{NoBreakCS,NoBreakCS+,NoBreakCS-}
%   \begin{syntax}
%     NoBreakCS = \{ \cs{footnote} \cs{footnotemark} \cs{nobreak} \}
%   \end{syntax}
%   设置不能在全角右标点后断行的控制序列。以上是 \pkg{xeCJK} 的默认设置。如果这些
%   控制序列在文档中只出现少量几次，也可以不必使用 |CheckFullRight| 选项，而是手工
%   在这些控制序列前面加上 \ref{subsec:others}~节介绍的 \cs{xeCJKnobreak}。
% \end{function}
%
% \begin{function}[updated=2013-05-29]{Verb}
%   \begin{syntax}
%     Verb = \meta{\TF\orvar\defaultvar{env}\orvar{}env+}
%   \end{syntax}
%   \texttt{true} 表示在 \cs{verb} 命令或 \texttt{verbatim} 环境里不自动调整中英文
%   之间的间距。\texttt{env} 选项在 \texttt{verbatim} 环境里自动计算中西文间距和中文
%   之间的间距，以便于保持代码的对齐；\texttt{env} 选项不调整 \cs{verb} 里的间距，^^A
%   \texttt{env+} 选项还将正文里设置的间距应用到 \cs{verb} 里。^^A
%   这个选项对使用到 \cs{verbatim@font} 命令的情形均有效，更一般的情况可以使用
%   \ref{subsec:others}~节介绍的 \cs{xeCJKVerbAddon}。
% \end{function}
%
% \subsection{字体设置与选择}
% \label{subsec:fontset}
%
% \begin{function}[EXP]{\setCJKmainfont}
%   \begin{syntax}
%     \cs{setCJKmainfont} \oarg{font features} \Arg{font name}
%   \end{syntax}
%   设置正文罗马族的 CJK 字体，影响 \cs{rmfamily} 和 \cs{textrm} 的字体。后面两个
%   参数继承自 \pkg{fontspec} 宏包， \meta{font features} 表示字体属性选项，
%   \meta{font name} 是字体名。字体名可以是字体族名，也可以是字体的文件名，查
%   找字体名见 \ref{subsubsec:fontsearch}~节；可用的字体属性选项参见
%   \pkg{fontspec} 宏包的文档。需要说明的是 \pkg{xeCJK} 修改了 |AutoFakeBold|
%   和 |AutoFakeSlant| 选项，以便配合全局伪粗体和伪斜体的设定。
% \end{function}
%
% \begin{function}{AutoFakeBold,AutoFakeSlant}
%   \begin{syntax}
%     AutoFakeBold  = \marg{\TF\orvar{}数字}
%     AutoFakeSlant = \marg{\TF\orvar{}数字}
%   \end{syntax}
%   局部设置当前字体族的伪粗和伪斜属性。如果没有在局部给出这些选项，将使用全局设定。
% \end{function}
%
% \begin{function}[added=2013-06-07]{Mapping}
%   \begin{syntax}
%     Mapping = \marg{fullwidth-stop\orvar{}full-stop\orvar{}han-trad\orvar{}han-simp\orvar{}...}
%   \end{syntax}
%   \pkg{xeCJK} 提供了以上四个 \href{http://scripts.sil.org/teckit}{TECKit} 映射
%   文件，可以在设置字体的时候通过 \texttt{Mapping} 选项来使用它们。其中
%   \texttt{fullwidth-stop} 用于将正常句号“。”转换成全角实心句号“．”，
%   \texttt{full-stop} 的作用相反。\texttt{han-trad} 用于将简体中文转换成繁体中文，
%   \texttt{han-simp} 的作用相反。需要注意的是，简繁互换都是简单机械的字字对译，
%   不能做到完全准确，使用时要小心。例如简体的“发挥”和“头发”被转换成繁体的
%   “發揮”和“頭發”，显然后者应作“頭髮”。也可以根据实际需要，制作新的映射文件，
%   请参考 TECKit 的文档。
% \end{function}
%
% \begin{function}[EXP]{\setCJKsansfont}
%   \begin{syntax}
%     \cs{setCJKsansfont} \oarg{font features} \Arg{font name}
%   \end{syntax}
%   设置正文无衬线族的 CJK 字体，影响 \cs{sffamily} 和 \cs{textsf} 的字体。
% \end{function}
%
% \begin{function}[EXP]{\setCJKmonofont}
%   \begin{syntax}
%     \cs{setCJKmonofont}  \oarg{font features} \Arg{font name}
%   \end{syntax}
%   设置正文等宽族的 CJK 字体，影响 \cs{ttfamily} 和 \cs{texttt} 的字体。
% \end{function}
%
% \begin{function}[EXP]{\setCJKfamilyfont}
%   \begin{syntax}
%     \cs{setCJKfamilyfont} \Arg{family} \oarg{font features} \Arg{font name}
%   \end{syntax}
%   声明新的 CJK 字体族 \meta{family} 并指定字体。
% \end{function}
%
% \begin{function}[updated=2012-10-27]{\CJKfamily}
%   \begin{syntax}
%     \cs{CJKfamily}  \Arg{family}
%     \cs{CJKfamily+} \Arg{family}
%     \cs{CJKfamily-} \Arg{family}
%   \end{syntax}
%   用于在文档中切换 |CJK| 字体族，\meta{family} 必须预先声明。\cs{CJKfamily} 仅对
%   CJK 字符类有效，\cs{CJKfamily+} 对所有字符类均有效，\cs{CJKfamily-} 对非 CJK 字
%   符类有效。当 \cs{CJKfamily+} 和 \cs{CJKfamily-} 的参数为空时，则使用当前的 |CJK| 字体族。
% \end{function}
%
% \begin{function}[EXP]{\newCJKfontfamily}
%   \begin{syntax}
%     \cs{newCJKfontfamily} \oarg{family} \cs{\meta{font-switch}} \oarg{font features} \Arg{font name}
%   \end{syntax}
%   声明新的 CJK 字体族 \meta{family} 并指定字体，并定义 \cs{\meta{font-switch}}，在
%   文档中可以使用它来切换 CJK 字体族。可以不必指定 \meta{family}，这时候 \meta{family}
%   将等于 \meta{font-switch}。事实上，\cs{newCJKfontfamily} 是 \cs{setCJKfamilyfont} 和
%   \cs{CJKfamily} 的合并。例如
%   \begin{verbatim}
%     \newCJKfontfamily[song]\songti{SimSun}
%   \end{verbatim}
%   等价于
%   \begin{verbatim}
%     \setCJKfamilyfont{song}{SimSun}
%     \newcommand*{\songti}{\CJKfamily{song}}
%   \end{verbatim}
% \end{function}
%
% \begin{function}{\CJKfontspec}
%   \begin{syntax}
%     \cs{CJKfontspec} \oarg{font features} \Arg{font name}
%   \end{syntax}
%   在文档中随机定义新的 CJK 字体族，并马上使用它。
% \end{function}
%
% \begin{function}[rEXP]{\defaultCJKfontfeatures}
%   \begin{syntax}
%     \cs{defaultCJKfontfeatures} \Arg{font features}
%   \end{syntax}
%   全局设置 CJK 字体族的默认选项。例如，使用
%   \begin{verbatim}
%     \defaultCJKfontfeatures{Scale=0.962216}
%   \end{verbatim}
%   可以将全部 CJK 字体缩小为 |0.962216|。\pkg{xeCJK} 宏包的初始化设置是
%   \begin{verbatim}
%     \defaultCJKfontfeatures{Script=CJK}
%   \end{verbatim}
% \end{function}
%
% \begin{function}[updated=2013-06-30]{\addCJKfontfeatures}
%   \begin{syntax}
%     \cs{addCJKfontfeatures}   \Arg{font features}
%     \cs{addCJKfontfeatures} * \Arg{font features}
%     \cs{addCJKfontfeatures}   \oarg{block_1, block_2, ...} \Arg{font features}
%     \cs{addCJKfontfeatures} * \oarg{block_1, block_2, ...} \Arg{font features}
%   \end{syntax}
%   临时增加当前使用的 CJK 字体的选项。第一条命令，仅对当前 CJK 主分区字体有效；
%   第二条对主分区和其它分区的字体都有效；第三条仅对可选参数中指定的分区有效；
%   第四条对主分区和可选参数中指定的分区有效。例如，使用
%   \begin{verbatim}
%     \addCJKfontfeatures{Scale=1.1}
%   \end{verbatim}
%   可以将文档中当前使用的 CJK 主分区字体放大为 |1.1|。
% \end{function}
%
% \begin{function}{\CJKrmdefault}
%   保存 \cs{textrm} 和 \cs{rmfamily} 所使用的 CJK 字体族，默认值是 |rm|。
%   类似西文字体的 \cs{rmdefault}。
% \end{function}
%
% \begin{function}{\CJKsfdefault}
%   保存 \cs{textsf} 和 \cs{sffamily} 所使用的 CJK 字体族，默认值是 |sf|。
%   类似西文字体的 \cs{sfdefault}。
% \end{function}
%
% \begin{function}{\CJKttdefault}
%   保存 \cs{texttt} 和 \cs{ttfamily} 所使用的 CJK 字体族，默认值是 |tt|。
%   类似西文字体的 \cs{ttdefault}。
% \end{function}
%
% \begin{function}[updated=2013-01-01]{\CJKfamilydefault}
%   保存 \cs{textnormal} 和 \cs{normalfont} 所使用的 CJK 字体族。类似西文字体的 \cs{familydefault}。
%   初始值是 \cs{CJKrmdefault}。如果没有在导言区中修改它，\pkg{xeCJK} 会在导言区
%   结束的时候根据西文字体的情况自动更新 \cs{CJKfamilydefault}。因此，在导言区里使用
%   \begin{verbatim}
%     \renewcommand\familydefault{\sfdefault}
%   \end{verbatim}
%   就可以将全文的 CJK 和西文默认字体都改为无衬线字体族。
% \end{function}
%
% \begin{function}[EXP]{\setCJKmathfont}
%   \begin{syntax}
%     \cs{setCJKmathfont} \oarg{font features} \Arg{font name}
%   \end{syntax}
%   设置数学公式中的 CJK 字体族。如果使用了 |CJKmath| 选项，但是没有使用
%   \cs{setCJKmathfont} 设置数学公式中的 CJK 字体，那么将使用 \cs{CJKfamilydefault}
%   作为数学公式中的 CJK 字体。
% \end{function}
%
% \begin{function}[EXP]{\setCJKfallbackfamilyfont}
%   \begin{syntax}
%     \cs{setCJKfallbackfamilyfont} \Arg{family} \oarg{font features} \Arg{font name}
%   \end{syntax}
%   设置 CJK 字体族 \meta{family} 的备用字体。例如，使用
%   \begin{verbatim}
%     \setCJKmainfont{SimSun}
%     \setCJKfallbackfamilyfont{\CJKrmdefault}{SimSun-ExtB}
%   \end{verbatim}
%   可以将 |SimSun-ExtB| 作为 |SimSun| 的备用字体。
% \end{function}
%
% \begin{function}{FallBack}
%   \begin{syntax}
%     FallBack = \{\oarg{font features}\Arg{font name}\}
%   \end{syntax}
%   \pkg{xeCJK} 在 \meta{font features} 里增加了 |FallBack| 这个选项。用来在声明主
%   字体的时候，同时设置备用字体。例如，上面的例子等价于：
%   \begin{verbatim}
%     \setCJKmainfont[FallBack=SimSun-ExtB]{SimSun}
%   \end{verbatim}
%   如果 |FallBack| 的值为空，将设置的是备用字体。例如，
%   \begin{verbatim}
%     \setCJKmainfont[FallBack,AutoFakeBold,Scale=.97]{SimSun-ExtB}
%   \end{verbatim}
%   等价于
%   \begin{verbatim}
%     \setCJKfallbackfamilyfont{\CJKrmdefault}[AutoFakeBold,Scale=.97]{SimSun-ExtB}
%   \end{verbatim}
% \end{function}
%
% \begin{function}[EXP,updated=2013-06-30]{\setCJKfallbackfamilyfont}
%   \begin{syntax}
%     \cs{setCJKfallbackfamilyfont} \Arg{family} \oarg{common font features}
%        \  \{
%        \    \{\oarg{font features_1} \Arg{font name_1}\} ,
%        \    \{\oarg{font features_2} \Arg{font name_2}\} ,
%        \     ......
%        \  \}
%   \end{syntax}
%   \cs{setCJKfallbackfamilyfont} 还可以用于设置多层的备用字体。例如，使用
%   \begin{verbatim}
%     \setCJKmainfont[AutoFakeBold,AutoFakeSlant]{KaiTi_GB2312}
%     \setCJKfallbackfamilyfont{\CJKrmdefault}[AutoFakeSlant]
%       { [BoldFont=SimHei]{SimSun} ,
%         [AutoFakeBold]   {SimSun-ExtB} }
%   \end{verbatim}
%   之后，就设置了 |SimSun| 是 |KaiTi_GB2312| 的备用字体，而 |SimSun-ExtB| 是
%   |SimSun| 的备用字体。若当前字体族缺字，并没有备用字体，则尝试使用
%   \cs{CJKfamilydefault} 的备用字体。
% \end{function}
%
% \subsubsection{\XeTeX 的字体名查找}
% \label{subsubsec:fontsearch}
%
% 由于在 \pkg{fontspec} 宏包文档中缺少关于如何查看 \XeTeX{} 可用字体名的说明，
% 这里略作说明。
%
% \XeTeX{} 通常使用 fontconfig 库查找和调用字体，因此，可以用 |fc-list| 命令显
% 示可用的字体。在命令行（Windows 的“命令提示符”，Linux 的 Console）下运行以
% 下命令：
% \begin{verbatim}
%     fc-list > fontlist.txt
% \end{verbatim}
% 可以将系统中所有安装的字体列表存入 \file{fontlist.txt} 文件中（可能很长）。
%
% |fc-list| 命令列出的信息很多，而且在安装字体较多的 Windows 系统上的输出将非
% 常庞大，如其中可能包含：
% \begin{verbatim}
% Times New Roman:style=cursiva,kurzíva,kursiv,Πλάγια,Italic,
%   Kursivoitu,Italique,Dőlt,Corsivo,Cursief,kursywa,Itálico,Курсив,
%   İtalik,Poševno,nghiêng,Etzana
% Times New Roman:style=Negreta cursiva,tučné kurzíva,fed kursiv,
%   Fett Kursiv,Έντονα Πλάγια,Bold Italic,Negrita Cursiva,
%   Lihavoitu Kursivoi,Gras Italique,Félkövér dőlt,Grassetto Corsivo,
%   Vet Cursief,Halvfet Kursiv,Pogrubiona kursywa,Negrito Itálico,
%   Полужирный Курсив,Tučná kurzíva,Fet Kursiv,Kalın İtalik,
%   Krepko poševno,nghiêng đậm,Lodi etzana
% Times New Roman:style=Negreta,tučné,fed,Fett,Έντονα,Bold,Negrita,
%   Lihavoitu,Gras,Félkövér,Grassetto,Vet,Halvfet,Pogrubiona,Negrito,
%   Полужирный,Fet,Kalın,Krepko,đậm,Lodia
% Times New Roman:style=Normal,obyčejné,Standard,Κανονικά,Regular,
%   Normaali,Normál,Normale,Standaard,Normalny,Обычный,Normálne,Navadno,
%   thường,Arrunta
% 宋体,SimSun:style=Regular
% 黑体,SimHei:style=Normal,obyčejné,Standard,Κανονικά,Regular,Normaali,
%   Normál,Normale,Standaard,Normalny,Обычный,Normálne,Navadno,Arrunta
% \end{verbatim}
% 在 \pkg{fontspec} 或 \pkg{xeCJK} 中使用的字体族名是上面列表中冒号前的部分。
% 例如可以使用
% \begin{verbatim}
%     \setmainfont{Times New Roman}
%     \setCJKmainfont{SimSun} % 或者 \setCJKmainfont{宋体}
% \end{verbatim}
% 来设置字体。
%
% 为了方便起见，|fc-list| 命令也可以加上各种选项控制输出格式，例如如果只要列出
% 所有的中文字体的字体族名，可以用命令：
% \begin{verbatim}
%     fc-list -f "%{family}\n" :lang=zh  > zhfont.txt
% \end{verbatim}
% 这样就把字体列表保存在文件 \file{zhfont.txt} 中\footnote{由于汉字编码原因，
% Windows 下总需要把字体列表输出的文件中防止乱码。}。这样列出的字体列表就比较
% 简明易用，如 Windows 下预装的中文字体：
% \begin{verbatim}
% Arial Unicode MS
% FangSong,仿宋
% KaiTi,楷体
% Microsoft YaHei,微软雅黑
% MingLiU,細明體
% NSimSun,新宋体
% PMingLiU,新細明體
% SimHei,黑体
% SimSun,宋体
% \end{verbatim}
% 要列出日文和韩文的字体，可以把 |:lang=zh| 选项中的 |zh| 改成 |ja| 或 |ko|。
%
% \pkg{fontspec} 和 \pkg{xeCJK} 也可以使用字体的文件名访问字体。例如 Windows
% 下的宋体也可以使用命令：
% \begin{verbatim}
%     \setCJKmainfont{simsun.ttc}
% \end{verbatim}
% 来设置。设置字体文件名的相关选项和语法在 \pkg{fontspec} 宏包手册中叙述甚详，
% 这里不再赘述。有个别字体名不规范的中文字体，\pkg{xeCJK} 宏包可能无法正确地通
% 过字体名访问，那么也可以使用这种方式设置。
%
% \subsection{CJK 分区字体设置}
% \label{subsec:block}
%
% 众所周知，CJK 文字数量极其庞大，单一的字体不可能涵盖所有的 CJK 文字。\pkg{xeCJK} 可
% 以在同一 CJK 字体族下，自动使用不同的字体输出 CJK 字符范围内不同区块里的文字。首先要
% 声明 CJK 子分区。
%
% \begin{function}[EXP]{\xeCJKDeclareSubCJKBlock}
%   \begin{syntax}
%     \cs{xeCJKDeclareSubCJKBlock}  \Arg{block} \Arg{block range}
%     \cs{xeCJKDeclareSubCJKBlock*} \Arg{block} \Arg{block range}
%   \end{syntax}
%   其中 \meta{block range} 是逗号列表，可以是 CJK 字符的 |Unicode| 范围，也可以是单个字符
%   的 |Unicode|。例如
%   \begin{verbatim}
%     { `中 -> `文 , "3400 -> "4DBF , "5000 -> "7000 , `汉 , `字 , "3500 }
%   \end{verbatim}
%   的形式。需要注意的是，这里设置的 \meta{block range} 除非确实需要（例如某些特殊字体使用
%   了 |Unicode| 中的私人使用区的情况），否则不要超出源代码中预设的
%   \hyperlink{CJKcharclass}{CJK 文字范围}。使用
%   \begin{verbatim}
%     \xeCJKDeclareSubCJKBlock{SPUA}{ "E400 -> "E4DA , "E500 -> "E5E8 , "E600 -> "E6CE }
%     \xeCJKDeclareSubCJKBlock{Ext-B}{ "20000 -> "2A6DF }
%   \end{verbatim}
%   就声明了 |SPUA| 和 |Ext-B| 这两个个子分区。同时在 \ref{subsec:fontset} 节介绍的
%   CJK 字体设置命令的 \meta{font features} 里新建了 |SPUA| 和 |Ext-B| 这两个选项。
%   新建的这两个选项的使用方法跟 \ref{subsec:fontset} 介绍的 |FallBack| 类似。可以
%   通过它们来设置字体。\strut
% \end{function}
%
%   例如，可以使用
%   \begin{verbatim}
%     \setCJKmainfont[SPUA=SunmanPUA,Ext-B=SimSun-ExtB]{SimSun}
%   \end{verbatim}
%   设置文档的主字体是 |SimSun|，|SPUA| 分区的字体是 |SunmanPUA|，而 |Ext-B| 分区
%   的字体是 |SimSun-ExtB|。
%
%   \cs{xeCJKDeclareSubCJKBlock} 应该在声明所有的 CJK 字体族之前使用。如果有某个 CJK 字
%   体族没有设置 \meta{block} 选项，将使用 \cs{CJKfamilydefault} 的 \meta{block} 选项
%   作为该 CJK 字体族的 \meta{block} 选项。如果希望在使用某 CJK 字体族时，不在 CJK 主
%   分区与 \meta{block} 之间切换字体，可以使用 \meta{block}|=*| 选项。带星号的命令除了
%   设置 CJK 子分区以外，还重置标点符号所属的字符类。
%
% \begin{function}{\xeCJKCancelSubCJKBlock}
%   \begin{syntax}
%     \cs{xeCJKCancelSubCJKBlock}  \Arg{block_1, block_2, ...}
%     \cs{xeCJKCancelSubCJKBlock*} \Arg{block_1, block_2, ...}
%   \end{syntax}
%   在文档中取消对 |CJK| 分区的声明。带星号的命令还重置标点符号所属的字符类。
% \end{function}
%
% \begin{function}{\xeCJKRestoreSubCJKBlock}
%   \begin{syntax}
%     \cs{xeCJKRestoreSubCJKBlock}  \Arg{block_1, block_2, ...}
%     \cs{xeCJKRestoreSubCJKBlock*} \Arg{block_1, block_2, ...}
%   \end{syntax}
%   在文档中恢复对 |CJK| 分区的声明。带星号的命令还重置标点符号所属的字符类。
% \end{function}
%
% \subsection{设置 CJK 字符范围}
%
% \begin{function}[EXP]{\xeCJKDeclareCharClass}
%   \begin{syntax}
%     \cs{xeCJKDeclareCharClass}  \Arg{class} \Arg{class range}
%     \cs{xeCJKDeclareCharClass*} \Arg{class} \Arg{class range}
%   \end{syntax}
%   \meta{class range} 的格式和 \ref{subsec:block} 节的 \meta{block range} 相同。
%   \meta{class} 的有效值见源代码（第 \ref{sec:xeCJK-class-set} 节）。\pkg{xeCJK} 已
%   经支持 |Unicode| 中所有 CJK 文字和标点。一般来说，不要轻易改变字符类别。带星号的
%   命令除了设置字符类别以外，为了确保标点处理的正确性，还重置标点符号所属的字符类。
% \end{function}
%
% \begin{function}[EXP]{\xeCJKResetCharClass}
%   用于恢复 \pkg{xeCJK} 对各个字符类别的初始化设置。
% \end{function}
%
% \begin{function}[EXP]{\xeCJKResetPunctClass}
%   用于重置标点符号所属的字符类。
% \end{function}
%
% \begin{function}{\normalspacedchars}
%   \begin{syntax}
%     \cs{normalspacedchars} \Arg{char list}
%   \end{syntax}
%   在 \meta{char list} 中出现的字符两端不自动添加空格，初始设置是 |/|。
% \end{function}
%
% \subsection{标点符号的处理}
%
% \pkg{xeCJK} 对标点符号的输出宽度的调整是通过调整其左边或右边的空白宽度来实现的。
% 按照目前的处理方式，对于位于左边的标点符号（如左引号），\pkg{xeCJK} 只能调整它
% 左边的空白；对于位于右边的标点符号（如右引号），\pkg{xeCJK} 只能调整它右边的空
% 白；对于居中的标点符号，则调整其左右空白，以保证其居中。
%
% \subsubsection{设置特定标点符号的宽度和间距}
% \label{subsec:punct}
%
% 这里的设置可用于除 |plain| 以外的所有标点处理格式。
%
% \begin{function}[EXP]{\xeCJKsetwidth}
%   \begin{syntax}
%     \cs{xeCJKsetwidth} \Arg{标点列表} \Arg{length}
%   \end{syntax}
%   \meta{标点列表}可以是单个标点，也可以是多个标点。例如，
%   \begin{verbatim}
%     \xeCJKsetwidth{。？}{0.7em}
%   \end{verbatim}
%   将设置句号和问号所占的宽度为 |0.7 em|。
% \end{function}
%
% \begin{function}[EXP]{\xeCJKsetkern}
%   \begin{syntax}
%     \cs{xeCJKsetkern} \Arg{前标点} \Arg{后标点} \Arg{length}
%   \end{syntax}
%   \pkg{xeCJK} 会根据选定的标点处理格式自动调整相邻的前后两个 |CJK| 标点符号的
%   空白宽度。如果需要对个别情况进行特殊调整，可以使用这个命令。例如，
%   \begin{verbatim}
%     \xeCJKsetkern{：}{“}{0.3em}
%   \end{verbatim}
%   将设置冒号与左双引号之间的空白宽度为 |0.3 em|。
% \end{function}
%
% \subsubsection{定义标点符号处理格式}
% \label{subsec:punctstyle}
%
% \begin{function}[EXP,added=2012-11-10]{\xeCJKDeclarePunctStyle}
%   \begin{syntax}
%     \cs{xeCJKDeclarePunctStyle} \Arg{style} \Arg{options}
%   \end{syntax}
%   定义新的标点符号处理格式，已经存在的同名格式将被覆盖。可以设置的选项将在下面介绍。
% \end{function}
%
% \begin{function}[EXP,added=2012-11-10]{\xeCJKEditPunctStyle}
%   \begin{syntax}
%     \cs{xeCJKEditPunctStyle} \Arg{style} \Arg{options}
%   \end{syntax}
%   修改已有的标点符号处理格式。\strut
% \end{function}
%
% 下面是可以设置的标点符号格式选项。其中左边一栏是选项名称，中间是选项的输入值类
% 型，右边则是相关说明。某些选项之间是互斥的，具有优先级关系。要使下一级的选项有
% 效，则需要先禁用上一级的设置：对于 \meta{boolean} 类型的选项，将其设置为 |false|，
% 对于 \meta{length} 类型的选项，将其设置为 \cs{maxdimen}，而对于 \meta{real} 类型
% 的选项，将其设置为 |0|。
%
% \begin{optiondesc}
%   \PSKeyVal{enabled-global-setting}{boolean}
%     是否使用 \cs{xeCJKsetup} 的 |PunctWidth| 选项和 \cs{xeCJKsetwidth}、
%     \cs{xeCJKsetkern} 的设置。默认值是 |true|。
% \end{optiondesc}
%
% \begin{optiondesc}
%   \PSKeyVal{fixed-punct-width}{length}
%     设置单个标点符号的宽度。默认值是 \cs{maxdimen}。
%   \PSKeyVal{fixed-punct-ratio}{real}
%     设置单个标点符号的输出宽度与实际宽度的比例。默认值是 |1.0|。
%   \PSKeyVal{mixed-punct-width}{length}
%     设置句末标点符号的宽度。其中句末标点符号通过 \cs{xeCJKsetup} 的 |KaiMingPunct|
%     来设置。默认值是与 |fixed-punct-width| 选项的值相同。
%   \PSKeyVal{mixed-punct-width}{real}
%     设置句末标点符号的宽度比例。默认值是与 |fixed-punct-ratio| 选项的值相同。
%   \PSKeyVal{middle-punct-width}{length}
%     设置居中标点符号的宽度。其中居中标点符号通过 \cs{xeCJKsetup} 的 |MiddlePunct|
%     来设置。默认值是与 |fixed-punct-width| 选项的值相同。
%   \PSKeyVal{middle-punct-width}{real}
%     设置居中标点符号的宽度比例。默认值是与 |fixed-punct-ratio| 选项的值相同。
% \end{optiondesc}
%
% 以上三个选项设置的是标点的固定宽度或比例，\pkg{xeCJK} 会根据设定的选项计算标点符号
% 左/右的空白宽度。下面的选项设置的是标点符号左/右的空白宽度或比例，因此不同标点符号
% 的宽度可能会不同。为了使下面的选项生效，需要先禁用上面的相应选项。优先级自上而下。
%
% \begin{optiondesc}
%   \PSKeyVal{fixed-margin-width}{length}
%     设置标点的左/右空白宽度。默认值是 \cs{maxdimen}。
%   \PSKeyVal{fixed-margin-ratio}{real}
%     设置标点的左/右空白宽度与字体中该标点的相应实际边界宽度的比例。默认值是 |1.0|。
%   \PSKeyVal{mixed-margin-width}{length}
%     设置句末标点的左/右空白宽度。默认值是与 |fixed-margin-width| 的值相同。
%   \PSKeyVal{mixed-margin-ratio}{real}
%     设置句末标点的左/右空白宽度的比例。默认值是与 |fixed-margin-ratio| 的值相同。
%   \PSKeyVal{middle-margin-width}{length}
%     设置居中标点的两边空白宽度。默认值是与 |fixed-margin-width| 的值相同。
%   \PSKeyVal{middle-margin-ratio}{real}
%     设置居中标点的两边空白宽度之和与两边实际两边边界宽度之和的比例。
%     默认值是与 |fixed-margin-ratio| 的值相同。
% \end{optiondesc}
%
% \begin{optiondesc}
%   \PSKeyVal{add-min-bound-to-margin}{boolean}
%     是否在以上计算结果的基础上再加上标点的左右实际边界宽度中的最小值。这个选项
%     对居中的标点无效。默认值是 |false|。
% \end{optiondesc}
%
% \begin{optiondesc}
%   \PSKeyVal{optimize-margin}{boolean}
%     使用以上设置空白宽度或比例的选项时，最终输出的标点符号左/右的空白宽度可能大
%     于原来的实际边界宽度。若此时本选项被设置为 |true|，则使用原来的实际边界宽度。
%     而使用 |fixed-punct-width| 选项计算得出的左/右宽度可能小于该标点的另一侧宽
%     度，若此时本选项被启用，则使用该标点的另一侧宽度。默认值为 |false|。
% \end{optiondesc}
%
% \begin{optiondesc}
%   \PSKeyVal{margin-minimum}{length}
%     指定标点符号左/右的最小空白宽度。当经过以上选项设置的空白宽度小于这个选项的值
%     时，则使用这个选项的值。默认值是 |0 pt|。
% \end{optiondesc}
%
% 下面的选项处理的是前后相邻的两个标点符号之间的空白宽度。这些选项是互斥的，优先级
% 自上而下。
%
% \begin{optiondesc}
%   \PSKeyVal{enabled-kerning}{boolean}
%     是否调整前后相邻的两个标点之间的空白宽度。如果设置为 |false|，则每个标点都按
%     原来的输出宽度输出。默认值是 |true|。
%   \PSKeyVal{min-bound-to-kerning}{boolean}
%     是否使用当前字体中前面标点实际左右边界的最小值与后面标点实际左右边界的最小值
%     中的最大值作为两个标点之间的空白宽度。默认值是 |false|。
%   \PSKeyVal{kerning-total-width}{length}
%     设置两个标点的总共宽度。此时 \pkg{xeCJK} 会自动计算两个标点之间的空白宽度。
%     默认值是 \cs{maxdimen}。
%   \PSKeyVal{kerning-total-ratio}{real}
%     设置两个标点的总共输出宽度与实际宽度的比例。默认值是 |0.75|。
%   \PSKeyVal{same-align-margin}{length}
%     前后两个标点位于同侧时，它们之间的空白宽度。默认值是 \cs{maxdimen}。
%   \PSKeyVal{same-align-ratio}{real}
%     前后两个标点位于同侧时，它们之间的空白宽度与实际输出宽度的比例。默认值是 |0|。
%   \PSKeyVal{different-align-margin}{length}
%     前后两个标点位于异侧时，它们之间的空白宽度。默认值是 \cs{maxdimen}。
%   \PSKeyVal{different-align-ratio}{real}
%     前后两个标点位于异侧时，它们之间的空白宽度与实际输出宽度的比例。默认值是 |0|。
%   \PSKeyVal{kerning-margin-width}{length}
%     设置前后两个标点之间的空白宽度。默认值是 \cs{maxdimen}。
%   \PSKeyVal{kerning-margin-ratio}{real}
%     设置前后两个标点之间的空白宽度与实际输出空白的比例。默认值是 |1.0|。
% \end{optiondesc}
%
% \begin{optiondesc}
%   \PSKeyVal{optimize-kerning}{boolean}
%     使用以上选项计算出两个标点之间的空白宽度可能小于通过 |min-bound-to-kerning|
%     选项得出的结果。当出现这一情况时，若此选项被设置为 |true|，则使用该选项的空
%     白宽度。默认值为 |false|。
% \end{optiondesc}
%
% \begin{optiondesc}
%   \PSKeyVal{kerning-margin-minimum}{length}
%     指定两个标点之间的最小空白宽度。当经过以上选项设置的空白宽度小于这个选项的值
%     时，则使用这个选项的值。默认值是 |0 pt|。
% \end{optiondesc}
%
% 事实上，\pkg{xeCJK} 的默认设置就相当于中文全角（|quanjiao|）格式。可以使用上面
% 说明的选项定义新的标点处理格式。例如，使用
% \begin{verbatim}
% \xeCJKDeclarePunctStyle { mine }
%   {
%     fixed-punct-ratio       = 0 ,
%     fixed-margin-width      = 0 pt ,
%     mixed-margin-width      = \maxdimen ,
%     mixed-margin-ratio      = 0.5 ,
%     middle-margin-width     = \maxdimen ,
%     middle-margin-ratio     = 0.5 ,
%     add-min-bound-to-margin = true ,
%     min-bound-to-kerning    = true ,
%     kerning-margin-minimum  = 0.1 em
%   }
% \end{verbatim}
% 就定义了一个名为 |mine| 的标点处理格式。可以在通过
% \begin{verbatim}
% \xeCJKsetup{PunctStyle=mine}
% \end{verbatim}
% 在文档中使用这个格式。它的意义是：使用标点符号的实际左右边界中的最小值作为其左/右
% 空白的宽度，对于句末标点和居中标点，再加上实际边界空白的一半；使用相邻两个标点的
% 实际边界中的较小值作为它们之间的空白宽度，并且最小的空白宽度是 |0.1 em|。
% 再例如，使用
% \begin{verbatim}
% \xeCJKEditPunctStyle { hangmobanjiao } { enabled-global-setting = false }
% \end{verbatim}
% 将使得 \cs{xeCJKsetkern} 等的设置对 |hangmobanjiao| 这一格式无效。
%
% \subsection{其它}
% \label{subsec:others}
%
% \begin{function}[updated=2013-06-29]{\xeCJKVerbAddon,\xeCJKOffVerbAddon}
%   调整文字间距以便于让 CJK 字符占的宽度等于西文等宽字体中两个空格的宽度。如果这两
%   个空格的宽度小于当前 CJK 正常文字的宽度，将对 CJK 字体进行适当地缩小。这有利于
%   等宽字体的代码对齐等情形。需要注意的是，\cs{xeCJKVerbAddon} 对 \pkg{xeCJK} 的内
%   部进行了比较大的修改，使用它之后，将禁止在 CJK 字符类之间自动换行，这与西文在
%   抄录环境中的情况是一致的。所以不应该单独使用，应该放在分组里限制其作用域，否则
%   是无效的。当然它可以和其它关于代码抄录的宏包配合使用。例如，可以使用于
%   \package{fancyvrb} 宏包的 \texttt{formatcom} 选项。此时设置的西文字体应该确实
%   是等宽的以保证对齐。若西文等宽字体发生变动（包括字体大小），则需要在其后面使用
%   \cs{xeCJKVerbAddon}，重新计算间距的宽度。\cs{xeCJKOffVerbAddon}
%   用于在使用 \cs{xeCJKVerbAddon} 的环境中局部取消它的作用。由于 \package{listings}
%   宏包有自己的代码对齐机制，所以 \cs{xeCJKVerbAddon} 在由 \pkg{listings} 定义的
%   代码环境中无效。
% \end{function}
%
% \begin{function}[added=2012-12-03]{\xeCJKnobreak}
%   \begin{syntax}
%     ……汉字。\cs{xeCJKnobreak}\cs{footnote}\{脚注\}
%   \end{syntax}
%   \cs{xeCJKnobreak} 用在全角标点符号后面，目的是确保不能在此处断行。如果已经启用了
%   前面介绍的 |CheckFullRight| 选项，则不需要再用此命令。
% \end{function}
%
% \section{已知问题和兼容性}
%
% \XeTeX 在配置文件 \file{unicode-letters.tex} 中将所有 CJK 表意文字的 \cs{catcode}
% 设置为 $11$。因此汉字可以直接用作控制序列的名字，但是当汉字出现在控制序列后面的
% 时候，要用空格分隔开，否则就会出现“\texttt{! Undefined control sequence.}”的错误。
%
% \pkg{xeCJK} 使用并重新定义了 \pkg{CJK} 宏包的部分宏命令，如 \cs{CJKfamily}、
% \cs{CJKsymbol} 和 \cs{CJKglue} 等。需要指出，\pkg{xeCJK} 不需要 \pkg{CJK}
% 的支持，并且 \pkg{xeCJK} 自动禁止在它之后载入 \pkg{CJK} 宏包。
% 可以在 \pkg{xeCJK} 之\emph{后}载入 \pkg{CJKnumb} 宏包，实现数字的中文化。
%
% \CJKunderline{\pkg{xeCJK} 包含有一个子宏包 \pkg{xeCJKfntef}，可以用它来实现%
% \CJKunderdot{汉字加点}和可断行的下划线等。它是 \pkg{CJKfntef} 宏包在 \XeLaTeX
% 下的替换版本，基本用法完全一致，在 \pkg{CJKfntef} 的源文件\ \file{CJKfntef.sty}
% 的注解部分里有说明。}
%
% \pkg{xeCJK} 进行了一些处理，使得在使用 \XeTeX 时 \package{listings} 宏包可以
% 支持 Unicode，因此在 \texttt{listings} 定义的代码环境中可以直接使用中文，不再
% 需要通过 \texttt{escapechar}。
%
% 新版本（\texttt{3.x}）的 \pkg{xeCJK} 完全使用 \hologo{LaTeX3} 的语法来编写。
% \hologo{LaTeX3} 放弃了 \cs{outer} 宏的概念，因此相关工具在
% 遇到 \cs{outer} 宏时可能会存在问题。按照目前 \pkg{xeCJK} 的实现方式，在 CJK 文字
% 后面遇到 \cs{outer} 宏时会出现类似
% \begin{verbatim}
%   ! Forbidden control sequence found while scanning use of \use_i:nn
% \end{verbatim}
% 的错误。目前已知的有 \package{cprotect} 宏包提供的 \cs{cprotect}。它的定义是
% \begin{verbatim}
%   \outer\long\def\cprotect{\icprotect}
% \end{verbatim}
% 因此，这时可以暂时用 \cs{icprotect} 代替 \cs{cprotect}。事实上，当 \pkg{cprotect}
% 被引入时，\pkg{xeCJK} 将使用
% \begin{verbatim}
%   \let\cprotect\icprotect
% \end{verbatim}
% 来取消 \cs{cprotect} 的外部宏限制。但由于 \cs{cprotect} 的特殊性，应该只在外部
% 使用它，即不要让它出现在任何宏的参数中。其它 \cs{outer} 宏的情况，可以在它前面
% 加上 \cs{relax} 来回避上面的错误。
%
% \pkg{xeCJK} 依赖 \XeTeX 的 \cs{XeTeXinterchartoks} 机制，与使用相同机制的宏包（例如
% \pkg{polyglossia}）可能会存在大小不一的冲突。\pkg{xeCJK} 虽然为此作了一些处理，但与
% 它们共同使用时应该小心。
%
%\end{documentation}
%
%
% \StopEventually{}
%
%
%\begin{implementation}
%
% \section{\pkg{xeCJK} 代码实现}
%
%    \begin{macrocode}
%<*package>
%    \end{macrocode}
%
%    \begin{macrocode}
%<@@=xeCJK>
%    \end{macrocode}
%
% \subsection{运行环境检查}
%
% \pkg{xeCJK} 必须使用 \XeTeX 引擎的支持。
%    \begin{macrocode}
\msg_new:nnn { xeCJK } { Require-XeTeX }
  {
    The~xeCJK~package~requires~XeTeX~to~function.\\\\
    You~must~change~your~typesetting~engine~to~"xelatex" \\
    instead~of~plain~"latex"~or~"pdflatex"~or~"lualatex".\\
    Loading~xeCJK~will~abort!
  }
\xetex_if_engine:F { \msg_critical:nn { xeCJK } { Require-XeTeX } }
%    \end{macrocode}
%
% \begin{macro}[pTF,internal]{\xeCJK_if_package_loaded:n}
% 判断宏包是否被引入，可用于文档正文中。
%    \begin{macrocode}
\prg_new_conditional:Npnn \xeCJK_if_package_loaded:n #1 { p , T , F , TF }
  {
    \tl_if_exist:cTF { ver@ #1 . \c_@@_package_ext_tl }
      { \prg_return_true: } { \prg_return_false: }
  }
\tl_const:Nx \c_@@_package_ext_tl { \@pkgextension }
%    \end{macrocode}
% \end{macro}
%
% 下面这些 \pkg{CJK} 系列宏包不应该被使用。
%    \begin{macrocode}
\msg_new:nnn { xeCJK } { incompatible-package }
  {
    The~`#1'~package~and~xeCJK~are~incompatible.\\\\
    Please~do~not~use~it.
  }
\msg_new:nnn { xeCJK } { after-package }
  {
    The~`#1'~package~and~xeCJK~are~incompatible.\\\\
    Please~load~it~after~xeCJK.
  }
\clist_map_inline:nn { CJKfntef , CJKnumb }
  {
    \xeCJK_if_package_loaded:nT {#1}
      { \msg_error:nnn { xeCJK } { after-package } {#1} }
  }
\clist_map_inline:nn { CJKulem , CJKvert , CJKpunct , CJKutf8 , CJK }
  {
    \xeCJK_if_package_loaded:nTF {#1}
      { \msg_error:nnn { xeCJK } { incompatible-package } {#1} }
      { \tl_const:cn { ver@ #1 . \c_@@_package_ext_tl } { 9999/99/99 } }
  }
%    \end{macrocode}
%
% 应该使用较新版本的 \pkg{expl3} 宏包。
%    \begin{macrocode}
\msg_new:nnn { xeCJK } { l3-too-old }
  {
    Support~package~`#1'~too~old. \\\\
    Please~update~an~up~to~date~version~of~the~bundles\\\\
    `l3kernel'~and~`l3packages'\\\\
    using~your~TeX~package~manager~or~from~CTAN.\\
    \str_if_eq:nnT {#1} { expl3 } { Loading~xeCJK~will~abort! }
  }
\@ifpackagelater { expl3 } { 2012/11/10 } { }
  { \msg_critical:nnn { xeCJK } { l3-too-old } { expl3 } }
%    \end{macrocode}
%
% 以下日期以前的 \pkg{xtemplate} 宏包关于 \cs{KeyValue} 的 Bug 会影响到后面
% 标点符号的处理。
%    \begin{macrocode}
\RequirePackage { xtemplate }
\@ifpackagelater { xtemplate } { 2012/11/10 } { }
  { \msg_error:nnn { xeCJK } { l3-too-old } { xtemplate } }
%    \end{macrocode}
%
%    \begin{macrocode}
\RequirePackage { xparse , l3keys2e }
%    \end{macrocode}
%
% \subsection{内部工具}
%
% 分配临时变量。
%    \begin{macrocode}
\tl_new:N \l_@@_tmp_tl
\int_new:N \l_@@_tmp_int
\box_new:N \l_@@_tmp_box
\dim_new:N \l_@@_tmp_dim
\bool_new:N \l_@@_tmp_bool
\skip_new:N \l_@@_tmp_skip
\clist_new:N \l_@@_tmp_clist
%    \end{macrocode}
%
% \begin{macro}[internal]
%  {\@@_msg_new:nn ,\@@_error:n,\@@_error:nx,\@@_warning:nx,\@@_info:nxx}
% 各种信息函数的缩略形式。
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_msg_new:nn   { \msg_new:nnn       { xeCJK } }
\cs_new_protected_nopar:Npn \@@_error:n      { \msg_error:nn      { xeCJK } }
\cs_new_protected_nopar:Npn \@@_error:nx     { \msg_error:nnx     { xeCJK } }
\cs_new_protected_nopar:Npn \@@_warning:n    { \msg_warning:nn    { xeCJK } }
\cs_new_protected_nopar:Npn \@@_warning:nx   { \msg_warning:nnx   { xeCJK } }
\cs_new_protected_nopar:Npn \@@_warning:nxx  { \msg_warning:nnxx  { xeCJK } }
\cs_new_protected_nopar:Npn \@@_warning:nxxx { \msg_warning:nnxxx { xeCJK } }
\cs_new_protected_nopar:Npn \@@_info:nxx     { \msg_info:nnxx     { xeCJK } }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_allow_break:,\xeCJK_no_break:}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \xeCJK_allow_break: { \tex_penalty:D \c_zero }
\cs_new_protected_nopar:Npn \xeCJK_no_break: { \tex_penalty:D \c_ten_thousand }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]
%  {\@@_at_end_preamble:n,\@@_after_preamble:n,\@@_after_end_preamble:n}
%  在 \cs{document} 前后加上各种钩子。
%    \begin{macrocode}
\tl_new:N \g_@@_at_end_preamble_hook_tl
\tl_new:N \g_@@_after_preamble_hook_tl
\tl_new:N \g_@@_after_end_preamble_hook_tl
\cs_new_protected:Npn \@@_at_end_preamble:n #1
  { \tl_gput_right:Nn \g_@@_at_end_preamble_hook_tl {#1} }
\cs_new_protected:Npn \@@_after_preamble:n #1
  { \tl_gput_right:Nn \g_@@_after_preamble_hook_tl {#1} }
\cs_new_protected:Npn \@@_after_end_preamble:n #1
  { \tl_gput_right:Nn \g_@@_after_end_preamble_hook_tl {#1} }
\xeCJK_if_package_loaded:nTF { etoolbox }
  {
    \AtEndPreamble { \g_@@_at_end_preamble_hook_tl }
    \AfterPreamble { \g_@@_after_preamble_hook_tl }
    \AfterEndPreamble { \g_@@_after_end_preamble_hook_tl }
  }
  {
    \AtBeginDocument { \g_@@_after_preamble_hook_tl }
    \cs_new_protected_nopar:Npn \@@_document_left_hook:
      { \group_end: \g_@@_at_end_preamble_hook_tl \group_begin: }
    \cs_new_protected_nopar:Npn \@@_document_right_hook:
      { \scan_stop: \g_@@_after_end_preamble_hook_tl \tex_ignorespaces:D }
    \cs_gset_nopar:Npx \document
      {
        \@@_document_left_hook:
        \exp_not:o { \document }
        \@@_document_right_hook:
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_reverse:nnn}
% |#1| 为 |#2| 或 |#3|，若 |#1| 和 |#2| 相等，则返回 |#3|，否则返回 |#2|。
%    \begin{macrocode}
\cs_new_nopar:Npn \xeCJK_reverse:nnn #1#2#3
  { \str_if_eq_x:nnTF {#1} {#2} {#3} {#2} }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]
% {\xeCJK_tl_remove_outer_braces:N,\xeCJK_tl_remove_outer_braces:n}
% \changes{v3.2.4}{2013/07/02}{去掉外层分组括号时，移除空格，避免死循环。}
% 去掉 |#1| 外层的分组括号。
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \xeCJK_tl_remove_outer_braces:N #1
  { \tl_set:Nx #1 { \exp_args:NV \xeCJK_tl_remove_outer_braces:n #1 } }
\cs_new:Npn \xeCJK_tl_remove_outer_braces:n #1
  {
    \exp_last_unbraced:Nf
    \@@_tl_remove_outer_braces:w { \tl_trim_spaces:n {#1} } \s__stop
  }
\cs_new:Npn \@@_tl_remove_outer_braces:w #1 \s__stop
  {
    \bool_if:nTF { \tl_if_single_p:n {#1} && ! ( \tl_if_head_is_N_type_p:n {#1} ) }
      { \xeCJK_tl_remove_outer_braces:n {#1} }
      { \tl_trim_spaces:n {#1} }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_cs_clear:N,\xeCJK_cs_gclear:N}
% 让控制序列的意义为空。
%    \begin{macrocode}
\cs_new_protected:Npn \xeCJK_cs_clear:N #1
  { \cs_set_eq:NN #1 \prg_do_nothing: }
\cs_new_protected:Npn \xeCJK_cs_gclear:N #1
  { \cs_gset_eq:NN #1 \prg_do_nothing: }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_swap_cs:NN}
% 交换 |#1| 和 |#2| 的意义。
%    \begin{macrocode}
\cs_new_protected:Npn \xeCJK_swap_cs:NN #1#2
  {
    \cs_set_eq:NN \@@_swap_cs_aux:w #1
    \cs_set_eq:NN #1 #2
    \cs_set_eq:NN #2 \@@_swap_cs_aux:w
    \cs_undefine:N \@@_swap_cs_aux:w
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_font_gset_to_current:c}
% |#1| 是控制序列的名字，令它等于当前字体命令。
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \xeCJK_font_gset_to_current:c #1
  {
    \exp_after:wN \cs_gset_eq:NN
    \cs:w #1 \exp_after:wN \cs_end: \tex_the:D \tex_font:D
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal,pTF]{\xeCJK_glyph_if_exist:N}
% \changes{v3.1.0}{2012/11/19}{改进 \pkg{fontspec} 宏包中定义的
% \cs{font_glyph_if_exist:NnTF}。}
% 判断当前字体中是否含有字符 |#1|。\pkg{fontspec} 中的类似函数在判断为真的时候，
% 会留有一个 \cs{scan_stop:}，造成不必要的边界，同时也不完全可展。因此，我们重新
% 定义它。
%    \begin{macrocode}
\prg_new_conditional:Npnn \xeCJK_glyph_if_exist:N #1 { p , T , F , TF }
  {
    \etex_iffontchar:D \tex_font:D `#1 \exp_stop_f:
      \prg_return_true: \else: \prg_return_false: \fi:
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal,var]{\c_xeCJK_space_skip_tl}
% \changes{v3.1.0}{2012/11/18}{字间空格考虑 \cs{spaceskip} 不为零的情况。}
% \changes{v3.2.0}{2013/05/22}{字间空格考虑到 \cs{spacefactor} 和 \cs{xspaceskip} 的情况。}
% 当前字体状态下，一个字间空格产生的 |glue| 的长度，包括伸展和收缩部分。
%    \begin{macrocode}
\tl_const:Nn \c_xeCJK_space_skip_tl
  {
    \int_compare:nNnTF \g_@@_spacefactor_int = \c_one_thousand
      {
        \skip_if_eq:nnTF \tex_spaceskip:D \c_zero_skip
          {
            \tex_fontdimen:D \c_two \tex_font:D
              plus  \tex_fontdimen:D \c_three \tex_font:D
              minus \tex_fontdimen:D \c_four  \tex_font:D
          }
          { \tex_spaceskip:D }
      }
      {
        \skip_if_eq:nnTF \tex_spaceskip:D \c_zero_skip
          {
            \int_compare:nNnTF \g_@@_spacefactor_int < { 2000 }
              { \_@@_space_skip_scale:nnn { \tex_fontdimen:D \c_two \tex_font:D } }
              {
                \skip_if_eq:nnTF \tex_xspaceskip:D \c_zero_skip
                  {
                    \_@@_space_skip_scale:nnn
                      {
                        \tex_fontdimen:D \c_two   \tex_font:D +
                        \tex_fontdimen:D \c_seven \tex_font:D
                      }
                  }
                  { \tex_xspaceskip:D  \use_none:nn }
              }
              { \tex_fontdimen:D \c_three \tex_font:D }
              { \tex_fontdimen:D \c_four  \tex_font:D }
          }
          {
            \int_compare:nNnTF \g_@@_spacefactor_int < { 2000 }
              { \_@@_space_skip_scale:nnn { \tex_spaceskip:D } }
              {
                \skip_if_eq:nnTF \tex_xspaceskip:D \c_zero_skip
                  {
                    \_@@_space_skip_scale:nnn
                      { \tex_spaceskip:D + \tex_fontdimen:D \c_seven \tex_font:D }
                  }
                  { \tex_xspaceskip:D \use_none:nn }
              }
              { \etex_gluestretch:D \tex_spaceskip:D }
              { \etex_glueshrink:D  \tex_spaceskip:D }
          }
      }
  }
\cs_new_nopar:Npn \_@@_space_skip_scale:nnn #1#2#3
  {
    \dim_eval:n {#1}
    plus \fp_eval:n { \g_@@_spacefactor_int / 1000 } #2
    minus
      \int_div_truncate:nn
        { 1000 * \tex_number:D #3 } { \g_@@_spacefactor_int } sp
  }
\int_new:N \g_@@_spacefactor_int
\int_gset_eq:NN \g_@@_spacefactor_int \c_one_thousand
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_glue_to_skip:nN}
% 取得一个 |glue| 的长度，包括伸展和收缩部分。
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \xeCJK_glue_to_skip:nN #1#2
  {
    \hbox_set:Nn \l_@@_tmp_box { #1 \scan_stop: \exp_after:wN } \exp_after:wN
    \skip_set:Nn \exp_after:wN #2 \exp_after:wN { \skip_use:N \tex_lastskip:D }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[pTF,internal]{\xeCJK_if_blank_x:n}
% 判断是否为空或者仅含一个空格。
%    \begin{macrocode}
\prg_new_conditional:Npnn \xeCJK_if_blank_x:n #1 { p , T , F , TF }
  {
    \if_case:w \pdftex_strcmp:D { } {#1} \exp_stop_f:
      \prg_return_true:
    \else:
      \if_case:w \pdftex_strcmp:D { ~ } {#1} \exp_stop_f:
        \prg_return_true: \else: \prg_return_false: \fi:
    \fi:
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_int_until_do:nn,\@@_int_until_do:wn}
% 由于定义较为简单，可以比 \cs{int_until_do:nNnn} 稍微快一点点。
%    \begin{macrocode}
\cs_new_protected:Npn \xeCJK_int_until_do:nn #1#2
  { \@@_int_until_do:wn \use_none:n { \reverse_if:N \if_int_compare:w #1#2 } }
\cs_new_protected:Npn \@@_int_until_do:wn \use_none:n #1
  { #1 \exp_after:wN \@@_int_until_do:wn \fi: \use_none:n {#1} }
\int_new:N \l_@@_begin_int
\int_new:N \l_@@_end_int
%    \end{macrocode}
%
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_peek_catcode_ignore_spaces:NTF}
% \changes{v3.1.1}{2012/12/04}{新增有省略空格标识的 \texttt{peek} 函数。}
% 我们在里面设置了一个变量 \cs{l_@@_peek_ignore_spaces_bool} 用于标识后面的空格
% 是否被省略掉了。
%    \begin{macrocode}
\cs_new_protected:Npn \xeCJK_peek_catcode_ignore_spaces:NTF #1#2#3
  {
    \cs_set_eq:NN \l__peek_search_token #1 \scan_stop:
    \tl_set:Nn \@@_peek_catcode_true:w  { \group_align_safe_end: #2 }
    \tl_set:Nn \@@_peek_catcode_false:w { \group_align_safe_end: #3 }
    \bool_set_false:N \l_@@_peek_ignore_spaces_bool
    \group_align_safe_begin:
    \peek_after:Nw \@@_peek_catcode_ignore_spaces_branches:w
  }
\cs_new_protected_nopar:Npn \@@_peek_catcode_ignore_spaces_branches:w
  {
    \if_meaning:w \l_peek_token \c_space_token
      \bool_set_true:N \l_@@_peek_ignore_spaces_bool
      \exp_after:wN \peek_after:Nw
      \exp_after:wN \@@_peek_catcode_ignore_spaces_branches:w
      \tex_romannumeral:D 0
    \else:
      \if_catcode:w
        \exp_not:N \l_peek_token \exp_not:N \l__peek_search_token
        \exp_after:wN \exp_after:wN
        \exp_after:wN \@@_peek_catcode_true:w
      \else:
        \exp_after:wN \exp_after:wN
        \exp_after:wN \@@_peek_catcode_false:w
      \fi:
    \fi:
  }
\tl_new:N \@@_peek_catcode_true:w
\tl_new:N \@@_peek_catcode_false:w
\bool_new:N \l_@@_peek_ignore_spaces_bool
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_peek_after_ignore_spaces:nw}
% 与 \cs{@ifnextchar} 和 \cs{futurenonspacelet} 类似，会省略掉后面的空格。
%    \begin{macrocode}
\cs_new_protected:Npn \xeCJK_peek_after_ignore_spaces:nw #1
  {
    \tl_set:Nn \@@_peek_after_do:w { \group_align_safe_end: #1 }
    \group_align_safe_begin:
    \peek_after:Nw \@@_peek_ignore_spaces_branches:w
  }
\cs_new_protected_nopar:Npn \@@_peek_ignore_spaces_branches:w
  {
    \if_meaning:w \l_peek_token \c_space_token
      \exp_after:wN \peek_after:Nw
      \exp_after:wN \@@_peek_ignore_spaces_branches:w
      \tex_romannumeral:D 0
    \else:
      \exp_after:wN \@@_peek_after_do:w
    \fi:
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_token_value_class:N}
% 用于取得记号 |#1| 所在的 \XeTeX 字符类。|#1| 应为 \cs{catcode} 为 |11| 或 |12|
% 的显性或隐性记号。
%    \begin{macrocode}
\cs_new_nopar:Npn \xeCJK_token_value_class:N #1
  { \XeTeXcharclass \xeCJK_token_value_charcode:N #1 }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_token_value_charcode:N}
% \changes{v3.2.4}{2013/06/30}{考虑 \texttt{charcode} 超出 BMP 的情况。}
% 当记号 |#1| 的 \texttt{charcode} 大于等于 \texttt{0x10000} 时，\XeTeX\
% \texttt{0.9999.0} 版以前的 \cs{meaning} 的返回结果比较特殊\footnote{参见
% \url{http://tug.org/pipermail/xetex/2013-January/023967.html} 和
% \url{http://tex.stackexchange.com/a/64848}。}，需要特别处理。同时使用较新版本中
% 提供的原语设置 \texttt{mathcode}。目前，\texttt{0.9999.0} 版以后的 \XeTeX 的
% \cs{meaning} 对于超出 BMP 的字符，会返回两个字符，分别对应于其 UTF-16 编码的
% 首尾代理。\footnote{参见 \url{http://tug.org/pipermail/xetex/2013-June/024543.html}。}
%    \begin{macrocode}
\cs_new_nopar:Npn \xeCJK_token_value_charcode:N #1
  { \exp_after:wN \@@_token_value_charcode:w \token_to_meaning:N #1 \q_stop }
\fp_compare:nNnTF { \int_use:N \xetex_XeTeXversion:D \XeTeXrevision } > { 0.9998 }
  {
    \cs_new_nopar:Npn \@@_token_value_charcode:w #1 ~ #2 ~ #3#4 \q_stop
      {
        \int_eval:n
          {
            \tl_if_empty:nTF {#4}
              { `#3 }
              { ( `#3 - "D800 ) * "400 + ( `#4 - "DC00 ) + "10000 }
          }
      }
    \cs_new_eq:NN \xeCJK_xetex_mathcode:w \Umathcode
  }
  {
    \cs_new_nopar:Npn \@@_token_value_charcode:w #1 ~ #2 ~ #3#4 \q_stop
      { \int_eval:n { \tl_if_empty:nTF {#4} { `#3 } { "20000 } } }
    \cs_new_eq:NN \xeCJK_xetex_mathcode:w \XeTeXmathcode
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[pTF,internal]{\xeCJK_if_CJK_class:N}
% 判断字符 |#1| 是否为 CJK 字符类，包括文字和标点符号。
%    \begin{macrocode}
\prg_new_conditional:Npnn \xeCJK_if_CJK_class:N #1 { p , T , F , TF }
  {
    \if_cs_exist:w \@@_CJK_class_tl:n { \xeCJK_token_value_class:N #1 } \cs_end:
      \prg_return_true: \else: \prg_return_false: \fi:
  }
\cs_new_nopar:Npn \@@_CJK_class_tl:n #1
  { c_@@_CJK_class_ \int_eval:n {#1} _tl }
\cs_generate_variant:Nn \@@_CJK_class_tl:n { c }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[pTF,internal]{\xeCJK_if_same_class:NN}
% 判断两个字符是否同属于一个字符类。
%    \begin{macrocode}
\prg_new_conditional:Npnn \xeCJK_if_same_class:NN #1#2 { p , T , F , TF }
  {
    \if_int_compare:w
      \xeCJK_token_value_class:N #1 = \xeCJK_token_value_class:N #2 \exp_stop_f:
      \prg_return_true: \else: \prg_return_false: \fi:
  }
%    \end{macrocode}
% \end{macro}
%
% \subsection{功能开关}
%
% \begin{macro}{xeCJKactive}
% 事实上，将开启或关闭 \XeTeX 的整个字符类机制。
%    \begin{macrocode}
\keys_define:nn { xeCJK / options }
  {
    xeCJKactive .choice: ,
    xeCJKactive / true  .code:n = { \makexeCJKactive   } ,
    xeCJKactive / false .code:n = { \makexeCJKinactive } ,
    xeCJKactive      .default:n = { true }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\makexeCJKactive, \makexeCJKinactive}
%    \begin{macrocode}
\NewDocumentCommand \makexeCJKactive   { } { \XeTeXinterchartokenstate = \c_one  }
\NewDocumentCommand \makexeCJKinactive { } { \XeTeXinterchartokenstate = \c_zero }
%    \end{macrocode}
% \end{macro}
%
% 抑制 |BOM|。
%    \begin{macrocode}
\char_set_catcode_ignore:n { "FEFF }
%    \end{macrocode}
%
% \subsection{字符类别设定}\label{sec:xeCJK-class-set}
%
% \begin{macro}[internal,var]{\g_@@_class_seq,\g_@@_new_class_seq}
% 分别用于记录在 \pkg{xeCJK} 中使用的字符类别名称和新建的字符类别的编号。
%    \begin{macrocode}
\seq_new:N \g_@@_class_seq
\seq_new:N \g_@@_new_class_seq
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_new_class:n}
% 新建一个字符类别。|#1| 是自定义名称。
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \xeCJK_new_class:n #1
  {
    \int_if_exist:cTF { \@@_class_csname:n {#1} }
      { \@@_error:nx { class-already-defined } {#1} }
      {
        \exp_args:Nc \newXeTeXintercharclass { \@@_class_csname:n {#1} }
        \clist_new:c { g_@@_#1_range_clist }
        \seq_gput_right:Nn \g_@@_class_seq {#1}
        \seq_gput_right:Nv \g_@@_new_class_seq { \@@_class_csname:n {#1} }
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_save_class:nn}
% \changes{v3.1.1}{2012/12/06}
% {使用 \cs{xeCJK_save_class:nn} 保存 \XeTeX 预定义的字符类别。}
% 保存 \XeTeX 预定义的字符类别。|#1| 是自定义名称，|#2| 是编号。
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \xeCJK_save_class:nn #1#2
  {
    \int_if_exist:cTF { \@@_class_csname:n {#1} }
      { \@@_error:nx { class-already-defined } {#1} }
      {
        \int_const:cn { \@@_class_csname:n {#1} } {#2}
        \clist_new:c { g_@@_#1_range_clist }
        \seq_gput_right:Nn \g_@@_class_seq {#1}
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_class_csname:n}
% 字符类名称对应的控制序列名字。
%    \begin{macrocode}
\cs_new_nopar:Npn \@@_class_csname:n #1 { c_@@_#1_class_int }
\cs_new_eq:cN { \@@_class_csname:n { Others } } \l_@@_tmp_int
\@@_msg_new:nn { class-already-defined }
  {
    XeTeX~character~class~`#1'~has~been~already~defined.\\\\
    Please~take~another~name. \\
  }
%    \end{macrocode}
% \end{macro}
%
% \changes{v3.2.0}{2013/05/20}{增加 \texttt{IVS} 字符类用于处理异体字选择符。}
%
% \pkg{xeCJK} 需要以下字符类别用于字符输出。其中 |Default|、|CJK|、|FullLeft|、
% |FullRight|、|Boundary| 为 \XeTeX\ 中预定义的类别，\pkg{xeCJK} 新增加了\
% |HalfLeft|、|HalfRight|、|NormalSpace| 和 |IVS|。其中异体字选择符
% (Ideographic Variation Selectors)\footnote{\url{http://www.unicode.org/reports/tr37/}}
% 需要 \XeTeX\ |0.9999.0| 以上的版本^^A
% \footnote{\url{http://tug.org/pipermail/xetex/2013-March/024118.html}}和相关字体的支持。
% \begin{center}\xeCJKsetup{PunctStyle=plain}
% \begin{tabular}{cll}
% \toprule
%   类别        & 说明               & 例子 \\ \midrule
% |Default|     & 西文一般符号       & abc123 \\
% |CJK|         & CJK 表意符号       & 汉字ぁぃぅ \\
% |FullLeft|    & 全角左标点         & （《：“ \\
% |FullRight|   & 全角右标点         & ，。）》” \\
% |HalfLeft|    & 半角左标点         & ( [ \{ \\
% |HalfRight|   & 半角右标点         & , . ? ) ] \} \\
% |NormalSpace| & 前后原始间距的符号 & / \\
% |Boundary|    & 边界               & 空格 \\
% |IVS|         & 异体字选择符       & “回字有四样写法”\\
% \bottomrule
% \end{tabular}
% \end{center}
%
% \begin{macro}[internal]{Default,CJK,FullLeft,FullRight,Boundary}
% 这五类是 \XeTeX\ 预定义的类别。
%    \begin{macrocode}
\xeCJK_save_class:nn { Default }   { \c_zero  }
\xeCJK_save_class:nn { CJK }       { \c_one   }
\xeCJK_save_class:nn { FullLeft }  { \c_two   }
\xeCJK_save_class:nn { FullRight } { \c_three }
\xeCJK_save_class:nn { Boundary }  { \c_two_hundred_fifty_five }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{HalfLeft,HalfRight,NormalSpace,IVS}
% 新增西文半角左/右标点、前后原始间距的符号和异体字选择符类。
%    \begin{macrocode}
\xeCJK_new_class:n { HalfLeft }
\xeCJK_new_class:n { HalfRight }
\xeCJK_new_class:n { NormalSpace }
\xeCJK_new_class:n { IVS }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[var,internal]
%  {\c_@@_HalfLeft_chars_clist,\c_@@_HalfRight_chars_clist,\c_@@_NormalSpace_chars_clist}
% \hypertarget{CJKcharclass}{西文半角左/右标点和前后原始间距的字符类。}
%    \begin{macrocode}
\clist_const:Nn \c_@@_HalfLeft_chars_clist
  { "28 , "2D , "5B , "60 , "7B }
\clist_const:Nn \c_@@_HalfRight_chars_clist
  { "21 , "22 , "25 , "27 , "29 , "2C , "2E , "3A , "3B , "3F , "5D , "7D }
\clist_const:Nn \c_@@_NormalSpace_chars_clist { "2F }
%    \end{macrocode}
% \end{macro}
%
% 以下对全角标点符号的归类来源于 \XeTeX 的脚本
% \href{http://sourceforge.net/p/xetex/code/ci/master/tree/source/texk/web2c/xetexdir/unicode-char-prep.pl}
% {\file{unicode-char-prep.pl}} 和 Unicode 数据库\footnote{\url{http://www.unicode.org/reports/tr14/}}。
%
% \changes{v3.2.3}{2013/06/09}{根据 \XeTeX 的脚本重新整理全角标点符号。}
%
% \begin{macro}[var,internal]{\c_@@_OP_chars_clist}
% Open Punctuation (OP)
% \PrintPunctList{OP}{Open Punctuation}
% 以下代码的第一行是中西文共用的左引号。
%    \begin{macrocode}
\clist_const:Nn \c_@@_OP_chars_clist
  {
    "2018 , "201C ,
    "2329 , "3008 , "300A , "300C , "300E , "3010 , "3014 , "3016 , "3018 , "301A ,
    "301D , "FE17 , "FE35 , "FE37 , "FE39 , "FE3B , "FE3D , "FE3F , "FE41 , "FE43 ,
    "FE47 , "FE59 , "FE5B , "FE5D , "FF08 , "FF3B , "FF5B , "FF5F , "FF62
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[var,internal]{\c_@@_PR_chars_clist}
% Prefix Numeric (PR)
% \PrintPunctList{PR}{Prefix Numeric}
%    \begin{macrocode}
\clist_const:Nn \c_@@_PR_chars_clist
  { "20A9 , "FE69 , "FF04 , "FFE1 , "FFE5 , "FFE6 }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[var,internal]{\c_@@_FullLeft_chars_clist}
% 以上两类标点符号出现在文字的左边，不应出现在行尾位置。
%    \begin{macrocode}
\clist_const:Nx \c_@@_FullLeft_chars_clist
  {
    \c_@@_OP_chars_clist ,
    \c_@@_PR_chars_clist
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[var,internal]{\c_@@_CL_chars_clist}
% Close Punctuation (CL)
% \PrintPunctList{CL}{Close Punctuation}
% 以下代码的第一行是中西文共用的一些标点符号。
%    \begin{macrocode}
\clist_const:Nn \c_@@_CL_chars_clist
  {
    "00B7 , "2019 , "201D , "2014 , "2015 , "2025 , "2026 , "2027 , "2500 ,
    "232A , "3001 , "3002 , "3009 , "300B , "300D , "300F , "3011 , "3015 , "3017 ,
    "3019 , "301B , "301E , "301F , "FE11 , "FE12 , "FE18 , "FE36 , "FE38 , "FE3A ,
    "FE3C , "FE3E , "FE40 , "FE42 , "FE44 , "FE48 , "FE50 , "FE52 , "FE5A , "FE5C ,
    "FE5E , "FF09 , "FF0C , "FF0E , "FF3D , "FF5D , "FF60 , "FF61 , "FF63 , "FF64
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[var,internal]{\c_@@_NS_chars_clist}
% Nonstarter (NS)
% \PrintPunctList{NS}{Nonstarter}
%    \begin{macrocode}
\clist_const:Nn \c_@@_NS_chars_clist
  {
    "3005 , "301C , "303B , "303C , "309B , "309C , "309D , "309E , "30A0 , "30FB ,
    "30FD , "30FE , "A015 , "FE54 , "FE55 , "FF1A , "FF1B , "FF65 , "FF9E , "FF9F
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[var,internal]{\c_@@_EX_chars_clist}
% Exclamation/Interrogation (EX)
% \PrintPunctList{EX}{Exclamation/Interrogation}
%    \begin{macrocode}
\clist_const:Nn \c_@@_EX_chars_clist
  { "FE15 , "FE16 , "FE56 , "FE57 , "FF01 , "FF1F }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[var,internal]{\c_@@_IS_chars_clist}
% Infix Numeric Separator (IS)
% \PrintPunctList{IS}{Infix Numeric Separator}
%    \begin{macrocode}
\clist_const:Nn \c_@@_IS_chars_clist { "FE10 , "FE13 , "FE14 }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[var,internal]{\c_@@_CJ_chars_clist}
% Conditional Japanese Starter (CJ)
% \PrintPunctList{CJ}{Conditional Japanese Starter}
%    \begin{macrocode}
\clist_const:Nn \c_@@_CJ_chars_clist
  {
    "3041 , "3043 , "3045 , "3047 , "3049 , "3063 , "3083 , "3085 , "3087 , "308E ,
    "3095 , "3096 , "30A1 , "30A3 , "30A5 , "30A7 , "30A9 , "30C3 , "30E3 , "30E5 ,
    "30E7 , "30EE , "30F5 , "30F6 , "30FC , "31F0 , "31F1 , "31F2 , "31F3 , "31F4 ,
    "31F5 , "31F6 , "31F7 , "31F8 , "31F9 , "31FA , "31FB , "31FC , "31FD , "31FE ,
    "31FF , "FF67 , "FF68 , "FF69 , "FF6A , "FF6B , "FF6C , "FF6D , "FF6E , "FF6F ,
    "FF70
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[var,internal]{\c_@@_PO_chars_clist}
% Postfix Numeric (PO)
% \PrintPunctList{PO}{Postfix Numeric}
%    \begin{macrocode}
\clist_const:Nn \c_@@_PO_chars_clist { "FE6A , "FF05 , "FFE0 }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[var,internal]{\c_@@_FullRight_chars_clist}
% 以上六类标点符号出现在文字的右边，不应出现在行首位置。
%    \begin{macrocode}
\clist_const:Nx \c_@@_FullRight_chars_clist
  {
    \c_@@_CL_chars_clist ,
    \c_@@_NS_chars_clist ,
    \c_@@_EX_chars_clist ,
    \c_@@_IS_chars_clist ,
    \c_@@_CJ_chars_clist ,
    \c_@@_PO_chars_clist
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[var,internal]{\c_@@_CJK_chars_clist}
% CJK 字符类，包括文字和标点符号。
%    \begin{macrocode}
\clist_const:Nn \c_@@_CJK_chars_clist
  {
%    \end{macrocode}
% \begin{itemize}[nosep,leftmargin=0pt]
% \item Hangul Jamo （谚文字母）
%    \begin{macrocode}
    "1100 -> "11FF ,
%    \end{macrocode}
% \item CJK Radicals Supplement （中日韩部首补充）
%    \begin{macrocode}
    "2E80 -> "2EFF ,
%    \end{macrocode}
% \item Kangxi Radicals （康熙部首）
%    \begin{macrocode}
    "2F00 -> "2FDF ,
%    \end{macrocode}
% \item Ideographic Description Characters （表意文字描述符）
%    \begin{macrocode}
    "2FF0 -> "2FFF ,
%    \end{macrocode}
% \item CJK Symbols and Punctuation （中日韩符号和标点）
%    \begin{macrocode}
    "3000 -> "303F ,
%    \end{macrocode}
% \item Hiragana （日文平假名）
%    \begin{macrocode}
    "3040 -> "309F ,
%    \end{macrocode}
% \item Katakana （日文片假名）
%    \begin{macrocode}
    "30A0 -> "30FF ,
%    \end{macrocode}
% \item Bopomofo （注音字母）
%    \begin{macrocode}
    "3100 -> "312F ,
%    \end{macrocode}
% \item Hangul Compatibility Jamo （谚文兼容字母）
%    \begin{macrocode}
    "3130 -> "318F ,
%    \end{macrocode}
% \item Kanbun （象形字注释标志）
%    \begin{macrocode}
    "3190 -> "319F ,
%    \end{macrocode}
% \item Bopomofo Extended （注音字母扩展）
%    \begin{macrocode}
    "31A0 -> "31BF ,
%    \end{macrocode}
% \item CJK Strokes （中日韩笔画）
%    \begin{macrocode}
    "31C0 -> "31EF ,
%    \end{macrocode}
% \item Katakana Phonetic Extensions （日文片假名语音扩展）
%    \begin{macrocode}
    "31F0 -> "31FF ,
%    \end{macrocode}
% \item Enclosed CJK Letters and Months （带圈中日韩字母和月份）
%    \begin{macrocode}
    "3200 -> "32FF ,
%    \end{macrocode}
% \item CJK Compatibility （中日韩兼容）
%    \begin{macrocode}
    "3300 -> "33FF ,
%    \end{macrocode}
% \item CJK Unified Ideographs Extension-A （中日韩统一表意文字扩展 A）
%    \begin{macrocode}
    "3400 -> "4DBF ,
%    \end{macrocode}
% \item Yijing Hexagrams Symbols （易经六十四卦符号）
%    \begin{macrocode}
    "4DC0 -> "4DFF ,
%    \end{macrocode}
% \item CJK Unified Ideographs （中日韩统一表意文字）
%    \begin{macrocode}
    "4E00 -> "9FFF ,
%    \end{macrocode}
% \item Yi Syllables （彝文音节）
%    \begin{macrocode}
    "A000 -> "A48F ,
%    \end{macrocode}
% \item Yi Radicals （彝文字根）
%    \begin{macrocode}
    "A490 -> "A4CF ,
%    \end{macrocode}
% \item Hangul Jamo Extended-A （谚文扩展 A）
%    \begin{macrocode}
    "A960 -> "A97F ,
%    \end{macrocode}
% \item Hangul Syllables （谚文音节）
%    \begin{macrocode}
    "AC00 -> "D7AF ,
%    \end{macrocode}
% \item Hangul Jamo Extended-B （谚文扩展 B）
%    \begin{macrocode}
    "D7B0 -> "D7FF ,
%    \end{macrocode}
% \item CJK Compatibility Ideographs （中日韩兼容表意文字）
%    \begin{macrocode}
    "F900 -> "FAFF ,
%    \end{macrocode}
% \item Vertical Forms （竖排形式）
%    \begin{macrocode}
    "FE10 -> "FE1F ,
%    \end{macrocode}
% \item CJK Compatibility Forms （中日韩兼容形式）
%    \begin{macrocode}
    "FE30 -> "FE4F ,
%    \end{macrocode}
% \item Halfwidth and Fullwidth Forms （半角及全角形式）
%    \begin{macrocode}
    "FF00 -> "FFEF ,
%    \end{macrocode}
% \item Kana Supplement （日文假名增补）
%    \begin{macrocode}
    "1B000 -> "1B0FF ,
%    \end{macrocode}
% \item Enclosed Ideographic Supplement （带圈表意文字增补）
%    \begin{macrocode}
    "1F200 -> "1F2FF ,
%    \end{macrocode}
% \item CJK Unified Ideographs Extension-B （中日韩统一表意文字扩展 B）
%    \begin{macrocode}
    "20000 -> "2A6DF ,
%    \end{macrocode}
% \item CJK Unified Ideographs Extension-C （中日韩统一表意文字扩展 C）
%    \begin{macrocode}
    "2A700 -> "2B73F ,
%    \end{macrocode}
% \item CJK Unified Ideographs Extension-D （中日韩统一表意文字扩展 D）
%    \begin{macrocode}
    "2B740 -> "2B81F ,
%    \end{macrocode}
% \item CJK Compatibility Ideographs Supplement （中日韩兼容表意文字增补）
%    \begin{macrocode}
    "2F800 -> "2FA1F
%    \end{macrocode}
% \end{itemize}
%    \begin{macrocode}
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[var,internal]{\c_@@_IVS_chars_clist}
% 包括日文假名浊点和异体字选择符。
%    \begin{macrocode}
\clist_const:Nn \c_@@_IVS_chars_clist
  {
%    \end{macrocode}
% \begin{itemize}[nosep,leftmargin=0pt]
% \item 日文假名浊点
%    \begin{macrocode}
    "3099 -> "309A ,
%    \end{macrocode}
% \item Variation Selectors （异体字选择符）
%    \begin{macrocode}
    "FE00 -> "FE0F ,
%    \end{macrocode}
% \item Variation Selectors Supplement （异体字选择符增补）
%    \begin{macrocode}
    "E0100 -> "E01EF
%    \end{macrocode}
% \end{itemize}
%    \begin{macrocode}
  }
%    \end{macrocode}
% \end{macro}
%
% \subsection{字符类别处理}
%
% \begin{macro}[internal,var]
%  {\g_@@_base_class_seq,\g_@@_non_CJK_class_seq,\g_@@_CJK_class_seq}
%    \begin{macrocode}
\seq_new:N \g_@@_base_class_seq
\seq_gset_eq:NN \g_@@_base_class_seq \g_@@_class_seq
\seq_new:N \g_@@_non_CJK_class_seq
\seq_gset_from_clist:Nn \g_@@_non_CJK_class_seq
  { Default , HalfLeft , HalfRight , NormalSpace , Boundary }
\seq_new:N \g_@@_CJK_class_seq
\cs_new_protected_nopar:Npn \@@_save_CJK_class:n #1
  {
    \seq_gput_right:Nn \g_@@_CJK_class_seq {#1}
    \tl_const:cn { \@@_CJK_class_tl:c { \@@_class_csname:n {#1} } } {#1}
  }
\clist_map_function:nN { CJK , FullLeft , FullRight , IVS } \@@_save_CJK_class:n
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_class_num:n}
% |#1| 为字符类别名称，用于取得字符类别对应的编号。
%    \begin{macrocode}
\cs_new_nopar:Npn \xeCJK_class_num:n #1 { \use:c { \@@_class_csname:n {#1} } }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\xeCJKDeclareCharClass}
%    \begin{macrocode}
\NewDocumentCommand \xeCJKDeclareCharClass { s > { \TrimSpaces } m m }
  {
    \xeCJK_declare_char_class:nx {#2} {#3}
    \IfBooleanT {#1} { \xeCJKResetPunctClass }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_declare_char_class:nn,\@@_set_char_class_aux:Nnw}
% 用于设置字符所属的类别，|#1| 为类别名称，|#2| 为字符的 |Unicode|，相邻字符用
% 半角逗号隔开，支持类似 |"1100 -> "11FF| 起止范围的使用方式。
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \xeCJK_declare_char_class:nn #1#2
  {
    \clist_set:Nn \l_@@_tmp_clist {#2}
    \clist_gconcat:ccN
      { g_@@_#1_range_clist } { g_@@_#1_range_clist } \l_@@_tmp_clist
    \clist_map_inline:Nn \l_@@_tmp_clist
      {
        \str_if_eq:nnF {##1} { -> }
          {
            \@@_set_char_class_aux:Nnw \xeCJK_set_char_class:nnn {##1}
              { \xeCJK_class_num:n {#1} }
          }
      }
    \xeCJK_set_char_class:nnn { "3099 } { "309A } { \xeCJK_class_num:n { IVS } }
  }
\NewDocumentCommand \@@_set_char_class_aux:Nnw
  { m > { \SplitArgument { 1 } { -> } } m } { #1 #2 }
\cs_generate_variant:Nn \clist_gconcat:NNN { cc }
\cs_generate_variant:Nn \xeCJK_declare_char_class:nn { nx , nV }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_check_num_range:nnNN}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_check_num_range:nnNN #1#2#3#4
  {
    \bool_if:nTF { \xeCJK_if_blank_x_p:n {#1} || \xeCJK_if_blank_x_p:n {#2} }
      {
        \int_set:Nn #3 { \xeCJK_if_blank_x:nTF {#1} {#2} {#1} }
        \int_set_eq:NN #3 #4
      }
      {
        \int_set:Nn #3 { \int_min:nn {#1} { \IfNoValueTF {#2} {#1} {#2} } }
        \int_set:Nn #4 { \int_max:nn {#1} { \IfNoValueTF {#2} {#1} {#2} } }
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \changes{v3.2.3}{2013/06/08}{不再改变 CJK 字符类的 \cs{catcode}。}
%
% \begin{macro}[internal]{\xeCJK_set_char_class:nnn}
% \changes{v3.1.1}{2012/12/05}{在文档中设置字符类别时不重复设置 \cs{catcode}。}
% 设置字符类别，|#1| 和 |#2| 为字符类别起止的 |Unicode|，|#3| 为类别名称对应编号。
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \xeCJK_set_char_class:nnn #1#2#3
  {
    \@@_check_num_range:nnNN {#1} {#2} \l_@@_begin_int \l_@@_end_int
    \int_set:Nn \l_@@_tmp_int {#3}
    \xeCJK_int_until_do:nn { \l_@@_begin_int > \l_@@_end_int }
      {
        \XeTeXcharclass \l_@@_begin_int = \l_@@_tmp_int
        \int_incr:N \l_@@_begin_int
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_set_char_class_eq:nn}
% \changes{v3.1.1}{2012/12/06}{交换参数的顺序。}
% 将字符类 |#1| 中的字符全部设置成字符类 |#2|。只适用于 |#1| 的字符类范围为离散的
% 逗号列表的情况。
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_set_char_class_eq:nn #1#2
  {
    \int_set:Nn \l_@@_tmp_int { \xeCJK_class_num:n {#2} }
    \clist_map_inline:cn { c_@@_#1_chars_clist }
      { \XeTeXcharclass ##1 = \l_@@_tmp_int }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\normalspacedchars}
% 声明前后不加间距的字符。
%    \begin{macrocode}
\NewDocumentCommand \normalspacedchars { m }
  {
    \tl_map_inline:nn {#1}
      { \XeTeXcharclass `##1 = \xeCJK_class_num:n { NormalSpace } }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\xeCJKResetPunctClass}
% 用于重置标点符号所属的字符类。
%    \begin{macrocode}
\NewDocumentCommand \xeCJKResetPunctClass { }
  {
    \xeCJK_declare_char_class:nV { HalfLeft  } \c_@@_HalfLeft_chars_clist
    \xeCJK_declare_char_class:nV { HalfRight } \c_@@_HalfRight_chars_clist
    \xeCJK_declare_char_class:nV { FullLeft  } \c_@@_FullLeft_chars_clist
    \xeCJK_declare_char_class:nV { FullRight } \c_@@_FullRight_chars_clist
  }
%    \end{macrocode}
% \end{macro}
%
%
% \begin{macro}{\xeCJKResetCharClass}
% 用于恢复 \pkg{xeCJK} 对字符类别的设置。
%    \begin{macrocode}
\NewDocumentCommand \xeCJKResetCharClass { }
  {
    \xeCJK_declare_char_class:nV { CJK } \c_@@_CJK_chars_clist
    \xeCJK_declare_char_class:nV { NormalSpace } \c_@@_NormalSpace_chars_clist
    \xeCJK_declare_char_class:nV { IVS } \c_@@_IVS_chars_clist
    \xeCJKResetPunctClass
  }
%    \end{macrocode}
% \end{macro}
%
% 设置字符类别。
%    \begin{macrocode}
\xeCJKResetCharClass
%    \end{macrocode}
%
% \begin{macro}[internal]{\xeCJK_inter_class_toks:nnn}
% 在相邻类别之间插入内容。
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \xeCJK_inter_class_toks:nnn #1#2#3
  { \XeTeXinterchartoks \xeCJK_class_num:n {#1} ~ \xeCJK_class_num:n {#2} = {#3} }
\cs_generate_variant:Nn \xeCJK_inter_class_toks:nnn { nnc , nnx }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_get_inter_class_toks:nn}
% 取出相邻类别之间的内容。
%    \begin{macrocode}
\cs_new_nopar:Npn \xeCJK_get_inter_class_toks:nn #1#2
  { \tex_the:D \XeTeXinterchartoks \xeCJK_class_num:n {#1} ~ \xeCJK_class_num:n {#2} }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_clear_inter_class_toks:nn}
% 清除相邻类别之间的内容。
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \xeCJK_clear_inter_class_toks:nn #1#2
  { \xeCJK_inter_class_toks:nnn {#1} {#2} { \prg_do_nothing: } }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_pre_inter_class_toks:nnn}
% 在相邻类别之间已有的内容前增加内容。
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \xeCJK_pre_inter_class_toks:nnn #1#2#3
  {
    \xeCJK_inter_class_toks:nnx {#1} {#2}
      { \exp_not:n {#3} \xeCJK_get_inter_class_toks:nn {#1} {#2} }
  }
\cs_generate_variant:Nn \xeCJK_pre_inter_class_toks:nnn { nnx }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_app_inter_class_toks:nnn}
% 在相邻类别之间已有的内容后追加内容。
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \xeCJK_app_inter_class_toks:nnn #1#2#3
  {
    \xeCJK_inter_class_toks:nnx {#1} {#2}
      { \xeCJK_get_inter_class_toks:nn {#1} {#2} \exp_not:n {#3} }
  }
\cs_generate_variant:Nn \xeCJK_app_inter_class_toks:nnn { nnc , nnx }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_copy_inter_class_toks:nnnn}
% 将 |#3| 和 |#4| 之间的内容复制到 |#1| 和 |#2| 之间。
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \xeCJK_copy_inter_class_toks:nnnn #1#2#3#4
  {
    \tl_set:Nx \l_@@_tmp_tl { \xeCJK_get_inter_class_toks:nn {#3} {#4} }
    \tl_if_empty:NF \l_@@_tmp_tl
      { \xeCJK_inter_class_toks:nnx {#1} {#2} { \exp_not:V \l_@@_tmp_tl } }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_replace_inter_class_toks:nnnn}
% 将 |#1| 和 |#2| 之间出现的 |#3| 用 |#4| 替换。
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \xeCJK_replace_inter_class_toks:nnnn #1#2#3#4
  {
    \tl_set:Nx \l_@@_tmp_tl { \xeCJK_get_inter_class_toks:nn {#1} {#2} }
    \tl_if_empty:NF \l_@@_tmp_tl
      {
        \tl_replace_all:Nnn \l_@@_tmp_tl {#3} {#4}
        \xeCJK_inter_class_toks:nnx {#1} {#2} { \exp_not:V \l_@@_tmp_tl }
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_clear_Boundary_and_CJK_toks:}
% 清除边界与 CJK 文字、全角左右标点之间的内容。
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \xeCJK_clear_Boundary_and_CJK_toks:
  { \seq_map_function:NN \g_@@_CJK_class_seq \@@_clear_Boundary_and_CJK_toks:n }
\cs_new_protected_nopar:Npn \@@_clear_Boundary_and_CJK_toks:n #1
  { \xeCJK_clear_inter_class_toks:nn { Boundary } {#1} }
%    \end{macrocode}
% \end{macro}
%
% \subsection{字符输出规则}
%
% \begin{center}
% \begin{tabular}{l*9c}
% \toprule
%   & |Default| & |CJK| & |FullL| & |FullR| & |HalfL|
%   & |HalfR| & |Normal| & |Bound| & |IVS| \\ \midrule
% |Default|
%   &
%   & \tokslink{def-cjk}
%   & \tokslink{def-cjk}
%   & \tokslink{def-cjk}
%   &
%   &
%   &
%   & \tokslink{def-bound}
%   & \tokslink{def-IVS}\\
% |CJK|
%   & \tokslink{def-cjk}
%   & \tokslink{cjk-cjk}
%   & \tokslink{cjk-fl-fr}
%   & \tokslink{cjk-fl-fr}
%   & \tokslink{def-cjk}
%   & \tokslink{def-cjk}
%   & \tokslink{def-cjk}
%   & \tokslink{cjk-bound}
%   &\\
% |FullLeft|
%   & \tokslink{def-cjk}
%   & \tokslink{fl-fr-others}
%   & \tokslink{cjk-fl-fr}
%   & \tokslink{cjk-fl-fr}
%   & \tokslink{def-cjk}
%   & \tokslink{def-cjk}
%   & \tokslink{def-cjk}
%   & \tokslink{fl-fr-bound}
%   & \tokslink{def-IVS}\\
% |FullRight|
%   & \tokslink{def-cjk}
%   & \tokslink{fl-fr-others}
%   & \tokslink{cjk-fl-fr}
%   & \tokslink{cjk-fl-fr}
%   & \tokslink{def-cjk}
%   & \tokslink{def-cjk}
%   & \tokslink{def-cjk}
%   & \tokslink{fl-fr-bound}
%   & \tokslink{def-IVS}\\
% |HalfLeft|
%   &
%   & \tokslink{def-cjk}
%   & \tokslink{def-cjk}
%   & \tokslink{def-cjk}
%   &
%   &
%   &
%   &
%   & \tokslink{def-IVS}\\
% |HalfRight|
%   &
%   & \tokslink{def-cjk}
%   & \tokslink{def-cjk}
%   & \tokslink{def-cjk}
%   &
%   &
%   &
%   & \tokslink{def-bound}
%   & \tokslink{def-IVS}\\
% |NormalSpace|
%   &
%   & \tokslink{def-cjk}
%   & \tokslink{def-cjk}
%   & \tokslink{def-cjk}
%   &
%   &
%   &
%   & \tokslink{ns-bound}
%   & \tokslink{def-IVS}\\
% |Boundary|
%   & \tokslink{bound-def}
%   & \tokslink{bound-cjk}
%   & \tokslink{bound-fl-fr}
%   & \tokslink{bound-fl-fr}
%   & \tokslink{bound-def}
%   &
%   & \tokslink{bound-ns}
%   &
%   & \tokslink{def-IVS}\\
% |IVS|
%   & \tokslink{def-IVS}
%   & \tokslink{def-IVS}
%   & \tokslink{def-IVS}
%   & \tokslink{def-IVS}
%   & \tokslink{def-IVS}
%   & \tokslink{def-IVS}
%   & \tokslink{def-IVS}
%   & \tokslink{def-IVS}
%   & \tokslink{def-IVS}\\
% \bottomrule
% \end{tabular}
% \end{center}
%
% \begin{macro}[internal]{\xeCJK_class_group_begin:,\xeCJK_class_group_end:}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \xeCJK_class_group_begin:
  {
    \c_group_begin_token
    \bool_set_true:N \l_@@_CJK_group_bool
  }
\bool_new:N \l_@@_CJK_group_bool
\cs_new_eq:NN \xeCJK_class_group_end: \c_group_end_token
%    \end{macrocode}
% \end{macro}
%
% \hypertarget{def-IVS}{\texttt{IVS}} 字符类与 |CJK| 字符类基本相同，只是从 |CJK|
% 转移到 |IVS| 时，不加入任何内容。
%    \begin{macrocode}
\AtEndOfPackage
  {
    \seq_map_inline:Nn \g_@@_class_seq
      {
        \str_if_eq:nnTF {#1} { IVS }
          { \xeCJK_copy_inter_class_toks:nnnn { IVS } {#1} { CJK } { CJK } }
          {
            \xeCJK_copy_inter_class_toks:nnnn { IVS } {#1} { CJK } {#1}
            \str_if_eq:nnF {#1} { CJK }
              { \xeCJK_copy_inter_class_toks:nnnn {#1} { IVS } {#1} { CJK } }
          }
      }
  }
%    \end{macrocode}
%
% \hypertarget{def-cjk}{}
%    \begin{macrocode}
\clist_map_inline:nn { Default , HalfLeft , HalfRight , NormalSpace }
  {
    \xeCJK_inter_class_toks:nnn {#1} { CJK }
      {
        \xeCJK_class_group_begin:
        \xeCJK_select_font:
        \xeCJK_clear_inter_class_toks:nn {#1} { CJK }
        \xeCJK_clear_Boundary_and_CJK_toks:
        \CJKsymbol
      }
    \xeCJK_inter_class_toks:nnn { CJK } {#1} { \xeCJK_class_group_end: }
  }
%    \end{macrocode}
%
% \hypertarget{bound-def}{}
%    \begin{macrocode}
\clist_map_inline:nn { Default , HalfLeft }
  {
    \xeCJK_inter_class_toks:nnn { Boundary } {#1} { \xeCJK_Boundary_and_Default: }
    \xeCJK_app_inter_class_toks:nnn { CJK } {#1} { \CJKecglue }
  }
%    \end{macrocode}
%
% \begin{macro}[internal]{\xeCJK_Boundary_and_Default:}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \xeCJK_Boundary_and_Default:
  {
    \bool_if:nTF
      {
        \l_@@_xecglue_bool &&
        \int_compare_p:nNn \etex_lastnodetype:D = \c_eleven &&
        \skip_if_eq_p:nn \tex_lastskip:D \c_xeCJK_space_skip_tl
      }
      {
        \tex_unskip:D
        \bool_if:nTF
          {
            \xeCJK_if_last_node_p:n { CJK }       ||
            \xeCJK_if_last_node_p:n { CJK-space }
          }
          { \xeCJK_remove_node: \CJKecglue } { ~ }
      }
      {
        \bool_if:nTF
          {
            \xeCJK_if_last_node_p:n { CJK }         ||
            \xeCJK_if_last_node_p:n { CJK-nobreak }
          }
          { \xeCJK_remove_node: \CJKecglue }
          {
            \xeCJK_if_last_node:nT { CJK-space }
              { \xeCJK_remove_node: \xeCJK_space_or_xecglue: }
          }
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \hypertarget{def-bound}{}
%    \begin{macrocode}
\clist_map_inline:nn { Default , HalfRight }
  {
    \xeCJK_inter_class_toks:nnn {#1} { Boundary }
      {
        \int_gset_eq:NN \g_@@_spacefactor_int \tex_spacefactor:D
        \peek_meaning_remove:NTF \tex_italiccorrection:D
          { \tex_italiccorrection:D { \xeCJK_make_node:n { default } } }
          {
            \token_if_space:NTF \l_peek_token
              { { \xeCJK_make_node:n { default-space } } }
              { { \xeCJK_make_node:n { default } } }
          }
      }
    \xeCJK_pre_inter_class_toks:nnn {#1} { CJK } { \CJKecglue }
  }
%    \end{macrocode}
%
% \changes{v3.2.5}{2013/07/10}
% {修正 \texttt{CJK} 和 \texttt{NormalSpace} 字符类之间因为边界造成的间距不正确的问题。}
%
% \hypertarget{bound-ns}{}
%    \begin{macrocode}
\xeCJK_inter_class_toks:nnn { Boundary } { NormalSpace }
  { \xeCJK_Boundary_and_NormalSp: }
%    \end{macrocode}
%
% \begin{macro}[internal]{\xeCJK_Boundary_and_NormalSp:}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \xeCJK_Boundary_and_NormalSp:
  {
    \bool_if:nTF
      {
        \l_@@_xecglue_bool &&
        \int_compare_p:nNn \etex_lastnodetype:D = \c_eleven &&
        \skip_if_eq_p:nn \tex_lastskip:D \c_xeCJK_space_skip_tl
      }
      {
        \tex_unskip:D
        \bool_if:nTF
          {
            \xeCJK_if_last_node_p:n { CJK }       ||
            \xeCJK_if_last_node_p:n { CJK-space }
          }
          { \xeCJK_remove_node: \CJKecglue } { ~ }
      }
      {
        \xeCJK_if_last_node:nT { CJK-space }
          { \xeCJK_remove_node: \xeCJK_space_or_xecglue: }
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \hypertarget{ns-bound}{}
%    \begin{macrocode}
\xeCJK_inter_class_toks:nnn { NormalSpace } { Boundary }
  {
    \int_gset_eq:NN \g_@@_spacefactor_int \tex_spacefactor:D
    \peek_meaning_remove:NTF \tex_italiccorrection:D
      { \tex_italiccorrection:D { \xeCJK_make_node:n { normalspace } } }
      {
        \token_if_space:NTF \l_peek_token
          { { \xeCJK_make_node:n { default-space } } }
          { { \xeCJK_make_node:n { normalspace } } }
      }
  }
%    \end{macrocode}
%
% \hypertarget{bound-cjk}{}
%    \begin{macrocode}
\xeCJK_inter_class_toks:nnn { Boundary } { CJK }
  {
    \xeCJK_check_for_glue:
    \xeCJK_class_group_begin:
    \xeCJK_clear_Boundary_and_CJK_toks:
    \xeCJK_select_font:
    \CJKsymbol
  }
%    \end{macrocode}
%
% \begin{macro}[internal]{\xeCJK_check_for_glue:}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \xeCJK_check_for_glue:
  {
    \bool_if:nTF
      { \xeCJK_if_last_node_p:n { CJK } || \xeCJK_if_last_node_p:n { CJK-space } }
      { \xeCJK_remove_node: \CJKglue }
      {
        \xeCJK_if_last_node:nTF { CJK-nobreak }
          { \xeCJK_remove_node: \xeCJK_no_break: \CJKglue }
          {
            \bool_if:nTF
              {
                \xeCJK_if_last_node_p:n { default }              ||
                \int_compare_p:nNn \etex_lastnodetype:D = \c_ten
              }
              { \xeCJK_remove_node: \CJKecglue }
              {
                \bool_if:nT
                  {
                    \l_@@_xecglue_bool &&
                    \int_compare_p:nNn \etex_lastnodetype:D = \c_eleven &&
                    ( \skip_if_eq_p:nn \tex_lastskip:D \c_xeCJK_space_skip_tl ||
                      \skip_if_eq_p:nn \tex_lastskip:D \l_@@_ecglue_skip )
                  }
                  {
                    \tex_unskip:D
                    \bool_if:nTF
                      {
                        \xeCJK_if_last_node_p:n { default-space }        ||
                        \int_compare_p:nNn \etex_lastnodetype:D = \c_ten ||
                        \xeCJK_if_last_node_p:n { default }
                      }
                      { \xeCJK_remove_node: \CJKecglue }
                      {
                        \bool_if:nTF
                          {
                            \xeCJK_if_last_node_p:n { CJK }       ||
                            \xeCJK_if_last_node_p:n { CJK-space }
                          }
                          {
                            \xeCJK_remove_node:
                            \bool_if:NTF \l_@@_reserve_space_bool
                              { ~ } { \CJKglue }
                          }
                          { ~ }
                      }
                  }
              }
          }
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[pTF,internal]{\xeCJK_if_last_node:n}
%    \begin{macrocode}
 \prg_new_conditional:Npnn \xeCJK_if_last_node:n #1 { p , T , F , TF }
  {
    \if_dim:w \use:c { c_@@_#1_node_dim } = \tex_lastkern:D
      \prg_return_true: \else: \prg_return_false: \fi:
  }
%    \end{macrocode}
% \end{macro}
%
% \changes{v3.1.0}{2012/11/14}{删除多余的 \texttt{default-itcorr} 结点。}
% \changes{v3.2.4}{2013/07/03}{尽量移除用作判断标志的 \cs{kern}。}
%
% \begin{macro}[internal]{\xeCJK_def_node:nn,\xeCJK_make_node:n}
% 用于判断插入的各种 |kern|。
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \xeCJK_def_node:nn #1#2
  {
    \dim_if_exist:cTF { c_@@_#1_node_dim }
      { \dim_gset:cn } { \dim_const:cn }
      { c_@@_#1_node_dim } {#2}
  }
\cs_new_protected_nopar:Npn \xeCJK_make_node:n #1
  {
    \tex_kern:D - \use:c { c_@@_#1_node_dim }
    \tex_kern:D   \use:c { c_@@_#1_node_dim }
  }
\cs_new_protected_nopar:Npn \xeCJK_remove_node:
  { \tex_unkern:D \tex_unkern:D }
\xeCJK_def_node:nn { CJK }           { 11 sp }
\xeCJK_def_node:nn { CJK-space }     { 12 sp }
\xeCJK_def_node:nn { default }       { 13 sp }
\xeCJK_def_node:nn { default-space } { 14 sp }
\xeCJK_def_node:nn { CJK-nobreak }   { 15 sp }
\xeCJK_def_node:nn { normalspace }   { 16 sp }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{CJKglue}
% CJK 文字之间插入的 |glue|。
%    \begin{macrocode}
\keys_define:nn { xeCJK / options }
  {
    CJKglue .code:n =
      {
        \cs_set_protected_nopar:Npn \CJKglue {#1}
        \xeCJK_glue_to_skip:nN {#1} \l_@@_ccglue_skip
      }
  }
\skip_new:N \l_@@_ccglue_skip
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{CJKecglue,xCJKecglue}
% CJK 与西文和数学行内数学公式之间自动添加的空白。
%    \begin{macrocode}
\keys_define:nn { xeCJK / options }
  {
    CJKecglue            .code:n =
      {
        \cs_set_protected_nopar:Npn \CJKecglue {#1}
        \xeCJK_glue_to_skip:nN {#1} \l_@@_ecglue_skip
      } ,
    xCJKecglue .choice: ,
    xCJKecglue / true    .code:n =
      {
        \bool_set_true:N  \l_@@_xecglue_bool
        \cs_set_eq:NN \xeCJK_space_or_xecglue: \CJKecglue
      } ,
    xCJKecglue / false   .code:n =
      {
        \bool_set_false:N \l_@@_xecglue_bool
        \cs_set_eq:NN \xeCJK_space_or_xecglue: \c_space_tl
      } ,
    xCJKecglue / unknown .code:n =
      {
        \bool_set_true:N  \l_@@_xecglue_bool
        \cs_set_protected_nopar:Npn \CJKecglue {#1}
        \xeCJK_glue_to_skip:nN {#1} \l_@@_ecglue_skip
        \cs_set_eq:NN \xeCJK_space_or_xecglue: \CJKecglue
      } ,
    xCJKecglue        .default:n = { true }
  }
\skip_new:N \l_@@_ecglue_skip
\bool_new:N \l_@@_xecglue_bool
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{CJKspace}
% 是否保留 CJK 文字间的空白，默认不保留。
%    \begin{macrocode}
\keys_define:nn { xeCJK / options }
  {
    CJKspace .bool_set:N = \l_@@_reserve_space_bool ,
    space        .meta:n = { CJKspace = true  } ,
    nospace      .meta:n = { CJKspace = false }
  }
%    \end{macrocode}
% \end{macro}
%
% \hypertarget{cjk-bound}{}
%    \begin{macrocode}
\xeCJK_inter_class_toks:nnn { CJK } { Boundary }
  {
    \xeCJK_class_group_end:
    { \xeCJK_make_node:n { CJK } }
    \xeCJK_ignore_spaces:w
  }
%    \end{macrocode}
%
% \begin{macro}[internal]{\xeCJK_ignore_spaces:w}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \xeCJK_ignore_spaces:w
  {
    \xeCJK_peek_catcode_ignore_spaces:NTF \c_math_toggle_token
      {
        \bool_if:NTF \l_@@_peek_ignore_spaces_bool
          { \xeCJK_space_or_xecglue: } { \CJKecglue }
      }
      {
        \bool_if:NTF \l_@@_peek_ignore_spaces_bool
          {
            \xeCJK_remove_node:
            { \xeCJK_make_node:n { CJK-space } }
            \bool_if:nT
              {
                \token_if_macro_p:N \l_peek_token ||
                ( \l_@@_reserve_space_bool &&
                  \token_if_letter_p:N \l_peek_token )
              }
              { \xeCJK_space_or_xecglue: }
          }
          { \@@_CJK_and_Boundary_aux: }
      }
  }
\cs_new_protected_nopar:Npn \@@_CJK_and_Boundary_aux:
  { \cs_if_exist:NF \l_peek_token \exp_not:N }
%    \end{macrocode}
% \end{macro}
%
% \hypertarget{cjk-cjk}{}
%    \begin{macrocode}
\xeCJK_inter_class_toks:nnn { CJK } { CJK } { \xeCJK_CJK_and_CJK:N }
%    \end{macrocode}
%
% \begin{macro}[internal]{\xeCJK_CJK_and_CJK:N}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \xeCJK_CJK_and_CJK:N #1 { \CJKglue \CJKsymbol {#1} }
%    \end{macrocode}
% \end{macro}
%
% \hypertarget{fl-fr-others}{}
%    \begin{macrocode}
\xeCJK_inter_class_toks:nnn { FullLeft }  { CJK }
  { \xeCJK_FullLeft_and_CJK: \CJKsymbol }
\xeCJK_inter_class_toks:nnn { FullRight } { CJK }
  { \xeCJK_FullRight_and_CJK: \CJKsymbol }
\seq_map_inline:Nn \g_@@_non_CJK_class_seq
  {
    \clist_map_inline:nn { FullLeft , FullRight }
      {
        \xeCJK_inter_class_toks:nnx {#1} {##1}
          { \exp_not:c { xeCJK_Default_and_##1:nN } {#1} }
        \xeCJK_inter_class_toks:nnc {##1} {#1} { xeCJK_##1_and_Default: }
      }
  }
%    \end{macrocode}
%
% \hypertarget{bound-fl-fr}{}
%    \begin{macrocode}
\xeCJK_inter_class_toks:nnn { Boundary } { FullLeft }
  { \xeCJK_Boundary_and_FullLeft:N  }
\xeCJK_inter_class_toks:nnn { Boundary } { FullRight }
  { \xeCJK_Boundary_and_FullRight:N  }
%    \end{macrocode}
%
% \begin{macro}[internal]{\xeCJK_FullRight_and_Boundary:}
% \hypertarget{fl-fr-bound}{}
%    \begin{macrocode}
\xeCJK_app_inter_class_toks:nnn { FullLeft } { Boundary } { \tex_ignorespaces:D }
\xeCJK_inter_class_toks:nnn { FullRight } { Boundary }
  { \xeCJK_FullRight_and_Boundary: }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_FullRight_and_Boundary:}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \xeCJK_FullRight_and_Boundary:
  { \xeCJK_FullRight_and_Default: \tex_ignorespaces:D }
%    \end{macrocode}
% \end{macro}
%
% \hypertarget{cjk-fl-fr}{}
%    \begin{macrocode}
\clist_map_inline:nn { CJK , FullLeft , FullRight }
  {
    \clist_map_inline:nn { FullLeft , FullRight }
      { \xeCJK_inter_class_toks:nnc {#1} {##1} { xeCJK_#1_and_##1:N } }
  }
%    \end{macrocode}
%
% \begin{macro}[internal]{\@@_punct_rule:NN}
% 用于抹去标点符号的左/右空白。
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_punct_rule:NN #1#2
  {
    \tex_vrule:D
      width  - \@@_use_punct_dim:nnn { bound } {#1} {#2}
      depth  \c_zero_dim
      height \c_zero_dim \scan_stop:
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_punct_glue:NN}
% 根据所选的标点处理方式在标点符号左/右增加的空白。
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_punct_glue:NN #1#2
  {
    \@@_punct_hskip:n
      {
        \@@_use_punct_dim:nnn { glue } {#1} {#2}
        minus \dim_eval:n { ( \@@_use_punct_dim:nnn { glue } {#1} {#2} ) / \c_two }
      }
  }
\cs_new_eq:NN \@@_punct_hskip:n \skip_horizontal:n
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_punct_kern:NN}
% 相邻两个标点之间的间距。
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_punct_kern:NN #1#2
  { \tex_kern:D \@@_use_punct_dim:nnn { kern } {#1} {#2} }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal,var]{\g_@@_last_punct_tl}
% 用于记录当前的标点符号。
%    \begin{macrocode}
\tl_new:N \g_@@_last_punct_tl
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_FullLeft_and_CJK:}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \xeCJK_FullLeft_and_CJK:
  {
    \@@_punct_if_middle:NTF \g_@@_last_punct_tl
      {
        \@@_punct_rule:NN \c_@@_right_tl \g_@@_last_punct_tl
        \xeCJK_no_break:
        \@@_punct_glue:NN \c_@@_left_tl  \g_@@_last_punct_tl
      }
      { \xeCJK_no_break: }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_FullLeft_and_Default:}
% \changes{v3.2.0}{2013/05/20}{修正 \pkg{xeCJK} 使西文在部分情况下无法断词的问题。}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \xeCJK_FullLeft_and_Default:
  {
    \@@_punct_if_middle:NTF \g_@@_last_punct_tl
      {
        \@@_punct_rule:NN \c_@@_right_tl \g_@@_last_punct_tl
        \xeCJK_class_group_end: \xeCJK_no_break:
        \@@_punct_glue:NN \c_@@_left_tl  \g_@@_last_punct_tl
      }
      { \xeCJK_class_group_end: \xeCJK_no_break: \@@_zero_glue: }
  }
\cs_new_protected_nopar:Npn \@@_zero_glue:
  { \skip_horizontal:N \c_zero_skip }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_FullRight_and_CJK:}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \xeCJK_FullRight_and_CJK:
  {
    \@@_punct_rule:NN \c_@@_right_tl \g_@@_last_punct_tl
    \@@_punct_glue:NN \c_@@_right_tl \g_@@_last_punct_tl
    \CJKglue
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_FullRight_and_Default:}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \xeCJK_FullRight_and_Default:
  {
    \@@_punct_rule:NN \c_@@_right_tl \g_@@_last_punct_tl
    \xeCJK_class_group_end:
    \@@_punct_glue:NN \c_@@_right_tl \g_@@_last_punct_tl
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_Default_and_FullLeft:nN}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \xeCJK_Default_and_FullLeft:nN #1#2
  {
    \xeCJK_get_punct_bounds:NN \c_@@_left_tl {#2}
    \@@_Default_and_FullLeft_glue:N {#2}
    \xeCJK_class_group_begin:
    \xeCJK_select_font:
    \xeCJK_clear_inter_class_toks:nn {#1} { FullLeft }
    \xeCJK_clear_Boundary_and_CJK_toks:
    \tl_gset:Nx \g_@@_last_punct_tl {#2}
    \@@_punct_rule:NN \c_@@_left_tl {#2}
    \CJKpunctsymbol {#2}
  }
\cs_new_protected_nopar:Npn \@@_Default_and_FullLeft_glue:N #1
  { \@@_punct_glue:NN \c_@@_left_tl {#1} }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_CJK_and_FullLeft:N}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \xeCJK_CJK_and_FullLeft:N #1
  {
    \xeCJK_get_punct_bounds:NN \c_@@_left_tl {#1}
    \@@_CJK_and_FullLeft_glue:N {#1}
    \tl_gset:Nx \g_@@_last_punct_tl {#1}
    \@@_punct_rule:NN \c_@@_left_tl {#1}
    \CJKpunctsymbol {#1}
  }
\cs_new_protected_nopar:Npn \@@_CJK_and_FullLeft_glue:N #1
  { \CJKglue \@@_punct_glue:NN \c_@@_left_tl {#1} }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_Boundary_and_FullLeft:N}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \xeCJK_Boundary_and_FullLeft:N #1
  {
    \xeCJK_get_punct_bounds:NN \c_@@_left_tl {#1}
    \@@_Boundary_and_FullLeft_glue:N {#1}
    \xeCJK_class_group_begin:
    \xeCJK_select_font:
    \xeCJK_clear_Boundary_and_CJK_toks:
    \tl_gset:Nx \g_@@_last_punct_tl {#1}
    \@@_punct_rule:NN \c_@@_left_tl {#1}
    \CJKpunctsymbol {#1}
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_Boundary_and_FullLeft_glue:N}
% \changes{v3.2.0}{2013/05/22}{当全角左标点前面是 \texttt{hlist}、\texttt{none}、
% \texttt{glue} 和 \texttt{penalty} 等节点时，压缩其左空白。}
% \changes{v3.2.4}{2013/07/03}{细化边界与全角左标点之间是否压缩空白的判断。}
% \changes{v3.2.5}{2013/07/13}{细化全角左标点是否位于段首的判断。}
% \changes{v3.2.5}{2013/07/13}{增加对 \pkg{enumitem} 宏包修改的 \cs{item} 的判断。}
% \cs{etex_lastnodetype:D} 为 $1$ 表示 hlist node，在这里用来判断是否位于段首。基于
% 正常情况下，\TeX 会在段落开头插入宽度为 \cs{parindent} 的水平盒子用于缩进。
% $-1$ 表示 empty list，常出现在盒子的起始位置，在段落前使用 \cs{noindent} 就
% 是这种情况。$11$ 表示 glue node，这里判断的目的是当全角左标点出现在 \LaTeX 表格
% 的非 \texttt{p} 列行首时，能够对齐到单元格的边界。判断基于标准 \LaTeX 表格的
% 列格式（\cs{@tabclassz}）定义中，在 \texttt{l} 列和 \texttt{r} 列前为了防止
% \cs{tabcolsep} 被无意 \cs{unskip} 掉，都加了 |\hskip1sp|，而 \texttt{c} 列前
% 则有 \cs{hfil}。$13$ 表示 penalty node，这里判断的目的是全角左标点出现在 \LaTeX
% 列表环境的 \cs{item} 后面时，能对齐到边界。判断基于 \cs{item} 的内部定义
% \cs{@item} 对 \cs{everypar} 进行了修改，在这里起到影响作用的是
% |\box\@labels \penalty\z@|。\package{enumitem} 宏包修改了 \env{description}
% 环境中使用的 \cs{item}（\cs{enit@postlabel@i}），在这里起到影响作用的是
% |\penalty\z@ \hskip\labelsep|。以上判断都比较粗略，暂时也没有想起更好的办法。
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_Boundary_and_FullLeft_glue:N #1
  {
    \int_case:nnn { \etex_lastnodetype:D }
      {
        { \c_one       }
        {
          \box_set_to_last:N \l_@@_tmp_box
          \bool_if:nTF
            {
              \int_compare_p:nNn \etex_lastnodetype:D = \c_minus_one &&
              \dim_compare_p:nNn { \box_wd:N \l_@@_tmp_box } = \tex_parindent:D
            }
            { \box_use_clear:N \l_@@_tmp_box \use_none:n }
            { \box_use_clear:N \l_@@_tmp_box \use:n }
        }
        { \c_minus_one } { \use_none:n }
        { \c_eleven    }
        {
          \bool_if:nTF
            {
              ! ( \skip_if_finite_p:n { \tex_lastskip:D } ) ||
              \skip_if_eq_p:nn { \tex_lastskip:D } { 1 sp }
            }
            { \use_none:n }
            {
              \skip_if_eq:nnTF { \tex_lastskip:D } { \labelsep }
                {
                  \tex_unskip:D
                  \bool_if:nTF
                    {
                      \int_compare_p:nNn \etex_lastnodetype:D = \c_thirteen &&
                      \int_compare_p:nNn \tex_lastpenalty:D = \c_zero
                    }
                    { \skip_horizontal:n { \labelsep } \use_none:n }
                    { \skip_horizontal:n { \labelsep } \use:n }
                }
                { \use:n }
            }
        }
        { \c_thirteen  }
        {
          \int_compare:nNnTF \tex_lastpenalty:D = \c_zero
            {
              \tex_unpenalty:D
              \int_compare:nNnTF \etex_lastnodetype:D = \c_one
                { \tex_penalty:D \c_zero \use_none:n }
                { \tex_penalty:D \c_zero \use:n }
            }
            { \use:n }
        }
      }
      { \use:n }
      { \@@_punct_glue:NN \c_@@_left_tl {#1} }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_Default_and_FullRight:nN}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \xeCJK_Default_and_FullRight:nN #1#2
  {
    \xeCJK_get_punct_bounds:NN \c_@@_right_tl {#2}
    \@@_Default_and_FullRight_glue:N {#2}
    \xeCJK_class_group_begin:
    \xeCJK_select_font:
    \xeCJK_clear_inter_class_toks:nn {#1} { FullRight }
    \xeCJK_clear_Boundary_and_CJK_toks:
    \tl_gset:Nx \g_@@_last_punct_tl {#2}
    \@@_punct_if_middle:NT {#2}
      { \@@_punct_rule:NN \c_@@_left_tl {#2} }
    \xeCJK_FullRight_symbol:N {#2}
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_Boundary_and_FullRight:N}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \xeCJK_Boundary_and_FullRight:N #1
  {
    \xeCJK_get_punct_bounds:NN \c_@@_right_tl {#1}
    \@@_Default_and_FullRight_glue:N {#1}
    \xeCJK_class_group_begin:
    \xeCJK_select_font:
    \xeCJK_clear_Boundary_and_CJK_toks:
    \tl_gset:Nx \g_@@_last_punct_tl {#1}
    \@@_punct_if_middle:NT {#1}
      { \@@_punct_rule:NN \c_@@_left_tl {#1} }
    \xeCJK_FullRight_symbol:N {#1}
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_CJK_and_FullRight:N}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \xeCJK_CJK_and_FullRight:N #1
  {
    \xeCJK_get_punct_bounds:NN \c_@@_right_tl {#1}
    \@@_CJK_and_FullRight_glue:N {#1}
    \tl_gset:Nx \g_@@_last_punct_tl {#1}
    \@@_punct_if_middle:NT {#1}
      { \@@_punct_rule:NN \c_@@_left_tl {#1} }
    \xeCJK_FullRight_symbol:N {#1}
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]
% {\@@_CJK_and_FullRight_glue:N,\@@_Default_and_FullRight_glue:N}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_CJK_and_FullRight_glue:N #1
  {
    \@@_punct_if_long:NTF {#1}
      { \CJKglue }
      {
        \xeCJK_no_break:
        \@@_punct_if_middle:NT {#1}
          {  \@@_punct_glue:NN \c_@@_right_tl {#1} }
      }
  }
\cs_new_eq:NN \@@_Default_and_FullRight_glue:N \@@_CJK_and_FullRight_glue:N
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_FullLeft_and_FullLeft:N}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \xeCJK_FullLeft_and_FullLeft:N #1
  {
    \xeCJK_no_break:
    \xeCJK_get_punct_bounds:NN \c_@@_left_tl {#1}
    \xeCJK_get_punct_kerning:oN \g_@@_last_punct_tl {#1}
    \@@_punct_kern:NN \g_@@_last_punct_tl {#1}
    \tl_gset:Nx \g_@@_last_punct_tl {#1}
    \CJKpunctsymbol {#1}
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_FullLeft_and_FullRight:N}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \xeCJK_FullLeft_and_FullRight:N #1
  {
    \xeCJK_no_break:
    \xeCJK_get_punct_bounds:NN \c_@@_right_tl {#1}
    \xeCJK_get_punct_kerning:oN \g_@@_last_punct_tl {#1}
    \@@_punct_kern:NN \g_@@_last_punct_tl {#1}
    \tl_gset:Nx \g_@@_last_punct_tl {#1}
    \xeCJK_no_break:
    \xeCJK_FullRight_symbol:N {#1}
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_FullRight_and_FullLeft:N}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \xeCJK_FullRight_and_FullLeft:N #1
  {
    \xeCJK_get_punct_bounds:NN \c_@@_left_tl {#1}
    \xeCJK_get_punct_kerning:oN \g_@@_last_punct_tl {#1}
    \xeCJK_punct_kern:NN \g_@@_last_punct_tl {#1}
    \tl_gset:Nx \g_@@_last_punct_tl {#1}
    \CJKpunctsymbol {#1}
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_punct_nobreak_kern:NN}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_punct_nobreak_kern:NN #1#2
  {
    \@@_punct_kern:NN #1#2
    \xeCJK_no_break:
  }
\cs_new_eq:NN \xeCJK_punct_kern:NN \@@_punct_nobreak_kern:NN
%    \end{macrocode}
% \end{macro}
%
% \changes{v3.2.4}{2013/07/06}
% {使用 \texttt{AllowBreakBetweenPuncts} 时，相应标点符号仍能与边界对齐。}
%
% \begin{macro}[internal]{\@@_punct_breakable_kern:NN}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_punct_breakable_kern:NN #1#2
  {
    \@@_punct_rule:NN \c_@@_right_tl #1
    \@@_punct_hskip:n
      {
        \@@_use_punct_dim:nnn { kern } {#1} {#2} +
        \@@_use_punct_dim:nnn { bound } { \c_@@_right_tl } {#1} +
        \@@_use_punct_dim:nnn { bound } { \c_@@_left_tl }  {#2}
      }
    \@@_punct_rule:NN \c_@@_left_tl #2
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_FullRight_and_FullRight:N}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \xeCJK_FullRight_and_FullRight:N #1
  {
    \xeCJK_get_punct_bounds:NN \c_@@_right_tl {#1}
    \xeCJK_get_punct_kerning:oN \g_@@_last_punct_tl {#1}
    \@@_punct_kern:NN \g_@@_last_punct_tl {#1}
    \tl_gset:Nx \g_@@_last_punct_tl {#1}
    \xeCJK_no_break:
    \xeCJK_FullRight_symbol:N {#1}
  }
%    \end{macrocode}
% \end{macro}
%
% \subsection{全角右标点后的断行}
%
% \begin{macro}{CheckFullRight}
% \changes{v3.1.1}{2012/12/02}{处理全角右标点之后的断行问题。}
% 选项设置。
%    \begin{macrocode}
\keys_define:nn { xeCJK / options }
  {
    CheckFullRight .choice: ,
    CheckFullRight / true  .code:n =
      {
        \cs_if_eq:NNF \xeCJK_FullRight_and_Boundary: \xeCJK_check_FullRight:
          {
            \cs_set_eq:NN \@@_save_FullRight_check: \xeCJK_FullRight_and_Boundary:
            \cs_set_eq:NN \@@_save_FullRight_symbol:N \xeCJK_FullRight_symbol:N
            \cs_set_eq:NN \xeCJK_FullRight_and_Boundary: \xeCJK_check_FullRight:
            \cs_set_eq:NN \xeCJK_FullRight_symbol:N \xeCJK_check_FullRight_symbol:Nw
          }
      } ,
    CheckFullRight / false .code:n =
      {
        \cs_if_eq:NNT \xeCJK_FullRight_and_Boundary: \xeCJK_check_FullRight:
          {
            \cs_set_eq:NN \xeCJK_FullRight_and_Boundary: \@@_save_FullRight_check:
            \cs_set_eq:NN \xeCJK_FullRight_symbol:N \@@_save_FullRight_symbol:N
          }
      } ,
    CheckFullRight      .default:n = { true }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_FullRight_symbol:N}
%    \begin{macrocode}
\cs_new_nopar:Npn \xeCJK_FullRight_symbol:N { \CJKpunctsymbol }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_check_FullRight:}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \xeCJK_check_FullRight:
  {
    \xeCJK_get_punct_bounds:NN \c_@@_right_tl \g_@@_last_punct_tl
    \@@_punct_rule:NN \c_@@_right_tl \g_@@_last_punct_tl
    \tl_case:Non \l_peek_token { \l_@@_no_break_cs_case_tl } {  }
    \@@_punct_glue:NN \c_@@_right_tl \g_@@_last_punct_tl
    \xeCJK_class_group_end:
  }
\cs_generate_variant:Nn \tl_case:Nnn { No }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_check_FullRight_symbol:Nw}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \xeCJK_check_FullRight_symbol:Nw #1
  { \xeCJK_peek_after_ignore_spaces:nw { \@@_save_FullRight_symbol:N {#1} } }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_cs_case_keys_define:nNNnn}
%    \begin{macrocode}
\cs_new_protected:Npn \xeCJK_cs_case_keys_define:nNNnn #1#2#3#4#5
  {
    \tl_new:N #2
    \seq_new:N #3
    \keys_define:nn { xeCJK / options }
      {
        #1  .code:n =
          {
            \seq_set_split:Nnn #3 { } {##1}
            \@@_update_cs_case_tl:NNnn #2#3 {#4} {#5}
          } ,
        #1+ .code:n =
          {
            \tl_map_inline:nn {##1}
              { \seq_if_in:NnF #3 {####1} { \seq_put_right:Nn #3 {####1} } }
            \@@_update_cs_case_tl:NNnn #2#3 {#4} {#5}
          } ,
        #1- .code:n =
          {
            \tl_map_inline:nn {##1} { \seq_remove_all:Nn #3 {####1} }
            \@@_update_cs_case_tl:NNnn #2#3 {#4} {#5}
          }
      }
  }
\cs_new_protected:Npn \@@_update_cs_case_tl:NNnn #1#2#3#4
  {
    \tl_clear:N #1
    \seq_map_inline:Nn #2 { \tl_put_right:Nn #1 { {##1} {#3} } }
    #4
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{NoBreakCS}
% 设置不能在全角右标点之后断行的控制序列。
%    \begin{macrocode}
\xeCJK_cs_case_keys_define:nNNnn { NoBreakCS }
  \l_@@_no_break_cs_case_tl \l_@@_no_break_cs_seq { \xeCJK_no_break: } { }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\xeCJKnobreak}
% \changes{v3.1.1}{2012/12/03}{增加 \cs{nobreak} 的 \pkg{xeCJK} 版本。}
% 为保险起见，我们在这里用了一个循环。
%    \begin{macrocode}
\NewDocumentCommand \xeCJKnobreak { }
  {
    \bool_set_true:N \l_@@_tmp_bool
    \int_while_do:nNnn \etex_lastnodetype:D = \c_eleven
      {
        \bool_if:NTF \l_@@_tmp_bool
          {
            \bool_set_false:N \l_@@_tmp_bool
            \skip_set_eq:NN \l_@@_tmp_skip \tex_lastskip:D
          }
          { \skip_add:Nn \l_@@_tmp_skip \tex_lastskip:D }
        \tex_unskip:D
      }
    \xeCJK_no_break:
    \bool_if:NF \l_@@_tmp_bool { \skip_horizontal:N \l_@@_tmp_skip }
  }
%    \end{macrocode}
% \end{macro}
%
% \subsection{段末孤字处理}
%
% \begin{macro}{CheckSingle}
% 孤字处理功能选项。
%    \begin{macrocode}
\keys_define:nn { xeCJK / options }
  {
    CheckSingle .choice: ,
    CheckSingle / true  .code:n =
      {
        \cs_if_eq:NNF \xeCJK_CJK_and_CJK:N \xeCJK_check_single:Nw
          {
            \cs_set_eq:NN \@@_check_single_save:N \xeCJK_CJK_and_CJK:N
            \cs_set_eq:NN \xeCJK_CJK_and_CJK:N \xeCJK_check_single:Nw
          }
      } ,
    CheckSingle / false .code:n =
      {
        \cs_if_eq:NNT \xeCJK_CJK_and_CJK:N \xeCJK_check_single:Nw
          { \cs_set_eq:NN  \xeCJK_CJK_and_CJK:N \@@_check_single_save:N }
      } ,
    CheckSingle      .default:n = { true } ,
    CJKchecksingle      .meta:n = { CheckSingle = true }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_check_single:Nw}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \xeCJK_check_single:Nw #1
  {
    \peek_catcode:NTF \c_catcode_letter_token
      { \xeCJK_check_single:NNw #1 }
      {
        \token_if_other:NTF \l_peek_token
          { \xeCJK_check_single:NNw #1 }
          {
            \bool_if:nTF
              {
                \xeCJK_if_blank_x_p:n { \token_get_arg_spec:N \l_peek_token } &&
                \exp_args:No \tl_if_single_token_p:n \l_peek_token            &&
                ( \exp_after:wN \token_if_other_p:N  \l_peek_token ||
                  \exp_after:wN \token_if_letter_p:N \l_peek_token )
              }
              { \exp_after:wN \xeCJK_check_single:NNw \exp_after:wN #1 }
              { \@@_check_single_save:N #1 }
          }
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_check_single:NNw}
% \changes{v3.1.1}{2012/12/04}{改进定义，减少使用 \texttt{peek} 函数的次数。}
% 使用 \cs{group_align_safe_begin:} 和 \cs{group_align_safe_end:} 是为了防止在表格
% 里面报错。
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \xeCJK_check_single:NNw #1#2
  {
    \xeCJK_peek_catcode_ignore_spaces:NTF \c_catcode_letter_token
      {
        \bool_if:NTF \l_@@_peek_ignore_spaces_bool
          { \@@_check_single_space:NN #1#2 }
          { \@@_check_single_save:N #1 #2 }
      }
      {
        \group_align_safe_begin:
        \token_if_other:NTF \l_peek_token
          {
            \group_align_safe_end:
            \bool_if:NTF \l_@@_peek_ignore_spaces_bool
              { \@@_check_single_space:NN #1#2 }
              { \@@_check_single_save:N #1 #2 }
          }
          {
            \token_if_cs:NTF \l_peek_token
              {
                \group_align_safe_end:
                \bool_if:NTF \l_@@_peek_ignore_spaces_bool
                  { \xeCJK_check_single_cs:NNn #1#2 { ~ } }
                  { \xeCJK_check_single_cs:NNn #1#2 { } }
              }
              {
                \group_align_safe_end:
                \bool_if:nTF
                  {
                    \l_@@_plain_equation_bool &&
                    \token_if_math_toggle_p:N \l_peek_token
                  }
                  {
                    \bool_if:NTF \l_@@_peek_ignore_spaces_bool
                      { \xeCJK_check_single_equation:NNnNw #1 #2 { ~ } }
                      { \xeCJK_check_single_equation:NNnNw #1 #2 { } }
                  }
                  {
                    \bool_if:NTF \l_@@_peek_ignore_spaces_bool
                      { \@@_check_single_save:N #1 #2 ~ }
                      { \@@_check_single_save:N #1 #2 }
                  }
              }
          }
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_check_single_space:NN}
% \changes{v3.1.1}{2012/12/13}
% {\texttt{CheckSingle} 支持段末“汉字$+$汉字$+$空格$+$汉字/标点”的形式。}
% \changes{v3.1.2}{2012/12/27}
% {使用 \cs{xeCJK_if_CJK_class:NTF} 来代替 \cs{int_case:nnn} 判断是否是 CJK 字符类。}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_check_single_space:NN #1#2
  {
    \xeCJK_if_CJK_class:NTF #2
      {
        \xeCJK_if_CJK_class:NTF \l_peek_token
          { \@@_check_single_save:N #1 #2 }
          { \@@_check_single_save:N #1 #2 ~ }
      }
      { \@@_check_single_save:N #1 #2 ~ }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_check_single_equation:NNnNw}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \xeCJK_check_single_equation:NNnNw #1#2#3#4
  {
    \peek_catcode:NTF \c_math_toggle_token
      {
        \xeCJK_no_break: \@@_check_single_save:N #1
        \xeCJK_make_node:n { CJK-nobreak } #2 #4
      }
      { \@@_check_single_save:N #1 #2#3#4 }
  }
%    \end{macrocode}
% \end{macro}
%
% \changes{v3.2.3}{2013/06/11}
% {解决 \texttt{CheckSingle} 选项与 \pkg{tablists} 宏包的冲突。}
% \changes{v3.2.4}{2013/06/26}
% {解决使用 \texttt{CheckSingle} 时，某些 \cs{CJKglue} 不能被正确加入的问题。}
%
% \begin{macro}[internal]{\xeCJK_check_single_cs:NNn}
% 在使用 \texttt{CheckSingle} 选项时，在 \package{tablists} 宏包定义的
% \env{tabenum} 环境中会出现下面的错误：
% \begin{verbatim}
%   ! Forbidden control sequence found while scanning use of \use_ii:nn.
%   <inserted text>
%                   \par
%    l.10 \item
% \end{verbatim}
% 原因在于 \env{tabenum} 实际上是一个 \TeX 对齐环境（\cs{halign}），\cs{par} 在
% 其中被重定义为 \cs{cr}。而在下面 \cs{tl_case:Nnn} 的分支里有对 \cs{par} 的
% \cs{ifx} 判断。解决办法是将判断用 \cs{group_align_safe_begin:} 和
% \cs{group_align_safe_end:} 包起来。或者改用原语 \cs{tex_par:D} 作为判断条件。
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \xeCJK_check_single_cs:NNn #1#2#3
  {
    \group_align_safe_begin:
    \tl_case:Non \l_peek_token
      { \l_@@_check_single_cs_case_tl }
      { \group_align_safe_end: \use_iii:nnn }
      { \xeCJK_check_single_env:nnNn }
      {
        \xeCJK_no_break: \@@_check_single_save:N #1
        \xeCJK_make_node:n { CJK-nobreak } #2
      }
      { \@@_check_single_save:N #1 #2#3 }
  }
\tl_new:N \l_@@_check_single_cs_case_tl
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_check_single_env:nnNn}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \xeCJK_check_single_env:nnNn #1#2#3#4
  {
    \group_align_safe_begin:
    \str_case_x:non {#4}
      { \l_@@_inline_env_case_tl }
      { \group_align_safe_end: \use_i:nn }
      {#1} {#2}
    \scan_stop: #3 {#4}
  }
\cs_generate_variant:Nn \str_case_x:nnn { no }
%    \end{macrocode}
% \end{macro}
%
% \changes{v3.1.1}{2012/12/04}{增加 \texttt{NewLineCS} 和 \texttt{EnvCS} 选项。}
%
% \begin{macro}{NewLineCS}
%    \begin{macrocode}
\xeCJK_cs_case_keys_define:nNNnn { NewLineCS }
  \l_@@_new_line_cs_case_tl \l_@@_new_line_cs_seq
  { \group_align_safe_end: \use_ii:nnn }
  {
    \tl_concat:NNN \l_@@_check_single_cs_case_tl
      \l_@@_new_line_cs_case_tl \l_@@_env_cs_case_tl
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{EnvCS}
%    \begin{macrocode}
\xeCJK_cs_case_keys_define:nNNnn { EnvCS }
  \l_@@_env_cs_case_tl \l_@@_env_cs_seq
  { \group_align_safe_end: \use:n }
  {
    \tl_concat:NNN \l_@@_check_single_cs_case_tl
      \l_@@_new_line_cs_case_tl \l_@@_env_cs_case_tl
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{InlineEnv}
% \changes{v3.1.1}{2012/12/05}{改变行内环境的设置方式，从而使用 \cs{str_case_x:nnn}
% 代替原来的 \cs{clist_if_in:NnTF} 来判断是否是行内环境。}
%    \begin{macrocode}
\keys_define:nn { xeCJK / options }
  {
    InlineEnv  .code:n =
      {
        \seq_set_from_clist:Nn \l_@@_inline_env_seq {#1}
        \@@_update_inline_env_case_tl:
      } ,
    InlineEnv+      .code:n =
      {
        \clist_map_inline:nn {#1}
          {
            \seq_if_in:NnF \l_@@_inline_env_seq {##1}
              { \seq_put_right:Nn \l_@@_inline_env_seq {##1} }
          }
        \@@_update_inline_env_case_tl:
      } ,
    InlineEnv-      .code:n =
      {
        \clist_map_inline:nn {#1}
          { \seq_remove_all:Nn \l_@@_inline_env_seq {##1} }
        \@@_update_inline_env_case_tl:
      }
  }
\seq_new:N \l_@@_inline_env_seq
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_update_inline_env_case_tl:}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_update_inline_env_case_tl:
  {
    \tl_clear:N \l_@@_inline_env_case_tl
    \seq_map_inline:Nn \l_@@_inline_env_seq
      { \tl_put_right:Nn \l_@@_inline_env_case_tl { {##1} { \use_ii:nn } } }
  }
\tl_new:N \l_@@_inline_env_case_tl
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{PlainEquation}
% \changes{v3.1.1}{2012/12/06}{增加 \texttt{PlainEquation} 选项。}
%    \begin{macrocode}
\keys_define:nn { xeCJK / options }
  { PlainEquation .bool_set:N = \l_@@_plain_equation_bool }
%    \end{macrocode}
% \end{macro}
%
% \subsection{增加 CJK 子分区}
%
% \begin{macro}[internal]{\g_@@_CJK_sub_class_seq}
%    \begin{macrocode}
\seq_new:N \g_@@_CJK_sub_class_seq
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\xeCJKDeclareSubCJKBlock}
% 声明 CJK 子区范围，|#1| 为自定义名称，|#2| 为子区的 |Unicode| 范围。
%    \begin{macrocode}
\NewDocumentCommand \xeCJKDeclareSubCJKBlock
  { s > { \TrimSpaces } m > { \TrimSpaces } m }
  {
    \xeCJK_declare_sub_char_class:nxx { CJK } {#2} {#3}
    \IfBooleanT {#1} { \xeCJKResetPunctClass }
  }
\@onlypreamble \xeCJKDeclareSubCJKBlock
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\xeCJKCancelSubCJKBlock,\xeCJKRestoreSubCJKBlock}
% 取消和恢复对 CJK 子区的声明。
%    \begin{macrocode}
\bool_new:N \l_@@_sub_cancel_bool
\NewDocumentCommand \xeCJKCancelSubCJKBlock { s m }
  {
    \bool_if:NF \l_@@_sub_cancel_bool
      {
        \bool_set_true:N \l_@@_sub_cancel_bool
        \@@_sub_restore_or_cancel:x {#2}
        \IfBooleanT {#1} { \xeCJKResetPunctClass }
      }
  }
\NewDocumentCommand \xeCJKRestoreSubCJKBlock { s m }
  {
    \bool_if:NT \l_@@_sub_cancel_bool
      {
        \bool_set_false:N \l_@@_sub_cancel_bool
        \@@_sub_restore_or_cancel:x {#2}
        \IfBooleanT {#1} { \xeCJKResetPunctClass }
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_sub_restore_or_cancel:n}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_sub_restore_or_cancel:n #1
  {
    \clist_map_inline:nn {#1}
      {
        \int_if_exist:cTF { \@@_class_csname:n { CJK/##1 } }
          {
            \xeCJK_declare_char_class:nx
              { CJK \bool_if:NF \l_@@_sub_cancel_bool { /##1 } }
              { \tl_use:c { g_@@_CJK/##1_range_clist } }
          }
          { \@@_error:nx { SubBlock-undefined } {##1} }
      }
  }
\cs_generate_variant:Nn \@@_sub_restore_or_cancel:n { x }
\@@_msg_new:nn { SubBlock-undefined }
  {
    The~CJK~sub~block~`#1'~is~undefined.\\\\
    Try~to~use~\token_to_str:N \xeCJKDeclareSubCJKBlock \
    to~declare~it.
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_declare_sub_char_class:nnn}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \xeCJK_declare_sub_char_class:nnn #1#2#3
  {
    \int_if_exist:cF { \@@_class_csname:n { #1/#2 } }
      {
        \xeCJK_new_class:n { #1/#2 }
        \@@_set_sub_class_toks:nn {#1} {#2}
        \xeCJK_new_sub_key:n {#2}
      }
    \xeCJK_declare_char_class:nn { #1/#2 } {#3}
  }
\cs_generate_variant:Nn \xeCJK_declare_sub_char_class:nnn { nxx }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_set_sub_class_toks:nn}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_set_sub_class_toks:nn #1#2
  {
    \seq_map_inline:Nn \g_@@_base_class_seq
      {
        \xeCJK_copy_inter_class_toks:nnnn { #1/#2 } {##1} {#1}  {##1}
        \xeCJK_copy_inter_class_toks:nnnn {##1} { #1/#2 } {##1} {#1}
        \str_if_eq:nnTF {##1} { CJK }
          {
            \xeCJK_pre_inter_class_toks:nnn {##1} { #1/#2 }
              { \@@_switch_font:nn {#1} {#2} }
          }
          {
            \xeCJK_replace_inter_class_toks:nnnn {##1} { #1/#2 }
              { \CJKsymbol }
              { \@@_switch_font:nn {#1} {#2} \CJKsymbol }
          }
      }
    \xeCJK_copy_inter_class_toks:nnnn { #1/#2 } { #1/#2 } {#1} {#1}
    \seq_map_inline:Nn \g_@@_CJK_sub_class_seq
      {
        \xeCJK_copy_inter_class_toks:nnnn { #1/#2  } { #1/##1 } {#1} {#1}
        \xeCJK_copy_inter_class_toks:nnnn { #1/##1 } { #1/#2  } {#1} {#1}
        \xeCJK_pre_inter_class_toks:nnn { #1/#2 } { #1/##1 }
          { \@@_switch_font:nn {#2} {##1} }
        \xeCJK_pre_inter_class_toks:nnn { #1/##1 } { #1/#2 }
          { \@@_switch_font:nn {##1} {#2} }
      }
    \seq_gput_right:Nn \g_@@_CJK_sub_class_seq {#2}
    \@@_save_CJK_class:n { #1/#2 }
    \clist_map_inline:nn { CJK , FullLeft , FullRight }
      {
        \xeCJK_pre_inter_class_toks:nnn { #1/#2 } {##1}
          { \@@_switch_font:nn {#2} {#1} }
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \subsection{标点处理}
%
% \cs{XeTeXglyphbouds} 可以得到一个字符的左右边距，用于标点压缩。如果它不可用，则
% 在文档中只能使用 |plain| 这一标点格式原样输出标点。
%    \begin{macrocode}
\cs_if_exist:NF \XeTeXglyphbounds
  {
    \@@_msg_new:nn { XeTeX-too-old }
      {
        \token_to_str:N \XeTeXglyphbounds \ is~not~defined.\\
        CJK~punctuation~kerning~will~not~be~available.\\\\
        You~have~to~update~XeTeX~to~the~version~0.9995.0~or~later.
      }
    \@@_error:n { XeTeX-too-old }
    \AtEndOfPackage
      {
        \keys_define:nn { xeCJK / options }
          {
            PunctStyle / unknown .code:n =
              { \@@_error:nx { punct-style-unknown } { \l_keys_value_tl } }
          }
        \seq_gclear:N \g_@@_punct_style_seq
        \xeCJKsetup { PunctStyle = plain }
      }
  }
%    \end{macrocode}
%
% \begin{macro}{\xeCJKsetwidth}
% 手动设置参数中的标点符号的宽度。
%    \begin{macrocode}
\NewDocumentCommand \xeCJKsetwidth { m m }
  { \tl_map_inline:xn {#1} { \tl_gset:cn { g_@@_punct_width/##1/tl } {#2} } }
\cs_generate_variant:Nn \tl_map_inline:nn { x }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\xeCJKsetkern}
% 手动设置相邻标点的距离。
%    \begin{macrocode}
\NewDocumentCommand \xeCJKsetkern { m m m }
  { \tl_gset:cn { g_@@_punct/kern/#1/#2/tl } {#3} }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal,var]{\c_@@_left_tl,\c_@@_right_tl}
%    \begin{macrocode}
\tl_const:Nn \c_@@_left_tl  { left }
\tl_const:Nn \c_@@_right_tl { right }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}
%  {AllowBreakBetweenPuncts, KaiMingPunct, LongPunct, MiddlePunct,PunctWidth}
% 相关选项声明。
%    \begin{macrocode}
\keys_define:nn { xeCJK / options }
  {
    AllowBreakBetweenPuncts .choice: ,
    AllowBreakBetweenPuncts / true  .code:n =
      {
        \bool_set_true:N \l_@@_punct_breakable_bool
        \cs_set_eq:NN \xeCJK_punct_kern:NN \@@_punct_breakable_kern:NN
      } ,
    AllowBreakBetweenPuncts / false .code:n =
      {
        \bool_set_false:N \l_@@_punct_breakable_bool
        \cs_set_eq:NN \xeCJK_punct_kern:NN \@@_punct_nobreak_kern:NN
      } ,
    AllowBreakBetweenPuncts      .default:n = { true } ,
    KaiMingPunct  .code:n = { \@@_set_special_punct:nn { mixed_width } {#1} } ,
    KaiMingPunct+ .code:n = { \@@_add_special_punct:nn { mixed_width } {#1} } ,
    KaiMingPunct- .code:n = { \@@_sub_special_punct:nn { mixed_width } {#1} } ,
    LongPunct     .code:n = { \@@_set_special_punct:nn { long } {#1} } ,
    LongPunct+    .code:n = { \@@_add_special_punct:nn { long } {#1} } ,
    LongPunct-    .code:n = { \@@_sub_special_punct:nn { long } {#1} } ,
    MiddlePunct   .code:n = { \@@_set_special_punct:nn { middle } {#1} } ,
    MiddlePunct+  .code:n = { \@@_add_special_punct:nn { middle } {#1} } ,
    MiddlePunct-  .code:n = { \@@_sub_special_punct:nn { middle } {#1} } ,
    PunctWidth .tl_gset:N = \g_@@_punct_width_tl
  }
\bool_new:N \l_@@_punct_breakable_bool
%    \end{macrocode}
% \end{macro}
%
% 相关选项定义的辅助函数。
%    \begin{macrocode}
\clist_new:N \g_@@_special_punct_clist
\clist_gset:Nn \g_@@_special_punct_clist { mixed_width , long , middle }
\cs_new_nopar:Npn \@@_special_punct_seq:n #1 { g_@@_special_punct_#1_seq }
\cs_new_nopar:Npn \@@_special_punct_tl:nN #1#2 { g_@@_special_punct_#1_#2_tl }
\clist_map_inline:Nn \g_@@_special_punct_clist
  { \seq_new:c { \@@_special_punct_seq:n {#1} } }
\cs_new_protected_nopar:Npn \@@_set_special_punct:nn #1#2
  {
    \seq_map_inline:cn { \@@_special_punct_seq:n {#1} }
      { \cs_undefine:c { \@@_special_punct_tl:nN {#1} {##1} } }
    \seq_gclear:c { \@@_special_punct_seq:n {#1} }
    \tl_map_inline:xn {#2}
      {
        \tl_new:c { \@@_special_punct_tl:nN {#1} {##1} }
        \seq_gput_right:cn { \@@_special_punct_seq:n {#1} } {##1}
      }
  }
\cs_new_protected_nopar:Npn \@@_add_special_punct:nn #1#2
  {
    \tl_map_inline:xn {#2}
      {
        \seq_if_in:cnF { \@@_special_punct_seq:n {#1} } {##1}
          {
            \tl_new:c { \@@_special_punct_tl:nN {#1} {##1} }
            \seq_gput_right:cn { \@@_special_punct_seq:n {#1} } {##1}
          }
      }
  }
\cs_new_protected_nopar:Npn \@@_sub_special_punct:nn #1#2
  {
    \tl_map_inline:xn {#2}
      {
        \cs_undefine:c { \@@_special_punct_tl:nN {#1} {##1} }
        \seq_gremove_all:cn { \@@_special_punct_seq:n {#1} } {##1}
      }
  }
%    \end{macrocode}
%
% 判断一个标点符号是否为全角右标点和长标点符号。
%    \begin{macrocode}
\prg_new_conditional:Npnn \@@_punct_if_right:N #1 { p , T , F , TF }
  {
    \if_int_compare:w \xeCJK_token_value_class:N #1 = \xeCJK_class_num:n { FullRight }
      \prg_return_true: \else: \prg_return_false: \fi:
  }
\clist_map_inline:Nn \g_@@_special_punct_clist
  {
    \exp_args:Nc
    \prg_new_conditional:Npnn { @@_punct_if_#1:N } ##1 { p , T , F , TF }
      {
        \if_cs_exist:w \@@_special_punct_tl:nN {#1} {##1} \cs_end:
          \prg_return_true: \else: \prg_return_false: \fi:
      }
  }
%    \end{macrocode}
%
% 一些用于记录的辅助函数。
%    \begin{macrocode}
\cs_new_nopar:Npn \@@_punct_dim_csname:nn #1#2
  { c__\l_xeCJK_current_font_tl/\l_xeCJK_punct_style_tl/#1/#2/tl }
\cs_new_nopar:Npn \@@_punct_dim_csname:nnn
  { \@@_punct_dim_csname:nnnn { \l_xeCJK_punct_style_tl } }
\cs_new_nopar:Npn \@@_punct_dim_csname:nnnn #1#2#3#4
  { c__\l_xeCJK_current_font_tl/#1/#2/#3/#4/tl }
\cs_new_nopar:Npn \@@_use_punct_dim:nn #1#2
  { \tl_use:c { \@@_punct_dim_csname:nn {#1} {#2} } }
\cs_new_nopar:Npn \@@_use_punct_dim:nnn #1#2#3
  { \tl_use:c { \@@_punct_dim_csname:nnn {#1} {#2} {#3} } }
\cs_new_protected_nopar:Npn \@@_save_punct_dim:nnn #1#2#3
  { \tl_const:cx { \@@_punct_dim_csname:nn {#1} {#2} } { \dim_eval:n {#3} } }
\cs_new_protected_nopar:Npn \@@_save_punct_dim:nnnn #1#2#3#4
  { \tl_const:cx { \@@_punct_dim_csname:nnn {#1} {#2} {#3} } { \dim_eval:n {#4} } }
%    \end{macrocode}
%
% \changes{v3.1.0}{2012/11/14}{使用 \pkg{xtemplate} 宏包的机制来组织标点符号的处理。}
%
% 定义标点处理模板。
%    \begin{macrocode}
\DeclareObjectType { xeCJK / punctuation } { \c_three }
%    \end{macrocode}
%
%    \begin{macrocode}
\DeclareTemplateInterface { xeCJK / punctuation } { basic } { \c_three }
  {
    enabled-global-setting  : boolean = true ,
    fixed-punct-width       : length  = \c_max_dim ,
    fixed-punct-ratio       : real    = \c_one_fp ,
    mixed-punct-width       : length  = \KeyValue { fixed-punct-width } ,
    mixed-punct-ratio       : real    = \KeyValue { fixed-punct-ratio } ,
    middle-punct-width      : length  = \KeyValue { fixed-punct-width } ,
    middle-punct-ratio      : real    = \KeyValue { fixed-punct-ratio } ,
    fixed-margin-width      : length  = \c_max_dim ,
    fixed-margin-ratio      : real    = \c_one_fp ,
    mixed-margin-width      : length  = \KeyValue { fixed-margin-width } ,
    mixed-margin-ratio      : real    = \KeyValue { fixed-margin-ratio } ,
    middle-margin-width     : length  = \KeyValue { fixed-margin-width } ,
    middle-margin-ratio     : real    = \KeyValue { fixed-margin-ratio } ,
    add-min-bound-to-margin : boolean = false ,
    optimize-margin         : boolean = false ,
    margin-minimum          : length  = \c_zero_dim ,
    enabled-kerning         : boolean = true ,
    min-bound-to-kerning    : boolean = false ,
    kerning-total-width     : length  = \c_max_dim ,
    kerning-total-ratio     : real    = 0.75 ,
    optimize-kerning        : boolean = false ,
    same-align-margin       : length  = \c_max_dim ,
    same-align-ratio        : real    = \c_zero_fp ,
    different-align-margin  : length  = \c_max_dim ,
    different-align-ratio   : real    = \c_zero_fp ,
    kerning-margin-width    : length  = \c_max_dim ,
    kerning-margin-ratio    : real    = \c_one_fp ,
    kerning-margin-minimum  : length  = \c_zero_dim
  }
%    \end{macrocode}
%
%    \begin{macrocode}
\DeclareTemplateCode { xeCJK / punctuation } { basic } { \c_three }
  {
    enabled-global-setting  = \l_@@_enabled_global_setting_bool ,
    fixed-punct-width       = \l_@@_fixed_punct_width_dim ,
    fixed-punct-ratio       = \l_@@_fixed_punct_ratio_fp ,
    mixed-punct-width       = \l_@@_mixed_punct_width_dim ,
    mixed-punct-ratio       = \l_@@_mixed_punct_ratio_fp ,
    middle-punct-width      = \l_@@_middle_punct_width_dim ,
    middle-punct-ratio      = \l_@@_middle_punct_ratio_fp ,
    fixed-margin-width      = \l_@@_fixed_margin_width_dim ,
    fixed-margin-ratio      = \l_@@_fixed_margin_ratio_fp ,
    mixed-margin-width      = \l_@@_mixed_margin_width_dim ,
    mixed-margin-ratio      = \l_@@_mixed_margin_ratio_fp ,
    middle-margin-width     = \l_@@_middle_margin_width_dim ,
    middle-margin-ratio     = \l_@@_middle_margin_ratio_fp ,
    add-min-bound-to-margin = \l_@@_add_min_bound_to_margin_bool ,
    optimize-margin         = \l_@@_optimize_margin_bool ,
    margin-minimum          = \l_@@_margin_minimum_dim ,
    enabled-kerning         = \l_@@_enabled_kerning_bool ,
    min-bound-to-kerning    = \l_@@_min_bound_to_kerning_bool ,
    kerning-total-width     = \l_@@_kerning_total_width_dim ,
    kerning-total-ratio     = \l_@@_kerning_total_ratio_fp ,
    optimize-kerning        = \l_@@_optimize_kerning_bool ,
    same-align-margin       = \l_@@_same_align_margin_dim ,
    same-align-ratio        = \l_@@_same_align_ratio_fp ,
    different-align-margin  = \l_@@_different_align_margin_dim ,
    different-align-ratio   = \l_@@_different_align_ratio_fp ,
    kerning-margin-width    = \l_@@_kerning_margin_width_dim ,
    kerning-margin-ratio    = \l_@@_kerning_margin_ratio_fp ,
    kerning-margin-minimum  = \l_@@_kerning_margin_minimum_dim
  }
  {
    \AssignTemplateKeys
    \tl_if_blank:nTF {#3}
      { \xeCJK_punct_margin_process:NN {#1} {#2} }
      { \xeCJK_punct_kerning_process:NN {#2} {#3} }
  }
%    \end{macrocode}
%
% \begin{macro}[internal]{\xeCJK_punct_margin_process:NN}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \xeCJK_punct_margin_process:NN #1#2
  {
    \dim_set:Nn \l_@@_bound_dim { \@@_use_punct_dim:nnn { bound } {#1} {#2} }
    \dim_set:Nn \l_@@_reverse_bound_dim
      {
        \@@_use_punct_dim:nnn { bound }
          { \xeCJK_reverse:nnn {#1} \c_@@_left_tl \c_@@_right_tl } {#2}
      }
    \dim_set:Nn \l_@@_tmp_dim
      {
        \bool_if:NTF \l_@@_enabled_global_setting_bool
          {
            \cs_if_exist_use:cTF { g_@@_punct_width/#2/tl }
              { \use_none:n }
              {
                \xeCJK_if_blank_x:nTF \g_@@_punct_width_tl
                  { \use:n }
                  { \g_@@_punct_width_tl \use_none:n }
              }
          }
          { \use:n }
          {
            \@@_punct_if_middle:NTF {#2}
              { \@@_punct_width_or_ratio:nN { middle } {#2} }
              {
                \@@_punct_if_mixed_width:NTF {#2}
                  { \@@_punct_width_or_ratio:nN { mixed } {#2} }
                  { \@@_punct_width_or_ratio:nN { fixed } {#2} }
              }
          }
      }
    \@@_save_punct_dim:nnnn { glue } {#1} {#2}
      {
        \dim_max:nn
          { \l_@@_margin_minimum_dim }
          {
            \dim_compare:nNnTF \l_@@_tmp_dim < \c_max_dim
              {
                \@@_punct_if_middle:NTF {#2}
                  {
                    (
                      \l_@@_tmp_dim - ( \@@_use_punct_dim:nn { dimen } {#2} )
                    ) / \c_two
                  }
                  {
                    \bool_if:NTF \l_@@_optimize_margin_bool
                      {
                        \dim_max:nn
                          { \dim_min:nn \l_@@_bound_dim \l_@@_reverse_bound_dim }
                      }
                      { \use:n }
                      {
                        \l_@@_tmp_dim - \l_@@_reverse_bound_dim
                        - ( \@@_use_punct_dim:nn { dimen } {#2} )
                      }
                  }
              }
              {
                \bool_if:NTF \l_@@_optimize_margin_bool
                  { \dim_min:nn { \l_@@_bound_dim } }
                  { \use:n }
                  {
                    \@@_punct_if_middle:NTF {#2}
                      {
                        \dim_compare:nNnTF \l_@@_middle_margin_width_dim < \c_max_dim
                          { \l_@@_middle_margin_width_dim }
                          {
                            \fp_use:N \l_@@_middle_margin_ratio_fp
                            \etex_dimexpr:D
                              ( \l_@@_bound_dim + \l_@@_reverse_bound_dim ) / \c_two
                            \scan_stop:
                          }
                      }
                      {
                        \@@_punct_if_mixed_width:NTF {#2}
                          { \@@_margin_width_or_ratio:n { mixed } }
                          { \@@_margin_width_or_ratio:n { fixed } }
                      }
                  }
              }
          }
      }
  }
\dim_new:N \l_@@_bound_dim
\dim_new:N \l_@@_reverse_bound_dim
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_punct_width_or_ratio:nN}
%    \begin{macrocode}
\cs_new_nopar:Npn \@@_punct_width_or_ratio:nN #1#2
  {
    \dim_compare:nNnTF { \use:c { l_@@_#1_punct_width_dim } } < \c_max_dim
      { \use:c { l_@@_#1_punct_width_dim } }
      {
        \fp_compare:nNnTF { \use:c { l_@@_#1_punct_ratio_fp } } = \c_zero_fp
          { \c_max_dim }
          {
            \fp_use:c { l_@@_#1_punct_ratio_fp }
            \etex_dimexpr:D \@@_use_punct_dim:nn { width } {#2} \scan_stop:
          }
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_margin_width_or_ratio:n}
%    \begin{macrocode}
\cs_new_nopar:Npn \@@_margin_width_or_ratio:n #1
  {
    \dim_compare:nNnTF { \use:c { l_@@_#1_margin_width_dim } } < \c_max_dim
      { \use:c { l_@@_#1_margin_width_dim } }
      {
        \fp_use:c { l_@@_#1_margin_ratio_fp }
        \etex_dimexpr:D \l_@@_bound_dim \scan_stop:
      }
    \bool_if:NT \l_@@_add_min_bound_to_margin_bool
      { + \dim_min:nn \l_@@_bound_dim \l_@@_reverse_bound_dim }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_punct_kerning_process:NN}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \xeCJK_punct_kerning_process:NN #1#2
  {
    \@@_save_punct_dim:nnnn { kern } {#1} {#2}
      {
        \bool_if:nTF
          {
            \l_@@_enabled_global_setting_bool &&
            \tl_if_exist_p:c { g_@@_punct/kern/#1/#2/tl }
          }
          { \tl_use:c { g_@@_punct/kern/#1/#2/tl } }
          {
            \bool_if:NTF \l_@@_enabled_kerning_bool
              { \@@_calc_kerning_margin:NN {#1} {#2} }
              { \@@_original_kerning_margin:NN {#1} {#2} }
          }
          - ( \@@_use_punct_dim:nnn { bound } \c_@@_right_tl {#1} )
          - ( \@@_use_punct_dim:nnn { bound } \c_@@_left_tl  {#2} )
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_original_kerning_margin:NN}
% 相邻两个标点符号之间的本来空白宽度。
%    \begin{macrocode}
\cs_new_nopar:Npn \@@_original_kerning_margin:NN #1#2
  {
    \dim_eval:n
      {
        \@@_use_punct_dim:nnn
          { \@@_punct_if_right:NTF {#1} { glue } { bound } }
          { \c_@@_right_tl } {#1} +
        \@@_use_punct_dim:nnn
          { \@@_punct_if_right:NTF {#2} { bound } { glue } }
          { \c_@@_left_tl  } {#2}
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_calc_kerning_margin:NN}
%    \begin{macrocode}
\cs_new_nopar:Npn \@@_calc_kerning_margin:NN #1#2
  {
    \dim_max:nn
      { \l_@@_kerning_margin_minimum_dim }
      {
        \bool_if:NTF \l_@@_min_bound_to_kerning_bool
          { \@@_punct_min_bound:NN  {#1} {#2} }
          {
            \bool_if:NTF \l_@@_optimize_kerning_bool
              { \dim_max:nn { \@@_punct_min_bound:NN  {#1} {#2} } }
              { \use:n }
              {
                \dim_compare:nNnTF \l_@@_kerning_total_width_dim < \c_max_dim
                  { \@@_calc_kerning_margin:nNN \l_@@_kerning_total_width_dim }
                  {
                    \fp_compare:nNnTF \l_@@_kerning_total_ratio_fp = \c_zero_fp
                      {
                        \xeCJK_if_same_class:NNTF {#1} {#2}
                          { \@@_kerning_width_or_ratio:nNN { same } }
                          { \@@_kerning_width_or_ratio:nNN { different } }
                      }
                      {
                        \@@_calc_kerning_margin:nNN
                          {
                            \fp_use:N \l_@@_kerning_total_ratio_fp
                            \etex_dimexpr:D
                              \@@_use_punct_dim:nn { width } {#1} +
                              \@@_use_punct_dim:nn { width } {#2}
                            \scan_stop:
                          }
                      }
                  }
                  {#1} {#2}
              }
          }
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_kerning_width_or_ratio:nNN}
%    \begin{macrocode}
\cs_new_nopar:Npn \@@_kerning_width_or_ratio:nNN #1#2#3
  {
    \dim_compare:nNnTF { \use:c { l_@@_#1_align_margin_dim } } < \c_max_dim
      { \use:c { l_@@_#1_align_margin_dim } \use_none:nn }
      {
        \fp_compare:nNnTF { \use:c { l_@@_#1_align_ratio_fp } } = \c_zero_fp
          { \use:n }
          { \fp_use:c { l_@@_#1_align_ratio_fp } \use_ii:nn }
      }
      {
        \dim_compare:nNnTF \l_@@_kerning_margin_width_dim < \c_max_dim
          { \l_@@_kerning_margin_width_dim \use_none:n }
          { \fp_use:N \l_@@_kerning_margin_ratio_fp \use:n }
      }
      { \etex_dimexpr:D \@@_original_kerning_margin:NN {#2} {#3} \scan_stop: }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_punct_min_bound:NN}
%    \begin{macrocode}
\cs_new_nopar:Npn \@@_punct_min_bound:NN #1#2
  {
    \dim_max:nn
      {
        \dim_min:nn
          { \@@_use_punct_dim:nnn { bound } \c_@@_left_tl  {#1} }
          { \@@_use_punct_dim:nnn { bound } \c_@@_right_tl {#1} }
      }
      {
        \dim_min:nn
          { \@@_use_punct_dim:nnn { bound } \c_@@_left_tl  {#2} }
          { \@@_use_punct_dim:nnn { bound } \c_@@_right_tl {#2} }
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_calc_kerning_margin:nNN}
% |#2| 和 |#3| 为相邻的两个标点，|#1| 为要确定的相邻两个标点总共占的宽度。
%    \begin{macrocode}
\cs_new_nopar:Npn \@@_calc_kerning_margin:nNN #1#2#3
  {
    \dim_eval:n
      {
        (#1)
        - ( \@@_use_punct_dim:nnn
              { \@@_punct_if_right:NTF {#2} { bound } { glue } }
              { \c_@@_left_tl } {#2} )
        - ( \@@_use_punct_dim:nnn
              { \@@_punct_if_right:NTF {#3} { glue } { bound } }
              { \c_@@_right_tl } {#3} )
        - ( \@@_use_punct_dim:nn { dimen } {#2} )
        - ( \@@_use_punct_dim:nn { dimen } {#3} )
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_get_punct_bounds:NN}
% |#1| 为 \cs{c_@@_left_tl} 或 \cs{c_@@_right_tl}，|#2| 为标点符号。
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \xeCJK_get_punct_bounds:NN #1#2
  {
    \tl_if_exist:cF { \@@_punct_dim_csname:nnn { glue } {#1} {#2} }
      {
        \tl_if_eq:NNTF \l_xeCJK_punct_style_tl \c_@@_punct_style_plain_tl
          {
            \@@_save_punct_dim:nnnn { glue } {#1} {#2} { \c_zero_dim }
            \@@_save_punct_dim:nnnn { bound } \c_@@_left_tl  {#2} { \c_zero_dim }
            \@@_save_punct_dim:nnnn { bound } \c_@@_right_tl {#2} { \c_zero_dim }
          }
          {
            { \xeCJK_select_font: \xeCJK_calc_punct_dimen:f {#2} }
            \@@_punct_if_long:NTF {#2}
              { \@@_save_punct_dim:nnnn { glue } {#1} {#2} { \c_zero_dim } }
              {
                \UseInstance { xeCJK / punctuation }
                  { \l_xeCJK_punct_style_tl } {#1} {#2} { }
              }
          }
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_calc_punct_dimen:N}
% 计算标点的左右实际边距和实际尺寸。对于破折号，计算两标点之间的空白，保证它中间
% 不被断开。
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \xeCJK_calc_punct_dimen:N #1
  {
    \@@_save_punct_dim:nnnn { bound } \c_@@_left_tl {#1}
      { \xeCJK_glyph_bounds:NN \c_one {#1} }
    \@@_save_punct_dim:nnnn { bound } \c_@@_right_tl {#1}
      { \xeCJK_glyph_bounds:NN \c_three {#1} }
    \dim_set:Nn \l_@@_tmp_dim
      {
        ( \@@_use_punct_dim:nnn { bound } \c_@@_left_tl  {#1} ) +
        ( \@@_use_punct_dim:nnn { bound } \c_@@_right_tl {#1} )
      }
    \@@_save_punct_dim:nnn { width } {#1}
      { \etex_fontcharwd:D \tex_font:D \xeCJK_token_value_charcode:N #1 }
    \@@_save_punct_dim:nnn { dimen } {#1}
      { \@@_use_punct_dim:nn { width } {#1} - \l_@@_tmp_dim }
    \@@_punct_if_long:NT {#1}
      {
        \@@_save_punct_dim:nnnn { kern } {#1} {#1}
          {
            \bool_if:nTF
              {
                \str_if_eq_p:nn {#1} { ^^^^2025 } ||
                \str_if_eq_p:nn {#1} { ^^^^2026 }
              }
              { \c_zero_dim }
              { - \l_@@_tmp_dim }
          }
      }
  }
\cs_generate_variant:Nn \xeCJK_calc_punct_dimen:N { f }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_glyph_bounds:NN}
% 用 \cs{XeTeXglyphbounds} 取得标点符号的上下左右空白。
%    \begin{macrocode}
\cs_new_nopar:Npn \xeCJK_glyph_bounds:NN #1#2
  {
    \dim_use:N \XeTeXglyphbounds #1 ~
    \XeTeXcharglyph \xeCJK_token_value_charcode:N #2 \exp_stop_f:
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_get_punct_kerning:NN}
% 标点挤压。
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \xeCJK_get_punct_kerning:NN #1#2
  {
    \tl_if_exist:cF { \@@_punct_dim_csname:nnn { kern } {#1} {#2} }
      {
        \tl_if_eq:NNTF \l_xeCJK_punct_style_tl \c_@@_punct_style_plain_tl
          { \@@_save_punct_dim:nnnn { kern } {#1} {#2} { \c_zero_dim } }
          {
            \UseInstance { xeCJK / punctuation }
              { \l_xeCJK_punct_style_tl } { } {#1} {#2}
          }
      }
  }
\cs_generate_variant:Nn \xeCJK_get_punct_kerning:NN { o }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{PunctStyle}
%    \begin{macrocode}
\keys_define:nn { xeCJK / options }
  {
    PunctStyle .choice: ,
    PunctStyle              .default:n = { quanjiao } ,
    PunctStyle / halfwidth     .meta:n = { PunctStyle = banjiao } ,
    PunctStyle / fullwidth     .meta:n = { PunctStyle = quanjiao } ,
    PunctStyle / mixedwidth    .meta:n = { PunctStyle = kaiming } ,
    PunctStyle / marginkerning .meta:n = { PunctStyle = hangmobanjiao } ,
    PunctStyle / plain         .code:n =
      { \tl_set_eq:NN \l_xeCJK_punct_style_tl \c_@@_punct_style_plain_tl } ,
    PunctStyle / unknown       .code:n =
      {
        \IfInstanceExistTF { xeCJK / punctuation } { \l_keys_value_tl }
          { \tl_set:Nx \l_xeCJK_punct_style_tl { \l_keys_value_tl } }
          { \@@_error:nx { punct-style-unknown } { \l_keys_value_tl } }
      }
  }
\tl_new:N \l_xeCJK_punct_style_tl
\tl_const:Nn \c_@@_punct_style_plain_tl { plain }
\@@_msg_new:nn { punct-style-unknown }
  {
    Punctuation~style~"#1"~is~unknown. \\\\
    The~available~styles~are~listed~as~follow.\\\\
    "plain,~\seq_use:Nnnn \g_@@_punct_style_seq { ~and~ } { ,~ } { ,~and~ }".\\
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\xeCJKDeclarePunctStyle}
% 定义新的标点处理风格，已经存在的同名风格将被覆盖。
%    \begin{macrocode}
\NewDocumentCommand \xeCJKDeclarePunctStyle { > { \TrimSpaces } m m }
  {
    \IfInstanceExistTF { xeCJK / punctuation } {#1}
      { \@@_warning:nx { punct-style-already-defined } {#1} }
      { \seq_gput_right:Nx \g_@@_punct_style_seq {#1} }
    \exp_args:Nnx \DeclareInstance { xeCJK / punctuation } {#1} { basic } {#2}
  }
\seq_new:N \g_@@_punct_style_seq
\@@_msg_new:nn { punct-style-already-defined }
  {
    Punctuation~style~"#1"~is~already~defined!. \\\\
    The~existing~style~of~"#1"~will~be~overwritten.\\
  }
\@onlypreamble \xeCJKDeclarePunctStyle
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\xeCJKEditPunctStyle}
% 对已有的标点处理风格进行修改。
%    \begin{macrocode}
\NewDocumentCommand \xeCJKEditPunctStyle { > { \TrimSpaces } m m }
  {
    \IfInstanceExistTF { xeCJK / punctuation } {#1}
      { \exp_args:Nnx \EditInstance { xeCJK / punctuation } {#1} {#2} }
      { \@@_error:nx { punct-style-unknown } {#1} }
  }
\@onlypreamble \xeCJKEditPunctStyle
%    \end{macrocode}
% \end{macro}
%
% 默认设置即为全角格式。
%    \begin{macrocode}
\xeCJKDeclarePunctStyle { quanjiao } { }
%    \end{macrocode}
%
%    \begin{macrocode}
\xeCJKDeclarePunctStyle { hangmobanjiao } { enabled-kerning = false }
%    \end{macrocode}
%
%    \begin{macrocode}
\xeCJKDeclarePunctStyle { banjiao }
  {
    fixed-punct-ratio   = 0.5  ,
    optimize-margin     = true ,
    kerning-total-ratio = 0.5  ,
    optimize-kerning    = true
  }
%    \end{macrocode}
%
%    \begin{macrocode}
\xeCJKDeclarePunctStyle { kaiming }
  {
    fixed-punct-ratio   = 0.5  ,
    mixed-punct-ratio   = 0.8  ,
    optimize-margin     = true ,
    kerning-total-ratio = 0.5  ,
    optimize-kerning    = true
  }
%    \end{macrocode}
%
%    \begin{macrocode}
\xeCJKDeclarePunctStyle { CCT }
  {
    fixed-punct-ratio   = 0.7  ,
    optimize-margin     = true ,
    kerning-total-ratio = 0.6  ,
    optimize-kerning    = true
  }
%    \end{macrocode}
%
% \subsection{后备字体}
%
% \begin{macro}{AutoFallBack}
% 后备字体的宏包选项声明。
%    \begin{macrocode}
\keys_define:nn { xeCJK / options }
  {
    AutoFallBack .choice: ,
    AutoFallBack / true  .code:n =
      {
        \cs_if_eq:NNF \CJKsymbol \xeCJK_fallback_test_glyph:N
          {
            \cs_set_eq:NN \@@_fallback_save_CJKsymbol:N \CJKsymbol
            \cs_set_eq:NN \CJKsymbol \xeCJK_fallback_test_glyph:N
          }
      } ,
    AutoFallBack / false .code:n =
      {
        \cs_if_eq:NNT \CJKsymbol \xeCJK_fallback_test_glyph:N
          { \cs_set_eq:NN \CJKsymbol \@@_fallback_save_CJKsymbol:N }
      } ,
    AutoFallBack      .default:n = { true } ,
    fallback             .meta:n = { AutoFallBack = true }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_fallback_test_glyph:N}
% 测试当前字体中是否存在当前字符，如存在则直接输出，否则启用后备字体。
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \xeCJK_fallback_test_glyph:N #1
  {
    \xeCJK_glyph_if_exist:NTF {#1}
      { \@@_fallback_save_CJKsymbol:N {#1} }
      {
        \xeCJK_class_group_begin:
        \tl_set_eq:NN \l_@@_fallback_family_tl \l_xeCJK_family_tl
        \xeCJK_fallback_loop:Nn {#1} { \l_@@_fallback_family_tl/FallBack }
        \xeCJK_class_group_end:
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_fallback_loop:Nn}
% \changes{v3.1.0}{2012/11/19}{调整备用字体的循环方式。}
% \changes{v3.2.4}{2013/06/30}
% {使 \cs{CJKfamilydefault} 的 \texttt{FallBack} 设置全局可用。}
% 循环测试后备字体是否包含字符 |#1|。若后备字体中存在该字符或者再没有后备字体，则
% 结束循环。当前字体族没有备用字体时，使用 \cs{CJKfamilydefault} 的设置。
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \xeCJK_fallback_loop:Nn #1#2
  {
    \xeCJK_family_if_exist:xTF {#2}
      {
        \xeCJK_select_font:x {#2}
        \tl_set:Nx \l_@@_fallback_family_tl {#2}
        \xeCJK_glyph_if_exist:NTF {#1}
          { \@@_fallback_save_CJKsymbol:N {#1} }
          { \xeCJK_fallback_loop:Nn {#1} { \l_@@_fallback_family_tl/FallBack } }
      }
      {
        \str_if_eq_x:nnTF { \CJKfamilydefault } { \l_xeCJK_family_tl }
          {
            \@@_warning:nxxx { missing-glyph }
              { \l_@@_fallback_family_tl } {#1}
              { \int_to_hexadecimal:n { `#1 } }
            \@@_fallback_save_CJKsymbol:N {#1}
          }
          {
            \tl_set:Nx \l_xeCJK_family_tl { \CJKfamilydefault }
            \xeCJK_fallback_loop:Nn {#1} { \l_xeCJK_family_tl }
          }
      }
  }
\@@_msg_new:nn { missing-glyph }
  {
    CJKfamily~`\@@_msg_family_map:n {#1}'~
    ( \prop_get:Nn \g_@@_family_font_name_prop {#1} )~
    does~not~contain~glyph~`#2'~(U+#3).\\
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\setCJKfallbackfamilyfont}
%    \begin{macrocode}
\NewDocumentCommand \setCJKfallbackfamilyfont { m O { } m }
  { \use:x { \xeCJK_set_family_fallback:nnn {#1} {#2} {#3} } }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_set_family_fallback:nnn}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \xeCJK_set_family_fallback:nnn #1#2#3
  {
    \group_begin:
    \tl_set:Nn \l_@@_fallback_family_tl {#1}
    \prop_get:NVNF \g_@@_family_font_name_prop
      \l_@@_fallback_family_tl \l_@@_font_name_tl
      { \tl_clear:N \l_@@_font_name_tl }
    \clist_map_inline:nn {#3}
      {
        \tl_put_right:Nn \l_@@_fallback_family_tl { /FallBack }
        \@@_get_sub_features:Vn \l_@@_fallback_family_tl {##1}
        \clist_put_left:Nn \l_@@_sub_font_options_clist {#2}
        \xeCJK_set_family:VVV \l_@@_fallback_family_tl
          \l_@@_sub_font_options_clist \l_@@_sub_font_name_tl
      }
    \group_end:
  }
\tl_new:N \l_@@_fallback_family_tl
%    \end{macrocode}
% \end{macro}
%
%
% \subsection{CJK 字体族声明方式}
%
%    \begin{macrocode}
\bool_new:N \g_@@_auto_fake_bold_bool
\bool_new:N \g_@@_auto_fake_slant_bool
\fp_new:N \g_@@_embolden_factor_fp
\fp_new:N \g_@@_slant_factor_fp
%    \end{macrocode}
%
% \begin{macro}{AutoFakeBold, AutoFakeSlant,EmboldenFactor,SlantFactor}
% 伪粗体和伪斜体的宏包选项声明。
%    \begin{macrocode}
\keys_define:nn { xeCJK / options }
  {
    AutoFakeBold .choices:nn = { true , false }
      { \use:c { bool_gset_ \l_keys_choice_tl :N } \g_@@_auto_fake_bold_bool } ,
    AutoFakeBold / unknown .code:n =
      {
        \bool_gset_true:N  \g_@@_auto_fake_bold_bool
        \fp_gset:Nn \g_@@_embolden_factor_fp { \l_keys_value_tl }
      } ,
    AutoFakeBold  .default:n  = { true } ,
    AutoFakeSlant .choices:nn = { true , false }
      { \use:c { bool_gset_ \l_keys_choice_tl :N } \g_@@_auto_fake_slant_bool } ,
    AutoFakeSlant / unknown  .code:n =
      {
        \bool_gset_true:N  \g_@@_auto_fake_slant_bool
        \fp_gset:Nn \g_@@_slant_factor_fp { \l_keys_value_tl }
      } ,
    AutoFakeSlant  .default:n = { true } ,
    EmboldenFactor .fp_gset:N = \g_@@_embolden_factor_fp ,
    SlantFactor    .fp_gset:N = \g_@@_slant_factor_fp ,
    BoldFont  .meta:n = { AutoFakeBold  = true } ,
    boldfont  .meta:n = { AutoFakeBold  = true } ,
    SlantFont .meta:n = { AutoFakeSlant = true } ,
    slantfont .meta:n = { AutoFakeSlant = true }
  }
%    \end{macrocode}
% \end{macro}
%
% \changes{v3.2.4}{2013/07/02}{内部调整分区字体的设置方法。}
%
% \begin{macro}[internal]{\xeCJK_new_sub_key:n,\g_@@_sub_key_seq}
% 用于定义 CJK 子区字体和备用字体的选项。
%    \begin{macrocode}
\seq_new:N \g_@@_sub_key_seq
\cs_new_protected_nopar:Npn \xeCJK_new_sub_key:n #1
  {
    \seq_gput_right:Nn \g_@@_sub_key_seq {#1}
    \keys_define:nn { xeCJK / features }
      {
        #1 .code:n =
          {
            \tl_if_blank:nTF {##1}
              {
                \prop_clear:N \l_@@_sub_key_prop
                \tl_put_right:Nn \l_@@_family_name_tl { /#1 }
                \clist_remove_all:Nn \l_@@_font_options_clist {#1}
              }
              {
                \str_if_eq:nnTF {##1} { * }
                  { \prop_put:Nnn \l_@@_sub_key_prop {#1} { \q_no_value } }
                  { \@@_get_sub_features:nn {#1} {##1} }
              }
          } ,
        #1 .default:n = { }
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \changes{v3.2.4}{2013/07/02}{改进获取分区字体属性的办法。}
%
% \begin{macro}[internal]{\@@_get_sub_features:nn,\@@_get_sub_features:w}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_get_sub_features:nn #1#2
  {
    \tl_set:Nx \l_@@_tmp_tl { \xeCJK_tl_remove_outer_braces:n {#2} }
    \clist_clear:N \l_@@_sub_font_options_clist
    \exp_after:wN \@@_get_sub_features:w \l_@@_tmp_tl
      \q_mark [ \q_nil ] \q_mark \q_stop
    \tl_if_empty:NTF \l_@@_sub_font_name_tl
      { \tl_set_eq:NN \l_@@_sub_font_name_tl \l_@@_font_name_tl }
      { \tl_replace_all:NnV \l_@@_sub_font_name_tl { * } \l_@@_font_name_tl }
    \prop_put:Nnx \l_@@_sub_key_prop {#1}
      {
        { \exp_not:V \l_@@_sub_font_options_clist }
        { \exp_not:V \l_@@_sub_font_name_tl }
      }
  }
\cs_new_protected_nopar:Npn \@@_get_sub_features:w #1 [#2] #3 \q_mark #4 \q_stop
  {
    \quark_if_nil:nTF {#2}
      { \tl_set_eq:NN \l_@@_sub_font_name_tl \l_@@_tmp_tl }
      {
        \tl_set:Nx \l_@@_sub_font_name_tl
          { \xeCJK_tl_remove_outer_braces:n {#3} }
        \tl_if_empty:NTF \l_@@_sub_font_name_tl
          { \tl_set_eq:NN \l_@@_sub_font_name_tl \l_@@_tmp_tl }
          { \clist_set:Nn \l_@@_sub_font_options_clist {#2} }
      }
  }
\tl_new:N \l_@@_sub_family_name_tl
\tl_new:N \l_@@_sub_font_name_tl
\clist_new:N \l_@@_sub_font_options_clist
\cs_generate_variant:Nn \@@_get_sub_features:nn { V }
\cs_generate_variant:Nn \tl_replace_all:Nnn { NnV }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{FallBack}
%    \begin{macrocode}
\xeCJK_new_sub_key:n { FallBack }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]
%   {\g_@@_features_id_prop, BoldFont, ItalicFont, BoldItalicFont,
%    SlantedFont, BoldSlantedFont,BoldFeatures, ItalicFeatures,
%    BoldItalicFeatures, SlantedFeatures, BoldSlantedFeatures}
% 调用字体的属性声明，同 \pkg{fontspec} 宏包。
%    \begin{macrocode}
\prop_new:N \g_@@_features_id_prop
\prop_put:Nnn \g_@@_features_id_prop { bf   } { Bold        }
\prop_put:Nnn \g_@@_features_id_prop { it   } { Italic      }
\prop_put:Nnn \g_@@_features_id_prop { bfit } { BoldItalic  }
\prop_put:Nnn \g_@@_features_id_prop { sl   } { Slanted     }
\prop_put:Nnn \g_@@_features_id_prop { bfsl } { BoldSlanted }
\prop_map_inline:Nn \g_@@_features_id_prop
  {
    \keys_define:nn { xeCJK / features }
      {
        #2Font        .tl_set:c = { l_@@_font_name_#1_tl } ,
        #2Features .clist_set:c = { l_@@_font_feat_#1_clist }
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{AutoFakeBold, AutoFakeSlant}
%    \begin{macrocode}
\keys_define:nn { xeCJK / features }
  {
    AutoFakeBold  .choice: ,
    AutoFakeBold / false   .code:n =
      { \bool_set_false:N \l_@@_auto_fake_bold_bool } ,
    AutoFakeBold / unknown .code:n =
      {
        \bool_set_true:N \l_@@_auto_fake_bold_bool
        \fp_set:Nn \l_@@_embolden_factor_fp { \l_keys_value_tl }
      } ,
    AutoFakeBold .default:n  = { \g_@@_embolden_factor_fp } ,
    AutoFakeSlant  .choice: ,
    AutoFakeSlant / false   .code:n =
      { \bool_set_false:N \l_@@_auto_fake_slant_bool } ,
    AutoFakeSlant / unknown .code:n =
      {
        \bool_set_true:N \l_@@_auto_fake_slant_bool
        \fp_set:Nn \l_@@_slant_factor_fp { \l_keys_value_tl }
      } ,
    AutoFakeSlant .default:n  = { \g_@@_slant_factor_fp }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_set_family_initial:}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_set_family_initial:
  {
    \int_gincr:N \g_@@_family_int
    \prop_clear:N \l_@@_add_font_prop
    \prop_clear:N \l_@@_sub_key_prop
    \prop_map_inline:Nn \g_@@_features_id_prop
      {
        \tl_clear:c { l_@@_font_name_##1_tl }
        \clist_clear:c { l_@@_font_feat_##1_clist }
      }
    \clist_clear:N \l_@@_fontspec_options_clist
    \bool_set_eq:NN \l_@@_auto_fake_bold_bool   \g_@@_auto_fake_bold_bool
    \bool_set_eq:NN \l_@@_auto_fake_slant_bool  \g_@@_auto_fake_slant_bool
    \fp_set_eq:NN \l_@@_embolden_factor_fp \g_@@_embolden_factor_fp
    \fp_set_eq:NN \l_@@_slant_factor_fp    \g_@@_slant_factor_fp
  }
\int_new:N \g_@@_family_int
\prop_new:N \l_@@_add_font_prop
\prop_new:N \l_@@_sub_key_prop
\clist_new:N \l_@@_fontspec_options_clist
\bool_new:N \l_@@_auto_fake_bold_bool
\bool_new:N \l_@@_auto_fake_slant_bool
\fp_new:N \l_@@_embolden_factor_fp
\fp_new:N \l_@@_slant_factor_fp
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_set_family:nnn}
% 设置一个 CJK 新字体族，与 \cs{newfontfamily} 类似，增加 |FallBack| 选项。
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \xeCJK_set_family:nnn #1#2#3
  {
    \group_begin:
    \@@_set_family_initial:
    \tl_set:Nn \l_@@_family_name_tl {#1}
    \clist_set:Nn \l_@@_font_options_clist {#2}
    \tl_set:Nn \l_@@_font_name_tl {#3}
    \clist_concat:NNN \l_@@_font_options_clist
      \g_@@_default_features_clist \l_@@_font_options_clist
    \@@_remove_duplicate_keys:N \l_@@_font_options_clist
    \keys_set_known:nVN { xeCJK / features }
      \l_@@_font_options_clist \l_@@_fontspec_options_clist
    \@@_parse_features:
    \@@_pass_features:
    \@@_check_family:V \l_@@_family_name_tl
    \@@_gset_family_cs:x { \l_@@_family_name_tl }
    \@@_save_family_info:
    \@@_set_sub_block_family:
    \group_end:
  }
\tl_new:N \l_@@_family_name_tl
\tl_new:N \l_@@_font_name_tl
\clist_new:N \l_@@_font_options_clist
\cs_generate_variant:Nn \xeCJK_set_family:nnn { Vnn , VVV , Voo }
\cs_new_protected_nopar:Npn \xeCJK_set_family:xxx #1#2#3
  { \use:x { \xeCJK_set_family:nnn {#1} {#2} {#3} } }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_remove_duplicate_keys:N}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_remove_duplicate_keys:N #1
  {
    \prop_clear:N \l_@@_font_options_prop
    \keyval_parse:NNV \@@_prop_put_aux:n \@@_prop_put_aux:nn #1
    \clist_clear:N #1
    \prop_map_inline:Nn \l_@@_font_options_prop
      {
        \tl_set:No \l_@@_tmp_tl { \use_ii:nn ##2 }
        \tl_if_blank:VTF \l_@@_tmp_tl
          { \clist_put_right:No #1 { \use_i:nn ##2 } }
          {
            \clist_put_right:Nx #1
              { \exp_not:o { \use_i:nn ##2 } = { \exp_not:V \l_@@_tmp_tl } }
          }
      }
  }
\prop_new:N \l_@@_font_options_prop
\cs_generate_variant:Nn \keyval_parse:NNn { NNV }
\cs_new_protected_nopar:Npn \@@_prop_put_aux:n #1
  { \prop_put:Nnn \l_@@_font_options_prop {#1} { {#1} {  } } }
\cs_new_protected_nopar:Npn \@@_prop_put_aux:nn #1#2
  { \prop_put:Nnn \l_@@_font_options_prop {#1} { {#1} {#2} } }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_gset_family_cs:x}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_gset_family_cs:x #1
  {
    \cs_gset_protected_nopar:cpx { \@@_family_csname:n {#1} }
      {
        \group_begin:
        \exp_not:n { \cs_set_eq:NN \@@_update_family:n \use_none:n }
        \exp_not:n { \fontspec_set_family:Nnn \l_@@_fontspec_family_tl }
          { \exp_not:V \l_@@_fontspec_options_clist }
          { \exp_not:V \l_@@_font_name_tl }
        \@@_gset_family_nfss_cs:xx {#1} { \exp_not:N \l_@@_fontspec_family_tl }
        \group_end:
      }
  }
\tl_new:N \l_@@_fontspec_family_tl
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_check_family:n}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_check_family:n #1
  {
    \prop_get:NnNT \g_@@_family_font_name_prop {#1} \l_@@_tmp_tl
      {
        \prop_gpop:NnNT \g_@@_family_name_prop {#1} \l_@@_tmp_tl
          { \cs_undefine:c { \@@_family_nfss_csname:n {#1} } }
        \@@_warning:nxx { CJKfamily-redef } {#1} { \l_@@_tmp_tl }
      }
  }
\cs_generate_variant:Nn \@@_check_family:n { V }
\@@_msg_new:nn { CJKfamily-redef }
  { Redefining~CJKfamily~`\@@_msg_family_map:n {#1}'~(#2). }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_add_font:nn,\@@_add_font_if_new:nn}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_add_font:nn #1#2
  { \prop_put:Nnn \l_@@_add_font_prop { #1 Font } {#2} }
\cs_new_protected_nopar:Npn \@@_add_font_if_new:nn #1#2
  { \prop_put_if_new:Nnn \l_@@_add_font_prop { #1 Font } {#2} }
\cs_generate_variant:Nn \@@_add_font:nn        { nx , nV , nv }
\cs_generate_variant:Nn \@@_add_font_if_new:nn { nx , nV , nv }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_add_fake_bold:n,\@@_add_fake_slant:n}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_add_fake_bold:n #1
  {
    \clist_put_left:cx { l_@@_font_feat_#1_clist }
      { FakeBold = { \fp_use:N \l_@@_embolden_factor_fp } }
  }
\cs_new_protected_nopar:Npn \@@_add_fake_slant:n #1
  {
    \clist_put_left:cx { l_@@_font_feat_#1_clist }
      { FakeSlant = { \fp_use:N \l_@@_slant_factor_fp } }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_parse_features:,\@@_parse_features:nn}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_parse_features:
  { \prop_map_function:NN \g_@@_features_id_prop \@@_parse_features:nn }
\cs_new_protected_nopar:Npn \@@_parse_features:nn #1#2
  {
    \@@_if_font_select:nTF {#1}
      {
        \@@_add_font:nv {#2} { l_@@_font_name_#1_tl }
        \@@_if_it_or_sl:nTF {#1}
          {
            \@@_if_font_select:nF { bf #1 }
              { \@@_add_font_if_new:nv {#2} { l_@@_font_name_#1_tl }  }
          }
          {
            \str_if_eq:nnT {#1} { bf }
              {
                \@@_if_font_select:nF { bfit }
                  { \@@_add_font:nV { BoldItalic } \l_@@_font_name_bf_tl }
                \@@_if_font_select:nF { bfsl }
                  { \@@_add_font:nV { BoldSlanted } \l_@@_font_name_bf_tl }
              }
          }
      }
      { \@@_set_fake_font:nn {#1} {#2} }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[pTF,internal]{\@@_if_font_select:n,\@@_if_it_or_sl:n}
%    \begin{macrocode}
\prg_new_conditional:Npnn \@@_if_font_select:n #1 { p , T , F , TF }
  {
    \tl_if_blank:vTF { l_@@_font_name_#1_tl }
      { \prg_return_false: } { \prg_return_true: }
  }
\prg_new_conditional:Npnn \@@_if_it_or_sl:n #1 { p , T , F , TF }
  {
    \if_predicate:w
      \bool_if_p:n { \str_if_eq_p:nn { it } {#1} || \str_if_eq_p:nn { sl } {#1} }
      \prg_return_true: \else: \prg_return_false: \fi:
  }
\cs_generate_variant:Nn \tl_if_blank:nTF { v }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_set_fake_font:nn}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_set_fake_font:nn #1#2
  {
    \str_if_eq:nnTF {#1} { bf }
      { \bool_if:NT \l_@@_auto_fake_bold_bool { \@@_add_fake_bold:n {#1} } }
      {
        \bool_if:NTF \l_@@_auto_fake_slant_bool
          {
            \bool_if:nT
              {    \@@_if_it_or_sl_p:n {#1}
                || ( \str_if_eq_p:nn {#1} { bfit }
                     && ! ( \@@_if_font_select_p:n { it } ) )
                || ( \str_if_eq_p:nn {#1} { bfsl }
                     && ! ( \@@_if_font_select_p:n { sl } ) )
              }
              { \@@_add_fake_slant:n {#1} }
          }
          { \@@_if_it_or_sl:nT {#1} { \@@_map_it_sl:nn {#1} {#2} } }
        \bool_if:nT
          {      \l_@@_auto_fake_bold_bool
            && ! ( \@@_if_it_or_sl_p:n {#1} )
            && ! ( \@@_if_font_select_p:n { bf } )
          }
          { \@@_add_fake_bold:n {#1} }
      }
    \@@_add_font_if_new:nn {#2} { * }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_map_it_sl:n}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_map_it_sl:nn #1#2
  {
    \@@_if_map_font_select:nT {#1}
      {
        \@@_add_font:nx {#2} { \@@_get_map_font:n {#1} }
        \@@_if_font_select:nF { bf #1 }
          { \@@_add_font_if_new:nx { Bold #2 } { \@@_get_map_font:n {#1} } }
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_get_map_font:n}
%    \begin{macrocode}
\cs_new_nopar:Npn \@@_get_map_font:n #1
  { \exp_not:v { l_@@_font_name_ \xeCJK_reverse:nnn {#1} { it } { sl } _tl } }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[pTF,internal]{\@@_if_map_font_select:n}
%    \begin{macrocode}
\prg_new_conditional:Npnn \@@_if_map_font_select:n #1 { p , T , F , TF }
  {
    \xeCJK_if_blank_x:nTF { \@@_get_map_font:n {#1} }
      { \prg_return_false: } { \prg_return_true: }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_pass_features:}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_pass_features:
  {
    \prop_map_inline:Nn \g_@@_features_id_prop
      {
        \clist_if_empty:cF { l_@@_font_feat_##1_clist }
          {
            \clist_put_right:Nx \l_@@_fontspec_options_clist
              { ##2Features = { \exp_not:v { l_@@_font_feat_##1_clist } } }
          }
      }
    \prop_map_inline:Nn \l_@@_add_font_prop
      { \clist_put_right:Nn \l_@@_fontspec_options_clist { ##1 = {##2} } }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal,var]
%  {\g_@@_family_name_prop,\g_@@_family_font_name_prop,\g_@@_family_font_options_prop}
%    \begin{macrocode}
\prop_new:N \g_@@_family_name_prop
\prop_new:N \g_@@_family_font_name_prop
\prop_new:N \g_@@_family_font_options_prop
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_save_family_info:}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_save_family_info:
  {
    \group_begin:
    \prop_gput:NVV \g_@@_family_font_name_prop
      \l_@@_family_name_tl \l_@@_font_name_tl
    \prop_gput:NVV \g_@@_family_font_options_prop
      \l_@@_family_name_tl \l_@@_font_options_clist
    \group_end:
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_set_sub_block_family:}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_set_sub_block_family:
  {
    \prop_map_inline:Nn \l_@@_sub_key_prop
      {
        \tl_set:Nx \l_@@_sub_family_name_tl { \l_@@_family_name_tl/##1 }
        \quark_if_no_value:nTF {##2}
          { \@@_copy_sub_family:n {##1} }
          {
            \xeCJK_set_family:Voo \l_@@_sub_family_name_tl
              { \use_i:nn ##2 } { \use_ii:nn ##2 }
          }
      }
  }
\cs_new_protected_nopar:Npn \@@_copy_sub_family:n #1
  {
    \@@_check_family:V \l_@@_sub_family_name_tl
    \prop_get:NVNT \g_@@_family_font_name_prop
      \l_@@_family_name_tl \l_@@_sub_font_name_tl
      {
        \prop_gput:NVV \g_@@_family_font_name_prop
          \l_@@_sub_family_name_tl \l_@@_sub_font_name_tl
      }
    \prop_get:NVNT \g_@@_family_font_options_prop
      \l_@@_family_name_tl \l_@@_sub_font_options_clist
      {
        \clist_remove_all:Nn \l_@@_sub_font_options_clist { #1 = * }
        \prop_gput:NVV \g_@@_family_font_options_prop
          \l_@@_sub_family_name_tl \l_@@_sub_font_options_clist
      }
    \cs_gset_protected_nopar:cpx
      { \@@_family_csname:n { \l_@@_sub_family_name_tl } }
      {
        \xeCJK_family_if_exist:xT { \l_@@_family_name_tl }
          {
            \prop_get:NnNT \exp_not:N \g_@@_family_name_prop
              { \l_@@_family_name_tl } \exp_not:N \l_@@_fontspec_family_tl
              {
                \@@_gset_family_nfss_cs:xx
                  { \l_@@_sub_family_name_tl }
                  { \exp_not:N \l_@@_fontspec_family_tl }
              }
          }
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_copy_family:nn}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_copy_family:nn #1#2
  {
    \xeCJK_family_if_exist:xT {#2}
      {
        \tl_map_inline:nn
          {
            \g_@@_family_name_prop
            \g_@@_family_font_name_prop
            \g_@@_family_font_options_prop
          }
          {
            \prop_get:NnNT ##1 {#2} \l_@@_tmp_tl
              { \prop_gput:NnV ##1 {#1} \l_@@_tmp_tl }
          }
        \cs_gset_eq:cc
          { \@@_family_nfss_csname:n {#1} }
          { \@@_family_nfss_csname:n {#2} }
      }
  }
\cs_generate_variant:Nn \@@_copy_family:nn { xx }
%    \end{macrocode}
% \end{macro}
%
% \subsection{字体切换}
%
% \begin{macro}[internal]{\l_xeCJK_current_font_tl,\xeCJK_select_font:,\xeCJK_select_font:x}
% 缓存当前字体的原始格式，以加速编译。
%    \begin{macrocode}
\tl_new:N \l_xeCJK_current_font_tl
\tl_set:Nn \l_xeCJK_current_font_tl { \@@_font_csname:n { \l_xeCJK_family_tl } }
\cs_new_nopar:Npn \@@_font_csname:n #1 { xeCJK/#1/\f@series/\f@shape/\f@size }
\cs_new_protected_nopar:Npn \xeCJK_select_font:
  {
    \cs_if_exist_use:cF { \l_xeCJK_current_font_tl }
      {
        \tl_set:Nx \l_@@_current_coor_tl { \l_xeCJK_current_font_tl }
        \@@_family_use:x { \l_xeCJK_family_tl }
        \xeCJK_font_gset_to_current:c { \l_@@_current_coor_tl }
      }
  }
\cs_new_protected_nopar:Npn \xeCJK_select_font:x #1
  {
    \cs_if_exist_use:cF { \@@_font_csname:n {#1} }
      {
        \tl_set:Nx \l_@@_current_coor_tl { \@@_font_csname:n {#1} }
        \@@_family_use:x {#1}
        \xeCJK_font_gset_to_current:c { \l_@@_current_coor_tl }
      }
  }
\tl_new:N \l_@@_current_coor_tl
\cs_new_eq:NN \xeCJK@setfont \xeCJK_select_font:
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_switch_font:nn}
% \changes{v3.1.0}{2012/11/18}{改进定义，加快切换速度。}
% 两个 CJK 分区之间的字体切换。
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_switch_font:nn #1#2
  {
    \str_if_eq:nnF {#1} {#2}
      {
        \@@_info:nxx { CJK-block } {#1} {#2}
        \str_if_eq:nnTF {#2} { CJK }
          { \xeCJK_select_font: }
          { \@@_block_select_font:n {#2} }
      }
  }
\@@_msg_new:nn { CJK-block } { Switch~from~block~`#1'~to~`#2'. }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_block_select_font:n}
% 若当前 CJK 字体族没有定义子分区 |#1| 的字体，则使用 \cs{CJKfamilydefault} 的对应
% 分区字体；若 \cs{CJKfamilydefault} 也没有定义该分区字体，则使用当前 CJK 字体族的
% 主分区字体。
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_block_select_font:n #1
  {
    \cs_if_exist_use:cF { \@@_font_csname:n { \l_xeCJK_family_tl/#1 } }
      {
        \tl_set:Nx \l_@@_current_coor_tl
          { \@@_font_csname:n { \l_xeCJK_family_tl/#1 } }
        \xeCJK_family_if_exist:xF { \l_xeCJK_family_tl/#1 }
          {
            \@@_copy_family:xx { \l_xeCJK_family_tl/#1 }
              {
                \cs_if_exist:cTF
                  { \@@_family_csname:n { \CJKfamilydefault/#1 } }
                  { \CJKfamilydefault/#1 } { \l_xeCJK_family_tl }
              }
          }
        \@@_family_use:x { \l_xeCJK_family_tl/#1 }
        \xeCJK_font_gset_to_current:c { \l_@@_current_coor_tl }
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]
%   {\@@_family_csname:n,\@@_family_nfss_csname:n,
%    \@@_family_use:x,\@@_gset_family_nfss_cs:xx}
%    \begin{macrocode}
\cs_new_nopar:Npn \@@_family_csname:n #1 { xeCJK/family/#1 }
\cs_new_nopar:Npn \@@_family_nfss_csname:n #1 { xeCJK/family/nfss/#1 }
\cs_new_nopar:Npn \@@_family_use:x #1 { \use:c { \@@_family_nfss_csname:n {#1} } }
\cs_new_protected_nopar:Npn \@@_gset_family_nfss_cs:xx #1#2
  {
    \prop_gput:Nxx \g_@@_family_name_prop {#1} {#2}
    \cs_gset_protected_nopar:cpx { \@@_family_nfss_csname:n {#1} }
      {
        \exp_not:N \fontencoding { \c_@@_encoding_tl }
        \tl_set:Nx \exp_not:N \f@family {#2}
        \exp_not:N \selectfont
      }
  }
\cs_generate_variant:Nn \prop_gput:Nnn { Nxx }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[TF,internal]{\xeCJK_family_if_exist:x}
%    \begin{macrocode}
\prg_new_protected_conditional:Npnn \xeCJK_family_if_exist:x #1 { T , F , TF }
  {
    \cs_if_exist:cTF { \@@_family_nfss_csname:n {#1} }
      { \use_i:nn }
      { \cs_if_exist_use:cTF { \@@_family_csname:n {#1} } }
      { \prg_return_true: } { \prg_return_false: }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\CJKfamily}
% 用于切换 CJK 字体族。
%    \begin{macrocode}
\NewDocumentCommand \CJKfamily { t+ t- m }
  {
    \xeCJK_if_blank_x:nTF {#3}
      {
        \IfBooleanF {#1} { \IfBooleanF {#2} { \use_none:nn } }
        \xeCJK_family_if_exist_use:x { \l_xeCJK_family_tl }
      }
      {
        \IfBooleanTF {#2} { \xeCJK_family_if_exist_use:x {#3} }
          {
            \xeCJK_family_if_exist:xTF {#3}
              {
                \tl_set:Nx \l_xeCJK_family_tl {#3}
                \tl_set_eq:NN \xeCJK@family \l_xeCJK_family_tl
                \IfBooleanT {#1} { \@@_family_use:x {#3} }
              }
              { \@@_family_unknown_warning:x {#3} }
          }
      }
    \tex_ignorespaces:D
  }
\cs_new_protected_nopar:Npn \xeCJK_switch_family:n #1
  {
    \xeCJK_family_if_exist:xTF {#1}
      {
        \tl_set:Nx \l_xeCJK_family_tl {#1}
        \tl_set_eq:NN \xeCJK@family \l_xeCJK_family_tl
      }
      { \@@_family_unknown_warning:x {#1} }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[var,internal]{\l_xeCJK_family_tl}
% 用于保存文档当前正在使用的 CJK 字体族。
% \changes{v3.2.0}{2013/04/14}{不将其初始化为 \cs{CJKfamilydefault}。}
%    \begin{macrocode}
\tl_new:N \l_xeCJK_family_tl
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_gobble_CJKfamily:}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_gobble_CJKfamily:
  { \cs_set_eq:NN \CJKfamily \@@_gobble_CJKfamily:wn }
\DeclareExpandableDocumentCommand \@@_gobble_CJKfamily:wn { t+ t- m } {  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_family_if_exist_use:x}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \xeCJK_family_if_exist_use:x #1
  {
    \xeCJK_family_if_exist:xTF {#1}
      { \@@_family_use:x {#1} }
      { \@@_family_unknown_warning:x {#1} }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_family_unknown_warning:n}
% \changes{v3.1.2}{2013/01/01}
% {在没有定义任何 CJK 字体的情况下，不再重复给出字体没有定义的警告。}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_family_unknown_warning:n #1
  {
    \prop_if_empty:NF \g_@@_family_font_name_prop
      {
        \seq_if_in:NnF \g_@@_unknown_family_seq {#1}
          {
            \seq_gput_right:Nn \g_@@_unknown_family_seq {#1}
            \@@_warning:nx { CJKfamily-Unknown } {#1}
          }
      }
  }
\cs_generate_variant:Nn \@@_family_unknown_warning:n { x }
\seq_new:N \g_@@_unknown_family_seq
\@@_msg_new:nn { CJKfamily-Unknown }
  {
    Unknown~CJK~family~`\@@_msg_family_map:n {#1}'~is~being~ignored.\\\\
    Try~to~use~`\@@_msg_def_family_map:n {#1}'~to~define~it.
  }
\cs_new_nopar:Npn \@@_msg_def_family_map:n #1
  {
    \str_case_x:nnn {#1}
      {
        \CJKrmdefault { \token_to_str:N \setCJKmainfont }
        \CJKsfdefault { \token_to_str:N \setCJKsansfont }
        \CJKttdefault { \token_to_str:N \setCJKmonofont }
      }
      { \token_to_str:N \setCJKfamilyfont {#1} }
    [...]{...}
  }
\cs_new_nopar:Npn \@@_msg_family_map:n #1
  {
    \str_case_x:nnn {#1}
      {
        \CJKrmdefault { \token_to_str:N \CJKrmdefault }
        \CJKsfdefault { \token_to_str:N \CJKsfdefault }
        \CJKttdefault { \token_to_str:N \CJKttdefault }
      }
      {#1}
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\setCJKmainfont,\setCJKsansfont,\setCJKmonofont}
% 设置文档的 CJK 普通字体、无衬线和等宽字体。
% \changes{v3.2.0}{2013/04/14}{定义中加入 \cs{normalfont}。}
%    \begin{macrocode}
\NewDocumentCommand \setCJKmainfont { O { } m }
  {
    \xeCJK_set_family:xxx { \CJKrmdefault } {#1} {#2}
    \normalfont
  }
\cs_new_eq:NN \setCJKromanfont \setCJKmainfont
\NewDocumentCommand \setCJKsansfont { O { } m }
  {
    \xeCJK_set_family:xxx { \CJKsfdefault } {#1} {#2}
    \normalfont
  }
\NewDocumentCommand \setCJKmonofont { O { } m }
  {
    \xeCJK_set_family:xxx { \CJKttdefault } {#1} {#2}
    \normalfont
  }
%    \end{macrocode}
% \end{macro}
%
%    \begin{macrocode}
\@onlypreamble \setCJKmainfont
\@onlypreamble \setCJKmathfont
\@onlypreamble \setCJKsansfont
\@onlypreamble \setCJKmonofont
\@onlypreamble \setCJKromanfont
%    \end{macrocode}
%
% \begin{macro}{\setCJKfamilyfont, \newCJKfontfamily, \CJKfontspec}
% 分别用于预声明 CJK 字体和随机调用 CJK 字体。
%    \begin{macrocode}
\NewDocumentCommand \setCJKfamilyfont { m O { } m }
  { \xeCJK_set_family:xxx {#1} {#2} {#3} }
\NewDocumentCommand \newCJKfontfamily { o m O { } m }
  {
    \tl_set:Nx \l_@@_tmp_tl { \IfNoValueTF {#1} { \cs_to_str:N #2 } {#1} }
    \cs_new_protected_nopar:Npx #2 { \xeCJK_switch_family:n { \l_@@_tmp_tl } }
    \xeCJK_set_family:xxx { \l_@@_tmp_tl } {#3} {#4}
  }
\NewDocumentCommand \CJKfontspec { O { } m }
  {
    \use:x { \xeCJK_fontspec:nn {#1} {#2} }
    \tex_ignorespaces:D
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_fontspec:nn}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \xeCJK_fontspec:nn #1#2
  {
    \prop_get:NnNTF \g_@@_fontspec_prop
      { CJKfontspec/#1/#2/id } \l_xeCJK_family_tl
      { \tl_set_eq:NN \xeCJK@family \l_xeCJK_family_tl }
      {
        \@@_fontspec:xnn
          { CJKfontspec ( \int_eval:n { \g_@@_family_int + \c_one } ) }
          {#1} {#2}
      }
  }
\cs_new_protected_nopar:Npn \@@_fontspec:nnn #1#2#3
  {
    \prop_gput:Nnn \g_@@_fontspec_prop { CJKfontspec/#2/#3/id } {#1}
    \xeCJK_set_family:nnn {#1} {#2} {#3}
    \xeCJK_switch_family:n {#1}
  }
\cs_generate_variant:Nn \xeCJK_fontspec:nn { VV }
\cs_generate_variant:Nn \@@_fontspec:nnn { x }
\prop_new:N \g_@@_fontspec_prop
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\defaultCJKfontfeatures, \addCJKfontfeatures}
% \changes{v3.2.4}{2013/06/30}{可以单独增加当前各个分区字体的属性。}
% 分别用于设置 CJK 字体的默认属性和增加当前 CJK 字体的属性。
%    \begin{macrocode}
\clist_new:N \g_@@_default_features_clist
\NewDocumentCommand \defaultCJKfontfeatures { m }
  { \clist_gset:Nn \g_@@_default_features_clist {#1} }
\@onlypreamble \defaultCJKfontfeatures
\NewDocumentCommand \addCJKfontfeatures { s O { } m }
  {
    \xeCJK_add_font_features:Nxx #1 {#2} {#3}
    \tex_ignorespaces:D
  }
\cs_new_eq:NN \addCJKfontfeature \addCJKfontfeatures
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_add_font_features:Nnn}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \xeCJK_add_font_features:Nnn #1#2#3
  {
    \prop_get:NVNTF \g_@@_family_font_name_prop
      \l_xeCJK_family_tl \l_@@_font_name_tl
      {
        \clist_set:Nn \l_@@_add_font_features_clist {#3}
        \seq_map_inline:Nn \g_@@_sub_key_seq
          { \clist_remove_all:Nn \l_@@_add_font_features_clist {##1} }
        \seq_clear:N \l_@@_sub_key_seq
        \clist_clear:N \l_@@_add_block_features_clist
        \clist_map_inline:nn {#2}
          {
            \seq_if_in:NnTF \g_@@_sub_key_seq {##1}
              {
                \seq_put_right:Nn \l_@@_sub_key_seq {##1}
                \@@_add_sub_class_features:n {##1}
              }
              { \@@_warning:nx { SubBlock-undefined } {##1} }
          }
        \bool_if:nT { #1 && \seq_if_empty_p:N \l_@@_sub_key_seq }
          {
            \seq_map_function:NN
              \g_@@_sub_key_seq \@@_add_sub_class_features:n
          }
        \prop_get:NVNT \g_@@_family_font_options_prop
          \l_xeCJK_family_tl \l_@@_font_options_clist
          {
            \bool_if:nT
              { \seq_if_empty_p:N \l_@@_sub_key_seq || #1 }
              {
                \clist_concat:NNN \l_@@_font_options_clist
                  \l_@@_font_options_clist \l_@@_add_font_features_clist
              }
            \clist_concat:NNN \l_@@_font_options_clist
              \l_@@_font_options_clist \l_@@_add_block_features_clist
          }
        \xeCJK_fontspec:VV \l_@@_font_options_clist \l_@@_font_name_tl
      }
      { \@@_warning:n { addCJKfontfeature-ignored } }
  }
\clist_new:N \l_@@_add_font_features_clist
\clist_new:N \l_@@_add_block_features_clist
\cs_generate_variant:Nn \xeCJK_add_font_features:Nnn { Nxx , Nnx }
\@@_msg_new:nn { addCJKfontfeature-ignored }
  {
    \token_to_str:N \addCJKfontfeature (s)~ignored.\\\\
    It~cannot~be~used~with~a~font~that~wasn't~selected~by~xeCJK.
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_add_sub_class_features:n}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_add_sub_class_features:n #1
  {
    \prop_get:NoNTF \g_@@_family_font_name_prop
      { \l_xeCJK_family_tl/#1 } \l_@@_sub_font_name_tl
      {
        \prop_get:NoN \g_@@_family_font_options_prop
          { \l_xeCJK_family_tl/#1 } \l_@@_sub_font_options_clist
      }
      {
        \prop_get:NxNTF \g_@@_family_font_name_prop
          { \CJKfamilydefault/#1 } \l_@@_sub_font_name_tl
          {
            \prop_get:NxN \g_@@_family_font_options_prop
              { \CJKfamilydefault/#1 } \l_@@_sub_font_options_clist
          }
          {
            \prop_get:NVN \g_@@_family_font_options_prop
              \l_xeCJK_family_tl \l_@@_sub_font_options_clist
            \tl_set_eq:NN \l_@@_sub_font_name_tl \l_@@_font_name_tl
          }
      }
    \clist_concat:NNN \l_@@_sub_font_options_clist
      \l_@@_sub_font_options_clist \l_@@_add_font_features_clist
    \clist_put_right:Nx \l_@@_add_block_features_clist
      {
        #1 =
          {
            [ \exp_not:V \l_@@_sub_font_options_clist ]
            { \exp_not:V \l_@@_sub_font_name_tl }
          }
      }
  }
\cs_generate_variant:Nn \prop_get:NnN   { Nx }
\cs_generate_variant:Nn \prop_get:NnNTF { Nx }
%    \end{macrocode}
% \end{macro}
%
% \changes{v3.1.2}{2013/01/01}{修正重定义 \cs{CJKfamilydefault} 无效的问题，恢复容错能力。}
%
% 在导言区结束的时候，若没有声明 CJK 字体，则给出一个警告。如果 \cs{CJKfamilydefault}
% 没有被更改，则在此时根据西文字体的情况更新 \cs{CJKfamilydefault}。
% 如果 \cs{CJKfamilydefault} 对应的字体族没有定义，则使用 \cs{CJKrmdefault} 作为
% 默认字体族。若 \cs{CJKrmdefault} 也没有定义，则使用在导言区设置的第一个 CJK 字体
% 作为默认字体族。最后设置数学字体。
%    \begin{macrocode}
\@@_at_end_preamble:n
  {
    \cs_set_eq:NN \@@_family_default_wrap:n \exp_not:n
    \tl_if_eq:NNT \CJKfamilydefault \l_@@_family_default_init_tl
      {
        \tl_gset:Nx \CJKfamilydefault
          {
            \str_case_x:nnn { \familydefault }
              {
                { \rmdefault } { \exp_not:N \CJKrmdefault }
                { \sfdefault } { \exp_not:N \CJKsfdefault }
                { \ttdefault } { \exp_not:N \CJKttdefault }
              }
              { \CJKfamilydefault }
          }
      }
    \cs_undefine:N \@@_family_default_wrap:n
    \prop_if_empty:NTF \g_@@_family_font_name_prop
      { \@@_warning:nx { no-CJKfamily } { \CJKfamilydefault } }
      {
        \xeCJK_family_if_exist:xF { \CJKfamilydefault }
          {
            \tl_set_eq:NN \l_@@_tmp_tl \CJKfamilydefault
            \str_if_eq_x:nnTF { \CJKfamilydefault } { \CJKrmdefault }
              { \use:n }
              {
                \xeCJK_family_if_exist:xTF { \CJKrmdefault }
                  { \tl_gset:Nn \CJKfamilydefault { \CJKrmdefault } }
              }
              {
                \prop_map_inline:Nn \g_@@_family_font_name_prop
                  {
                    \prop_map_break:n
                      { \tl_gset_rescan:Nnn \CJKfamilydefault { } {#1} }
                  }
              }
            \@@_warning:nxx { CJKfamilydefault-undefined }
              { \l_@@_tmp_tl } { \CJKfamilydefault }
          }
        \xeCJK_switch_family:n { \CJKfamilydefault }
        \bool_if:NT \g_@@_math_bool { \xeCJK_set_mathfont: }
      }
  }
\@@_msg_new:nn { no-CJKfamily }
  {
    It~seems~that~you~have~not~declare~a~CJKfamily.\\
    If~you~want~to~use~xeCJK~in~the~right~way,~you~should~use\\\\
    `\@@_msg_def_family_map:n {#1}'\\\\
    in~the~preamble~to~declare~the~default~CJKfamily.\\
  }
\@@_msg_new:nn { CJKfamilydefault-undefined }
  {
    Undefined~CJK~default~family~`\@@_msg_family_map:n {#1}'~
    has~been~replaced~by~`\@@_msg_family_map:n {#2}'.\\\\
    Try~to~use~`\@@_msg_def_family_map:n {#1}'~to~define~it.
  }
%    \end{macrocode}
%
% \subsection{数学字体设置}
%
% \begin{macro}{CJKmath}
% 是否启用 CJK 数学字体的宏包选项。
%    \begin{macrocode}
\keys_define:nn { xeCJK / options } { CJKmath .bool_gset:N = \g_@@_math_bool }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\setCJKmathfont}
% 设置 CJK 数学字体。
%    \begin{macrocode}
\NewDocumentCommand \setCJKmathfont { O { } m }
  { \xeCJK_set_family:xxx { \c_@@_math_tl } {#1} {#2} }
\tl_const:Nn \c_@@_math_tl { CJKmath }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_set_mathfont:}
% 当没有设置 CJK 数学字体时，使用 \cs{CJKfamilydefault} 作为数学字体。
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \xeCJK_set_mathfont:
  {
    \xeCJK_family_if_exist:xTF { \c_@@_math_tl }
      { \tl_set:Nx \l_@@_tmp_tl { \c_@@_math_tl } }
      {
        \xeCJK_family_if_exist:xTF { \CJKfamilydefault }
          { \tl_set:Nx \l_@@_tmp_tl { \CJKfamilydefault } }
          { \use_none:nnnnn }
      }
    \prop_get:NVNT \g_@@_family_name_prop \l_@@_tmp_tl \l_@@_tmp_tl
      {
        \tl_const:Nx \c_@@_math_family_tl { \l_@@_tmp_tl }
        \DeclareSymbolFont \c_@@_math_tl
          \c_@@_encoding_tl \c_@@_math_family_tl \mddefault \shapedefault
        \SetSymbolFont \c_@@_math_tl { bold }
          \c_@@_encoding_tl \c_@@_math_family_tl \bfdefault \shapedefault
        \int_const:Nn \c_xeCJK_math_fam_int { \use:c { sym \c_@@_math_tl } }
        \clist_concat:NNN \l_@@_tmp_clist
          \c_@@_CJK_chars_clist \c_@@_FullLeft_chars_clist
        \clist_concat:NNN \l_@@_tmp_clist
          \l_@@_tmp_clist \c_@@_FullRight_chars_clist
        \clist_map_inline:Nn \l_@@_tmp_clist
          {
            \@@_set_char_class_aux:Nnw \xeCJK_gset_mathcode:nnnn {##1}
              { \c_seven } { \c_xeCJK_math_fam_int }
          }
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_gset_mathcode:nnnn}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \xeCJK_gset_mathcode:nnnn #1#2#3#4
  {
    \@@_check_num_range:nnNN {#1} {#2} \l_@@_begin_int \l_@@_end_int
    \xeCJK_int_until_do:nn { \l_@@_begin_int > \l_@@_end_int }
      {
        \tex_global:D \xeCJK_xetex_mathcode:w
          \l_@@_begin_int = #3 ~ #4 \l_@@_begin_int
        \int_incr:N \l_@@_begin_int
      }
  }
%    \end{macrocode}
% \end{macro}
%
%
% \subsection{抄录环境中的间距调整}
%
% \changes{v3.1.0}{2012/11/19}{放弃使用放缩字体大小的方式，而只采用调整间距的方式
% 与西文等宽字体对齐。并且只适用于与抄录环境下。}
% \changes{v3.2.0}{2013/04/15}{增加 \texttt{Verb} 选项。}
% \changes{v3.2.1}{2013/05/29}{调整 \texttt{Verb} 选项：在命令 \cs{verb} 里使用时，
% 不破坏标点禁则，增加值 \texttt{env+}。}
%
% \begin{macro}{Verb}
% \changes{v3.2.5}{2013/07/25}{微调定义。}
% 如果设置为 \texttt{env}，则只在 \LaTeX 的抄录环境里使用 \cs{xeCJKVerbAddon}，^^A
% 而不包括 \cs{verb}。对当前使用环境的判断基于在标准 \LaTeX 的坏境定义里使用
% \cs{begingroup} 和 \cs{endgroup} 来分组。
%    \begin{macrocode}
\int_new:N \l_@@_verb_case_int
\keys_define:nn { xeCJK / options }
  {
    Verb .choices:nn =
      { true , env , env+ , false }
      { \int_set_eq:NN \l_@@_verb_case_int \l_keys_choice_int } ,
    Verb  .default:n = { env }
  }
\cs_new_protected_nopar:Npn \@@_verb_font_hook:
  {
    \if_case:w \l_@@_verb_case_int
    \or:
      \cs_set_eq:NN \CJKglue   \@@_zero_glue:
      \cs_set_eq:NN \CJKecglue \@@_zero_glue:
    \or:
      \int_compare:nNnTF \etex_currentgrouptype:D = \c_fourteen
        { \xeCJKVerbAddon }
        {
          \cs_set_eq:NN \CJKglue   \@@_zero_glue:
          \cs_set_eq:NN \CJKecglue \@@_zero_glue:
        }
    \or:
      \int_compare:nNnT \etex_currentgrouptype:D = \c_fourteen
        { \xeCJKVerbAddon }
    \fi:
  }
\@@_after_preamble:n
  {
    \cs_set_protected_nopar:Npx \verbatim@font
      { \exp_not:o { \verbatim@font } \@@_verb_font_hook: }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\xeCJKOffVerbAddon,\xeCJKVerbAddon}
% \changes{v3.1.0}{2012/11/19}{新增 \cs{xeCJKVerbAddon} 用于抄录环境中的间距调整。}
% \changes{v3.2.3}{2013/06/04}
% {新增 \cs{xeCJKOffVerbAddon} 用于局部取消 \cs{xeCJKOffVerbAddon} 的影响；并解决
% 跨页使用时影响到页眉页脚的问题。}
% \changes{v3.2.5}{2013/07/13}{禁止自动换行，与西文一致。}
% \cs{xeCJKVerbAddon} 进行了比较大的调整，应该只在分组环境里使用。为了方便调整间距
% 以利于对齐，这里只把字符分成了两类，并且在 CJK 类与边界（空格）之间也插入
% \cs{CJKecglue}。当然，这样做之后，关于标点符号的禁则就没有了。
%    \begin{macrocode}
\NewDocumentCommand \xeCJKVerbAddon { }
  {
    \int_compare:nNnF \etex_currentgrouplevel:D = \c_zero
      {
        \bool_if:NF \l_@@_listings_env_bool
          {
            \@@_set_verb_exspace:
            \@@_verb_addon:
          }
      }
  }
\bool_new:N \l_@@_listings_env_bool
\NewDocumentCommand \xeCJKOffVerbAddon { } { }
\cs_new_protected_nopar:Npn \@@_verb_addon:
  {
    \bool_if:NF \l_@@_verb_addon_bool
      {
        \bool_set_true:N \l_@@_verb_addon_bool
        \@@_set_char_class_eq:nn { FullLeft }    { CJK }
        \@@_set_char_class_eq:nn { FullRight }   { CJK }
        \@@_set_char_class_eq:nn { HalfLeft }    { Default }
        \@@_set_char_class_eq:nn { HalfRight }   { Default }
        \@@_set_char_class_eq:nn { NormalSpace } { Default }
        \cs_set_eq:NN \@@_verb_CJKglue:   \CJKglue
        \cs_set_eq:NN \@@_verb_CJKecglue: \CJKecglue
        \cs_set_eq:NN \@@_verb_check_for_glue: \xeCJK_check_for_glue:
        \cs_set_eq:NN \@@_verb_ignore_spaces:w \xeCJK_ignore_spaces:w
        \cs_set_protected_nopar:Npx \xeCJKOffVerbAddon
          {
            \@@_reset_char_class:n { FullLeft }
            \@@_reset_char_class:n { FullRight }
            \@@_reset_char_class:n { HalfLeft }
            \@@_reset_char_class:n { HalfLeft }
            \@@_reset_char_class:n { NormalSpace }
            \exp_not:c
              { bool_set_ \bool_if:NTF \l_@@_xecglue_bool  { true } { false } :N }
              \l_@@_xecglue_bool
            \exp_not:n
              {
                \cs_set_eq:NN \CJKglue   \@@_verb_CJKglue:
                \cs_set_eq:NN \CJKecglue \@@_verb_CJKecglue:
                \cs_set_eq:NN \xeCJK_check_for_glue: \@@_verb_check_for_glue:
                \cs_set_eq:NN \xeCJK_ignore_spaces:w \@@_verb_ignore_spaces:w
              }
          }
        \tex_output:D \exp_after:wN \exp_after:wN \exp_after:wN
          {
            \exp_after:wN \exp_after:wN
            \exp_after:wN \xeCJKOffVerbAddon
            \exp_after:wN \use:n
            \tex_the:D \tex_output:D
          }
        \xeCJKsetup { xCJKecglue = false }
      }
    \skip_if_eq:nnTF { \l_@@_verb_exspace_skip } { \c_zero_skip }
      {
        \xeCJK_cs_clear:N \CJKglue
        \xeCJK_cs_clear:N \CJKecglue
      }
      {
        \cs_set_eq:NN \CJKglue   \@@_verb_ccglue:
        \cs_set_eq:NN \CJKecglue \@@_verb_ecglue:
      }
    \cs_set_eq:NN \xeCJK_check_for_glue: \CJKecglue
    \cs_set_eq:NN \xeCJK_ignore_spaces:w \CJKecglue
  }
\cs_new_protected_nopar:Npn \@@_verb_ccglue:
  { \xeCJK_no_break: \skip_horizontal:N \l_@@_verb_exspace_skip }
\cs_new_protected_nopar:Npn \@@_verb_ecglue:
  { \xeCJK_no_break: \skip_horizontal:n { 0.5 \l_@@_verb_exspace_skip } }
\cs_new_protected_nopar:Npn \@@_reset_char_class:n #1
  {
    \int_set:Nn \l_@@_tmp_int { \xeCJK_class_num:n {#1} }
    \clist_map_inline:cn { c_@@_#1_chars_clist }
      { \XeTeXcharclass ##1 = \l_@@_tmp_int }
  }
\bool_new:N \l_@@_verb_addon_bool
\cs_new_eq:NN \CJKfixedspacing \xeCJKVerbAddon
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_set_verb_exspace:}
% \changes{v3.1.1}{2012/12/08}{调整间距的计算方法。}
% \changes{v3.2.4}{2013/06/29}{当计算得出的间距为负时，缩小 CJK 字体。}
% 在抄录环境中，CJK 文字之间的间距为当前西文字体两个空格的宽度与当前字体大小之差，
% 而与西文和空格的间距为 CJK 文字之间的间距的一半。
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_set_verb_exspace:
  {
    \tl_if_exist:cTF { xeCJK/verb/\l_xeCJK_family_tl/\curr@fontshape/\f@size }
      {
        \skip_set:Nn \l_@@_verb_exspace_skip
          { \use:c { xeCJK/verb/\l_xeCJK_family_tl/\curr@fontshape/\f@size } }
      }
      {
        \tl_set:Nx \l_@@_current_coor_tl { \l_xeCJK_family_tl/\curr@fontshape }
        \prop_get:NVNTF \g_@@_scale_family_prop
          \l_@@_current_coor_tl \l_xeCJK_family_tl
          {
            \tl_set_eq:NN \xeCJK@family \l_xeCJK_family_tl
            \skip_zero:N \l_@@_verb_exspace_skip
          }
          {
            \group_begin: \xeCJK_select_font: \exp_after:wN \group_end:
            \exp_after:wN \@@_set_verb_exspace:n
            \exp_after:wN { \dim_use:N \etex_fontcharwd:D \tex_font:D "4E00 }
          }
      }
  }
\skip_new:N \l_@@_verb_exspace_skip
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_set_verb_exspace:n}
% 当两个西文空格的宽度小于一个 CJK 文字的宽度时，对目前使用的 CJK 字体进行适当缩小。
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_set_verb_exspace:n #1
  {
    \skip_set:Nn \l_@@_verb_exspace_skip
      { \c_two \tex_fontdimen:D \c_two \tex_font:D - #1 }
    \dim_compare:nNnTF \l_@@_verb_exspace_skip < \c_zero_dim
      {
        \skip_zero:N \l_@@_verb_exspace_skip
        \use:x
          {
            \@@_set_verb_scale:nn
              { \dim_to_fp:n { \c_two \tex_fontdimen:D \c_two \tex_font:D } }
              { \dim_to_fp:n {#1} }
          }
      }
      {
        \tl_const:cx { xeCJK/verb/\l_xeCJK_family_tl/\curr@fontshape/\f@size }
          { \skip_use:N \l_@@_verb_exspace_skip }
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_set_verb_scale:nn}
% 缩小 CJK 字体，并保存相关信息。
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_set_verb_scale:nn #1#2
  {
    \fp_set:Nn \l_@@_scale_factor_fp { #1 / #2 }
    \@@_warning:nxx { scale-factor }
      { \fp_eval:n { round - ( \l_@@_scale_factor_fp , 4 ) } }
      { \fp_eval:n { round + ( #2 / #1 , 4 ) } }
    \xeCJK_add_font_features:Nnx \c_true_bool
      { } { Scale = { \fp_use:N \l_@@_scale_factor_fp } }
    \prop_gput:NVV \g_@@_scale_family_prop
      \l_@@_current_coor_tl \l_xeCJK_family_tl
  }
\@@_msg_new:nn { scale-factor }
  {
    `\token_to_str:N \xeCJKVerbAddon'~may~not~work~properly.\\\\
    You~may~set~`Scale=#1'~to~CJKfamily~
    `\@@_msg_family_map:n { \l_xeCJK_family_tl }',\\
    or~set~`Scale=#2'~to~family~
    `\str_if_eq_x:nnTF \f@family \ttdefault
      { \token_to_str:N \ttdefault } { \f@family }'.
  }
\fp_new:N \l_@@_scale_factor_fp
\prop_new:N \g_@@_scale_family_prop
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_visible_space:}
% \changes{v3.2.5}{2013/07/13}{可视空格考虑传统 \TeX 字体的情况。}
% 如果文档不使用 \texttt{EU1} 作为默认字体编码，那么默认的打字机字体族很可能是
% 传统的 \TeX 字体，这时可视空格按照 \texttt{OT1} 编码传统一般就是字体中的 |\char32|。
% 这里加入 \cs{scan_stop:} 的目的是强制发生状态转移。这样当空格出现在 CJK 文字
% 后面时，使字体回到西文，保证在当前西文字体而不是在 CJK 字体中检查有没有 |U+2423|。
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \xeCJK_visible_space:
  {
    \bool_if:NT \l_@@_CJK_group_bool { \scan_stop: }
    \xeCJK_glyph_if_exist:NTF { ^^^^2423 }
      { ^^^^2423 }
      {
        \int_compare:nNnTF { \XeTeXfonttype \tex_font:D } = \c_zero
          {
            \str_if_eq_x:nnTF { \f@family } { \ttdefault }
              { \c_catcode_other_space_tl }
              { \textvisiblespace }
          }
          { \xeCJK_visible_space_fallback: }
      }
  }
\AtEndOfPackage
  { \cs_gset_eq:NN \fontspec_visible_space: \xeCJK_visible_space: }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_visible_space_fallback:}
% \changes{v3.1.0}{2012/11/19}
% {调整 \pkg{fontspec} 的后备可视空格符号，以便于使用时对齐。}
% \pkg{fontspec} 使用 |lmtt| 字体中的可视空格符号（|U+2423|）作为当前字体中相应符号
% 的后备。但是 |lmtt| 的字体大小未必与当前字体匹配。因此，我们在这里做一些调整，以
% 保证使用后备可视空格符号时，也能保证对齐。
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \xeCJK_visible_space_fallback:
  { {
      \cs_if_exist_use:cF { xeCJK/space/\curr@fontshape/\f@size }
        { \xeCJK_set_visible_space_font: }
      ^^^^2423
  } }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_set_visible_space_font:}
% 当前字体空格的宽度与后备字体 |lmtt| 不一样时，就对 \cs{textvisiblespace} 的字体尺寸
% 按相应的比例放缩。
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \xeCJK_set_visible_space_font:
  {
    \tl_set:Nx \l_@@_current_coor_tl { xeCJK/space/\curr@fontshape/\f@size }
    \exp_after:wN \@@_set_visible_space_size:n
    \exp_after:wN { \dim_use:N \tex_fontdimen:D \c_two \tex_font:D }
    \xeCJK_font_gset_to_current:c { \l_@@_current_coor_tl }
  }
\cs_new_protected_nopar:Npn \@@_set_visible_space_size:n #1
  {
    \fontencoding { \g_fontspec_encoding_tl }
    \tl_set:Nx \f@family { lmtt }
    \selectfont
    \dim_compare:nNnF {#1} = { \tex_fontdimen:D \c_two \tex_font:D }
      {
        \fontsize
          {
            \dim_eval:n
              {
                \f@size pt *
                \dim_ratio:nn {#1} { \tex_fontdimen:D \c_two \tex_font:D }
              }
          }
          { \f@baselineskip }
        \selectfont
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \subsection{\pkg{xeCJK} 其它选项}
%
% \begin{macro}{LocalConfig}
% \changes{v3.1.0}{2012/11/20}{增加 \texttt{LocalConfig} 选项用于载入本地配置文件。}
% 声明载入本地配置文件的选项。
%    \begin{macrocode}
\keys_define:nn { xeCJK / options }
  {
    LocalConfig .choice: ,
    LocalConfig / false   .code:n =
      { \bool_gset_false:N \g_@@_config_bool } ,
    LocalConfig / true    .code:n =
      {
        \bool_gset_true:N \g_@@_config_bool
        \tl_gset:Nn \g_@@_config_name_tl { xeCJK }
      } ,
    LocalConfig / unknown .code:n =
      {
        \bool_gset_true:N \g_@@_config_bool
        \tl_gset:Nx \g_@@_config_name_tl { xeCJK - \l_keys_value_tl }
      } ,
    LocalConfig        .default:n = { true }
  }
\tl_new:N \g_@@_config_name_tl
\bool_new:N \g_@@_config_bool
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{indentfirst}
% 首行是否缩进。
%    \begin{macrocode}
\keys_define:nn { xeCJK / options }
  {
    CJKnumber    .bool_gset:N = \g_@@_number_bool ,
    indentfirst  .bool_gset:N = \g_@@_indent_bool ,
    normalindentfirst .meta:n = { indentfirst = false }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{quiet,silent}
% 将调用 \pkg{xeCJK} 时使用的未知的选项传递给 \pkg{fontspec} 宏包。
% 对 \pkg{fontspec} 的 |quiet| 和 |silent| 选项进行修改，使其适用于 \pkg{xeCJK}。
%    \begin{macrocode}
\keys_define:nn { xeCJK / options }
  {
    quiet .code:n =
      {
        \msg_redirect_module:nnn { xeCJK } { warning } { info }
        \msg_redirect_module:nnn { xeCJK } { info }    { none }
        \xeCJK_if_package_loaded:nF { fontspec }
          { \PassOptionsToPackage { quiet } { fontspec } }
      } ,
    silent .code:n =
      {
        \msg_redirect_module:nnn { xeCJK } { warning } { none }
        \msg_redirect_module:nnn { xeCJK } { info }    { none }
        \xeCJK_if_package_loaded:nF { fontspec }
          { \PassOptionsToPackage { silent } { fontspec } }
      } ,
    unknown .code:n =
      {
        \xeCJK_if_package_loaded:nTF { fontspec }
          { \@@_error:nx { key-unknown } { \l_keys_key_tl } }
          { \PassOptionsToPackage { \l_keys_key_tl } { fontspec } }
      }
  }
\@@_msg_new:nn { key-unknown }
  {
    Sorry,~but~\l__keys_module_tl \ does~not~have~a~key~called~`#1'.\\\\
    The~key~`#1'~is~being~ignored.
  }
%    \end{macrocode}
% \end{macro}
%
% \subsection{\pkg{xeCJK} 初始化设置}
%
% \begin{macro}[internal]{\CJKsymbol, \CJKpunctsymbol}
%    \begin{macrocode}
\cs_new_nopar:Npn \CJKsymbol      #1 {#1}
\cs_new_nopar:Npn \CJKpunctsymbol #1 {#1}
%    \end{macrocode}
% \end{macro}
%
% \pkg{xeCJK} 宏包的初始化设置。
%
%    \begin{macrocode}
\keys_set:nn { xeCJK / options }
  {
    CJKglue         = { \skip_horizontal:n { \c_zero_dim plus 0.08 \tex_baselineskip:D } } ,
    CJKecglue       = { ~ } ,
    xCJKecglue      = false ,
    CheckSingle     = false ,
    PlainEquation   = false ,
    CheckFullRight  = false ,
    CJKspace        = false ,
    CJKmath         = false ,
    CJKnumber       = false ,
    xeCJKactive     = true  ,
    LocalConfig     = true  ,
    indentfirst     = true  ,
    Verb            = env   ,
    EmboldenFactor  = 4     ,
    SlantFactor     = 0.167 ,
    PunctStyle      = quanjiao ,
    NewLineCS       = { \par \[ } ,
    EnvCS           = { \begin \end } ,
    NoBreakCS       = { \footnote \footnotemark \nobreak } ,
    KaiMingPunct    = { ^^^^3002 ^^^^ff0e ^^^^ff1f ^^^^ff01 } ,
    LongPunct       = { ^^^^2014 ^^^^2015 ^^^^2500 ^^^^2025 ^^^^2026 } ,
    MiddlePunct     = { ^^^^2014 ^^^^2015 ^^^^2027 ^^^^2500 ^^^^00b7 ^^^^30fb ^^^^ff65 } ,
    AllowBreakBetweenPuncts = false
  }
\defaultCJKfontfeatures { Script = CJK }
%    \end{macrocode}
%
% 执行宏包选项，并载入 \pkg{fontspec} 宏包和 \pkg{xunicode-addon}。
%    \begin{macrocode}
\ProcessKeysOptions { xeCJK / options }
\RequirePackage { fontspec } [ 2011/09/13 ]
\RequirePackage { xunicode-addon }
%    \end{macrocode}
%
% \begin{macro}[internal,var]{\c_@@_encoding_tl}
% 保存 \pkg{fontspec} 声明字体时使用的字体编码。
%    \begin{macrocode}
\tl_const:Nx \c_@@_encoding_tl { \g_fontspec_encoding_tl }
%    \end{macrocode}
% \end{macro}
%
% 章节标题后面的首个段落的首行是否缩进。
% \changes{v3.1.0}{2012/11/21}{改用 \pkg{indentfirst} 宏包处理缩进的问题。}
%    \begin{macrocode}
\bool_if:NT \g_@@_indent_bool { \RequirePackage { indentfirst } }
%    \end{macrocode}
%
% 对不能通过 \cs{xeCJKsetup} 设置的选项给出警告。
%    \begin{macrocode}
\keys_define:nn { xeCJK / options }
  {
    LocalConfig .code:n = { \@@_warning:nx { option-invalid } { \l_keys_key_tl } } ,
    CJKnumber   .code:n = { \@@_warning:nx { option-invalid } { \l_keys_key_tl } } ,
    indentfirst .code:n = { \@@_warning:nx { option-invalid } { \l_keys_key_tl } }
  }
\@@_msg_new:nn { option-invalid }
  {
    The~`#1'~option~only~can~be~set~in~the~optional~argument~to~the\\
    \token_to_str:N \usepackage \ command~when~xeCJK~is~being~loaded.\\\\
    Please~do~not~set~it~via~the~\token_to_str:N \xeCJKsetup \ command.
  }
%    \end{macrocode}
%
% \begin{macro}[var]{\CJKrmdefault,\CJKsfdefault,\CJKttdefault,\CJKfamilydefault}
%    \begin{macrocode}
\tl_if_exist:NF \CJKrmdefault { \tl_gset:Nn \CJKrmdefault { rm } }
\tl_if_exist:NF \CJKsfdefault { \tl_gset:Nn \CJKsfdefault { sf } }
\tl_if_exist:NF \CJKttdefault { \tl_gset:Nn \CJKttdefault { tt } }
\tl_new:N \l_@@_family_default_init_tl
\cs_new_eq:NN \@@_family_default_wrap:n \use:n
\tl_set:Nx \l_@@_family_default_init_tl
  {
    \exp_not:N \@@_family_default_wrap:n
      {
        \tl_if_exist:NTF \CJKfamilydefault
          { \exp_not:V \CJKfamilydefault }
          { \exp_not:N \CJKrmdefault }
      }
  }
\tl_gset_eq:NN \CJKfamilydefault \l_@@_family_default_init_tl
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\xeCJKsetup}
% 在导言区或文档中设置 \pkg{xeCJK} 的接口。
%    \begin{macrocode}
\NewDocumentCommand \xeCJKsetup { +m }
  {
    \keys_set:nn { xeCJK / options } {#1}
    \tex_ignorespaces:D
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJKsetemboldenfactor, \xeCJKsetslantfactor}
%    \begin{macrocode}
\NewDocumentCommand \xeCJKsetemboldenfactor { m }
  { \xeCJKsetup { EmboldenFactor = {#1} } }
\NewDocumentCommand \xeCJKsetslantfactor { m }
  { \xeCJKsetup { SlantFactor = {#1} } }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\punctstyle, \xeCJKplainchr}
%    \begin{macrocode}
\NewDocumentCommand \punctstyle { m } { \xeCJKsetup { PunctStyle = {#1} } }
\NewDocumentCommand \xeCJKplainchr { } { \xeCJKsetup { PunctStyle = plain } }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\CJKsetecglue}
%    \begin{macrocode}
\NewDocumentCommand \CJKsetecglue { m } { \xeCJKsetup { CJKecglue = {#1} } }
\cs_new_eq:NN \xeCJKsetecglue \CJKsetecglue
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\CJKspace,\CJKnospace}
%    \begin{macrocode}
\NewDocumentCommand \CJKspace   { } { \xeCJKsetup { CJKspace = true } }
\NewDocumentCommand \CJKnospace { } { \xeCJKsetup { CJKspace = false } }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJKallowbreakbetweenpuncts, \xeCJKnobreakbetweenpuncts}
%    \begin{macrocode}
\NewDocumentCommand \xeCJKallowbreakbetweenpuncts { }
  { \xeCJKsetup { AllowBreakBetweenPuncts = true } }
\NewDocumentCommand \xeCJKnobreakbetweenpuncts { }
  { \xeCJKsetup { AllowBreakBetweenPuncts = false } }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJKenablefallback, \xeCJKdisablefallback}
%    \begin{macrocode}
\NewDocumentCommand \xeCJKenablefallback { }
  { \xeCJKsetup { AutoFallBack = true } }
\NewDocumentCommand \xeCJKdisablefallback { }
  { \xeCJKsetup { AutoFallBack = false } }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJKsetcharclass}
%    \begin{macrocode}
\NewDocumentCommand \xeCJKsetcharclass { m m m }
  {
    \xeCJK_set_char_class:nnn {#1} {#2} {#3}
    \xeCJKResetPunctClass
  }
%    \end{macrocode}
% \end{macro}
%
% \subsection{兼容性修补}
%
% \begin{macro}[internal]{\(}
% \changes{v3.2.5}{2013/07/25}
% {解决汉字后紧跟 \cs{(}\texttt{...}\cs{)} 形式的行内数学公式时，不能加入间距的问题。}
% \cs{(} 的在 \LaTeXe 中的定义是
% \begin{verbatim}
%   \def\({\relax\ifmmode\@badmath\else$\fi}
% \end{verbatim}
% 这个定义最开始的 \cs{relax} 是为了防止 \cs{(} 出现在表格单元格的开始位置时，模式
% 判断不正确（因为 \TeX 会先看单元格中第一个不可展的非空格记号是否是 \cs{omit} 或 \cs{noalign}）。^^A
% 但是它会造成一个边界，使 \pkg{xeCJK} 不能看到 \cs{relax} 后面出现的 |$|，从而不能
% 加入间距\footnote{\url{http://tex.stackexchange.com/q/124773}}。使用 \hologo{eTeX}
% 的 \cs{protected} 来定义它，可以不需要 \cs{relax}，或者将 \cs{relax} 改成
% \cs{scan_align_safe_stop:}，都可以避免这些情况。同时 \pkg{fixltx2e}中还使用了
% |\MakeRobust\(|，我们需要小心处理。
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_math_robust:N #1
  { \exp_args:NNc \@@_math_robust:NN #1 { \cs_to_str:N #1 ~ } }
\cs_new_protected_nopar:Npn \@@_math_robust:NN #1#2
  {
    \bool_if:nT
      {
        \str_if_eq_x_p:nn
          { \token_get_replacement_spec:N #1 }
          { \exp_not:n { \x@protect #1 \protect #2 } } &&
        \exp_args:Nf \tl_if_empty_p:n { \token_get_arg_spec:N #2 }  &&
        \exp_args:No \tl_if_head_eq_meaning_p:nN {#2} \scan_stop:
      }
      { \cs_gset_protected_nopar:Npx #1 { \scan_align_safe_stop: \tl_tail:N #2 } }
  }
\@@_math_robust:N \(
\@@_math_robust:N \)
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\[}
% \changes{v3.2.5}{2013/07/25}{解决 \pkg{fixltx2e} 和 \pkg{amsthm} 的冲突。}
% 当 \pkg{amsmath} 没有在 \pkg{amsthm} 之前被调用时，\pkg{amsthm} 会展开 \cs{[}，
% 并用 |$| 作为参数定界记号，相关代码为
% \begin{verbatim}
%   \def\@tempa#1$#2#3\@nil{%
%     \def\[{#1$#2\def\@currenvir{displaymath}#3}%
%   }%
%   \expandafter\@tempa\[\@nil
% \end{verbatim}
% 而 \pkg{fixltx2e} 中使用了 |\MakeRobust\[|，使得将 \cs{[} 展开一次的内容中并
% 不直接含有 |$|，从而造成了 \texttt{Runaway argument?} 的错误。可以在 \pkg{amsthm}
% 之前引入 \pkg{amsmath}，避免出现这个错误。我们下面用 \hologo{eTeX} 的 \cs{protected}
% 来定义它。当然，如果之后只使用 \pkg{amsthm}，那么 \cs{[} 会被修改，将不再是^^A
% “健壮”的了。这也是上面 \cs{@@_math_robust:NN} 中还使用 \cs{scan_align_safe_stop:} 的原因。
%    \begin{macrocode}
\bool_if:nF
  {
    \xeCJK_if_package_loaded_p:n { amsmath } ||
    \xeCJK_if_package_loaded_p:n { amsthm }
  }
  {
    \@@_math_robust:N \[
    \@@_math_robust:N \]
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\nobreakspace}
% \changes{v3.1.2}{2013/01/01}
% {修正非 \cs{UTFencname} 编码下面 \pkg{xunicode} 重定义的 \cs{nobreakspace} 会失效的问题。}
% \changes{v3.2.5}{2013/07/18}{恢复 \cs{nobreakspace} 的原始定义。}
% 空格在 \TeX 中是特殊的记号，似乎不应该把它定义为字体中的符号（\texttt{U+00A0}）。
%    \begin{macrocode}
\UndeclareTextCommand \nobreakspace { \UTFencname }
\RenewDocumentCommand \nobreakspace { } { \leavevmode \nobreak \ }
%    \end{macrocode}
% \end{macro}
%
% 当符号命令紧跟在 CJK 字符类后面时，强制发生状态转移，使字体回到西文状态。
%    \begin{macrocode}
\AtBeginUTFCommand { \bool_if:NT \l_@@_CJK_group_bool { \scan_stop: } }
%    \end{macrocode}
%
% \changes{v3.1.1}{2012/12/13}{对于与 \pkg{xltxtra} 的冲突给出错误警告。}
% 比较老版本的 \pkg{realscripts} 定义了 \cs{dim_max:nn} 和 \cs{dim_min:nn}，这与
% 新版本的 \pkg{expl3} 冲突。
%    \begin{macrocode}
\@@_msg_new:nn { conflict-package }
  {
    The~`#1'~package~is~too~old. \\
    Please~update~an~up~to~date~version~of~it\\
    using~your~TeX~package~manager~or~from~CTAN.
  }
\xeCJK_if_package_loaded:nTF { realscripts }
  {
    \@ifpackagelater { realscripts } { 2010/10/10 } { }
      {
        \@@_error:nx { conflict-package }
          {
            \xeCJK_if_package_loaded:nTF { xltxtra }
              { xltxtra } { realscripts }
          }
      }
  }
  {
    \cs_new_eq:NN \@@_dim_max:nn \dim_max:nn
    \cs_new_eq:NN \@@_dim_min:nn \dim_min:nn
    \@@_at_end_preamble:n
      {
        \xeCJK_if_package_loaded:nT { realscripts }
          {
            \@ifpackagelater { realscripts } { 2010/10/10 } { }
              {
                \cs_gset_eq:NN \dim_max:nn \@@_dim_max:nn
                \cs_gset_eq:NN \dim_min:nn \@@_dim_min:nn
              }
          }
        \cs_undefine:N \@@_dim_max:nn
        \cs_undefine:N \@@_dim_min:nn
      }
  }
%    \end{macrocode}
%
% \begin{macro}[internal]{\fontfamily}
% \changes{v3.1.1}{2012/12/06}{修改主要 \texttt{CJK} 字体族的自动更新方式。}
% \changes{v3.1.2}{2013/01/01}{不将参数完全展开。}
% 修改 \cs{fontfamily}，使主要 |CJK| 字体族能随西文主要字体更新。
%    \begin{macrocode}
\RenewDocumentCommand \fontfamily { m }
  {
    \tl_set:Nx \f@family {#1}
    \@@_update_family:n
      {
        \str_case:nnn {#1}
          {
            { \rmdefault }     { \xeCJK_switch_family:n { \CJKrmdefault } }
            { \sfdefault }     { \xeCJK_switch_family:n { \CJKsfdefault } }
            { \ttdefault }     { \xeCJK_switch_family:n { \CJKttdefault } }
            { \familydefault } { \xeCJK_switch_family:n { \CJKfamilydefault } }
          }
          { }
      }
  }
\cs_new_eq:NN \@@_update_family:n \use:n
%    \end{macrocode}
% \end{macro}
%
% \newbox\xeCJKtmpbox
% \setbox\xeCJKtmpbox\vbox\bgroup\gobblelineno
%    \begin{macrocode}
%<@@=>
%    \end{macrocode}\egroup
%
% \begin{macro}[internal]{\xeCJK@fix@penalty}
% \changes{v3.1.0}{2012/11/13}{采用通过不修改原语 \cs{/} 的方式对修复倾斜校正。}
% 对 \LaTeXe 内核中的 \cs{fix@penalty} 被用于诸如 \cs{textit} 之类的文档
% 字体转换命令的定义之中。这里对它进行补丁的目的是修复其中的倾斜校正，并使得这些
% 文档命令与紧随其后的汉字之间可以正确的插入 \cs{CJKecglue} 或者忽略其中的空格。
% 例如 \verb*|这是 \emph{强调} 文本|，第二个空格可以被忽略掉。如果使用 |xCJKecglue|
% 选项，第一个空格也可以被省略。事实上，在 \cs{sw@slant} 的定义中，\cs{@@italiccorr}
% 前面的 \cs{lastskip} 和 \cs{lastpenalty} 有四种情况，这里只对它们都为零的情况进行
% 处理。
%    \begin{macrocode}
\cs_new_eq:NN \xeCJK@fix@penalty \fix@penalty
\tl_replace_once:Nnn \xeCJK@fix@penalty { \@@italiccorr } { \xeCJK@italiccorr }
\tl_replace_once:Nnn \sw@slant          { \fix@penalty }  { \xeCJK@fix@penalty }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK@italiccorr}
% 修复倾斜校正，并处理汉字后面的空格。
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \xeCJK@italiccorr
  {
    \int_compare:nNnTF \XeTeXinterchartokenstate > \c_zero
      {
        \xeCJK_if_last_node:nTF { default }
          {
            \xeCJK_remove_node: \@@italiccorr
            { \xeCJK_make_node:n { default } }
          }
          {
            \xeCJK_if_last_node:nTF { CJK }
              {
                \xeCJK_remove_node: \@@italiccorr
                { \xeCJK_make_node:n { CJK } } \use:n
              }
              {
                \xeCJK_if_last_node:nTF { CJK-space }
                  {
                    \xeCJK_remove_node: \@@italiccorr
                    { \xeCJK_make_node:n { CJK-space } } \use:n
                  }
                  { \@@italiccorr \use_none:n }
              }
%    \end{macrocode}
% \cs{xeCJK_ignore_spaces:w} 里面用到 |peek| 函数来判断后面是不是空格，而此时它
% 后面还有 $4$ 个 \cs{fi} 或者 |\else...\fi| 没有被展开，将影响 |peek| 函数的
% 判断。因此我们需要用 $2^4-1=15$ 个 \cs{exp_after:wN} 来展开它们。
% 显然，这里用 \cs{exp_last_unbraced:Nf} 会比较方便，但是它会吃掉
% \verb*|\textit{...} | 等后面原来存在的空格作为完全展开的结束。要正确使用它还
% 需要另外的处理（使用 \cs{exp_stop_f:}）。
%    \begin{macrocode}
              {
                              \exp_after:wN \exp_after:wN \exp_after:wN
                \exp_after:wN \exp_after:wN \exp_after:wN \exp_after:wN
                \exp_after:wN \exp_after:wN \exp_after:wN \exp_after:wN
                \exp_after:wN \exp_after:wN \exp_after:wN \exp_after:wN
                \xeCJK_ignore_spaces:w
              }
          }
      }
      { \@@italiccorr }
  }
%    \end{macrocode}
% \end{macro}
%
% \setbox\xeCJKtmpbox\vbox\bgroup\gobblelineno
%    \begin{macrocode}
%<@@=xeCJK>
%    \end{macrocode}\egroup
%
% \begin{macro}[internal]{\@@_set_others_toks:n}
% 简单处理与同样使用 \cs{XeTeXinterchartoks} 机制的宏包的兼容问题。
%     \begin{macrocode}
\@@_after_end_preamble:n
  {
    \int_compare:nNnF
      { \c_three + \seq_count:N \g_@@_new_class_seq } = \xe@alloc@intercharclass
      {
        \int_step_inline:nnnn \c_four \c_one \xe@alloc@intercharclass
          {
            \seq_if_in:NnF \g_@@_new_class_seq {#1}
              { \@@_set_others_toks:n {#1} }
          }
      }
  }
\cs_new_protected_nopar:Npn \@@_set_others_toks:n #1
  {
    \int_set:cn { \@@_class_csname:n { Others } } {#1}
    \seq_map_inline:Nn \g_@@_CJK_class_seq
      {
        \xeCJK_copy_inter_class_toks:nnnn {##1} { Others } {##1} { NormalSpace }
        \xeCJK_copy_inter_class_toks:nnnn { Others } {##1} { NormalSpace } {##1}
        \xeCJK_app_inter_class_toks:nnx {##1} { Others }
          { \xeCJK_get_inter_class_toks:nn { Default } { Others } }
        \xeCJK_pre_inter_class_toks:nnx { Others } {##1}
          { \xeCJK_get_inter_class_toks:nn { Others } { Default } }
        \xeCJK_if_blank_x:nT
          { \xeCJK_get_inter_class_toks:nn { Others } { Boundary } }
          {
            \xeCJK_copy_inter_class_toks:nnnn
              { Others } { Boundary } { Default } { Boundary }
          }
        \xeCJK_if_blank_x:nT
          { \xeCJK_get_inter_class_toks:nn { Boundary } { Others } }
          {
            \xeCJK_copy_inter_class_toks:nnnn
              { Boundary } { Others } { Boundary } { Default }
          }
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_group_begin:,\@@_group_end:}
% 用于保护下面歧义宽度标点的分组。
%    \begin{macrocode}
\cs_new_eq:NN \@@_group_begin: \group_begin:
\cs_new_eq:NN \@@_group_end:   \group_end:
%    \end{macrocode}
% \end{macro}
%
% 单独处理宽度有分歧的几个标点：包括省略号、破折号、间隔号、引号等中西文混用的
% 符号， 保证其命令形式输出的是西文字体。并对一些编码的符号宏包做特殊处理。在
% 使用 \texttt{T1} 编码的时候，|\r{u}| 的实际定义是 |\char183| 与常被用作中文
% 间隔号的 \texttt{U+00B7} 冲突。
%    \begin{macrocode}
\@@_after_preamble:n
  {
    \tl_map_inline:nn
      {
        \textellipsis  \textemdash     \textperiodcentered \textcentereddot
        \textquoteleft \textquoteright \textquotedblleft   \textquotedblright
      }
      {
        \cs_gset_protected_nopar:Npx #1
          { \@@_group_begin: \makexeCJKinactive \exp_not:o {#1} \@@_group_end: }
      }
    \tl_put_left:Nn \tipaencoding { \makexeCJKinactive }
    \cs_new_eq:NN \@@_aux_r:n \r
    \cs_set_nopar:Npn \r #1
      {
        \bool_if:nTF
          {
            \str_if_eq_x_p:nn { \f@encoding } { T1 } &&
            \str_if_eq_x_p:nn {#1} { u }
          }
          { { \makexeCJKinactive \@@_aux_r:n {#1} } }
          { \@@_aux_r:n {#1} }
      }
    \xeCJK_if_package_loaded:nT { pifont }
      {
        \RenewDocumentCommand \Pifont { m }
          { \makexeCJKinactive \usefont { U } {#1} { m } { n } }
      }
  }
%    \end{macrocode}
%
% 简单处理与 \pkg{hyperref} 宏包的兼容问题。
%    \begin{macrocode}
\@@_after_end_preamble:n
  {
    \xeCJK_if_package_loaded:nT { hyperref }
      {
        \pdfstringdefDisableCommands
          {
            \@@_gobble_CJKfamily:
            \xeCJK_cs_clear:N \makexeCJKinactive
            \xeCJK_cs_clear:N \@@_group_begin:
            \xeCJK_cs_clear:N \@@_group_end:
          }
      }
  }
%    \end{macrocode}
%
% \changes{v3.1.0}{2012/11/13}{取消 \cs{cprotect} 的外部宏限制。}
% 当探测到 \pkg{cprotect} 宏包被引入时，则取消 \cs{cprotect} 宏的 \cs{outer} 定义。
%    \begin{macrocode}
\@@_after_end_preamble:n
  {
    \bool_if:nT
      { \xeCJK_if_package_loaded_p:n { cprotect } && \cs_if_exist_p:N \icprotect }
      { \exp_after:wN \tex_let:D \cs:w cprotect \cs_end: \icprotect }
  }
%    \end{macrocode}
%
% \begin{macro}[internal]{\xeCJKcaption}
% 可以使用 \pkg{CJK} 宏包中的 |.cpx| 文件。
%    \begin{macrocode}
\cs_if_exist:NF \CJK@ifundefined
  { \cs_set_eq:NN \CJK@ifundefined \cs_if_free:NTF }
\NewDocumentCommand \xeCJKcaption { o m }
  {
    \IfNoValueF {#1} { \XeTeXdefaultencoding "#1" }
    \use:x
      {
        \char_set_catcode_letter:n { 64 }
        \file_input:n { #2.cpx }
        \char_set_catcode:nn { 64 } { \char_value_catcode:n { 64 } }
      }
    \XeTeXdefaultencoding "UTF-8"
  }
%    \end{macrocode}
% \end{macro}
%
% 由于 \pkg{xeCJK} 禁止 \pkg{CJKulem} 的载入，因此当使用 \pkg{ctex} 宏包的 |fntef|
% 选项时，就会出现 \cs{normalem} 没有定义的问题。此时改用 \pkg{xeCJKfntef} 以便
% 载入 \pkg{ulem}。
%
% 判断过于繁琐，应该在 \pkg{ctex} 包中妥善处理。这段代码应在 \pkg{ctex} 包发布
% 新版本后删去。
%    \begin{macrocode}
\cs_if_eq:NNTF \ifCTEX@fntef \tex_iftrue:D
  { \AtEndOfPackage { \RequirePackage { xeCJKfntef } } }
  {
    \@@_at_end_preamble:n
      {
        \xeCJK_if_package_loaded:nF { xeCJKfntef }
          {
            \xeCJK_if_package_loaded:nTF { CJKfntef }
              { \RequirePackage { xeCJKfntef } }
              {
                \xeCJK_if_package_loaded:nT { ulem }
                  { \RequirePackage { xeCJKfntef } }
              }
          }
      }
  }
%    \end{macrocode}
%
% 导言区末尾检测到 \pkg{listings} 时，自动载入 \pkg{xeCJK-listings}。
%    \begin{macrocode}
\@@_at_end_preamble:n
  {
    \xeCJK_if_package_loaded:nT { listings }
      { \RequirePackage { xeCJK-listings } }
  }
%    \end{macrocode}
%
% \changes{v3.2.4}{2013/06/25}{不再使用 \texttt{CJKnumber} 选项，可以在
% \pkg{xeCJK} 之后直接使用 \pkg{CJKnumb} 宏包得到中文数字。}
%
% \begin{macro}[internal]{\CJKaddEncHook}
% 为使用 \pkg{CJKnumb} 宏包而作一些处理。另外 \pkg{CJKnumb} 使用的是传统汉字“萬”
% 和“億”，我们在这里把它们修正为简体字。
%    \begin{macrocode}
\cs_new_protected:Npn \CJKaddEncHook #1#2
  {
    \str_if_eq:nnT {#1} { \CJK@UnicodeEnc }
      {
        \group_begin:
        \cs_set_nopar:Npn \Unicode ##1##2
          { (##1) * \c_two_hundred_fifty_six + (##2) }
        \cs_set_eq:NN \def \xeCJK_char_from_charcode:Nn
        #2
        \group_end:
        \tl_gset:Nn \CJK@tenthousand    { ^^^^4e07 }
        \tl_gset:Nn \CJK@hundredmillion { ^^^^4ebf }
      }
  }
\cs_new_protected_nopar:Npn \xeCJK_char_from_charcode:Nn #1#2
  {
    \group_begin:
    \char_set_lccode:nn { "4E00 } {#2}
    \tl_to_lowercase:n
      {
        \group_end:
        \tl_const:Nn #1 { ^^^^4e00 }
      }
  }
\bool_if:NT \g_@@_number_bool { \RequirePackage { CJKnumb } }
%    \end{macrocode}
% \end{macro}
%
% 最后引入本地配置文件。
%    \begin{macrocode}
\bool_if:NT \g_@@_config_bool
  {
    \tl_const:Nn \c_@@_config_ext_tl { cfg }
    \@onefilewithoptions
      { \g_@@_config_name_tl } [ ] [ ] { \c_@@_config_ext_tl }
  }
%    \end{macrocode}
%
%    \begin{macrocode}
%</package>
%    \end{macrocode}
%
% \subsection{\pkg{xeCJKfntef}}
%
% \changes{v3.1.1}{2012/12/08}{增加小宏包 \pkg{xeCJKfntef}，用于处理下划线的问题。}
% \changes{v3.2.4}{2013/06/23}{修正 \pkg{xeCJKfntef} 与 \pkg{natbib} 等的冲突。}
%
%    \begin{macrocode}
%<*fntef>
%    \end{macrocode}
%
% \pkg{xeCJKfntef} 不需要 \pkg{CJKulem} 宏包的支持，因此当使用 \pkg{CJKfntef} 时，%
% 需要另行载入 \pkg{ulem}。
%    \begin{macrocode}
\PassOptionsToPackage { normalem } { ulem }
\DeclareOption* { \PassOptionsToPackage { \CurrentOption } { ulem } }
\ProcessOptions \scan_stop:
%    \end{macrocode}
%
%    \begin{macrocode}
\RequirePackage { xeCJK }
\RequirePackage { ulem }
\RequirePackage { CJKfntef }
\RequirePackage { environ }
%    \end{macrocode}
%
%    \begin{macrocode}
\addto@hook \UL@hook { \xeCJK_hook_for_ulem: }
%    \end{macrocode}
%
% \begin{macro}[internal]{\xeCJK_hook_for_ulem:}
% \changes{v3.1.0}{2012/11/16}{简化对 \pkg{ulem} 宏包的兼容补丁。}
% \changes{v3.1.1}{2012/12/08}{完全处理下划线里的标点符号的有关问题。}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \xeCJK_hook_for_ulem:
  {
    \bool_if:NF \l_@@_ulem_hook_used_bool
      {
        \bool_set_true:N \l_@@_ulem_hook_used_bool
        \xeCJKsetup { CheckFullRight = false , xCJKecglue = false }
        \bool_if:NTF \l_@@_ulem_skip_punct_bool
          { \cs_set_eq:NN \@@_ulem_leader_type: \UL@leadtype }
          {
            \xeCJK_cs_clear:N \@@_ulem_skip_punct_begin:
            \xeCJK_cs_clear:N \@@_ulem_skip_punct_end:
          }
        \@@_ulem_initial:
        \xeCJK_glue_to_skip:nN
          {
            \cs_set_eq:NN \  \tex_space:D
            \cs_set_eq:NN \penalty \tex_penalty:D
            \cs_set_eq:NN \hskip \skip_horizontal:N
            \CJKglue
          } \l_@@_ccglue_skip
        \xeCJK_glue_to_skip:nN
          {
            \cs_set_eq:NN \  \tex_space:D
            \cs_set_eq:NN \penalty \tex_penalty:D
            \cs_set_eq:NN \hskip \skip_horizontal:N
            \CJKecglue
          } \l_@@_ecglue_skip
        \cs_set_protected_nopar:Npn \CJKglue
          { \@@_ulem_glue:n \l_@@_ccglue_skip }
        \cs_set_protected_nopar:Npn \CJKecglue
          { \@@_ulem_glue:n \l_@@_ecglue_skip }
      }
  }
\bool_new:N \l_@@_ulem_hook_used_bool
%    \end{macrocode}
% \end{macro}
%
% \setbox\xeCJKtmpbox\vbox\bgroup\gobblelineno
%    \begin{macrocode}
%<@@=>
%    \end{macrocode}\egroup
%
% \begin{macro}[internal]{\CJK@UL,\CJK@@UL}
% 修改 \pkg{CJKfntef} 中的 \cs{CJK@UL} 和 \cs{CJK@@UL} 以适应下面的修改。
%    \begin{macrocode}
\cs_set_eq:NN \CJK@UL \CJK@@UL
\tl_replace_once:Nnn \CJK@UL { \ULon }
  { \bool_set_true:N \l__xeCJK_ulem_skip_punct_bool \ULon }
\tl_replace_once:Nnn \CJK@@UL { \ULon }
  { \bool_set_false:N \l__xeCJK_ulem_skip_punct_bool \ULon }
\bool_new:N \l__xeCJK_ulem_skip_punct_bool
%    \end{macrocode}
% \end{macro}
%
% \setbox\xeCJKtmpbox\vbox\bgroup\gobblelineno
%    \begin{macrocode}
%<@@=xeCJK>
%    \end{macrocode}\egroup
%
% \begin{macro}[internal]{\@@_ulem_skip_punct_begin:,\@@_ulem_skip_punct_end:}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_ulem_skip_punct_begin:
  { \xeCJK_cs_clear:N \UL@leadtype }
\cs_new_protected_nopar:Npn \@@_ulem_skip_punct_end:
  { \cs_set_eq:NN \UL@leadtype \@@_ulem_leader_type: }
\xeCJK_cs_clear:N \@@_ulem_leader_type:
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_ulem_initial:}
% 这里的设置是为了在下划线状态下，下划线可以自动跳过全角标点符号和正确的在它们
% 前/后断行，并且与行首行末对齐。
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_ulem_initial:
  {
    \@@_ulem_swap_cs:NN
    \xeCJK_FullLeft_and_Default:  \@@_ulem_FullLeft_and_Default:
    \xeCJK_FullLeft_and_CJK:      \@@_ulem_FullLeft_and_CJK:
    \xeCJK_FullRight_and_Default: \@@_ulem_FullRight_and_Default:
    \xeCJK_FullRight_and_CJK:     \@@_ulem_FullRight_and_CJK:
    \xeCJK_CJK_and_CJK:N          \@@_ulem_CJK_and_CJK:N
    \xeCJK_Boundary_and_Default:  \@@_ulem_Boundary_and_Default:
    \xeCJK_Boundary_and_NormalSp: \@@_ulem_Boundary_and_NormalSp:
    \xeCJK@fix@penalty            \@@_ulem_fix_penalty:
    \@@_punct_hskip:n                \@@_ulem_punct_hskip:n
    \@@_CJK_and_Boundary_aux:        \@@_ulem_CJK_and_Boundary_aux:
    \@@_Default_and_FullLeft_glue:N  \@@_ulem_Default_and_FullLeft_glue:N
    \@@_Default_and_FullRight_glue:N \@@_ulem_Default_and_FullRight_glue:N
    \@@_CJK_and_FullLeft_glue:N      \@@_ulem_CJK_and_FullLeft_glue:N
    \@@_CJK_and_FullRight_glue:N     \@@_ulem_CJK_and_FullRight_glue:N
    \@@_Boundary_and_FullLeft_glue:N \@@_ulem_Boundary_and_FullLeft_glue:N
    \q_recursion_tail \q_nil \q_recursion_stop
    \seq_map_inline:Nn \g_@@_CJK_sub_class_seq
      {
        \seq_map_inline:Nn \g_@@_CJK_sub_class_seq
          {
            \str_if_eq:nnTF {##1} {####1}
              {
                \xeCJK_inter_class_toks:nnn { CJK } { CJK/##1 }
                  { \@@_ulem_between_CJK_blocks:nnN { CJK } {##1} }
                \xeCJK_inter_class_toks:nnn { CJK/##1 } { CJK/##1 }
                  { \@@_ulem_between_CJK_blocks:nnN { CJK } {##1} }
              }
              {
                \xeCJK_inter_class_toks:nnn { CJK/##1 } { CJK/####1 }
                  { \@@_ulem_between_CJK_blocks:nnN {##1} {####1} }
              }
          }
      }
  }
\cs_new_protected_nopar:Npn \@@_ulem_swap_cs:NN #1#2
  {
    \quark_if_recursion_tail_stop:N #1
    \xeCJK_swap_cs:NN #1#2
    \@@_ulem_swap_cs:NN
  }
%    \end{macrocode}
% \end{macro}
%
% \changes{v3.1.2}{2012/12/27}{解决在下划线状态下使用 \cs{makebox} 时的错误。}
%
% \begin{macro}[internal]{\xeCJK_if_ulem_patch:TF}
% 在下划线状态下，\pkg{ulem} 宏包在数学模式或者盒子中使用 \cs{UL@hrest} 恢复
% \verb*|\ | 等的定义，此时不需要使用 \cs{UL@stop} 和 \cs{UL@start} 来断开下划线而
% 产生断点。
%    \begin{macrocode}
\cs_new_nopar:Npn \xeCJK_if_ulem_patch:TF
  {
    \if_meaning:w \  \LA@space
      \exp_after:wN \use_ii:nn
    \else:
      \exp_after:wN \use_i:nn
    \fi:
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_ulem_Boundary_and_Default:}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_ulem_Boundary_and_Default:
  {
    \xeCJK_if_ulem_patch:TF
      {
        \xeCJK_if_last_node:nTF { CJK }
          { \xeCJK_remove_node: \skip_horizontal:N \l_@@_ecglue_skip }
          { \xeCJK_if_last_node:nT { CJK-space } { \xeCJK_remove_node: \c_space_tl } }
      }
      { \@@_ulem_Boundary_and_Default: }
  }
%    \end{macrocode}
% \end{macro}
%
%
% \begin{macro}[internal]{\@@_ulem_Boundary_and_NormalSp:}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_ulem_Boundary_and_NormalSp:
  {
    \xeCJK_if_ulem_patch:TF
      { \xeCJK_if_last_node:nT { CJK-space } { \xeCJK_remove_node: \c_space_tl } }
      { \@@_ulem_Boundary_and_NormalSp: }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_ulem_CJK_and_Boundary_aux:}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_ulem_CJK_and_Boundary_aux:
  {
    \xeCJK_if_ulem_patch:TF
      {
        \UL@stop \@@_ulem_hskip:n { \c_zero_skip } \UL@start
        { \xeCJK_make_node:n { CJK } }
      }
      { \@@_ulem_CJK_and_Boundary_aux: }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_ulem_fix_penalty:}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_ulem_fix_penalty:
  {
    \xeCJK_if_ulem_patch:TF
      { \fix@penalty }
      { \@@_ulem_fix_penalty: }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_ulem_CJK_and_CJK:N}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_ulem_CJK_and_CJK:N
  {
    \xeCJK_if_ulem_patch:TF
      {
        \xeCJK_class_group_end:
        \UL@stop \@@_ulem_ccglue: \UL@start
        \@@_ulem_class_group_begin:
        \CJKsymbol
      }
      { \@@_ulem_CJK_and_CJK:N }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_ulem_class_group_begin:}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_ulem_class_group_begin:
  {
    \xeCJK_class_group_begin:
    \xeCJK_clear_Boundary_and_CJK_toks:
    \xeCJK_select_font:
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_ulem_between_CJK_blocks:nnN}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_ulem_between_CJK_blocks:nnN #1#2
  {
    \xeCJK_if_ulem_patch:TF
      {
        \xeCJK_class_group_end:
        \UL@stop \@@_ulem_ccglue: \UL@start
        \xeCJK_class_group_begin:
        \xeCJK_clear_Boundary_and_CJK_toks:
        \@@_switch_font:nn {#1} {#2}
        \CJKsymbol
      }
      {
        \skip_horizontal:N \l_@@_ccglue_skip
        \@@_switch_font:nn {#1} {#2}
        \CJKsymbol
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_ulem_Default_and_FullLeft_glue:N}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_ulem_Default_and_FullLeft_glue:N #1
  {
    \xeCJK_if_ulem_patch:TF
      {
        \UL@stop
        \@@_ulem_skip_punct_begin:
        \@@_punct_glue:NN \c_@@_left_tl {#1}
        \UL@start
      }
      { \@@_ulem_Default_and_FullLeft_glue:N #1 }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_ulem_Boundary_and_FullLeft_glue:N}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_ulem_Boundary_and_FullLeft_glue:N #1
  {
    \xeCJK_if_ulem_patch:TF
      {
        \UL@stop
        \@@_ulem_skip_punct_begin:
        \int_compare:nNnF \etex_lastnodetype:D = \c_one
          { \@@_punct_glue:NN \c_@@_left_tl {#1} }
        \UL@start
      }
      { \@@_ulem_Boundary_and_FullLeft_glue:N #1 }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_ulem_CJK_and_FullLeft_glue:N}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_ulem_CJK_and_FullLeft_glue:N #1
  {
    \xeCJK_if_ulem_patch:TF
      {
        \xeCJK_class_group_end:
        \UL@stop
        \@@_ulem_skip_punct_begin:
        \@@_ulem_ccglue:
        \@@_punct_glue:NN \c_@@_left_tl {#1}
        \UL@start
        \@@_ulem_class_group_begin:
      }
      { \@@_ulem_CJK_and_FullLeft_glue:N #1 }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_ulem_Default_and_FullRight_glue:N}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_ulem_Default_and_FullRight_glue:N #1
  {
    \xeCJK_if_ulem_patch:TF
      {
        \UL@stop
        \@@_ulem_skip_punct_begin:
        \@@_punct_if_long:NTF {#1}
          { \@@_ulem_ccglue: }
          {
            \xeCJK_no_break:
            \@@_punct_if_middle:NT {#1}
              { \@@_punct_glue:NN \c_@@_right_tl {#1} }
          }
        \UL@start
      }
      { \@@_ulem_Default_and_FullRight_glue:N #1 }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_ulem_CJK_and_FullRight_glue:N}
% \changes{v3.2.2}{2013/05/30}{修正下划线不能跳过全角右标点的问题。}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_ulem_CJK_and_FullRight_glue:N #1
  {
    \xeCJK_if_ulem_patch:TF
      {
        \xeCJK_class_group_end:
        \@@_Default_and_FullRight_glue:N {#1}
        \@@_ulem_class_group_begin:
      }
      { \@@_ulem_CJK_and_FullRight_glue:N #1 }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_ulem_FullLeft_and_Default:}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_ulem_FullLeft_and_Default:
  {
    \xeCJK_if_ulem_patch:TF
      {
        \@@_punct_if_middle:NTF \g_@@_last_punct_tl
          {
            \xeCJK_get_punct_bounds:NN \c_@@_left_tl \g_@@_last_punct_tl
            \@@_punct_rule:NN \c_@@_right_tl \g_@@_last_punct_tl
            \xeCJK_class_group_end: \UL@stop \xeCJK_no_break:
            \@@_punct_glue:NN \c_@@_left_tl  \g_@@_last_punct_tl
          }
          { \xeCJK_class_group_end: \UL@stop }
        \@@_ulem_skip_punct_end:
        \xeCJK_no_break:
        \UL@start
      }
      { \@@_ulem_FullLeft_and_Default: }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_ulem_FullLeft_and_CJK:}
% \changes{v3.2.3}{2013/06/04}
% {修正全角左标点后下划线与 \cs{CJKunderdot} 连用时结果不正常的问题。}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_ulem_FullLeft_and_CJK:
  {
    \xeCJK_if_ulem_patch:TF
      {
        \xeCJK_FullLeft_and_Default:
        \@@_ulem_class_group_begin:
      }
      { \@@_ulem_FullLeft_and_CJK: }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_ulem_FullRight_and_Default:}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_ulem_FullRight_and_Default:
  {
    \xeCJK_if_ulem_patch:TF
      {
        \@@_punct_rule:NN \c_@@_right_tl \g_@@_last_punct_tl
        \xeCJK_class_group_end:
        \UL@stop
        \@@_punct_glue:NN \c_@@_right_tl \g_@@_last_punct_tl
        \@@_ulem_skip_punct_end:
        \UL@start
      }
      { \@@_ulem_FullRight_and_Default: }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_ulem_FullRight_and_CJK:}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_ulem_FullRight_and_CJK:
  {
    \xeCJK_if_ulem_patch:TF
      {
        \@@_punct_rule:NN \c_@@_right_tl \g_@@_last_punct_tl
        \xeCJK_class_group_end:
        \UL@stop
        \@@_punct_glue:NN \c_@@_right_tl \g_@@_last_punct_tl
        \@@_ulem_ccglue:
        \@@_ulem_skip_punct_end:
        \UL@start
        \@@_ulem_class_group_begin:
      }
      { \@@_ulem_FullRight_and_CJK: }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_ulem_punct_hskip:n}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_ulem_punct_hskip:n
  {
    \xeCJK_if_ulem_patch:TF
      { \@@_ulem_hskip:n }
      { \@@_ulem_punct_hskip:n }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_ulem_glue:n,\@@_ulem_ccglue:,\@@_ulem_hskip:n}
% 在下划线状态下的分别代替 \cs{CJKglue} 等。
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_ulem_glue:n #1
  {
    \xeCJK_if_ulem_patch:TF
      { \UL@stop \@@_ulem_hskip:n {#1} \UL@start }
      { \skip_horizontal:n {#1} }
  }
\cs_new_protected_nopar:Npn \@@_ulem_ccglue:
  { \skip_set_eq:NN \UL@skip \l_@@_ccglue_skip \UL@leaders }
\cs_new_protected_nopar:Npn \@@_ulem_hskip:n #1
  {
    \int_compare:nNnTF \tex_lastkern:D = \c_three
      { \skip_horizontal:n {#1} }
      { \skip_set:Nn \UL@skip {#1} \UL@leaders }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\CJKunderdot}
% 使用 \pkg{xeCJK} 时，\pkg{CJKfntef} 中的 \cs{CJKunderdot} 和 \cs{CJKunderanysymbol}
% 在汉字之间不能断行。因此需要我们在这里修改它们。
%    \begin{macrocode}
\RenewDocumentCommand \CJKunderdot { m }
  {
    \bool_if:NT \l_@@_ulem_hook_used_bool
      { \UL@stop \@@_ulem_restore_CJK_and_Boundary: }
    \CJK@preUnderdot
    \@@_make_under_symbol:n { \CJK@underdotSkip }
    \cs_gset_eq:NN \@@_save_under_dot_CJKsymbol:N \CJKsymbol
    \cs_set_eq:NN \CJKsymbol \@@_under_CJKsymbol:N
    \@@_restore_output_CJKsymbol:
    \bool_if:NT \l_@@_ulem_hook_used_bool { \UL@start }
    #1
    \bool_if:NT \l_@@_ulem_hook_used_bool { \UL@stop }
    \cs_set_eq:NN \CJKsymbol \@@_save_under_dot_CJKsymbol:N
    \tex_output:D \exp_after:wN { \l_@@_underdot_output_tl }
    \CJK@postUnderdot
    \bool_if:NT \l_@@_ulem_hook_used_bool
      { \@@_ulem_restore_CJK_and_Boundary: \UL@start }
    \tex_ignorespaces:D
  }
\box_new:N \g_@@_under_symbol_box
\cs_new_protected_nopar:Npn \@@_ulem_restore_CJK_and_Boundary:
  {
    \xeCJK_if_ulem_patch:TF
      {
        \xeCJK_swap_cs:NN
          \@@_CJK_and_Boundary_aux: \@@_ulem_CJK_and_Boundary_aux:
      }
      { }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\CJKunderanysymbol}
%    \begin{macrocode}
\RenewDocumentCommand \CJKunderanysymbol { m m m }
  {
    \group_begin:
    \hbox_set:Nn \CJK@underdotBox {#2}
    \@@_make_under_symbol:n {#1}
    \cs_gset_eq:NN \@@_save_under_dot_CJKsymbol:N \CJKsymbol
    \cs_set_eq:NN \CJKsymbol \@@_under_CJKsymbol:N
    \@@_restore_output_CJKsymbol:
    #3
    \group_end:
    \tex_ignorespaces:D
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_restore_output_CJKsymbol:}
% \changes{v3.2.3}{2013/06/04}{解决 \cs{CJKunderdot} 跨页使用时影响到页眉页脚的问题。}
% \cs{CJKunderdot} 中对 \cs{CJKsymbol} 的修改会影响到页眉和页脚，需要小心处理。
%    \begin{macrocode}
\cs_new_protected:Npn \@@_restore_output_CJKsymbol:
  {
    \tl_set:Nx \l_@@_underdot_output_tl
      { \exp_after:wN \exp_not:n \tex_the:D \tex_output:D }
    \tex_output:D \exp_after:wN
      {
        \exp_after:wN \cs_set_eq:NN
        \exp_after:wN \CJKsymbol
        \exp_after:wN \@@_save_under_dot_CJKsymbol:N
        \l_@@_underdot_output_tl
      }
  }
\tl_new:N \l_@@_underdot_output_tl
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_make_under_symbol:n}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_make_under_symbol:n #1
  {
    \hbox_set:Nn \l_@@_tmp_box { ^^^^4e00 }
    \vbox_gset_to_ht:Nnn \g_@@_under_symbol_box \c_zero_dim
      {
        \skip_vertical:n {#1}
        \hbox_to_zero:n
          {
            \tex_kern:D - \box_wd:N \l_@@_tmp_box
            \tex_hss:D \box_use:N \CJK@underdotBox \tex_hss:D
          }
        \tex_vss:D
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_under_CJKsymbol:N}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_under_CJKsymbol:N #1
  {
    \@@_save_under_dot_CJKsymbol:N {#1}
    \hbox_overlap_left:n { \box_use:N \g_@@_under_symbol_box }
    { \xeCJK_make_node:n { CJK } }
    \xeCJK_ignore_spaces:w
  }
%    \end{macrocode}
% \end{macro}
%
%
% \begin{macro}[internal]{CJKfilltwosides}
% \changes{v3.2.4}{2012/06/26}{改用 \texttt{minipage} 和 \LaTeX 表格（\texttt{tabular}）来实现。}
% 使用 \texttt{minipage} 和 \LaTeX 表格（\texttt{tabular}）来定义 |CJKfilltwosides| 环境。
% 可选参数 |#1| 表示环境的垂直对齐位置，默认居中；参数 |#2| 表示环境的宽度。
% 带星号的环境，如果 |#2| 不大于零或者不大于环境最长文本行的宽度，则取环境的自然宽度。
%    \begin{macrocode}
\RenewDocumentEnvironment { CJKfilltwosides } { O { c } m }
  {
    \use:x { \exp_not:N \minipage [#1] { \dim_eval:n {#2} } }
    \cs_set_eq:NN \CJKglue \tex_hfill:D
  }
  {
    \endminipage
    \ignorespacesafterend
  }
\NewEnviron { CJKfilltwosides* } [ 2 ] [ c ]
  {
    \cs_set_eq:NN \CJKglue \tex_hfill:D
    \tl_set:Nn \arraystretch { 1 }
    \token_if_dim_register:NT \extrarowheight
      { \dim_set_eq:NN \extrarowheight \c_zero_dim }
    \dim_compare:nNnTF {#2} > \c_zero_dim
      {
        \hbox_set:Nn \l_@@_tmp_box
          {
            \tabular [#1] { @ { } c @ { } }
              \BODY
            \endtabular
          }
        \dim_compare:nNnTF {#2} > { \box_wd:N \l_@@_tmp_box }
          {
            \tabular [#1] { @ { } p { \dim_eval:n {#2} } @ { } }
              \BODY
            \endtabular
          }
          { \hbox_unpack:N \l_@@_tmp_box }
      }
      {
        \tabular [#1] { @ { } c @ { } }
          \BODY
        \endtabular
      }
  }
  [ \ignorespacesafterend ]
%    \end{macrocode}
% \end{macro}
%
%    \begin{macrocode}
%</fntef>
%    \end{macrocode}
%
% \subsection{\pkg{xeCJK-listings}}
%
% \changes{v3.2.2}{2012/06/04}{增加小宏包 \pkg{xeCJK-listings}，用于支持 \pkg{listings} 宏包。}
% \changes{v3.2.3}{2012/06/06}{完善对 \pkg{listings} 宏包的支持。}
%
% 仿照 \package{luatexja} 宏包中 \pkg{lltjp-listings} 的处理，支持 \package{listings} 宏包。
%
%    \begin{macrocode}
%<*listings>
%    \end{macrocode}
%
%    \begin{macrocode}
\DeclareOption* { \PassOptionsToPackage { \CurrentOption } { xeCJK } }
\ProcessOptions \scan_stop:
%    \end{macrocode}
%
%    \begin{macrocode}
\RequirePackage { xeCJK }
\RequirePackage { listings }
%    \end{macrocode}
%
%    \begin{macrocode}
\lst@AddToHook { Init } { \@@_listings_initial_hook: }
\lst@AddToHook { SelectCharTable } { \@@_listings_toks_hook: }
\lst@AddToHook { OutputBox }
  {
    \tl_set_eq:NN \l_xeCJK_punct_style_tl \c_@@_punct_style_plain_tl
    \l_@@_restore_listings_toks_tl
    \@@_listings_output_IVS:
  }
\lst@AddToHook { PreSet } { \bool_set_true:N \l_@@_listings_env_bool }
%    \end{macrocode}
%
% \begin{macro}[internal]{\@@_listings_initial_hook:}
% \changes{v3.2.3}{2013/06/04}
% {解决 \texttt{listings} 坏境中代码行号输出不正确的问题，并解决在其中跨页时对页眉
%  和页脚的影响。}
% 为使代码行号结果正确，需要在 \cs{lst@numberstyle} 中恢复 \cs{XeTeXinterchartoks}。
% 在 \texttt{listings} 环境中换页时，对 \cs{XeTeXinterchartoks} 的修改会影响到
% 页眉和页脚，需要在 \cs{output} 中恢复成正常定义。这里使用 \cs{use:n} 是为了在
% \cs{tex_output:D} 中不增加额外的分组。加入 \cs{tex_noindent:D} 是为了防止汉字
% 出现在首行的时候可能会产生额外空行。
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_listings_initial_hook:
  {
    \tex_noindent:D
    \bool_gset_false:N \g_@@_listings_IVS_bool
    \tl_put_left:Nn \lst@numberstyle { \l_@@_restore_listings_toks_tl }
    \tex_output:D \exp_after:wN \exp_after:wN \exp_after:wN
      {
        \exp_after:wN \exp_after:wN
        \exp_after:wN \l_@@_restore_listings_toks_tl
        \exp_after:wN \use:n
        \tex_the:D \tex_output:D
      }
    \lst@ifbreaklines
      \cs_set_eq:NN \@@_listings_CJK_toks: \@@_listings_breaklines_toks:
    \fi:
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_listings_toks_hook:}
% 采用不同的 \cs{XeTeXinterchartoks} 处理方式，输入的时候是将汉字加入到 \pkg{listings}
% 的输出队列，实际输出的时候是普通文字。
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_listings_toks_hook:
  {
    \tl_set:Nx \l_@@_restore_listings_toks_tl
      {
        \@@_backup_inter_class_toks:nn { Boundary } { Default }
        \@@_backup_inter_class_toks:nn { Boundary } { CJK }
        \@@_backup_inter_class_toks:nn { Boundary } { IVS }
        \@@_backup_inter_class_toks:nn { Boundary } { FullLeft }
        \@@_backup_inter_class_toks:nn { Boundary } { FullRight }
      }
    \seq_map_inline:Nn \g_@@_CJK_sub_class_seq
      {
        \tl_put_right:Nx \l_@@_restore_listings_toks_tl
          { \@@_backup_inter_class_toks:nn { Boundary } { CJK/##1 } }
      }
    \xeCJK_inter_class_toks:nnn { Boundary } { Default }
      { \@@_listings_process_Default:N }
    \xeCJK_inter_class_toks:nnn { Boundary } { IVS }
      { \@@_listings_process_IVS:nN { \c_zero } }
    \@@_listings_CJK_toks_hook:
  }
\tl_new:N \l_@@_restore_listings_toks_tl
\cs_new_nopar:Npn \@@_backup_inter_class_toks:nn #1#2
  {
    \xeCJK_inter_class_toks:nnn {#1} {#2}
      { \xeCJK_get_inter_class_toks:nn {#1} {#2} }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_listings_CJK_toks_hook:,\@@_listings_breaklines_toks:}
% 根据 \texttt{breaklines} 选项的使用与否，选择不同的处理方式。
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_listings_CJK_toks_hook:
  {
    \xeCJK_inter_class_toks:nnn { Boundary } { CJK }
      { \@@_listings_process_CJK:nN { \c_two } }
    \xeCJK_inter_class_toks:nnn { Boundary } { FullLeft }
      { \@@_listings_process_CJK:nN { \c_two } }
    \xeCJK_inter_class_toks:nnn { Boundary } { FullRight }
      { \@@_listings_process_CJK:nN { \c_two } }
    \seq_map_inline:Nn \g_@@_CJK_sub_class_seq
      {
        \xeCJK_inter_class_toks:nnn { Boundary } { CJK/##1 }
          { \@@_listings_process_CJK:nN { \c_two } }
      }
  }
\cs_new_protected_nopar:Npn \@@_listings_breaklines_toks:
  {
    \xeCJK_inter_class_toks:nnn { Boundary } { CJK }
      { \@@_listings_process_breaklines_CJK:nN { \c_two } }
    \xeCJK_inter_class_toks:nnn { Boundary } { FullLeft }
      { \@@_listings_process_FullLeft:nN { \c_two } }
    \xeCJK_inter_class_toks:nnn { Boundary } { FullRight }
      { \@@_listings_process_FullRight:nN { \c_two } }
    \seq_map_inline:Nn \g_@@_CJK_sub_class_seq
      {
        \xeCJK_inter_class_toks:nnn { Boundary } { CJK/##1 }
          { \@@_listings_process_breaklines_CJK:nN { \c_two } }
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_listings_process_Default:N,\@@_listings_process_CJK:nN}
% \changes{v3.2.3}{2013/06/08}{在 \texttt{listings} 坏境中对 \cs{charcode} 大于
% $255$ 的字符根据其 \cs{catcode} 区分 \texttt{letter} 和 \texttt{other}。}
% 对于 \cs{charcode} 大于 $255$ 的字符，根据 \cs{catcode} 进行处理。
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_listings_process_Default:N #1
  {
    \token_if_letter:NTF #1
      { \lst@ProcessLetter #1 }
      { \lst@ProcessOther  #1 }
  }
\cs_new_protected_nopar:Npn \@@_listings_process_CJK:nN #1#2
  {
    \token_if_letter:NTF #2
      { \@@_listings_process_letter:nN {#1} #2 }
      { \@@_listings_process_other:nN  {#1} #2 }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_listings_append:nN}
% 普通 CJK 字符的宽度为一般基本宽度的两倍，IVS 类不增加宽度。这里有一个问题，
% 对 CJK 字符类中的一些半角字符（例如半角日文假名）没有区分开。\pkg{listings} 通过
% 重定义 \cs{lst@Append} 将代码写入外部文件，因此需要保留。
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_listings_append:nN #1#2
  {
    \int_add:Nn \lst@length { #1 - \c_one }
    \lst@Append #2
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_listings_process_letter:nN,\@@_listings_process_other:nN}
% 在 \texttt{letter} 类中区分汉字和西文字母。
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_listings_process_letter:nN
  {
    \lst@whitespacefalse
    \bool_if:NTF \l_@@_listings_letter_bool
      { \lst@lettertrue }
      {
        \lst@ifletter \lst@Output \else: \lst@OutputOther \lst@lettertrue \fi:
        \bool_set_true:N \l_@@_listings_letter_bool
      }
    \@@_listings_append:nN
  }
\cs_new_protected_nopar:Npn \@@_listings_process_other:nN #1#2
  {
    \lst@whitespacefalse
    \bool_if:NTF \l_@@_listings_letter_bool
      {
        \lst@Output \lst@letterfalse
        \bool_set_false:N \l_@@_listings_letter_bool
      }
      { \lst@ifletter \lst@Output \lst@letterfalse \fi: }
    \cs_set_eq:NN \lst@lastother #2
    \@@_listings_append:nN {#1} #2
  }
%    \end{macrocode}
% \end{macro}
%
% \changes{v3.2.4}{2012/07/05}
% {使 \pkg{listings} 的 \texttt{breaklines} 选项对 CJK 字符类可用，并保持标点符号的禁则。}
%
% \begin{macro}[internal]
% {\@@_listings_process_breaklines_CJK:nN,
%  \@@_listings_process_FullLeft:nN,
%  \@@_listings_process_FullRight:nN}
% 当使用 \texttt{breaklines} 选项时，立即输出之前的单个文字，以便于断行。并将标点
% 与它前/后的 CJK 文字放在同一个盒子中，以保持禁则。但是不能区分 letter 和 other。
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_listings_process_breaklines_CJK:nN
  {
    \lst@whitespacefalse
    \bool_if:NTF \l_@@_listings_letter_bool
      {
        \int_compare:nNnF \l_@@_listings_flag_int = \c_two { \lst@Output }
        \lst@lettertrue
      }
      {
        \lst@ifletter \lst@Output \else: \lst@OutputOther \lst@lettertrue \fi:
        \bool_set_true:N \l_@@_listings_letter_bool
      }
    \int_set_eq:NN \l_@@_listings_flag_int \c_one
    \@@_listings_append:nN
  }
\cs_new_protected_nopar:Npn \@@_listings_process_FullLeft:nN #1#2
  {
    \lst@whitespacefalse
    \bool_if:NTF \l_@@_listings_letter_bool
      {
        \bool_if:nF
          {
            \int_compare_p:nNn \l_@@_listings_flag_int = \c_two ||
            ( \int_compare_p:nNn \l_@@_listings_flag_int = \c_three &&
              ! \l_@@_punct_breakable_bool )
          }
          { \lst@Output }
        \lst@lettertrue
      }
      {
        \lst@ifletter \lst@Output \else: \lst@OutputOther \lst@lettertrue \fi:
        \bool_set_true:N \l_@@_listings_letter_bool
      }
    \int_set_eq:NN \l_@@_listings_flag_int \c_two
    \@@_listings_append:nN {#1} #2
  }
\cs_new_protected_nopar:Npn \@@_listings_process_FullRight:nN #1#2
  {
    \lst@whitespacefalse
    \bool_if:NTF \l_@@_listings_letter_bool
      {
        \bool_if:nT
          {
            \int_compare_p:nNn \l_@@_listings_flag_int < \c_two &&
            \@@_punct_if_long_p:N #2
          }
          { \lst@Output }
        \lst@lettertrue
      }
      {
        \lst@ifletter \lst@Output \else: \lst@OutputOther \lst@lettertrue \fi:
        \bool_set_true:N \l_@@_listings_letter_bool
      }
    \int_set_eq:NN \l_@@_listings_flag_int \c_three
    \@@_listings_append:nN {#1} #2
  }
\int_new:N \l_@@_listings_flag_int
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\lst@AppendLetter,\lst@AppendOther}
%    \begin{macrocode}
\cs_set_protected_nopar:Npn \lst@AppendLetter
  {
    \bool_if:NTF \l_@@_listings_letter_bool
      {
        \lst@Output \lst@lettertrue
        \bool_set_false:N \l_@@_listings_letter_bool
      }
      { \reverse_if:N \lst@ifletter \lst@OutputOther \lst@lettertrue \fi: }
    \lst@ifbreaklines \int_zero:N \l_@@_listings_flag_int \fi:
    \lst@Append
  }
\cs_set_protected_nopar:Npn \lst@AppendOther
  {
    \bool_if:NTF \l_@@_listings_letter_bool
      {
        \lst@Output \lst@letterfalse
        \bool_set_false:N \l_@@_listings_letter_bool
      }
      { \lst@ifletter \lst@Output \lst@letterfalse \fi: }
    \lst@ifbreaklines \int_zero:N \l_@@_listings_flag_int \fi:
    \tex_futurelet:D \lst@lastother \lst@Append
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_listings_process_IVS:nN}
% \texttt{IVS} 类作为 \texttt{letter} 处理，不用增加 \cs{lst@length}。
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_listings_process_IVS:nN
  {
    \reverse_if:N \lst@ifflexible
      \bool_gset_true:N \g_@@_listings_IVS_bool
    \fi:
    \@@_listings_process_letter:nN
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_listings_output_IVS:}
% 在使用 \texttt{columns=fixed} 选项时，\pkg{listings} 会在输出盒子里的每个字符
% 之间加入 \cs{hss}，这就破坏了 \XeTeX 将基本字和 IVS 正确的组合起来。
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_listings_output_IVS:
  {
    \reverse_if:N \lst@ifflexible
      \bool_if:NT \g_@@_listings_IVS_bool
        {
          \bool_gset_false:N \g_@@_listings_IVS_bool
          \xeCJK_cs_clear:N \lst@FillOutputBox
          \cs_set_eq:NN \CJKglue \tex_hss:D
        }
    \fi:
  }
\bool_new:N \g_@@_listings_IVS_bool
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_listings_peek_active_loop:TF}
% \cs{lstinline} 通过判断参数中第一个字符是否是 \texttt{active} 类来区分
% 它是否被用在其它宏的参数之中。如果这第一个字符不在 \pkg{listings} 预定义的
% 符号表中，判断就会出问题。我们在这里通过一个循环跳过这些字符。
%    \begin{macrocode}
\cs_new_protected:Npn \@@_listings_peek_active_loop:TF #1#2#3
  {
    \token_if_active:NTF #3
      { #1#3 }
      {
        \token_if_cs:NTF #3
          { #2#3 }
          {
            \int_compare:nNnTF { `#3 } > { \lst@ifec 255 \else: 127 \fi: }
              { \@@_listings_peek_active_loop:TF { #1#3 } { #2#3 } }
              { #2#3 }
          }
      }
  }
\cs_set_eq:NN \lst@IfNextCharActive \@@_listings_peek_active_loop:TF
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_listings_inside_convert:nw,\@@_listings_inline_group:w}
% 当 \cs{lstinline} 被使用在参数中时，\pkg{listings} 会使用一个循环逐个将
% \cs{lstinline} 参数中的字符设置为活动字符。我们可以通过 \cs{tl_set_rescan:Nnn}
% 来完成这里的 \cs{catcode} 转换，避免将 \cs{charcode} 超过 $255$ 的字符都设置为
% 活动字符。
%    \begin{macrocode}
\cs_new_protected:Npn \@@_listings_inside_convert:nw #1 ~ \@empty
  {
    \tl_set_rescan:Nnn \l_@@_tmp_tl { } {#1}
    \@@_set_listings_escape:
    \tl_put_right:NV \lst@arg \l_@@_tmp_tl
  }
\cs_set_eq:NN \lst@InsideConvert@ \@@_listings_inside_convert:nw
\cs_new_protected_nopar:Npn \@@_listings_inline_group:w
  {
    \exp_after:wN \@@_listings_inline_group:n
    \exp_after:wN { \if_false: } \fi:
  }
\cs_set_eq:NN \lst@InlineGJ \@@_listings_inline_group:w
\cs_new_protected:Npn \@@_listings_inline_group:n #1
  {
    \tl_set_rescan:Nnn \lst@arg { } {#1}
    \@@_set_listings_escape:
    \lst@InlineGJEnd
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_set_listings_escape:}
% 由于我们在上面的修改，需要保留 |\| 用于转义 \cs{lstinline} 参数中的某些 \TeX
% 特殊字符，与原来宏包一致。
%    \begin{macrocode}
\group_begin:
\cs_set:Npn \@@_tmp:w #1
  {
    \group_end:
    \cs_new_protected:Npn \@@_set_listings_escape:
      { \xeCJK_swap_cs:NN #1 \@@_listings_escape:N }
    \cs_new_protected:Npn \@@_listings_escape:N ##1
      { \cs_if_eq:NNTF #1 ##1 { \@@_listings_escape:N } {##1} }
  }
\use:n
  {
    \char_set_catcode_active:N \\
    \@@_tmp:w
  }
  { \ }
%    \end{macrocode}
% \end{macro}
%
%    \begin{macrocode}
%</listings>
%    \end{macrocode}
%
% \setbox\xeCJKtmpbox\vbox\bgroup\gobblelineno
%    \begin{macrocode}
%<@@=xunadd>
%    \end{macrocode}\egroup
%
% \subsection{\pkg{xunicode-addon}}
%
% \changes{v3.2.5}{2012/07/18}
% {增加小宏包 \pkg{xunicode-addon}，为 \pkg{xunicode} 提供判断字符是否存在的功能。}
%
%    \begin{macrocode}
%<*xunicode>
%    \end{macrocode}
%
% \pkg{xunicode} 对编码相关的符号命令的定义中用的是诸如 |\char"0022\relax| 的形式。
% 例如 \cs{textbar} 被展开为 |\char"007C\relax|。并且诸如下述的定义是无效的：
% \begin{verbatim}
%   \DeclareUTFcomposite[\UTFencname]{x1EBF}{\'}{\^e}
% \end{verbatim}
% 我们在这里做的修改是把符号命令定义为实际的字符并且使上述定义生效。另外在使用
% 这些符号命令的时候，先判断当前字体中是否存在对应的字符，如果不存在，则使用这
% 些符号命令的默认设置。
%
%    \begin{macrocode}
\pdftex_if_engine:T
  {
    \msg_new:nnnn { xunicode-addon } { cannot-use-pdftex }
      { This~package~requires~either~XeTeX~or~LuaTeX~to~function.}
      {
        You~must~change~your~typesetting~engine~to,~e.g.,\\
        "xelatex"~or~"lualatex"~instead~of~plain~"latex"~or~"pdflatex".
      }
    \msg_critical:nn { xunicode-addon } { cannot-use-pdftex }
  }
\RequirePackage { xparse }
%    \end{macrocode}
%
% 宏包选项是编码的名字。
%
%    \begin{macrocode}
\clist_new:N \g_@@_encname_clist
\DeclareOption*
  { \clist_gput_left:NV \g_@@_encname_clist \CurrentOption }
\ProcessOptions \scan_stop:
\tl_if_exist:NT \UTFencname
  { \clist_gput_left:Nx \g_@@_encname_clist { \UTFencname } }
%    \end{macrocode}
%
% 若 \pkg{xunicode} 已经被调用，则在宏包结束的时候，重新设置 \cs{UTFencname}
% 对应的编码命令。否则设置 \cs{UTFencname}，如果使用的是 \hologo{LuaLaTeX}，则
% 需要作一些设置，使得 \pkg{xunicode} 可用。
%
%    \begin{macrocode}
\@ifpackageloaded { xunicode } { }
  {
    \clist_get:NNF \g_@@_encname_clist \UTFencname
      {
        \xetex_if_engine:TF
          { \tl_set:Nn \UTFencname { EU1 } }
          { \tl_set:Nn \UTFencname { EU2 } }
        \clist_set_eq:NN \g_@@_encname_clist \UTFencname
      }
    \xetex_if_engine:TF
      { \RequirePackage { xunicode } }
      {
        \cs_set_eq:NN \@@_tmp:w \XeTeXpicfile
        \cs_set_eq:NN \XeTeXpicfile \prg_do_nothing:
        \RequirePackage { xunicode }
        \cs_set_eq:NN \XeTeXpicfile \@@_tmp:w
      }
  }
\AtEndOfPackage { \ReloadXunicode { \g_@@_encname_clist } }
%    \end{macrocode}
%
% \begin{macro}[internal]{\ReloadXunicode}
% 参数可以是多个编码，设置这些编码对应的命令。如果编码没有预先声明，则给出
% 一个错误警告。
%    \begin{macrocode}
\RenewDocumentCommand \ReloadXunicode { m }
  {
    \clist_set:Nx \l_@@_encname_clist {#1}
    \clist_remove_duplicates:N \l_@@_encname_clist
    \use:x
      {
        \bool_if:NT \l__kernel_expl_bool { \ExplSyntaxOff }
        \char_set_catcode_letter:n { 64 }
        \@@_reload:N \exp_not:N \l_@@_encname_clist
        \char_set_catcode:nn { 64 } { \char_value_catcode:n { 64 } }
        \bool_if:NT \l__kernel_expl_bool { \ExplSyntaxOn }
      }
  }
\cs_new_protected:Npn \@@_reload:N #1
  {
    \cs_set_eq:NN \@@_tmp:w \iftipaonetoken
    \cs_set_eq:NN \iftipaonetoken \scan_stop:
    \clist_map_inline:Nn #1
      {
        \cs_if_exist:cTF { T@ ##1 }
          {
            \tl_set:Nx \UTFencname {##1}
            \clist_gput_right:Nx \g_@@_encname_clist {##1}
            \file_input:n { xunicode.sty }
          }
          { \msg_error:nnn { xunicode-addon } { encoding-unknown } {##1} }
      }
    \cs_set_eq:NN \iftipaonetoken \@@_tmp:w
    \clist_gremove_duplicates:N \g_@@_encname_clist
  }
\clist_new:N \l_@@_encname_clist
\msg_new:nnnn { xunicode-addon } { encoding-unknown }
  {Encoding~scheme~"#1"~unknown.}
  {
    You~may~use \\\\
    \token_to_str:N \usepackage [ #1 , \encodingdefault ] {fontenc} \\\\
    before~xunicode-addon~or~xunicode.
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal,pTF]{\@@_glyph_if_exist:n}
% 判断字符在当前字体中是否存在。
%    \begin{macrocode}
\prg_new_conditional:Npnn \@@_glyph_if_exist:n #1 { p , T , F , TF }
  {
    \etex_iffontchar:D \tex_font:D \etex_numexpr:D #1 \scan_stop:
      \prg_return_true: \else: \prg_return_false: \fi:
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\UndeclareUTFcharacter}
% 取消编码 |#1| 下的符号命令 |#3|。
%    \begin{macrocode}
\RenewDocumentCommand \UndeclareUTFcharacter { O { \UTFencname } m m }
  {
    \@@_if_csname:nTF {#3}
      { \UndeclareTextCommand {#3} }
      { \exp_args:Nc \UndeclareTextCommand { \tl_to_str:n {#3} } }
      {#1}
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\UndeclareUTFcomposite}
% 取消编码 |#1| 下的复合符号命令 |#3{#4}|。
%    \begin{macrocode}
\RenewDocumentCommand \UndeclareUTFcomposite { O { \UTFencname } m m m }
  {
    \@@_if_csname:nTF {#3}
      { \@@_undeclare_composite:Nnnn #3 }
      { \exp_args:Nc \@@_undeclare_composite:Nnnn { \tl_to_str:n {#3} } }
      {#1} {#4} {#2}
  }
\cs_new_protected:Npn \@@_undeclare_composite:Nnnn #1#2#3#4
  { \cs_undefine:c { \token_to_str:c {#2} \token_to_str:N #1 - \tl_to_str:n {#3} } }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_if_csname:nTF}
% 判断 |#1| 是否可以作为控制序列的名字。这是因为 \pkg{xunicide} 使用了下面的定义。
% \begin{verbatim}
%   \DeclareUTFcharacter[\UTFencname]{x0149}{'n}
% \end{verbatim}
%    \begin{macrocode}
\prg_new_conditional:Npnn \@@_if_csname:n #1 { TF }
  {
    \tl_if_single_token:nTF {#1}
      {
        \if_predicate:w
          \bool_if_p:n { \token_if_cs_p:N #1 || \token_if_active_p:N #1 }
          \prg_return_true: \else: \prg_return_false: \fi:
      }
      { \prg_return_false: }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\DeclareUTFcharacter}
% 定义编码 |#1| 下的符号命令 |#3|，其对应符号的 Unicode 是 |#2|。
%    \begin{macrocode}
\RenewDocumentCommand \DeclareUTFcharacter { O { \UTFencname } m m }
  {
    \@@_if_csname:nTF {#3}
      { \@@_declare_character:Nnn #3 }
      { \exp_args:Nc \@@_declare_character:Nnn { \tl_to_str:n {#3} } }
      {#1} {#2}
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_declare_character:Nnn}
% 通过 \texttt{lowercase} 技巧，直接由 Unicode |#3| 得到编码 |#2| 下的符号命令
% |#1| 对应的实际字符。
%    \begin{macrocode}
\cs_new_protected:Npn \@@_declare_character:Nnn #1#2#3
  {
    \@@_provide_text_command_default:N #1
    \group_begin:
    \char_set_lccode:nn { `0 } { \@@_check_slot:n {#3} }
    \tl_to_lowercase:n
      {
        \group_end:
        \@@_declare_character:NNxn 0
      }
      #1 { \token_to_str:N #1 } {#2}
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_provide_text_command_default:N}
% 如果控制序列 |#1| 已经存在，但不是符号命令，\pkg{xunicode} 会将它定义为
% \cs{UTFencname} 编码下的符号命令。但是编码被转换之后，再使用这些控制序列，
% \pkg{NFSS} 就会报错。为此需要给出这些符号命令的默认定义，与原来的意义相同。
% 这些命令包括
% \begin{verbatim}
%   \nobreakspace   macro:->\protect \nobreakspace
%   \copyright      macro:->\protect \copyright
%   \AA             macro:->\r A
%   \aa             macro:->\r a
%   \textrhookopeno \long macro:->\textrethookbelow {\textopeno }
%   \hbar           macro:->{\mathchar '26\mkern -9muh}
%   \textaolig      macro:->{a\kern -.25em o}
% \end{verbatim}
% 影响比较大的是 \cs{nobreakspace}、\cs{copyright} 和 \cs{hbar}。
%    \begin{macrocode}
\cs_new_protected:Npn \@@_provide_text_command_default:N #1
  {
    \bool_if:nF
      {
        \cs_if_exist_p:c { ?   \token_to_str:N #1 } ||
        \cs_if_free_p:c  { ? - \token_to_str:N #1 }
      }
      { \exp_args:NNv \ProvideTextCommandDefault #1 { ? - \token_to_str:N #1 } }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_declare_character:NNnn}
% 使用编码 |#4| 下的符号命令 |#2| 的时候先判断它对应的实际字符 |#1| 在当前字体中
% 是否存在。如果不存在则转换到 \cs{DeclareTextSymbolDefault} 中设置的编码或者使用
% \cs{Declare\-Text\-Command\-Default} 中设置的命令。
%    \begin{macrocode}
\cs_new_protected:Npn \@@_declare_character:NNnn #1#2#3#4
  { \DeclareTextCommand #2 {#4} { \@@_text_character:nN {#3} {#1} } }
\cs_new_protected:Npn \@@_text_character:nN #1#2
  {
    \tl_use:N \l_@@_begin_hook_tl
    \@@_glyph_if_exist:nTF { `#2 }
      {#2} { \cs_if_exist_use:cF { ? #1 } {#2} }
    \tl_use:N \l_@@_end_hook_tl
  }
\cs_generate_variant:Nn \@@_declare_character:NNnn { NNx }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_check_slot:n}
% \pkg{xunicode} 中使用的 Unicode 格式是诸如 |x0022| 的形式，这就需要一些转换。
%    \begin{macrocode}
\cs_new_nopar:Npn \@@_check_slot:n #1
  {
    \int_eval:n
      {
        \tl_if_head_eq_charcode:nNTF {#1} x
          { " \use_none:n #1 } {#1}
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\DeclareUTFcomposite}
% 设置编码 |#1| 下的符号命令 |#3| 与它的参数 |#4| 的复合对应的符号的 Unicode 是 |#2|。
%    \begin{macrocode}
\RenewDocumentCommand \DeclareUTFcomposite { O { \UTFencname } m m m }
  {
    \@@_if_csname:nTF {#3}
      { \@@_declare_composite:Nnnn #3 }
      { \exp_args:Nc \@@_declare_composite:Nnnn { \tl_to_str:n {#3} } }
      {#1} {#4} {#2}
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_declare_composite:Nnnn}
% 这里使用 \cs{tex_afterassignment:D} 是因为 \pkg{xunicode} 有如下的定义。
% \begin{verbatim}
%   \DeclareUTFcomposite[\UTFencname]{x02E8\char"02E5}{\tonebar}{25}
%   \DeclareUTFcomposite[\UTFencname]{x02E5\char"02E8}{\tonebar}{52}
% \end{verbatim}
% 但是这个定义原本就是无效的，|\tonebar{25}| 和 |\tonebar{2}| 的定义一样。
% 对复合符号命令的定义用的是 \cs{chardef}，这有利于下面字符是否存在的判断。
%    \begin{macrocode}
\cs_new_protected:Npn \@@_declare_composite:Nnnn #1#2#3#4
  {
    \tex_afterassignment:D \use_none_delimit_by_q_stop:w
    \@@_chardef:cn
      { \token_to_str:c {#2} \token_to_str:N #1 - \tl_to_str:n {#3} }
      { \@@_check_slot:n {#4} }
    \q_stop
  }
\cs_new_protected:Npn \@@_chardef:Nn #1#2
  { \tex_chardef:D #1 = \etex_numexpr:D #2 \scan_stop: }
\cs_generate_variant:Nn \@@_chardef:Nn { c }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\DeclareEncodedCompositeCharacter}
% |#1| 是编码，|#2| 是重音命令，|#3| 是组合重音符号的 Unicode，|#4| 是基本重音符号
% 的 Unicode。当 |#2| 的参数为空时，输出 |#4|，否则是 |#2| 与 |#3| 的组合。
%    \begin{macrocode}
\RenewDocumentCommand \DeclareEncodedCompositeCharacter { m m m m }
  {
    \@@_if_csname:nTF {#2}
      { \@@_declare_accent:Nnnn #2 }
      { \exp_args:Nc \@@_declare_accent:Nnnn { \tl_to_str:n {#2} } }
      {#1} {#3} {#4}
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\DeclareEncodedCompositeAccents}
% |#1| 是编码，|#2| 是重音命令，|#3| 和 |#4| 都是组合重音的 Unicode。当 |#2| 的
% 参数为空时，输出 |#3|，否则是 |#2| 与 |#4| 的组合。
%    \begin{macrocode}
\RenewDocumentCommand \DeclareEncodedCompositeAccents { m m m m }
  {
    \@@_if_csname:nTF {#2}
      { \@@_declare_accent:Nnnn #2 }
      { \exp_args:Nc \@@_declare_accent:Nnnn { \tl_to_str:n {#2} } }
      {#1} {#4} {#3}
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_declare_accent:Nnnn}
% 通过 \texttt{lowercase} 技巧，直接由重音符号的 Unicode 得到实际字符。
%    \begin{macrocode}
\cs_new_protected:Npn \@@_declare_accent:Nnnn #1#2#3#4
  {
    \group_begin:
    \char_set_lccode:nn { `3 } { "#3 }
    \char_set_lccode:nn { `4 } { \tl_if_blank:nTF {#4} { "#3 } { "#4 } }
    \tl_to_lowercase:n
      {
        \group_end:
        \@@_declare_accent:NNNxxn 3 4
      }
      #1 { \token_to_str:c {#2} } { \token_to_str:N #1 } {#2}
  }
\cs_new_protected:Npn \@@_declare_accent:NNNnnn #1#2#3#4#5#6
  {
    \DeclareTextCommand #3 {#6}
      { \@@_text_composite:nnNNn {#4} {#5} {#1} {#2} }
  }
\cs_generate_variant:Nn \@@_declare_accent:NNNnnn { NNNxx }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_text_composite:nnNNn}
% 若重音命令 |#2| 与它的参数 |#5| 的复合已经由 \cs{DeclareUTFcomposite} 设置，
% 并且在当前字体中存在该字符，则直接使用。否则使用组合命令。
%    \begin{macrocode}
\cs_new_protected:Npn \@@_text_composite:nnNNn #1#2#3#4#5
  {
    \tl_use:N \l_@@_begin_hook_tl
    \cs_if_exist:cTF { #1#2 - \tl_to_str:n {#5} }
      {
        \token_if_chardef:cTF { #1#2 - \tl_to_str:n {#5} }
          {
            \@@_glyph_if_exist:nTF { \use:c { #1#2 - \tl_to_str:n {#5} } }
              { \use:c { #1#2 - \tl_to_str:n {#5} } }
              { \@@_add_accent:nnNN {#5} {#2} #3#4 }
          }
          { \use:c { #1#2 - \tl_to_str:n {#5} } }
      }
      { \@@_add_accent:nnNN {#5} {#2} #3#4 }
    \tl_use:N \l_@@_end_hook_tl
  }
\cs_generate_variant:Nn \token_if_chardef:NTF { c }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_add_accent:nnNN}
% 若组合重音符号的 |#3| 和基本重音符号 |#4| 在当前字体中都不存在，则转换到
% \cs{Declare\-Text\-Accent\-Default} 设置的编码或者使用
% \cs{DeclareTextCommandDefault} 中设置的命令。\texttt{0.9999} 版以前的 \XeTeX
% 需要设置 \cs{XeTeXinputnormalization} 为 $1$，才能使用字体中由基础字符和组合符号
% 对应的实际字符；而 \texttt{0.9999} 版以后的 \XeTeX 默认就启用这个功能，^^A
% \cs{XeTeXinputnormalization} 似乎是无效的，怀疑是使用 HarfBuzz 库替代 ICU 进行
% 字体排版的缘故\footnote{\url{http://tug.org/pipermail/xetex/2013-July/024579.html}}。
%    \begin{macrocode}
\cs_new_protected:Npn \@@_add_accent:nnNN #1#2#3#4
  {
    \tl_if_blank:nTF {#1}
      {
        \@@_glyph_if_exist:nTF { `#4 }
          {#4}
          {
            \cs_if_exist_use:cTF { ? #2 }
              { {#1} } {#4}
          }
      }
      {
        \@@_glyph_if_exist:nTF { `#3 }
          { #1#3 }
          {
            \@@_glyph_if_exist:nTF { `#4 }
              { \add@accent { `#4 } {#1} }
              {
                \cs_if_exist_use:cTF { ? #2 }
                  { {#1} } {#1}
              }
          }
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\AtBeginUTFCommand,\AtEndUTFCommand}
% 在符号命令前后使用的钩子。
%    \begin{macrocode}
\NewDocumentCommand \AtBeginUTFCommand { s +m }
  {
    \IfBooleanTF {#1}
      { \tl_set:Nn \l_@@_begin_hook_tl {#2} }
      { \tl_put_right:Nn \l_@@_begin_hook_tl {#2} }
  }
\NewDocumentCommand \AtEndUTFCommand { s +m }
  {
    \IfBooleanTF {#1}
      { \tl_set:Nn \l_@@_end_hook_tl {#2} }
      { \tl_put_right:Nn \l_@@_end_hook_tl {#2} }
  }
\tl_new:N \l_@@_begin_hook_tl
\tl_new:N \l_@@_end_hook_tl
%    \end{macrocode}
% \end{macro}
%
%    \begin{macrocode}
%</xunicode>
%    \end{macrocode}
%
% \setbox\xeCJKtmpbox\vbox\bgroup\gobblelineno
%    \begin{macrocode}
%<@@=xeCJK>
%    \end{macrocode}\egroup
%
% \subsection{\pkg{xeCJK.cfg}}
%
%    \begin{macrocode}
%<*config>
%    \end{macrocode}
%
% 预设的配置文件 \file{xeCJK.cfg} 为一个空文件。可以在里面增加设置，然后保存到
% 本地目录下面。
%    \begin{macrocode}

%    \end{macrocode}
%
%    \begin{macrocode}
%</config>
%    \end{macrocode}
%
% \iffalse
%
% \makeatletter
% \let\special@index\@gobble
% \makeatother
%
% \section{例子}
%
% \subsection{\pkg{xeCJK-example-autofake.tex}}
%
%    \begin{macrocode}
%<*ex-autofake>
%    \end{macrocode}
%
%    \begin{macrocode}
\documentclass{article}
\usepackage[AutoFakeBold,AutoFakeSlant]{xeCJK}
\setCJKmainfont[BoldFont=simhei.ttf, ItalicFont=simkai.ttf]{simsun.ttc}
\setCJKsansfont[AutoFakeSlant=false,
  BoldFont=simhei.ttf, ItalicFont=simkai.ttf]{simsun.ttc}
\setCJKmonofont[ItalicFont=simkai.ttf]{simsun.ttc}
\begin{document}
\centering
\begin{tabular}{lllll}
\hline
 {\bf rm} & md & up & \verb|\rmfamily\mdseries\upshape| &
                      {\rmfamily\mdseries\upshape English 中文字体} \\
          & md & it & \verb|\rmfamily\mdseries\itshape| &
                      {\rmfamily\mdseries\itshape English 中文字体} \\
          & md & sl & \verb|\rmfamily\mdseries\slshape| &
                      {\rmfamily\mdseries\slshape English 中文字体} \\ \cline{2-5}
          & bf & up & \verb|\rmfamily\bfseries\upshape| &
                      {\rmfamily\bfseries\upshape English 中文字体} \\
          & bf & it & \verb|\rmfamily\bfseries\itshape| &
                      {\rmfamily\bfseries\itshape English 中文字体} \\
          & bf & sl & \verb|\rmfamily\bfseries\slshape| &
                      {\rmfamily\bfseries\slshape English 中文字体} \\ \hline
 {\bf sf} & md & up & \verb|\sffamily\mdseries\upshape| &
                      {\sffamily\mdseries\upshape English 中文字体} \\
          & md & it & \verb|\sffamily\mdseries\itshape| &
                      {\sffamily\mdseries\itshape English 中文字体} \\
          & md & sl & \verb|\sffamily\mdseries\slshape| &
                      {\sffamily\mdseries\slshape English 中文字体} \\ \cline{2-5}
          & bf & up & \verb|\sffamily\bfseries\upshape| &
                      {\sffamily\bfseries\upshape English 中文字体} \\
          & bf & it & \verb|\sffamily\bfseries\itshape| &
                      {\sffamily\bfseries\itshape English 中文字体} \\
          & bf & sl & \verb|\sffamily\bfseries\slshape| &
                      {\sffamily\bfseries\slshape English 中文字体} \\ \hline
 {\bf tt} & md & up & \verb|\ttfamily\mdseries\upshape| &
                      {\ttfamily\mdseries\upshape English 中文字体} \\
          & md & it & \verb|\ttfamily\mdseries\itshape| &
                      {\ttfamily\mdseries\itshape English 中文字体} \\
          & md & sl & \verb|\ttfamily\mdseries\slshape| &
                      {\ttfamily\mdseries\slshape English 中文字体} \\ \cline{2-5}
          & bf & up & \verb|\ttfamily\bfseries\upshape| &
                      {\ttfamily\bfseries\upshape English 中文字体} \\
          & bf & it & \verb|\ttfamily\bfseries\itshape| &
                      {\ttfamily\bfseries\itshape English 中文字体} \\
          & bf & sl & \verb|\ttfamily\bfseries\slshape| &
                      {\ttfamily\bfseries\slshape English 中文字体} \\ \hline
\end{tabular}
\end{document}
%    \end{macrocode}
%
%    \begin{macrocode}
%</ex-autofake>
%    \end{macrocode}
%
% \subsection{\pkg{xeCJK-example-fallback.tex}}
%
%    \begin{macrocode}
%<*ex-fallback>
%    \end{macrocode}
%
%    \begin{macrocode}
\documentclass{article}
\usepackage[AutoFallBack]{xeCJK}
\usepackage{xeCJKfntef}
\usepackage{array}
\setCJKmainfont[AutoFakeBold,AutoFakeSlant]{KaiTi_GB2312}
\setCJKfallbackfamilyfont{\CJKrmdefault}[BoldFont=SimHei]
  { [SlantedFont=FangSong]{SimSun} ,
    [BoldFont=*]          {SimSun-ExtB} }
\begin{document}
漢字源𣴑考

\textbf{漢字源𣴑考}

\textsl{漢字源𣴑考}

\CJKunderwave{漢字源𣴑考}
\begin{table}[ht]
\caption{生僻字测试}
\medskip\centering
\begin{tabular}{*4{|c>{\ttfamily U+}l}|}
㐀 & 3400  & 㐁 & 3401  & 㐂 & 3402  & 㐃 & 3403  \\
㐄 & 3404  & 㐅 & 3405  & 㐆 & 3406  & 㐇 & 3407  \\
㐈 & 3408  & 㐉 & 3409  & 㐊 & 340A  & 㐋 & 340B  \\
㐌 & 340C  & 㐍 & 340D  & 㐎 & 340E  & 㐏 & 341F  \\
㐐 & 3410  & 㐑 & 3411  & 㐒 & 3412  & 㐓 & 3413  \\
㐔 & 3414  & 㐕 & 3415  & 㐖 & 3416  & 㐗 & 3417  \\
㐘 & 3418  & 㐙 & 3419  & 㐚 & 341A  & 㐛 & 341B  \\
㐜 & 341C  & 㐝 & 341D  & 㐞 & 341E  & 㐟 & 341F  \\[1ex]
𠀀 & 20000 & 𠀁 & 20001 & 𠀂 & 20002 & 𠀃 & 20003 \\
𠀄 & 20004 & 𠀅 & 20005 & 𠀆 & 20006 & 𠀇 & 20007 \\
𠀈 & 20008 & 𠀉 & 20009 & 𠀊 & 2000A & 𠀋 & 2000B \\
𠀌 & 2000C & 𠀍 & 2000D & 𠀎 & 2000E & 𠀏 & 2000F \\
𠀐 & 20010 & 𠀑 & 20011 & 𠀒 & 20012 & 𠀓 & 20013 \\
𠀔 & 20014 & 𠀕 & 20015 & 𠀖 & 20016 & 𠀗 & 20017 \\
𠀘 & 20018 & 𠀙 & 20019 & 𠀚 & 2001A & 𠀛 & 2001B \\
𠀜 & 2001C & 𠀝 & 2001D & 𠀞 & 2001E & 𠀟 & 2001F \\
\end{tabular}
\end{table}
\end{document}
%    \end{macrocode}
%
%    \begin{macrocode}
%</ex-fallback>
%    \end{macrocode}
%
% \subsection{\pkg{xeCJK-example-subCJKblock.tex}}
%
%    \begin{macrocode}
%<*ex-block>
%    \end{macrocode}
%
%    \begin{macrocode}
\documentclass{article}
\usepackage{xeCJK}
\usepackage{array}
\xeCJKDeclareSubCJKBlock{Ext-A} { "3400 -> "4DBF }
\xeCJKDeclareSubCJKBlock{Ext-B} { "20000 -> "2A6DF }
\xeCJKDeclareSubCJKBlock{Kana}  { "3040 -> "309F, "30A0 -> "30FF, "31F0 -> "31FF, }
\xeCJKDeclareSubCJKBlock{Hangul}{ "1100 -> "11FF, "3130 -> "318F, "A960 -> "A97F, "AC00 -> "D7AF }
\setCJKmainfont[Ext-A=SimHei,Ext-B=SimSun-ExtB]{SimSun}
\setCJKmainfont[Kana]{Meiryo}
\setCJKmainfont[Hangul]{Malgun Gothic}
\parindent=2em
\begin{document}
\long\def\showtext{%
中日韩越统一表意文字（英语：CJKV Unified Ideographs），旧称中日韩统一表意文字（英语：CJK Unified Ideographs），也称统一汉字（英语：Unihan），目的是要把分别来自中文、日文、韩文、越文、壮文中，对于相同起源、本义相同、形状一样或稍异的表意文字（主要为汉字，但也有仿汉字如：方块壮字、日文汉字（かんじ / kanji）、韩文汉字（한자 / hanja）、越南的喃字（Chữ Nôm）与越文汉字（Chữ Nho，在越南也称作儒字），应赋予其在ISO 10646及统一码标准中有相同编码。此计划原本只包含中文、日文及韩文中所使用的汉字，是以旧称中日韩统一表意文字（CJK）。后来，此计划加入了越文的喃字，所以合称中日韩越统一表意文字（CJKV）。

CJK統合漢字（シージェーケーとうごうかんじ、CJK Unified Ideographs）は、ISO/IEC 10646 (Universal Multiple-Octet Coded Character Set, 略称 UCS) および Unicodeにて採用されている符号化用漢字集合およびその符号表である。CJK統合漢字の名称は、中国語(Chinese)、日本語(Japanese)、韓国語(Korean)で使われている漢字をひとまとめにしたことからきている。CJK統合漢字の初版である Unified Repertoire and Ordering (URO) 第二版は1992年に制定されたが、1994年にベトナムで使われていた漢字も含めることにしたため、CJKVと呼ばれる事もある。CJKVは、中国語・日本語・韓国語・ベトナム語 (英語: Chinese-Japanese-Korean-Vietnamese) の略。特に、その4言語で共通して使われる、または使われていた文字体系である漢字（チュノムを含む）のこと。ソフトウェアの国際化、中でも文字コードに関する分野で用いられる。

\CJKspace
한중일월 통합 한자(또는 한중일 통합 한자)는 유니코드에 담겨 있는 한자들의 집합으로, 한국, 중국, 일본에서 쓰이는 한자를 묶은 것이기 때문에 머리 글자를 따서 한중일(CJK) 통합 한자라고 불렀는데, 최근에는 베트남에서 쓰이는 한자도 추가되었기에 한중일월(CJKV) 통합 한자로 부르게 되었다.

처음에 유니코드에는 65,536($=2^{16}$)자만 들어갈 수 있었기 때문에, 가장 많은 문자가 배당되는 한자를 위해서 한국, 중국, 일본에서 사용하는 한자 중에 모양이 유사하며 그 뜻이 같은 글자를 같은 코드로 통합했다. 따라서 문자 코드만으로 그 한자가 사용되는 언어를 알아 낼 수 없는데, 다만 중국의 간체자나 번체자, 일본의 구자체나 신자체 등 분명하게 모양이 다른 글자는 별도의 부호를 할당하고 있다. 이런 문자 할당 정책에 반발하여 TRON과 같은 인코딩이 만들어지기도 했으나, 실제로 통합된 한자의 차이가 별로 크지 않기 때문에 문제가 되지 않는다는 의견도 있다.
\CJKnospace}
\showtext

\bigskip
\xeCJKCancelSubCJKBlock{Kana,Hangul}
\showtext

\bigskip
\xeCJKRestoreSubCJKBlock{Hangul}
\showtext

\begin{table}[ht]
\caption{生僻字测试}
\medskip\centering
\begin{tabular}{*4{|c>{\ttfamily U+}l}|}
㐀 & 3400  & 㐁 & 3401  & 㐂 & 3402  & 㐃 & 3403  \\
㐄 & 3404  & 㐅 & 3405  & 㐆 & 3406  & 㐇 & 3407  \\
㐈 & 3408  & 㐉 & 3409  & 㐊 & 340A  & 㐋 & 340B  \\
㐌 & 340C  & 㐍 & 340D  & 㐎 & 340E  & 㐏 & 341F  \\
㐐 & 3410  & 㐑 & 3411  & 㐒 & 3412  & 㐓 & 3413  \\
㐔 & 3414  & 㐕 & 3415  & 㐖 & 3416  & 㐗 & 3417  \\
㐘 & 3418  & 㐙 & 3419  & 㐚 & 341A  & 㐛 & 341B  \\
㐜 & 341C  & 㐝 & 341D  & 㐞 & 341E  & 㐟 & 341F  \\[1ex]
𠀀 & 20000 & 𠀁 & 20001 & 𠀂 & 20002 & 𠀃 & 20003 \\
𠀄 & 20004 & 𠀅 & 20005 & 𠀆 & 20006 & 𠀇 & 20007 \\
𠀈 & 20008 & 𠀉 & 20009 & 𠀊 & 2000A & 𠀋 & 2000B \\
𠀌 & 2000C & 𠀍 & 2000D & 𠀎 & 2000E & 𠀏 & 2000F \\
𠀐 & 20010 & 𠀑 & 20011 & 𠀒 & 20012 & 𠀓 & 20013 \\
𠀔 & 20014 & 𠀕 & 20015 & 𠀖 & 20016 & 𠀗 & 20017 \\
𠀘 & 20018 & 𠀙 & 20019 & 𠀚 & 2001A & 𠀛 & 2001B \\
𠀜 & 2001C & 𠀝 & 2001D & 𠀞 & 2001E & 𠀟 & 2001F \\
\end{tabular}
\end{table}
\end{document}
%    \end{macrocode}
%
%    \begin{macrocode}
%</ex-block>
%    \end{macrocode}
%
%
% \subsection{\pkg{xeCJK-example-CJKecglue.tex}}
%
%    \begin{macrocode}
%<*ex-ecglue>
%    \end{macrocode}
%
%    \begin{macrocode}
\documentclass{minimal}
\usepackage{xeCJK}
\setCJKmainfont[BoldFont=SimHei]{SimSun}
\long\def\showtext{%
 这是 English 中文 {\itshape Chinese} 中文    \TeX\
  间隔 \textit{Italic} 中文\textbf{字体} a 数学 $b$ 数学 $c$ $d$\par
 这是English中文{\itshape Chinese}中文\TeX\
 间隔\textit{Italic}中文\textbf{字体}a数学$b$数学$c$ $d$\par
This is an example. 这是一个例子}
\begin{document}
\showtext

\hrulefill\bigskip

\xeCJKsetup{xCJKecglue=\quad}
\showtext
\end{document}
%    \end{macrocode}
%
%    \begin{macrocode}
%</ex-ecglue>
%    \end{macrocode}
%
% \subsection{\pkg{xeCJK-example-checksingle.tex}}
%
%    \begin{macrocode}
%<*ex-single>
%    \end{macrocode}
%
%    \begin{macrocode}
\documentclass{minimal}
\usepackage{xeCJK}
\setCJKmainfont{SimSun}
\catcode`\。=\active
\def。{．}
\def\foo{一}
\long\def\showtext{一二三四五六七八九十一二三四五六七八九十一二三四五六七八九十。
\[x^2+y^2\]
一二三四五六七八九十一二三四五六七八九十一二三四五六七八九十\foo
$$x^2+y^2$$
一二三四五六七八九十一二三四五六七八九十一二三四五六七八九十\foo
\begin{equation}
x^2+y^2
\end{equation}
一二三四五六七八九十一二三四五六七八九十一二三四五六七八九十。}
\begin{document}
\hsize=30em
\parindent=0pt
\showtext

\hrulefill\bigskip

\xeCJKsetup{CheckSingle}
\showtext

\hrulefill\bigskip

\xeCJKsetup{PlainEquation}
\showtext
\end{document}
%    \end{macrocode}
%
%    \begin{macrocode}
%</ex-single>
%    \end{macrocode}
%
% \subsection{\pkg{xeCJK-example-CJKfntef.tex}}
%
%    \begin{macrocode}
%<*ex-fntef>
%    \end{macrocode}
%
%    \begin{macrocode}
\documentclass{ctexart}
\usepackage{xcolor}
\usepackage{xeCJKfntef}
\xeCJKDeclareSubCJKBlock{test}{ `殆 , `已 }
\setCJKmainfont[test=SimHei]{SimSun}

\def\showtext{%
  庄子曰：“吾生也有涯，而知也无涯。以有涯随无涯，殆已！已而为知者，殆而已矣！%
  为善无近名，为恶无近刑，缘督以为经，可以保身，可以全生，可以养亲，可以尽年。”}
\newcommand\CJKfntef[1]{%
  \csname#1\endcsname{\showtext}\par
  \csname#1\endcsname*{\showtext}\par
  \CJKunderdot{\showtext\csname#1\endcsname{\showtext}}\par
  \CJKunderdot{\showtext\csname#1\endcsname*{\showtext}}\par
  \csname#1\endcsname{\showtext\CJKunderdot{\showtext}}\par
  \csname#1\endcsname*{\showtext\CJKunderdot{\showtext}}\par\bigskip}

\begin{document}

\section{\textsf{xeCJKfntef} 的简单测试文件}

\CJKunderline{汉 字}\CJKunderline{加下划线}
\varCJKunderline{汉字}\varCJKunderline{加下划线}
\CJKunderanyline{0.5em}{\sixly \kern-.021em\char58 \kern-.021em}{自定义下划线}
\CJKunderanyline{0.2em}{\kern-.03em\vtop{\kern.2ex\hrule width.2em\kern 0.11em
  \hrule height 0.1em}\kern-.03em}{自定义下划线}

\indent\CJKunderanyline{0.5em}{-}{\showtext}

\CJKunderanysymbol{0.2em}{-}{\showtext}

\CJKunderdot{\showtext}

\hrule depth \dp\strutbox width 0 pt \relax

\begin{CJKfilltwosides}{40mm}
两端分散对齐\\
分散对齐\\
\CJKunderdot{汉字可加点}\\
\CJKunderline{汉字可加下划线}
\end{CJKfilltwosides}

\hrule depth \dp\strutbox width 0 pt \relax

\CJKfntef{CJKunderline}
\CJKfntef{CJKunderwave}
\CJKfntef{CJKunderdblline}
\CJKfntef{CJKsout}
\CJKfntef{CJKxout}

\end{document}
%    \end{macrocode}
%
%    \begin{macrocode}
%</ex-fntef>
%    \end{macrocode}
%
% \subsection{\pkg{xeCJK-example-punctstyle.tex}}
%
%    \begin{macrocode}
%<*ex-punctstyle>
%    \end{macrocode}
%
%    \begin{macrocode}
\documentclass{article}
\usepackage{xeCJK}
\setCJKmainfont{SimSun}
\xeCJKDeclarePunctStyle { mine }
  {
    fixed-punct-ratio       = 0 ,
    fixed-margin-width      = 0 pt ,
    mixed-margin-width      = \maxdimen ,
    mixed-margin-ratio      = 0.5 ,
    middle-margin-width     = \maxdimen ,
    middle-margin-ratio     = 0.5 ,
    add-min-bound-to-margin = true ,
    min-bound-to-kerning    = true ,
    kerning-margin-minimum  = 0.1 em
  }

\begin{document}
\setlength\parindent{2em}
\newcommand\showexample[1]{%
  \texttt{------ #1 ------}\par\xeCJKsetup{PunctStyle=#1}\showtexts\par}

\newcommand\showtexts{%
列位看官：你道此书从何而来？说起根由，虽近荒唐，细按则深有趣味。待在下将此来历注明，方使阅者了然不惑。

原来女娲氏炼石补天之时，于大荒山无稽崖炼成高经十二丈、方经二十四丈顽石三万六千五百零一块。娲皇氏只用了三万六千五百块，只单单剩了一块未用，便弃在此山青埂峰下。谁知此石自经煆炼之后，灵性已通，因见众石俱得补天，独自己无材不堪入选，遂自怨自叹，日夜悲号惭愧。

一日，正当嗟悼之际，俄见一僧一道远远而来，生得骨格不凡，丰神迥别，说说笑笑，来至峰下，坐于石边，高谈快论：先是说些云山雾海、神仙玄幻之事，后便说到红尘中荣华富贵。此石听了，不觉打动凡心，也想要到人间去享一享这荣华富贵，但自恨粗蠢，不得已，便口吐人言，向那僧道说道：“大师，弟子蠢物，不能见礼了！适闻二位谈那人世间荣耀繁华，心切慕之。弟子质虽粗蠢，性却稍通，况见二师仙形道体，定非凡品，必有补天济世之材，利物济人之德。如蒙发一点慈心，携带弟子得入红尘，在那富贵场中，温柔乡里受享几年，自当永佩洪恩，万劫不忘也！”二仙师听毕，齐憨笑道：“善哉，善哉！那红尘中有却有些乐事，但不能永远依恃；况又有‘美中不足，好事多磨’八个字紧相连属，瞬息间则又乐极悲生，人非物换，究竟是到头一梦，万境归空，倒不如不去的好。”这石凡心已炽，那里听得进这话去，乃复苦求再四。二仙知不可强制，乃叹道：“此亦静极思动，无中生有之数也！既如此，我们便携你去受享受享，只是到不得意时，切莫后悔！”石道：“自然，自然。”那僧又道：“若说你性灵，却又如此质蠢，并更无奇贵之处。如此也只好踮脚而已。也罢！我如今大施佛法，助你助，待劫终之日，复还本质，以了此案。你道好否？”石头听了，感谢不尽。那僧便念咒书符，大展幻术，将一块大石登时变成一块鲜明莹洁的美玉，且又缩成扇坠大小的可佩可拿。那僧托于掌上，笑道：“形体倒也是个宝物了！还只没有实在的好处，须得再镌上数字，使人一见便知是奇物方妙。然后好携你到那昌明隆盛之邦、诗礼簪缨之族、花柳繁华地、温柔富贵乡去安身乐业。”石头听了，喜不能禁，乃问：“不知赐了弟子那哪几件奇处？又不知携了弟子到何地方？望乞明示，使弟子不惑。”那僧笑道：“你且莫问，日后自然明白的。”说着，便袖了这石，同那道人飘然而去，竟不知投奔何方何舍。

\hfill
——曹雪芹《红楼梦》}


\showexample{mine}
\showexample{quanjiao}
\showexample{banjiao}
\showexample{CCT}
\showexample{kaiming}
\showexample{hangmobanjiao}
\showexample{plain}

\end{document}
%    \end{macrocode}
%
%    \begin{macrocode}
%</ex-punctstyle>
%    \end{macrocode}
%
% \subsection{\pkg{xeCJK-example-verbatim.tex}}
%
%    \begin{macrocode}
%<*ex-verb>
%    \end{macrocode}
%
%    \begin{macrocode}
\documentclass[a4paper]{article}
\usepackage{xeCJK}
\usepackage{fullpage}
\xeCJKDeclareSubCJKBlock{Hangul}
  { "1100 -> "11FF, "3130 -> "318F, "A960 -> "A97F, "AC00 -> "D7AF }
\setCJKmainfont{Adobe Song Std}
\setCJKmainfont[Hangul]{Adobe Myungjo Std}
%%\setmonofont{Inconsolata}
%%\setmonofont{Source Code Pro}
%%\setCJKmonofont{Adobe Kaiti Std}
\setmonofont{TeX Gyre Cursor}
\setCJKmonofont{Adobe Fangsong Std}
\begin{document}

\begin{verbatim}
*************************************************************
*                                                           *
*      精致甲版红楼梦 - 精致工作室特别奉献（2010年9月）     *
*                                                           *
*        GBK简体、GBK繁体、Big5繁体、PDF简体、PDF繁体       *
*                                                           *
*  为 GBK、Big5 特别度身订制，并经反复修订，方便纯文本阅读  *
*                                                           *
*  精致甲、乙版红楼梦下载专页                               *
*  http://www.speedy7.com/cn/stguru/gb2312/redmansions.htm  *
*  http://www.speedy7.com/cn/stguru/big5/redmansions.htm    *
*                                                           *
*  此电子文件可随意传播、发布、下载。                       *
*  传播时建议保留此版本说明供查阅与识别。                   *
*  未经修订者同意，不得将此版本用于商业用途。               *
*  如您对红楼梦不具深入了解，请勿随便修改其中的内容。       *
*                                                           *
*************************************************************
\end{verbatim}

\verb|a 汉 字 b|

\verb*|a 汉 字 b|

\begin{verbatim}
lang_set = {
  ["English"]   = lang.en,
  ["Français"]  = lang.fr,
  ["Español"]   = lang.es,
  ["中文"]      = lang.zh,
  ["日本語"]    = lang.ja,
  ["한국어"]    = lang.ko,
}
\end{verbatim}

\end{document}
%    \end{macrocode}
%
%    \begin{macrocode}
%</ex-verb>
%    \end{macrocode}
%
% \subsection{\pkg{xeCJK-example-IVS.tex}}
%
%    \begin{macrocode}
%<*ex-IVS>
%    \end{macrocode}
%
%    \begin{macrocode}
\documentclass{article}
\usepackage{xeCJK}
\usepackage{hologo}

\setCJKmainfont{HanaMinA} %% http://fonts.jp/hanazono/

\begin{document}

\hologo{XeTeX} でIVSを使うテスト。

消󠄀化󠄀器󠄂 / 消󠄁火器󠄃 / 消󠄂化󠄁器󠄄

消󠄃火器󠄅 / 消化󠄂器󠄆 / 消火器󠄇

消化󠄃器󠄈 / 消火器󠄉 / 辻󠄂辻󠄃辻󠄄辻󠄅　

\CJKfontspec{Meiryo}

か

がか゚

ぼぽ

ぼぽ

ばぱ

ばぱ

\end{document}
%    \end{macrocode}
%
%    \begin{macrocode}
%</ex-IVS>
%    \end{macrocode}
%
% \subsection{\pkg{xeCJK-example-listings.tex}}
%
%    \begin{macrocode}
%<*ex-listings>
%    \end{macrocode}
%
%    \begin{macrocode}
\documentclass{article}
\usepackage[margin=1in]{geometry}
\usepackage{listings,xcolor}
\usepackage{showexpl}

\usepackage{xeCJK}
\setCJKmainfont{HanaMinA}
\setCJKmonofont{SimSun}
\xeCJKDeclareSubCJKBlock{Kana}  { "3040 -> "309F, "30A0 -> "30FF, "31F0 -> "31FF }
\setCJKmonofont[Kana]{Meiryo}
\setmonofont{Latin Modern Mono Light}

\makeatletter
\lstset{%
  basicstyle=\small\ttfamily,breakindent=4\lst@width,
  numbers=left,numberstyle=\tiny\color{gray},
  commentstyle=\color{green!50!black},keywordstyle=\color{blue}\bfseries,
  identifierstyle=\color{violet},stringstyle=\color{brown},
  escapebegin=\normalfont}
\lstnewenvironment{cppcode}[1][]
  {\lstset{language=C++,#1}}
  {}
\makeatother

\begin{document}

\section{\lstinline{\\lstinline} 测试}

\section{\lstinline|甲*乙| 测试}

\lstinline|abc汉字abc|

\lstinline|甲*乙|

\lstinline[mathescape]|数学公式$x^2+y^2$|

\section{\lstinline{lstlisting} 环境测试}

\begin{lstlisting}[basicstyle=\rmfamily]
纯文字text测试
    纯文字text测试
文字+文字
文字（符号）文字
辻󠄂辻󠄃辻󠄄辻󠄅
かがか゚
\end{lstlisting}

\begin{lstlisting}
text纯文字测试
\end{lstlisting}

\begin{lstlisting}
text 纯文字测试
\end{lstlisting}

\begin{lstlisting}
text,纯文字测试
\end{lstlisting}

\section{自定义环境测试}

\begin{LTXexample}[pos=t,varwidth,numbersep=5pt,columns=fixed]
\begin{cppcode}[escapechar=`,morekeywords=返回]
#define 返回 return
#include <iostream>
/*
 * 块注释
 * `逃逸字符，测试$f(x)$`
 */
int main()
{
    // 行注释
    const char *欢迎 = "hello 世界（ワールド）";
    std::cout << 欢迎 << std::endl;
    返回 0;
}
\end{cppcode}
\end{LTXexample}

\section{\texttt{breaklines} 测试}

\begin{lstlisting}[linewidth=10em,frame=single,rulecolor=\color{black},breaklines]
断行文字text测试，断行文字text测试，断行文字text测试，断行文字text测试
断行文字text测试，断行文字text测试，断行文字text测试，断行文字text测试
\end{lstlisting}

\section{\lstinline|\\lstinputlisting| 测试}

\lstinputlisting[language={[AlLaTeX]TeX}]{\jobname}

\end{document}
%    \end{macrocode}
%
%    \begin{macrocode}
%</ex-listings>
%    \end{macrocode}
%
% \fi
%
% \end{implementation}
%
%
% \Finale
%
\endinput
