% \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
             and the derived files xeCJK.pdf,
                                   xeCJK.sty,
                                   xeCJK.ins,
                                   xeCJK-example-autofake.tex,
                                   xeCJK-example-fallback.tex,
                                   xeCJK-example-subCJKblock.tex,
                                   xeCJK-example-CJKecglue.tex,
                                   xeCJK-example-checksingle.tex 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 extract it and also 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
\ifnum\strcmp{\fmtname}{plain}=0 \else
  \expandafter\begingroup
\fi
%</internal>
%<*batchfile>

\input docstrip.tex
\keepsilent
\askforoverwritefalse
\preamble

$Id: xeCJK.dtx 343 2012-05-21 05:59:17Z 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--2012 Wenchang Sun

   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
                and the derived files xeCJK.pdf,
                                      xeCJK.sty,
                                      xeCJK.ins,
                                      xeCJK-example-autofake.tex,
                                      xeCJK-example-fallback.tex,
                                      xeCJK-example-subCJKblock.tex,
                                      xeCJK-example-CJKecglue.tex,
                                      xeCJK-example-checksingle.tex and
                                      README.txt.
\endpostamble
\usedir{tex/xelatex/xecjk}
\generate{\file{\jobname.sty}{\from{\jobname.dtx}{package}}}
\usedir{doc/xelatex/xecjk/example}
\generate{
  \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}}}
%</batchfile>
%<batchfile>\endbatchfile
%<*internal>
\usedir{source/xelatex/xecjk}
\generate{\file{\jobname.ins}{\from{\jobname.dtx}{batchfile}}}
\nopreamble\nopostamble
\usedir{doc/xelatex/xecjk}
\generate{\file{README.txt}{\from{\jobname.dtx}{readme}}}
\ifnum\strcmp{\fmtname}{plain}=0
  \expandafter\endbatchfile
\else
  \expandafter\endgroup
\fi
%</internal>
%
%<*driver|package>
\NeedsTeXFormat{LaTeX2e}
%<driver>\ProvidesFile{xeCJK.dtx}
\RequirePackage{expl3}
%</driver|package>
%
%<*driver>
\documentclass{l3doc}
\hypersetup{pdfstartview=FitH}
\usepackage{xeCJK}
\fvset{formatcom=\CJKfixedspacing}
\usepackage{metalogo}
\linespread{1.1}
\setlist{nosep}
\setmainfont[Ligatures=TeX]{TeX Gyre Pagella}
\setmonofont{Inconsolata}
\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\normalfont\ttfamily\punctstyle{plain}}
\parindent=2em
\AtBeginDocument{\DeleteShortVerb{\"}\MakeShortVerb{\|}}
\def\contentsname{目录}
\def\email#1{\href{mailto:#1}{\path{#1}}}
\addtolength{\voffset}{-5\baselineskip}
\addtolength\textheight{8\baselineskip}
\makeatletter
\let\orig@meta\meta
\def\meta#1{\orig@meta{\mbox{\normalfont\itshape\CJKfixedspacing#1}}}
\def\TF{true\orvar{}false}
\def\TTF{\defaultvar{true}\orvar{}false}
\def\TFF{true\orvar\defaultvar{false}}
\def\orvar{\char`\|}
\let\defaultvar\textbf
\def\argbrace#1{\char`\{#1\char`\}}
\@addtoreset{CodelineNo}{section}
\def\tokslink#1{\hyperlink{#1}{\ding{51}}}
\makeatother
%</driver>
%<*driver|package>
\GetIdInfo$Id: xeCJK.dtx 343 2012-05-21 05:59:17Z sobenlee $
          {package for typesetting CJK scripts with XeLaTeX}
%</driver|package>
%<*driver>
\begin{document}
  \DocInput{\jobname.dtx}
\end{document}
%</driver>
% \fi
%
% \GetFileInfo{\jobname.sty}
%
% \title{\bfseries\pkg{xeCJK} 宏包}
% \author{\texttt{ctex.org}\thanks{\url{http://www.ctex.org}}}
% \date{\filedate\qquad\fileversion}
% \maketitle
%
% \tableofcontents
%
% \begin{documentation}
%
% \section{简介}
%
% \pkg{xeCJK} 是一个 \XeLaTeX 宏包，用于排版中日韩（CJK）文字。主要功能：
% \begin{enumerate}
% \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} 的依赖 \LaTeX~3 项目的 \pkg{expl3}，\pkg{xparse} 和
%  \pkg{l3keys2e} 宏包，并依赖 \pkg{fontspec}^^A
% \footnote{\url{http://mirrors.ctan.org/macros/latex/contrib/fontspec}}
% 宏包来调用系统字体。个别兼容性功能需要 \pkg{CJK} 宏包的支持。
%
% \pkg{xeCJK} 的原始作者是孙文昌，2009 年 5 月起宏包被收入
% \texttt{ctex-kit}\footnote{\url{http://code.google.com/p/ctex-kit/}} 项目进行
% 维护，目前主要维护者是刘海洋\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 语言支持。对于中文文档，可以使
% 用更为高层的 \pkg{ctex} 宏包或文档类，它将自动调用 \pkg{xeCJK} 并设置好中文
% 字体，同时提供了进一步的本地化支持。详细内容参看 \pkg{ctex} 宏包^^A
% \footnote{\url{http://mirrors.ctan.org/language/chinese/ctex/}}的说明。
%
% \pkg{xeCJK} 提供了大量选项，可以在宏包调用时作为宏包选项或用 |\xeCJKsetup|
% 命令进行设置，详见 \ref{subsec:opts}~节。除了 |\setCJKmainfont| 命令，
% \pkg{xeCJK} 还提供了许多其他命令设置和选择中文字体，详见
% \ref{subsec:fontset}~节。其他更详细的功能也都将在下面详细说明。
%
% \section{用户手册}
%
% \subsection{宏包选项}
% \label{subsec:opts}
%
% \pkg{xeCJK} 以 \meta{key}|=|\meta{var} 的形式提供宏包选项，你可以在调用宏包
% 的时候直接设置这些选项，也可以在调用宏包之后使用 |\xeCJKsetup| 来设置这些选
% 项。
%
% \begin{function}{\xeCJKsetup}
%   \begin{syntax}
%     \cs{xeCJKsetup} \argbrace{\meta{key1}=\meta{var1}, \meta{key2}=\meta{var2}, ...}
%   \end{syntax}
%   其中 \meta{key1}, \meta{key2} 是设置选项，而 \meta{val1}, \meta{val2} 则是对应选项的
%   设置内容。多个选项可以在一个语句中完成设置。例如
%   \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}{xeCJKactive}
%   \begin{syntax}
%     xeCJKactive = \meta{\TTF}
%   \end{syntax}
%   打开/关闭对中文的特殊处理。
% \end{function}
%
% \begin{function}[EXP]{CJKnumber}
%   \begin{syntax}
%     CJKnumber = \meta{\TFF}
%   \end{syntax}
%   是否使用 \pkg{CJKnumb} 宏包。请注意，为了与 \pkg{CJKnumb} 兼容，\pkg{xeCJK} 修改了
%   它的一些内部命令，所以这个选项需要在调用 \pkg{xeCJK} 时设置，而不能通过 |\xeCJKsetup|
%   来设置它。
% \end{function}
%
% \begin{function}[EXP]{indentfirst}
%   \begin{syntax}
%     indentfirst = \meta{\TTF}
%   \end{syntax}
%   跟在章节标题后面的第一段首行是否缩进。这个选项需要在调用 \pkg{xeCJK} 时设置，不能
%   通过 |\xeCJKsetup| 来设置它。\pkg{xeCJK} 宏包中只有 |indentfirst| 和 |CJKnumber|
%   这两个选项，不能通过 |\xeCJKsetup| 来设置。
% \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 = \argbrace{\cs{hspace}\argbrace{0pt plus .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}{CheckSingle}
%   \begin{syntax}
%     CheckSingle = \meta{\TFF}
%   \end{syntax}
%   是否避免单个 CJK 文字单独占一个段落的最后一行。
% \end{function}
%
% \begin{function}{InlineEnv,InlineEnv+,InlineEnv-}
%   \begin{syntax}
%     InlineEnv = \argbrace{env1, env2, env3, ...}
%   \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}{PunctStyle}
%   \begin{syntax}
%     PunctStyle = \marg{\defaultvar{quanjiao}\orvar{}banjiao\orvar{}kaiming\orvar{}hangmobanjiao\orvar{}CCT\orvar{}plain}
%   \end{syntax}
%   设置标点处理格式，有效值分别为
%   \begin{itemize}[font=\ttfamily,labelsep=1em]
%     \item[quanjiao] 全角式：所有标点占一个汉字宽度，相邻两个标点占 1.5 汉字宽度；
%     \item[banjiao]  半角式：所有标点占半个汉字宽度；
%     \item[kaiming]  开明式：句末点号用全角，其他半角；
%     \item[hangmobanjiao] 行末半角式：所有标点占一个汉字宽度，行首行末对齐；
%     \item[CCT]   CCT 格式；
%     \item[plain] 原样（不调整标点间距）。
%   \end{itemize}
% \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{dimen}
%   \end{syntax}
%   缺省状态下，\pkg{xeCJK} 会根据所选择的标点处理格式自动计算标点所占的宽度，如果对缺
%   省设置不满意，可以通过这一选项来改变它。为了使得标点所占的宽度能够适应字体大小的变化，
%   这里设置的 |dimen| 的单位最好用 |em| 等相对距离单位，而不建议使用诸如 |pt| 之类的
%   绝对距离单位。这里的设置对除了 |plain| 以外的所有标点处理格式都有效。同时，这里的
%   设置对所有的 CJK 标点都生效，如果只要设置部分标点，请使用 \ref{subsec:punct} 节的
%   |\xeCJKsetwidth|。
% \end{function}
%
% \begin{function}{AllowBreakBetweenPuncts}
%   \begin{syntax}
%     AllowBreakBetweenPuncts = \meta{\TFF}
%   \end{syntax}
%   缺省状态下，\pkg{xeCJK} 禁止在相邻 CJK 右标点和 CJK 左标点之间换行，可以使用
%   这一选项改变这一设置。
% \end{function}
%
% \subsection{字体设置与选择}
% \label{subsec:fontset}
%
% \begin{function}[EXP]{\setCJKmainfont}
%   \begin{syntax}
%     \cs{setCJKmainfont} \oarg{font features} \Arg{font name}
%   \end{syntax}
%   设置正文罗马族的 CJK 字体，影响 |\rmfamily| 和 |\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}[EXP]{\setCJKsansfont}
%   \begin{syntax}
%     \cs{setCJKsansfont} \oarg{font features} \Arg{font name}
%   \end{syntax}
%   设置正文无衬线族的 CJK 字体，影响 |\sffamily| 和 |\textsf| 的字体。
% \end{function}
%
% \begin{function}[EXP]{\setCJKmonofont}
%   \begin{syntax}
%     \cs{setCJKmonofont}  \oarg{font features} \Arg{font name}
%   \end{syntax}
%   设置正文等宽族的 CJK 字体，影响 |\ttfamily| 和 |\texttt| 的字体。为了有利于等宽
%   字体的代码对齐等情形，\pkg{xeCJK} 在 \meta{font features} 里增加了 |Mono| 这个选项。
% \end{function}
%
% \begin{function}{Mono}
%   \begin{syntax}
%     Mono = \meta{Scale\orvar{}Exspace}
%   \end{syntax}
%   有效值仅为 |Scale| 或 |Exspace|。分别表示使用放缩的方式或改变间距的方式，让 CJK
%   字符的宽度等于西文字体中两个空格的宽度。
% \end{function}
%
% \begin{function}{\CJKfixedspacing,\CJKflexiblespacing}
%   \begin{syntax}
%     \cs{CJKfixedspacing}
%     \cs{CJKflexiblespacing}
%   \end{syntax}
%   |\CJKfixedspacing| 命令设置固定字距和不压缩标点格式，符合上面 |Mono=Exspace| 选
%   项的意义。|\CJKflexiblespacing| 命令恢复可伸缩的字距和标点压缩。抄录 |(verbatim)|
%   环境将自动使用 |\CJKfixedspacing| 的效果。
% \end{function}
%
% \begin{function}{\setCJKmonoscale,\setCJKmonoexspace}
%   \begin{syntax}
%     \cs{setCJKmonoscale}
%     \cs{setCJKmonoexspace}
%   \end{syntax}
%   |\setCJKmonoscale| 和 |\setCJKmonoexspace| 命令分别计算并设置当前字号下西文等宽
%   字体与 CJK 字体的放缩比例和字距，以符合上面所说的 |Mono=Scale| 与 |Mono=Exspace|
%   选项的意义。在字号改变时，应该使用两个命令之一，重新计算相关参数；但注意两个命令不
%   能同时使用。
% \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}{\CJKfamily}
%   \begin{syntax}
%     \cs{CJKfamily} \Arg{family}
%   \end{syntax}
%   用于在文档中切换 |CJK| 字体族，\meta{family} 必须预先声明。
% \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}。事实上，|\newCJKfontfamily| 是 |\setCJKfamilyfont| 和
%   |\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|。
% \end{function}
%
% \begin{function}{\addCJKfontfeatures}
%   \begin{syntax}
%     \cs{addCJKfontfeatures} \Arg{font features}
%   \end{syntax}
%   临时增加当前使用的 CJK 字体的选项。例如，使用
%   \begin{verbatim}
%     \addCJKfontfeatures{Scale=1.1}
%   \end{verbatim}
%   可以将文档中当前使用的 CJK 字体放大为 |1.1|。
% \end{function}
%
% \begin{function}{\CJKrmdefault}
%   保存 |\rmfamily| 所使用的 CJK 字体族，默认值是 |rm|。类似西文字体的 |\rmdefault|。
% \end{function}
%
% \begin{function}{\CJKsfdefault}
%   保存 |\sffamily| 所使用的 CJK 字体族，默认值是 |sf|。类似西文字体的 |\sfdefault|。
% \end{function}
%
% \begin{function}{\CJKttdefault}
%   保存 |\ttfamily| 所使用的 CJK 字体族，默认值是 |tt|。类似西文字体的 |\ttdefault|。
% \end{function}
%
% \begin{function}{\CJKfamilydefault}
%   保存 |\normalfont| 所使用的 CJK 字体族，默认值是 |\CJKrmdefault|。类似西文字
%   体的 |\familydefault|。可以通过更改它来改变文档的默认 CJK 字体族。例如，使用
%   \begin{verbatim}
%     \renewcommand\familydefault{\sfdefault}
%     \renewcommand\CJKfamilydefault{\CJKsfdefault}
%   \end{verbatim}
%   可以将全文的 CJK 和西文默认字体改为无衬线字体族。
% \end{function}
%
% \begin{function}[EXP]{\setCJKmathfont}
%   \begin{syntax}
%     \cs{setCJKmathfont} \oarg{font features} \Arg{font name}
%   \end{syntax}
%   设置数学公式中的 CJK 字体族。如果使用了 |CJKmath| 选项，但是没有使用
%   |\setCJKmathfont| 设置数学公式中的 CJK 字体，那么将使用 |\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 = \argbrace{\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]{\setCJKfallbackfamilyfont}
%   \begin{syntax}
%     \cs{setCJKfallbackfamilyfont} \Arg{family} \oarg{common font features}
%        \  \char`\{
%        \    \argbrace{\oarg{font features 1} \Arg{font name 1}} ,
%        \    \argbrace{\oarg{font features 2} \Arg{font name 2}} ,
%        \     ......
%        \  \char`\}
%   \end{syntax}
%   |\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| 的备用字体。
% \end{function}
%
% \subsubsection{\texorpdfstring{\XeTeX}{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}[formatcom=\fontspec{CMU Typewriter Text}\CJKfixedspacing]
% 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}
%   \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|。
%
%   |\xeCJKDeclareSubCJKBlock| 应该在声明所有的 CJK 字体族之前使用。如果有某个 CJK 字
%   体族没有设置 \meta{block} 选项，将使用 |\CJKfamilydefault| 的 \meta{block} 选项
%   作为该 CJK 字体族的 \meta{block} 选项。如果希望在使用某 CJK 字体族时，不在 CJK 主
%   分区与 \meta{block} 之间切换字体，可以使用 \meta{block}|=*| 选项。
%
% \begin{function}{\xeCJKCancelSubCJKBlock}
%   \begin{syntax}
%     \cs{xeCJKCancelSubCJKBlock} \Arg{block1,block2,...}
%   \end{syntax}
%   在文档中取消对 |CJK| 分区的声明。
% \end{function}
%
% \begin{function}{\xeCJKRestoreSubCJKBlock}
%   \begin{syntax}
%     \cs{xeCJKRestoreSubCJKBlock} \Arg{block1,block2,...}
%   \end{syntax}
%   在文档中恢复对 |CJK| 分区的声明。
% \end{function}
%
% \subsection{设置 CJK 字符范围}
%
% \begin{function}[EXP]{\xeCJKDeclareCharClass}
%   \begin{syntax}
%     \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]{\xeCJKResetPunctClass}
%   \begin{syntax}
%     \cs{xeCJKResetPunctClass}
%   \end{syntax}
%   用于重置标点符号所属的字符类。使用 |\xeCJKDeclareCharClass| 更改字符类别之后，为
%   了确保标点处理的正确性，应该在它之后使用 |\xeCJKResetPunctClass|。
% \end{function}
%
% \begin{function}{\normalspacedchars}
%   \begin{syntax}
%     \cs{normalspacedchars} \Arg{char list}
%   \end{syntax}
%   在 \meta{char list} 中出现的字符两端不自动添加空格，默认设置是 |/|。
% \end{function}
%
% \subsection{设置标点宽度和间距}
% \label{subsec:punct}
%
% 这里的设置对除了 |plain| 以外的所有标点处理格式都有效。
%
% \begin{function}[EXP]{\xeCJKsetwidth}
%   \begin{syntax}
%     \cs{xeCJKsetwidth} \Arg{标点列表} \Arg{dimen}
%   \end{syntax}
%   \meta{标点列表}可以是单个标点，也可以是多个标点。例如，
%   \begin{verbatim}
%     \xeCJKsetwidth{。？}{0.7em}
%   \end{verbatim}
%   将设置句号和问号所占的宽度为 |0.7em|。
% \end{function}
%
% \begin{function}[EXP]{\xeCJKsetkern}
%   \begin{syntax}
%     \cs{xeCJKsetkern} \Arg{标点1} \Arg{标点2} \Arg{dimen}
%   \end{syntax}
%   \pkg{xeCJK} 会根据选定的标点处理格式自动调整相邻两个 |CJK| 标点的距离。如果对缺省
%   配置不满意，可以使用此命令设置两个标点之间的距离。例如，
%   \begin{verbatim}
%     \xeCJKsetkern{：}{“}{0.3em}
%   \end{verbatim}
% \end{function}
%
% \section{已知问题和兼容性}
%
% 为了与 \pkg{CJKnumb} 和 \pkg{CJKulem} 宏包兼容，\pkg{xeCJK} 使用并重新定义了
% \pkg{CJK} 宏包的部分宏命令，如 |\CJKfamily|、|\CJKsymbol| 和 |\CJKpunctsymbol| 等。
% 需要指出，\pkg{xeCJK} 不需要 \pkg{CJK} 的支持，并且 \pkg{xeCJK} 自动禁止在它之后载入
% \pkg{CJK} 宏包。
%
% 可以在 \pkg{xeCJK} 之后载入 \pkg{CJKfntef} 宏包，以实现汉字加点等。
%
% \pkg{xeCJK} 依赖 \XeTeX 的 |\XeTeXinterchartoks| 机制，与使用相同机制的宏包（例如
% \pkg{polyglossia}）会存在大小不一的冲突。\pkg{xeCJK} 虽然为此作了一些处理，但与它们
% 共同使用时应该小心。
%
%\end{documentation}
%
%
%\begin{implementation}
%
% \section{\pkg{xeCJK} 代码实现}
%
%    \begin{macrocode}
%<*package>
%    \end{macrocode}
%
%    \begin{macrocode}
\ProvidesExplPackage{\ExplFileName}{\ExplFileDate}{3.0.6}{\ExplFileDescription}
%    \end{macrocode}
%
% \begin{macro}[aux]
%  {\xeCJK_msg_new:nn ,\xeCJK_error:n,\xeCJK_error:nx,
%  \xeCJK_warning:nx,\xeCJK_info:nx,\xeCJK_info:nxx}
%    \begin{macrocode}
\cs_new_nopar:Npn \xeCJK_msg_new:nn  { \msg_new:nnn      { xeCJK } }
\cs_new_nopar:Npn \xeCJK_error:n     { \msg_error:nn     { xeCJK } }
\cs_new_nopar:Npn \xeCJK_error:nx    { \msg_error:nnx    { xeCJK } }
\cs_new_nopar:Npn \xeCJK_warning:n   { \msg_warning:nn   { xeCJK } }
\cs_new_nopar:Npn \xeCJK_warning:nx  { \msg_warning:nnx  { xeCJK } }
\cs_new_nopar:Npn \xeCJK_warning:nxx { \msg_warning:nnxx { xeCJK } }
\cs_new_nopar:Npn \xeCJK_info:nx     { \msg_info:nnx     { xeCJK } }
\cs_new_nopar:Npn \xeCJK_info:nxx    { \msg_info:nnxx    { xeCJK } }
%    \end{macrocode}
% \end{macro}
%
% \pkg{xeCJK} 必须使用 \XeTeX 引擎的支持。
%    \begin{macrocode}
\xeCJK_msg_new:nn { 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".
  }
\xetex_if_engine:F { \msg_critical:nn { xeCJK } { Require-XeTeX } }
%    \end{macrocode}
%
%    \begin{macrocode}
\xeCJK_msg_new:nn { l3-too-old }
  {
    Support~package~'expl3'~too~old. \\\\
    Please~update~an~up~to~date~version~of~the~bundles\\\\
    'l3kernel'~and~'l3packages'\\\\
    using~your~TeX~package~manager~or~from~CTAN.
  }
\@ifpackagelater { expl3 } { 2012/02/19 } { } { \xeCJK_error:n { l3-too-old } }
%    \end{macrocode}
%
%    \begin{macrocode}
\RequirePackage{xparse}
\RequirePackage{l3keys2e}
%    \end{macrocode}
%
% |\XeTeXglyphbouds| 可以得到一个字符的左右边距，用于标点压缩。
%    \begin{macrocode}
\xeCJK_msg_new:nn { XeTeX-too-old }
  {
    \token_to_str:N \XeTeXglyphbounds\c_space_tl is~not~defined.\\\\
    You~have~to~update~XeTeX~to~the~version~0.9995.0~or~later.
  }
\cs_if_free:NT \XeTeXglyphbounds
  {
    \xeCJK_error:n { XeTeX-too-old }
    \AtBeginDocument { \punctstyle { plain } }
  }
%    \end{macrocode}
%
% 抑制 |BOM|。
%    \begin{macrocode}
\char_set_catcode_ignore:n { "FEFF }
%    \end{macrocode}
%
% \begin{macro}{xeCJKactive}
% \pkg{xeCJK} 功能开关。
%    \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}
%
% \subsection{字符类别设定}\label{sec:xeCJK-class-set}
%
% \pkg{xeCJK} 需要以下字符类别用于字符输出。其中 |Default|、|CJK|、|FullLeft|、
% |FullRight|、|Boundary| 为 \XeTeX\ 中预定义的类别，\pkg{xeCJK} 新增加了\
% |HalfLeft|、|HalfRight|、|NormalSpace|。
% \begin{center}\punctstyle{plain}
% \begin{tabular}{cll}
% \toprule
%   类别        & 说明                & 例子 \\ \midrule
% |Default|     & 西文一般符号       & abc123 \\
% |CJK|         & CJK 表意符号       & 汉字ぁぃぅ \\
% |FullLeft|    & 全角左标点         & （《：“ \\
% |FullRight|   & 全角右标点         & ，。）》” \\
% |HalfLeft|    & 半角左标点         & ( [ \{ \\
% |HalfRight|   & 半角右标点         & , . ? ) ] \} \\
% |NormalSpace| & 前后原始间距的符号 & / \\
% |Boundary|    & 边界              & 空格 \\
% \bottomrule
% \end{tabular}
% \end{center}
%
% \begin{macro}[internal,var]
%   { \xeCJK_Default_class, \xeCJK_CJK_class, \xeCJK_FullLeft_class,
%     \xeCJK_FullRight_class, \xeCJK_Boundary_class }
% 这五类是 \XeTeX\ 预定义的类别。
%    \begin{macrocode}
\int_const:Nn \xeCJK_Default_class   {   0 }
\int_const:Nn \xeCJK_CJK_class       {   1 }
\int_const:Nn \xeCJK_FullLeft_class  {   2 }
\int_const:Nn \xeCJK_FullRight_class {   3 }
\int_const:Nn \xeCJK_Boundary_class  { 255 }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_new_class:n,\g_xeCJK_class_seq}
% 新建一个字符类别。
%    \begin{macrocode}
\cs_new_nopar:Nn \xeCJK_new_class:n
  {
    \exp_args:Nc \newXeTeXintercharclass { xeCJK_#1_class }
    \clist_gclear_new:c { g_xeCJK_#1_range_clist }
    \seq_gput_right:Nv \g_xeCJK_class_seq { xeCJK_#1_class }
  }
\seq_new:N \g_xeCJK_class_seq
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal,var]
%   { \xeCJK_HalfLeft_class, \xeCJK_HalfRight_class, \xeCJK_NormalSpace_class }
% 新增西文半角左/右标点和前后原始间距的符号类。
%    \begin{macrocode}
\xeCJK_new_class:n { HalfLeft }
\xeCJK_new_class:n { HalfRight }
\xeCJK_new_class:n { NormalSpace }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal,var]
%  {\g_xeCJK_base_class_clist,\g_xeCJK_CJK_class_clist,\g_xeCJK_CJK_class_prop}
%    \begin{macrocode}
\clist_set:Nn \g_xeCJK_base_class_clist
  {
    Default, CJK, FullLeft, FullRight, Boundary,
    HalfLeft, HalfRight, NormalSpace
  }
\clist_map_inline:Nn \g_xeCJK_base_class_clist
  { \clist_gclear_new:c { g_xeCJK_#1_range_clist } }
\clist_new:N \g_xeCJK_CJK_class_clist
\prop_new:N \g_xeCJK_CJK_class_prop
\cs_new_nopar:Nn \xeCJK_save_CJK_class:n
  {
    \clist_gput_right:Nx \g_xeCJK_CJK_class_clist {#1}
    \prop_gput:Nvx \g_xeCJK_CJK_class_prop { xeCJK_#1_class } {#1}
  }
\cs_generate_variant:Nn \prop_gput:Nnn { Nvx }
\clist_map_inline:nn { CJK, FullLeft, FullRight } { \xeCJK_save_CJK_class:n {#1} }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_class_num:n}
% |#1| 为字符类别名称，用于取得字符类别对应的编号。
%    \begin{macrocode}
\cs_new_nopar:Nn \xeCJK_class_num:n { \tl_use:c { xeCJK_#1_class } }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[pTF,internal]{\tl_if_blank:x}
%    \begin{macrocode}
\prg_set_conditional:Nnn \tl_if_blank:x { p , T , F , TF }
  {
    \if_int_compare:w \pdftex_strcmp:D { \c_empty_tl } {#1} = \c_zero
      \prg_return_true:
    \else:
      \if_int_compare:w \pdftex_strcmp:D { \c_space_tl } {#1} = \c_zero
        \prg_return_true: \else: \prg_return_false: \fi:
    \fi:
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\xeCJKDeclareCharClass}
% \begin{macro}[aux]{\xeCJK_setcharclass_aux:Nn}
% 用于设置字符所属的类别，|#1| 为类别名称，|#2| 为字符的 |Unicode|，相邻字符用
% 半角逗号隔开，支持类似 |"1100 -> "11FF| 起止范围的使用方式。
%    \begin{macrocode}
\NewDocumentCommand \xeCJKDeclareCharClass { > { \TrimSpaces } m m }
  {
    \clist_gput_right:cx { g_xeCJK_#1_range_clist } {#2}
    \clist_map_inline:xn {#2}
      {
        \str_if_eq:xxF {##1} { -> }
          {
            \xeCJK_setcharclass_aux:Nn \xeCJKsetcharclass {##1}
            { \xeCJK_class_num:n {#1} }
          }
      }
  }
\NewDocumentCommand \xeCJK_setcharclass_aux:Nn
  { m > { \SplitArgument { 1 } { -> } } m } { #1 #2 }
\cs_generate_variant:Nn \clist_map_inline:nn { x }
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJKsetcharclass}
% |#1| 和 |#2| 为字符类别起止的 |Unicode|，|#3| 为类别名称对应编号。
%    \begin{macrocode}
\NewDocumentCommand \xeCJKsetcharclass { m m m }
  {
    \xeCJK_check_num_range:nnNN {#1} {#2} \l_tmpa_int \l_tmpb_int
    \int_set:Nn \l_tmpc_int {#3}
    \prop_if_in:NVTF \g_xeCJK_CJK_class_prop \l_tmpc_int
      { \cs_set_eq:NN \xeCJK_set_catcode:n \char_set_catcode_other:n }
      { \cs_set_eq:NN \xeCJK_set_catcode:n \use_none:n }
    \loop
      \xeCJK_set_catcode:n \l_tmpa_int
      \XeTeXcharclass \l_tmpa_int = \l_tmpc_int
      \if_int_compare:w \l_tmpa_int < \l_tmpb_int
        \int_incr:N \l_tmpa_int
    \repeat
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_check_num_range:nnNN}
%    \begin{macrocode}
\cs_new_nopar:Nn \xeCJK_check_num_range:nnNN
  {
    \bool_if:nTF { \tl_if_blank_p:x {#1} || \tl_if_blank_p:x {#2} }
      {
        \int_set:Nn #3 { \tl_if_blank:xTF {#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}
%
% \begin{macro}{\xeCJKResetPunctClass}
% 用于重置标点符号所属的字符类。
%    \begin{macrocode}
\NewDocumentCommand \xeCJKResetPunctClass { }
  {
    \xeCJKDeclareCharClass { HalfLeft }
      { "28 , "2D , "5B , "60 , "7B }
    \xeCJKDeclareCharClass { HalfRight }
      { "21 , "22 , "25 , "27 , "29 , "2C , "2E , "3A , "3B , "3F , "5D , "7D , }
    \xeCJKDeclareCharClass { FullLeft }
      {
        "2018 , "201C , "2116 , "3008 , "300A , "300C , "300E , "3010 , "3012 ,
        "3014 , "3016 , "3018 , "301A , "301D , "3036 , "E76C , "FE59 , "FE5B ,
        "FE5D , "FE5F , "FE60 , "FE69 , "FE6B , "FF03 , "FF04 , "FF08 , "FF20 ,
        "FF3B , "FF5B , "FFE0 , "FFE1 , "FFE5 , "FFE6 ,
      }
    \xeCJKDeclareCharClass { FullRight }
      {
        "00B7 , "2019 , "201D , "2014 , "2025 , "2026 , "2030 , "2500 , "3001 ,
        "3002 , "3005 , "3006 , "3009 , "300B , "300D , "300F , "3011 , "3015 ,
        "3017 , "3019 , "301B , "301E , "301F , "3041 , "3043 , "3045 , "3047 ,
        "3049 , "3063 , "3083 , "3085 , "3087 , "308E , "309B , "309C , "309D ,
        "309E , "30A1 , "30A3 , "30A5 , "30A7 , "30A9 , "30C3 , "30E3 , "30E5 ,
        "30E7 , "30EE , "30F5 , "30F6 , "30FB , "30FC , "30FD , "30FE , "FE50 ,
        "FE51 , "FE52 , "FE54 , "FE55 , "FE56 , "FE57 , "FE5A , "FE5C , "FE5E ,
        "FE6A , "FF01 , "FF05 , "FF09 , "FF0C , "FF0E , "FF1A , "FF1B , "FF1F ,
        "FF3D , "FF5D , "FF61 , "FF63 , "FF64 , "FF65 , "FF67 , "FF68 , "FF69 ,
        "FF6A , "FF6B , "FF6C , "FF6D , "FF6E , "FF6F , "FF70 , "FF9E , "FF9F ,
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \hypertarget{CJKcharclass}{设置 CJK 文字的范围}。
%    \begin{macrocode}
\xeCJKDeclareCharClass { CJK }
  {
%    \end{macrocode}
% \begin{itemize}
% \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 Kana Supplement （日文假名增补）
%    \begin{macrocode}
    "B000 -> "B0FF ,
%    \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 CJK Compatibility Forms （中日韩兼容形式）
%    \begin{macrocode}
    "FE30 -> "FE4F ,
%    \end{macrocode}
% \item Halfwidth and Fullwidth Forms （半角及全角形式）
%    \begin{macrocode}
    "FF00 -> "FFEF ,
%    \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}
%
% 重置标点符号的字符类。
%    \begin{macrocode}
\xeCJKResetPunctClass
%    \end{macrocode}
%
% \begin{macro}{\normalspacedchars}
% 声明前后不加间距的字符。
%    \begin{macrocode}
\NewDocumentCommand \normalspacedchars { m }
  {
    \tl_map_inline:nn {#1}
      { \XeTeXcharclass `##1 = \xeCJK_class_num:n { NormalSpace } }
  }
\normalspacedchars{/}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_inter_class_toks:nnn,\xeCJK_inter_class_toks:nnc}
% 在相邻类别之间插入内容。
%    \begin{macrocode}
\cs_new_nopar:Nn \xeCJK_inter_class_toks:nnn
  { \XeTeXinterchartoks \xeCJK_class_num:n {#1} \xeCJK_class_num:n {#2} = {#3} }
\cs_generate_variant:Nn \xeCJK_inter_class_toks:nnn { nnc }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_clear_inter_class_toks:nn}
% 清除相邻类别之间的内容。
%    \begin{macrocode}
\cs_new_nopar:Nn \xeCJK_clear_inter_class_toks:nn
  {
    \XeTeXinterchartoks
    \xeCJK_class_num:n {#1} \xeCJK_class_num:n {#2} = { \c_empty_tl }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_pre_inter_class_toks:nnn}
% 在相邻类别之间已有的内容前增加内容。
%    \begin{macrocode}
\cs_new_nopar:Nn \xeCJK_pre_inter_class_toks:nnn
  {
    \use:x
      {
        \XeTeXinterchartoks \xeCJK_class_num:n {#1} \xeCJK_class_num:n {#2} =
          {
            \exp_not:n {#3} \tex_the:D \XeTeXinterchartoks
              \xeCJK_class_num:n {#1} \xeCJK_class_num:n {#2}
          }
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_app_inter_class_toks:nnn,\xeCJK_app_inter_class_toks:nnc}
% 在相邻类别之间已有的内容后追加内容。
%    \begin{macrocode}
\cs_new_nopar:Nn \xeCJK_app_inter_class_toks:nnn
  {
    \use:x
      {
        \XeTeXinterchartoks \xeCJK_class_num:n {#1} \xeCJK_class_num:n {#2} =
          {
            \tex_the:D \XeTeXinterchartoks \xeCJK_class_num:n {#1}
            \xeCJK_class_num:n {#2} \exp_not:n {#3}
          }
      }
  }
\cs_generate_variant:Nn \xeCJK_app_inter_class_toks:nnn { nnc }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_get_inter_class_toks:nn}
% 取出相邻类别之间的内容。
%    \begin{macrocode}
\cs_new_nopar:Nn \xeCJK_get_inter_class_toks:nn
  { \tex_the:D \XeTeXinterchartoks \xeCJK_class_num:n {#1} \xeCJK_class_num:n {#2} }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_copy_inter_class_toks:nnnn}
% 将 |#3| 和 |#4| 之间的内容复制到 |#1| 和 |#2| 之间。
%    \begin{macrocode}
\cs_new_nopar:Nn \xeCJK_copy_inter_class_toks:nnnn
  {
    \use:x
      {
        \xeCJK_inter_class_toks:nnn {#1} {#2}
          { \xeCJK_get_inter_class_toks:nn {#3} {#4} }
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_clear_CJK_toks:}
% 清除边界与 CJK 文字、全角左右标点之间的内容。
%    \begin{macrocode}
\cs_new_nopar:Nn \xeCJK_clear_CJK_toks:
  {
    \clist_map_inline:Nn \g_xeCJK_CJK_class_clist
      { \xeCJK_clear_inter_class_toks:nn { Boundary } {##1} }
  }
%    \end{macrocode}
% \end{macro}
%
% \subsection{字符输出规则}
%
% \hspace*{-.8\marginparwidth}
% \begin{tabular}{l*8c}
% \toprule
%   & |Default| & |CJK| & |FullLeft| & |FullRight| & |HalfLeft|
%   & |HalfRight| & |NormalSpace| & |Boundary| \\ \midrule
% |Default|
%   &
%   & \tokslink{def-cjk}
%   & \tokslink{def-cjk}
%   & \tokslink{def-cjk}
%   &
%   &
%   &
%   & \tokslink{def-bound}\\
% |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-bound-cjk}
%   & \tokslink{cjk-fl-fr}
%   & \tokslink{cjk-fl-fr}
%   & \tokslink{def-cjk}
%   & \tokslink{def-cjk}
%   & \tokslink{def-cjk}
%   & \tokslink{fl-fr-bound-cjk}\\
% |FullRight|
%   & \tokslink{def-cjk}
%   & \tokslink{fl-fr-bound-cjk}
%   & \tokslink{cjk-fl-fr}
%   & \tokslink{cjk-fl-fr}
%   & \tokslink{def-cjk}
%   & \tokslink{def-cjk}
%   & \tokslink{def-cjk}
%   & \tokslink{fl-fr-bound-cjk}\\
% |HalfLeft|
%   &
%   & \tokslink{def-cjk}
%   & \tokslink{def-cjk}
%   & \tokslink{def-cjk} \\
% |HalfRight|
%   &
%   & \tokslink{def-cjk}
%   & \tokslink{def-cjk}
%   & \tokslink{def-cjk}
%   &
%   &
%   &
%   & \tokslink{def-bound} \\
% |NormalSpace|
%   &
%   & \tokslink{def-cjk}
%   & \tokslink{def-cjk}
%   & \tokslink{def-cjk}
%   &
%   &
%   &
%   & \tokslink{def-bound}\\
% |Boundary|
%   & \tokslink{bound-def}
%   & \tokslink{bound-cjk}
%   & \tokslink{fl-fr-bound-cjk}
%   & \tokslink{fl-fr-bound-cjk}
%   & \tokslink{bound-def}
%   &
%   & \tokslink{bound-def}\\
% \bottomrule
% \end{tabular}
%
% \hypertarget{def-cjk}{}
%    \begin{macrocode}
\clist_map_inline:nn { Default, HalfLeft, HalfRight, NormalSpace }
  {
    \clist_map_inline:nn { CJK, FullLeft, FullRight }
      {
        \xeCJK_inter_class_toks:nnn {#1} {##1}
          {
            \c_group_begin_token
            \xeCJK_select_font:
            \xeCJK_clear_inter_class_toks:nn {#1} {##1}
            \xeCJK_clear_CJK_toks:
          }
        \xeCJK_inter_class_toks:nnn {##1} {#1} { \c_group_end_token }
      }
    \xeCJK_app_inter_class_toks:nnn {#1} { CJK } { \CJKsymbol }
    \clist_map_inline:nn { FullLeft, FullRight }
      { \xeCJK_app_inter_class_toks:nnc {#1} {##1} { xeCJK_CJK_and_##1:N } }
  }
%    \end{macrocode}
%
% \hypertarget{bound-def}{}
%    \begin{macrocode}
\clist_map_inline:nn { Default, HalfLeft, NormalSpace }
  {
    \xeCJK_inter_class_toks:nnn { Boundary } {#1}
      {
        \bool_if:nTF
          {
            \l_xeCJK_xecglue_bool &&
            \int_compare_p:nNn \etex_lastnodetype:D = \c_eleven &&
            \skip_if_eq_p:nn \tex_lastskip:D \l_xeCJK_space_glue_tl
          }
          {
            \tex_unskip:D
            \bool_if:nTF
              {
                \int_compare_p:nNn \tex_lastkern:D = \c_one ||
                \int_compare_p:nNn \tex_lastkern:D = \c_two
              }
              { \CJKecglue } { \c_space_token }
          }
          {
            \int_compare:nNnTF \tex_lastkern:D = \c_one { \CJKecglue }
              { \int_compare:nNnT \tex_lastkern:D = \c_two \xeCJK_space_or_xecglue: }
          }
      }
    \str_if_eq:xxF {#1} { NormalSpace }
      { \xeCJK_app_inter_class_toks:nnn { CJK } {#1} { \CJKecglue } }
  }
%    \end{macrocode}
%
% \hypertarget{def-bound}{}
%    \begin{macrocode}
\clist_map_inline:nn { Default, HalfRight, NormalSpace }
  {
    \xeCJK_inter_class_toks:nnn {#1} { Boundary }
      {
        \peek_catcode:NTF \c_space_token
          { { \xeCJK_default_space_kern: } } { { \xeCJK_default_kern: } }
      }
    \str_if_eq:xxF {#1} { NormalSpace }
      { \xeCJK_pre_inter_class_toks:nnn {#1} { CJK } { \CJKecglue } }
  }
%    \end{macrocode}
%
% \hypertarget{bound-cjk}{}
%    \begin{macrocode}
\xeCJK_inter_class_toks:nnn { Boundary } { CJK } { \xeCJK_Boundary_and_CJK: }
%    \end{macrocode}
%
% \begin{macro}[internal]{\xeCJK_Boundary_and_CJK:}
%    \begin{macrocode}
\cs_new_nopar:Nn \xeCJK_Boundary_and_CJK:
  {
    {
      \bool_if:nTF
        {
          \int_compare_p:nNn \tex_lastkern:D = \c_one ||
          \int_compare_p:nNn \tex_lastkern:D = \c_two
        }
        { \CJKglue }
        {
          \bool_if:nTF
            {
              \int_compare_p:nNn \tex_lastkern:D = \c_three             ||
              \int_compare_p:nNn \etex_lastnodetype:D = \c_ten          ||
               ( \int_compare_p:nNn \etex_lastnodetype:D = \c_twelve &&
                 \g_xeCJK_itcorr_bool )
            }
            { \CJKecglue }
            {
              \bool_if:nT
                {
                  \l_xeCJK_xecglue_bool &&
                  \int_compare_p:nNn \etex_lastnodetype:D = \c_eleven &&
                  ( \skip_if_eq_p:nn \tex_lastskip:D \l_xeCJK_space_glue_tl ||
                    \int_compare_p:nNn \tex_lastskip:D = \l_xeCJK_ecglue_dim )
                }
                {
                  \skip_set_eq:NN \l_tmpa_skip \tex_lastskip:D
                  \tex_unskip:D
                  \bool_if:nTF
                    {
                      \int_compare_p:nNn \tex_lastkern:D = \c_four     ||
                      \int_compare_p:nNn \etex_lastnodetype:D = \c_ten ||
                      \int_compare_p:nNn \tex_lastkern:D = \c_three    ||
                      \g_xeCJK_itcorr_bool
                    }
                    { \CJKecglue }
                    {
                      \bool_if:nT
                        {
                          ! ( \int_compare_p:nNn \tex_lastkern:D = \c_one ) ||
                          ! ( \int_compare_p:nNn \tex_lastkern:D = \c_two ) ||
                          \l_xeCJK_space_bool
                        }
                        { \skip_horizontal:N \l_tmpa_skip }
                    }
                }
            }
        }
      \bool_gset_false:N \g_xeCJK_itcorr_bool
    }
    \c_group_begin_token
    \xeCJK_clear_CJK_toks:
    \xeCJK_select_font:
    \CJKsymbol
  }
\tl_set:Nn \l_xeCJK_space_glue_tl
  {
    \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
  }
%    \end{macrocode}
% \end{macro}
%
% \hypertarget{cjk-bound}{}
%    \begin{macrocode}
\xeCJK_inter_class_toks:nnn { CJK } { Boundary }
  {
    \c_group_end_token
    { \xeCJK_CJK_kern: }
    \xeCJK_ignorespaces:
  }
%    \end{macrocode}
%
% \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_nopar:Nn \xeCJK_CJK_and_CJK:N { \CJKglue \CJKsymbol {#1} }
%    \end{macrocode}
% \end{macro}
%
% \hypertarget{fl-fr-bound-cjk}{}
%    \begin{macrocode}
\clist_map_inline:nn { FullLeft, FullRight }
  {
    \xeCJK_inter_class_toks:nnn { Boundary } {#1}
      {
        \c_group_begin_token
        \xeCJK_clear_CJK_toks:
        \xeCJK_select_font:
        \use:c { xeCJK_CJK_and_#1:N }
      }
    \xeCJK_inter_class_toks:nnn {#1} { CJK } { \CJKsymbol }
    \xeCJK_inter_class_toks:nnn {#1} { Boundary } { \c_group_end_token \tex_ignorespaces:D }
  }
%    \end{macrocode}
%
%    \begin{macrocode}
\clist_map_inline:Nn \g_xeCJK_base_class_clist
  {
    \bool_if:nF
      { \str_if_eq_p:xx {#1} { FullLeft } || \str_if_eq_p:xx {#1} { FullRight } }
      {
        \xeCJK_pre_inter_class_toks:nnn { FullLeft }  {#1} { \nobreak }
        \xeCJK_pre_inter_class_toks:nnn { FullRight } {#1} { \xeCJK_after_FullRight: }
      }
  }
%    \end{macrocode}
%
% \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]{\xeCJK_punct_rule:n}
% 用于抹去标点符号的左/右空白。
%    \begin{macrocode}
\cs_new_nopar:Nn \xeCJK_punct_rule:n
  {
    \tex_vrule:D
      \@width  \dim_eval:c { \l_xeCJK_punct_coor_tl/rule/#1 }
      \@depth  \c_zero_dim
      \@height \c_zero_dim \scan_stop:
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_punct_glue:n}
% 根据所选的标点处理方式在标点符号左/右增加的空白。
%    \begin{macrocode}
\cs_new_nopar:Nn \xeCJK_punct_glue:n
  {
    \skip_horizontal:n
      {
        \dim_eval:c { \l_xeCJK_punct_coor_tl/glue/#1 }
        \@plus  \dim_eval:n { \tl_use:c { \l_xeCJK_punct_coor_tl/glue/#1 } /4 }
        \@minus \dim_eval:n { \tl_use:c { \l_xeCJK_punct_coor_tl/glue/#1 } /2 }
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_punct_kern:n}
% 相邻两个标点之间的间距。
%    \begin{macrocode}
\cs_new_nopar:Nn \xeCJK_punct_kern:n
  { \tex_kern:D \dim_eval:c { \l_xeCJK_punct_coor_tl/kern/#1 } }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\dim_eval:c}
%    \begin{macrocode}
\cs_generate_variant:Nn \dim_eval:n { c }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_before_FullLeft:N}
%    \begin{macrocode}
\cs_new_nopar:Nn \xeCJK_before_FullLeft:N
  {
    \tl_set:Nx \l_xeCJK_lastpunct_tl {#1}
    \xeCJK_punct_rule:n { l/#1 }
    \CJKpunctsymbol {#1}
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_after_FullRight:}
%    \begin{macrocode}
\cs_new_nopar:Nn \xeCJK_after_FullRight:
  {
    \xeCJK_get_punct_bounds:nV { r } \l_xeCJK_lastpunct_tl
    \xeCJK_punct_rule:n { r/\l_xeCJK_lastpunct_tl }
    \xeCJK_punct_glue:n { r/\l_xeCJK_lastpunct_tl }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_CJK_and_FullLeft:N}
%    \begin{macrocode}
\cs_new_nopar:Nn \xeCJK_CJK_and_FullLeft:N
  {
    \xeCJK_get_punct_bounds:nN { l } {#1}
    \int_compare:nNnF \etex_lastnodetype:D = \c_one
      { \xeCJK_punct_glue:n { l/#1 } }
    \xeCJK_before_FullLeft:N {#1}
    \xeCJK_punct_if_middle:NT {#1}
      {
        \xeCJK_punct_rule:n { m/#1 }
        \xeCJK_punct_glue:n { l/#1 }
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_CJK_and_FullRight:N}
%    \begin{macrocode}
\cs_new_nopar:Nn \xeCJK_CJK_and_FullRight:N
  {
    \xeCJK_get_punct_bounds:nN { r } {#1}
    \xeCJK_punct_if_long:NTF {#1} \CJKglue
      {
        \nobreak
        \xeCJK_punct_if_middle:NT {#1}
          {
            \xeCJK_punct_glue:n { r/#1 }
            \xeCJK_punct_rule:n { m/#1 }
          }
      }
    \tl_gset:Nx \l_xeCJK_lastpunct_tl {#1}
    \CJKpunctsymbol {#1}
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_FullLeft_and_FullLeft:N}
%    \begin{macrocode}
\cs_new_nopar:Nn \xeCJK_FullLeft_and_FullLeft:N
  {
    \nobreak
    \xeCJK_get_punct_bounds:nN { l } {#1}
    \xeCJK_get_kern:VN \l_xeCJK_lastpunct_tl {#1}
    \xeCJK_punct_kern:n { \l_xeCJK_lastpunct_tl - #1 }
    \xeCJK_before_FullLeft:N {#1}
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_FullLeft_and_FullRight:N}
%    \begin{macrocode}
\cs_new_nopar:Nn \xeCJK_FullLeft_and_FullRight:N
  {
    \nobreak
    \xeCJK_get_punct_bounds:nN { r } {#1}
    \xeCJK_get_kern:VN \l_xeCJK_lastpunct_tl {#1}
    \xeCJK_punct_kern:n { \l_xeCJK_lastpunct_tl - #1 }
    \nobreak
    \tl_set:Nx \l_xeCJK_lastpunct_tl {#1}
    \CJKpunctsymbol {#1}
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_FullRight_and_FullLeft:N}
%    \begin{macrocode}
\cs_new_nopar:Nn \xeCJK_FullRight_and_FullLeft:N
  {
    \xeCJK_punct_rule:n { r/\l_xeCJK_lastpunct_tl }
    \xeCJK_get_punct_bounds:nN { l } {#1}
    \xeCJK_get_kern:VN \l_xeCJK_lastpunct_tl {#1}
    \xeCJK_punct_kern:n { \l_xeCJK_lastpunct_tl - #1 }
    \xeCJK_punct_nobreak:
    \xeCJK_before_FullLeft:N {#1}
 }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_FullRight_and_FullRight:N}
%    \begin{macrocode}
\cs_new_nopar:Nn \xeCJK_FullRight_and_FullRight:N
  {
    \xeCJK_punct_rule:n { r/\l_xeCJK_lastpunct_tl }
    \xeCJK_get_punct_bounds:nN { r } {#1}
    \xeCJK_get_kern:VN \l_xeCJK_lastpunct_tl {#1}
    \xeCJK_punct_kern:n { \l_xeCJK_lastpunct_tl - #1 }
    \nobreak
    \tl_set:Nx \l_xeCJK_lastpunct_tl {#1}
    \CJKpunctsymbol {#1}
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]
% {\xeCJK_CJK_kern:,\xeCJK_CJK_space_kern:,
%  \xeCJK_default_kern:, \xeCJK_default_space_kern:}
% 用于判断插入的各种 |kern|。
%    \begin{macrocode}
\cs_new_nopar:Nn \xeCJK_CJK_kern:           { \tex_kern:D -1 sp  \tex_kern:D 1 sp }
\cs_new_nopar:Nn \xeCJK_CJK_space_kern:     { \tex_kern:D -2 sp  \tex_kern:D 2 sp }
\cs_new_nopar:Nn \xeCJK_default_kern:       { \tex_kern:D -3 sp  \tex_kern:D 3 sp }
\cs_new_nopar:Nn \xeCJK_default_space_kern: { \tex_kern:D -4 sp  \tex_kern:D 4 sp }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{CJKglue}
% CJK 文字之间插入的 |glue|。
%    \begin{macrocode}
\keys_define:nn { xeCJK / options } { CJKglue .tl_set_x:N = \CJKglue }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{CJKecglue,xCJKecglue}
% CJK 与西文和数学行内数学公式之间自动添加的空白。
%    \begin{macrocode}
\keys_define:nn { xeCJK / options }
  {
    CJKecglue .code:n =
      {
        \cs_set:Npx \CJKecglue {#1}
        \settowidth \l_xeCJK_ecglue_dim \CJKecglue
      },
    xCJKecglue .choice:,
    xCJKecglue / true    .code:n =
      {
        \bool_set_true:N  \l_xeCJK_xecglue_bool
        \cs_set_eq:NN \xeCJK_space_or_xecglue: \CJKecglue
      },
    xCJKecglue / false   .code:n =
      {
        \bool_set_false:N \l_xeCJK_xecglue_bool
        \cs_set_eq:NN \xeCJK_space_or_xecglue: \c_space_token
      },
    xCJKecglue / unknown .code:n =
      {
        \bool_set_true:N  \l_xeCJK_xecglue_bool
        \cs_set:Npx \CJKecglue {#1}
        \settowidth \l_xeCJK_ecglue_dim \CJKecglue
        \cs_set_eq:NN \xeCJK_space_or_xecglue: \CJKecglue
      },
    xCJKecglue .default:n = { true } ,
  }
\dim_new:N \l_xeCJK_ecglue_dim
\bool_new:N \l_xeCJK_xecglue_bool
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{CJKspace}
% 是否保留 CJK 文字间的空白，默认不保留。
%    \begin{macrocode}
\keys_define:nn { xeCJK / options }
  {
    CJKspace .choice:,
    CJKspace / true  .code:n = { \CJKspace   },
    CJKspace / false .code:n = { \CJKnospace },
    CJKspace      .default:n = { true },
    space         .meta:n = { CJKspace = true  },
    nospace       .meta:n = { CJKspace = false },
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\CJKspace,\CJKnospace}
% 是否保留 CJK 文字间的空白，默认不保留。
%    \begin{macrocode}
\NewDocumentCommand \CJKspace { }
  {
    \bool_set_true:N \l_xeCJK_space_bool
    \cs_set_eq:NN \xeCJK_ignorespaces: \xeCJK_peek_math:
  }
\NewDocumentCommand \CJKnospace { }
  {
    \bool_set_false:N \l_xeCJK_space_bool
    \cs_set_eq:NN \xeCJK_ignorespaces: \xeCJK_ignore_spaces:
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_ignore_spaces:,\xeCJK_peek_math:}
%    \begin{macrocode}
\cs_new_nopar:Nn \xeCJK_ignore_spaces:
  {
    \peek_catcode:NTF \c_space_token
      {
        \int_compare:nNnT \tex_lastkern:D = \c_one
          { \tex_unkern:D \tex_unkern:D { \xeCJK_CJK_space_kern: } }
        \peek_catcode_ignore_spaces:NTF \c_math_toggle_token
          { \xeCJK_space_or_xecglue: }
          {
            \bool_if:nT
              {
                \xeCJK_if_outer_macro_p:c { l_peek_token } ||
                \token_if_cs_p:c { l_peek_token }
              }
              { \xeCJK_space_or_xecglue: }
          }
      }
      {
        \xeCJK_if_outer_macro:cF { l_peek_token }
          {
            \token_if_math_toggle:cTF { l_peek_token } \CJKecglue
              { \cs_if_exist:cF { l_peek_token } \exp_not:N }
          }
      }
  }
\cs_new_nopar:Nn \xeCJK_peek_math:
  {
    \peek_catcode:NTF \c_space_token
      {
        \int_compare:nNnT \tex_lastkern:D = \c_one
          { \tex_unkern:D \tex_unkern:D { \xeCJK_CJK_space_kern: } }
        \peek_catcode_ignore_spaces:NF \c_space_token { \xeCJK_space_or_xecglue: }
      }
      {
        \xeCJK_if_outer_macro:cF { l_peek_token }
          {
            \token_if_math_toggle:cTF { l_peek_token } \CJKecglue
              { \cs_if_exist:cF { l_peek_token } \exp_not:N }
          }
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \subsection{段末孤字处理}
%
% \begin{macro}{CheckSingle}
% \begin{macro}[aux]{CheckSingleglue}
% 孤字处理功能选项。
%    \begin{macrocode}
\keys_define:nn { xeCJK / options }
  {
    CheckSingle .choice:,
    CheckSingle / true  .code:n = { \xeCJKenablechecksingle  },
    CheckSingle / false .code:n = { \xeCJKdisablechecksingle },
    CheckSingle      .default:n = { true },
    CJKchecksingle      .meta:n = { CheckSingle = true } ,
    CheckSingleglue .tl_set_x:N = { \CJKchecksingleglue },
  }
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJKenablechecksingle,\xeCJKdisablechecksingle}
% 孤字处理功能开关。
%    \begin{macrocode}
\bool_new:N \l_xeCJK_checksingle_bool
\NewDocumentCommand \xeCJKenablechecksingle { }
  {
    \bool_if:NF \l_xeCJK_checksingle_bool
      {
        \bool_set_true:N \l_xeCJK_checksingle_bool
        \cs_set_eq:NN \xeCJK_checksingle_save_CJKsymbol:N \xeCJK_CJK_and_CJK:N
        \cs_set_eq:NN \xeCJK_checksingle_save_CJKglue \CJKglue
        \cs_set_eq:NN \xeCJK_CJK_and_CJK:N \xeCJK_checksingle:N
        \cs_set_nopar:Npn \CJKglue { \CJKchecksingleglue }
      }
  }
\NewDocumentCommand \xeCJKdisablechecksingle { }
  {
    \bool_if:NT \l_xeCJK_checksingle_bool
      {
        \bool_set_false:N \l_xeCJK_checksingle_bool
        \cs_set_eq:NN \CJKglue \xeCJK_checksingle_save_CJKglue
        \cs_set_eq:NN \xeCJK_CJK_and_CJK:N \xeCJK_checksingle_save_CJKsymbol:N
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_checksingle:N,\xeCJK_checksingle:NN,\xeCJK_checksingle:NNN}
%    \begin{macrocode}
\cs_new_nopar:Nn \xeCJK_checksingle:N
  {
    \peek_catcode:NTF \c_catcode_other_token
      { \xeCJK_checksingle:NN {#1} }
      {
        \bool_if:nTF
          {
            ! ( \xeCJK_if_outer_macro_p:c { l_peek_token } )                    &&
            \tl_if_empty_p:x { \token_get_arg_spec:c { l_peek_token } }         &&
            \exp_args:NNc \exp_args:No \tl_if_single_token_p:n { l_peek_token } &&
            \exp_args:NNc \exp_after:wN \token_if_other_p:N    { l_peek_token }
          }
          { \xeCJK_checksingle:NN {#1} }
          { \xeCJK_checksingle_save_CJKsymbol:N {#1} }
      }
  }
\cs_new_nopar:Nn \xeCJK_checksingle:NN
  {
    \peek_catcode:NTF \c_catcode_other_token
      { \xeCJK_checksingle_save_CJKsymbol:N {#1} #2 }
      {
        \bool_if:nTF
          {
            ! ( \xeCJK_if_outer_macro_p:c { l_peek_token } )  &&
              ( \token_if_space_p:c       { l_peek_token } ||
                \token_if_math_toggle_p:c { l_peek_token } ||
                \token_if_cs_p:c          { l_peek_token } )
          }
          {
            \bool_if:nTF { \token_if_space_p:c { l_peek_token } }
              {
                \peek_catcode_ignore_spaces:NTF \c_catcode_other_token
                  { \xeCJK_checksingle_save_CJKsymbol:N {#1} #2 \c_space_token }
                  {
                    \bool_if:nTF
                      {
                        ! ( \xeCJK_if_outer_macro_p:c { l_peek_token } )  &&
                          ( \token_if_math_toggle_p:c { l_peek_token } ||
                            \token_if_cs_p:c          { l_peek_token } )
                      }
                      { \xeCJK_checksingle:NNN {#1} {#2} \c_space_token }
                      { \xeCJK_checksingle_save_CJKsymbol:N {#1} #2 \c_space_token }
                  }
              }
              { \xeCJK_checksingle:NNN {#1} {#2} { } }
          }
          { \xeCJK_checksingle_save_CJKsymbol:N {#1} #2 }
      }
  }
\cs_new_nopar:Nn \xeCJK_checksingle:NNN
  {
    \bool_if:nTF
      {
        \token_if_eq_meaning_p:NN \l_peek_token \par ||
        \token_if_eq_meaning_p:NN \l_peek_token \[ % \]
      }
      { \CJKsymbol {#1} \nobreak #2 }
      { \xeCJK_checksingle_env:NNN {#1} {#2} {#3} }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_checksingle_env:NNN,\xeCJK_checksingle_env:NNNNn}
%    \begin{macrocode}
\cs_new_nopar:Nn \xeCJK_checksingle_env:NNN
  {
    \peek_catcode_remove:NTF \c_math_toggle_token
      {
        \peek_catcode:NTF \c_math_toggle_token
          { \CJKsymbol {#1} \nobreak #2 \c_math_toggle_token }
          { \xeCJK_checksingle_save_CJKsymbol:N {#1} #2#3 \c_math_toggle_token }
      }
      {
        \peek_meaning_remove:NTF \begin
          { \xeCJK_checksingle_env:NNNNn {#1} {#2} {#3} \begin }
          {
            \peek_meaning_remove:NTF \end
              { \xeCJK_checksingle_env:NNNNn {#1} {#2} {#3} \end }
              { \xeCJK_checksingle_save_CJKsymbol:N {#1} #2#3 }
          }
      }
  }
\cs_new_nopar:Nn \xeCJK_checksingle_env:NNNNn
  {
    \clist_if_in:NnTF \g_xeCJK_inline_env_clist {#5}
      { \xeCJK_checksingle_save_CJKsymbol:N {#1} #2#3 }
      { \CJKsymbol {#1} \nobreak #2 }
    \scan_stop: #4 {#5}
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{InlineEnv,InlineEnv+,InlineEnv-}
%    \begin{macrocode}
\keys_define:nn { xeCJK / options }
  {
    InlineEnv .clist_gset:N = \g_xeCJK_inline_env_clist ,
    InlineEnv+      .code:n =
      {
        \clist_gput_right:Nx \g_xeCJK_inline_env_clist {#1}
        \clist_gremove_duplicates:N \g_xeCJK_inline_env_clist
      },
    InlineEnv-      .code:n =
      {
        \clist_map_inline:xn {#1}
          { \clist_gremove_all:Nn \g_xeCJK_inline_env_clist {##1} }
      },
  }
%    \end{macrocode}
% \end{macro}
%
%    \begin{macrocode}
\cs_generate_variant:Nn \token_if_cs_p:N          { c }
\cs_generate_variant:Nn \token_if_space_p:N       { c }
\cs_generate_variant:Nn \token_get_arg_spec:N     { c }
\cs_generate_variant:Nn \token_if_math_toggle_p:N { c }
\cs_generate_variant:Nn \token_if_math_toggle:NTF { c }
%    \end{macrocode}
%
% \begin{macro}[pTF,internal]{\xeCJK_if_outer_macro:c}
% 判断是否是 |\outer| 宏，输入的是控制序列的名字。
%    \begin{macrocode}
\group_begin:
\char_set_lccode:nn { `\; } { `\: }
\char_set_lccode:nn { `\Z } { `\t }
\char_set_lccode:nn { `\Y } { `\c }
\char_set_lccode:nn { `\T } { `\T }
\char_set_lccode:nn { `\F } { `\F }
\tl_map_function:nN { O U Z E R M A Y R ; } \char_set_catcode_other:N
\tl_to_lowercase:n
  {
    \group_end:
    \prg_new_conditional:Nnn \xeCJK_if_outer_macro:c { p, T , F , TF }
      {
        \exp_last_unbraced:Nf \xeCJK_if_outer_macro_aux:w
        \cs_meaning:c {#1} OUZER ~ MAYRO ; \q_stop
      }
    \cs_new_nopar:Npn \xeCJK_if_outer_macro_aux:w #1 OUZER ~ MAYRO ; #2 \q_stop
      {
        \prg_case_str:xxn { #1 ~ }
          {
            { \token_to_str:N \ } { \prg_return_true: }
            { \token_to_str:N \long \token_to_str:N \ } { \prg_return_true: }
            { \token_to_str:N \protected \token_to_str:N \ } { \prg_return_true: }
            { \token_to_str:N \protected \token_to_str:N \long \token_to_str:N \ }
            { \prg_return_true: }
          }
          { \prg_return_false: }
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[pTF,internal]{\xeCJK_if_eq_meaning:cN}
% 对 \pkg{l3token} 打的补丁，使 |\peek_catcode_ignore_spaces:NTF| 等可用于 |\outer| 宏。
%    \begin{macrocode}
\tl_replace_once:Nnn \peek_ignore_spaces_execute_branches:
  { \token_if_eq_meaning:NNTF \l_peek_token }
  { \xeCJK_if_eq_meaning:cNTF { l_peek_token } }
\prg_new_conditional:Nnn \xeCJK_if_eq_meaning:cN { p , T , F , TF }
  {
    \exp_args:Nc \if_meaning:w {#1} #2
      \prg_return_true: \else: \prg_return_false: \fi:
  }
%    \end{macrocode}
% \end{macro}
%
% \subsection{增加 CJK 子分区}
%
% \begin{macro}[internal]{\xeCJK_UL_subclass_patch_tl,\g_xeCJK_CJK_subclass_clist}
%    \begin{macrocode}
\tl_new:N \xeCJK_UL_subclass_patch_tl
\clist_new:N \g_xeCJK_CJK_subclass_clist
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_erase_CJKsymbol:, \xeCJK_restore_CJKsymbol:}
%    \begin{macrocode}
\cs_new_nopar:Nn \xeCJK_erase_CJKsymbol:
  {
    \cs_gset_eq:NN \CJKsymbol_Block \CJKsymbol
    \cs_gset_eq:NN \CJKsymbol \prg_do_nothing:
  }
\cs_new_nopar:Nn \xeCJK_restore_CJKsymbol:
  {
    \cs_gset_eq:NN \CJKsymbol \CJKsymbol_Block
    \CJKsymbol
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\xeCJKDeclareSubCJKBlock}
% 声明 CJK 子区范围，|#1| 为自定义名称，|#2| 为子区的 |Unicode| 范围。
%    \begin{macrocode}
\NewDocumentCommand \xeCJKDeclareSubCJKBlock { m m }
  { \xeCJKDeclareSubCharClass { CJK } {#1} {#2} }
\@onlypreamble \xeCJKDeclareSubCJKBlock
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\xeCJKCancelSubCJKBlock,\xeCJKRestoreSubCJKBlock}
% 取消和恢复对 CJK 子区的声明。
%    \begin{macrocode}
\bool_new:N \l_xeCJK_sub_cancel_bool
\NewDocumentCommand \xeCJKCancelSubCJKBlock { m }
  {
    \bool_if:NF \l_xeCJK_sub_cancel_bool
      {
        \bool_set_true:N \l_xeCJK_sub_cancel_bool
        \xeCJK_sub_restore_or_cancel:n {#1}
      }
  }
\NewDocumentCommand \xeCJKRestoreSubCJKBlock { m }
  {
    \bool_if:NT \l_xeCJK_sub_cancel_bool
      {
        \bool_set_false:N \l_xeCJK_sub_cancel_bool
        \xeCJK_sub_restore_or_cancel:n {#1}
      }
  }
\cs_new_nopar:Nn \xeCJK_sub_restore_or_cancel:n
  {
    \clist_map_inline:xn {#1}
      {
        \cs_if_free:cTF { xeCJK_ CJK/##1 _class }
          { \xeCJK_error:nx { SubBlock-undefined } {##1} }
          {
            \xeCJKDeclareCharClass
              { CJK \bool_if:NF \l_xeCJK_sub_cancel_bool { /##1 } }
              { \clist_use:c { g_xeCJK_CJK/##1_range_clist } }
          }
      }
  }
\xeCJK_msg_new:nn { SubBlock-undefined }
  {
    The~CJK~sub~block~'#1'~is~undefined.\\\\
    Try~to~use~\token_to_str:N \xeCJKDeclareSubCJKBlock \c_space_tl
    to~declare~it.
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJKDeclareSubCharClass}
%    \begin{macrocode}
\NewDocumentCommand \xeCJKDeclareSubCharClass
  { > { \TrimSpaces } m > { \TrimSpaces } m m }
  {
    \cs_if_free:cT { xeCJK_ #1/#2 _class }
      {
        \xeCJK_new_class:n { #1/#2 }
        \xeCJK_set_sub_class_toks:nn {#1} {#2}
        \xeCJK_new_sub_key:n {#2}
        \xeCJK_UL_subclass_patch:nn {#1} {#2}
      }
    \xeCJKDeclareCharClass { #1/#2 } {#3}
  }
\@onlypreamble \xeCJKDeclareSubCharClass
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_set_sub_class_toks:nn}
%    \begin{macrocode}
\cs_new_nopar:Nn \xeCJK_set_sub_class_toks:nn
  {
    \clist_map_inline:Nn \g_xeCJK_base_class_clist
      {
        \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:xxTF {##1} { CJK }
          {
            \xeCJK_pre_inter_class_toks:nnn {##1} { #1/#2 }
              { \xeCJK_switch_font:nn {#1} {#2} }
          }
          {
            \xeCJK_pre_inter_class_toks:nnn {##1} { #1/#2 }
              { \xeCJK_erase_CJKsymbol: }
            \xeCJK_app_inter_class_toks:nnn {##1} { #1/#2 }
              { \xeCJK_switch_font:nn {#1} {#2} \xeCJK_restore_CJKsymbol: }
          }
      }
    \xeCJK_copy_inter_class_toks:nnnn { #1/#2 } { #1/#2 } {#1} {#1}
    \clist_if_empty:NF \g_xeCJK_CJK_subclass_clist
      {
        \clist_map_inline:Nn \g_xeCJK_CJK_subclass_clist
          {
            \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 }
              { \xeCJK_switch_font:nn {#2} {##1} }
            \xeCJK_pre_inter_class_toks:nnn { #1/##1 } { #1/#2 }
              { \xeCJK_switch_font:nn {##1} {#2} }
          }
      }
    \clist_gput_right:Nn \g_xeCJK_CJK_subclass_clist {#2}
    \xeCJK_save_CJK_class:n { #1/#2 }
    \clist_map_inline:nn { CJK, FullLeft, FullRight }
      {
        \xeCJK_pre_inter_class_toks:nnn { #1/#2 } {##1}
          { \xeCJK_switch_font:nn {#2} {#1} }
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_UL_subclass_patch:nn}
%    \begin{macrocode}
\cs_new_nopar:Nn \xeCJK_UL_subclass_patch:nn
  {
    \tl_put_right:Nn \xeCJK_UL_subclass_patch_tl
      {
        \clist_map_inline:nn { Default, HalfLeft, HalfRight }
          {
            \str_if_eq:xxTF {##1} { HalfLeft }
              { \xeCJK_inter_class_toks:nnn {#1} { #1/#2 } { \CJKecglue \CJKsymbol } }
              { \xeCJK_inter_class_toks:nnn {#1} { #1/#2 } { \CJKsymbol } }
            \xeCJK_inter_class_toks:nnn { Boundary } { #1/#2 }
              {
                \xeCJK_erase_CJKsymbol:
                \xeCJK_Boundary_and_CJK:
                \xeCJK_switch_font:nn {#1} {#2}
                \xeCJK_restore_CJKsymbol:
              }
          }
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \subsection{标点处理}
%
% \begin{macro}[internal,var]{\g_xeCJK_punctstyle_clist}
% 标点处理方式。
%    \begin{macrocode}
\clist_new:N \g_xeCJK_punctstyle_clist
\clist_set:Nn \g_xeCJK_punctstyle_clist
  { CCT , halfwidth , fullwidth , marginkerning , mixedwidth , plain }
\clist_map_inline:Nn \g_xeCJK_punctstyle_clist
  { \tl_const:cn { c_xeCJK_ps_#1_tl } {#1} }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\keys_choices_generate:V, .generate_choices:V}
%    \begin{macrocode}
\cs_generate_variant:Nn \keys_choices_generate:n { V }
\cs_new_protected:cpn { \c_keys_props_root_tl .generate_choices:V } #1
  { \keys_choices_generate:V {#1} }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}
%    {PunctStyle, AllowBreakBetweenPuncts, KaiMingPunct,
%     LongPunct, MiddlePunct,PunctWidth}
% 相关选项声明。
%    \begin{macrocode}
\keys_define:nn { xeCJK / options }
  {
    AllowBreakBetweenPuncts .choice:,
    AllowBreakBetweenPuncts / true  .code:n = { \xeCJKallowbreakbetweenpuncts },
    AllowBreakBetweenPuncts / false .code:n = { \xeCJKnobreakbetweenpuncts },
    AllowBreakBetweenPuncts      .default:n = { true },
    KaiMingPunct  .code:n = { \xeCJK_set_special_punct:nn { mixedwidth } {#1} },
    KaiMingPunct+ .code:n = { \xeCJK_add_special_punct:nn { mixedwidth } {#1} },
    KaiMingPunct- .code:n = { \xeCJK_sub_special_punct:nn { mixedwidth } {#1} },
    LongPunct     .code:n = { \xeCJK_set_special_punct:nn { long } {#1} },
    LongPunct+    .code:n = { \xeCJK_add_special_punct:nn { long } {#1} },
    LongPunct-    .code:n = { \xeCJK_sub_special_punct:nn { long } {#1} },
    MiddlePunct+  .code:n = { \xeCJK_add_special_punct:nn { middle } {#1} },
    MiddlePunct   .code:n = { \xeCJK_set_special_punct:nn { middle } {#1} },
    MiddlePunct-  .code:n = { \xeCJK_sub_special_punct:nn { middle } {#1} },
    PunctWidth .tl_gset:N = \g_xeCJK_punct_width_tl ,
    PunctStyle .choice_code:n =
      {
        \tl_set:Nx \l_xeCJK_punctstyle_tl { \l_keys_choice_tl }
        \tl_if_eq:NNT \l_xeCJK_punctstyle_tl \c_xeCJK_ps_plain_tl
                      { \xeCJKallowbreakbetweenpuncts }
      },
    PunctStyle .generate_choices:V = \g_xeCJK_punctstyle_clist,
    PunctStyle / banjiao       .meta:n = { PunctStyle = halfwidth },
    PunctStyle / quanjiao      .meta:n = { PunctStyle = fullwidth },
    PunctStyle / kaiming       .meta:n = { PunctStyle = mixedwidth },
    PunctStyle / hangmobanjiao .meta:n = { PunctStyle = marginkerning },
    PunctStyle / unknown .code:n =
      { \xeCJK_error:nx { PunctStyle-undefined } \l_keys_value_tl },
  }
\xeCJK_msg_new:nn { PunctStyle-undefined }
  {
    Punctstyle~"#1"~is~not~available. \\\\
    You~can~only~use~one~of\\\\
    "\g_xeCJK_punctstyle_clist".\\
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJKallowbreakbetweenpuncts, \xeCJKnobreakbetweenpuncts}
%    \begin{macrocode}
\NewDocumentCommand \xeCJKallowbreakbetweenpuncts { }
  { \cs_set_eq:NN \xeCJK_punct_nobreak: \xeCJK_zero_glue: }
\NewDocumentCommand \xeCJKnobreakbetweenpuncts { }
  { \cs_set_eq:NN \xeCJK_punct_nobreak: \nobreak }
\cs_new_protected_nopar:Nn \xeCJK_zero_glue: { \skip_horizontal:N \c_zero_skip }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]
% {\g_xeCJK_special_ps_clist,   \xeCJK_set_special_punct:nn,
%  \xeCJK_add_special_punct:nn, \xeCJK_sub_special_punct:nn}
%    \begin{macrocode}
\clist_set:Nn \g_xeCJK_special_ps_clist { mixedwidth , long , middle }
\clist_map_inline:Nn \g_xeCJK_special_ps_clist
  {
    \tl_new:c    { l_xeCJK_#1_punct_tl   }
    \prop_new:c  { l_xeCJK_#1_punct_prop }
  }
\cs_new_nopar:Nn \xeCJK_set_special_punct:nn
  {
    \tl_set:cx { l_xeCJK_#1_punct_tl } {#2}
    \prop_clear:c { l_xeCJK_#1_punct_prop }
    \tl_map_inline:cn { l_xeCJK_#1_punct_tl }
      { \prop_put:cnn  { l_xeCJK_#1_punct_prop } {##1} { } }
  }
\cs_new_nopar:Nn \xeCJK_add_special_punct:nn
  {
    \tl_put_right:cx { l_xeCJK_#1_punct_tl } {#2}
    \tl_map_inline:cn { l_xeCJK_#1_punct_tl }
      { \prop_put:cnn  { l_xeCJK_#1_punct_prop } {##1} { } }
  }
\cs_new_nopar:Nn \xeCJK_sub_special_punct:nn
  {
    \tl_map_inline:xn {#2}
      {
        \tl_remove_all:cn { l_xeCJK_#1_punct_tl } {##1}
        \prop_del:cn { l_xeCJK_#1_punct_prop } {##1}
      }
  }
\cs_generate_variant:Nn \tl_map_inline:nn { x }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\dim_set:Nc,\dim_add:Nc,\dim_set_min:Nc}
%    \begin{macrocode}
\cs_generate_variant:Nn \dim_set:Nn { Nc }
\cs_generate_variant:Nn \dim_add:Nn { Nc }
\cs_generate_variant:Nn \dim_set_min:Nn { Nc }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal,var]{\l_xeCJK_punct_coor_tl}
% 记录当前字体状态下的标点处理方式。
%    \begin{macrocode}
\tl_set:Nn \l_xeCJK_punct_coor_tl { \l_xeCJK_font_coor_tl/\l_xeCJK_punctstyle_tl }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_get_punct_bounds:nN}
% |#1| 为 |l/r|，|#2| 为标点字符，返回标点的实际左/右空白的负值和根据标点处理方式
% 决定添加的左/右空白。全角方式直接添加标点的实际左/右空白；半角方式添加标点左右空白
% 中的最小值；开明方式与半角相同，只在开明标点再增加实际左/右空白的一半。
%    \begin{macrocode}
\cs_new_nopar:Nn \xeCJK_get_punct_bounds:nN
  {
    \tl_if_exist:cF { \l_xeCJK_punct_coor_tl/rule/#1/#2 }
      {
        \tl_if_eq:NNTF \l_xeCJK_punctstyle_tl \c_xeCJK_ps_plain_tl
          {
            \clist_map_inline:nn { glue , rule , bound }
              { \tl_gset:cV { \l_xeCJK_punct_coor_tl/##1/#1/#2 } \c_zero_dim }
            \tl_gset:cV { \l_xeCJK_punct_coor_tl/rule/m/#2 } \c_zero_dim
          }
          {
            \tl_if_exist:cF { \l_xeCJK_punct_coor_tl/bound/#1/#2 }
              { { \xeCJK_select_font: \xeCJK_get_punct_dimen:N {#2} } }
            \xeCJK_punct_if_long:NTF {#2}
              {
                \dim_zero:N \l_tmpa_dim
                \dim_zero:N \l_tmpb_dim
                \dim_zero:N \l_tmpc_dim
              }
              {
                \dim_set:Nc \l_tmpa_dim { \l_xeCJK_punct_coor_tl/bound/#1/#2 }
                \dim_set:Nc \l_tmpb_dim
                  { \l_xeCJK_punct_coor_tl/bound/\str_if_eq:xxTF {#1} lrl/#2 }
                \dim_set:Nn \l_tmpc_dim
                  {
                    \cs_if_exist_use:cF { g_xeCJK_punct_width/#2 }
                      {
                        \tl_if_blank:xTF \g_xeCJK_punct_width_tl
                          { \c_zero_dim }
                          { \g_xeCJK_punct_width_tl }
                      }
                  }
                \xeCJK_punct_if_middle:NTF {#2}
                  {
                    \dim_set:Nn \l_tmpc_dim
                      {
                        \dim_compare:nNnTF \l_tmpc_dim > \c_zero_dim
                          { \l_tmpc_dim }
                          {
                            \prg_case_tl:Nnn \l_xeCJK_punctstyle_tl
                              {
                                \c_xeCJK_ps_halfwidth_tl  { .5 em }
                                \c_xeCJK_ps_mixedwidth_tl { .5 em }
                                \c_xeCJK_ps_CCT_tl        { .7 em }
                                \c_xeCJK_ps_fullwidth_tl  {  1 em }
                              }
                              { 1 em }
                          }
                        - \tl_use:c { \l_xeCJK_punct_coor_tl/dimen/#2 }
                      }
                    \dim_set:Nn \l_tmpc_dim { .5 \l_tmpc_dim }
                  }
                  {
                    \dim_compare:nNnTF \l_tmpc_dim > \c_zero_dim
                      {
                        \dim_set:Nn \l_tmpc_dim
                          {
                            \l_tmpc_dim - \l_tmpb_dim
                            - \tl_use:c { \l_xeCJK_punct_coor_tl/dimen/#2 }
                          }
                      }
                      {
                        \dim_set_eq:NN  \l_tmpc_dim \l_tmpa_dim
                        \dim_set_min:Nn \l_tmpc_dim \l_tmpb_dim
                        \prg_case_tl:Nnn \l_xeCJK_punctstyle_tl
                          {
                            \c_xeCJK_ps_halfwidth_tl { \prg_do_nothing: }
                            \c_xeCJK_ps_mixedwidth_tl
                              {
                                \xeCJK_punct_if_mixedwidth:NT {#2}
                                  {
                                    \dim_add:Nn \l_tmpc_dim { .5 \l_tmpa_dim }
                                    \dim_set_min:Nn \l_tmpc_dim \l_tmpa_dim
                                  }
                              }
                            \c_xeCJK_ps_CCT_tl
                              {
                                \xeCJK_punct_if_mixedwidth:NTF {#2}
                                  { \dim_add:Nn \l_tmpc_dim { .5 \l_tmpa_dim } }
                                  { \dim_add:Nn \l_tmpc_dim { .3 \l_tmpa_dim } }
                                \dim_set_min:Nn \l_tmpc_dim \l_tmpa_dim
                              }
                          }
                          { \dim_set_eq:NN \l_tmpc_dim \l_tmpa_dim }
                      }
                  }
                \dim_set_max:Nn \l_tmpc_dim \c_zero_dim
              }
            \tl_gset:cx { \l_xeCJK_punct_coor_tl/rule/#1/#2 }
                        { - \dim_use:N \l_tmpa_dim }
            \tl_gset:cx { \l_xeCJK_punct_coor_tl/rule/m/#2  }
                        { - \dim_use:N \l_tmpb_dim }
            \tl_gset:cV { \l_xeCJK_punct_coor_tl/glue/#1/#2 } \l_tmpc_dim
          }
      }
  }
\cs_generate_variant:Nn \xeCJK_get_punct_bounds:nN { nV }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\xeCJKsetwidth}
%    \begin{macrocode}
\NewDocumentCommand \xeCJKsetwidth { m m }
  { \tl_map_inline:xn {#1} { \tl_gset:cn { g_xeCJK_punct_width/##1 } {#2} } }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[pTF,internal]
% {\xeCJK_punct_if_right:N,\xeCJK_punct_if_both_left_right:NN,
%  \xeCJK_punct_if_long:N,\xeCJK_punct_if_middle:N,
%  \xeCJK_punct_if_mixedwidth:N}
%    \begin{macrocode}
\prg_new_conditional:Nnn \xeCJK_punct_if_right:N { p , T , F , TF }
  {
    \tl_if_exist:cTF { \l_xeCJK_punct_coor_tl/glue/r/#1 }
      \prg_return_true: \prg_return_false:
  }
\prg_new_conditional:Nnn \xeCJK_punct_if_both_left_right:NN { p , T , F , TF }
  {
    \bool_if:nTF
      {
        ( \tl_if_exist_p:c { \l_xeCJK_punct_coor_tl/glue/r/#1 } &&
          \tl_if_exist_p:c { \l_xeCJK_punct_coor_tl/glue/r/#2 } )  ||
        ( \tl_if_exist_p:c { \l_xeCJK_punct_coor_tl/glue/l/#1 } &&
          \tl_if_exist_p:c { \l_xeCJK_punct_coor_tl/glue/l/#2 } )
      }
      \prg_return_true: \prg_return_false:
  }
\cs_generate_variant:Nn \prg_new_conditional:Nnn { c }
\clist_map_inline:Nn \g_xeCJK_special_ps_clist
  {
    \prg_new_conditional:cnn { xeCJK_punct_if_#1:N } { p , T , F , TF }
      {
        \prop_if_in:cnTF { l_xeCJK_#1_punct_prop } {##1}
          \prg_return_true: \prg_return_false:
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_get_kern:NN,\xeCJK_calc_kern:NN}
% 标点挤压。行末半角方式，相邻标点不挤压；全角方式，相邻两个标点占 1.5 汉字宽度，
% 半角、开明和 CCT 方式相邻标点占一个汉字宽度。
%    \begin{macrocode}
\cs_new_nopar:Nn \xeCJK_get_kern:NN
  {
    \tl_if_exist:cF { \l_xeCJK_punct_coor_tl/kern/#1-#2 }
      {
        \dim_zero:N \l_tmpa_dim
        \tl_if_eq:NNF \l_xeCJK_punctstyle_tl \c_xeCJK_ps_plain_tl
          {
            \tl_if_exist:cTF { g_xeCJK_punct/kern/#1-#2 }
              { \dim_set:Nc \l_tmpa_dim { g_xeCJK_punct/kern/#1-#2 } }
              { \xeCJK_calc_kern:NN {#1} {#2} }
          }
        \tl_gset:cV { \l_xeCJK_punct_coor_tl/kern/#1-#2 } \l_tmpa_dim
      }
  }
\cs_new_nopar:Nn \xeCJK_calc_kern:NN
  {
    \tl_if_eq:NNTF \l_xeCJK_punctstyle_tl \c_xeCJK_ps_marginkerning_tl
      {
        \xeCJK_punct_if_right:NT {#1}
          { \dim_add:Nc \l_tmpa_dim { \l_xeCJK_punct_coor_tl/bound/r/#1 } }
        \xeCJK_punct_if_right:NF {#2}
          { \dim_add:Nc \l_tmpa_dim { \l_xeCJK_punct_coor_tl/bound/l/#2 } }
      }
      {
        \tl_if_eq:NNTF \l_xeCJK_punctstyle_tl \c_xeCJK_ps_fullwidth_tl
          { \xeCJK_calc_kern:NnNN \l_tmpa_dim { 1.5 em } {#1} {#2} }
          {
            \xeCJK_punct_if_both_left_right:NNTF {#1} {#2}
              {
                \xeCJK_punct_if_mixedwidth:NT {#1}
                  {
                    \xeCJK_calc_kern:NnNN \l_tmpa_dim { 1 em } {#1} {#2}
                    \dim_compare:nNnT \l_tmpa_dim < { .1 em }
                      { \dim_set:Nc \l_tmpa_dim { \l_xeCJK_punct_coor_tl/bound/l/#1 } }
                  }
              }
              {
                \bool_if:nTF
                  {
                    \xeCJK_punct_if_mixedwidth_p:N {#1} &&
                    ! ( \tl_if_eq_p:NN \l_xeCJK_punctstyle_tl \c_xeCJK_ps_halfwidth_tl )
                  }
                  {
                    \dim_set:Nc \l_tmpa_dim { \l_xeCJK_punct_coor_tl/glue/r/#1 }
                    \dim_set:Nn \l_tmpa_dim { .7 \l_tmpa_dim }
                  }
                  {
                    \xeCJK_calc_kern:NnNN \l_tmpa_dim { 1 em } {#1} {#2}
                    \dim_compare:nNnT \l_tmpa_dim < { .1 em }
                      {
                        \dim_set:Nc     \l_tmpa_dim { \l_xeCJK_punct_coor_tl/bound/l/#1 }
                        \dim_set_min:Nc \l_tmpa_dim { \l_xeCJK_punct_coor_tl/bound/r/#1 }
                        \dim_set:Nc     \l_tmpb_dim { \l_xeCJK_punct_coor_tl/bound/l/#2 }
                        \dim_set_min:Nc \l_tmpb_dim { \l_xeCJK_punct_coor_tl/bound/r/#2 }
                        \dim_set_max:Nn \l_tmpa_dim \l_tmpb_dim
                      }
                  }
              }
          }
        \bool_if:nT
          { \xeCJK_punct_if_long_p:N {#1} || \xeCJK_punct_if_long_p:N {#2} }
          { \dim_set_max:Nn \l_tmpa_dim { .1 em } }
      }
    \dim_set_max:Nn \l_tmpa_dim \c_zero_dim
  }
\cs_generate_variant:Nn \xeCJK_get_kern:NN { V }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_calc_kern:NnNN}
% |#3| 和 |#4| 为相邻的两个标点，|#2| 为要确定的相邻两个标点总共占的宽度，|#1| 是
% 尺寸寄存器，用于记录根据所占宽度计算得出的相邻两个标点间距。
%    \begin{macrocode}
\cs_new_nopar:Nn \xeCJK_calc_kern:NnNN
  {
    \dim_set:Nn #1
      {
        #2
        - \tl_use:c { \l_xeCJK_punct_coor_tl/
                      \xeCJK_punct_if_right:NTF {#3} { bound } { glue } /l/#3 }
        - \tl_use:c { \l_xeCJK_punct_coor_tl/
                      \xeCJK_punct_if_right:NTF {#4} { glue } { bound } /r/#4 }
        - \tl_use:c { \l_xeCJK_punct_coor_tl/dimen/#3 }
        - \tl_use:c { \l_xeCJK_punct_coor_tl/dimen/#4 }
        \xeCJK_punct_if_both_left_right:NNT {#3} {#4}
          { + \tl_use:c { \l_xeCJK_punct_coor_tl/rule/m/#4 } }
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_get_punct_dimen:N}
% 返回标点的左右空白和实际尺寸。对于破折号，计算两标点之间的空白，保证它中间不被断开。
%    \begin{macrocode}
\cs_new_nopar:Nn \xeCJK_get_punct_dimen:N
  {
    \xeCJK_get_glyph_bounds:nNN { left } {#1} \l_tmpa_dim
    \tl_gset:cV { \l_xeCJK_punct_coor_tl/bound/l/#1 } \l_tmpa_dim
    \dim_set_eq:NN \l_tmpb_dim \l_tmpa_dim
    \xeCJK_get_glyph_bounds:nNN { right } {#1} \l_tmpa_dim
    \tl_gset:cV { \l_xeCJK_punct_coor_tl/bound/r/#1 } \l_tmpa_dim
    \dim_add:Nn \l_tmpb_dim \l_tmpa_dim
    \tl_gset:cx { \l_xeCJK_punct_coor_tl/dimen/#1 }
      { \dim_eval:n { \etex_fontcharwd:D \tex_font:D `#1 - \l_tmpb_dim } }
    \bool_if:nT
      {
        \xeCJK_punct_if_long_p:N {#1} &&
        ! ( \str_if_eq_p:xx {#1} { … } || \str_if_eq_p:xx {#1} { ‥ } )
      }
      {
        \clist_map_inline:Nn \g_xeCJK_punctstyle_clist
          {
            \str_if_eq:xxF \c_xeCJK_ps_plain_tl {##1}
              {
                \tl_gset:cx { \l_xeCJK_font_coor_tl/##1/kern/#1-#1 }
                  { - \dim_use:N \l_tmpb_dim }
              }
          }
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_get_glyph_bounds:nNN}
% 用 |\XeTeXglyphbounds| 计算标点的上下左右空白。
%    \begin{macrocode}
\cs_new_nopar:Nn \xeCJK_get_glyph_bounds:nNN
  {
    \dim_set:Nn #3
      {
        \XeTeXglyphbounds
          \prg_case_str:xxn {#1}
            {
                { left   } { \c_one   }     { top    } { \c_two  }
                { right  } { \c_three }     { bottom } { \c_four }
            }
            { \c_three }
          \int_eval:n { \XeTeXcharglyph `#2 }
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\xeCJKsetkern}
% 手动设置相邻标点的距离，仅在当前字体状态下有效。
%    \begin{macrocode}
\NewDocumentCommand \xeCJKsetkern { m m m }
  { \tl_gset:cn { g_xeCJK_punct/kern/#1-#2 } {#3} }
%    \end{macrocode}
% \end{macro}
%
% \subsection{后备字体}
%
% \begin{macro}{AutoFallBack}
% 后备字体的宏包选项声明。
%    \begin{macrocode}
\keys_define:nn { xeCJK / options }
  {
    AutoFallBack .choice:,
    AutoFallBack / true  .code:n = { \xeCJKenablefallback },
    AutoFallBack / false .code:n = { \xeCJKdisablefallback },
    AutoFallBack      .default:n = { true },
    fallback             .meta:n = { AutoFallBack = true },
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJKenablefallback, \xeCJKdisablefallback}
% 后备字体的启用开关，默认关闭。
%    \begin{macrocode}
\bool_new:N \l_xeCJK_fallback_bool
\NewDocumentCommand \xeCJKenablefallback { }
  {
    \bool_if:NF \l_xeCJK_fallback_bool
      {
        \bool_set_true:N \l_xeCJK_fallback_bool
        \cs_set_eq:NN \xeCJK_fallback_save_CJKsymbol:N \CJKsymbol
        \cs_set_eq:NN \CJKsymbol \xeCJK_fallback_testsymbol:N
      }
  }
\NewDocumentCommand \xeCJKdisablefallback { }
  {
    \bool_if:NT \l_xeCJK_fallback_bool
      {
        \bool_set_false:N \l_xeCJK_fallback_bool
        \cs_set_eq:NN \CJKsymbol \xeCJK_fallback_save_CJKsymbol:N
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_fallback_testsymbol:N}
% 测试当前字体中是否存在当前字符，如存在则直接输出，否则启用后备字体。
%    \begin{macrocode}
\cs_new_nopar:Nn \xeCJK_fallback_testsymbol:N
  {
    \font_glyph_if_exist:NnTF \tex_font:D {`#1}
      { \xeCJK_fallback_save_CJKsymbol:N {#1} }
      {
        \xeCJK_family_if_exist:nTF { \xeCJK@family/FallBack }
          { {
              \tl_put_right:Nn \xeCJK@family { /FallBack }
              \xeCJK_select_font:
              \xeCJK_fallback_testsymbol:N {#1}
          } }
          {
            \xeCJK_warning:nx { fallback } {#1}
            \xeCJK_fallback_save_CJKsymbol:N {#1}
          }
      }
  }
\xeCJK_msg_new:nn { fallback }
  {
    CJKfamily~'\xeCJK@family'~
    ( \prop_get:NV \g_xeCJK_family_prop \xeCJK@family )~
    does~not~contain~glyph~'#1'~(U+\int_to_hexadecimal:n {`#1}).\\
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\setCJKfallbackfamilyfont}
% \begin{macro}[aux]{\xeCJK_set_family_fallback:nnn}
%    \begin{macrocode}
\NewDocumentCommand \setCJKfallbackfamilyfont { m O{} m }
  {
    \exp_args:Nx \tl_if_in:nnTF {#3} { , }
      { \xeCJK_set_family_fallback:nnn {#1} {#2} {#3} }
      { \xeCJK_set_family:nnn { #1/FallBack } {#2} {#3} }
  }
\cs_new_nopar:Nn \xeCJK_set_family_fallback:nnn
  {
    \group_begin:
    \tl_set:Nx \l_xeCJK_family_fb_tl {#1}
    \prop_get:NVNF \g_xeCJK_family_prop \l_xeCJK_family_fb_tl \l_xeCJK_fontname_tl
      { \tl_clear:N \l_xeCJK_fontname_tl }
    \clist_map_inline:xn {#3}
      {
        \tl_put_right:Nn \l_xeCJK_family_fb_tl { /FallBack }
        \xeCJK_get_sub_features:nn \l_xeCJK_family_fb_tl {##1}
        \clist_put_left:cx { l_xeCJK_ \l_xeCJK_family_fb_tl _fontfeat_clist } {#2}
        \xeCJK_set_family:ncc \l_xeCJK_family_fb_tl
          { l_xeCJK_ \l_xeCJK_family_fb_tl _fontfeat_clist }
          { l_xeCJK_ \l_xeCJK_family_fb_tl _fontname_tl }
      }
    \group_end:
  }
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
%
% \subsection{CJK 字体族声明方式}
%
%    \begin{macrocode}
\bool_new:N \g_xeCJK_AutoFakeBold_bool
\bool_new:N \g_xeCJK_AutoFakeSlant_bool
\fp_new:N \g_xeCJK_EmboldenFactor_fp
\fp_new:N \g_xeCJK_SlantFactor_fp
%    \end{macrocode}
%
% \begin{macro}{AutoFakeBold, AutoFakeSlant,EmboldenFactor,SlantFactor}
% 伪粗体和伪斜体的宏包选项声明。
%    \begin{macrocode}
\keys_define:nn { xeCJK / options }
  {
    AutoFakeBold .choice:,
    AutoFakeBold / true    .code:n =
      { \bool_set_true:N  \g_xeCJK_AutoFakeBold_bool },
    AutoFakeBold / false   .code:n =
      { \bool_set_false:N \g_xeCJK_AutoFakeBold_bool },
    AutoFakeBold / unknown .code:n =
      {
        \bool_set_true:N  \g_xeCJK_AutoFakeBold_bool
        \fp_set:Nn \g_xeCJK_EmboldenFactor_fp { \l_keys_value_tl }
      },
    AutoFakeBold .default:n  = { true },
    AutoFakeSlant .choice:,
    AutoFakeSlant / true     .code:n =
      { \bool_set_true:N  \g_xeCJK_AutoFakeSlant_bool },
    AutoFakeSlant / false    .code:n =
      { \bool_set_false:N \g_xeCJK_AutoFakeSlant_bool },
    AutoFakeSlant / unknown  .code:n =
      {
        \bool_set_true:N  \g_xeCJK_AutoFakeSlant_bool
        \fp_set:Nn \g_xeCJK_SlantFactor_fp { \l_keys_value_tl }
      },
    AutoFakeSlant .default:n = { true },
    EmboldenFactor .fp_set:N = \g_xeCJK_EmboldenFactor_fp,
    SlantFactor    .fp_set:N = \g_xeCJK_SlantFactor_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}
%
% \begin{macro}[internal]{\keys_set_known:nxN}
%    \begin{macrocode}
\cs_generate_variant:Nn \keys_set_known:nnN { nx }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_new_sub_key:n,\g_xeCJK_sub_key_clist}
% 用于定义 CJK 子区字体和备用字体的选项。
%    \begin{macrocode}
\clist_new:N \g_xeCJK_sub_key_clist
\cs_new_nopar:Nn \xeCJK_new_sub_key:n
  {
    \clist_gput_right:Nx \g_xeCJK_sub_key_clist {#1}
    \keys_define:nn { xeCJK / features }
      {
        #1 .code:n =
          {
            \tl_if_blank:xTF {##1}
              {
                \bool_set_false:c { l_xeCJK_copy_#1_bool }
                \bool_set_false:c { l_xeCJK_add_#1_bool }
                \tl_put_right:Nx \l_xeCJK_family_tl { /#1 }
              }
              {
                \clist_put_right:Nx \l_xeCJK_sub_key_clist {#1}
                \str_if_eq:xxTF {##1} *
                  {
                    \bool_set_true:c  { l_xeCJK_copy_#1_bool }
                    \bool_set_false:c { l_xeCJK_add_#1_bool }
                  }
                  {
                    \bool_set_false:c { l_xeCJK_copy_#1_bool }
                    \bool_set_true:c  { l_xeCJK_add_#1_bool }
                    \xeCJK_get_sub_features:nn {#1} {##1}
                  }
              }
          },
        #1 .default:n = \c_empty_tl,
      }
  }
\cs_generate_variant:Nn \xeCJK_new_sub_key:n { x }
\cs_new_nopar:Nn \xeCJK_get_sub_features:nn
  {
    \tl_set:Nx \l_tmpa_tl {#2}
    \clist_clear:N \l_xeCJK_sub_fontfeat_clist
    \xeCJK_gobble_brace:N \l_tmpa_tl
    \exp_args:No \tl_if_head_eq_charcode:nNTF \l_tmpa_tl [ % ]
      {
        \exp_after:wN \xeCJK_get_sub_features_aux:n \l_tmpa_tl \c_empty_tl
        \tl_if_blank:xT \l_xeCJK_sub_fontname_tl
          {
            \tl_set:Nx \l_xeCJK_sub_fontname_tl \l_tmpa_tl
            \clist_clear:N \l_xeCJK_sub_fontfeat_clist
          }
      }
      { \tl_set:Nx \l_xeCJK_sub_fontname_tl \l_tmpa_tl }
    \tl_if_blank:xTF \l_xeCJK_sub_fontname_tl
      { \tl_set:Nx \l_xeCJK_sub_fontname_tl \l_xeCJK_fontname_tl }
      { \tl_replace_all:Nnx \l_xeCJK_sub_fontname_tl * \l_xeCJK_fontname_tl }
    \clist_set:cx { l_xeCJK_#1_fontfeat_clist } \l_xeCJK_sub_fontfeat_clist
    \tl_set:cx { l_xeCJK_#1_fontname_tl } \l_xeCJK_sub_fontname_tl
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[aux]
% {\xeCJK_gobble_brace:N,\xeCJK_get_sub_features:nn,\xeCJK_get_sub_features_aux:n}
%    \begin{macrocode}
\cs_new_nopar:Nn \xeCJK_gobble_brace:N
  {
    \exp_args:No \tl_if_head_group:nT #1
      {
        \exp_last_unbraced:NNo \tl_set:Nn #1 #1
        \xeCJK_gobble_brace:N #1
      }
  }
\cs_new_nopar:Npn \xeCJK_get_sub_features_aux:n [#1] #2
  {
    \clist_set:Nx \l_xeCJK_sub_fontfeat_clist {#1}
    \tl_set:Nx \l_xeCJK_sub_fontname_tl {#2}
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{FallBack}
%    \begin{macrocode}
\xeCJK_new_sub_key:n { FallBack }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{.clist_set_x:N,.clist_set_x:c}
%    \begin{macrocode}
\cs_set_protected:cpn { \c_keys_props_root_tl .clist_set_x:N } #1
  { \keys_variable_set:NnN #1 { clist } x }
\cs_set_protected:cpn { \c_keys_props_root_tl .clist_set_x:c } #1
  { \keys_variable_set:cnN {#1} { clist } x }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\g_xeCJK_features_id_prop, BoldFont, ItalicFont, BoldItalicFont,
% SlantedFont, BoldSlantedFont,BoldFeatures, ItalicFeatures, BoldItalicFeatures,
% SlantedFeatures, BoldSlantedFeatures}
% 调用字体的属性声明，同 \pkg{fontspec} 宏包。
%    \begin{macrocode}
\cs_new_nopar:Nn \xeCJK_map_features_id:n
  { \prop_get:Nn \g_xeCJK_features_id_prop { #1 } }
\prop_new:N \g_xeCJK_features_id_prop
\prop_put:Nnn \g_xeCJK_features_id_prop { bf   } { Bold        }
\prop_put:Nnn \g_xeCJK_features_id_prop { it   } { Italic      }
\prop_put:Nnn \g_xeCJK_features_id_prop { bfit } { BoldItalic  }
\prop_put:Nnn \g_xeCJK_features_id_prop { sl   } { Slanted     }
\prop_put:Nnn \g_xeCJK_features_id_prop { bfsl } { BoldSlanted }
\prop_map_inline:Nn \g_xeCJK_features_id_prop
  {
    \keys_define:nn { xeCJK / features }
      {
        #2Font        .tl_set_x:c = { l_xeCJK_fontname_#1_tl },
        #2Features .clist_set_x:c = { l_xeCJK_fontfeat_#1_clist } ,
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{AutoFakeBold, AutoFakeSlant, Mono}
%    \begin{macrocode}
\keys_define:nn { xeCJK / features }
  {
    AutoFakeBold  .choice:,
    AutoFakeBold / false   .code:n =
      { \bool_set_false:N \l_xeCJK_AutoFakeBold_bool },
    AutoFakeBold / unknown .code:n =
      {
        \bool_set_true:N \l_xeCJK_AutoFakeBold_bool
        \fp_set:Nn \l_xeCJK_EmboldenFactor_fp { \l_keys_value_tl }
      },
    AutoFakeBold .default:n  = { \g_xeCJK_EmboldenFactor_fp },
    AutoFakeSlant  .choice:,
    AutoFakeSlant / false   .code:n =
      { \bool_set_false:N \l_xeCJK_AutoFakeSlant_bool },
    AutoFakeSlant / unknown .code:n =
      {
        \bool_set_true:N \l_xeCJK_AutoFakeSlant_bool
        \fp_set:Nn \l_xeCJK_SlantFactor_fp { \l_keys_value_tl }
      },
    AutoFakeSlant .default:n  = { \g_xeCJK_SlantFactor_fp },
    Mono .choice:,
    Mono / Exspace .code:n =
      {
        \xeCJK_set_monoexspace:
        \tl_clear:N \l_xeCJK_monoscale_tl
      },
    Mono / Scale   .code:n =
      {
        \xeCJK_set_monoscale:
        \tl_set:Nx \l_xeCJK_monoscale_tl { Scale = { \fp_use:N \g_xeCJK_monoscale_fp } }
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_set_init:}
%    \begin{macrocode}
\cs_new_nopar:Nn \xeCJK_set_init:
  {
    \tl_clear:N \l_xeCJK_monoscale_tl
    \int_gincr:N \g_xeCJK_fontspec_int
    \prop_clear:N \l_xeCJK_add_font_prop
    \prop_map_inline:Nn \g_xeCJK_features_id_prop
      {
        \tl_clear:c    { l_xeCJK_fontname_##1_tl    }
        \clist_clear:c { l_xeCJK_fontfeat_##1_clist }
      }
    \clist_clear:N \l_xeCJK_pass_features_clist
    \clist_clear:N \l_xeCJK_sub_key_clist
    \clist_map_inline:Nn \g_xeCJK_sub_key_clist
      {
        \bool_set_false:c { l_xeCJK_copy_##1_bool }
        \bool_set_false:c { l_xeCJK_add_##1_bool  }
      }
    \bool_set_eq:NN \l_xeCJK_AutoFakeBold_bool   \g_xeCJK_AutoFakeBold_bool
    \bool_set_eq:NN \l_xeCJK_AutoFakeSlant_bool  \g_xeCJK_AutoFakeSlant_bool
    \fp_set_eq:NN \l_xeCJK_EmboldenFactor_fp \g_xeCJK_EmboldenFactor_fp
    \fp_set_eq:NN \l_xeCJK_SlantFactor_fp    \g_xeCJK_SlantFactor_fp
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_set_family:nnn}
% 设置一个 CJK 新字体族，与 |\newfontfamily| 类似，增加 |FallBack| 选项。
%    \begin{macrocode}
\cs_new_protected_nopar:Nn \xeCJK_set_family:nnn
  {
    \group_begin:
    \xeCJK_set_init:
    \tl_set:Nx \l_xeCJK_family_tl {#1}
    \clist_set:Nx \l_xeCJK_fontoptions_clist {#2}
    \tl_set:Nx \l_xeCJK_fontname_tl {#3}
    \keys_set_known:nxN { xeCJK / features }
      { \g_xeCJK_default_features_clist, #2 } \l_xeCJK_pass_features_clist
    \xeCJK_parse_features:
    \xeCJK_pass_features:
    \xeCJK_check_family:V \l_xeCJK_family_tl
    \cs_gset_protected_nopar:cpx { xeCJK/family/\l_xeCJK_family_tl }
      {
        \exp_not:n { \fontspec_set_family:Nnn \l_xeCJK_fontspec_family_tl }
          { \l_xeCJK_pass_features_clist } { \l_xeCJK_fontname_tl }
        \prop_gput:NnV \exp_not:N \g_xeCJK_family_name_prop
          { \l_xeCJK_family_tl } \exp_not:N \l_xeCJK_fontspec_family_tl
        \cs_gset_protected_nopar:cpx { xeCJK/family/\l_xeCJK_family_tl }
        \exp_not:n
          { {
            \exp_not:N \fontencoding { \g_fontspec_encoding_tl }
            \exp_not:N \fontfamily   { \l_xeCJK_fontspec_family_tl }
            \exp_not:N \selectfont
          } }
      }
    \xeCJK_save_family_info:
    \xeCJK_add_sub_block:
    \group_end:
  }
\cs_generate_variant:Nn \xeCJK_set_family:nnn { ncc }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_check_family:n}
%    \begin{macrocode}
\cs_new_nopar:Nn \xeCJK_check_family:n
  {
    \prop_get:NnNT \g_xeCJK_family_prop {#1} \l_tmpa_tl
      {
        \prop_gpop:NnNT \g_xeCJK_family_name_prop {#1} \l_tmpa_tl { }
        \xeCJK_warning:nxx { CJKfamily-redef } {#1} \l_tmpa_tl
      }
  }
\cs_generate_variant:Nn \xeCJK_check_family:n { V }
\xeCJK_msg_new:nn { CJKfamily-redef } { Redefining~CJKfamily~`#1'~(#2). }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_add_font:nn,\xeCJK_add_font_if_new:nn}
%    \begin{macrocode}
\cs_new_nopar:Nn \xeCJK_add_font:nn
  {
    \prop_put:Nxx \l_xeCJK_add_font_prop
      { \xeCJK_map_features_id:n {#1} Font } {#2}
  }
\cs_new_nopar:Nn \xeCJK_add_font_if_new:nn
  {
    \prop_put_if_new:Nxx \l_xeCJK_add_font_prop
      { \xeCJK_map_features_id:n {#1} Font } {#2}
  }
\cs_generate_variant:Nn \xeCJK_add_font:nn        { nx, nc, nV, nv }
\cs_generate_variant:Nn \xeCJK_add_font_if_new:nn { nx, nc, nV, nv }
%    \end{macrocode}
% \end{macro}
%
%    \begin{macrocode}
\cs_generate_variant:Nn \prop_put:Nnn        { Nxx }
\cs_generate_variant:Nn \prop_get:Nn         { NV  }
\cs_generate_variant:Nn \prop_get:NnNF       { Nx  }
\cs_generate_variant:Nn \prop_if_in:NnF      { Nx  }
\cs_generate_variant:Nn \prop_put_if_new:Nnn { Nxx }
%    \end{macrocode}
%
% \begin{macro}[internal]{\xeCJK_add_fake_bold:n,\xeCJK_add_fake_slant:n}
%    \begin{macrocode}
\cs_new_nopar:Nn \xeCJK_add_fake_bold:n
  {
    \clist_put_left:cx { l_xeCJK_fontfeat_#1_clist }
      { FakeBold = { \fp_use:N \l_xeCJK_EmboldenFactor_fp } }
  }
\cs_new_nopar:Nn \xeCJK_add_fake_slant:n
  {
    \clist_put_left:cx { l_xeCJK_fontfeat_#1_clist }
      { FakeSlant = { \fp_use:N \l_xeCJK_SlantFactor_fp } }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_parse_features:,\xeCJK_parse_features:n}
%    \begin{macrocode}
\cs_new_nopar:Nn \xeCJK_parse_features:
  { \prop_map_inline:Nn \g_xeCJK_features_id_prop { \xeCJK_parse_features:n {##1} } }
\cs_new_nopar:Nn \xeCJK_parse_features:n
  {
    \xeCJK_if_font_select:nTF {#1}
      {
        \xeCJK_add_font:nv {#1} { l_xeCJK_fontname_#1_tl }
        \xeCJK_if_it_or_sl:nTF {#1}
          {
            \xeCJK_if_font_select:nF {bf#1}
              { \xeCJK_add_font_if_new:nv {bf#1} { l_xeCJK_fontname_#1_tl }  }
          }
          {
            \str_if_eq:xxT {#1} {bf}
              {
                \clist_map_inline:nn { it , sl }
                  {
                    \xeCJK_if_font_select:nF {bf##1}
                      { \xeCJK_add_font:nV {bf##1} \l_xeCJK_fontname_bf_tl }
                  }
              }
          }
      }
      { \xeCJK_set_fake:n {#1} }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[pTF,internal]{\xeCJK_if_font_select:n,\xeCJK_if_it_or_sl:n}
%    \begin{macrocode}
\prg_new_conditional:Nnn \xeCJK_if_font_select:n { p, T, F, TF }
  {
    \exp_args:Nc
    \tl_if_blank:xTF { l_xeCJK_fontname_#1_tl } \prg_return_false: \prg_return_true:
  }
\prg_new_conditional:Nnn \xeCJK_if_it_or_sl:n { p, T, F, TF }
  {
    \bool_if:nTF { \str_if_eq_p:xx { it } {#1} || \str_if_eq_p:xx { sl } {#1} }
      \prg_return_true: \prg_return_false:
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_set_fake:n}
%    \begin{macrocode}
\cs_new_nopar:Nn \xeCJK_set_fake:n
  {
    \str_if_eq:xxTF {#1} { bf }
      { \bool_if:NT \l_xeCJK_AutoFakeBold_bool { \xeCJK_add_fake_bold:n {#1} } }
      {
        \bool_if:NTF \l_xeCJK_AutoFakeSlant_bool
          {
            \bool_if:nT
              {   \xeCJK_if_it_or_sl_p:n {#1} ||
                ( \str_if_eq_p:xx {#1} {bfit} && ! ( \xeCJK_if_font_select_p:n {it} ) ) ||
                ( \str_if_eq_p:xx {#1} {bfsl} && ! ( \xeCJK_if_font_select_p:n {sl} ) )
              }
              { \xeCJK_add_fake_slant:n {#1} }
          }
          { \xeCJK_if_it_or_sl:nT {#1} { \xeCJK_map_it_sl:n {#1} } }
        \bool_if:nT
          {      \l_xeCJK_AutoFakeBold_bool
            && ! ( \xeCJK_if_it_or_sl_p:n {#1} )
            && ! ( \xeCJK_if_font_select_p:n {bf} )
          }
          { \xeCJK_add_fake_bold:n {#1} }
      }
    \xeCJK_add_font_if_new:nn {#1} *
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_map_it_sl:n}
%    \begin{macrocode}
\cs_new_nopar:Nn \xeCJK_map_it_sl:n
  {
    \xeCJK_if_map_font_select:nT {#1}
      {
        \xeCJK_add_font:nx {#1} { \xeCJK_get_map_font:n {#1} }
        \xeCJK_if_font_select:nF {bf#1}
          { \xeCJK_add_font_if_new:nx {bf#1} { \xeCJK_get_map_font:n {#1} } }
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_get_map_font:n}
%    \begin{macrocode}
\cs_new_nopar:Nn \xeCJK_get_map_font:n
  { \tl_use:c { l_xeCJK_fontname_\str_if_eq:xxTF {#1} {it} {sl} {it} _tl } }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[pTF,internal]{\xeCJK_if_map_font_select:n}
%    \begin{macrocode}
\prg_new_conditional:Nnn \xeCJK_if_map_font_select:n { p, T, F, TF }
  {
    \tl_if_blank:xTF { \xeCJK_get_map_font:n {#1} }
      \prg_return_false: \prg_return_true:
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_pass_features:}
%    \begin{macrocode}
\cs_new_nopar:Nn \xeCJK_pass_features:
  {
    \prop_map_inline:Nn \g_xeCJK_features_id_prop
      {
        \clist_if_empty:cF { l_xeCJK_fontfeat_##1_clist }
          {
            \clist_put_right:Nx \l_xeCJK_pass_features_clist
              { ##2Features = { \clist_use:c { l_xeCJK_fontfeat_##1_clist } } }
          }
      }
    \prop_map_inline:Nn \l_xeCJK_add_font_prop
      { \clist_put_right:Nx \l_xeCJK_pass_features_clist { ##1 = { ##2 } } }
    \tl_if_blank:xF \l_xeCJK_monoscale_tl
      { \clist_put_right:Nx \l_xeCJK_pass_features_clist \l_xeCJK_monoscale_tl }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal,var]
%  {\g_xeCJK_family_prop,\g_xeCJK_family_name_prop,\g_xeCJK_family_options_prop}
%    \begin{macrocode}
\prop_new:N \g_xeCJK_family_prop
\prop_new:N \g_xeCJK_family_name_prop
\prop_new:N \g_xeCJK_family_options_prop
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_save_family_info:}
%    \begin{macrocode}
\cs_new_nopar:Nn \xeCJK_save_family_info:
  {
    \group_begin:
    \clist_map_inline:Nn \g_xeCJK_CJK_subclass_clist
      {
        \clist_remove_all:Nn \l_xeCJK_fontoptions_clist {##1}
        \clist_remove_all:Nn \l_xeCJK_fontoptions_clist { ##1 = }
      }
    \prop_gput:NVV \g_xeCJK_family_prop \l_xeCJK_family_tl \l_xeCJK_fontname_tl
    \prop_gput:NVV \g_xeCJK_family_options_prop \l_xeCJK_family_tl \l_xeCJK_fontoptions_clist
    \group_end:
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_add_sub_block:}
%    \begin{macrocode}
\cs_new_nopar:Nn \xeCJK_add_sub_block:
  {
    \clist_remove_duplicates:N \l_xeCJK_sub_key_clist
    \clist_map_inline:Nn \l_xeCJK_sub_key_clist
      {
        \tl_set:Nx \l_xeCJK_sub_family_tl { \l_xeCJK_family_tl/##1 }
        \bool_if:cT { l_xeCJK_copy_##1_bool }
          {
            \xeCJK_check_family:V \l_xeCJK_sub_family_tl
            \prop_get:NVNT \g_xeCJK_family_prop \l_xeCJK_family_tl \l_tmpa_tl
              { \prop_gput:NVV \g_xeCJK_family_prop \l_xeCJK_sub_family_tl \l_tmpa_tl }
            \prop_get:NVNT \g_xeCJK_family_options_prop \l_xeCJK_family_tl \l_tmpa_clist
              {
                \clist_remove_all:Nn \l_tmpa_clist { ##1 = * }
                \prop_gput:NVV \g_xeCJK_family_options_prop \l_xeCJK_sub_family_tl \l_tmpa_clist
              }
            \cs_gset_protected_nopar:cpx { xeCJK/family/\l_xeCJK_sub_family_tl }
              {
                \xeCJK_family_if_exist:nT { \l_xeCJK_family_tl }
                  {
                    \prop_get:NnNT \exp_not:N \g_xeCJK_family_name_prop
                      { \l_xeCJK_family_tl } \exp_not:N \l_tmpa_tl
                      {
                        \prop_gput:NnV \exp_not:N \g_xeCJK_family_name_prop
                          { \l_xeCJK_sub_family_tl } \exp_not:N \l_tmpa_tl
                        \cs_gset_protected_nopar:cpx { xeCJK/family/\l_xeCJK_sub_family_tl }
                        \exp_not:n
                          { {
                              \exp_not:N \fontencoding { \g_fontspec_encoding_tl }
                              \exp_not:N \fontfamily   { \l_tmpa_tl }
                              \exp_not:N \selectfont
                          } }
                      }
                  }
              }
          }
        \bool_if:cT { l_xeCJK_add_##1_bool }
          {
            \xeCJK_set_family:ncc \l_xeCJK_sub_family_tl
              { l_xeCJK_##1_fontfeat_clist } { l_xeCJK_##1_fontname_tl }
          }
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_copy_family:nn}
%    \begin{macrocode}
\cs_new_nopar:Nn \xeCJK_copy_family:nn
  {
    \xeCJK_family_if_exist:nT {#2}
      {
        \tl_map_inline:nn
          { \g_xeCJK_family_prop \g_xeCJK_family_options_prop \g_xeCJK_family_name_prop }
          { \prop_get:NnNT ##1 {#2} \l_tmpa_tl { \prop_gput:NnV ##1 {#1} \l_tmpa_tl } }
        \cs_gset_eq:cc { xeCJK/family/#1 } { xeCJK/family/#2 }
      }
  }
\cs_generate_variant:Nn \xeCJK_copy_family:nn { xx }
%    \end{macrocode}
% \end{macro}
%
% \subsection{字体切换}
%
% \begin{macro}[internal]{\l_xeCJK_font_coor_tl,\xeCJK_select_font:,\xeCJK_switch_font:nn}
% 缓存当前字体的原始格式，以加速编译。
%    \begin{macrocode}
\tl_set:Nn \l_xeCJK_font_coor_tl { xeCJK/\xeCJK@family/\f@series/\f@shape/\f@size }
\cs_new_nopar:Nn \xeCJK_select_font:
  {
    \cs_if_exist_use:cF \l_xeCJK_font_coor_tl
      {
        \tl_set:Nx \l_xeCJK_current_coor_tl \l_xeCJK_font_coor_tl
        \cs_if_exist_use:cT { xeCJK/family/\xeCJK@family }
          { \exp_last_unbraced:NNV \cs_gset_eq:cN \l_xeCJK_current_coor_tl \font@name }
      }
  }
\cs_new_eq:NN \xeCJK@setfont \xeCJK_select_font:
\cs_new_nopar:Nn \xeCJK_switch_font:nn
  {
    \str_if_eq:xxF {#1} {#2}
      {
        \xeCJK_info:nxx { CJK-block } {#1} {#2}
        \tl_remove_all:Nn \xeCJK@family { /#1 }
        \str_if_eq:xxF {#2} { CJK }
          {
            \tl_set:Nx \l_tmpa_tl \xeCJK@family
            \tl_put_right:Nx \xeCJK@family { /#2 }
            \xeCJK_family_if_exist:nF \xeCJK@family
              {
                \xeCJK_family_if_exist:nTF { \CJKfamilydefault/#2 }
                  { \xeCJK_copy_family:xx \xeCJK@family { \CJKfamilydefault/#2 } }
                  { \xeCJK_copy_family:xx \xeCJK@family \l_tmpa_tl }
              }
          }
      }
    \xeCJK_select_font:
  }
\xeCJK_msg_new:nn { CJK-block } { Switch~from~block~'#1'~to~'#2'. }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[TF,internal]{\xeCJK_family_if_exist:n}
%    \begin{macrocode}
\prg_new_protected_conditional:Nnn \xeCJK_family_if_exist:n { T, F, TF }
  {
    \cs_if_free:cTF { xeCJK/family/#1 } \prg_return_false:
      {
        \prop_if_in:NxF \g_xeCJK_family_name_prop {#1} { \use:c { xeCJK/family/#1 } }
        \prg_return_true:
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\CJKfamily}
% 用于切换 CJK 字体族。
%    \begin{macrocode}
\NewDocumentCommand \CJKfamily { m }
  {
    \xeCJK_family_if_exist:nTF {#1}
      { \tl_set:Nx \xeCJK@family {#1} }
      {
        \cs_if_free:cT { xeCJK_warned_aux_#1 }
          {
            \xeCJK_warning:nx { CJKfamily-Unknown } {#1}
            \tl_new:c { xeCJK_warned_aux_#1 }
          }
      }
    \tex_ignorespaces:D
  }
\xeCJK_msg_new:nn { CJKfamily-Unknown }
  {
    Unknown~CJK~family~'#1'~is~ignored.\\\\
    Try~to~use~
    \prg_case_str:xxn {#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} }
    [...]{...}~to~define~it.
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\setCJKfamilyfont, \newCJKfontfamily, \CJKfontspec}
% 分别用于预声明 CJK 字体和随机调用 CJK 字体。
%    \begin{macrocode}
\NewDocumentCommand \setCJKfamilyfont { m O{} m }
  { \xeCJK_set_family:nnn {#1} {#2} {#3} }
\NewDocumentCommand \newCJKfontfamily { o m O{} m }
  {
    \IfNoValueTF {#1}
      { \tl_set:Nx \l_xeCJK_family_tl { \cs_to_str:N #2 } }
      { \tl_set:Nx \l_xeCJK_family_tl {#1} }
    \xeCJK_set_family:nnn \l_xeCJK_family_tl {#3} {#4}
    \cs_new_protected_nopar:Npx #2 { \exp_not:N \CJKfamily { \l_xeCJK_family_tl } }
  }
\int_new:N   \g_xeCJK_fontspec_int
\int_gdecr:N \g_xeCJK_fontspec_int
\NewDocumentCommand \CJKfontspec { O{} m }
  {
    \tl_set:Nx \l_xeCJK_CJKfontspec_id_tl { CJKfontspec/#1/#2/id }
    \cs_if_free:cT \l_xeCJK_CJKfontspec_id_tl
      {
        \int_gincr:N \g_xeCJK_fontspec_int
        \tl_gset:Nx \g_xeCJK_family_spec_tl
          { CJKfontspec ( \int_use:N \g_xeCJK_fontspec_int ) }
        \xeCJK_set_family:nnn \g_xeCJK_family_spec_tl {#1} {#2}
        \tl_gset:cx \l_xeCJK_CJKfontspec_id_tl \g_xeCJK_family_spec_tl
      }
    \exp_args:Nv \CJKfamily \l_xeCJK_CJKfontspec_id_tl
    \tex_ignorespaces:D
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\defaultCJKfontfeatures, \addCJKfontfeatures}
% 分别用于设置 CJK 字体的默认属性和增加当前 CJK 字体的属性。
%    \begin{macrocode}
\clist_new:N \g_xeCJK_default_features_clist
\NewDocumentCommand \defaultCJKfontfeatures { m }
  { \clist_gset:Nn \g_xeCJK_default_features_clist {#1} }
\@onlypreamble \defaultCJKfontfeatures
\NewDocumentCommand \addCJKfontfeatures { m }
  {
    \prop_if_in:NVTF \g_xeCJK_family_prop \xeCJK@family
      {
        \group_begin:
        \clist_set:Nx \l_tmpa_clist {#1}
        \prop_get:NVN \g_xeCJK_family_prop \xeCJK@family \l_tmpa_tl
        \clist_map_inline:Nn \g_xeCJK_CJK_subclass_clist
          {
            \clist_if_in:NnT \l_tmpa_clist {##1}
              {
                \clist_remove_all:Nn \l_tmpa_clist {##1}
                \prop_get:NxNF \g_xeCJK_family_prop { \xeCJK@family/##1 } \l_tmpb_tl
                  {
                    \prop_get:NxNF \g_xeCJK_family_prop
                      { \CJKfamilydefault/##1 } \l_tmpb_tl
                      { \tl_set:Nx \l_tmpb_tl \l_tmpa_tl }
                  }
                \clist_set:Nx \l_tmpa_clist
                  { ##1 = { [ \l_tmpa_clist ] { \l_tmpb_tl } } }
                \clist_map_break:
              }
          }
        \prop_get:NVNF \g_xeCJK_family_options_prop \xeCJK@family \l_tmpb_clist
          { \clist_clear:N \l_tmpb_clist }
        \clist_clear:N \l_tmpc_clist
        \clist_map_inline:Nn \l_tmpb_clist
          {
            \clist_if_in:NnF \l_tmpa_clist {##1}
              { \clist_put_right:Nn \l_tmpc_clist {##1} }
          }
        \clist_put_left:NV \l_tmpa_clist \l_tmpc_clist
        \use:x
          { \group_end: \exp_not:N \CJKfontspec [ \l_tmpa_clist ] { \l_tmpa_tl } }
      }
      { \xeCJK_warning:n { addCJKfontfeature-ignored } }
    \tex_ignorespaces:D
  }
\cs_new_eq:NN \addCJKfontfeature \addCJKfontfeatures
\xeCJK_msg_new:nn { addCJKfontfeature-ignored }
  {
    \tl_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}{\setCJKmainfont,\setCJKsansfont}
% 设置文档的 CJK 普通字体、无衬线字体。
%    \begin{macrocode}
\NewDocumentCommand \setCJKmainfont { O{} m }
  { \xeCJK_set_family:nnn \CJKrmdefault {#1} {#2} }
\cs_new_eq:NN \setCJKromanfont \setCJKmainfont
\NewDocumentCommand \setCJKsansfont { O{} m }
  { \xeCJK_set_family:nnn \CJKsfdefault {#1} {#2} }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\CJKrmdefault,\CJKsfdefault,\CJKttdefault,\CJKfamilydefault}
%    \begin{macrocode}
\cs_if_free:NT \CJKrmdefault     { \tl_set:Nn \CJKrmdefault { rm } }
\cs_if_free:NT \CJKsfdefault     { \tl_set:Nn \CJKsfdefault { sf } }
\cs_if_free:NT \CJKttdefault     { \tl_set:Nn \CJKttdefault { tt } }
\cs_if_free:NT \CJKfamilydefault { \tl_set:Nn \CJKfamilydefault { \CJKrmdefault } }
\tl_new:c { xeCJK/family/\CJKfamilydefault }
%    \end{macrocode}
% \end{macro}
%
% 补丁 |\normalfont|，|\rmfamily|，|\sffamily| 和 |\ttfamily|，使其同时对
% CJK 字体族有效。
% \begin{macrocode}
\AtEndOfPackage
  {
    \etex_protected:D \tl_put_right:Nn \normalfont { \CJKfamily \CJKfamilydefault }
    \etex_protected:D \tl_put_right:Nn \rmfamily   { \CJKfamily \CJKrmdefault }
    \etex_protected:D \tl_put_right:Nn \sffamily   { \CJKfamily \CJKsfdefault }
    \etex_protected:D \tl_put_right:Nn \ttfamily   { \CJKfamily \CJKttdefault }
    \cs_set_eq:NN \reset@font \normalfont
  }
%    \end{macrocode}
%
% \begin{macro}{\setCJKmathfont}
% 设置 CJK 数学字体。
%    \begin{macrocode}
\NewDocumentCommand \setCJKmathfont { O{} m }
  { \xeCJK_set_family:nnn \c_xeCJK_math_tl {#1} {#2} }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{CJKmath}
% 数学公式内的 CJK 字体。
%    \begin{macrocode}
\tl_const:Nn \c_xeCJK_math_tl { CJKmath }
\keys_define:nn { xeCJK / options } { CJKmath .bool_gset:N = \g_xeCJK_math_bool }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJKsetmathcode}
%    \begin{macrocode}
\NewDocumentCommand \xeCJKsetmathcode { m m m m }
  {
    \xeCJK_check_num_range:nnNN {#1} {#2} \l_tmpa_int \l_tmpb_int
    \int_set:Nn \l_tmpc_int { \xeCJK_math_type:n {#3} }
    \loop
      \XeTeXmathcode \l_tmpa_int = \l_tmpc_int \use:c { sym #4 } \l_tmpa_int
      \if_int_compare:w \l_tmpa_int < \l_tmpb_int
        \int_incr:N \l_tmpa_int
    \repeat
  }
\cs_set_eq:NN \xeCJK_math_type:n \mathchar@type
%    \end{macrocode}
% \end{macro}
%
% 当没有设置 CJK 数学字体时，使用 |\CJKfamilydefault| 作为数学字体。
%    \begin{macrocode}
\tl_put_left:Nn \document
  {
    \group_end:
    \normalfont
    \prop_if_empty:NTF \g_xeCJK_family_prop
      { \xeCJK_warning:n { no-CJKfamily } }
      {
        \bool_if:NT \g_xeCJK_math_bool
          {
            \xeCJK_family_if_exist:nTF \c_xeCJK_math_tl
              { \tl_set:Nx \l_tmpa_tl \c_xeCJK_math_tl }
              {
                \xeCJK_family_if_exist:nTF \CJKfamilydefault
                  { \tl_set:Nx \l_tmpa_tl \CJKfamilydefault }
                  { \tl_set_eq:NN \l_tmpa_tl \q_no_value }
              }
            \prop_get:NVNT \g_xeCJK_family_name_prop \l_tmpa_tl \l_xeCJK_math_family_tl
              {
                \DeclareSymbolFont \c_xeCJK_math_tl       \g_fontspec_encoding_tl
                  \l_xeCJK_math_family_tl \mddefault \shapedefault
                \SetSymbolFont  \c_xeCJK_math_tl { bold } \g_fontspec_encoding_tl
                  \l_xeCJK_math_family_tl \bfdefault \shapedefault
                \clist_map_inline:nn { CJK, FullLeft, FullRight  }
                  {
                    \clist_map_inline:cn { g_xeCJK_#1_range_clist }
                      {
                        \xeCJK_setcharclass_aux:Nn \xeCJKsetmathcode {##1}
                          \mathalpha \c_xeCJK_math_tl
                      }
                  }
              }
          }
      }
    \group_begin:
  }
\xeCJK_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\\\\
    \token_to_str:N~\xeCJKmainfont[...]{...}\\\\
    in~the~preamble~to~declare~the~main~CJKfamily.\\
  }
%    \end{macrocode}
%
% 为了保证 CJK 数学字体的正确设置，应在导言区内设置 CJK 字体。
%    \begin{macrocode}
\@onlypreamble \setCJKmainfont
\@onlypreamble \setCJKmathfont
\@onlypreamble \setCJKsansfont
\@onlypreamble \setCJKmonofont
\@onlypreamble \setCJKromanfont
%    \end{macrocode}
%
% \subsection{处理等宽字体和抄录环境}
%
% \begin{macro}[internal,var]{\g_xeCJK_monoscale_fp,\g_xeCJK_exspace_dim}
%    \begin{macrocode}
\fp_new:N \g_xeCJK_monoscale_fp
\dim_new:N \g_xeCJK_exspace_dim
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\setCJKmonoscale}
%    \begin{macrocode}
\NewDocumentCommand \setCJKmonoscale { }
  {
    \CJKflexiblespacing
    \xeCJK_set_monoscale:
    \addCJKfontfeatures { Scale = \fp_use:N \g_xeCJK_monoscale_fp }
  }
\cs_new_nopar:Nn \xeCJK_set_monoscale:
  {
    \group_begin:
      \fontfamily \ttdefault \selectfont
      \fp_gset_from_dim:Nn \g_xeCJK_monoscale_fp
        { \c_two \tex_fontdimen:D \c_two \tex_font:D }
      \fp_gdiv:Nn \g_xeCJK_monoscale_fp \f@size
    \group_end:
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\setCJKmonoexspace}
%    \begin{macrocode}
\NewDocumentCommand \setCJKmonoexspace { } { \xeCJK_set_monoexspace: }
\cs_new_nopar:Nn \xeCJK_set_monoexspace:
  {
    \group_begin:
      \fontfamily \ttdefault \selectfont
      \dim_gset:Nn \g_xeCJK_exspace_dim
        { \c_two \tex_fontdimen:D \c_two \tex_font:D - \f@size \p@ }
    \group_end:
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\CJKfixedspacing,\CJKflexiblespacing}
%    \begin{macrocode}
\NewDocumentCommand \CJKfixedspacing { }
  {
    \bool_if:NF \l_xeCJK_fixed_spacing_bool
      {
        \bool_set_true:N \l_xeCJK_fixed_spacing_bool
        \tl_set_eq:NN \l_xeCJK_flexible_punctstyle_tl \l_xeCJK_punctstyle_tl
        \bool_set_eq:NN \l_xeCJK_fixed_xecglue_bool \l_xeCJK_xecglue_bool
        \cs_set_eq:NN \xeCJK_flexible_ecglue:  \CJKecglue
        \cs_set_eq:NN \xeCJK_flexible_cjkglue: \CJKglue
        \xeCJK_set_monoexspace:
        \xeCJKsetup
          {
            PunctStyle = plain ,
            CJKglue    = { \skip_horizontal:N \g_xeCJK_exspace_dim } ,
            CJKecglue  = { \skip_horizontal:n { .5\g_xeCJK_exspace_dim } } ,
            xCJKecglue = false ,
          }
      }
  }
\NewDocumentCommand \CJKflexiblespacing { }
  {
    \bool_if:NT \l_xeCJK_fixed_spacing_bool
      {
        \bool_set_false:N \l_xeCJK_fixed_spacing_bool
        \exp_args:Nx \xeCJKsetup
          {
            PunctStyle = { \l_xeCJK_flexible_punctstyle_tl } ,
            CJKglue    = { \xeCJK_flexible_cjkglue: } ,
            CJKecglue  = { \xeCJK_flexible_ecglue: } ,
            xCJKecglue = { \bool_if:NTF \l_xeCJK_fixed_xecglue_bool { true } { false } } ,
          }
      }
  }
\bool_new:N \l_xeCJK_fixed_spacing_bool
\AtBeginDocument { \tl_put_right:Nn \verbatim@font \CJKfixedspacing }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\setCJKmonofont}
% 设置文档的 CJK 等宽字体族。
%    \begin{macrocode}
\NewDocumentCommand \setCJKmonofont { s t+ O{} m }
  {
    \IfBooleanTF {#1}
      { \xeCJK_set_family:nnn \CJKttdefault { Mono = Scale, #3 } {#4} }
      {
        \IfBooleanTF {#2}
          { \xeCJK_set_family:nnn \CJKttdefault { Mono = Exspace, #3 } {#4} }
          { \xeCJK_set_family:nnn \CJKttdefault {#3} {#4} }
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \subsection{\pkg{xeCJK} 其它选项}
%
% \begin{macro}{CJKnumber,indentfirst}
% 是否启用 \pkg{CJKnumber} 宏包和首行是否缩进，其中 |CJKnumber| 选项仅
% 在 \pkg{xeCJK} 宏包调用的时候有效。并将 \pkg{xeCJK} 中未知的选项传递给
% \pkg{fontspec} 宏包。
%    \begin{macrocode}
\bool_new:N \g_xeCJK_indent_bool
\keys_define:nn { xeCJK / options }
  {
    CJKnumber     .bool_set:N = \g_xeCJK_number_bool ,
    indentfirst   .bool_set:N = \g_xeCJK_indent_bool ,
    normalindentfirst .meta:n = { indentfirst = false } ,
    quiet .code:n =
      {
        \msg_redirect_module:nnn { xeCJK } { warning } { info }
        \msg_redirect_module:nnn { xeCJK } { info }    { none }
        \PassOptionsToPackage { quiet } { fontspec }
      },
    silent .code:n =
      {
        \msg_redirect_module:nnn { xeCJK } { warning } { none }
        \msg_redirect_module:nnn { xeCJK } { info }    { none }
        \PassOptionsToPackage { silent } { fontspec }
      },
    unknown .code:n =
      {
        \@ifpackageloaded { fontspec }
          { \xeCJK_error:nx { key-unknown } { \l_keys_key_tl } }
          { \PassOptionsToPackage { \l_keys_key_tl } { fontspec } }
      },
  }
\xeCJK_msg_new:nn { key-unknown }
  {
    Sorry,~but~\l_keys_module_tl\c_space_tl does~not~have~a~key~called~'#1'.\\\\
    The~key~'#1'~is~being~ignored.
  }
%    \end{macrocode}
% \end{macro}
%
% \subsection{\pkg{xeCJK} 初始化设置}
%
% \pkg{xeCJK} 宏包的初始化设置。
%    \begin{macrocode}
\keys_set:nn { xeCJK / options }
  {
    CJKglue = { \skip_horizontal:n { \c_zero_skip \@plus .08\baselineskip } } ,
    CJKecglue       = \c_space_token ,
    CheckSingleglue = \CJKglue ,
    xCJKecglue      = false ,
    CheckSingle     = false ,
    CJKspace        = false ,
    CJKmath         = false ,
    xeCJKactive     = true  ,
    indentfirst     = true  ,
    EmboldenFactor  = 4 ,
    SlantFactor     = .167 ,
    PunctStyle      = quanjiao ,
    KaiMingPunct    = { ． 。？ ！ } ,
    LongPunct       = { — ‥ … ─ } ,
    MiddlePunct     = { · ・ ･ } ,
    AllowBreakBetweenPuncts = false ,
  }
%    \end{macrocode}
%
% \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{fontspec} 宏包。
%    \begin{macrocode}
\ProcessKeysOptions { xeCJK / options }
\RequirePackage { fontspec } [ 2011/09/13 ]
%    \end{macrocode}
%
%    \begin{macrocode}
\bool_if:NT \g_xeCJK_indent_bool
  {
    \cs_set_eq:NN \@afterindentfalse \@afterindenttrue
    \@afterindenttrue
  }
%    \end{macrocode}
%
% \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} } }
\cs_new_nopar:Npn \xeCJKplainchr { \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}
%
% \subsection{兼容性修补}
%
% \begin{macro}[internal]{\/}
% 修复倾斜校正。
%    \begin{macrocode}
\cs_new_eq:NN \xeCJK_itcorr_aux \/
\bool_new:N \g_xeCJK_itcorr_bool
\cs_set_protected_nopar:Npn \/
  {
    \scan_stop:
    \int_compare:nNnT \tex_lastkern:D = \c_three
      {
        \tex_unkern:D \tex_unkern:D
        \bool_gset_true:N \g_xeCJK_itcorr_bool
      }
    \xeCJK_itcorr_aux
  }
\cs_set_eq:NN \@@italiccorr \/
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]
%   {\xeCJK_compatibility_hook:,\xeCJK_set_others_toks:n,\xeCJK_Others_class}
% 简单处理与同样使用 |\XeTeXinterchartoks| 机制的宏包的兼容问题。
% \begin{macrocode}
\cs_new_nopar:Nn \xeCJK_compatibility_hook:
  {
    \int_compare:nNnF
      { \c_three + \seq_length:N \g_xeCJK_class_seq } = \xe@alloc@intercharclass
      {
        \int_set_eq:NN \l_tmpa_int \c_three
        \int_until_do:nNnn \l_tmpa_int = \xe@alloc@intercharclass
          {
            \int_incr:N \l_tmpa_int
            \seq_if_in:NVF \g_xeCJK_class_seq \l_tmpa_int
              { \xeCJK_set_others_toks:n \l_tmpa_int }
          }
      }
  }
\cs_new_nopar:Nn \xeCJK_set_others_toks:n
  {
    \int_set:Nn \xeCJK_Others_class {#1}
    \clist_map_inline:Nn \g_xeCJK_CJK_class_clist
      {
        \xeCJK_copy_inter_class_toks:nnnn {##1} { Others } {##1} { NormalSpace }
        \xeCJK_copy_inter_class_toks:nnnn { Others } {##1} { NormalSpace } {##1}
        \exp_args:Nnnx \xeCJK_app_inter_class_toks:nnn {##1} { Others }
          { \xeCJK_get_inter_class_toks:nn { Default } { Others } }
        \exp_args:Nnnx \xeCJK_pre_inter_class_toks:nnn { Others } {##1}
          { \xeCJK_get_inter_class_toks:nn { Others } { Default } }
        \tl_if_empty:xT { \xeCJK_get_inter_class_toks:nn { Others } { Boundary } }
          {
            \xeCJK_copy_inter_class_toks:nnnn
              { Others } { Boundary } { Default } { Boundary }
          }
        \tl_if_empty:xT { \xeCJK_get_inter_class_toks:nn { Boundary } { Others } }
          {
            \xeCJK_copy_inter_class_toks:nnnn
              { Boundary } { Others } { Boundary } { Default }
          }
      }
  }
\int_new:N \xeCJK_Others_class
\tl_put_right:Nn \document { \xeCJK_compatibility_hook: \tex_ignorespaces:D }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xeCJK_patch:Nnn}
% 给已有宏内容前后附加补丁。
%    \begin{macrocode}
\cs_new_nopar:Nn \xeCJK_patch:Nnn
  { \tl_put_left:Nn  #1 {#2} \tl_put_right:Nn #1 {#3} }
%    \end{macrocode}
% \end{macro}
%
% 单独处理宽度有分歧的几个标点：包括省略号、破折号、间隔号、引号等中西文混用的
% 符号， 保证其命令形式输出的是西文字体。并对一些编码的符号宏包做特殊处理。
% \begin{macrocode}
\AtBeginDocument
  {
    \tl_map_inline:nn
      {
        \textellipsis  \textemdash     \textperiodcentered \textcentereddot
        \textquoteleft \textquoteright \textquotedblleft   \textquotedblright
      }
      { \xeCJK_patch:Nnn #1 { \group_begin: \makexeCJKinactive } { \group_end: } }
    \xeCJK_patch:Nnn \tipaencoding { \makexeCJKinactive } { }
    \cs_set_eq:NN \xeCJK_aux_r \r
    \cs_set_nopar:Npn \r #1 { { \makexeCJKinactive \xeCJK_aux_r {#1} } }
    \@ifpackageloaded { pifont }
      {
        \RenewDocumentCommand \Pifont { m }
          {
            \fontencoding {U} \fontfamily {#1} \fontseries {m} \fontshape {n}
            \selectfont \makexeCJKinactive
          }
      } { }
  }
%    \end{macrocode}
%
% 禁止在 \pkg{xeCJK} 宏包后再载入 \pkg{CJK} 宏包。
%    \begin{macrocode}
\tl_set:cn { ver@CJK.sty } { 2050/01/01 }
%    \end{macrocode}
%
% \begin{macro}[internal]{\xeCJKcaption}
% 可以使用 \pkg{CJK} 宏包中的 |.cpx| 文件。
%    \begin{macrocode}
\cs_if_free:NT \CJK@ifundefined
  { \cs_set_eq:NN \CJK@ifundefined \cs_if_free:NTF }
\NewDocumentCommand \xeCJKcaption { o m }
  {
    \IfValueT {#1} { \XeTeXdefaultencoding "#1" }
    \cs_set_nopar:Nx \xeCJK_reset_at_catcode:
      {
        \exp_not:n { \char_set_catcode:nn  { `\@ } }
                   { \char_value_catcode:n { `\@ } }
      }
    \char_set_catcode_letter:N \@
    \file_input:n { #2.cpx }
    \xeCJK_reset_at_catcode:
    \XeTeXdefaultencoding "UTF-8"
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]
%   {\xeCJK_ULprepunctchar:n, \xeCJK_ULpostpunctchar:n, \xeCJK_ULroutines:}
%    \begin{macrocode}
\cs_new_protected_nopar:Nn \xeCJK_ULprepunctchar:n
  {
    { \makexeCJKinactive \CJKpunctsymbol {#1} \nobreak }
    \tex_ignorespaces:D
  }
\cs_new_protected_nopar:Nn \xeCJK_ULpostpunctchar:n
  {
    { \makexeCJKinactive \CJKpunctsymbol {#1} }
    \xeCJK_ignorespaces:
  }
\cs_new_protected_nopar:Nn \xeCJK_ULroutines:
  {
    \xeCJK_inter_class_toks:nnn { Default   } { CJK } { \CJKecglue \CJKsymbol }
    \xeCJK_inter_class_toks:nnn { HalfLeft  } { CJK } { \CJKsymbol }
    \xeCJK_inter_class_toks:nnn { HalfRight } { CJK } { \CJKecglue \CJKsymbol }
    \xeCJK_inter_class_toks:nnn { Boundary  } { CJK } { \xeCJK_Boundary_and_CJK: }
    \clist_map_inline:nn { Default, HalfLeft, HalfRight, Boundary }
      {
        \xeCJK_inter_class_toks:nnn {##1} { FullLeft }  { \xeCJK_ULprepunctchar:n }
        \xeCJK_inter_class_toks:nnn {##1} { FullRight } { \xeCJK_ULpostpunctchar:n }
      }
    \xeCJK_UL_subclass_patch_tl
  }
%    \end{macrocode}
% \end{macro}
%
% 对 \pkg{ulem} 宏包打补丁，以支持 \pkg{CJKfntef} 宏包。
% \begin{macrocode}
\AtBeginDocument
  {
    \cs_if_exist:NT \UL@hook
      {
        \addto@hook \UL@hook
          {
            \cs_set_eq:NN \xeCJK_UL_CJKsymbol \CJKsymbol
            \cs_set_eq:NN \xeCJK_UL_CJKpunctsymbol \CJKpunctsymbol
            \cs_set_nopar:Npn \CJKsymbol #1
              {
                { \xeCJK_select_font: \xeCJK_UL_CJKsymbol {#1} }
                \xeCJK_CJK_kern: \xeCJK_ignorespaces:
              }
            \cs_set_nopar:Npn \CJKpunctsymbol #1
              { { \xeCJK_select_font: \xeCJK_UL_CJKpunctsymbol {#1} } }
            \xeCJK_ULroutines:
          }
      }
    \cs_if_exist:NT \XeTeX@CJKfntef@hook
      {
        \cs_set_nopar:Npn \XeTeX@CJKfntef@hook
          { \xeCJK_select_font: \makexeCJKinactive }
      }
  }
%    \end{macrocode}
%
% 使用 \pkg{CJKnumb} 宏包。
%    \begin{macrocode}
\bool_if:NT \g_xeCJK_number_bool
  {
    \tl_set:Nn \CJK@UnicodeEnc { UTF8 }
    \cs_set_nopar:Npn \CJKaddEncHook #1#2 { \cs_set_nopar:cpn { xeCJK_enc_#1 } {#2} }
    \cs_set_nopar:Npn \Unicode #1#2
      { \tex_char:D \int_eval:n { #1 * \c_two_hundred_fifty_six + #2 } }
    \RequirePackage { CJKnumb }
    \use:c { xeCJK_enc_\CJK@UnicodeEnc }
    \tl_set:Nn \CJK@tenthousand    { 万 }
    \tl_set:Nn \CJK@hundredmillion { 亿 }
  }
%    \end{macrocode}
%
%    \begin{macrocode}
%</package>
%    \end{macrocode}
%
% \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{CJKfntef}
\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}
%
% \CJKfixedspacing
%
% \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
\begin{equation}
x^2+y^2
\end{equation}
一二三四五六七八九十一二三四五六七八九十一二三四五六七八九十。}
\begin{document}
\hsize=30em
\parindent=0pt
\showtext

\hrulefill\bigskip

\xeCJKsetup{CheckSingle}
\showtext
\end{document}
%    \end{macrocode}
%
%    \begin{macrocode}
%</ex-single>
%    \end{macrocode}
%
% \end{implementation}
%
% \Finale
%
\endinput
