texlive[46497] Master/texmf-dist: xecjk (30jan18)

commits+karl at tug.org commits+karl at tug.org
Tue Jan 30 22:06:15 CET 2018


Revision: 46497
          http://tug.org/svn/texlive?view=revision&revision=46497
Author:   karl
Date:     2018-01-30 22:06:15 +0100 (Tue, 30 Jan 2018)
Log Message:
-----------
xecjk (30jan18)

Modified Paths:
--------------
    trunk/Master/texmf-dist/doc/xelatex/xecjk/README.md
    trunk/Master/texmf-dist/doc/xelatex/xecjk/xeCJK.pdf
    trunk/Master/texmf-dist/doc/xelatex/xecjk/xunicode-symbols.pdf
    trunk/Master/texmf-dist/source/xelatex/xecjk/xeCJK.dtx
    trunk/Master/texmf-dist/source/xelatex/xecjk/xeCJK.ins
    trunk/Master/texmf-dist/tex/xelatex/xecjk/xeCJK-listings.sty
    trunk/Master/texmf-dist/tex/xelatex/xecjk/xeCJK.cfg
    trunk/Master/texmf-dist/tex/xelatex/xecjk/xeCJK.sty
    trunk/Master/texmf-dist/tex/xelatex/xecjk/xeCJKfntef.sty
    trunk/Master/texmf-dist/tex/xelatex/xecjk/xunicode-addon.sty
    trunk/Master/texmf-dist/tex/xelatex/xecjk/xunicode-extra.def

Modified: trunk/Master/texmf-dist/doc/xelatex/xecjk/README.md
===================================================================
--- trunk/Master/texmf-dist/doc/xelatex/xecjk/README.md	2018-01-30 21:05:58 UTC (rev 46496)
+++ trunk/Master/texmf-dist/doc/xelatex/xecjk/README.md	2018-01-30 21:06:15 UTC (rev 46497)
@@ -20,8 +20,8 @@
 ---------------------
 
     Copyright (C) 2007--2010 by Wenchang Sun <sunwch at nankai.edu.cn>
-    Copyright (C) 2009--2017 by Leo Liu <leoliu.pku at gmail.com>
-    Copyright (C) 2012--2017 by Qing Lee <sobenlee at gmail.com>
+    Copyright (C) 2009--2018 by Leo Liu <leoliu.pku at gmail.com>
+    Copyright (C) 2012--2018 by Qing Lee <sobenlee at gmail.com>
     ----------------------------------------------------------------------
 
     This work may be distributed and/or modified under the

Modified: trunk/Master/texmf-dist/doc/xelatex/xecjk/xeCJK.pdf
===================================================================
(Binary files differ)

Modified: trunk/Master/texmf-dist/doc/xelatex/xecjk/xunicode-symbols.pdf
===================================================================
(Binary files differ)

Modified: trunk/Master/texmf-dist/source/xelatex/xecjk/xeCJK.dtx
===================================================================
--- trunk/Master/texmf-dist/source/xelatex/xecjk/xeCJK.dtx	2018-01-30 21:05:58 UTC (rev 46496)
+++ trunk/Master/texmf-dist/source/xelatex/xecjk/xeCJK.dtx	2018-01-30 21:06:15 UTC (rev 46497)
@@ -26,8 +26,8 @@
 ---------------------
 
     Copyright (C) 2007--2010 by Wenchang Sun <sunwch at nankai.edu.cn>
-    Copyright (C) 2009--2017 by Leo Liu <leoliu.pku at gmail.com>
-    Copyright (C) 2012--2017 by Qing Lee <sobenlee at gmail.com>
+    Copyright (C) 2009--2018 by Leo Liu <leoliu.pku at gmail.com>
+    Copyright (C) 2012--2018 by Qing Lee <sobenlee at gmail.com>
     ----------------------------------------------------------------------
 
     This work may be distributed and/or modified under the
@@ -92,8 +92,8 @@
 \preamble
 
     Copyright (C) 2007--2010 by Wenchang Sun <sunwch at nankai.edu.cn>
-    Copyright (C) 2009--2017 by Leo Liu <leoliu.pku at gmail.com>
-    Copyright (C) 2012--2017 by Qing Lee <sobenlee at gmail.com>
+    Copyright (C) 2009--2018 by Leo Liu <leoliu.pku at gmail.com>
+    Copyright (C) 2012--2018 by Qing Lee <sobenlee at gmail.com>
 ----------------------------------------------------------------------
 
     This work may be distributed and/or modified under the
@@ -194,7 +194,7 @@
 %<*package|config|fntef|listings|xunicode|xunextra>
 %<!(config|xunextra)>\NeedsTeXFormat{LaTeX2e}
 %<!(config|xunextra)>\RequirePackage{expl3}
-%<+!driver>\GetIdInfo$Id: xeCJK.dtx 5a18688 2017-11-22 19:12:51 +0800 Qing Lee <sobenlee at gmail.com> $
+%<+!driver>\GetIdInfo$Id: xeCJK.dtx 54327e6 2018-01-28 19:10:14 +0800 Qing Lee <sobenlee at gmail.com> $
 %<package>  {Typesetting CJK scripts with XeLaTeX}
 %<config>  {Configuration file for xeCJK package}
 %<fntef>  {xeCJK font effect}
@@ -207,17 +207,17 @@
 %<listings>\ProvidesExplPackage{xeCJK-listings}
 %<xunicode>\ProvidesExplPackage{xunicode-addon}
 %<xunextra>\ProvidesExplFile{xunicode-extra.def}
-%<!driver>  {\ExplFileDate}{3.5.1}{\ExplFileDescription}
+%<!driver>  {\ExplFileDate}{3.6.0}{\ExplFileDescription}
 %</package|config|fntef|listings|xunicode|xunextra>
 %<*driver>
 \documentclass{ctxdoc}
-\xeCJKDeclareSubCJKBlock{HKMD}  { "FF65 }
+\xeCJKDeclareSubCJKBlock{SP} { "2E3A , "301C , "30A0 , "FF65 }
 \xeCJKDeclareSubCJKBlock{Ext-B} { "20000 -> "2A6DF }
 \xeCJKDeclareSubCJKBlock{Hangul}
   { "1100 -> "11FF, "3130 -> "318F, "A960 -> "A97F, "AC00 -> "D7AF }
-\setCJKmainfont[HKMD]{Microsoft YaHei}
+\setCJKmainfont[SP, Language=Chinese Simplified]{Source Han Serif}
 \setCJKmainfont[Ext-B]{SimSun-ExtB}
-\setCJKmainfont[Hangul, Script=Hangul]{Malgun Gothic}
+\setCJKmainfont[Hangul, Script=Hangul, Language=Korean]{Source Han Serif}
 \newlist{psopt}{description}{3}
 \setlist[psopt]{%
   font=\mdseries\ttfamily, align=right,
@@ -231,7 +231,7 @@
   {
     \par
     \begingroup
-    \CJKfontspec{Microsoft~YaHei}
+    \CJKfontspec[Language=Chinese ~ Simplified]{Source ~ Han ~ Serif}
     \tl_clear:N \l_tmpa_tl
     \int_zero:N \l_tmpa_int
     \tl_set:Nx \l_tmpb_tl { \tl_to_str:n { c__xeCJK_#2_chars_clist } }
@@ -245,7 +245,7 @@
             \int_compare:nNnF \l_tmpa_int = \l_tmpb_int
               {
                 \int_compare:nNnTF { \int_mod:nn \l_tmpa_int {#1} } = \c_zero
-                    { \exp_not:N \\ \scan_stop: } { & }
+                  { \exp_not:N \\ \scan_stop: } { & }
               }
           }
       }
@@ -270,15 +270,16 @@
 % \changes{v3.1.1}{2012/12/07}{不再依赖 \pkg{xpatch} 宏包。}
 % \changes{v3.2.2}{2013/06/01}{修正某些重音不能正确显示的问题。}
 % \changes{v3.2.3}{2013/06/07}{提供四个 TECkit 映射文件用于句号转换和简繁互换。}
-% \changes{v3.2.4}{2013/07/02}{遵循 \hologo{LaTeX3} 变量需要预先声明的原则。}
-% \changes{v3.2.6}{2013/07/29}{\texttt{case} 类函数的用法与 \hologo{LaTeX3} 同步。}
+% \changes{v3.2.4}{2013/07/02}{遵循 \LaTeXiii{} 变量需要预先声明的原则。}
+% \changes{v3.2.6}{2013/07/29}{\texttt{case} 类函数的用法与 \LaTeXiii{} 同步。}
 % \changes{v3.3.2}{2015/05/15}{随 Unicode 7.0.0 更新简繁汉字映射。}
-% \changes{v3.3.3}{2015/09/25}{更新 \hologo{LaTeX3} 代码。}
+% \changes{v3.3.3}{2015/09/25}{更新 \LaTeXiii{} 代码。}
 % \changes{v3.5.0}{2017/07/19}{常数 \cs{c_minus_one} 已过时。}
 % \changes{v3.5.0}{2017/07/22}{使用 \texttt{lazy} 函数对 Boolean 表达式
-% 进行最小化运算(\hologo{LaTeX3} 2017/07/19)。}
+% 进行最小化运算(\LaTeXiii{} 2017/07/19)。}
+% \changes{v3.6.0}{2018/01/13}{同步 \LaTeXiii{} 2017/12/16。}
 %
-% \CheckSum{10190}
+% \CheckSum{10769}
 % \GetFileId{xeCJK.sty}
 %
 % \title{\bfseries\pkg{xeCJK} 宏包}
@@ -303,7 +304,7 @@
 % \end{enumerate}
 %
 % \pkg{xeCJK} 使用了 \XeTeX 的一些最新特性,需要 \XeTeX{} 0.9995.0 (2009/06/29) 以
-% 后的版本。\pkg{xeCJK} 依赖 \hologo{LaTeX3} 项目的宏包套件
+% 后的版本。\pkg{xeCJK} 依赖 \LaTeXiii{} 项目的宏包套件
 % \package{l3kernel} 和 \package{l3packages} 。
 % \pkg{xeCJK} 还需要通过 \package{fontspec} 宏包来调用系统字体。
 % \pkg{xeCJK} 会自动根据需要载入这些宏包。
@@ -512,7 +513,8 @@
 %   \begin{syntax}
 %     AutoFakeBold = \Arg{\TFF|数字}
 %   \end{syntax}
-%   全局设定当没有声明对应的粗体时,是否使用\textbf{\CJKfontspec[AutoFakeBold]{Adobe Song Std}伪粗体};
+%   全局设定当没有声明对应的粗体时,是否使用^^A
+%   \textbf{\CJKfontspec[AutoFakeBold]{FandolSong-Regular.otf}伪粗体};
 %   当输入的是数字时,将使用伪粗体,并将使用输入的数字作为伪粗体的默认粗细程度。
 % \end{function}
 %
@@ -520,7 +522,8 @@
 %   \begin{syntax}
 %     AutoFakeSlant = \Arg{\TFF|数字}
 %   \end{syntax}
-%   全局设定当没有声明对应的斜体时,是否使用\textit{\CJKfontspec[AutoFakeSlant]{Adobe Song Std}伪斜体};
+%   全局设定当没有声明对应的斜体时,是否使用^^A
+%   \textit{\CJKfontspec[AutoFakeSlant]{FandolSong-Regular.otf}伪斜体};
 %   当输入的是数字时,将使用伪斜体,并将使用输入的数字作为伪斜体的默认倾斜程度。
 % 倾斜程度的取值范围是 $[-0.999, 0.999]$。
 % \end{function}
@@ -556,6 +559,15 @@
 %   格式。
 % \end{function}
 %
+% \begin{function}[added=2018-01-24]{PunctFamily}
+%   \begin{syntax}
+%     PunctFamily = \Arg{(false)|family}
+%   \end{syntax}
+%   默认情况下,CJK 标点符号的字体与 CJK 正文一致,\opt{PunctFamily} 用于单独对标点符号设置字体。
+%   \meta{family} 需要使用随后说明的 \tn{setCJKfamilyfont} 或 \tn{newCJKfontfamily}
+%   预先定义。\opt{false} 表示取消本选项的作用,让标点符号字体与正文一致。
+% \end{function}
+%
 % \begin{function}[EXP]{KaiMingPunct,KaiMingPunct+,KaiMingPunct-}
 %   \begin{syntax}
 %     KaiMingPunct = \Arg{( . 。? !)}
@@ -566,7 +578,7 @@
 %
 % \begin{function}[EXP]{LongPunct,LongPunct+,LongPunct-}
 %   \begin{syntax}
-%     LongPunct = \Arg{( — ‥ … )}
+%     LongPunct = \Arg{( — ⸺ ‥ … )}
 %   \end{syntax}
 %   设置长标点,例如破折号“——”与省略号“……”,允许在长标点前后
 %   断行,但是禁止在它们之间断行。
@@ -574,7 +586,7 @@
 %
 % \begin{function}[EXP]{MiddlePunct,MiddlePunct+,MiddlePunct-}
 %   \begin{syntax}
-%     MiddlePunct = \Arg{( – — · · ・ )}
+%     MiddlePunct = \Arg{( – — ⸺ · · ・ 〜゠~)}
 %   \end{syntax}
 %   设置居中显示的标点,例如间隔号“\textbf{·}”。对于在 CJK 文字之间的居中标点,
 %   \pkg{xeCJK} 会根据不同的标点处理格式,调整居中标点与前后文字之间的空白,保证
@@ -1530,8 +1542,8 @@
 % 支持 Unicode,因此在 \texttt{listings} 定义的代码环境中可以直接使用中文,不再
 % 需要通过 \texttt{escapechar}。
 %
-% 新版本(\texttt{3.x})的 \pkg{xeCJK} 完全使用 \hologo{LaTeX3} 的语法来编写。
-% \hologo{LaTeX3} 放弃了 \tn{outer} 宏的概念,因此相关工具在
+% 新版本(\texttt{3.x})的 \pkg{xeCJK} 完全使用 \LaTeXiii{} 的语法来编写。
+% \LaTeXiii{} 放弃了 \tn{outer} 宏的概念,因此相关工具在
 % 遇到 \tn{outer} 宏时可能会存在问题。按照目前 \pkg{xeCJK} 的实现方式,在 CJK 文字
 % 后面遇到 \tn{outer} 宏时会出现类似
 % \begin{frameverb}
@@ -1596,11 +1608,11 @@
     using~your~TeX~package~manager~or~from~CTAN.\\
     \str_if_eq:nnT {#1} { expl3 } { Loading~xeCJK~will~abort! }
   }
-\@ifpackagelater { expl3 } { 2017/07/19 } { }
+\@ifpackagelater { expl3 } { 2017/12/16 } { }
   { \msg_critical:nnn { xeCJK } { l3-too-old } { expl3 } }
 %    \end{macrocode}
 %
-% \begin{macro}[pTF,internal]{\xeCJK_if_package_loaded:n}
+% \begin{macro}[pTF,int]{\xeCJK_if_package_loaded:n}
 % 判断宏包是否被引入,可用于文档正文中。
 %    \begin{macrocode}
 \prg_new_conditional:Npnn \xeCJK_if_package_loaded:n #1 { p , T , F , TF }
@@ -1662,7 +1674,7 @@
 \clist_new:N \l_@@_tmp_clist
 %    \end{macrocode}
 %
-% \begin{macro}[internal]
+% \begin{macro}
 %  {\@@_msg_new:nn ,\@@_error:n,\@@_error:nx,\@@_warning:nx,\@@_info:nxx}
 % 各种信息函数的缩略形式。
 %    \begin{macrocode}
@@ -1678,14 +1690,16 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\xeCJK_allow_break:,\xeCJK_no_break:}
+% \begin{macro}[int]{\xeCJK_allow_break:,\xeCJK_no_break:}
 %    \begin{macrocode}
-\cs_new_protected_nopar:Npn \xeCJK_allow_break: { \tex_penalty:D \c_zero }
-\cs_new_protected_nopar:Npn \xeCJK_no_break: { \tex_penalty:D \c_ten_thousand }
+\cs_new_protected_nopar:Npn \xeCJK_allow_break:
+  { \tex_penalty:D \c_zero }
+\cs_new_protected_nopar:Npn \xeCJK_no_break:
+  { \tex_penalty:D \c_ten_thousand }
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]
+% \begin{macro}
 %  {\@@_at_end_preamble:n,\@@_after_preamble:n,\@@_after_end_preamble:n}
 %  在 \tn{document} 前后加上各种钩子。
 %    \begin{macrocode}
@@ -1750,7 +1764,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\xeCJK_add_to_shipout:n}
+% \begin{macro}[int]{\xeCJK_add_to_shipout:n}
 % \changes{v3.2.11}{2014/03/14}{不再使用内部名字。}
 % 往 \tn{shipout} 盒子中加入钩子。
 %    \begin{macrocode}
@@ -1761,15 +1775,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\xeCJK_reverse:nnn}
-% |#1| 为 |#2| 或 |#3|,若 |#1| 和 |#2| 相等,则返回 |#3|,否则返回 |#2|。
-%    \begin{macrocode}
-\cs_new_nopar:Npn \xeCJK_reverse:nnn #1#2#3
-  { \str_if_eq_x:nnTF {#1} {#2} {#3} {#2} }
-%    \end{macrocode}
-% \end{macro}
-%
-% \begin{macro}[internal]
+% \begin{macro}[int]
 % {\xeCJK_tl_remove_outer_braces:N,\xeCJK_tl_remove_outer_braces:n}
 % \changes{v3.2.4}{2013/07/02}{去掉外层分组括号时,移除空格,避免死循环。}
 % 去掉 |#1| 外层的分组括号。
@@ -1792,7 +1798,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\xeCJK_cs_clear:N,\xeCJK_cs_gclear:N}
+% \begin{macro}[int]{\xeCJK_cs_clear:N,\xeCJK_cs_gclear:N}
 % 让控制序列的意义为空。
 %    \begin{macrocode}
 \cs_new_protected:Npn \xeCJK_cs_clear:N #1
@@ -1802,7 +1808,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\xeCJK_swap_cs:NN}
+% \begin{macro}[int]{\xeCJK_swap_cs:NN}
 % 交换 |#1| 和 |#2| 的意义。
 %    \begin{macrocode}
 \cs_new_protected:Npn \xeCJK_swap_cs:NN #1#2
@@ -1815,7 +1821,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\xeCJK_font_gset_to_current:c}
+% \begin{macro}[int]{\xeCJK_font_gset_to_current:c}
 % |#1| 是控制序列的名字,令它等于当前字体命令。
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \xeCJK_font_gset_to_current:c #1
@@ -1826,7 +1832,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal,pTF]{\xeCJK_glyph_if_exist:N}
+% \begin{macro}[int,pTF]{\xeCJK_glyph_if_exist:N}
 % \changes{v3.1.0}{2012/11/19}{改进 \pkg{fontspec} 宏包中定义的
 % \cs{font_glyph_if_exist:NnTF}。}
 % 判断当前字体中是否含有字符 |#1|。\pkg{fontspec} 中的类似函数在判断为真的时候,
@@ -1841,7 +1847,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal,var]{\c_xeCJK_space_skip_tl}
+% \begin{variable}[int]{\c_xeCJK_space_skip_tl}
 % \changes{v3.1.0}{2012/11/18}{字间空格考虑 \tn{spaceskip} 不为零的情况。}
 % \changes{v3.2.0}{2013/05/22}{字间空格考虑到 \tn{spacefactor} 和 \tn{xspaceskip} 的情况。}
 % 当前字体状态下,一个字间空格产生的 |glue| 的长度,包括伸展和收缩部分。
@@ -1848,7 +1854,7 @@
 %    \begin{macrocode}
 \tl_const:Nn \c_xeCJK_space_skip_tl
   {
-    \int_compare:nNnTF \g_@@_spacefactor_int = \c_one_thousand
+    \int_compare:nNnTF \g_@@_space_factor_int = \c_one_thousand
       {
         \skip_if_eq:nnTF \tex_spaceskip:D \c_zero_skip
           {
@@ -1861,9 +1867,12 @@
       {
         \skip_if_eq:nnTF \tex_spaceskip:D \c_zero_skip
           {
-            \int_compare:nNnTF \g_@@_spacefactor_int < { 2000 }
-              { \_@@_space_skip_scale:nnn { \tex_fontdimen:D \c_two \tex_font:D } }
+            \int_compare:nNnTF \g_@@_space_factor_int < { 2000 }
               {
+                \_@@_space_skip_scale:nnn
+                  { \tex_fontdimen:D \c_two \tex_font:D }
+              }
+              {
                 \skip_if_eq:nnTF \tex_xspaceskip:D \c_zero_skip
                   {
                     \_@@_space_skip_scale:nnn
@@ -1878,13 +1887,16 @@
               { \tex_fontdimen:D \c_four  \tex_font:D }
           }
           {
-            \int_compare:nNnTF \g_@@_spacefactor_int < { 2000 }
+            \int_compare:nNnTF \g_@@_space_factor_int < { 2000 }
               { \_@@_space_skip_scale:nnn { \tex_spaceskip:D } }
               {
                 \skip_if_eq:nnTF \tex_xspaceskip:D \c_zero_skip
                   {
                     \_@@_space_skip_scale:nnn
-                      { \tex_spaceskip:D + \tex_fontdimen:D \c_seven \tex_font:D }
+                      {
+                        \tex_spaceskip:D +
+                        \tex_fontdimen:D \c_seven \tex_font:D
+                      }
                   }
                   { \tex_xspaceskip:D \use_none:nn }
               }
@@ -1896,17 +1908,17 @@
 \cs_new_nopar:Npn \_@@_space_skip_scale:nnn #1#2#3
   {
     \dim_eval:n {#1}
-    plus \fp_eval:n { \g_@@_spacefactor_int / 1000 } #2
+    plus \fp_eval:n { \g_@@_space_factor_int / 1000 } #2
     minus
       \int_div_truncate:nn
-        { 1000 * \tex_number:D #3 } { \g_@@_spacefactor_int } sp
+        { 1000 * \tex_number:D #3 } { \g_@@_space_factor_int } sp
   }
-\int_new:N \g_@@_spacefactor_int
-\int_gset_eq:NN \g_@@_spacefactor_int \c_one_thousand
+\int_new:N \g_@@_space_factor_int
+\int_gset_eq:NN \g_@@_space_factor_int \c_one_thousand
 %    \end{macrocode}
-% \end{macro}
+% \end{variable}
 %
-% \begin{macro}[internal]{\xeCJK_glue_to_skip:nN}
+% \begin{macro}[int]{\xeCJK_glue_to_skip:nN}
 % 取得一个 |glue| 的长度,包括伸展和收缩部分。如果参数不是 |glue|,则取其宽度。
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \xeCJK_glue_to_skip:nN #1#2
@@ -1928,7 +1940,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[pTF,internal]{\xeCJK_if_blank_x:n}
+% \begin{macro}[pTF,int]{\xeCJK_if_blank_x:n}
 % 判断是否为空或者仅含一个空格。
 %    \begin{macrocode}
 \prg_new_conditional:Npnn \xeCJK_if_blank_x:n #1 { p , T , F , TF }
@@ -1943,20 +1955,24 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\xeCJK_int_until_do:nn,\@@_int_until_do:wn}
+% \begin{macro}[int]{\xeCJK_int_until_do:nn}
+% \begin{macro}{\@@_int_until_do:wn}
 % 由于定义较为简单,可以比 \cs{int_until_do:nNnn} 稍微快一点点。
 %    \begin{macrocode}
 \cs_new_protected:Npn \xeCJK_int_until_do:nn #1#2
-  { \@@_int_until_do:wn \use_none:n { \reverse_if:N \if_int_compare:w #1#2 } }
+  {
+    \@@_int_until_do:wn \use_none:n
+      { \reverse_if:N \if_int_compare:w #1#2 }
+  }
 \cs_new_protected:Npn \@@_int_until_do:wn \use_none:n #1
   { #1 \exp_after:wN \@@_int_until_do:wn \fi: \use_none:n {#1} }
 \int_new:N \l_@@_begin_int
 \int_new:N \l_@@_end_int
 %    \end{macrocode}
-%
 % \end{macro}
+% \end{macro}
 %
-% \begin{macro}[internal]{\xeCJK_peek_catcode_ignore_spaces:NTF}
+% \begin{macro}[int]{\xeCJK_peek_catcode_ignore_spaces:NTF}
 % \changes{v3.1.1}{2012/12/04}{新增有省略空格标识的 \texttt{peek} 函数。}
 % 我们在里面设置了一个变量 \cs{l_@@_peek_ignore_spaces_bool} 用于标识后面的空格
 % 是否被省略掉了。
@@ -1963,7 +1979,7 @@
 %    \begin{macrocode}
 \cs_new_protected:Npn \xeCJK_peek_catcode_ignore_spaces:NTF #1#2#3
   {
-    \cs_set_eq:NN \l__peek_search_token #1 \scan_stop:
+    \cs_set_eq:NN \l_@@_peek_search_token #1 \scan_stop:
     \tl_set:Nn \@@_peek_catcode_true:w  { \group_align_safe_end: #2 }
     \tl_set:Nn \@@_peek_catcode_false:w { \group_align_safe_end: #3 }
     \bool_set_false:N \l_@@_peek_ignore_spaces_bool
@@ -1979,7 +1995,7 @@
       \tex_romannumeral:D 0
     \else:
       \if_catcode:w
-        \exp_not:N \l_peek_token \exp_not:N \l__peek_search_token
+        \exp_not:N \l_peek_token \exp_not:N \l_@@_peek_search_token
         \exp_after:wN \exp_after:wN
         \exp_after:wN \@@_peek_catcode_true:w
       \else:
@@ -1988,6 +2004,7 @@
       \fi:
     \fi:
   }
+\cs_new_eq:NN \l_@@_peek_search_token ?
 \tl_new:N \@@_peek_catcode_true:w
 \tl_new:N \@@_peek_catcode_false:w
 \bool_new:N \l_@@_peek_ignore_spaces_bool
@@ -1994,7 +2011,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\xeCJK_peek_after_ignore_spaces:nw}
+% \begin{macro}[int]{\xeCJK_peek_after_ignore_spaces:nw}
 % 与 \tn{@ifnextchar} 和 \tn{futurenonspacelet} 类似,会省略掉后面的空格。
 %    \begin{macrocode}
 \cs_new_protected:Npn \xeCJK_peek_after_ignore_spaces:nw #1
@@ -2016,7 +2033,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\xeCJK_token_value_class:N}
+% \begin{macro}[int]{\xeCJK_token_value_class:N}
 % 用于取得记号 |#1| 所在的 \XeTeX 字符类。|#1| 应为 \tn{catcode} 为 |11| 或 |12|
 % 的显性或隐性记号。
 %    \begin{macrocode}
@@ -2025,7 +2042,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\xeCJK_token_value_charcode:N}
+% \begin{macro}[int]{\xeCJK_token_value_charcode:N}
 % \changes{v3.2.4}{2013/06/30}{考虑 \texttt{charcode} 超出 BMP 的情况。}
 % \changes{v3.3.1}{2015/05/08}{\texttt{0.99992} 版修复了 \tn{meaning} 的 Bug。}
 % 当记号 |#1| 的 \texttt{charcode} 大于等于 \texttt{0x10000} 时,\XeTeX\
@@ -2072,27 +2089,28 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[pTF,internal]{\xeCJK_if_CJK_class:N}
+% \begin{macro}[pTF,int]{\xeCJK_if_CJK_class:N}
 % 判断字符 |#1| 是否为 CJK 字符类,包括文字和标点符号。
 %    \begin{macrocode}
 \prg_new_conditional:Npnn \xeCJK_if_CJK_class:N #1 { p , T , F , TF }
   {
-    \if_cs_exist:w \@@_CJK_class_tl:n { \xeCJK_token_value_class:N #1 } \cs_end:
+    \if_cs_exist:w
+      \@@_CJK_class_tl:n { \xeCJK_token_value_class:N #1 }
+    \cs_end:
       \prg_return_true: \else: \prg_return_false: \fi:
   }
 \cs_new_nopar:Npn \@@_CJK_class_tl:n #1
   { c_@@_CJK_class_ \int_eval:n {#1} _tl }
-\cs_generate_variant:Nn \@@_CJK_class_tl:n { c }
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[pTF,internal]{\xeCJK_if_same_class:NN}
+% \begin{macro}[pTF,int]{\xeCJK_if_same_class:NN}
 % 判断两个字符是否同属于一个字符类。
 %    \begin{macrocode}
 \prg_new_conditional:Npnn \xeCJK_if_same_class:NN #1#2 { p , T , F , TF }
   {
-    \if_int_compare:w
-      \xeCJK_token_value_class:N #1 = \xeCJK_token_value_class:N #2 \exp_stop_f:
+    \if_int_compare:w \xeCJK_token_value_class:N #1 =
+                      \xeCJK_token_value_class:N #2 \exp_stop_f:
       \prg_return_true: \else: \prg_return_false: \fi:
   }
 %    \end{macrocode}
@@ -2113,10 +2131,12 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\makexeCJKactive, \makexeCJKinactive}
+% \begin{macro}[int]{\makexeCJKactive, \makexeCJKinactive}
 %    \begin{macrocode}
-\NewDocumentCommand \makexeCJKactive   { } { \xetex_interchartokenstate:D = \c_one  }
-\NewDocumentCommand \makexeCJKinactive { } { \xetex_interchartokenstate:D = \c_zero }
+\NewDocumentCommand \makexeCJKactive   { }
+  { \xetex_interchartokenstate:D = \c_one }
+\NewDocumentCommand \makexeCJKinactive { }
+  { \xetex_interchartokenstate:D = \c_zero }
 %    \end{macrocode}
 % \end{macro}
 %
@@ -2127,15 +2147,15 @@
 %
 % \subsection{字符类别设定}\label{sec:xeCJK-class-set}
 %
-% \begin{macro}[internal,var]{\g_@@_class_seq,\g_@@_new_class_seq}
+% \begin{variable}{\g_@@_class_seq,\g_@@_new_class_seq}
 % 分别用于记录在 \pkg{xeCJK} 中使用的字符类别名称和新建的字符类别的编号。
 %    \begin{macrocode}
 \seq_new:N \g_@@_class_seq
 \seq_new:N \g_@@_new_class_seq
 %    \end{macrocode}
-% \end{macro}
+% \end{variable}
 %
-% \begin{macro}[internal]{\xeCJK_new_class:n}
+% \begin{macro}[int]{\xeCJK_new_class:n}
 % 新建一个字符类别。|#1| 是自定义名称。
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \xeCJK_new_class:n #1
@@ -2143,16 +2163,18 @@
     \int_if_exist:cTF { \@@_class_csname:n {#1} }
       { \@@_error:nx { class-already-defined } {#1} }
       {
-        \exp_args:Nc \newXeTeXintercharclass { \@@_class_csname:n {#1} }
+        \exp_args:Nc \newXeTeXintercharclass
+          { \@@_class_csname:n {#1} }
         \clist_new:c { g_@@_#1_range_clist }
         \seq_gput_right:Nn \g_@@_class_seq {#1}
-        \seq_gput_right:Nv \g_@@_new_class_seq { \@@_class_csname:n {#1} }
+        \seq_gput_right:Nv \g_@@_new_class_seq
+          { \@@_class_csname:n {#1} }
       }
   }
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\xeCJK_save_class:nn}
+% \begin{macro}[int]{\xeCJK_save_class:nn}
 % \changes{v3.1.1}{2012/12/06}
 % {使用 \cs{xeCJK_save_class:nn} 保存 \XeTeX 预定义的字符类别。}
 % 保存 \XeTeX 预定义的字符类别。|#1| 是自定义名称,|#2| 是编号。
@@ -2170,7 +2192,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_class_csname:n}
+% \begin{macro}{\@@_class_csname:n}
 % 字符类名称对应的控制序列名字。
 %    \begin{macrocode}
 \cs_new_nopar:Npn \@@_class_csname:n #1 { c_@@_#1_class_int }
@@ -2212,7 +2234,7 @@
 %
 % \changes{v3.3.3}{2016/01/20}{兼容 \LaTeXe{} 2016/02/01 的字符类设置。}
 % \changes{v3.3.4}{2016/02/07}{兼容 \XeTeX{} 0.99994 的边界字符类。}
-% \begin{macro}[internal]{Default,CJK,FullLeft,FullRight,Boundary}
+% \begin{macro}[int]{Default,CJK,FullLeft,FullRight,Boundary}
 %    \begin{macrocode}
 \xeCJK_save_class:nn { Default } { \c_zero }
 %    \end{macrocode}
@@ -2245,7 +2267,7 @@
 %
 % \changes{v3.2.15}{2014/11/10}{增加 \texttt{HangulJamo} 字符类。}
 %
-% \begin{macro}[internal]{HalfLeft,HalfRight,NormalSpace,CM,HangulJamo}
+% \begin{macro}[int]{HalfLeft,HalfRight,NormalSpace,CM,HangulJamo}
 % 新增西文半角左/右标点、前后原始间距的符号和异体字选择符类。
 %    \begin{macrocode}
 \xeCJK_new_class:n { HalfLeft }
@@ -2262,7 +2284,7 @@
 % {把 REVERSE SOLIDUS(\texttt{U+005C})、HYPHEN-MINUS(\texttt{U+002D})和
 %  EN DASH(\texttt{U+2013})归入 \texttt{NormalSpace} 类。}
 %
-% \begin{macro}[var,internal]
+% \begin{variable}
 %  {\c_@@_HalfLeft_chars_clist,\c_@@_HalfRight_chars_clist,\c_@@_NormalSpace_chars_clist}
 % \hypertarget{CJKcharclass}{西文半角左/右标点和前后原始间距的字符类。}
 %    \begin{macrocode}
@@ -2272,7 +2294,7 @@
   { "21 , "22 , "25 , "27 , "29 , "2C , "2E , "3A , "3B , "3F , "5D , "7D , "232A }
 \clist_const:Nn \c_@@_NormalSpace_chars_clist { "2D , "2F , "5C }
 %    \end{macrocode}
-% \end{macro}
+% \end{variable}
 %
 % 以下对全角标点符号的归类来源于 \XeTeX 的脚本
 % \href{http://sourceforge.net/p/xetex/code/ci/master/tree/source/texk/web2c/xetexdir/unicode-char-prep.pl}
@@ -2280,7 +2302,7 @@
 %
 % \changes{v3.2.3}{2013/06/09}{根据 \XeTeX 的脚本重新整理全角标点符号。}
 %
-% \begin{macro}[var,internal]{\c_@@_OP_chars_clist}
+% \begin{variable}{\c_@@_OP_chars_clist}
 % Open Punctuation (OP)
 % \PrintPunctList{OP}{Open Punctuation}
 % 以下代码的第一行是中西文共用的左引号。
@@ -2293,9 +2315,9 @@
     "FE59 , "FE5B , "FE5D , "FF08 , "FF3B , "FF5B , "FF5F , "FF62
   }
 %    \end{macrocode}
-% \end{macro}
+% \end{variable}
 %
-% \begin{macro}[var,internal]{\c_@@_PR_chars_clist}
+% \begin{variable}{\c_@@_PR_chars_clist}
 % \changes{v3.3.0}{2014/12/26}{不把 \texttt{U+20A9} 归入 CJK 的 PR 类。}
 % Prefix Numeric (PR)
 % \PrintPunctList{PR}{Prefix Numeric}
@@ -2303,25 +2325,27 @@
 \clist_const:Nn \c_@@_PR_chars_clist
   { "FE69 , "FF04 , "FFE1 , "FFE5 , "FFE6 }
 %    \end{macrocode}
-% \end{macro}
+% \end{variable}
 %
-% \begin{macro}[var,internal]{\c_@@_FullLeft_chars_clist}
+% \begin{variable}{\c_@@_FullLeft_chars_clist}
 % 以上两类标点符号出现在文字的左边,不应出现在行尾位置。
 %    \begin{macrocode}
-\clist_const:Nx \c_@@_FullLeft_chars_clist
-  {
-    \c_@@_OP_chars_clist ,
-    \c_@@_PR_chars_clist
-  }
+\clist_new:N \c_@@_FullLeft_chars_clist
+\clist_gconcat:NNN \c_@@_FullLeft_chars_clist
+                   \c_@@_OP_chars_clist
+                   \c_@@_PR_chars_clist
 %    \end{macrocode}
-% \end{macro}
+% \end{variable}
 %
 % \changes{v3.3.3}{2015/12/12}
 % {把 EN DASH(\texttt{U+2013})作为半字线连接号归入 \texttt{FullRight} 类。}
 % \changes{v3.3.3}{2015/12/12}
 % {不再把 \texttt{U+2015} 和 \texttt{U+2500} 归入 \texttt{FullRight} 类。}
+% \changes{v3.6.0}{2018/01/14}
+% {把 TWO-EM DASH (\texttt{U+2E3A}) 归入 \texttt{FullRight} 类和设为
+%  \texttt{LongPunct} 与 \texttt{MiddlePunct}。}
 %
-% \begin{macro}[var,internal]{\c_@@_CL_chars_clist}
+% \begin{variable}{\c_@@_CL_chars_clist}
 % Close Punctuation (CL)
 % \PrintPunctList{CL}{Close Punctuation}
 % 以下代码的第一行是中西文共用的一些标点符号。
@@ -2328,7 +2352,7 @@
 %    \begin{macrocode}
 \clist_const:Nn \c_@@_CL_chars_clist
   {
-    "00B7 , "2019 , "201D , "2013 , "2014 , "2025 , "2026 , "2027 ,
+    "00B7 , "2019 , "201D , "2013 , "2014 , "2025 , "2026 , "2027 , "2E3A ,
     "3001 , "3002 , "3009 , "300B , "300D , "300F , "3011 , "3015 , "3017 , "3019 ,
     "301B , "301E , "301F , "FE11 , "FE12 , "FE18 , "FE36 , "FE38 , "FE3A , "FE3C ,
     "FE3E , "FE40 , "FE42 , "FE44 , "FE48 , "FE50 , "FE52 , "FE5A , "FE5C , "FE5E ,
@@ -2335,11 +2359,13 @@
     "FF09 , "FF0C , "FF0E , "FF3D , "FF5D , "FF60 , "FF61 , "FF63 , "FF64
   }
 %    \end{macrocode}
-% \end{macro}
+% \end{variable}
 %
-% \changes{v3.3.0}{2014/12/26}{不把 NS 类中的一些有禁则的日文归入 FullRight 类。}
+% \changes{v3.3.0}{2014/12/26}{不把 NS 类中的一些有禁则的日文归入 \texttt{FullRight} 类。}
+% \changes{v3.6.0}{2018/01/14}
+% {将全角浪线 \texttt{U+FF5E} 等连接号归入 \texttt{FullRight} 类和设为 \texttt{MiddlePunct}。}
 %
-% \begin{macro}[var,internal]{\c_@@_NS_chars_clist}
+% \begin{variable}{\c_@@_NS_chars_clist}
 % Nonstarter (NS)
 % \PrintPunctList{NS}{Nonstarter}
 % \noindent Hyphens (cl-03)
@@ -2347,18 +2373,27 @@
 % \noindent Iteration marks (cl-09)
 % \PrintPunctList{iteration_marks}{Iteration marks}
 % 根据 W3C 的资料\footnote{\url{http://www.w3.org/TR/jlreq/}},\texttt{cl-03} 和
-% \texttt{cl-09} 在非常松散的情况下可以没有禁则。我们就不把它们当成标点来处理禁则,
-% 避免间距错误。
+% \texttt{cl-09} 在非常松散的情况下可以没有禁则。我们仅将全角浪线 \texttt{U+FF5E} 等连接号归入
+% \texttt{FullRight} 类并在宏包末尾设为 \texttt{MiddlePunct}。
 %    \begin{macrocode}
-\clist_const:Nn \c_@@_hyphens_chars_clist { "301C , "30A0 }
+\clist_const:Nn \c_@@_hyphens_chars_clist
+  { "301C , "30A0 , "FF5E }
 \clist_const:Nn \c_@@_iteration_marks_chars_clist
   { "3005 , "303B , "309D , "309E , "30FD , "30FE }
 \clist_const:Nn \c_@@_NS_chars_clist
   { "30FB , "FE54 , "FE55 , "FF1A , "FF1B , "FF65 , "16FE0 }
+\AtEndOfPackage
+  {
+    \cs_set:Npn \@@_tmp:w #1
+      { \char_generate:nn {#1} { \c_twelve } }
+    \@@_add_special_punct:nn { middle }
+      { \clist_map_function:NN \c_@@_hyphens_chars_clist \@@_tmp:w }
+    \cs_undefine:N \@@_tmp:w
+  }
 %    \end{macrocode}
-% \end{macro}
+% \end{variable}
 %
-% \begin{macro}[var,internal]{\c_@@_EX_chars_clist}
+% \begin{variable}{\c_@@_EX_chars_clist}
 % Exclamation/Interrogation (EX)
 % \PrintPunctList{EX}{Exclamation/Interrogation}
 %    \begin{macrocode}
@@ -2365,17 +2400,17 @@
 \clist_const:Nn \c_@@_EX_chars_clist
   { "FE15 , "FE16 , "FE56 , "FE57 , "FF01 , "FF1F }
 %    \end{macrocode}
-% \end{macro}
+% \end{variable}
 %
-% \begin{macro}[var,internal]{\c_@@_IS_chars_clist}
+% \begin{variable}{\c_@@_IS_chars_clist}
 % Infix Numeric Separator (IS)
 % \PrintPunctList{IS}{Infix Numeric Separator}
 %    \begin{macrocode}
 \clist_const:Nn \c_@@_IS_chars_clist { "FE10 , "FE13 , "FE14 }
 %    \end{macrocode}
-% \end{macro}
+% \end{variable}
 %
-% \begin{macro}[var,internal]{\c_@@_CJ_chars_clist}
+% \begin{variable}{\c_@@_CJ_chars_clist}
 % Conditional Japanese Starter (CJ)。这类字符的禁则是可选的^^A
 % \footnote{\url{https://github.com/CTeX-org/ctex-kit/issues/165}},
 % 为实现的简单计,我们把它们归入 CJK 类,即没有禁则。
@@ -2391,37 +2426,43 @@
     "FF70
   }
 %    \end{macrocode}
-% \end{macro}
+% \end{variable}
 %
-% \begin{macro}[var,internal]{\c_@@_PO_chars_clist}
+% \begin{variable}{\c_@@_PO_chars_clist}
 % Postfix Numeric (PO)
 % \PrintPunctList{PO}{Postfix Numeric}
 %    \begin{macrocode}
 \clist_const:Nn \c_@@_PO_chars_clist { "FE6A , "FF05 , "FFE0 }
 %    \end{macrocode}
-% \end{macro}
+% \end{variable}
 %
-% \changes{v3.3.0}{2014/12/26}{不把小写日文假名归入 FullRight 类。}
+% \changes{v3.3.0}{2014/12/26}{不把小写日文假名归入 \texttt{FullRight} 类。}
 %
-% \begin{macro}[var,internal]{\c_@@_FullRight_chars_clist}
+% \begin{variable}{\c_@@_FullRight_chars_clist}
 % 以上六类标点符号出现在文字的右边,不应出现在行首位置。
 %    \begin{macrocode}
-\clist_const:Nx \c_@@_FullRight_chars_clist
+\clist_new:N \c_@@_FullRight_chars_clist
+\tl_map_inline:nn
   {
-    \c_@@_CL_chars_clist ,
-    \c_@@_NS_chars_clist ,
-    \c_@@_EX_chars_clist ,
-    \c_@@_IS_chars_clist ,
+    \c_@@_CL_chars_clist
+    \c_@@_NS_chars_clist
+    \c_@@_EX_chars_clist
+    \c_@@_IS_chars_clist
     \c_@@_PO_chars_clist
+    \c_@@_hyphens_chars_clist
   }
+  {
+    \clist_gconcat:NNN \c_@@_FullRight_chars_clist
+                       \c_@@_FullRight_chars_clist #1
+  }
 %    \end{macrocode}
-% \end{macro}
+% \end{variable}
 %
 % \changes{v3.3.3}{2015/06/25}{补充 Ext-E。}
 % \changes{v3.4.1}{2016/08/18}{补充 Unicode 9.0.0 的西夏文。}
 % \changes{v3.5.0}{2017/07/22}{补充 Ext-F。}
 %
-% \begin{macro}[var,internal]{\c_@@_CJK_chars_clist}
+% \begin{variable}{\c_@@_CJK_chars_clist}
 % CJK 字符类,包括文字和标点符号。
 %    \begin{macrocode}
 \clist_const:Nn \c_@@_CJK_chars_clist
@@ -2572,9 +2613,9 @@
 %    \begin{macrocode}
   }
 %    \end{macrocode}
-% \end{macro}
+% \end{variable}
 %
-% \begin{macro}[var,internal]{\c_@@_CM_chars_clist}
+% \begin{variable}{\c_@@_CM_chars_clist}
 % \changes{v3.3.1}{2015/01/22}{补充音调符号。}
 % 包括日文假名浊点和异体字选择符。组合标识是最好是归入 256 类,即透明类,不会影响
 % 状态。但也会产生一定问题。比如下面的例子,位于行尾的“二”造成分组不匹配。
@@ -2614,9 +2655,9 @@
 %    \begin{macrocode}
   }
 %    \end{macrocode}
-% \end{macro}
+% \end{variable}
 %
-% \begin{macro}[var,internal]{\c_@@_HangulJamo_chars_clist}
+% \begin{variable}{\c_@@_HangulJamo_chars_clist}
 % 朝鲜文字母。
 %    \begin{macrocode}
 \clist_const:Nn \c_@@_HangulJamo_chars_clist
@@ -2639,14 +2680,15 @@
 %    \begin{macrocode}
   }
 %    \end{macrocode}
-% \end{macro}
+% \end{variable}
 %
 % \subsection{字符类别处理}
 %
-% \begin{macro}[internal]{\xeCJK_class_num:n}
+% \begin{macro}[int]{\xeCJK_class_num:n}
 % |#1| 为字符类别名称,用于取得字符类别对应的编号。
 %    \begin{macrocode}
-\cs_new_nopar:Npn \xeCJK_class_num:n #1 { \use:c { \@@_class_csname:n {#1} } }
+\cs_new_nopar:Npn \xeCJK_class_num:n #1
+  { \use:c { \@@_class_csname:n {#1} } }
 %    \end{macrocode}
 % \end{macro}
 %
@@ -2660,8 +2702,8 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\xeCJK_declare_char_class:nn,\xeCJK_declare_char_class:nN}
-% \begin{macro}[aux]{\@@_set_char_class_aux:Nnw}
+% \begin{macro}[int]{\xeCJK_declare_char_class:nn,\xeCJK_declare_char_class:nN}
+% \begin{macro}{\@@_set_char_class_aux:Nnw}
 % 用于设置字符所属的类别,|#1| 为类别名称,|#2| 为字符的 |Unicode|,相邻字符用
 % 半角逗号隔开,支持类似 |"1100 -> "11FF| 起止范围的使用方式。
 %    \begin{macrocode}
@@ -2687,12 +2729,11 @@
 \NewDocumentCommand \@@_set_char_class_aux:Nnw
   { m > { \SplitArgument { 1 } { -> } } m } { #1 #2 }
 \cs_generate_variant:Nn \clist_gconcat:NNN { cc }
-\cs_generate_variant:Nn \xeCJK_declare_char_class:nn { nc }
 %    \end{macrocode}
 % \end{macro}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_check_num_range:nnNN}
+% \begin{macro}{\@@_check_num_range:nnNN}
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \@@_check_num_range:nnNN #1#2#3#4
   {
@@ -2704,8 +2745,8 @@
         \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} } }
+        \int_set:Nn #3 { \int_min:nn {#1} { \tl_if_novalue:nTF {#2} {#1} {#2} } }
+        \int_set:Nn #4 { \int_max:nn {#1} { \tl_if_novalue:nTF {#2} {#1} {#2} } }
       }
   }
 %    \end{macrocode}
@@ -2727,7 +2768,7 @@
   }
 %    \end{macrocode}
 %
-% \begin{macro}[internal]{\xeCJK_set_char_class:nnn}
+% \begin{macro}[int]{\xeCJK_set_char_class:nnn}
 % \changes{v3.1.1}{2012/12/05}{在文档中设置字符类别时不重复设置 \tn{catcode}。}
 % 设置字符类别,|#1| 和 |#2| 为字符类别起止的 |Unicode|,|#3| 为类别名称对应编号。
 %    \begin{macrocode}
@@ -2744,7 +2785,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_set_char_class_eq:nn}
+% \begin{macro}{\@@_set_char_class_eq:nn}
 % \changes{v3.1.1}{2012/12/06}{交换参数的顺序。}
 % 将字符类 |#1| 中的字符全部设置成字符类 |#2|。只适用于 |#1| 的字符类范围为离散的
 % 逗号列表的情况。
@@ -2810,24 +2851,30 @@
 \xeCJKResetCharClass
 %    \end{macrocode}
 %
-% \begin{macro}[internal]{\xeCJK_inter_class_toks:nnn}
+% \begin{macro}[int]{\xeCJK_inter_class_toks:nnn}
 % 在相邻类别之间插入内容。
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \xeCJK_inter_class_toks:nnn #1#2#3
-  { \xetex_interchartoks:D \xeCJK_class_num:n {#1} ~ \xeCJK_class_num:n {#2} = {#3} }
-\cs_generate_variant:Nn \xeCJK_inter_class_toks:nnn { nnc , nnx }
+  {
+    \xetex_interchartoks:D \xeCJK_class_num:n {#1} ~
+                           \xeCJK_class_num:n {#2} = {#3}
+  }
+\cs_generate_variant:Nn \xeCJK_inter_class_toks:nnn { nnx }
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\xeCJK_get_inter_class_toks:nn}
+% \begin{macro}[int]{\xeCJK_get_inter_class_toks:nn}
 % 取出相邻类别之间的内容。
 %    \begin{macrocode}
 \cs_new_nopar:Npn \xeCJK_get_inter_class_toks:nn #1#2
-  { \tex_the:D \xetex_interchartoks:D \xeCJK_class_num:n {#1} ~ \xeCJK_class_num:n {#2} }
+  {
+    \tex_the:D \xetex_interchartoks:D \xeCJK_class_num:n {#1} ~
+                                      \xeCJK_class_num:n {#2}
+  }
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\xeCJK_clear_inter_class_toks:nn}
+% \begin{macro}[int]{\xeCJK_clear_inter_class_toks:nn}
 % 清除相邻类别之间的内容。注意,直接赋空值可能会导致 \XeTeX 崩溃。例如
 % \begin{verbatim}
 %   \XeTeXinterchartokenstate = 1
@@ -2845,7 +2892,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\xeCJK_pre_inter_class_toks:nnn}
+% \begin{macro}[int]{\xeCJK_pre_inter_class_toks:nnn}
 % 在相邻类别之间已有的内容前增加内容。
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \xeCJK_pre_inter_class_toks:nnn #1#2#3
@@ -2857,7 +2904,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\xeCJK_app_inter_class_toks:nnn}
+% \begin{macro}[int]{\xeCJK_app_inter_class_toks:nnn}
 % 在相邻类别之间已有的内容后追加内容。
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \xeCJK_app_inter_class_toks:nnn #1#2#3
@@ -2865,19 +2912,21 @@
     \xeCJK_inter_class_toks:nnx {#1} {#2}
       { \xeCJK_get_inter_class_toks:nn {#1} {#2} \exp_not:n {#3} }
   }
-\cs_generate_variant:Nn \xeCJK_app_inter_class_toks:nnn { nnc , nnx }
+\cs_generate_variant:Nn \xeCJK_app_inter_class_toks:nnn { nnx }
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\xeCJK_copy_inter_class_toks:nnnn}
+% \begin{macro}[int]{\xeCJK_copy_inter_class_toks:nnnn}
 % 将 |#3| 和 |#4| 之间的内容复制到 |#1| 和 |#2| 之间。
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \xeCJK_copy_inter_class_toks:nnnn #1#2#3#4
   {
-    \tl_set:Nx \l_@@_tmp_tl { \xeCJK_get_inter_class_toks:nn {#3} {#4} }
+    \tl_set:Nx \l_@@_tmp_tl
+      { \xeCJK_get_inter_class_toks:nn {#3} {#4} }
     \tl_if_empty:NTF \l_@@_tmp_tl
       {
-        \tl_set:Nx \l_@@_tmp_tl { \xeCJK_get_inter_class_toks:nn {#1} {#2} }
+        \tl_set:Nx \l_@@_tmp_tl
+          { \xeCJK_get_inter_class_toks:nn {#1} {#2} }
         \tl_if_empty:NF \l_@@_tmp_tl
           { \xeCJK_clear_inter_class_toks:nn {#1} {#2} }
       }
@@ -2886,22 +2935,24 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\xeCJK_replace_inter_class_toks:nnnn}
+% \begin{macro}[int]{\xeCJK_replace_inter_class_toks:nnnn}
 % 将 |#1| 和 |#2| 之间出现的 |#3| 用 |#4| 替换。
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \xeCJK_replace_inter_class_toks:nnnn #1#2#3#4
   {
-    \tl_set:Nx \l_@@_tmp_tl { \xeCJK_get_inter_class_toks:nn {#1} {#2} }
+    \tl_set:Nx \l_@@_tmp_tl
+      { \xeCJK_get_inter_class_toks:nn {#1} {#2} }
     \tl_if_empty:NF \l_@@_tmp_tl
       {
         \tl_replace_all:Nnn \l_@@_tmp_tl {#3} {#4}
-        \xeCJK_inter_class_toks:nnx {#1} {#2} { \exp_not:o \l_@@_tmp_tl }
+        \xeCJK_inter_class_toks:nnx {#1} {#2}
+          { \exp_not:o \l_@@_tmp_tl }
       }
   }
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\xeCJK_clear_Boundary_and_CJK_toks:}
+% \begin{macro}[int]{\xeCJK_clear_Boundary_and_CJK_toks:}
 % \changes{v3.4.2}{2016/10/19}{提高效率,避免重复循环。}
 % 清除边界与 CJK 文字、全角左右标点之间的内容。
 %    \begin{macrocode}
@@ -2913,14 +2964,14 @@
       {
         \exp_not:o { \xeCJK_clear_Boundary_and_CJK_toks: }
         \xetex_interchartoks:D
-          \xeCJK_class_num:n { Boundary } ~ \xeCJK_class_num:n {#1}
-            = { \exp_not:N \prg_do_nothing: }
+          \xeCJK_class_num:n { Boundary } ~
+          \xeCJK_class_num:n {#1} = { \exp_not:N \prg_do_nothing: }
       }
   }
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal,var]
+% \begin{variable}
 %  {\g_@@_base_class_seq,\g_@@_non_CJK_class_seq,\g_@@_CJK_class_seq}
 %  保存宏包预先定义的字符类。
 %    \begin{macrocode}
@@ -2933,13 +2984,15 @@
 \cs_new_protected_nopar:Npn \@@_save_CJK_class:n #1
   {
     \seq_gput_right:Nn \g_@@_CJK_class_seq {#1}
-    \tl_const:cn { \@@_CJK_class_tl:c { \@@_class_csname:n {#1} } } {#1}
+    \tl_const:cn
+      { \@@_CJK_class_tl:n { \use:c { \@@_class_csname:n {#1} } } }
+      {#1}
     \@@_update_clear_toks:n {#1}
   }
 \clist_map_function:nN
   { CJK , FullLeft , FullRight , CM , HangulJamo } \@@_save_CJK_class:n
 %    \end{macrocode}
-% \end{macro}
+% \end{variable}
 %
 % \subsection{字符输出规则}
 %
@@ -3044,7 +3097,7 @@
 %
 % \changes{v3.4.2}{2016/10/19}{避免在破折号之间折行。}
 %
-% \begin{macro}[internal]{\xeCJK_class_group_begin:,\xeCJK_class_group_end:}
+% \begin{macro}[int]{\xeCJK_class_group_begin:,\xeCJK_class_group_end:}
 % 在 CJK 类开始时,设置 \tn{XeTeXdashbreakstate} 为零,避免破折号之间的折行。
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \xeCJK_class_group_begin:
@@ -3051,7 +3104,7 @@
   {
     \c_group_begin_token
     \bool_set_true:N \l_@@_CJK_group_bool
-    \int_gset_eq:NN \g_@@_spacefactor_int \c_one_thousand
+    \int_gset_eq:NN \g_@@_space_factor_int \c_one_thousand
     \int_zero:N \xetex_dashbreakstate:D
   }
 \bool_new:N \l_@@_CJK_group_bool
@@ -3113,12 +3166,14 @@
 %    \begin{macrocode}
 \clist_map_inline:nn { Default , HalfLeft }
   {
-    \xeCJK_inter_class_toks:nnn { Boundary } {#1} { \xeCJK_Boundary_and_Default: }
-    \xeCJK_app_inter_class_toks:nnn { CJK } {#1} { \CJKecglue }
+    \xeCJK_inter_class_toks:nnn { Boundary } {#1}
+      { \xeCJK_Boundary_and_Default: }
+    \xeCJK_app_inter_class_toks:nnn { CJK } {#1}
+      { \CJKecglue }
   }
 %    \end{macrocode}
 %
-% \begin{macro}[internal]{\xeCJK_Boundary_and_Default:}
+% \begin{macro}[int]{\xeCJK_Boundary_and_Default:}
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \xeCJK_Boundary_and_Default:
   { \xeCJK_check_for_ecglue: }
@@ -3145,7 +3200,7 @@
 %
 % \changes{v3.4.0}{2016/05/08}{改进 \texttt{xCJKecglue} 的实现。}
 %
-% \begin{macro}[internal]{\@@_replace_space:}
+% \begin{macro}{\@@_replace_space:}
 % 将空格替换为 \tn{CJKecglue}。注意由 \tn{leaders} 等产生的 glue,并不能正确地还回去。
 % 好在 \LaTeXe{} 中常用的 \tn{hrulefill} 和 \tn{dotfill} 定义末尾都有 |\kern\z@| 保护。
 %    \begin{macrocode}
@@ -3158,7 +3213,9 @@
       {
         \xeCJK_if_last_node:nTF { CJK }
           {
-            \skip_if_eq:nnTF { \l_@@_last_skip } { \c_xeCJK_space_skip_tl }
+            \skip_if_eq:nnTF
+              { \l_@@_last_skip }
+              { \c_xeCJK_space_skip_tl }
               { \xeCJK_remove_node: \CJKecglue }
               { \skip_horizontal:N \l_@@_last_skip }
           }
@@ -3175,7 +3232,7 @@
   {
     \xeCJK_inter_class_toks:nnn {#1} { Boundary }
       {
-        \int_gset_eq:NN \g_@@_spacefactor_int \tex_spacefactor:D
+        \int_gset_eq:NN \g_@@_space_factor_int \tex_spacefactor:D
         \peek_meaning_remove:NTF \tex_italiccorrection:D
           {
             \tex_italiccorrection:D
@@ -3200,7 +3257,7 @@
   { \xeCJK_Boundary_and_NormalSp: }
 %    \end{macrocode}
 %
-% \begin{macro}[internal]{\xeCJK_Boundary_and_NormalSp:}
+% \begin{macro}[int]{\xeCJK_Boundary_and_NormalSp:}
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \xeCJK_Boundary_and_NormalSp:
   { \xeCJK_check_for_ecglue_normalsp: }
@@ -3215,7 +3272,8 @@
     \xeCJK_if_last_node:nT { CJK-space }
       { \xeCJK_remove_node: \xeCJK_space_or_xecglue: }
   }
-\cs_new_eq:NN \xeCJK_check_for_ecglue_normalsp: \@@_check_for_ecglue_normalsp:
+\cs_new_eq:NN \xeCJK_check_for_ecglue_normalsp:
+              \@@_check_for_ecglue_normalsp:
 %    \end{macrocode}
 % \end{macro}
 %
@@ -3223,7 +3281,7 @@
 %    \begin{macrocode}
 \xeCJK_inter_class_toks:nnn { NormalSpace } { Boundary }
   {
-    \int_gset_eq:NN \g_@@_spacefactor_int \tex_spacefactor:D
+    \int_gset_eq:NN \g_@@_space_factor_int \tex_spacefactor:D
     \peek_meaning_remove:NTF \tex_italiccorrection:D
       {
         \tex_italiccorrection:D
@@ -3249,26 +3307,48 @@
   }
 %    \end{macrocode}
 %
-% \begin{macro}[internal]{\xeCJK_check_for_glue:}
+% \begin{macro}[int]{\xeCJK_check_for_glue:}
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \xeCJK_check_for_glue:
   {
-    \bool_lazy_or:nnTF
-      { \xeCJK_if_last_node_p:n { CJK } }
-      { \xeCJK_if_last_node_p:n { CJK-space } }
-      { \xeCJK_remove_node: \CJKglue }
+    \int_compare:nNnTF \etex_lastnodetype:D = \c_twelve
+      { \@@_check_for_glue_auxi: }
       {
-        \xeCJK_if_last_node:nTF { CJK-widow }
-          { \xeCJK_remove_node: \xeCJK_widow_penalty: \CJKglue }
-          {
-            \bool_lazy_or:nnTF
-              { \xeCJK_if_last_node_p:n { default } }
-              { \int_compare_p:nNn \etex_lastnodetype:D = \c_ten }
-              { \xeCJK_remove_node: \CJKecglue }
-              { \xeCJK_check_for_xglue: }
-          }
+        \int_compare:nNnTF \etex_lastnodetype:D = \c_ten
+          { \xeCJK_remove_node: \CJKecglue }
+          { \@@_check_for_glue_auxii: }
       }
   }
+\cs_new_protected_nopar:Npn \@@_check_for_glue_auxi:
+  {
+    \dim_case:nn { \tex_lastkern:D }
+      {
+        { \@@_node:n { CJK } }
+        { \xeCJK_remove_node: \CJKglue }
+        { \@@_node:n { CJK-space } }
+        { \xeCJK_remove_node: \CJKglue }
+        { \@@_node:n { CJK-widow } }
+        { \xeCJK_remove_node: \xeCJK_widow_penalty: \CJKglue }
+        { \@@_node:n { default } }
+        { \xeCJK_remove_node: \CJKecglue }
+      }
+  }
+\cs_new_protected_nopar:Npn \@@_check_for_glue_auxii:
+  {
+    \xeCJK_if_last_punct:TF
+      { \@@_check_for_glue_auxiii: }
+      { \xeCJK_check_for_xglue: }
+  }
+\cs_new_protected_nopar:Npn \@@_check_for_glue_auxiii:
+  {
+    \bool_if:NT \l_@@_last_penalty_bool
+      {
+        \@@_add_offset_skip:N \l_@@_last_skip
+        \tex_penalty:D \l_@@_last_penalty_int
+      }
+    \skip_horizontal:N \l_@@_last_skip
+    \tl_if_eq:NNF \l_@@_aligni_tl \c_@@_left_tl { \CJKglue }
+  }
 \cs_new_eq:NN \xeCJK_check_for_xglue: \prg_do_nothing:
 \cs_new_protected_nopar:Npn \@@_check_for_xglue:
   {
@@ -3287,7 +3367,9 @@
   }
 \cs_new_protected_nopar:Npn \@@_check_for_xglue_aux:
   {
-    \skip_if_eq:nnTF { \l_@@_last_skip } { \c_xeCJK_space_skip_tl }
+    \skip_if_eq:nnTF
+      { \l_@@_last_skip }
+      { \c_xeCJK_space_skip_tl }
       {
         \xeCJK_if_last_node:nTF { CJK }
           { \xeCJK_remove_node: \@@_ccglue_or_space: }
@@ -3308,7 +3390,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[pTF,internal]{\xeCJK_if_last_node:n}
+% \begin{macro}[pTF,int]{\xeCJK_if_last_node:n}
 %    \begin{macrocode}
  \prg_new_conditional:Npnn \xeCJK_if_last_node:n #1 { p , T , F , TF }
   {
@@ -3321,8 +3403,7 @@
 % \changes{v3.1.0}{2012/11/14}{删除多余的 \texttt{default-itcorr} 结点。}
 % \changes{v3.2.4}{2013/07/03}{尽量移除用作判断标志的 \tn{kern}。}
 %
-% \begin{macro}[internal]{\xeCJK_declare_node:n,\xeCJK_make_node:n}
-% \changes{v3.2.14}{2014/11/01}{保持 \tn{spacefactor}。}
+% \begin{macro}[int]{\xeCJK_declare_node:n,\xeCJK_make_node:n}
 % 用于判断插入的各种 |kern|。
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \xeCJK_declare_node:n #1
@@ -3335,11 +3416,9 @@
 \int_new:N \g_@@_node_int
 \int_gset_eq:NN \g_@@_node_int \c_ten
 \cs_new_protected_nopar:Npn \xeCJK_make_node:n #1
-  {
-    \exp_after:wN \@@_make_node:N
-    \cs:w c_@@_#1_node_dim \exp_after:wN \cs_end:
-    \exp_after:wN \tex_spacefactor:D \int_use:N \tex_spacefactor:D \exp_stop_f:
-  }
+  { \exp_args:Nc \@@_make_node:N { c_@@_#1_node_dim } }
+\cs_new_nopar:Npn \@@_node:n #1
+  { \use:c { c_@@_#1_node_dim } }
 \cs_new_protected_nopar:Npn \@@_make_node:N #1
   {
     \tex_kern:D - #1
@@ -3465,7 +3544,7 @@
 \xeCJK_inter_class_toks:nnn { CJK } { Boundary } { \xeCJK_CJK_and_Boundary:w }
 %    \end{macrocode}
 %
-% \begin{macro}[internal]{\xeCJK_CJK_and_Boundary:w}
+% \begin{macro}[int]{\xeCJK_CJK_and_Boundary:w}
 % \changes{v3.2.6}{2013/08/05}{更好的处理边界是 \tn{relax} 的情况。}
 % 当边界是 \tn{relax} 的时候,它可能是由 |\csname ...\endcsname| 的形式产生的,
 % 这样就可能出现问题\footnote{参见 \url{http://bbs.ctex.org/forum.php?mod=viewthread&tid=71563}。}。
@@ -3523,7 +3602,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\xeCJK_ignore_spaces:w}
+% \begin{macro}[int]{\xeCJK_ignore_spaces:w}
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \xeCJK_ignore_spaces:w
   {
@@ -3561,12 +3640,14 @@
 %
 % \hypertarget{cjk-cjk}{}
 %    \begin{macrocode}
-\xeCJK_inter_class_toks:nnn { CJK } { CJK } { \xeCJK_CJK_and_CJK:N }
+\xeCJK_inter_class_toks:nnn { CJK } { CJK }
+  { \xeCJK_CJK_and_CJK:N }
 %    \end{macrocode}
 %
-% \begin{macro}[internal]{\xeCJK_CJK_and_CJK:N}
+% \begin{macro}[int]{\xeCJK_CJK_and_CJK:N}
 %    \begin{macrocode}
-\cs_new_protected_nopar:Npn \xeCJK_CJK_and_CJK:N #1 { \CJKglue \CJKsymbol {#1} }
+\cs_new_protected_nopar:Npn \xeCJK_CJK_and_CJK:N
+  { \CJKglue \CJKsymbol }
 %    \end{macrocode}
 % \end{macro}
 %
@@ -3582,7 +3663,8 @@
       {
         \xeCJK_inter_class_toks:nnx {#1} {##1}
           { \exp_not:c { xeCJK_Default_and_##1:nN } {#1} }
-        \xeCJK_inter_class_toks:nnc {##1} {#1} { xeCJK_##1_and_Default: }
+        \xeCJK_inter_class_toks:nnx {##1} {#1}
+          { \exp_not:c { xeCJK_##1_and_Default: } }
       }
   }
 %    \end{macrocode}
@@ -3595,38 +3677,215 @@
   { \xeCJK_Boundary_and_FullRight:N  }
 %    \end{macrocode}
 %
-% \begin{macro}[internal]{\xeCJK_FullRight_and_Boundary:}
 % \hypertarget{fl-fr-bound}{}
 %    \begin{macrocode}
-\xeCJK_app_inter_class_toks:nnn { FullLeft } { Boundary } { \tex_ignorespaces:D }
+\xeCJK_inter_class_toks:nnn { FullLeft } { Boundary }
+  { \xeCJK_FullLeft_and_Boundary: }
 \xeCJK_inter_class_toks:nnn { FullRight } { Boundary }
   { \xeCJK_FullRight_and_Boundary: }
 %    \end{macrocode}
+%
+% \begin{macro}[int]{\xeCJK_FullLeft_and_Boundary:}
+% \hypertarget{fl-fr-bound}{}
+%    \begin{macrocode}
+\cs_new_protected_nopar:Npn \xeCJK_FullLeft_and_Boundary:
+  {
+    \@@_punct_if_middle:NTF \g_@@_last_punct_tl
+      {
+        \@@_punct_bound_rule:NN \c_@@_right_tl \g_@@_last_punct_tl
+        \xeCJK_class_group_end:
+        \exp_after:wN \xeCJK_punct_node:N \g_@@_last_punct_tl
+        \xeCJK_no_break:
+        \@@_punct_glue:NN \c_@@_left_tl  \g_@@_last_punct_tl
+      }
+      {
+        \xeCJK_class_group_end:
+        \exp_after:wN \xeCJK_punct_node:N \g_@@_last_punct_tl
+        \xeCJK_no_break: \@@_zero_glue:
+      }
+    \tex_ignorespaces:D
+  }
+%    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\xeCJK_FullRight_and_Boundary:}
+% \begin{macro}[int]{\xeCJK_FullRight_and_Boundary:}
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \xeCJK_FullRight_and_Boundary:
-  { \xeCJK_FullRight_and_Default: \tex_ignorespaces:D }
+  {
+    \@@_punct_rule:NN \c_@@_right_tl \g_@@_last_punct_tl
+    \xeCJK_class_group_end:
+    \@@_punct_offset:NN \c_@@_right_tl \g_@@_last_punct_tl
+    \exp_after:wN \xeCJK_punct_node:N \g_@@_last_punct_tl
+    \@@_punct_glue:NN \c_@@_right_tl \g_@@_last_punct_tl
+    \tex_ignorespaces:D
+  }
 %    \end{macrocode}
 % \end{macro}
 %
+% \begin{macro}[int]{\xeCJK_punct_node:N}
+% 保存标点的当前边界宽度和字符码,通过插入 \tn{kern} 实现。
+%    \begin{macrocode}
+\cs_new_protected_nopar:Npn \xeCJK_punct_node:N #1
+  {
+    \@@_punct_bound_unitization:NN #1 \l_@@_tmp_dim
+    \@@_make_node:N \l_@@_tmp_dim
+    \dim_set:Nn \l_@@_tmp_dim { `#1 sp }
+    \@@_make_node:N \l_@@_tmp_dim
+  }
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_punct_bound_unitization:NN}
+% 我们不想出现过大的 \tn{kern},因此当边界大于 \SI{1}{pt} 时,以 \cs{c_max_dim} 为标准
+% 对其进行“单位化”。
+%    \begin{macrocode}
+\cs_new_protected_nopar:Npn \@@_punct_bound_unitization:NN #1#2
+  {
+    \dim_set:Nn #2
+      {
+        \dim_max:nn
+          { \c_zero_dim }
+          { \@@_use_punct_dim:nNN { bound } \c_@@_right_tl #1 }
+      }
+    \dim_compare:nNnF {#2} < { 1pt }
+      { \dim_set:Nn #2 { -1pt * \dim_ratio:nn {#2} { \c_max_dim } } }
+  }
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[int]{\xeCJK_punct_bound_kern:N}
+% \begin{macro}{\@@_punct_bound_kern:NN}
+%    \begin{macrocode}
+\cs_new_protected_nopar:Npn \xeCJK_punct_bound_kern:N #1
+  {
+    \exp_after:wN \@@_punct_bound_kern:NN
+      \g_@@_last_punct_tl #1
+  }
+\cs_new_protected_nopar:Npn \@@_punct_bound_kern:NN #1#2
+  {
+    \xeCJK_get_punct_bounds:NN \l_@@_aligni_tl #1
+    \xeCJK_get_punct_kerning:NN #1 #2
+    \@@_punct_bound_unitization:NN #1 \l_@@_tmp_dim
+    \skip_set:Nn \l_@@_punct_kern_skip
+      { \@@_use_dim_or_skip:nNN { bound_kern } #1 #2 }
+    \dim_compare:nNnF \l_@@_tmp_dim = \l_@@_last_bound_dim
+      { \@@_punct_bound_kern_ratio:NN #1 #2 }
+    \@@_add_offset_skip:N \l_@@_punct_kern_skip
+    \bool_if:NTF \l_@@_last_penalty_bool
+      {
+        \tex_penalty:D \l_@@_last_penalty_int
+        \skip_horizontal:N
+      }
+      { \@@_punct_bound_kern_aux:NNN #1 #2 }
+      \l_@@_punct_kern_skip
+  }
+\cs_new_protected_nopar:Npn \@@_add_offset_skip:N #1
+  {
+    \tl_if_eq:NNF \l_@@_aligni_tl \c_@@_left_tl
+      {
+        \int_compare:nNnT \etex_lastnodetype:D = \c_eleven
+          {
+            \skip_add:Nn #1 { \tex_lastskip:D }
+            \tex_unskip:D
+          }
+      }
+  }
+\skip_new:N \l_@@_punct_kern_skip
+%    \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_punct_bound_kern_ratio:NN}
+% 当标点前后的字体情况不一致时,按一定的比例进行压缩。
+%    \begin{macrocode}
+\cs_new_protected_nopar:Npn \@@_punct_bound_kern_ratio:NN #1#2
+  {
+    \dim_set:Nn \l_@@_bound_dim
+      { \@@_use_punct_dim:nNN { bound_width } #1 #2 }
+    \dim_compare:nNnT \l_@@_bound_dim > \c_zero_dim
+      {
+        \dim_compare:nNnF \l_@@_last_bound_dim > \c_zero_dim
+          {
+            \dim_set:Nn \l_@@_last_bound_dim
+              {
+                - \l_@@_last_bound_dim *
+                  \dim_ratio:nn { \c_max_dim } { 1pt }
+              }
+          }
+        \@@_punct_bound_kern_ratio_aux:N #2
+      }
+  }
+\cs_new_protected_nopar:Npn \@@_punct_bound_kern_ratio_aux:N #1
+  {
+    \skip_set:Nn \l_@@_punct_kern_skip
+      {
+        \l_@@_punct_kern_skip *
+        \dim_ratio:nn
+          {
+              \l_@@_last_bound_dim
+            + \@@_use_punct_dim:nNN { bound } \c_@@_left_tl #1
+          }
+          { \l_@@_bound_dim }
+      }
+  }
+%    \end{macrocode}
+% \end{macro}
+%
+%
+% \begin{macro}{\@@_nobreak_hskip:N,\@@_nobreak_hskip:n,
+% \@@_punct_bound_kern:N, \@@_punct_bound_breakable_kern:N}
+%    \begin{macrocode}
+\cs_new_protected_nopar:Npn \@@_nobreak_hskip:N
+  { \xeCJK_no_break: \skip_horizontal:N }
+\cs_new_protected_nopar:Npn \@@_nobreak_hskip:n
+  { \xeCJK_no_break: \skip_horizontal:n }
+\cs_new_eq:NN \@@_punct_bound_kern:N \@@_nobreak_hskip:N
+\cs_new_protected_nopar:Npn \@@_punct_bound_breakable_kern:N
+  {
+    \tl_if_eq:NNTF \l_@@_aligni_tl \c_@@_right_tl
+      {
+        \tl_if_eq:NNTF \l_@@_alignii_tl \c_@@_left_tl
+          { \skip_horizontal:N }
+          { \@@_nobreak_hskip:N }
+      }
+      { \@@_nobreak_hskip:N }
+  }
+\cs_new_protected_nopar:Npn \@@_punct_bound_kern_aux:NNN #1#2
+  {
+    \str_if_eq:nnTF {#1} {#2}
+      { \@@_punct_bound_kern:N }
+      {
+        \@@_punct_if_long:NTF #1
+          { \skip_horizontal:N }
+          {
+            \@@_punct_if_long:NTF #2
+              { \skip_horizontal:N }
+              { \@@_punct_bound_kern:N }
+          }
+      }
+  }
+%    \end{macrocode}
+% \end{macro}
+%
 % \hypertarget{cjk-fl-fr}{}
 %    \begin{macrocode}
 \clist_map_inline:nn { CJK , FullLeft , FullRight }
   {
     \clist_map_inline:nn { FullLeft , FullRight }
-      { \xeCJK_inter_class_toks:nnc {#1} {##1} { xeCJK_#1_and_##1:N } }
+      {
+        \xeCJK_inter_class_toks:nnx {#1} {##1}
+          { \exp_not:c { xeCJK_#1_and_##1:N } }
+      }
   }
 %    \end{macrocode}
 %
-% \begin{macro}[internal]{\@@_punct_bound_rule:NN}
+% \begin{macro}{\@@_punct_bound_rule:NN}
 % 用于抹去标点符号的全部左/右空白。
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \@@_punct_bound_rule:NN #1#2
   {
     \tex_vrule:D
-      width - \@@_use_punct_dim:nnn { bound } {#1} {#2}
+      width - \@@_use_punct_dim:nNN { bound } #1 #2 ~
       depth  \c_zero_dim
       height \c_zero_dim \scan_stop:
   }
@@ -3633,13 +3892,13 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_punct_rule:NN}
+% \begin{macro}{\@@_punct_rule:NN}
 % 用于减少标点符号的左/右空白。
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \@@_punct_rule:NN #1#2
   {
     \tex_vrule:D
-      width  \@@_use_punct_dim:nnn { rule } {#1} {#2}
+      width  \@@_use_punct_dim:nNN { rule } #1 #2 ~
       depth  \c_zero_dim
       height \c_zero_dim \scan_stop:
   }
@@ -3646,53 +3905,88 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_punct_offset:NN}
+% \begin{macro}{\@@_punct_offset:NN}
 % 经过以上 \tn{vrule} 处理后,标点输出边界与实际边界的距离。
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \@@_punct_offset:NN #1#2
-  { \@@_punct_kern:n { - \@@_use_punct_dim:nnn { offset } {#1} {#2} } }
-\cs_new_protected_nopar:Npn \@@_punct_kern:n #1
   {
-    \dim_set:Nn \l_@@_tmp_dim {#1}
+    \dim_set:Nn \l_@@_tmp_dim
+      { - \@@_use_punct_dim:nNN { offset } #1 #2 }
     \dim_compare:nNnF \l_@@_tmp_dim = \c_zero_dim
-      { \tex_kern:D \l_@@_tmp_dim }
+      { \@@_punct_hskip:n { \l_@@_tmp_dim } }
   }
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_punct_glue:NN}
+% \begin{macro}{\@@_punct_glue:NN}
 % \changes{v3.2.7}{2013/08/23}
 % {标点符号左/右空白的伸展值不超过原始边界,收缩值不小于另一侧边界。}
 % 根据所选的标点处理方式在标点符号左/右增加的空白。
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \@@_punct_glue:NN #1#2
-  {
-    \@@_punct_hskip:n
-      { \@@_use_dim_or_skip:nnn { glue } {#1} {#2} }
-  }
+  { \@@_punct_hskip:n { \@@_use_dim_or_skip:nNN { glue } #1 #2 } }
 \cs_new_eq:NN \@@_punct_hskip:n \skip_horizontal:n
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_punct_kern:NN}
-% 相邻两个标点之间的间距。
+% \changes{v3.6.0}{2018/01/23}{总允许长标点与其他标点之间折行。}
+% \begin{macro}{\xeCJK_punct_kern:NN,\@@_punct_kern:NN}
+% 相邻两个标点之间的间距,总允许长标点与其他标点之间折行。
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \@@_punct_kern:NN #1#2
   {
-    \@@_nobreak_hskip:n
-      { \@@_use_dim_or_skip:nnn { kern } {#1} {#2} }
+    \str_if_eq_x:nnTF {#1} {#2}
+      { \@@_punct_nobreak_kern:NN }
+      {
+        \@@_punct_if_long:NTF #1
+          { \@@_punct_breakable_kern:NN }
+          {
+            \@@_punct_if_long:NTF #2
+              { \@@_punct_breakable_kern:NN }
+              { \@@_punct_nobreak_kern:NN }
+          }
+      }
+    #1 #2
   }
+\cs_new_eq:NN \xeCJK_punct_kern:NN \@@_punct_kern:NN
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal,var]{\g_@@_last_punct_tl}
+% \begin{macro}{\@@_punct_nobreak_kern:NN}
+%    \begin{macrocode}
+\cs_new_protected_nopar:Npn \@@_punct_nobreak_kern:NN #1#2
+  { \@@_nobreak_hskip:n { \@@_use_dim_or_skip:nNN { kern } #1 #2 } }
+%    \end{macrocode}
+% \end{macro}
+%
+% \changes{v3.2.4}{2013/07/06}
+% {使用 \texttt{AllowBreakBetweenPuncts} 时,相应标点符号仍能与边界对齐。}
+% \changes{v3.2.7}{2013/08/23}
+% {处理 \texttt{AllowBreakBetweenPuncts} 与 \pkg{xeCJKfntef} 的兼容问题。}
+%
+% \begin{macro}{\@@_punct_breakable_kern:NN}
+%    \begin{macrocode}
+\cs_new_protected_nopar:Npn \@@_punct_breakable_kern:NN #1#2
+  {
+    \exp_after:wN \@@_punct_if_right:NT #1
+      { \@@_punct_rule:NN \c_@@_right_tl #1 }
+    \@@_punct_breakable_kern:n
+      { \@@_use_dim_or_skip:nNN { break_kern } #1 #2 }
+    \@@_punct_if_right:NF #2
+      { \@@_punct_rule:NN \c_@@_left_tl #2 }
+  }
+\cs_new_eq:NN \@@_punct_breakable_kern:n \skip_horizontal:n
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{variable}{\g_@@_last_punct_tl}
 % 用于记录当前的标点符号。
 %    \begin{macrocode}
 \tl_new:N \g_@@_last_punct_tl
 %    \end{macrocode}
-% \end{macro}
+% \end{variable}
 %
-% \begin{macro}[internal]{\xeCJK_FullLeft_and_CJK:}
+% \begin{macro}[int]{\xeCJK_FullLeft_and_CJK:}
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \xeCJK_FullLeft_and_CJK:
   {
@@ -3700,14 +3994,15 @@
       {
         \@@_punct_bound_rule:NN \c_@@_right_tl \g_@@_last_punct_tl
         \xeCJK_no_break:
-        \@@_punct_glue:NN \c_@@_left_tl  \g_@@_last_punct_tl
+        \@@_punct_glue:NN \c_@@_left_tl \g_@@_last_punct_tl
       }
-      { \xeCJK_no_break: }
+      { }
+    \@@_select_font:
   }
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\xeCJK_FullLeft_and_Default:}
+% \begin{macro}[int]{\xeCJK_FullLeft_and_Default:}
 % \changes{v3.2.0}{2013/05/20}{修正 \pkg{xeCJK} 使西文在部分情况下无法断词的问题。}
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \xeCJK_FullLeft_and_Default:
@@ -3718,7 +4013,7 @@
         \xeCJK_class_group_end: \xeCJK_no_break:
         \@@_punct_glue:NN \c_@@_left_tl  \g_@@_last_punct_tl
       }
-      { \xeCJK_class_group_end: \xeCJK_no_break: \@@_zero_glue: }
+      { \xeCJK_class_group_end: }
   }
 \cs_new_protected_nopar:Npn \@@_zero_glue:
   { \skip_horizontal:N \c_zero_skip }
@@ -3725,7 +4020,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\xeCJK_FullRight_and_CJK:}
+% \begin{macro}[int]{\xeCJK_FullRight_and_CJK:}
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \xeCJK_FullRight_and_CJK:
   {
@@ -3732,12 +4027,13 @@
     \@@_punct_rule:NN \c_@@_right_tl \g_@@_last_punct_tl
     \@@_punct_offset:NN \c_@@_right_tl \g_@@_last_punct_tl
     \@@_punct_glue:NN \c_@@_right_tl \g_@@_last_punct_tl
+    \@@_select_font:
     \CJKglue
   }
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\xeCJK_FullRight_and_Default:}
+% \begin{macro}[int]{\xeCJK_FullRight_and_Default:}
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \xeCJK_FullRight_and_Default:
   {
@@ -3749,280 +4045,462 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\xeCJK_Default_and_FullLeft:nN}
+% \begin{macro}[int]{\xeCJK_Default_and_FullLeft:nN}
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \xeCJK_Default_and_FullLeft:nN #1#2
   {
-    \xeCJK_get_punct_bounds:NN \c_@@_left_tl {#2}
-    \@@_Default_and_FullLeft_glue:N {#2}
+    \xeCJK_get_punct_bounds:NN \c_@@_left_tl #2
+    \@@_Default_and_FullLeft_glue:N #2
     \xeCJK_class_group_begin:
-    \xeCJK_select_font:
+    \xeCJK_select_punct_font:
     \xeCJK_clear_inter_class_toks:nn {#1} { FullLeft }
     \xeCJK_clear_Boundary_and_CJK_toks:
-    \tl_gset:Nx \g_@@_last_punct_tl {#2}
-    \@@_punct_rule:NN \c_@@_left_tl {#2}
-    \CJKpunctsymbol {#2}
+    \tl_gset:Nn \g_@@_last_punct_tl {#2}
+    \@@_punct_rule:NN \c_@@_left_tl #2
+    \CJKpunctsymbol #2
   }
 \cs_new_protected_nopar:Npn \@@_Default_and_FullLeft_glue:N #1
   {
-    \@@_punct_glue:NN \c_@@_left_tl {#1}
-    \@@_punct_offset:NN \c_@@_left_tl {#1}
+    \@@_punct_glue:NN \c_@@_left_tl #1
+    \@@_punct_offset:NN \c_@@_left_tl #1
   }
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\xeCJK_CJK_and_FullLeft:N}
+% \begin{macro}[int]{\xeCJK_CJK_and_FullLeft:N}
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \xeCJK_CJK_and_FullLeft:N #1
   {
-    \xeCJK_get_punct_bounds:NN \c_@@_left_tl {#1}
-    \@@_CJK_and_FullLeft_glue:N {#1}
-    \tl_gset:Nx \g_@@_last_punct_tl {#1}
-    \@@_punct_rule:NN \c_@@_left_tl {#1}
-    \CJKpunctsymbol {#1}
+    \xeCJK_get_punct_bounds:NN \c_@@_left_tl #1
+    \@@_CJK_and_FullLeft_glue:N #1
+    \tl_gset:Nn \g_@@_last_punct_tl {#1}
+    \@@_punct_rule:NN \c_@@_left_tl #1
+    \@@_select_punct_font:
+    \CJKpunctsymbol #1
   }
 \cs_new_protected_nopar:Npn \@@_CJK_and_FullLeft_glue:N #1
   {
     \CJKglue
-    \@@_punct_glue:NN \c_@@_left_tl {#1}
-    \@@_punct_offset:NN \c_@@_left_tl {#1}
+    \@@_punct_glue:NN \c_@@_left_tl #1
+    \@@_punct_offset:NN \c_@@_left_tl #1
   }
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\xeCJK_Boundary_and_FullLeft:N}
+% \begin{macro}[int]{\xeCJK_Boundary_and_FullLeft:N}
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \xeCJK_Boundary_and_FullLeft:N #1
   {
-    \xeCJK_get_punct_bounds:NN \c_@@_left_tl {#1}
-    \@@_Boundary_and_FullLeft_glue:N {#1}
-    \@@_punct_offset:NN \c_@@_left_tl {#1}
+    \xeCJK_get_punct_bounds:NN \c_@@_left_tl #1
+    \@@_Boundary_and_FullLeft_glue:N #1
+    \@@_punct_offset:NN \c_@@_left_tl #1
     \xeCJK_class_group_begin:
-    \xeCJK_select_font:
+    \xeCJK_select_punct_font:
     \xeCJK_clear_Boundary_and_CJK_toks:
-    \tl_gset:Nx \g_@@_last_punct_tl {#1}
-    \@@_punct_rule:NN \c_@@_left_tl {#1}
-    \CJKpunctsymbol {#1}
+    \tl_gset:Nn \g_@@_last_punct_tl {#1}
+    \@@_punct_rule:NN \c_@@_left_tl #1
+    \CJKpunctsymbol #1
   }
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_Boundary_and_FullLeft_glue:N}
+% \begin{macro}{\@@_Boundary_and_FullLeft_glue:N}
 % \changes{v3.2.0}{2013/05/22}{当全角左标点前面是 \texttt{hlist}、\texttt{none}、
 % \texttt{glue} 和 \texttt{penalty} 等节点时,压缩其左空白。}
 % \changes{v3.2.4}{2013/07/03}{细化边界与全角左标点之间是否压缩空白的判断。}
 % \changes{v3.2.5}{2013/07/13}{细化全角左标点是否位于段首的判断。}
 % \changes{v3.2.5}{2013/07/13}{增加对 \pkg{enumitem} 宏包修改的 \tn{item} 的判断。}
-% \cs{etex_lastnodetype:D} 为 $1$ 表示 hlist node,在这里用来判断是否位于段首。基于
-% 正常情况下,\TeX 会在段落开头插入宽度为 \tn{parindent} 的水平盒子用于缩进。
-% $-1$ 表示 empty list,常出现在盒子的起始位置,在段落前使用 \tn{noindent} 就
-% 是这种情况。$11$ 表示 glue node,这里判断的目的是当全角左标点出现在 \LaTeX 表格
-% 的非 \texttt{p} 列行首时,能够对齐到单元格的边界。判断基于标准 \LaTeX 表格的
-% 列格式(\tn{@tabclassz})定义中,在 \texttt{l} 列和 \texttt{r} 列前为了防止
-% \tn{tabcolsep} 被无意 \tn{unskip} 掉,都加了 |\hskip1sp|,而 \texttt{c} 列前
-% 则有 \tn{hfil}。$13$ 表示 penalty node,这里判断的目的是全角左标点出现在 \LaTeX
-% 列表环境的 \tn{item} 后面时,能对齐到边界。判断基于 \tn{item} 的内部定义
-% \tn{@item} 对 \tn{everypar} 进行了修改,在这里起到影响作用的是
-% |\box\@labels \penalty\z@|。\package{enumitem} 宏包修改了 \env{description}
-% 环境中使用的 \tn{item}(\tn{enit at postlabel@i}),在这里起到影响作用的是
-% |\penalty\z@ \hskip\labelsep|。以上判断都比较粗略,暂时也没有想起更好的办法。
+% 根据 \cs{etex_lastnodetype:D} 的值进行分别处理。
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \@@_Boundary_and_FullLeft_glue:N #1
   {
-    \int_case:nnTF { \etex_lastnodetype:D }
+    \tl_set_eq:NN \l_@@_alignii_tl \c_@@_left_tl
+    \group_begin: \exp_args:NNc \group_end: \cs_if_exist_use:NTF
+      { @@_bound_type_ \int_use:N \etex_lastnodetype:D _glue:Nn }
+      {#1}
+      { \use:n }
+      { \@@_punct_glue:NN \c_@@_left_tl #1 }
+  }
+\tl_new:N \c_@@_alignii_tl
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_bound_type_-1_glue:Nn}
+% \cs{etex_lastnodetype:D} 为 $-1$ 表示 empty list,常出现在盒子的起始位置,
+% 在段落前使用 \tn{noindent} 就是这种情况。
+%    \begin{macrocode}
+\cs_new_protected_nopar:cpn { @@_bound_type_ -1 _glue:Nn } #1#2
+  { \@@_zero_glue: }
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_bound_type_1_glue:Nn}
+% $1$ 表示 hlist node,在这里用来判断是否位于段首。基于正常情况下,\TeX 会在段落开头插入宽度为
+% \tn{parindent} 的水平盒子用于缩进。
+%    \begin{macrocode}
+\cs_new_protected_nopar:cpn { @@_bound_type_  1 _glue:Nn } #1#2
+  {
+    \box_set_to_last:N \l_@@_tmp_box
+    \int_compare:nNnTF \etex_lastnodetype:D = { -1 }
       {
-        { \c_one       }
-        {
-          \box_set_to_last:N \l_@@_tmp_box
-          \bool_lazy_and:nnTF
-            { \int_compare_p:nNn \etex_lastnodetype:D = { -1 } }
-            { \dim_compare_p:nNn { \box_wd:N \l_@@_tmp_box } = \tex_parindent:D }
-            { \box_use_clear:N \l_@@_tmp_box \use_none:n }
-            { \box_use_clear:N \l_@@_tmp_box \use:n }
-        }
-        { -1 } { \@@_zero_glue: \use_none:n }
-        { \c_eleven    }
-        {
-          \bool_lazy_or:nnTF
-            { ! ( \skip_if_finite_p:n { \tex_lastskip:D } ) }
-            { \skip_if_eq_p:nn { \tex_lastskip:D } { 1 sp } }
-            { \@@_zero_glue: \use_none:n }
-            {
-              \skip_if_eq:nnTF { \tex_lastskip:D } { \labelsep }
-                {
-                  \tex_unskip:D
-                  \bool_lazy_and:nnTF
-                    { \int_compare_p:nNn \etex_lastnodetype:D = \c_thirteen }
-                    { \int_compare_p:nNn \tex_lastpenalty:D = \c_zero }
-                    { \skip_horizontal:n { \labelsep } \use_none:n }
-                    { \skip_horizontal:n { \labelsep } \use:n }
-                }
-                { \use:n }
-            }
-        }
-        { \c_thirteen  }
-        {
-          \int_compare:nNnTF \tex_lastpenalty:D = \c_zero
-            {
-              \tex_unpenalty:D
-              \int_compare:nNnTF \etex_lastnodetype:D = \c_one
-                { \tex_penalty:D \c_zero \use_none:n }
-                { \tex_penalty:D \c_zero \use:n }
-            }
-            { \use:n }
-        }
+        \dim_compare:nNnTF
+          { \box_wd:N \l_@@_tmp_box } = \tex_parindent:D
+          { \box_use_drop:N \l_@@_tmp_box }
+          { \box_use_drop:N \l_@@_tmp_box #2 }
       }
-      { { \@@_punct_glue:NN \c_@@_left_tl {#1} } }
-      { \@@_punct_glue:NN \c_@@_left_tl {#1} }
+      { \box_use_drop:N \l_@@_tmp_box #2 }
   }
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\xeCJK_Default_and_FullRight:nN}
+% \begin{macro}{\@@_bound_type_11_glue:Nn}
+% $11$ 表示 glue node,这里判断的目的是当全角左标点出现在 \LaTeX 表格的非 \texttt{p} 列行首时,
+% 能够对齐到单元格的边界。判断基于标准 \LaTeX 表格的列格式(\tn{@tabclassz})定义中,
+% 在 \texttt{l} 列和 \texttt{r} 列前为了防止 \tn{tabcolsep} 被无意 \tn{unskip} 掉,
+% 都加了 |\hskip1sp|,而 \texttt{c} 列前则有 \tn{hfil}。\package{enumitem} 宏包修改了
+% \env{description} 环境中使用的 \tn{item}(\tn{enit at postlabel@i}),
+% 在这里起到影响作用的是 |\penalty\z@ \hskip\labelsep|。
 %    \begin{macrocode}
+\cs_new_protected_nopar:cpn { @@_bound_type_ 11 _glue:Nn } #1#2
+  {
+    \skip_if_finite:nTF { \tex_lastskip:D }
+      { \@@_bound_glue_auxi:Nn #1 {#2} }
+      { \@@_zero_glue: }
+  }
+\cs_new_protected_nopar:Npn \@@_bound_glue_auxi:Nn #1#2
+  {
+    \@@_if_last_punct_glue:TF
+      { \xeCJK_punct_bound_kern:N #1 }
+      { \@@_bound_glue_auxii:n {#2} }
+  }
+\cs_new_protected_nopar:Npn \@@_bound_glue_auxii:n #1
+  {
+    \skip_if_eq:nnTF { \l_@@_last_skip } { 1sp }
+      { \@@_zero_glue: }
+      {
+        \skip_if_eq:nnTF { \l_@@_last_skip } { \labelsep }
+          {
+            \tex_unskip:D
+            \int_compare:nNnTF \etex_lastnodetype:D = \c_thirteen
+              {
+                \int_compare:nNnTF \tex_lastpenalty:D = \c_zero
+                  { \skip_horizontal:N \l_@@_last_skip }
+                  { \skip_horizontal:N \l_@@_last_skip #1 }
+              }
+              { \skip_horizontal:N \l_@@_last_skip #1 }
+          }
+          {#1}
+      }
+  }
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_bound_type_12_glue:Nn}
+% $12$ 表示 kern node,用于判断之前的字符是否是 CJK 类,如果是,则插入 \tn{CJKglue}。
+%    \begin{macrocode}
+\cs_new_protected_nopar:cpn { @@_bound_type_ 12 _glue:Nn } #1#2
+  {
+    \xeCJK_if_last_node:nF { CJK }
+      { \xeCJK_if_last_node:nF { CJK-space } { \use_none:nn } }
+    \xeCJK_remove_node: \CJKglue
+    #2
+  }
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_bound_type_13_glue:n}
+% $13$ 表示 penalty node,这里判断的目的是全角左标点出现在 \LaTeX 列表环境的 \tn{item} 后面时,
+% 能对齐到边界。判断基于 \tn{item} 的内部定义 \tn{@item} 对 \tn{everypar} 进行了修改,在这里起
+% 到影响作用的是 |\box\@labels \penalty\z@|。以上判断都比较粗略,暂时也没有想起更好的办法。
+%    \begin{macrocode}
+\cs_new_protected_nopar:cpn { @@_bound_type_ 13 _glue:Nn } #1#2
+  {
+    \@@_if_last_punct_penalty:TF
+      { \xeCJK_punct_bound_kern:N #1 }
+      {
+        \int_compare:nNnTF \tex_lastpenalty:D = \c_zero
+          {
+            \tex_unpenalty:D
+            \int_compare:nNnTF \etex_lastnodetype:D = \c_one
+              { \tex_penalty:D \c_zero }
+              { \tex_penalty:D \c_zero #2 }
+          }
+          {#2}
+      }
+  }
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[int]{\xeCJK_Default_and_FullRight:nN}
+%    \begin{macrocode}
 \cs_new_protected_nopar:Npn \xeCJK_Default_and_FullRight:nN #1#2
   {
-    \xeCJK_get_punct_bounds:NN \c_@@_right_tl {#2}
-    \@@_Default_and_FullRight_glue:N {#2}
+    \xeCJK_get_punct_bounds:NN \c_@@_right_tl #2
+    \@@_Default_and_FullRight_glue:N #2
     \xeCJK_class_group_begin:
-    \xeCJK_select_font:
+    \xeCJK_select_punct_font:
     \xeCJK_clear_inter_class_toks:nn {#1} { FullRight }
     \xeCJK_clear_Boundary_and_CJK_toks:
-    \tl_gset:Nx \g_@@_last_punct_tl {#2}
-    \xeCJK_FullRight_symbol:N {#2}
+    \tl_gset:Nn \g_@@_last_punct_tl {#2}
+    \xeCJK_FullRight_symbol:N #2
   }
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\xeCJK_Boundary_and_FullRight:N}
+% \begin{macro}[int]{\xeCJK_Boundary_and_FullRight:N}
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \xeCJK_Boundary_and_FullRight:N #1
   {
-    \xeCJK_get_punct_bounds:NN \c_@@_right_tl {#1}
-    \@@_Default_and_FullRight_glue:N {#1}
+    \xeCJK_get_punct_bounds:NN \c_@@_right_tl #1
+    \xeCJK_if_last_punct:TF
+      {
+        \tl_set_eq:NN \l_@@_alignii_tl \c_@@_right_tl
+        \xeCJK_punct_bound_kern:N
+      }
+      { \@@_Default_and_FullRight_glue:N }
+      #1
     \xeCJK_class_group_begin:
-    \xeCJK_select_font:
+    \xeCJK_select_punct_font:
     \xeCJK_clear_Boundary_and_CJK_toks:
-    \tl_gset:Nx \g_@@_last_punct_tl {#1}
-    \xeCJK_FullRight_symbol:N {#1}
+    \tl_gset:Nn \g_@@_last_punct_tl {#1}
+    \xeCJK_FullRight_symbol:N #1
   }
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\xeCJK_CJK_and_FullRight:N}
+% \begin{macro}[int]{\xeCJK_CJK_and_FullRight:N}
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \xeCJK_CJK_and_FullRight:N #1
   {
-    \xeCJK_get_punct_bounds:NN \c_@@_right_tl {#1}
-    \@@_CJK_and_FullRight_glue:N {#1}
-    \tl_gset:Nx \g_@@_last_punct_tl {#1}
-    \xeCJK_FullRight_symbol:N {#1}
+    \xeCJK_get_punct_bounds:NN \c_@@_right_tl #1
+    \@@_CJK_and_FullRight_glue:N #1
+    \tl_gset:Nn \g_@@_last_punct_tl {#1}
+    \@@_select_punct_font:
+    \xeCJK_FullRight_symbol:N #1
   }
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]
-% {\@@_CJK_and_FullRight_glue:N,\@@_Default_and_FullRight_glue:N}
+% \changes{v3.6.0}{2018/01/23}{解决标点中间被隔开的禁则与压缩问题。}
+%
+% \begin{macro}{\xeCJK_if_last_punct:TF}
+% 判断之前是否是一个标点符号。
 %    \begin{macrocode}
-\cs_new_protected_nopar:Npn \@@_CJK_and_FullRight_glue:N #1
+\cs_new_protected_nopar:Npn \xeCJK_if_last_punct:TF
   {
-    \@@_punct_if_long:NTF {#1}
-      { \CJKglue }
+    \bool_set_false:N \l_@@_last_penalty_bool
+    \int_compare:nNnTF \etex_lastnodetype:D = \c_eleven
+      { \@@_if_last_punct_glue:TF }
       {
-        \@@_punct_if_middle:NTF {#1}
-          {
-            \xeCJK_no_break:
-            \@@_punct_glue:NN \c_@@_right_tl {#1}
-            \@@_punct_bound_rule:NN \c_@@_left_tl {#1}
-          }
-          { \xeCJK_no_break: }
+        \int_compare:nNnTF \etex_lastnodetype:D = \c_thirteen
+         { \@@_if_last_punct_penalty:TF }
+         { \use_ii:nn }
       }
   }
-\cs_new_eq:NN \@@_Default_and_FullRight_glue:N \@@_CJK_and_FullRight_glue:N
+\cs_new_protected:Npn \@@_if_last_punct_glue:TF #1#2
+  {
+    \skip_set_eq:NN \l_@@_last_skip \tex_lastskip:D
+    \tex_unskip:D
+    \int_compare:nNnTF \tex_lastpenalty:D = \c_ten_thousand
+      { \@@_if_last_punct_auxi:TF }
+      {
+        \xeCJK_if_last_node:TF
+          { \@@_if_last_punct_auxii:TF }
+          { \@@_if_last_punct_auxiii:TF }
+      }
+      {#1}
+      { \skip_horizontal:N \l_@@_last_skip #2 }
+  }
+\cs_new_protected:Npn \@@_if_last_punct_penalty:TF #1#2
+  {
+    \int_set_eq:NN \l_@@_last_penalty_int \tex_lastpenalty:D
+    \tex_unpenalty:D
+    \bool_set_true:N \l_@@_last_penalty_bool
+    \int_compare:nNnTF \etex_lastnodetype:D = \c_eleven
+      {
+        \@@_if_last_punct_glue:TF
+          {#1}
+          { \tex_penalty:D \l_@@_last_penalty_int #2 }
+      }
+      { \tex_penalty:D \l_@@_last_penalty_int #2 }
+  }
+\cs_new_protected:Npn \@@_if_last_punct_auxi:TF #1#2
+  {
+    \tex_unpenalty:D
+    \bool_if:NF \l_@@_last_penalty_bool
+      {
+        \bool_set_true:N \l_@@_last_penalty_bool
+        \int_set_eq:NN \l_@@_last_penalty_int \c_ten_thousand
+      }
+    \@@_if_last_punct_auxiv:TF
+      {#1}
+      { \xeCJK_no_break: #2 }
+  }
+\cs_new_protected:Npn \@@_if_last_punct_auxii:TF #1#2
+  {
+    \int_case:nnTF { \xetex_charclass:D \l_@@_last_kern_dim }
+      {
+        { \xeCJK_class_num:n { FullRight } }
+        { \tl_set_eq:NN \l_@@_aligni_tl \c_@@_right_tl }
+        { \xeCJK_class_num:n { FullLeft } }
+        { \tl_set_eq:NN \l_@@_aligni_tl \c_@@_left_tl }
+      }
+      { \@@_if_last_punct_auxv:TF {#1} {#2} }
+      { \@@_make_node:N \l_@@_last_kern_dim #2 }
+  }
+\cs_new_protected_nopar:Npn \@@_if_last_punct_auxiii:TF
+  {
+    \int_compare:nNnTF \etex_lastnodetype:D = \c_eleven
+      { \@@_if_last_punct_auxvi:TF }
+      { \use_ii:nn }
+  }
+\cs_new_protected_nopar:Npn \@@_if_last_punct_auxiv:TF
+  {
+    \xeCJK_if_last_node:TF
+      { \@@_if_last_punct_auxii:TF }
+      { \use_ii:nn }
+  }
+\cs_new_protected:Npn \@@_if_last_punct_auxv:TF #1#2
+  {
+    \dim_set_eq:NN \l_@@_tmp_dim \l_@@_last_kern_dim
+    \xeCJK_if_last_node:TF
+      {
+        \tl_gset:Nx \g_@@_last_punct_tl
+          { \utex_char:D \l_@@_tmp_dim }
+        \dim_set_eq:NN \l_@@_last_bound_dim \l_@@_last_kern_dim
+        #1
+      }
+      { \@@_make_node:N \l_@@_tmp_dim #2 }
+  }
+\cs_new_protected:Npn \@@_if_last_punct_auxvi:TF #1#2
+  {
+    \int_gset_eq:NN \g_@@_space_factor_int \tex_spacefactor:D
+    \skip_if_eq:nnTF
+      { \l_@@_last_skip }
+      { \c_xeCJK_space_skip_tl }
+      {
+        \skip_set_eq:NN \l_@@_tmp_skip \tex_lastskip:D
+        \tex_unskip:D
+        \int_compare:nNnTF \tex_lastpenalty:D = \c_ten_thousand
+          { \@@_if_last_punct_auxi:TF }
+          { \@@_if_last_punct_auxiv:TF }
+          { \skip_set_eq:NN \l_@@_last_skip \l_@@_tmp_skip #1 }
+          { \skip_horizontal:N \l_@@_tmp_skip #2 }
+      }
+      {#2}
+  }
+\tl_new:N \l_@@_aligni_tl
+\tl_new:N \l_@@_alignii_tl
+\int_new:N \l_@@_last_penalty_int
+\dim_new:N \l_@@_last_bound_dim
+\bool_new:N \l_@@_last_penalty_bool
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\xeCJK_FullLeft_and_FullLeft:N}
+% \begin{macro}{\xeCJK_if_last_node:TF}
 %    \begin{macrocode}
-\cs_new_protected_nopar:Npn \xeCJK_FullLeft_and_FullLeft:N #1
+\cs_new_protected:Npn \xeCJK_if_last_node:TF #1#2
   {
-    \xeCJK_no_break:
-    \xeCJK_get_punct_bounds:NN \c_@@_left_tl {#1}
-    \xeCJK_get_punct_kerning:oN \g_@@_last_punct_tl {#1}
-    \@@_punct_kern:NN \g_@@_last_punct_tl {#1}
-    \tl_gset:Nx \g_@@_last_punct_tl {#1}
-    \CJKpunctsymbol {#1}
+    \int_compare:nNnTF \etex_lastnodetype:D = \c_twelve
+      {
+        \dim_set_eq:NN \l_@@_last_kern_dim \tex_lastkern:D
+        \tex_unkern:D
+        \int_compare:nNnTF \etex_lastnodetype:D = \c_twelve
+          {
+            \dim_compare:nNnTF \tex_lastkern:D = { - \l_@@_last_kern_dim }
+              { \tex_unkern:D #1 }
+              { \tex_kern:D \l_@@_last_kern_dim #2 }
+          }
+          { \tex_kern:D \l_@@_last_kern_dim #2 }
+      }
+      {#2}
   }
+\dim_new:N \l_@@_last_kern_dim
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\xeCJK_FullLeft_and_FullRight:N}
+% \changes{v3.6.0}{2018/01/14}
+% {修正标点同为 \texttt{LongPunct} 与 \texttt{MiddlePunct} 时的实现错误。}
+% \changes{v3.6.0}{2018/01/14}
+% {\texttt{Default} 类与 \texttt{MiddlePunct} 之间不应该有 \tn{CJKglue}。}
+%
+% \begin{macro}
+% {\@@_CJK_and_FullRight_glue:N,\@@_Default_and_FullRight_glue:N}
 %    \begin{macrocode}
-\cs_new_protected_nopar:Npn \xeCJK_FullLeft_and_FullRight:N #1
+\cs_new_protected_nopar:Npn \@@_CJK_and_FullRight_glue:N #1
   {
-    \xeCJK_no_break:
-    \xeCJK_get_punct_bounds:NN \c_@@_right_tl {#1}
-    \xeCJK_get_punct_kerning:oN \g_@@_last_punct_tl {#1}
-    \@@_punct_kern:NN \g_@@_last_punct_tl {#1}
-    \tl_gset:Nx \g_@@_last_punct_tl {#1}
-    \xeCJK_no_break:
-    \xeCJK_FullRight_symbol:N {#1}
+    \@@_punct_if_long:NTF #1
+      { \xeCJK_allow_break: }
+      { \xeCJK_no_break: }
+    \@@_punct_if_middle:NT #1
+      {
+        \CJKglue
+        \@@_punct_glue:NN \c_@@_right_tl #1
+        \@@_punct_bound_rule:NN \c_@@_left_tl #1
+      }
   }
+\cs_new_protected_nopar:Npn \@@_Default_and_FullRight_glue:N #1
+  {
+    \@@_punct_if_long:NTF #1
+      { \xeCJK_allow_break: }
+      { \xeCJK_no_break: }
+    \@@_punct_if_middle:NT #1
+      {
+        \@@_punct_glue:NN \c_@@_right_tl #1
+        \@@_punct_bound_rule:NN \c_@@_left_tl #1
+      }
+  }
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\xeCJK_FullRight_and_FullLeft:N}
+% \begin{macro}[int]{\xeCJK_FullLeft_and_FullLeft:N}
 %    \begin{macrocode}
-\cs_new_protected_nopar:Npn \xeCJK_FullRight_and_FullLeft:N #1
+\cs_new_protected_nopar:Npn \xeCJK_FullLeft_and_FullLeft:N #1
   {
-    \xeCJK_get_punct_bounds:NN \c_@@_left_tl {#1}
-    \xeCJK_get_punct_kerning:oN \g_@@_last_punct_tl {#1}
-    \xeCJK_punct_kern:NN \g_@@_last_punct_tl {#1}
-    \tl_gset:Nx \g_@@_last_punct_tl {#1}
-    \CJKpunctsymbol {#1}
+    \xeCJK_get_punct_bounds:NN \c_@@_left_tl #1
+    \xeCJK_get_punct_kerning:oN \g_@@_last_punct_tl #1
+    \@@_punct_kern:NN \g_@@_last_punct_tl #1
+    \tl_gset:Nn \g_@@_last_punct_tl {#1}
+    \CJKpunctsymbol #1
   }
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_punct_nobreak_kern:NN}
+% \begin{macro}[int]{\xeCJK_FullLeft_and_FullRight:N}
 %    \begin{macrocode}
-\cs_new_protected_nopar:Npn \@@_punct_nobreak_kern:NN #1#2
+\cs_new_protected_nopar:Npn \xeCJK_FullLeft_and_FullRight:N #1
   {
-    \@@_punct_kern:NN #1#2
-    \xeCJK_no_break:
+    \xeCJK_get_punct_bounds:NN \c_@@_right_tl #1
+    \xeCJK_get_punct_kerning:oN \g_@@_last_punct_tl #1
+    \@@_punct_kern:NN \g_@@_last_punct_tl #1
+    \tl_gset:Nn \g_@@_last_punct_tl {#1}
+    \xeCJK_FullRight_symbol:N #1
   }
-\cs_new_eq:NN \xeCJK_punct_kern:NN \@@_punct_nobreak_kern:NN
 %    \end{macrocode}
 % \end{macro}
 %
-% \changes{v3.2.4}{2013/07/06}
-% {使用 \texttt{AllowBreakBetweenPuncts} 时,相应标点符号仍能与边界对齐。}
-% \changes{v3.2.7}{2013/08/23}
-% {处理 \texttt{AllowBreakBetweenPuncts} 与 \pkg{xeCJKfntef} 的兼容问题。}
-%
-% \begin{macro}[internal]{\@@_punct_breakable_kern:NN}
+% \begin{macro}[int]{\xeCJK_FullRight_and_FullLeft:N}
 %    \begin{macrocode}
-\cs_new_protected_nopar:Npn \@@_punct_breakable_kern:NN #1#2
+\cs_new_protected_nopar:Npn \xeCJK_FullRight_and_FullLeft:N #1
   {
-    \@@_punct_rule:NN \c_@@_right_tl #1
-    \@@_punct_breakable_kern:n
-      { \@@_use_dim_or_skip:nnn { break_kern } {#1} {#2} }
-    \@@_punct_rule:NN \c_@@_left_tl #2
+    \xeCJK_get_punct_bounds:NN \c_@@_left_tl #1
+    \xeCJK_get_punct_kerning:oN \g_@@_last_punct_tl #1
+    \xeCJK_punct_kern:NN \g_@@_last_punct_tl #1
+    \tl_gset:Nn \g_@@_last_punct_tl {#1}
+    \CJKpunctsymbol #1
   }
-\cs_new_eq:NN \@@_punct_breakable_kern:n \skip_horizontal:n
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\xeCJK_FullRight_and_FullRight:N}
+% \begin{macro}[int]{\xeCJK_FullRight_and_FullRight:N}
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \xeCJK_FullRight_and_FullRight:N #1
   {
-    \xeCJK_get_punct_bounds:NN \c_@@_right_tl {#1}
-    \xeCJK_get_punct_kerning:oN \g_@@_last_punct_tl {#1}
-    \@@_punct_kern:NN \g_@@_last_punct_tl {#1}
-    \tl_gset:Nx \g_@@_last_punct_tl {#1}
-    \xeCJK_no_break:
-    \xeCJK_FullRight_symbol:N {#1}
+    \xeCJK_get_punct_bounds:NN \c_@@_right_tl #1
+    \xeCJK_get_punct_kerning:oN \g_@@_last_punct_tl #1
+    \@@_punct_kern:NN \g_@@_last_punct_tl #1
+    \tl_gset:Nn \g_@@_last_punct_tl {#1}
+    \xeCJK_FullRight_symbol:N #1
   }
 %    \end{macrocode}
 % \end{macro}
@@ -4040,10 +4518,14 @@
       {
         \cs_if_eq:NNF \xeCJK_FullRight_and_Boundary: \xeCJK_check_FullRight:
           {
-            \cs_set_eq:NN \@@_save_FullRight_check: \xeCJK_FullRight_and_Boundary:
-            \cs_set_eq:NN \@@_save_FullRight_symbol:N \xeCJK_FullRight_symbol:N
-            \cs_set_eq:NN \xeCJK_FullRight_and_Boundary: \xeCJK_check_FullRight:
-            \cs_set_eq:NN \xeCJK_FullRight_symbol:N \xeCJK_check_FullRight_symbol:Nw
+            \cs_set_eq:NN \@@_save_FullRight_check:
+                          \xeCJK_FullRight_and_Boundary:
+            \cs_set_eq:NN \@@_save_FullRight_symbol:N
+                          \xeCJK_FullRight_symbol:N
+            \cs_set_eq:NN \xeCJK_FullRight_and_Boundary:
+                          \xeCJK_check_FullRight:
+            \cs_set_eq:NN \xeCJK_FullRight_symbol:N
+                          \xeCJK_check_FullRight_symbol:Nw
           }
       } ,
     CheckFullRight / false .code:n =
@@ -4050,8 +4532,10 @@
       {
         \cs_if_eq:NNT \xeCJK_FullRight_and_Boundary: \xeCJK_check_FullRight:
           {
-            \cs_set_eq:NN \xeCJK_FullRight_and_Boundary: \@@_save_FullRight_check:
-            \cs_set_eq:NN \xeCJK_FullRight_symbol:N \@@_save_FullRight_symbol:N
+            \cs_set_eq:NN \xeCJK_FullRight_and_Boundary:
+                          \@@_save_FullRight_check:
+            \cs_set_eq:NN \xeCJK_FullRight_symbol:N
+                          \@@_save_FullRight_symbol:N
           }
       } ,
     CheckFullRight      .default:n = { true }
@@ -4059,39 +4543,47 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\xeCJK_FullRight_symbol:N}
+% \begin{macro}[int]{\xeCJK_FullRight_symbol:N}
 %    \begin{macrocode}
 \cs_new_nopar:Npn \xeCJK_FullRight_symbol:N { \CJKpunctsymbol }
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\xeCJK_check_FullRight:}
+% \begin{macro}[int]{\xeCJK_check_FullRight:}
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \xeCJK_check_FullRight:
   {
-    \xeCJK_get_punct_bounds:NN \c_@@_right_tl \g_@@_last_punct_tl
+    \xeCJK_get_punct_bounds:No \c_@@_right_tl \g_@@_last_punct_tl
     \@@_punct_rule:NN \c_@@_right_tl \g_@@_last_punct_tl
-    \@@_punct_offset:NN \c_@@_right_tl \g_@@_last_punct_tl
     \group_align_safe_begin:
     \tl_case:NoTF \l_peek_token
       { \l_@@_no_break_cs_case_tl }
-      { \group_align_safe_end: \xeCJK_no_break: }
+      {
+        \group_align_safe_end:
+        \xeCJK_no_break:
+        \group_insert_after:N \xeCJK_no_break:
+      }
       { \group_align_safe_end: }
+    \@@_punct_offset:NN \c_@@_right_tl \g_@@_last_punct_tl
+    \exp_after:wN \xeCJK_punct_node:N \g_@@_last_punct_tl
+    \xeCJK_class_group_end:
     \@@_punct_glue:NN \c_@@_right_tl \g_@@_last_punct_tl
-    \xeCJK_class_group_end:
   }
-\cs_generate_variant:Nn \tl_case:NnTF { No }
+\prg_generate_conditional_variant:Nnn \tl_case:Nn { No } { TF , F }
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\xeCJK_check_FullRight_symbol:Nw}
+% \begin{macro}[int]{\xeCJK_check_FullRight_symbol:Nw}
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \xeCJK_check_FullRight_symbol:Nw #1
-  { \xeCJK_peek_after_ignore_spaces:nw { \@@_save_FullRight_symbol:N {#1} } }
+  {
+    \xeCJK_peek_after_ignore_spaces:nw
+      { \@@_save_FullRight_symbol:N #1 }
+  }
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\xeCJK_cs_case_keys_define:nNNnn}
+% \begin{macro}[int]{\xeCJK_cs_case_keys_define:nNNnn}
 %    \begin{macrocode}
 \cs_new_protected:Npn \xeCJK_cs_case_keys_define:nNNnn #1#2#3#4#5
   {
@@ -4146,13 +4638,30 @@
         \bool_if:NTF \l_@@_tmp_bool
           {
             \bool_set_false:N \l_@@_tmp_bool
-            \skip_set_eq:NN \l_@@_tmp_skip \tex_lastskip:D
+            \skip_set_eq:NN \l_@@_last_skip \tex_lastskip:D
           }
-          { \skip_add:Nn \l_@@_tmp_skip \tex_lastskip:D }
+          { \skip_add:Nn \l_@@_last_skip \tex_lastskip:D }
         \tex_unskip:D
       }
+    \xeCJK_if_last_node:TF
+      {
+        \dim_set_eq:NN \l_@@_tmp_dim \l_@@_last_kern_dim
+        \xeCJK_if_last_node:TF
+          {
+            \int_compare:nNnT \etex_lastnodetype:D = \c_eleven
+              {
+                \exp_args:NNNo \tex_unskip:D \xeCJK_no_break:
+                \skip_horizontal:n { \skip_use:N \tex_lastskip:D }
+              }
+            \@@_make_node:N \l_@@_last_kern_dim
+          }
+          { }
+        \@@_make_node:N \l_@@_tmp_dim
+      }
+      { }
     \xeCJK_no_break:
-    \bool_if:NF \l_@@_tmp_bool { \skip_horizontal:N \l_@@_tmp_skip }
+    \bool_if:NF \l_@@_tmp_bool
+      { \skip_horizontal:N \l_@@_last_skip }
   }
 %    \end{macrocode}
 % \end{macro}
@@ -4197,7 +4706,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\xeCJK_widow_penalty:}
+% \begin{macro}[int]{\xeCJK_widow_penalty:}
 % 预防段末孤字而插入的 penalty,值为 \cs{l_@@_widow_penalty_int}。
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \xeCJK_widow_penalty:
@@ -4207,8 +4716,8 @@
 %
 % \changes{v3.4.7}{2017/03/20}{简化 \texttt{CheckSingle} 的实现,不再展开宏。}
 %
-% \begin{macro}[internal]{\xeCJK_check_single:Nw}
-% \begin{macro}[aux]{\@@_check_single_end:N}
+% \begin{macro}[int]{\xeCJK_check_single:Nw}
+% \begin{macro}{\@@_check_single_end:N}
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \xeCJK_check_single:Nw #1
   {
@@ -4231,8 +4740,8 @@
 % \end{macro}
 % \end{macro}
 %
-% \begin{macro}[internal]{\xeCJK_check_single:NNw}
-% \begin{macro}[aux]{\@@_check_single_aux:nNNw}
+% \begin{macro}[int]{\xeCJK_check_single:NNw}
+% \begin{macro}{\@@_check_single_aux:nNNw}
 % \changes{v3.1.1}{2012/12/04}{改进定义,减少使用 \texttt{peek} 函数的次数。}
 % \changes{v3.2.7}{2013/08/30}{与 \tn{CJKspace} 兼容。}
 % 使用 \cs{group_align_safe_begin:} 和 \cs{group_align_safe_end:} 是为了防止在表格
@@ -4276,8 +4785,8 @@
 % \end{macro}
 % \end{macro}
 %
-% \begin{macro}[internal]{\xeCJK_check_single_end:NNnw}
-% \begin{macro}[aux]{\@@_check_single_end_aux:NNn,\@@_check_single_end_equation:NNnw}
+% \begin{macro}[int]{\xeCJK_check_single_end:NNnw}
+% \begin{macro}{\@@_check_single_end_aux:NNn,\@@_check_single_end_equation:NNnw}
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \@@_check_single_end_aux:NNn #1#2#3
   { \@@_check_single_end:N #1 #2 #3 }
@@ -4313,7 +4822,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_check_single_space:NN}
+% \begin{macro}{\@@_check_single_space:NN}
 % \changes{v3.1.1}{2012/12/13}
 % {\texttt{CheckSingle} 支持段末“汉字$+$汉字$+$空格$+$汉字/标点”的形式。}
 % \changes{v3.1.2}{2012/12/27}
@@ -4332,7 +4841,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\xeCJK_check_single_equation:NNnNw}
+% \begin{macro}[int]{\xeCJK_check_single_equation:NNnNw}
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \xeCJK_check_single_equation:NNnNw #1#2#3#4
   {
@@ -4351,7 +4860,7 @@
 % \changes{v3.2.4}{2013/06/26}
 % {解决使用 \texttt{CheckSingle} 时,某些 \tn{CJKglue} 不能被正确加入的问题。}
 %
-% \begin{macro}[internal]{\xeCJK_check_single_cs:NNn}
+% \begin{macro}[int]{\xeCJK_check_single_cs:NNn}
 % \changes{v3.3.1}{2015/04/06}{补充可能遗漏的空格。}
 % 在使用 \texttt{CheckSingle} 选项时,在 \package{tablists} 宏包定义的
 % \env{tabenum} 环境中会出现下面的错误:
@@ -4380,11 +4889,10 @@
       { \@@_check_single_end:N #1 #2#3 }
   }
 \tl_new:N \l_@@_check_single_cs_case_tl
-\cs_generate_variant:Nn \tl_case:NnF { No }
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\xeCJK_check_single_env:nnNn}
+% \begin{macro}[int]{\xeCJK_check_single_env:nnNn}
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \xeCJK_check_single_env:nnNn #1#2#3#4
   {
@@ -4394,7 +4902,7 @@
       {#1}
     #3 {#4}
   }
-\cs_generate_variant:Nn \str_case_x:nnTF { no }
+\prg_generate_conditional_variant:Nnn \str_case_x:nn { no } { TF }
 %    \end{macrocode}
 % \end{macro}
 %
@@ -4455,7 +4963,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_update_inline_env_case_tl:}
+% \begin{macro}{\@@_update_inline_env_case_tl:}
 %    \begin{macrocode}
 \cs_new_protected:Npn \@@_update_inline_env_case_tl:
   {
@@ -4469,11 +4977,11 @@
 %
 % \subsection{增加 CJK 子分区}
 %
-% \begin{macro}[internal]{\g_@@_CJK_sub_class_seq}
+% \begin{variable}{\g_@@_CJK_sub_class_seq}
 %    \begin{macrocode}
 \seq_new:N \g_@@_CJK_sub_class_seq
 %    \end{macrocode}
-% \end{macro}
+% \end{variable}
 %
 % \begin{macro}{\xeCJKDeclareSubCJKBlock}
 % 声明 CJK 子区范围,|#1| 为自定义名称,|#2| 为子区的 |Unicode| 范围。
@@ -4513,7 +5021,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_sub_restore_or_cancel:n}
+% \begin{macro}{\@@_sub_restore_or_cancel:n}
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \@@_sub_restore_or_cancel:n #1
   {
@@ -4521,9 +5029,9 @@
       {
         \int_if_exist:cTF { \@@_class_csname:n { CJK/##1 } }
           {
-            \xeCJK_declare_char_class:nc
+            \xeCJK_declare_char_class:nn
               { CJK \bool_if:NF \l_@@_sub_cancel_bool { /##1 } }
-              { g_@@_CJK/##1_range_clist }
+              { \use:c { g_@@_CJK/##1_range_clist } }
           }
           { \@@_error:nx { SubBlock-undefined } {##1} }
       }
@@ -4538,7 +5046,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\xeCJK_declare_sub_char_class:nnn}
+% \begin{macro}[int]{\xeCJK_declare_sub_char_class:nnn}
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \xeCJK_declare_sub_char_class:nnn #1#2#3
   {
@@ -4554,7 +5062,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_set_sub_class_toks:nn}
+% \begin{macro}{\@@_set_sub_class_toks:nn}
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \@@_set_sub_class_toks:nn #1#2
   {
@@ -4652,12 +5160,12 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal,var]{\c_@@_left_tl,\c_@@_right_tl}
+% \begin{variable}{\c_@@_left_tl,\c_@@_right_tl}
 %    \begin{macrocode}
 \tl_const:Nn \c_@@_left_tl  { left }
 \tl_const:Nn \c_@@_right_tl { right }
 %    \end{macrocode}
-% \end{macro}
+% \end{variable}
 %
 % \changes{v3.2.12}{2014/05/12}{新增 \texttt{RubberPunctSkip} 选项。}
 % \changes{v3.4.0}{2016/05/13}{\texttt{RubberPunctSkip} 选项有新的值 \texttt{plus} 和 \texttt{minus}。}
@@ -4674,11 +5182,15 @@
       {
         \bool_set_true:N \l_@@_punct_breakable_bool
         \cs_set_eq:NN \xeCJK_punct_kern:NN \@@_punct_breakable_kern:NN
+        \cs_set_eq:NN \@@_punct_bound_kern:N
+                      \@@_punct_bound_breakable_kern:N
       } ,
     AllowBreakBetweenPuncts / false .code:n =
       {
         \bool_set_false:N \l_@@_punct_breakable_bool
-        \cs_set_eq:NN \xeCJK_punct_kern:NN \@@_punct_nobreak_kern:NN
+        \cs_set_eq:NN \xeCJK_punct_kern:NN \@@_punct_kern:NN
+        \cs_set_eq:NN \@@_punct_bound_kern:N
+                      \@@_nobreak_hskip:N
       } ,
     AllowBreakBetweenPuncts      .default:n = { true } ,
     KaiMingPunct  .code:n = { \@@_set_special_punct:nn { mixed_width } {#1} } ,
@@ -4697,13 +5209,13 @@
     RubberPunctSkip .choice: ,
     RubberPunctSkip      .default:n = { true } ,
     RubberPunctSkip / true  .code:n =
-      { \cs_set_eq:NN \@@_use_dim_or_skip:nnn \@@_use_punct_skip:nnn } ,
+      { \cs_set_eq:NN \@@_use_dim_or_skip:nNN \@@_use_punct_skip:nNN } ,
     RubberPunctSkip / plus  .code:n =
-      { \cs_set_eq:NN \@@_use_dim_or_skip:nnn \@@_use_punct_skip_plus:nnn } ,
+      { \cs_set_eq:NN \@@_use_dim_or_skip:nNN \@@_use_punct_skip_plus:nNN } ,
     RubberPunctSkip / minus .code:n =
-      { \cs_set_eq:NN \@@_use_dim_or_skip:nnn \@@_use_punct_skip_minus:nnn } ,
+      { \cs_set_eq:NN \@@_use_dim_or_skip:nNN \@@_use_punct_skip_minus:nNN } ,
     RubberPunctSkip / false .code:n =
-      { \cs_set_eq:NN \@@_use_dim_or_skip:nnn \@@_use_punct_dim:nnn }
+      { \cs_set_eq:NN \@@_use_dim_or_skip:nNN \@@_use_punct_dim:nNN }
   }
 \bool_new:N \l_@@_punct_breakable_bool
 %    \end{macrocode}
@@ -4753,7 +5265,8 @@
 %    \begin{macrocode}
 \prg_new_conditional:Npnn \@@_punct_if_right:N #1 { p , T , F , TF }
   {
-    \if_int_compare:w \xeCJK_token_value_class:N #1 = \xeCJK_class_num:n { FullRight }
+    \if_int_compare:w \xeCJK_token_value_class:N #1 =
+                      \xeCJK_class_num:n { FullRight }
       \prg_return_true: \else: \prg_return_false: \fi:
   }
 \clist_map_inline:Nn \g_@@_special_punct_clist
@@ -4771,27 +5284,27 @@
 %    \begin{macrocode}
 \cs_new:Npn \@@_punct_csname:n #1
   { c_@@_\l_xeCJK_current_font_tl/\l_xeCJK_punct_style_tl/#1/tl }
-\cs_new:Npn \@@_use_punct_dim:nn #1#2
+\cs_new:Npn \@@_use_punct_dim:nN #1#2
   { \use:c { \@@_punct_csname:n { dim/#1/#2 } } }
-\cs_new:Npn \@@_use_punct_dim:nnn #1#2#3
+\cs_new:Npn \@@_use_punct_dim:nNN #1#2#3
   { \use:c { \@@_punct_csname:n { dim/#1/#2/#3 } } }
-\cs_new:Npn \@@_use_punct_skip:nnn #1#2#3
+\cs_new:Npn \@@_use_punct_skip:nNN #1#2#3
   { \use:c { \@@_punct_csname:n { skip/#1/#2/#3 } } }
-\cs_new:Npn \@@_use_punct_skip_plus:nnn #1#2#3
+\cs_new:Npn \@@_use_punct_skip_plus:nNN #1#2#3
   { \use:c { \@@_punct_csname:n { skip/plus/#1/#2/#3 } } }
-\cs_new:Npn \@@_use_punct_skip_minus:nnn #1#2#3
+\cs_new:Npn \@@_use_punct_skip_minus:nNN #1#2#3
   { \use:c { \@@_punct_csname:n { skip/minus/#1/#2/#3 } } }
-\cs_new_protected:Npn \@@_save_punct_dim:nnn #1#2
+\cs_new_protected:Npn \@@_save_punct_dim:nNn #1#2
   { \@@_save_punct_width_aux:nnn { dim } { #1/#2 } }
-\cs_new_protected:Npn \@@_save_punct_dim:nnnn #1#2#3
+\cs_new_protected:Npn \@@_save_punct_dim:nNNn #1#2#3
   { \@@_save_punct_width_aux:nnn { dim } { #1/#2/#3 } }
-\cs_new_protected:Npn \@@_save_punct_skip:nnnn #1#2#3#4
+\cs_new_protected:Npn \@@_save_punct_skip:nNNn #1#2#3#4
   {
     \@@_save_punct_width_aux:nnn { skip } { #1/#2/#3 } {#4}
     \@@_save_punct_width_aux:nnn { skip } { plus/#1/#2/#3 } {#4}
     \@@_save_punct_width_aux:nnn { skip } { minus/#1/#2/#3 } {#4}
   }
-\cs_new_protected:Npn \@@_save_punct_skip:nnnnnn #1#2#3#4#5#6
+\cs_new_protected:Npn \@@_save_punct_skip:nNNnnn #1#2#3#4#5#6
   {
     \use:x
       {
@@ -4803,9 +5316,12 @@
   }
 \cs_new_protected:Npn \@@_save_punct_skip_aux:nnnn #1#2#3#4
   {
-    \@@_save_punct_width_aux:nnn { skip } {#1} { #2 ~ plus ~ #3 ~ minus ~ #4 ~ }
-    \@@_save_punct_width_aux:nnn { skip } { plus/#1 } { #2 ~ plus ~ #3 ~ }
-    \@@_save_punct_width_aux:nnn { skip } { minus/#1 } { #2 ~ minus ~ #4 ~ }
+    \@@_save_punct_width_aux:nnn { skip }
+      {#1}         { #2 ~ plus ~ #3 ~ minus ~ #4 ~ }
+    \@@_save_punct_width_aux:nnn { skip }
+      { plus/#1 }  { #2 ~ plus ~ #3 ~ }
+    \@@_save_punct_width_aux:nnn { skip }
+      { minus/#1 } { #2 ~ minus ~ #4 ~ }
   }
 \cs_new_protected:Npn \@@_save_punct_width_aux:nnn #1#2#3
   {
@@ -4812,7 +5328,7 @@
     \tl_const:cx { \@@_punct_csname:n { #1/#2 } }
                  { \use:c { #1_eval:n } {#3} }
   }
-\cs_new_eq:NN \@@_use_dim_or_skip:nnn \@@_use_punct_skip:nnn
+\cs_new_eq:NN \@@_use_dim_or_skip:nNN \@@_use_punct_skip:nNN
 %    \end{macrocode}
 %
 % \changes{v3.1.0}{2012/11/14}{使用 \pkg{xtemplate} 宏包的机制来组织标点符号的处理。}
@@ -4901,44 +5417,49 @@
   { \AssignTemplateKeys }
 %    \end{macrocode}
 %
-% \begin{macro}[internal]{\xeCJK_get_punct_bounds:NN}
+% \begin{macro}[int]{\xeCJK_get_punct_bounds:NN}
 % |#1| 为 \cs{c_@@_left_tl} 或 \cs{c_@@_right_tl},|#2| 为标点符号。
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \xeCJK_get_punct_bounds:NN #1#2
   {
     \tl_if_exist:cF { \@@_punct_csname:n { dim/glue/#1/#2 } }
+      { \@@_get_punct_bounds_aux:NN #1 #2 }
+  }
+\cs_new_protected_nopar:Npn \@@_get_punct_bounds_aux:NN #1#2
+  {
+    \tl_if_eq:NNTF \l_xeCJK_punct_style_tl \c_@@_punct_style_plain_tl
       {
-        \tl_if_eq:NNTF \l_xeCJK_punct_style_tl \c_@@_punct_style_plain_tl
+        \@@_save_punct_dim:nNNn { rule }   #1 #2 { \c_zero_dim }
+        \@@_save_punct_dim:nNNn { glue }   #1 #2 { \c_zero_dim }
+        \@@_save_punct_dim:nNNn { offset } #1 #2 { \c_zero_dim }
+        \@@_save_punct_dim:nNNn { bound } \c_@@_left_tl  {#2} { \c_zero_dim }
+        \@@_save_punct_dim:nNNn { bound } \c_@@_right_tl {#2} { \c_zero_dim }
+        \@@_save_punct_skip:nNNn { glue }  #1 #2 { \c_zero_skip }
+      }
+      {
+        { \xeCJK_select_punct_font: \xeCJK_calc_punct_dimen:N #2 }
+        \dim_set:Nn \l_@@_bound_dim
+          { \@@_use_punct_dim:nNN { bound } #1 #2 }
+        \dim_set:Nn \l_@@_reverse_bound_dim
           {
-            \@@_save_punct_dim:nnnn { rule }   {#1} {#2} { \c_zero_dim }
-            \@@_save_punct_dim:nnnn { glue }   {#1} {#2} { \c_zero_dim }
-            \@@_save_punct_dim:nnnn { offset } {#1} {#2} { \c_zero_dim }
-            \@@_save_punct_dim:nnnn { bound } \c_@@_left_tl  {#2} { \c_zero_dim }
-            \@@_save_punct_dim:nnnn { bound } \c_@@_right_tl {#2} { \c_zero_dim }
-            \@@_save_punct_skip:nnnn { glue }  {#1} {#2} { \c_zero_skip }
+            \tl_if_eq:NNTF #1 \c_@@_left_tl
+              { \@@_use_punct_dim:nNN { bound } \c_@@_right_tl }
+              { \@@_use_punct_dim:nNN { bound } \c_@@_left_tl }
+              #2
           }
-          {
-            { \xeCJK_select_font: \xeCJK_calc_punct_dimen:o {#2} }
-            \dim_set:Nn \l_@@_bound_dim
-              { \@@_use_punct_dim:nnn { bound } {#1} {#2} }
-            \dim_set:Nn \l_@@_reverse_bound_dim
-              {
-                \@@_use_punct_dim:nnn { bound }
-                  { \xeCJK_reverse:nnn {#1} \c_@@_left_tl \c_@@_right_tl }
-                  {#2}
-              }
-            \UseInstance { xeCJK / punctuation } { \l_xeCJK_punct_style_tl }
-            \xeCJK_punct_margin_process:NN {#1} {#2}
-            \xeCJK_punct_offset_process:NN {#1} {#2}
-          }
+        \UseInstance { xeCJK / punctuation } { \l_xeCJK_punct_style_tl }
+        \xeCJK_punct_margin_process:NN #1 #2
+        \xeCJK_punct_offset_process:NN #1 #2
       }
   }
+\cs_new_protected_nopar:Npn \xeCJK_get_punct_bounds:No
+  { \exp_last_unbraced:NNo \xeCJK_get_punct_bounds:NN }
 \dim_new:N \l_@@_bound_dim
 \dim_new:N \l_@@_reverse_bound_dim
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\xeCJK_get_punct_kerning:NN}
+% \begin{macro}[int]{\xeCJK_get_punct_kerning:NN}
 % 标点挤压。
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \xeCJK_get_punct_kerning:NN #1#2
@@ -4947,22 +5468,26 @@
       {
         \tl_if_eq:NNTF \l_xeCJK_punct_style_tl \c_@@_punct_style_plain_tl
           {
-            \@@_save_punct_dim:nnnn { kern } {#1} {#2} { \c_zero_dim }
-            \@@_save_punct_dim:nnnn { break_kern } {#1} {#2} { \c_zero_dim }
-            \@@_save_punct_skip:nnnn { kern } {#1} {#2} { \c_zero_skip }
-            \@@_save_punct_skip:nnnn { break_kern } {#1} {#2} { \c_zero_skip }
+            \@@_save_punct_dim:nNNn { kern } #1 #2 { \c_zero_dim }
+            \@@_save_punct_dim:nNNn { break_kern } #1 #2 { \c_zero_dim }
+            \@@_save_punct_dim:nNNn { bound_kern } #1 #2 { \c_zero_dim }
+            \@@_save_punct_dim:nNNn { bound_width } #1 #2 { \c_zero_dim }
+            \@@_save_punct_skip:nNNn { kern } #1 #2 { \c_zero_skip }
+            \@@_save_punct_skip:nNNn { break_kern } #1 #2 { \c_zero_skip }
+            \@@_save_punct_skip:nNNn { bound_kern } #1 #2 { \c_zero_skip }
           }
           {
             \UseInstance { xeCJK / punctuation } { \l_xeCJK_punct_style_tl }
-            \xeCJK_punct_kerning_process:NN {#1} {#2}
+            \xeCJK_punct_kerning_process:NN #1 #2
           }
       }
   }
-\cs_generate_variant:Nn \xeCJK_get_punct_kerning:NN { o }
+\cs_new_protected_nopar:Npn \xeCJK_get_punct_kerning:oN
+  { \exp_after:wN \xeCJK_get_punct_kerning:NN }
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\xeCJK_punct_margin_process:NN}
+% \begin{macro}[int]{\xeCJK_punct_margin_process:NN}
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \xeCJK_punct_margin_process:NN #1#2
   {
@@ -4970,24 +5495,14 @@
       {
         \bool_if:NTF \l_@@_enabled_global_setting_bool
           {
-            \cs_if_exist_use:cTF { g_@@_punct_width/#2/tl }
-              { \use_none:n }
+            \cs_if_exist_use:cF { g_@@_punct_width/#2/tl }
               {
                 \tl_if_empty:NTF \g_@@_punct_width_tl
-                  { \use:n }
-                  { \g_@@_punct_width_tl \use_none:n }
+                  { \@@_calc_punct_width:N #2 }
+                  { \g_@@_punct_width_tl }
               }
           }
-          { \use:n }
-          {
-            \@@_punct_if_middle:NTF {#2}
-              { \@@_punct_width_or_ratio:nN { middle } {#2} }
-              {
-                \@@_punct_if_mixed_width:NTF {#2}
-                  { \@@_punct_width_or_ratio:nN { mixed } {#2} }
-                  { \@@_punct_width_or_ratio:nN { fixed } {#2} }
-              }
-          }
+          { \@@_calc_punct_width:N #2 }
       }
     \dim_set:Nn \l_@@_tmp_dim
       {
@@ -4996,10 +5511,10 @@
           {
             \dim_compare:nNnTF \l_@@_tmp_dim < \c_max_dim
               {
-                \@@_punct_if_middle:NTF {#2}
+                \@@_punct_if_middle:NTF #2
                   {
-                    (
-                      \l_@@_tmp_dim - ( \@@_use_punct_dim:nn { dimen } {#2} )
+                    (   \l_@@_tmp_dim
+                      - ( \@@_use_punct_dim:nN { dimen } #2 )
                     ) / \c_two
                   }
                   {
@@ -5006,12 +5521,17 @@
                     \bool_if:NTF \l_@@_optimize_margin_bool
                       {
                         \dim_max:nn
-                          { \dim_min:nn \l_@@_bound_dim \l_@@_reverse_bound_dim }
+                          {
+                            \dim_min:nn
+                              { \l_@@_bound_dim }
+                              { \l_@@_reverse_bound_dim }
+                          }
                       }
                       { \use:n }
                       {
-                        \l_@@_tmp_dim - \l_@@_reverse_bound_dim
-                        - ( \@@_use_punct_dim:nn { dimen } {#2} )
+                          \l_@@_tmp_dim
+                        - \l_@@_reverse_bound_dim
+                        - ( \@@_use_punct_dim:nN { dimen } #2 )
                       }
                   }
               }
@@ -5019,41 +5539,24 @@
                 \bool_if:NTF \l_@@_optimize_margin_bool
                   { \dim_min:nn { \l_@@_bound_dim } }
                   { \use:n }
-                  {
-                    \@@_punct_if_middle:NTF {#2}
-                      {
-                        \dim_compare:nNnTF \l_@@_middle_margin_width_dim < \c_max_dim
-                          { \l_@@_middle_margin_width_dim }
-                          {
-                            \fp_use:N \l_@@_middle_margin_ratio_fp
-                            \etex_dimexpr:D
-                              ( \l_@@_bound_dim + \l_@@_reverse_bound_dim ) / \c_two
-                            \scan_stop:
-                          }
-                      }
-                      {
-                        \@@_punct_if_mixed_width:NTF {#2}
-                          { \@@_margin_width_or_ratio:n { mixed } }
-                          { \@@_margin_width_or_ratio:n { fixed } }
-                      }
-                  }
+                  { \@@_calc_margin_width:N #2 }
               }
           }
       }
-    \@@_save_punct_dim:nnnn { glue } {#1} {#2} { \l_@@_tmp_dim }
-    \@@_save_punct_skip:nnnnnn { glue } {#1} {#2}
+    \@@_save_punct_dim:nNNn { glue } #1 #2 { \l_@@_tmp_dim }
+    \@@_save_punct_skip:nNNnnn { glue } #1 #2
       { \l_@@_tmp_dim }
       {
-        \@@_punct_if_middle:NTF {#2}
+        \@@_punct_if_middle:NTF #2
           {
-            ( \@@_use_punct_dim:nn { width } {#2} -
-              \@@_use_punct_dim:nn { dimen } {#2} ) / \c_two
+            ( \@@_use_punct_dim:nN { width } #2 -
+              \@@_use_punct_dim:nN { dimen } #2 ) / \c_two
             - \l_@@_tmp_dim
           }
           { \l_@@_bound_dim - \l_@@_tmp_dim }
       }
       {
-        \@@_punct_if_middle:NTF {#2}
+        \@@_punct_if_middle:NTF #2
           { .5 \l_@@_tmp_dim }
           { \l_@@_tmp_dim - \l_@@_reverse_bound_dim }
       }
@@ -5061,8 +5564,48 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\xeCJK_punct_offset_process:NN}
+% \begin{macro}{\@@_calc_punct_width:N}
 %    \begin{macrocode}
+\cs_new_nopar:Npn \@@_calc_punct_width:N #1
+  {
+    \@@_punct_if_middle:NTF #1
+      { \@@_punct_width_or_ratio:nN { middle } }
+      {
+        \@@_punct_if_mixed_width:NTF #1
+          { \@@_punct_width_or_ratio:nN { mixed } }
+          { \@@_punct_width_or_ratio:nN { fixed } }
+      }
+      #1
+  }
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_calc_margin_width:N}
+%    \begin{macrocode}
+\cs_new_nopar:Npn \@@_calc_margin_width:N #1
+  {
+    \@@_punct_if_middle:NTF #1
+      {
+        \dim_compare:nNnTF \l_@@_middle_margin_width_dim < \c_max_dim
+          { \l_@@_middle_margin_width_dim }
+          {
+            \fp_use:N \l_@@_middle_margin_ratio_fp
+            \etex_dimexpr:D
+              ( \l_@@_bound_dim + \l_@@_reverse_bound_dim ) / \c_two
+            \scan_stop:
+          }
+      }
+      {
+        \@@_punct_if_mixed_width:NTF #1
+          { \@@_margin_width_or_ratio:n { mixed } }
+          { \@@_margin_width_or_ratio:n { fixed } }
+      }
+  }
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[int]{\xeCJK_punct_offset_process:NN}
+%    \begin{macrocode}
 \cs_new_protected_nopar:Npn \xeCJK_punct_offset_process:NN #1#2
   {
     \dim_set:Nn \l_@@_tmp_dim
@@ -5069,16 +5612,14 @@
       {
         \bool_if:NTF \l_@@_enabled_global_setting_bool
           {
-            \cs_if_exist_use:cTF { g_@@_punct_bound_width/#2/tl }
-              { \use_none:n }
+            \cs_if_exist_use:cF { g_@@_punct_bound_width/#2/tl }
               {
                 \tl_if_empty:NTF \g_@@_punct_bound_width_tl
-                  { \use:n }
-                  { \g_@@_punct_bound_width_tl \use_none:n }
+                  { \@@_punct_width_or_ratio:nN { bound } #2 }
+                  { \g_@@_punct_bound_width_tl }
               }
           }
-          { \use:n }
-          { \@@_punct_width_or_ratio:nN { bound } {#2} }
+          { \@@_punct_width_or_ratio:nN { bound } #2 }
       }
     \dim_set:Nn \l_@@_tmp_dim
       {
@@ -5088,15 +5629,16 @@
           {
             \dim_compare:nNnTF \l_@@_tmp_dim < \c_max_dim
               {
-                \@@_punct_if_middle:NTF {#2}
+                \@@_punct_if_middle:NTF #2
                   {
-                    \l_@@_tmp_dim
-                    - ( \@@_use_punct_dim:nnn { glue } {#1} {#2} )
-                    - ( \@@_use_punct_dim:nn { dimen } {#2} )
+                      \l_@@_tmp_dim
+                    - ( \@@_use_punct_dim:nNN { glue } #1 #2 )
+                    - ( \@@_use_punct_dim:nN { dimen } #2 )
                   }
                   {
-                    \l_@@_tmp_dim - \l_@@_reverse_bound_dim
-                    - ( \@@_use_punct_dim:nn { dimen } {#2} )
+                      \l_@@_tmp_dim
+                    - \l_@@_reverse_bound_dim
+                    - ( \@@_use_punct_dim:nN { dimen } #2 )
                   }
               }
               {
@@ -5107,8 +5649,9 @@
               }
           }
       }
-    \@@_save_punct_dim:nnnn { offset } {#1} {#2} { \l_@@_tmp_dim }
-    \@@_save_punct_dim:nnnn { rule } {#1} {#2}
+    \@@_save_punct_dim:nNNn { offset } #1 #2
+      { \l_@@_tmp_dim }
+    \@@_save_punct_dim:nNNn { rule }   #1 #2
       { - \l_@@_bound_dim + \l_@@_tmp_dim }
   }
 %    \end{macrocode}
@@ -5116,7 +5659,7 @@
 %
 % \changes{v3.2.7}{2013/08/25}{标点宽度设置禁用比例选项的值改为 \texttt{nan}。}
 %
-% \begin{macro}[internal]{\@@_punct_width_or_ratio:nN}
+% \begin{macro}{\@@_punct_width_or_ratio:nN}
 %    \begin{macrocode}
 \cs_new_nopar:Npn \@@_punct_width_or_ratio:nN #1#2
   {
@@ -5127,7 +5670,7 @@
           { \c_max_dim }
           {
             \fp_use:c { l_@@_#1_punct_ratio_fp }
-            \etex_dimexpr:D \@@_use_punct_dim:nn { width } {#2} \scan_stop:
+            \etex_dimexpr:D \@@_use_punct_dim:nN { width } #2 \scan_stop:
           }
       }
   }
@@ -5134,7 +5677,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_margin_width_or_ratio:n}
+% \begin{macro}{\@@_margin_width_or_ratio:n}
 %    \begin{macrocode}
 \cs_new_nopar:Npn \@@_margin_width_or_ratio:n #1
   {
@@ -5152,19 +5695,19 @@
 %
 % \changes{v3.4.4}{2016/11/30}{不压缩长标点与其他标点的间距。}
 %
-% \begin{macro}[internal]{\xeCJK_punct_kerning_process:NN}
+% \begin{macro}[int]{\xeCJK_punct_kerning_process:NN}
 % 当标点之一为长标点时,不必进行压缩。
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \xeCJK_punct_kerning_process:NN #1#2
   {
     \dim_set:Nn \l_@@_original_margin_dim
-      { \@@_original_kerning_margin:NN {#1} {#2} }
+      { \@@_original_kerning_margin:NN #1 #2 }
     \dim_set:Nn \l_@@_minimum_bound_dim
-      { \@@_punct_min_bound:NN {#1} {#2} }
-    \@@_punct_if_long:NTF {#1}
+      { \@@_punct_min_bound:NN #1 #2 }
+    \@@_punct_if_long:NTF #1
       { \bool_set_false:N \l_@@_enabled_kerning_bool }
       {
-        \@@_punct_if_long:NT {#2}
+        \@@_punct_if_long:NT #2
           { \bool_set_false:N \l_@@_enabled_kerning_bool }
       }
     \dim_set:Nn \l_@@_kerning_margin_dim
@@ -5171,24 +5714,53 @@
       {
         \bool_if:NTF \l_@@_enabled_global_setting_bool
           {
-            \cs_if_exist_use:cTF { g_@@_punct/kern/#1/#2/tl }
-              { \use_none:n }
-              { \use:n }
+            \cs_if_exist_use:cF { g_@@_punct/kern/#1/#2/tl }
+              { \@@_punct_kerning_process_aux:NN #1 #2 }
           }
-          { \use:n }
+          { \@@_punct_kerning_process_aux:NN #1 #2 }
+      }
+    \@@_save_kerning:nnNN { kern } { bound }  #1 #2
+    \@@_save_punct_dim:nNNn { bound_width } #1 #2
+      { \l_@@_kerning_margin_dim - \l_@@_tmp_dim }
+    \@@_punct_if_right:NTF #1
+      {
+        \@@_punct_if_right:NTF #2
           {
-            \bool_if:NTF \l_@@_enabled_kerning_bool
-              { \@@_calc_kerning_margin:NN {#1} {#2} }
-              { \l_@@_original_margin_dim }
+            \@@_save_kerning:nNNNN
+              { bound_kern } \c_@@_left_tl #2 #1 #2
+            \@@_save_kerning:nnnNN
+              { break_kern } { offset } { bound }
           }
+          {
+            \@@_save_kerning:nnNN
+              { break_kern } { offset } #1 #2
+            \@@_save_kerning_aux:nnNN
+              { bound_kern } { \l_@@_kerning_margin_dim }
+          }
       }
-    \@@_save_kerning:nnNN { kern } { bound } {#1} {#2}
-    \@@_punct_if_right:NF {#2}
       {
-        \@@_punct_if_right:NT {#1}
-          { \@@_save_kerning:nnNN { break_kern } { offset } {#1} {#2} }
+        \@@_punct_if_right:NTF #2
+          {
+            \@@_save_kerning:nnNN
+              { bound_kern } { bound } #1 #2
+            \@@_save_kerning_aux:nnNN
+              { break_kern } { \l_@@_tmp_dim }
+          }
+          {
+            \@@_save_kerning:nNNNN
+              { bound_kern } \c_@@_right_tl #1 #1 #2
+            \@@_save_kerning:nnnNN
+              { break_kern } { bound } { offset }
+          }
       }
+      #1 #2
   }
+\cs_new_nopar:Npn \@@_punct_kerning_process_aux:NN #1#2
+  {
+    \bool_if:NTF \l_@@_enabled_kerning_bool
+      { \@@_calc_kerning_margin:NN #1 #2 }
+      { \l_@@_original_margin_dim }
+  }
 \dim_new:N \l_@@_minimum_bound_dim
 \dim_new:N \l_@@_kerning_margin_dim
 \dim_new:N \l_@@_original_margin_dim
@@ -5197,20 +5769,35 @@
 %
 % \changes{v3.4.0}{2016/05/12}{标点符号的压缩量能伸长到原始空白,能收缩到较小边距。}
 %
-% \begin{macro}[internal]{\@@_save_kerning:nnNN}
+% \begin{macro}{\@@_save_kerning:nnNN}
 % 相邻两个标点符号的间距能伸长到原始空白(未压缩时的状态),能收缩到较小边距。
 %    \begin{macrocode}
-\cs_new_protected_nopar:Npn \@@_save_kerning:nnNN #1#2#3#4
+\cs_new_protected_nopar:Npn \@@_save_kerning:nnNN #1#2
+  { \@@_save_kerning:nnnNN {#1} {#2} {#2} }
+\cs_new_protected_nopar:Npn \@@_save_kerning:nnnNN #1#2#3#4#5
   {
     \dim_set:Nn \l_@@_tmp_dim
       {
-        \l_@@_kerning_margin_dim
-        - ( \@@_use_punct_dim:nnn {#2} \c_@@_right_tl {#3} )
-        - ( \@@_use_punct_dim:nnn {#2} \c_@@_left_tl  {#4} )
+          \l_@@_kerning_margin_dim
+        - ( \@@_use_punct_dim:nNN {#2} \c_@@_right_tl #4 )
+        - ( \@@_use_punct_dim:nNN {#3} \c_@@_left_tl  #5 )
       }
-    \@@_save_punct_dim:nnnn {#1} {#3} {#4} { \l_@@_tmp_dim }
-    \@@_save_punct_skip:nnnnnn {#1} {#3} {#4}
-      { \l_@@_tmp_dim }
+    \@@_save_kerning_aux:nnNN {#1} { \l_@@_tmp_dim } #4 #5
+  }
+\cs_new_protected_nopar:Npn \@@_save_kerning:nNNNN #1#2#3#4#5
+  {
+    \dim_set:Nn \l_@@_tmp_dim
+      {
+          \l_@@_kerning_margin_dim
+        - ( \@@_use_punct_dim:nNN { bound } #2 #3 )
+      }
+    \@@_save_kerning_aux:nnNN {#1} { \l_@@_tmp_dim } #4 #5
+  }
+\cs_new_protected_nopar:Npn \@@_save_kerning_aux:nnNN #1#2#3#4
+  {
+    \@@_save_punct_dim:nNNn {#1} #3 #4 {#2}
+    \@@_save_punct_skip:nNNnnn {#1} #3 #4
+      {#2}
       { \l_@@_original_margin_dim - \l_@@_kerning_margin_dim }
       { \l_@@_kerning_margin_dim - \l_@@_minimum_bound_dim }
   }
@@ -5217,7 +5804,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_original_kerning_margin:NN}
+% \begin{macro}{\@@_original_kerning_margin:NN}
 % 相邻两个标点符号之间的本来空白宽度。
 %    \begin{macrocode}
 \cs_new_nopar:Npn \@@_original_kerning_margin:NN #1#2
@@ -5224,18 +5811,17 @@
   {
     \dim_eval:n
       {
-        \@@_use_punct_dim:nnn
-          { \@@_punct_if_right:NTF {#1} { glue } { bound } }
-          { \c_@@_right_tl } {#1} +
-        \@@_use_punct_dim:nnn
-          { \@@_punct_if_right:NTF {#2} { bound } { glue } }
-          { \c_@@_left_tl  } {#2}
+        \@@_use_punct_dim:nNN
+          { \@@_punct_if_right:NTF #1 { glue } { bound } } \c_@@_right_tl #1
+        +
+        \@@_use_punct_dim:nNN
+          { \@@_punct_if_right:NTF #2 { bound } { glue } } \c_@@_left_tl #2
       }
   }
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_calc_kerning_margin:NN}
+% \begin{macro}{\@@_calc_kerning_margin:NN,\@@_calc_kerning_margin_aux:NN}
 %    \begin{macrocode}
 \cs_new_nopar:Npn \@@_calc_kerning_margin:NN #1#2
   {
@@ -5248,36 +5834,38 @@
             \bool_if:NTF \l_@@_optimize_kerning_bool
               { \dim_max:nn { \l_@@_minimum_bound_dim } }
               { \use:n }
+              { \@@_calc_kerning_margin_aux:NN #1 #2 }
+          }
+      }
+  }
+\cs_new_nopar:Npn \@@_calc_kerning_margin_aux:NN #1#2
+  {
+    \dim_compare:nNnTF \l_@@_kerning_total_width_dim < \c_max_dim
+      { \@@_calc_kerning_margin:nNN \l_@@_kerning_total_width_dim }
+      {
+        \fp_compare:nNnTF \l_@@_kerning_total_ratio_fp ? \c_zero_fp
+          {
+            \xeCJK_if_same_class:NNTF #1 #2
+              { \@@_kerning_width_or_ratio:nNN { same } }
+              { \@@_kerning_width_or_ratio:nNN { different } }
+          }
+          {
+            \@@_calc_kerning_margin:nNN
               {
-                \dim_compare:nNnTF \l_@@_kerning_total_width_dim < \c_max_dim
-                  { \@@_calc_kerning_margin:nNN \l_@@_kerning_total_width_dim }
-                  {
-                    \fp_compare:nNnTF \l_@@_kerning_total_ratio_fp ? \c_zero_fp
-                      {
-                        \xeCJK_if_same_class:NNTF {#1} {#2}
-                          { \@@_kerning_width_or_ratio:nNN { same } }
-                          { \@@_kerning_width_or_ratio:nNN { different } }
-                      }
-                      {
-                        \@@_calc_kerning_margin:nNN
-                          {
-                            \fp_use:N \l_@@_kerning_total_ratio_fp
-                            \etex_dimexpr:D
-                              \@@_use_punct_dim:nn { width } {#1} +
-                              \@@_use_punct_dim:nn { width } {#2}
-                            \scan_stop:
-                          }
-                      }
-                  }
-                  {#1} {#2}
+                \fp_use:N \l_@@_kerning_total_ratio_fp
+                \etex_dimexpr:D
+                  \@@_use_punct_dim:nN { width } #1 +
+                  \@@_use_punct_dim:nN { width } #2
+                \scan_stop:
               }
           }
       }
+      #1 #2
   }
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_kerning_width_or_ratio:nNN}
+% \begin{macro}{\@@_kerning_width_or_ratio:nNN}
 %    \begin{macrocode}
 \cs_new_nopar:Npn \@@_kerning_width_or_ratio:nNN #1#2#3
   {
@@ -5297,7 +5885,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_punct_min_bound:NN}
+% \begin{macro}{\@@_punct_min_bound:NN}
 %    \begin{macrocode}
 \cs_new_nopar:Npn \@@_punct_min_bound:NN #1#2
   {
@@ -5304,19 +5892,19 @@
     \dim_max:nn
       {
         \dim_min:nn
-          { \@@_use_punct_dim:nnn { bound } \c_@@_left_tl  {#1} }
-          { \@@_use_punct_dim:nnn { bound } \c_@@_right_tl {#1} }
+          { \@@_use_punct_dim:nNN { bound } \c_@@_left_tl  #1 }
+          { \@@_use_punct_dim:nNN { bound } \c_@@_right_tl #1 }
       }
       {
         \dim_min:nn
-          { \@@_use_punct_dim:nnn { bound } \c_@@_left_tl  {#2} }
-          { \@@_use_punct_dim:nnn { bound } \c_@@_right_tl {#2} }
+          { \@@_use_punct_dim:nNN { bound } \c_@@_left_tl  #2 }
+          { \@@_use_punct_dim:nNN { bound } \c_@@_right_tl #2 }
       }
   }
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_calc_kerning_margin:nNN}
+% \begin{macro}{\@@_calc_kerning_margin:nNN}
 % |#2| 和 |#3| 为相邻的两个标点,|#1| 为要确定的相邻两个标点总共占的宽度。
 %    \begin{macrocode}
 \cs_new_nopar:Npn \@@_calc_kerning_margin:nNN #1#2#3
@@ -5323,21 +5911,21 @@
   {
     \dim_eval:n
       {
-        (#1)
-        - ( \@@_use_punct_dim:nnn
-              { \@@_punct_if_right:NTF {#2} { bound } { glue } }
-              { \c_@@_left_tl } {#2} )
-        - ( \@@_use_punct_dim:nnn
-              { \@@_punct_if_right:NTF {#3} { glue } { bound } }
-              { \c_@@_right_tl } {#3} )
-        - ( \@@_use_punct_dim:nn { dimen } {#2} )
-        - ( \@@_use_punct_dim:nn { dimen } {#3} )
+          (#1)
+        - ( \@@_use_punct_dim:nNN
+              { \@@_punct_if_right:NTF #2 { bound } { glue } }
+              \c_@@_left_tl #2 )
+        - ( \@@_use_punct_dim:nNN
+              { \@@_punct_if_right:NTF #3 { glue } { bound } }
+              \c_@@_right_tl #3 )
+        - ( \@@_use_punct_dim:nN { dimen } #2 )
+        - ( \@@_use_punct_dim:nN { dimen } #3 )
       }
   }
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\xeCJK_calc_punct_dimen:N}
+% \begin{macro}[int]{\xeCJK_calc_punct_dimen:N}
 % \changes{v3.4.3}{2016/10/27}{考虑破折号边界为负值的情况。}
 % 计算标点的左右实际边距和实际尺寸。对于破折号,计算两标点之间的空白,保证它中间
 % 不被断开。注意,破折号的边界可能为负值(比如方正新书宋),此时不必压缩。
@@ -5344,20 +5932,20 @@
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \xeCJK_calc_punct_dimen:N #1
   {
-    \@@_save_punct_dim:nnnn { bound } \c_@@_left_tl {#1}
-      { \xeCJK_glyph_bounds:NN \c_one {#1} }
-    \@@_save_punct_dim:nnnn { bound } \c_@@_right_tl {#1}
-      { \xeCJK_glyph_bounds:NN \c_three {#1} }
+    \@@_save_punct_dim:nNNn { bound } \c_@@_left_tl #1
+      { \xeCJK_glyph_bounds:NN \c_one #1 }
+    \@@_save_punct_dim:nNNn { bound } \c_@@_right_tl #1
+      { \xeCJK_glyph_bounds:NN \c_three #1 }
     \dim_set:Nn \l_@@_tmp_dim
       {
-        ( \@@_use_punct_dim:nnn { bound } \c_@@_left_tl  {#1} ) +
-        ( \@@_use_punct_dim:nnn { bound } \c_@@_right_tl {#1} )
+        ( \@@_use_punct_dim:nNN { bound } \c_@@_left_tl  #1 ) +
+        ( \@@_use_punct_dim:nNN { bound } \c_@@_right_tl #1 )
       }
-    \@@_save_punct_dim:nnn { width } {#1}
-      { \etex_fontcharwd:D \tex_font:D \xeCJK_token_value_charcode:N #1 }
-    \@@_save_punct_dim:nnn { dimen } {#1}
-      { \@@_use_punct_dim:nn { width } {#1} - \l_@@_tmp_dim }
-    \@@_punct_if_long:NT {#1}
+    \@@_save_punct_dim:nNn { width } #1
+      { \etex_fontcharwd:D \tex_font:D `#1 }
+    \@@_save_punct_dim:nNn { dimen } #1
+      { \@@_use_punct_dim:nN { width } #1 - \l_@@_tmp_dim }
+    \@@_punct_if_long:NT #1
       {
         \dim_set:Nn \l_@@_tmp_dim
           {
@@ -5366,22 +5954,18 @@
               { \c_zero_dim }
               { \dim_min:nn { - \l_@@_tmp_dim } { \c_zero_dim } }
           }
-        \@@_save_punct_dim:nnnn { kern } {#1} {#1} { \l_@@_tmp_dim }
-        \@@_save_punct_skip:nnnn { kern } {#1} {#1} { \l_@@_tmp_dim }
+        \@@_save_punct_dim:nNNn  { kern } #1 #1 { \l_@@_tmp_dim }
+        \@@_save_punct_skip:nNNn { kern } #1 #1 { \l_@@_tmp_dim }
       }
   }
-\cs_generate_variant:Nn \xeCJK_calc_punct_dimen:N { o }
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\xeCJK_glyph_bounds:NN}
+% \begin{macro}[int]{\xeCJK_glyph_bounds:NN}
 % 用 \tn{XeTeXglyphbounds} 取得标点符号的上下左右空白。
 %    \begin{macrocode}
 \cs_new_nopar:Npn \xeCJK_glyph_bounds:NN #1#2
-  {
-    \dim_use:N \xetex_glyphbounds:D #1 ~
-    \xetex_charglyph:D \xeCJK_token_value_charcode:N #2 \exp_stop_f:
-  }
+  { \xetex_glyphbounds:D #1 ~ \xetex_charglyph:D `#2 \exp_stop_f: }
 %    \end{macrocode}
 % \end{macro}
 %
@@ -5515,18 +6099,18 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\xeCJK_fallback_test_glyph:N}
+% \begin{macro}[int]{\xeCJK_fallback_test_glyph:N}
 % 测试当前字体中是否存在当前字符,如存在则直接输出,否则启用后备字体。
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \xeCJK_fallback_test_glyph:N #1
   {
-    \xeCJK_glyph_if_exist:NTF {#1}
-      { \@@_fallback_save_CJKsymbol:N {#1} }
+    \xeCJK_glyph_if_exist:NTF #1
+      { \@@_fallback_save_CJKsymbol:N #1 }
       {
         \group_begin:
-          \xeCJK_aftergroup_reset_Boundary:N {#1}
+          \xeCJK_aftergroup_reset_Boundary:N #1
           \tl_set_eq:NN \l_@@_fallback_family_tl \l_xeCJK_family_tl
-          \xeCJK_fallback_loop:Nn {#1} { \l_xeCJK_family_tl/FallBack }
+          \xeCJK_fallback_loop:No #1 { \l_xeCJK_family_tl/FallBack }
         \group_end:
       }
   }
@@ -5535,7 +6119,7 @@
 %
 % \changes{v3.5.1}{2017/11/16}{修正 fallback 字体后无法忽略空格的错误。}
 %
-% \begin{macro}[internal]{\xeCJK_aftergroup_reset_Boundary:N}
+% \begin{macro}[int]{\xeCJK_aftergroup_reset_Boundary:N}
 % 在分组中暂时清空 |#1| 与边界的 toks,分组后恢复。
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \xeCJK_aftergroup_reset_Boundary:N #1
@@ -5553,7 +6137,7 @@
 %
 % \changes{v3.2.12}{2014/05/12}{更新 \cs{int_to_Hex:n}。}
 %
-% \begin{macro}[internal]{\xeCJK_fallback_loop:Nn}
+% \begin{macro}[int]{\xeCJK_fallback_loop:Nn}
 % \changes{v3.1.0}{2012/11/19}{调整备用字体的循环方式。}
 % \changes{v3.2.4}{2013/06/30}
 % {使 \tn{CJKfamilydefault} 的 \texttt{FallBack} 设置全局可用。}
@@ -5562,14 +6146,14 @@
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \xeCJK_fallback_loop:Nn #1#2
   {
-    \xeCJK_family_if_exist:xTF {#2}
+    \xeCJK_family_if_exist:nTF {#2}
       {
-        \tl_set:Nx \l_xeCJK_family_tl {#2}
+        \tl_set:Nn \l_xeCJK_family_tl {#2}
         \tl_set_eq:NN \CJK at family \l_@@_fontspec_family_tl
         \xeCJK_select_font:
-        \xeCJK_glyph_if_exist:NTF {#1}
-          { \@@_fallback_save_CJKsymbol:N {#1} }
-          { \xeCJK_fallback_loop:Nn {#1} { \l_xeCJK_family_tl/FallBack } }
+        \xeCJK_glyph_if_exist:NTF #1
+          { \@@_fallback_save_CJKsymbol:N #1 }
+          { \xeCJK_fallback_loop:No #1 { \l_xeCJK_family_tl/FallBack } }
       }
       {
         \str_if_eq_x:nnTF { \CJKfamilydefault } { \l_@@_fallback_family_tl }
@@ -5577,14 +6161,15 @@
             \@@_warning:nxxx { missing-glyph }
               { \l_xeCJK_family_tl } {#1}
               { \int_to_Hex:n { `#1 } }
-            \@@_fallback_save_CJKsymbol:N {#1}
+            \@@_fallback_save_CJKsymbol:N #1
           }
           {
             \tl_set:Nx \l_@@_fallback_family_tl { \CJKfamilydefault }
-            \xeCJK_fallback_loop:Nn {#1} { \l_@@_fallback_family_tl }
+            \xeCJK_fallback_loop:Nn #1 { \l_@@_fallback_family_tl }
           }
       }
   }
+\cs_generate_variant:Nn \xeCJK_fallback_loop:Nn { No }
 \@@_msg_new:nn { missing-glyph }
   {
     CJKfamily~`\@@_msg_family_map:n {#1}'~
@@ -5606,13 +6191,13 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[int]{\xeCJK_set_family_fallback:nnn,}
+% \begin{macro}[int]{\xeCJK_set_family_fallback:nnn}
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \xeCJK_set_family_fallback:nnn #1#2#3
   {
     \group_begin:
     \tl_set:Nn \l_@@_fallback_family_tl {#1}
-    \prop_get:NVNF \g_@@_family_font_name_prop
+    \prop_get:NoNF \g_@@_family_font_name_prop
       \l_@@_fallback_family_tl \l_@@_font_name_tl
       { \tl_clear:N \l_@@_font_name_tl }
     \clist_map_inline:nn {#3}
@@ -5672,7 +6257,8 @@
 %
 % \changes{v3.2.4}{2013/07/02}{内部调整分区字体的设置方法。}
 %
-% \begin{macro}[internal]{\xeCJK_new_sub_key:n,\g_@@_sub_key_seq}
+% \begin{macro}[int]{\xeCJK_new_sub_key:n}
+% \begin{variable}{\g_@@_sub_key_seq}
 % 用于定义 CJK 子区字体和备用字体的选项。
 %    \begin{macrocode}
 \seq_new:N \g_@@_sub_key_seq
@@ -5699,11 +6285,12 @@
       }
   }
 %    \end{macrocode}
+% \end{variable}
 % \end{macro}
 %
 % \changes{v3.2.4}{2013/07/02}{改进获取分区字体属性的办法。}
 %
-% \begin{macro}[internal]{\@@_get_sub_features:nn,\@@_get_sub_features:w}
+% \begin{macro}{\@@_get_sub_features:nn,\@@_get_sub_features:w}
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \@@_get_sub_features:nn #1#2
   {
@@ -5746,7 +6333,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{BoldFont,ItalicFont}
+% \begin{macro}[int]{BoldFont,ItalicFont}
 % 调用字体的属性声明,同 \pkg{fontspec} 宏包。
 %    \begin{macrocode}
 \keys_define:nn { xeCJK / features }
@@ -5760,7 +6347,7 @@
 % \changes{v3.2.6}{2013/07/31}{\texttt{AutoFakeBold} 和 \texttt{AutoFakeSlant}
 % 选项直接使用 \pkg{fontspec} 的设置,修正不能调用相应实际字体的问题。}
 %
-% \begin{macro}[internal]{AutoFakeBold, AutoFakeSlant}
+% \begin{macro}[int]{AutoFakeBold, AutoFakeSlant}
 %    \begin{macrocode}
 \keys_define:nn { xeCJK / features }
   {
@@ -5796,7 +6383,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_set_family_initial:}
+% \begin{macro}{\@@_set_family_initial:}
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \@@_set_family_initial:
   {
@@ -5820,7 +6407,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\xeCJK_set_family:nnn}
+% \begin{macro}[int]{\xeCJK_set_family:nnn}
 % 设置一个 CJK 新字体族,与 \tn{newfontfamily} 类似,增加 |FallBack| 选项。
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \xeCJK_set_family:nnn #1#2#3
@@ -5849,7 +6436,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_remove_duplicate_keys:N}
+% \begin{macro}{\@@_remove_duplicate_keys:N}
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \@@_remove_duplicate_keys:N #1
   {
@@ -5876,7 +6463,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_gset_family_cs:x}
+% \begin{macro}{\@@_gset_family_cs:x}
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \@@_gset_family_cs:x #1
   {
@@ -5898,7 +6485,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_check_family:n}
+% \begin{macro}{\@@_check_family:n}
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \@@_check_family:n #1
   {
@@ -5918,7 +6505,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_parse_font_shape:}
+% \begin{macro}{\@@_parse_font_shape:}
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \@@_parse_font_shape:
   {
@@ -5950,7 +6537,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal,var]
+% \begin{variable}
 %  {\g_@@_family_name_prop,\g_@@_family_font_name_prop,\g_@@_family_font_options_prop}
 %    \begin{macrocode}
 \prop_new:N \g_@@_family_name_prop
@@ -5957,9 +6544,9 @@
 \prop_new:N \g_@@_family_font_name_prop
 \prop_new:N \g_@@_family_font_options_prop
 %    \end{macrocode}
-% \end{macro}
+% \end{variable}
 %
-% \begin{macro}[internal]{\@@_save_family_info:}
+% \begin{macro}{\@@_save_family_info:}
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \@@_save_family_info:
   {
@@ -5971,7 +6558,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_set_sub_block_family:}
+% \begin{macro}{\@@_set_sub_block_family:}
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \@@_set_sub_block_family:
   {
@@ -5989,13 +6576,13 @@
 \cs_new_protected_nopar:Npn \@@_copy_sub_family:n #1
   {
     \@@_check_family:V \l_@@_sub_family_name_tl
-    \prop_get:NVNT \g_@@_family_font_name_prop
+    \prop_get:NoNT \g_@@_family_font_name_prop
       \l_@@_family_name_tl \l_@@_sub_font_name_tl
       {
         \prop_gput:NVV \g_@@_family_font_name_prop
           \l_@@_sub_family_name_tl \l_@@_sub_font_name_tl
       }
-    \prop_get:NVNT \g_@@_family_font_options_prop
+    \prop_get:NoNT \g_@@_family_font_options_prop
       \l_@@_family_name_tl \l_@@_sub_font_options_clist
       {
         \clist_remove_all:Nn \l_@@_sub_font_options_clist { #1 = * }
@@ -6016,7 +6603,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_copy_family:nn,\@@_copy_family:xx}
+% \begin{macro}{\@@_copy_family:nn,\@@_copy_family:xx}
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \@@_copy_family:nn #1#2
   {
@@ -6045,17 +6632,20 @@
 %
 % \subsection{字体切换}
 %
-% \begin{macro}[internal]{\l_xeCJK_current_font_tl,\xeCJK_select_font:}
+% \begin{macro}[int]{\xeCJK_select_font:}
+% \begin{variable}[int]{\l_xeCJK_current_font_tl}
 % 缓存当前字体的原始格式,以加速编译。
 %    \begin{macrocode}
+\cs_new_nopar:Npn \@@_font_csname:n #1
+  { xeCJK/#1/\f at series/\f at shape/\f at size }
 \tl_new:N \l_xeCJK_current_font_tl
-\tl_set:Nn \l_xeCJK_current_font_tl { \@@_font_csname:n { \CJK at family } }
-\cs_new_nopar:Npn \@@_font_csname:n #1 { xeCJK/#1/\f at series/\f at shape/\f at size }
+\tl_set:No \l_xeCJK_current_font_tl
+  { \@@_font_csname:n { \CJK at family } }
 \cs_new_protected_nopar:Npn \xeCJK_select_font:
   {
     \exp_args:Nc \cs_if_exist_use:NF { \l_xeCJK_current_font_tl }
       {
-        \@@_family_use:x { \l_xeCJK_family_tl }
+        \@@_family_use:n { \l_xeCJK_family_tl }
         \xeCJK_font_gset_to_current:c { \l_xeCJK_current_font_tl }
       }
   }
@@ -6062,9 +6652,34 @@
 \tl_new:N \l_@@_current_coor_tl
 \cs_new_eq:NN \xeCJK at setfont \xeCJK_select_font:
 %    \end{macrocode}
+% \end{variable}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_switch_font:nn}
+% \begin{macro}[int]{\xeCJK_select_punct_font:}
+% \begin{variable}[int]{\l_xeCJK_current_punct_font_tl}
+% 切换标点符号字体。
+%    \begin{macrocode}
+\cs_new_eq:NN \xeCJK_select_punct_font: \xeCJK_select_font:
+\cs_new_protected_nopar:Npn \@@_select_punct_font_aux:
+  {
+    \exp_args:Nc \cs_if_exist_use:NF { \l_xeCJK_current_punct_font_tl }
+      {
+        \@@_family_use:n { \l_xeCJK_punct_family_tl }
+        \xeCJK_font_gset_to_current:c { \l_xeCJK_current_punct_font_tl }
+      }
+  }
+\tl_new:N \CJK at punctfamily
+\tl_new:N \l_xeCJK_punct_family_tl
+\tl_new:N \l_xeCJK_current_punct_font_tl
+\tl_set:No \l_xeCJK_current_punct_font_tl
+  { \@@_font_csname:n { \CJK at punctfamily } }
+\cs_new_eq:NN \@@_select_font: \prg_do_nothing:
+\cs_new_eq:NN \@@_select_punct_font: \prg_do_nothing:
+%    \end{macrocode}
+% \end{variable}
+% \end{macro}
+%
+% \begin{macro}{\@@_switch_font:nn}
 % \changes{v3.1.0}{2012/11/18}{改进定义,加快切换速度。}
 % 两个 CJK 分区之间的字体切换。
 %    \begin{macrocode}
@@ -6082,7 +6697,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\xeCJK_select_font:n,\xeCJK_block_family:nn}
+% \begin{macro}[int]{\xeCJK_select_font:n,\xeCJK_block_family:nn}
 % 若当前 CJK 字体族没有定义子分区 |#1| 的字体,则使用 \tn{CJKfamilydefault} 的对应
 % 分区字体;若 \tn{CJKfamilydefault} 也没有定义该分区字体,则使用当前 CJK 字体族的
 % 主分区字体。
@@ -6089,10 +6704,11 @@
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \xeCJK_select_font:n #1
   {
-    \exp_args:Nc \cs_if_exist_use:NF { \@@_font_csname:n { \CJK at family/#1 } }
+    \exp_args:Nc \cs_if_exist_use:NF
+      { \@@_font_csname:n { \CJK at family/#1 } }
       {
         \xeCJK_block_family:nn { \l_xeCJK_family_tl } {#1}
-        \@@_family_use:x { \l_xeCJK_family_tl/#1 }
+        \@@_family_use:n { \l_xeCJK_family_tl/#1 }
         \xeCJK_font_gset_to_current:c
           { \@@_font_csname:n { \CJK at family/#1 } }
       }
@@ -6112,41 +6728,45 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]
+% \begin{macro}
 %   {\@@_family_csname:n,\@@_family_nfss_csname:n,
-%    \@@_family_use:x,\@@_gset_family_nfss_cs:xx}
+%    \@@_family_use:n,\@@_gset_family_nfss_cs:nn}
 %    \begin{macrocode}
-\cs_new_nopar:Npn \@@_family_csname:n #1 { xeCJK/family/#1 }
-\cs_new_nopar:Npn \@@_family_nfss_csname:n #1 { xeCJK/family/nfss/#1 }
-\cs_new_nopar:Npn \@@_family_use:x #1 { \use:c { \@@_family_nfss_csname:n {#1} } }
-\cs_new_protected_nopar:Npn \@@_gset_family_nfss_cs:xx #1#2
+\cs_new_nopar:Npn \@@_family_csname:n #1
+  { xeCJK/family/#1 }
+\cs_new_nopar:Npn \@@_family_nfss_csname:n #1
+  { xeCJK/family/nfss/#1 }
+\cs_new_nopar:Npn \@@_family_use:n #1
+  { \use:c { \@@_family_nfss_csname:n {#1} } }
+\cs_new_protected_nopar:Npn \@@_gset_family_nfss_cs:nn #1#2
   {
-    \prop_gput:Nxx \g_@@_family_name_prop {#1} {#2}
-    \cs_gset_protected_nopar:cpx { \@@_family_nfss_csname:n {#1} }
+    \prop_gput:Nnn \g_@@_family_name_prop {#1} {#2}
+    \cs_gset_protected_nopar:cpx
+      { \@@_family_nfss_csname:n {#1} }
       {
         \exp_not:N \fontencoding { \c_@@_encoding_tl }
-        \tl_set:Nx \exp_not:N \f at family {#2}
+        \tl_set:Nn \exp_not:N \f at family {#2}
         \exp_not:N \selectfont
       }
   }
-\cs_generate_variant:Nn \prop_gput:Nnn { Nxx }
+\cs_generate_variant:Nn \@@_gset_family_nfss_cs:nn { xx }
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[TF,internal]{\xeCJK_family_if_exist:n}
+% \begin{macro}[TF,int]{\xeCJK_family_if_exist:n}
 %    \begin{macrocode}
 \prg_new_protected_conditional:Npnn \xeCJK_family_if_exist:n #1 { T , F , TF }
   {
-    \prop_get:NnNTF \g_@@_family_name_prop {#1} \l_@@_fontspec_family_tl
+    \prop_get:NnNTF \g_@@_family_name_prop
+      {#1} \l_@@_fontspec_family_tl
       { \prg_return_true: }
       {
-        \exp_args:Nc \cs_if_exist_use:NTF { \@@_family_csname:n {#1} }
-          { \prg_return_true: } { \prg_return_false: }
+        \cs_if_exist_use:cTF { \@@_family_csname:n {#1} }
+          { \prg_return_true: }
+          { \prg_return_false: }
       }
   }
-\cs_generate_variant:Nn \xeCJK_family_if_exist:nT  { x }
-\cs_generate_variant:Nn \xeCJK_family_if_exist:nF  { x }
-\cs_generate_variant:Nn \xeCJK_family_if_exist:nTF { x }
+\prg_generate_conditional_variant:Nnn \xeCJK_family_if_exist:n { x } { T , F , TF }
 %    \end{macrocode}
 % \end{macro}
 %
@@ -6155,54 +6775,97 @@
 %    \begin{macrocode}
 \NewDocumentCommand \CJKfamily { t+ t- m }
   {
-    \xeCJK_if_blank_x:nTF {#3}
+    \xeCJK_family:NNx #1 #2 {#3}
+    \tex_ignorespaces:D
+  }
+\cs_new_protected_nopar:Npn \xeCJK_family:NNn #1#2#3
+  {
+    \tl_if_blank:nTF {#3}
       {
-        \IfBooleanF {#1} { \IfBooleanF {#2} { \use_none:nn } }
+        \bool_if:NF #1 { \bool_if:NF #2 { \use_none:nn } }
         \xeCJK_family_if_exist_use:x { \l_xeCJK_family_tl }
       }
       {
-        \IfBooleanTF {#2} { \xeCJK_family_if_exist_use:x {#3} }
+        \bool_if:NTF #2
+          { \xeCJK_family_if_exist_use:n {#3} }
           {
-            \xeCJK_family_if_exist:xTF {#3}
+            \xeCJK_family_if_exist:nTF {#3}
               {
-                \tl_set:Nx \l_xeCJK_family_tl {#3}
+                \tl_set:Nn \l_xeCJK_family_tl {#3}
                 \tl_set_eq:NN \CJK at family \l_@@_fontspec_family_tl
-                \IfBooleanT {#1} { \@@_family_use:x {#3} }
+                \bool_if:NT #1 { \@@_family_use:n {#3} }
               }
-              { \@@_family_unknown_warning:x {#3} }
+              { \@@_family_unknown_warning:n {#3} }
           }
       }
-    \tex_ignorespaces:D
   }
+\cs_generate_variant:Nn \xeCJK_family:NNn { NNx }
 \cs_new_protected_nopar:Npn \xeCJK_switch_family:n #1
   {
-    \xeCJK_family_if_exist:xTF {#1}
+    \xeCJK_family_if_exist:nTF {#1}
       {
-        \tl_set:Nx \l_xeCJK_family_tl {#1}
+        \tl_set:Nn \l_xeCJK_family_tl {#1}
         \tl_set_eq:NN \CJK at family \l_@@_fontspec_family_tl
       }
-      { \@@_family_unknown_warning:x {#1} }
+      { \@@_family_unknown_warning:n {#1} }
   }
+\cs_generate_variant:Nn \xeCJK_switch_family:n { x , o }
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[var,internal]{\l_xeCJK_family_tl,\CJK at family}
+% \changes{v3.6.0}{2018/01/24}{新增 \opt{PunctFamily} 选项支持对汉字标点单独切换字体。}
+%
+% \begin{macro}{PunctFamily}
+% 设置汉字标点符号的字体。
+%    \begin{macrocode}
+\keys_define:nn { xeCJK / options }
+  {
+    PunctFamily .choice: ,
+    PunctFamily .value_required:n = { true } ,
+    PunctFamily / false   .code:n =
+      {
+        \tl_clear:N \l_xeCJK_punct_family_tl
+        \tl_clear:N \CJK at punctfamily
+        \xeCJK_cs_clear:N \@@_select_font:
+        \xeCJK_cs_clear:N \@@_select_punct_font:
+        \cs_set_eq:NN \xeCJK_select_punct_font: \xeCJK_select_font:
+      } ,
+    PunctFamily / unknown .code:n =
+      { \xeCJK_punct_family:x {#1} } ,
+  }
+\cs_new_protected_nopar:Npn \xeCJK_punct_family:n #1
+  {
+    \xeCJK_family_if_exist:nTF {#1}
+      {
+        \tl_set:Nn \l_xeCJK_punct_family_tl {#1}
+        \tl_set_eq:NN \CJK at punctfamily \l_@@_fontspec_family_tl
+        \cs_set_eq:NN \@@_select_font: \xeCJK_select_font:
+        \cs_set_eq:NN \@@_select_punct_font: \@@_select_punct_font_aux:
+        \cs_set_eq:NN \xeCJK_select_punct_font: \@@_select_punct_font:
+      }
+      { \@@_family_unknown_warning:n {#1} }
+  }
+\cs_generate_variant:Nn \xeCJK_punct_family:n { x }
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{variable}[int]{\l_xeCJK_family_tl}
 % 用于保存文档当前正在使用的 CJK 字体族。
 % \changes{v3.2.0}{2013/04/14}{不将其初始化为 \tn{CJKfamilydefault}。}
 %    \begin{macrocode}
 \tl_new:N \l_xeCJK_family_tl
 %    \end{macrocode}
-% \end{macro}
+% \end{variable}
 %
-% \begin{macro}[var,internal]{\CJK at family}
+% \begin{variable}{\CJK at family}
 % 用于保存实际的字体族名称。
 % \changes{v3.2.11}{2014/03/29}{引入 \tn{CJK at family} 保存实际的字体族名。}
 %    \begin{macrocode}
 \tl_new:N \CJK at family
 %    \end{macrocode}
-% \end{macro}
+% \end{variable}
 %
-% \begin{macro}[internal]{\@@_gobble_CJKfamily:}
+% \begin{macro}{\@@_gobble_CJKfamily:}
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \@@_gobble_CJKfamily:
   { \cs_set_eq:NN \CJKfamily \@@_gobble_CJKfamily:wn }
@@ -6210,18 +6873,19 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\xeCJK_family_if_exist_use:x}
+% \begin{macro}[int]{\xeCJK_family_if_exist_use:n}
 %    \begin{macrocode}
-\cs_new_protected_nopar:Npn \xeCJK_family_if_exist_use:x #1
+\cs_new_protected_nopar:Npn \xeCJK_family_if_exist_use:n #1
   {
-    \xeCJK_family_if_exist:xTF {#1}
-      { \@@_family_use:x {#1} }
-      { \@@_family_unknown_warning:x {#1} }
+    \xeCJK_family_if_exist:nTF {#1}
+      { \@@_family_use:n {#1} }
+      { \@@_family_unknown_warning:n {#1} }
   }
+\cs_generate_variant:Nn \xeCJK_family_if_exist_use:n { x }
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_family_unknown_warning:n}
+% \begin{macro}{\@@_family_unknown_warning:n}
 % \changes{v3.1.2}{2013/01/01}
 % {在没有定义任何 CJK 字体的情况下,不再重复给出字体没有定义的警告。}
 %    \begin{macrocode}
@@ -6267,13 +6931,13 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[int]{\@@_pass_args:nnnn}
+% \begin{macro}{\@@_pass_args:nnnn}
 % 为了支持字体属性可选项在前在后两种语法,给出两个辅助工具,类似
 % \package{fontspec} 的实现。自带展开功能,额外参数 |#4| 用于后处理。
 %    \begin{macrocode}
 \cs_new_protected:Npn \@@_pass_args:nnnn #1#2#3#4
   {
-    \IfNoValueTF {#2}
+    \tl_if_novalue:nTF {#2}
       { \@@_post_arg:w {#1} {#3} {#4} }
       {
         \use:x { #1 {#2} {#3} }
@@ -6335,8 +6999,10 @@
   }
 \NewDocumentCommand \newCJKfontfamily { o m o m }
   {
-    \tl_set:Nx \l_@@_tmp_tl { \IfNoValueTF {#1} { \cs_to_str:N #2 } {#1} }
-    \cs_new_protected_nopar:Npx #2 { \xeCJK_switch_family:n { \l_@@_tmp_tl } }
+    \tl_set:Nx \l_@@_tmp_tl
+      { \tl_if_novalue:nTF {#1} { \cs_to_str:N #2 } {#1} }
+    \cs_new_protected_nopar:Npx #2
+      { \xeCJK_switch_family:n { \l_@@_tmp_tl } }
     \@@_pass_args:nnnn
       { \xeCJK_set_family:nnn { \l_@@_tmp_tl } } {#3} {#4}
       { }
@@ -6350,13 +7016,13 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\xeCJK_fontspec:nn}
+% \begin{macro}[int]{\xeCJK_fontspec:nn}
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \xeCJK_fontspec:nn #1#2
   {
     \prop_get:NnNTF \g_@@_fontspec_prop
       { CJKfontspec/#1/#2/id } \l_xeCJK_family_tl
-      { \xeCJK_switch_family:n { \l_xeCJK_family_tl } }
+      { \xeCJK_switch_family:o { \l_xeCJK_family_tl } }
       {
         \@@_fontspec:xnn
           { CJKfontspec ( \int_eval:n { \g_@@_family_int + \c_one } ) }
@@ -6392,11 +7058,11 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\xeCJK_add_font_features:Nnn}
+% \begin{macro}[int]{\xeCJK_add_font_features:Nnn}
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \xeCJK_add_font_features:Nnn #1#2#3
   {
-    \prop_get:NVNTF \g_@@_family_font_name_prop
+    \prop_get:NoNTF \g_@@_family_font_name_prop
       \l_xeCJK_family_tl \l_@@_font_name_tl
       {
         \clist_set:Nn \l_@@_add_font_features_clist {#3}
@@ -6420,7 +7086,7 @@
             \seq_map_function:NN
               \g_@@_sub_key_seq \@@_add_sub_class_features:n
           }
-        \prop_get:NVNT \g_@@_family_font_options_prop
+        \prop_get:NoNT \g_@@_family_font_options_prop
           \l_xeCJK_family_tl \l_@@_font_options_clist
           {
             \bool_lazy_or:nnT
@@ -6448,7 +7114,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_add_sub_class_features:n}
+% \begin{macro}{\@@_add_sub_class_features:n}
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \@@_add_sub_class_features:n #1
   {
@@ -6466,7 +7132,7 @@
               { \CJKfamilydefault/#1 } \l_@@_sub_font_options_clist
           }
           {
-            \prop_get:NVN \g_@@_family_font_options_prop
+            \prop_get:NoN \g_@@_family_font_options_prop
               \l_xeCJK_family_tl \l_@@_sub_font_options_clist
             \tl_set_eq:NN \l_@@_sub_font_name_tl \l_@@_font_name_tl
           }
@@ -6482,8 +7148,8 @@
           }
       }
   }
-\cs_generate_variant:Nn \prop_get:NnN   { Nx }
-\cs_generate_variant:Nn \prop_get:NnNTF { Nx }
+\cs_generate_variant:Nn \prop_get:NnN { Nx }
+\prg_generate_conditional_variant:Nnn \prop_get:NnN { Nx } { TF }
 %    \end{macrocode}
 % \end{macro}
 %
@@ -6566,7 +7232,7 @@
         \@@_warning:nxx { CJKfamilydefault-undefined }
           { \l_@@_tmp_tl } { \CJKfamilydefault }
       }
-    \xeCJK_switch_family:n { \CJKfamilydefault }
+    \xeCJK_switch_family:x { \CJKfamilydefault }
     \bool_if:NT \g_@@_math_bool { \xeCJK_set_mathfont: }
   }
 \@@_msg_new:nn { no-CJKfamily }
@@ -6612,7 +7278,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\xeCJK_set_mathfont:}
+% \begin{macro}[int]{\xeCJK_set_mathfont:}
 % \changes{v3.2.6}{2013/08/01}{设置粗体时先检查对应字体是否存在。}
 % \changes{v3.2.7}{2013/11/09}{将 CJK 字符的数学归类由 $7$ 改为 $0$,解决汉字路径的问题。}
 % \changes{v3.2.13}{2014/06/20}{修复参数类型错误。}
@@ -6633,14 +7299,19 @@
   }
 \cs_new_protected_nopar:Npn \@@_set_mathfont_aux:
   {
-    \tl_const:Nx \c_@@_math_family_tl { \l_@@_fontspec_family_tl }
-    \xeCJK_declare_mathfont:nn { \c_@@_math_tl } { \c_@@_math_family_tl }
-    \int_const:Nn \c_xeCJK_math_fam_int { \use:c { sym \c_@@_math_tl } }
-    \clist_concat:NNN \g_@@_math_chars_clist
+    \tl_const:Nx \c_@@_math_family_tl
+      { \l_@@_fontspec_family_tl }
+    \xeCJK_declare_mathfont:xx
+      { \c_@@_math_tl }
+      { \c_@@_math_family_tl }
+    \int_const:Nn \c_xeCJK_math_fam_int
+      { \use:c { sym \c_@@_math_tl } }
+    \clist_gconcat:NNN \g_@@_math_chars_clist
       \g_@@_CJK_range_clist \g_@@_FullLeft_range_clist
-    \clist_concat:NNN \g_@@_math_chars_clist
+    \clist_gconcat:NNN \g_@@_math_chars_clist
       \g_@@_math_chars_clist \g_@@_FullRight_range_clist
-    \xeCJK_gset_mathcode:Nn \g_@@_math_chars_clist { \c_xeCJK_math_fam_int }
+    \xeCJK_gset_mathcode:Nn \g_@@_math_chars_clist
+      { \c_xeCJK_math_fam_int }
     \xeCJK_set_mathfont_block:
   }
 \clist_new:N \g_@@_math_chars_clist
@@ -6650,7 +7321,7 @@
 %
 % \changes{v3.4.0}{2016/05/04}{CJKmath 功能也支持分区字体。}
 %
-% \begin{macro}[internal]{\xeCJK_set_mathfont_block:}
+% \begin{macro}[int]{\xeCJK_set_mathfont_block:}
 % 分区数学字体。
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \xeCJK_set_mathfont_block:
@@ -6666,12 +7337,14 @@
   {
     \xeCJK_block_family:nn { \c_@@_math_tl } {#1}
     \prop_get:NoNTF \g_@@_fam_prop
-      { \l_@@_fontspec_family_tl } \l_@@_tmp_tl
+      \l_@@_fontspec_family_tl \l_@@_tmp_tl
       { \int_set:Nn \l_@@_fam_int { \l_@@_tmp_tl } }
       {
-        \xeCJK_declare_mathfont:nn
-          { \c_@@_math_tl / #1 } { \l_@@_fontspec_family_tl }
-        \@@_set_mathfont_block_aux:cn { sym \c_@@_math_tl / #1 } {#1}
+        \xeCJK_declare_mathfont:xx
+          { \c_@@_math_tl / #1 }
+          { \l_@@_fontspec_family_tl }
+        \@@_set_mathfont_block_aux:cn
+          { sym \c_@@_math_tl / #1 } {#1}
       }
     \xeCJK_gset_mathcode:cn { g_@@_CJK/#1_range_clist } { \l_@@_fam_int }
   }
@@ -6686,7 +7359,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\xeCJK_declare_mathfont:nn}
+% \begin{macro}[int]{\xeCJK_declare_mathfont:nn}
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \xeCJK_declare_mathfont:nn #1#2
   {
@@ -6698,12 +7371,14 @@
         \SetSymbolFont {#1} { bold } { \c_@@_encoding_tl }
           {#2} { \bfdefault } { \updefault }
       }
-    \prop_gput:Nxx \g_@@_fam_prop {#2} { \exp_not:c { sym #1 } }
+    \prop_gput:Nnx \g_@@_fam_prop {#2} { \exp_not:c { sym #1 } }
   }
+\cs_generate_variant:Nn \prop_gput:Nnn { Nnx }
+\cs_generate_variant:Nn \xeCJK_declare_mathfont:nn { xx }
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\xeCJK_declare_symbol_font:nnnnn}
+% \begin{macro}[int]{\xeCJK_declare_symbol_font:nnnnn}
 % 主要功能同 \tn{DeclareSymbolFont},不带编码和重复定义检查。
 %    \begin{macrocode}
 \cs_new_protected:Npn \xeCJK_declare_symbol_font:nnnnn #1
@@ -6717,7 +7392,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\xeCJK_new_fam:N}
+% \begin{macro}[int]{\xeCJK_new_fam:N}
 % 我们从 $255$ 往下分配 \tn{fam},|\count18| 是 \LaTeXe{} 记录最后分配的 \tn{fam} 编号,
 % 作为我们的分配器的下限。事实上,还应该相应地减小 \tn{e at mathgroup@top} 才合理,但这可能会有不利影响,
 % 我们暂未处理。
@@ -6746,8 +7421,8 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\xeCJK_new_symbol_font:Nnnnn}
-% \begin{macro}[aux]{\@@_new_symbol_font:NN}
+% \begin{macro}[int]{\xeCJK_new_symbol_font:Nnnnn}
+% \begin{macro}{\@@_new_symbol_font:NN}
 % 功能同 \tn{new at symbolfont},但我们不增加 \tn{c at mv@normal} 和 \tn{c at mv@bold} 之类的计数器。
 %    \begin{macrocode}
 \cs_new_protected:Npn \xeCJK_new_symbol_font:Nnnnn #1#2#3#4#5
@@ -6764,7 +7439,7 @@
 % \end{macro}
 % \end{macro}
 %
-% \begin{macro}[internal]{\xeCJK_gset_mathcode:Nn,
+% \begin{macro}[int]{\xeCJK_gset_mathcode:Nn,
 %   \xeCJK_gset_mathcode:Nnn,\xeCJK_gset_mathcode:nnnn}
 % CJK 字符的数学类别固定为 $0$(\tn{mathord})。
 %    \begin{macrocode}
@@ -6837,7 +7512,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_nobreak_skip_zero:,\@@_nobreak_skip:}
+% \begin{macro}{\@@_nobreak_skip_zero:,\@@_nobreak_skip:}
 % \changes{v3.2.8}{2013/11/16}{禁止在 \tn{verb} 中断行。}
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \@@_nobreak_skip_zero:
@@ -6875,12 +7550,10 @@
   { \xeCJK_no_break: \skip_horizontal:N \l_@@_ccglue_skip }
 \cs_new_protected_nopar:Npn \@@_nobreak_ecglue:
   { \xeCJK_no_break: \skip_horizontal:N \l_@@_ecglue_skip }
-\cs_new_protected_nopar:Npn \@@_nobreak_hskip:n
-  { \xeCJK_no_break: \skip_horizontal:n }
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_reset_shipout_skip:}
+% \begin{macro}{\@@_reset_shipout_skip:}
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \@@_reset_shipout_skip:
   {
@@ -6899,8 +7572,8 @@
             \cs_set_eq:NN \CJKglue \@@_shipout_CJKglue:
             \cs_set_eq:NN \CJKecglue \@@_shipout_CJKecglue:
             \cs_set_eq:NN \@@_punct_hskip:n \@@_shipout_punct_hskip:n
-            \cs_set_eq:NN
-              \@@_punct_breakable_kern:n \@@_shipout_punct_breakable_kern:n
+            \cs_set_eq:NN \@@_punct_breakable_kern:n
+                          \@@_shipout_punct_breakable_kern:n
             \l_@@_reset_shipout_skip_hook_tl
           }
       }
@@ -7011,7 +7684,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_set_verb_exspace:}
+% \begin{macro}{\@@_set_verb_exspace:}
 % \changes{v3.1.1}{2012/12/08}{调整间距的计算方法。}
 % \changes{v3.2.4}{2013/06/29}{当计算得出的间距为负时,缩小 CJK 字体。}
 % 在抄录环境中,CJK 文字之间的间距为当前西文字体两个空格的宽度与当前字体大小之差,
@@ -7026,10 +7699,10 @@
       }
       {
         \tl_set:Nx \l_@@_current_coor_tl { \CJK at family/\curr at fontshape }
-        \prop_get:NVNTF \g_@@_scale_family_prop
+        \prop_get:NoNTF \g_@@_scale_family_prop
           \l_@@_current_coor_tl \l_xeCJK_family_tl
           {
-            \xeCJK_switch_family:n { \l_xeCJK_family_tl }
+            \xeCJK_switch_family:o { \l_xeCJK_family_tl }
             \skip_zero:N \l_@@_verb_exspace_skip
           }
           {
@@ -7043,7 +7716,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_set_verb_exspace:n}
+% \begin{macro}{\@@_set_verb_exspace:n}
 % 当两个西文空格的宽度小于一个 CJK 文字的宽度时,对目前使用的 CJK 字体进行适当缩小。
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \@@_set_verb_exspace:n #1
@@ -7068,7 +7741,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_set_verb_scale:nn}
+% \begin{macro}{\@@_set_verb_scale:nn}
 % 缩小 CJK 字体,并保存相关信息。
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \@@_set_verb_scale:nn #1#2
@@ -7096,7 +7769,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\xeCJK_visible_space:}
+% \begin{macro}[int]{\xeCJK_visible_space:}
 % \changes{v3.2.5}{2013/07/13}{可视空格考虑传统 \TeX 字体的情况。}
 % 如果文档不使用 \texttt{EU1} 作为默认字体编码,那么默认的打字机字体族很可能是
 % 传统的 \TeX 字体,这时可视空格按照 \texttt{OT1} 编码传统一般就是字体中的 |\char32|。
@@ -7123,7 +7796,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\xeCJK_visible_space_fallback:}
+% \begin{macro}[int]{\xeCJK_visible_space_fallback:}
 % \changes{v3.1.0}{2012/11/19}
 % {调整 \pkg{fontspec} 的后备可视空格符号,以便于使用时对齐。}
 % \pkg{fontspec} 使用 |lmtt| 字体中的可视空格符号(|U+2423|)作为当前字体中相应符号
@@ -7139,13 +7812,14 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\xeCJK_set_visible_space_font:}
+% \begin{macro}[int]{\xeCJK_set_visible_space_font:}
 % 当前字体空格的宽度与后备字体 |lmtt| 不一样时,就对 \tn{textvisiblespace} 的字体尺寸
 % 按相应的比例放缩。
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \xeCJK_set_visible_space_font:
   {
-    \tl_set:Nx \l_@@_current_coor_tl { xeCJK/space/\curr at fontshape/\f at size }
+    \tl_set:Nx \l_@@_current_coor_tl
+      { xeCJK/space/\curr at fontshape/\f at size }
     \exp_after:wN \@@_set_visible_space_size:n
     \exp_after:wN { \dim_use:N \tex_fontdimen:D \c_two \tex_font:D }
     \xeCJK_font_gset_to_current:c { \l_@@_current_coor_tl }
@@ -7222,7 +7896,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{quiet,silent}
+% \begin{macro}[int]{quiet,silent}
 % 将调用 \pkg{xeCJK} 时使用的未知的选项传递给 \pkg{fontspec} 宏包。
 % 对 \pkg{fontspec} 的 |quiet| 和 |silent| 选项进行修改,使其适用于 \pkg{xeCJK}。
 %    \begin{macrocode}
@@ -7251,7 +7925,7 @@
   }
 \@@_msg_new:nn { key-unknown }
   {
-    Sorry,~but~\l__keys_module_tl \ does~not~have~a~key~called~`#1'.\\\\
+    Sorry,~but~xeCJK/options~does~not~have~a~key~called~`#1'.\\\\
     The~key~`#1'~is~being~ignored.
   }
 %    \end{macrocode}
@@ -7259,7 +7933,7 @@
 %
 % \subsection{\pkg{xeCJK} 初始化设置}
 %
-% \begin{macro}[internal]{\CJKsymbol, \CJKpunctsymbol}
+% \begin{macro}[int]{\CJKsymbol, \CJKpunctsymbol}
 %    \begin{macrocode}
 \cs_new_nopar:Npn \CJKsymbol      #1 {#1}
 \cs_new_nopar:Npn \CJKpunctsymbol #1 {#1}
@@ -7292,8 +7966,8 @@
     WidowPenalty    = \c_ten_thousand ,
     NoBreakCS       = { \footnote \footnotemark \nobreak } ,
     KaiMingPunct    = { ^^^^3002 ^^^^ff0e ^^^^ff1f ^^^^ff01 } ,
-    LongPunct       = { ^^^^2014 ^^^^2025 ^^^^2026 } ,
-    MiddlePunct     = { ^^^^2013 ^^^^2014 ^^^^2027 ^^^^00b7 ^^^^30fb ^^^^ff65 } ,
+    LongPunct       = { ^^^^2014 ^^^^2e3a ^^^^2025 ^^^^2026 } ,
+    MiddlePunct     = { ^^^^2013 ^^^^2014 ^^^^2e3a ^^^^2027 ^^^^00b7 ^^^^30fb ^^^^ff65 } ,
     AllowBreakBetweenPuncts = false
   }
 \defaultCJKfontfeatures { Script = CJK }
@@ -7312,12 +7986,12 @@
 \RequirePackage { xunicode-addon }
 %    \end{macrocode}
 %
-% \begin{macro}[internal,var]{\c_@@_encoding_tl}
+% \begin{variable}{\c_@@_encoding_tl}
 % 保存 \pkg{fontspec} 声明字体时使用的字体编码。
 %    \begin{macrocode}
 \tl_const:Nx \c_@@_encoding_tl { \g_fontspec_encoding_tl }
 %    \end{macrocode}
-% \end{macro}
+% \end{variable}
 %
 % \changes{v3.1.0}{2012/11/21}{改用 \pkg{indentfirst} 宏包处理缩进的问题。}
 %
@@ -7336,7 +8010,7 @@
   }
 %    \end{macrocode}
 %
-% \begin{macro}[var]{\CJKrmdefault,\CJKsfdefault,\CJKttdefault,\CJKfamilydefault}
+% \begin{variable}{\CJKrmdefault,\CJKsfdefault,\CJKttdefault,\CJKfamilydefault}
 %    \begin{macrocode}
 \tl_if_exist:NF \CJKrmdefault { \tl_gset:Nn \CJKrmdefault { rm } }
 \tl_if_exist:NF \CJKsfdefault { \tl_gset:Nn \CJKsfdefault { sf } }
@@ -7354,7 +8028,7 @@
   }
 \tl_gset_eq:NN \CJKfamilydefault \l_@@_family_default_init_tl
 %    \end{macrocode}
-% \end{macro}
+% \end{variable}
 %
 % \begin{macro}{\xeCJKsetup}
 % 在导言区或文档中设置 \pkg{xeCJK} 的接口。
@@ -7367,7 +8041,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\xeCJKsetemboldenfactor, \xeCJKsetslantfactor}
+% \begin{macro}[int]{\xeCJKsetemboldenfactor, \xeCJKsetslantfactor}
 %    \begin{macrocode}
 \NewDocumentCommand \xeCJKsetemboldenfactor { m }
   { \xeCJKsetup { EmboldenFactor = {#1} } }
@@ -7376,7 +8050,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\punctstyle, \xeCJKplainchr}
+% \begin{macro}[int]{\punctstyle, \xeCJKplainchr}
 %    \begin{macrocode}
 \NewDocumentCommand \punctstyle { m } { \xeCJKsetup { PunctStyle = {#1} } }
 \NewDocumentCommand \xeCJKplainchr { } { \xeCJKsetup { PunctStyle = plain } }
@@ -7383,7 +8057,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\CJKsetecglue}
+% \begin{macro}[int]{\CJKsetecglue}
 %    \begin{macrocode}
 \NewDocumentCommand \CJKsetecglue { m } { \xeCJKsetup { CJKecglue = {#1} } }
 \cs_new_eq:NN \xeCJKsetecglue \CJKsetecglue
@@ -7390,7 +8064,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\CJKspace,\CJKnospace}
+% \begin{macro}[int]{\CJKspace,\CJKnospace}
 %    \begin{macrocode}
 \NewDocumentCommand \CJKspace   { } { \xeCJKsetup { CJKspace = true } }
 \NewDocumentCommand \CJKnospace { } { \xeCJKsetup { CJKspace = false } }
@@ -7397,7 +8071,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\xeCJKallowbreakbetweenpuncts, \xeCJKnobreakbetweenpuncts}
+% \begin{macro}[int]{\xeCJKallowbreakbetweenpuncts, \xeCJKnobreakbetweenpuncts}
 %    \begin{macrocode}
 \NewDocumentCommand \xeCJKallowbreakbetweenpuncts { }
   { \xeCJKsetup { AllowBreakBetweenPuncts = true } }
@@ -7406,7 +8080,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\xeCJKenablefallback, \xeCJKdisablefallback}
+% \begin{macro}[int]{\xeCJKenablefallback, \xeCJKdisablefallback}
 %    \begin{macrocode}
 \NewDocumentCommand \xeCJKenablefallback { }
   { \xeCJKsetup { AutoFallBack = true } }
@@ -7415,7 +8089,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\xeCJKsetcharclass}
+% \begin{macro}[int]{\xeCJKsetcharclass}
 %    \begin{macrocode}
 \NewDocumentCommand \xeCJKsetcharclass { m m m }
   {
@@ -7427,7 +8101,7 @@
 %
 % \subsection{兼容性修补}
 %
-% \begin{macro}[internal]{\hbar}
+% \begin{macro}[int]{\hbar}
 % \changes{v3.2.16}{2014/11/20}{修复 \tn{hbar}。}
 % \pkg{fontspec} 会设置 operators 数学字体族(|\fam0|)为 \texttt{EU1} 编码的
 % \tn{rmdefault} 字体。这导致 \LaTeXe{} 定义的 \tn{hbar} 只显示为 $h$。
@@ -7444,7 +8118,7 @@
           \cs_set_protected_nopar:Npx \hbar
             { {
               \mathchar
-                \int_eval:n { \symlegacymaths * \c_two_hundred_fifty_six + '26 } ~
+                \int_eval:n { \symlegacymaths * 256 + '26 } ~
               \mkern -9mu h
             } }
         \fi:
@@ -7462,7 +8136,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\xeCJK at update@fam,\Url at MathSetup}
+% \begin{macro}[int]{\xeCJK at update@fam,\Url at MathSetup}
 % \changes{v3.2.7}{2013/11/09}{使通过 \tn{UrlFont} 等命令设置的 CJK 字体生效。}
 % 使通过 \tn{urlstyle} 或者 \tn{UrlFont} 设置的路径中使用的 CJK 字体生效。
 % 使用 \tn{everymath} 钩子中数学模式中重定义 CJK 数学字体,以确保我们的设置在
@@ -7509,7 +8183,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\fontspec_setup_maths:,\mathrm}
+% \begin{macro}[int]{\fontspec_setup_maths:,\mathrm}
 % \changes{v3.2.6}{2013/08/02}{为 \tn{mathrm} 减少一个可能的数学字体族。}
 % 如果没有设置 \tn{setboldmathrm},即 \cs{g_fontspec_bfmathrm_tl} 为空,那么
 % \tn{mathrm} 的字体实际与 \texttt{operators} 字体族完全一致。这时候应该通过
@@ -7533,9 +8207,10 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \changes{v3.4.5}{2017/01/02}{更新 \hologo{LaTeX3} 的过时用法。}
+% \changes{v3.4.5}{2017/01/02}{更新 \LaTeXiii{} 的过时用法。}
 %
-% \begin{macro}[internal]{\(,\),\math,\endmath,\ensuremath,\@@_math_robust:N}
+% \begin{macro}[int]{\(,\),\math,\endmath,\ensuremath}
+% \begin{macro}{\@@_math_robust:N}
 % \changes{v3.2.5}{2013/07/25}
 % {解决汉字后紧跟 \tn{(}\texttt{...}\tn{)} 形式的行内数学公式时,不能加入间距的问题。}
 % \changes{v3.2.6}{2013/08/03}{考虑 \pkg{ulem} 对 \tn{MakeRobust} 的不当定义。}
@@ -7624,11 +8299,12 @@
 \@@_math_robust:N \ensuremath
 %    \end{macrocode}
 % \end{macro}
+% \end{macro}
 %
 % \changes{v3.2.5}{2013/07/25}{解决 \pkg{fixltx2e} 和 \pkg{amsthm} 的冲突。}
 % \changes{v3.3.1}{2015/04/15}{删去 \pkg{fixltx2e} 和 \pkg{amsthm} 的冲突补丁。}
 %
-% \begin{macro}[internal]{\nobreakspace}
+% \begin{macro}[int]{\nobreakspace}
 % \changes{v3.1.2}{2013/01/01}
 % {修正非 \tn{UTFencname} 编码下面 \pkg{xunicode} 重定义的 \tn{nobreakspace} 会失效的问题。}
 % \changes{v3.2.5}{2013/07/18}{恢复 \tn{nobreakspace} 的原始定义。}
@@ -7684,8 +8360,8 @@
   }
 %    \end{macrocode}
 %
-% \begin{macro}[internal]{\fontfamily}
-% \begin{macro}[internal]{\xeCJK at fontfamily}
+% \begin{macro}[int]{\fontfamily}
+% \begin{macro}[int]{\xeCJK at fontfamily}
 % \changes{v3.1.1}{2012/12/06}{修改主要 \texttt{CJK} 字体族的自动更新方式。}
 % \changes{v3.1.2}{2013/01/01}{不将参数完全展开。}
 % \changes{v3.4.6}{2017/02/23}
@@ -7700,7 +8376,7 @@
 \cs_new_protected_nopar:Npn \xeCJK at fontfamily #1
   {
     \str_if_eq:nnTF {#1} { \familydefault }
-      { \xeCJK_switch_family:n { \CJKfamilydefault } }
+      { \xeCJK_switch_family:x { \CJKfamilydefault } }
       { \@@_update_family_aux: }
   }
 \cs_new_protected_nopar:Npn \@@_update_family_aux:
@@ -7707,10 +8383,10 @@
   {
     \str_case_x:nn { \f at family }
       {
-        { \rmdefault }     { \xeCJK_switch_family:n { \CJKrmdefault } }
-        { \sfdefault }     { \xeCJK_switch_family:n { \CJKsfdefault } }
-        { \ttdefault }     { \xeCJK_switch_family:n { \CJKttdefault } }
-        { \familydefault } { \xeCJK_switch_family:n { \CJKfamilydefault } }
+        { \rmdefault }     { \xeCJK_switch_family:x { \CJKrmdefault } }
+        { \sfdefault }     { \xeCJK_switch_family:x { \CJKsfdefault } }
+        { \ttdefault }     { \xeCJK_switch_family:x { \CJKttdefault } }
+        { \familydefault } { \xeCJK_switch_family:x { \CJKfamilydefault } }
       }
   }
 %    \end{macrocode}
@@ -7721,7 +8397,7 @@
 %<@@=>
 %    \end{macrocode}
 %
-% \begin{macro}[internal]{\xeCJK at fix@penalty}
+% \begin{macro}[int]{\xeCJK at fix@penalty}
 % \changes{v3.1.0}{2012/11/13}{采用通过不修改原语 \tn{/} 的方式对修复倾斜校正。}
 % 对 \LaTeXe 内核中的 \tn{fix at penalty} 被用于诸如 \tn{textit} 之类的文档
 % 字体转换命令的定义之中。这里对它进行补丁的目的是修复其中的倾斜校正,并使得这些
@@ -7737,32 +8413,54 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\xeCJK at italiccorr}
+% \begin{macro}[int]{\xeCJK at italiccorr}
 % 修复倾斜校正,并处理汉字后面的空格。
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \xeCJK at italiccorr
   {
     \int_compare:nNnTF \xetex_interchartokenstate:D > \c_zero
+      { \xeCJK_italic_correction: }
+      { \@@italiccorr }
+  }
+%    \end{macrocode}
+% \end{macro}
+%
+%    \begin{macrocode}
+%<@@=xeCJK>
+%    \end{macrocode}
+%
+% \begin{macro}[int]{\xeCJK_italic_correction:}
+% 修复倾斜校正,并处理汉字后面的空格。
+%    \begin{macrocode}
+\cs_new_protected_nopar:Npn \xeCJK_italic_correction:
+  {
+    \int_compare:nNnT \etex_lastnodetype:D = \c_twelve
+      { \@@_italic_correction: }
+  }
+\cs_new_protected_nopar:Npn \@@_italic_correction:
+  {
+    \dim_case:nnF { \tex_lastkern:D }
       {
-        \xeCJK_if_last_node:nTF { default }
-          {
-            \xeCJK_remove_node: \@@italiccorr
-            { \xeCJK_make_node:n { default } }
-          }
-          {
-            \xeCJK_if_last_node:nTF { CJK }
-              {
-                \xeCJK_remove_node: \@@italiccorr
-                { \xeCJK_make_node:n { CJK } } \use:n
-              }
-              {
-                \xeCJK_if_last_node:nTF { CJK-space }
-                  {
-                    \xeCJK_remove_node: \@@italiccorr
-                    { \xeCJK_make_node:n { CJK-space } } \use:n
-                  }
-                  { \@@italiccorr \use_none:n }
-              }
+        { \@@_node:n { default } }
+        {
+          \xeCJK_remove_node: \tex_italiccorrection:D
+          \xeCJK_make_node:n { default }
+        }
+        { \@@_node:n { CJK } }
+        {
+          \xeCJK_remove_node: \tex_italiccorrection:D
+          \xeCJK_make_node:n { CJK }
+          \@@_italic_correction_aux:
+        }
+        { \@@_node:n { CJK-space } }
+        {
+          \xeCJK_remove_node: \tex_italiccorrection:D
+          \xeCJK_make_node:n { CJK-space }
+          \@@_italic_correction_aux:
+        }
+      }
+      { \tex_italiccorrection:D }
+  }
 %    \end{macrocode}
 % \cs{xeCJK_ignore_spaces:w} 里面用到 |peek| 函数来判断后面是不是空格,而此时它
 % 后面还有 $4$ 个 \tn{fi} 或者 |\else...\fi| 没有被展开,将影响 |peek| 函数的
@@ -7771,25 +8469,18 @@
 % \verb*|\textit{...} | 等后面原来存在的空格作为完全展开的结束。要正确使用它还
 % 需要另外的处理(使用 \cs{exp_stop_f:})。
 %    \begin{macrocode}
-              {
-                              \exp_after:wN \exp_after:wN \exp_after:wN
-                \exp_after:wN \exp_after:wN \exp_after:wN \exp_after:wN
-                \exp_after:wN \exp_after:wN \exp_after:wN \exp_after:wN
-                \exp_after:wN \exp_after:wN \exp_after:wN \exp_after:wN
-                \xeCJK_ignore_spaces:w
-              }
-          }
-      }
-      { \@@italiccorr }
+\cs_new_protected_nopar:Npn \@@_italic_correction_aux:
+  {
+                  \exp_after:wN \exp_after:wN \exp_after:wN
+    \exp_after:wN \exp_after:wN \exp_after:wN \exp_after:wN
+    \exp_after:wN \exp_after:wN \exp_after:wN \exp_after:wN
+    \exp_after:wN \exp_after:wN \exp_after:wN \exp_after:wN
+    \xeCJK_ignore_spaces:w
   }
 %    \end{macrocode}
 % \end{macro}
 %
-%    \begin{macrocode}
-%<@@=xeCJK>
-%    \end{macrocode}
-%
-% \begin{macro}[internal]{\g_@@_xetex_allocator_int}
+% \begin{variable}{\g_@@_xetex_allocator_int}
 % \changes{v3.3.1}{2015/04/14}{兼容 \LaTeXe{} 2015。}
 % \changes{v3.3.2}{2015/05/15}{\tn{xe at alloc@intercharclass} 总是有定义的。}
 % \LaTeXe{} 2015/01/01 接管了 \tn{newXeTeXintercharclass}。
@@ -7796,9 +8487,9 @@
 %    \begin{macrocode}
 \cs_new_eq:NN \g_@@_xetex_allocator_int \xe at alloc@intercharclass
 %    \end{macrocode}
-% \end{macro}
+% \end{variable}
 %
-% \begin{macro}[internal]{\@@_set_others_toks:n}
+% \begin{macro}{\@@_set_others_toks:n}
 % 简单处理与同样使用 \tn{XeTeXinterchartoks} 机制的宏包的兼容问题。
 %     \begin{macrocode}
 \@@_after_end_preamble:n
@@ -7808,7 +8499,9 @@
       { \g_@@_xetex_allocator_int }
       {
         \int_step_inline:nnnn
-          { \c_@@_class_begin_int + \c_one } \c_one \g_@@_xetex_allocator_int
+          { \c_@@_class_begin_int + \c_one }
+          { \c_one }
+          { \g_@@_xetex_allocator_int }
           {
             \seq_if_in:NnF \g_@@_new_class_seq {#1}
               { \@@_set_others_toks:n {#1} }
@@ -7843,7 +8536,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_group_begin:,\@@_group_end:}
+% \begin{macro}{\@@_group_begin:,\@@_group_end:}
 % 用于保护下面歧义宽度标点的分组。
 %    \begin{macrocode}
 \cs_new_eq:NN \@@_group_begin: \group_begin:
@@ -7851,7 +8544,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\textellipsis}
+% \begin{macro}[int]{\textellipsis}
 % 单独处理宽度有分歧的几个标点:包括省略号、破折号、间隔号、引号等中西文混用的
 % 符号, 保证其命令形式输出的是西文字体。
 %    \begin{macrocode}
@@ -7868,7 +8561,8 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\l_@@_patch_Bxii_tl,\@@_patch_Bxii:n}
+% \begin{macro}{\@@_patch_Bxii:n}
+% \begin{variable}{\l_@@_patch_Bxii_tl}
 % \changes{v3.2.9}{2013/12/08}
 % {完整处理 \file{encguide.pdf} 的编码符号表中,与旧编码的 \texttt{U+00B7} 冲突。}
 % 常被用作中文间隔号的 \texttt{U+00B7} 与 T1 等旧字体编码下定义的符号命令冲突。
@@ -7968,6 +8662,7 @@
       }
   }
 %    \end{macrocode}
+% \end{variable}
 % \end{macro}
 %
 % 简单处理与 \pkg{hyperref} 宏包的兼容问题。
@@ -8056,7 +8751,7 @@
 % \changes{v3.2.4}{2013/06/25}{不再使用 \texttt{CJKnumber} 选项,可以在
 % \pkg{xeCJK} 之后直接使用 \pkg{CJKnumb} 宏包得到中文数字。}
 %
-% \begin{macro}[internal]{\CJKaddEncHook}
+% \begin{macro}[int]{\CJKaddEncHook}
 % 为使用 \pkg{CJKnumb} 宏包而作一些处理。另外 \pkg{CJKnumb} 使用的是传统汉字“萬”
 % 和“億”,我们在这里把它们修正为简体字。
 % \changes{v3.2.10}{2014/03/01}{使用 \pkg{CJKnumb} 时,让 \tn{Unicode} 有定义。}
@@ -8154,7 +8849,7 @@
 %
 % \changes{v3.4.1}{2016/06/03}{新的下划线选项 \texttt{textformat}。}
 %
-% \begin{macro}[internal]{\xeCJK_hook_for_ulem:}
+% \begin{macro}[int]{\xeCJK_hook_for_ulem:}
 % \changes{v3.1.0}{2012/11/16}{简化对 \pkg{ulem} 宏包的兼容补丁。}
 % \changes{v3.1.1}{2012/12/08}{完全处理下划线里的标点符号的有关问题。}
 %    \begin{macrocode}
@@ -8186,7 +8881,6 @@
         \cs_set_eq:NN \@@_ulem_hskip_aux:n \xeCJK_ulem_hskip:n
       }
       {
-        \xeCJK_swap_cs:NN \@@_punct_kern:n  \@@_ulem_punct_kern:n
         \xeCJK_swap_cs:NN \@@_punct_hskip:n \@@_ulem_punct_hskip:n
         \xeCJK_cs_clear:N \@@_ulem_skip_punct_begin:
         \xeCJK_cs_clear:N \@@_ulem_skip_punct_end:
@@ -8212,6 +8906,8 @@
       { \@@_ulem_glue:n \l_@@_ecglue_skip }
     \cs_set_protected_nopar:Npn \xeCJK_space_glue:
       { \@@_ulem_glue:n \l_@@_space_skip }
+    \cs_set_eq:NN \xeCJK_punct_node:N \use_none:n
+    \cs_set_eq:NN \xeCJK_if_last_punct:TF \use_ii:nn
     \keys_set:nn { xeCJK / options }
       { CheckFullRight = false , xCJKecglue = false }
   }
@@ -8220,7 +8916,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\UL at word,\xeCJK_ulem_word:nw}
+% \begin{macro}[int]{\UL at word,\xeCJK_ulem_word:nw}
 % 修改 \tn{UL at word},目的是取得分组中的 \tn{UL at leadtype},以便加入 \cs{xeCJK_ulem_right_skip:}。
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \xeCJK_ulem_word:nw #1 ~
@@ -8257,7 +8953,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\xeCJK_ulem_left:, \xeCJK_ulem_detect_node:}
+% \begin{macro}[int]{\xeCJK_ulem_left:, \xeCJK_ulem_detect_node:}
 % 在下划线开始之前探测之前的 \texttt{node},以便随后插入 \tn{CJKglue} 或 \tn{CJKecglue}。
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \xeCJK_ulem_left:
@@ -8303,7 +8999,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_ulem_hskip_first:n, \xeCJK_ulem_hskip:n}
+% \begin{macro}{\@@_ulem_hskip_first:n, \xeCJK_ulem_hskip:n}
 % 如果第一次调用的 \tn{CJKglue} 或 \tn{CJKecglue} 由下划线中的第一个文字和之前的
 % 内容产生,就不用画下划线。
 %    \begin{macrocode}
@@ -8323,7 +9019,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\xeCJK_ulem_right:, \xeCJK_ulem_right_node:}
+% \begin{macro}[int]{\xeCJK_ulem_right:, \xeCJK_ulem_right_node:}
 % 在下划线最后的位置保存 \texttt{node}。
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \xeCJK_ulem_right:
@@ -8362,7 +9058,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\xeCJK_ulem_var_leaders:}
+% \begin{macro}[int]{\xeCJK_ulem_var_leaders:}
 % 第一次画下划线时,不需要向左平移 \tn{UL at pixel},让左侧有间距。
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \xeCJK_ulem_leaders:
@@ -8381,7 +9077,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\xeCJK_ulem_right_skip:}
+% \begin{macro}[int]{\xeCJK_ulem_right_skip:}
 % 在下划线完全画好之后,我们检测最后的情况。用 \tn{unskip} 去掉最后一个下划线,再
 % 重新画一个减少 \tn{UL at pixel} 的。
 %    \begin{macrocode}
@@ -8401,7 +9097,7 @@
     \int_compare:nNnTF \etex_lastnodetype:D = \c_twelve
       { \@@_ulem_right_skip_kern: }
       { \@@_ulem_right_skip_glue: }
-    \box_use_clear:N \l_@@_tmp_box
+    \box_use_drop:N \l_@@_tmp_box
   }
 \cs_new_protected_nopar:Npn \@@_ulem_right_skip_kern:
   {
@@ -8434,7 +9130,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_ulem_hidden_box:}
+% \begin{macro}{\@@_ulem_hidden_box:}
 % 只画线,不输出盒子。
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \@@_ulem_hidden_box:
@@ -8452,7 +9148,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_ulem_skip_punct_begin:,\@@_ulem_skip_punct_end:}
+% \begin{macro}{\@@_ulem_skip_punct_begin:,\@@_ulem_skip_punct_end:}
 % 让下划线跳过标点符号的设置。
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \@@_ulem_skip_punct_begin:
@@ -8469,12 +9165,12 @@
 \cs_new_protected_nopar:Npn \@@_ulem_skip_putbox:
   {
     \tl_if_empty:NF \UL at start
-      { \box_use_clear:N \UL at box }
+      { \box_use_drop:N \UL at box }
   }
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_ulem_initial:}
+% \begin{macro}{\@@_ulem_initial:}
 % 这里的设置是为了在下划线状态下,下划线可以自动跳过全角标点符号和正确的在它们
 % 前/后断行,并且与行首行末对齐。
 %    \begin{macrocode}
@@ -8481,13 +9177,15 @@
 \cs_new_protected_nopar:Npn \@@_ulem_initial:
   {
     \@@_ulem_swap_cs:NN
-    \xeCJK_FullLeft_and_Default:  \@@_ulem_FullLeft_and_Default:
-    \xeCJK_FullLeft_and_CJK:      \@@_ulem_FullLeft_and_CJK:
-    \xeCJK_FullRight_and_Default: \@@_ulem_FullRight_and_Default:
-    \xeCJK_FullRight_and_CJK:     \@@_ulem_FullRight_and_CJK:
-    \xeCJK_CJK_and_CJK:N          \@@_ulem_CJK_and_CJK:N
-    \xeCJK_CJK_and_Boundary:w     \@@_ulem_CJK_and_Boundary:w
-    \xeCJK at fix@penalty            \@@_ulem_fix_penalty:
+    \xeCJK_FullLeft_and_Default:   \@@_ulem_FullLeft_and_Default:
+    \xeCJK_FullLeft_and_CJK:       \@@_ulem_FullLeft_and_CJK:
+    \xeCJK_FullLeft_and_Boundary:  \@@_ulem_FullLeft_and_Boundary:
+    \xeCJK_FullRight_and_Default:  \@@_ulem_FullRight_and_Default:
+    \xeCJK_FullRight_and_CJK:      \@@_ulem_FullRight_and_CJK:
+    \xeCJK_FullRight_and_Boundary: \@@_ulem_FullRight_and_Boundary:
+    \xeCJK_CJK_and_CJK:N           \@@_ulem_CJK_and_CJK:N
+    \xeCJK_CJK_and_Boundary:w      \@@_ulem_CJK_and_Boundary:w
+    \xeCJK at fix@penalty             \@@_ulem_fix_penalty:
     \@@_punct_breakable_kern:n       \@@_ulem_punct_breakable_kern:n
     \@@_Default_and_FullLeft_glue:N  \@@_ulem_Default_and_FullLeft_glue:N
     \@@_Default_and_FullRight_glue:N \@@_ulem_Default_and_FullRight_glue:N
@@ -8524,7 +9222,7 @@
 %
 % \changes{v3.1.2}{2012/12/27}{解决在下划线状态下使用 \tn{makebox} 时的错误。}
 %
-% \begin{macro}[internal]{\xeCJK_if_ulem_patch:TF}
+% \begin{macro}[int]{\xeCJK_if_ulem_patch:TF}
 % 在下划线状态下,\pkg{ulem} 宏包在数学模式或者盒子中使用 \tn{UL at hrest} 恢复
 % \verb*|\ | 等的定义,此时不需要使用 \tn{UL at stop} 和 \tn{UL at start} 来断开下划线而
 % 产生断点。
@@ -8540,7 +9238,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_ulem_CJK_and_Boundary:w}
+% \begin{macro}{\@@_ulem_CJK_and_Boundary:w}
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \@@_ulem_CJK_and_Boundary:w
   {
@@ -8570,7 +9268,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_ulem_fix_penalty:}
+% \begin{macro}{\@@_ulem_fix_penalty:}
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \@@_ulem_fix_penalty:
   {
@@ -8581,7 +9279,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_ulem_CJK_and_CJK:N}
+% \begin{macro}{\@@_ulem_CJK_and_CJK:N}
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \@@_ulem_CJK_and_CJK:N
   {
@@ -8590,6 +9288,7 @@
         \xeCJK_class_group_end:
         \UL at stop \@@_ulem_ccglue: \UL at start
         \@@_ulem_class_group_begin:
+        \xeCJK_select_font:
         \CJKsymbol
       }
       { \@@_ulem_CJK_and_CJK:N }
@@ -8597,18 +9296,17 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_ulem_class_group_begin:}
+% \begin{macro}{\@@_ulem_class_group_begin:}
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \@@_ulem_class_group_begin:
   {
     \xeCJK_class_group_begin:
     \xeCJK_clear_Boundary_and_CJK_toks:
-    \xeCJK_select_font:
   }
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_ulem_between_CJK_blocks:nnN}
+% \begin{macro}{\@@_ulem_between_CJK_blocks:nnN}
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \@@_ulem_between_CJK_blocks:nnN #1#2
   {
@@ -8630,7 +9328,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_ulem_Default_and_FullLeft_glue:N}
+% \begin{macro}{\@@_ulem_Default_and_FullLeft_glue:N}
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \@@_ulem_Default_and_FullLeft_glue:N #1
   {
@@ -8638,8 +9336,8 @@
       {
         \UL at stop
         \@@_ulem_skip_punct_begin:
-        \@@_punct_glue:NN \c_@@_left_tl {#1}
-        \@@_punct_offset:NN \c_@@_left_tl {#1}
+        \@@_punct_glue:NN \c_@@_left_tl #1
+        \@@_punct_offset:NN \c_@@_left_tl #1
         \UL at start
       }
       { \@@_ulem_Default_and_FullLeft_glue:N #1 }
@@ -8647,7 +9345,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_ulem_Boundary_and_FullLeft_glue:N}
+% \begin{macro}{\@@_ulem_Boundary_and_FullLeft_glue:N}
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \@@_ulem_Boundary_and_FullLeft_glue:N #1
   {
@@ -8655,7 +9353,7 @@
       {
         \UL at stop
         \@@_ulem_skip_punct_begin:
-        \@@_punct_glue:NN \c_@@_left_tl {#1}
+        \@@_punct_glue:NN \c_@@_left_tl #1
         \UL at start
       }
       { \@@_ulem_Boundary_and_FullLeft_glue:N #1 }
@@ -8663,7 +9361,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_ulem_CJK_and_FullLeft_glue:N}
+% \begin{macro}{\@@_ulem_CJK_and_FullLeft_glue:N}
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \@@_ulem_CJK_and_FullLeft_glue:N #1
   {
@@ -8672,11 +9370,12 @@
         \xeCJK_class_group_end:
         \UL at stop
         \@@_ulem_skip_punct_begin:
-        \@@_ulem_ccglue:
-        \@@_punct_glue:NN \c_@@_left_tl {#1}
-        \@@_punct_offset:NN \c_@@_left_tl {#1}
+        \@@_ulem_punct_ccglue:
+        \@@_punct_glue:NN \c_@@_left_tl #1
+        \@@_punct_offset:NN \c_@@_left_tl #1
         \UL at start
         \@@_ulem_class_group_begin:
+        \xeCJK_select_punct_font:
       }
       { \@@_ulem_CJK_and_FullLeft_glue:N #1 }
   }
@@ -8683,7 +9382,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_ulem_Default_and_FullRight_glue:N}
+% \begin{macro}{\@@_ulem_Default_and_FullRight_glue:N}
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \@@_ulem_Default_and_FullRight_glue:N #1
   {
@@ -8691,16 +9390,13 @@
       {
         \UL at stop
         \@@_ulem_skip_punct_begin:
-        \@@_punct_if_long:NTF {#1}
-          { \@@_ulem_ccglue: }
+        \@@_punct_if_long:NTF #1
+          { \xeCJK_allow_break: }
+          { \xeCJK_no_break: }
+        \@@_punct_if_middle:NT #1
           {
-            \@@_punct_if_middle:NTF {#1}
-              {
-                \xeCJK_no_break:
-                \@@_punct_glue:NN \c_@@_right_tl {#1}
-                \@@_punct_bound_rule:NN \c_@@_left_tl {#1}
-              }
-              { \xeCJK_no_break: }
+            \@@_punct_glue:NN \c_@@_right_tl #1
+            \@@_punct_bound_rule:NN \c_@@_left_tl #1
           }
         \UL at start
       }
@@ -8709,7 +9405,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_ulem_CJK_and_FullRight_glue:N}
+% \begin{macro}{\@@_ulem_CJK_and_FullRight_glue:N}
 % \changes{v3.2.2}{2013/05/30}{修正下划线不能跳过全角右标点的问题。}
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \@@_ulem_CJK_and_FullRight_glue:N #1
@@ -8717,8 +9413,20 @@
     \xeCJK_if_ulem_patch:TF
       {
         \xeCJK_class_group_end:
-        \@@_Default_and_FullRight_glue:N {#1}
+        \UL at stop
+        \@@_ulem_skip_punct_begin:
+        \@@_punct_if_long:NTF #1
+          { \xeCJK_allow_break: }
+          { \xeCJK_no_break: }
+        \@@_punct_if_middle:NT #1
+          {
+            \@@_ulem_punct_ccglue:
+            \@@_punct_glue:NN \c_@@_right_tl #1
+            \@@_punct_bound_rule:NN \c_@@_left_tl #1
+          }
+        \UL at start
         \@@_ulem_class_group_begin:
+        \xeCJK_select_punct_font:
       }
       { \@@_ulem_CJK_and_FullRight_glue:N #1 }
   }
@@ -8725,7 +9433,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_ulem_FullLeft_and_Default:}
+% \begin{macro}{\@@_ulem_FullLeft_and_Default:}
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \@@_ulem_FullLeft_and_Default:
   {
@@ -8733,7 +9441,7 @@
       {
         \@@_punct_if_middle:NTF \g_@@_last_punct_tl
           {
-            \xeCJK_get_punct_bounds:NN \c_@@_left_tl \g_@@_last_punct_tl
+            \xeCJK_get_punct_bounds:No \c_@@_left_tl \g_@@_last_punct_tl
             \@@_punct_bound_rule:NN \c_@@_right_tl \g_@@_last_punct_tl
             \xeCJK_class_group_end: \UL at stop \xeCJK_no_break:
             \@@_punct_glue:NN \c_@@_left_tl  \g_@@_last_punct_tl
@@ -8748,7 +9456,31 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_ulem_FullLeft_and_CJK:}
+% \begin{macro}{\@@_ulem_FullLeft_and_Boundary:}
+%    \begin{macrocode}
+\cs_new_protected_nopar:Npn \@@_ulem_FullLeft_and_Boundary:
+  {
+    \xeCJK_if_ulem_patch:TF
+      {
+        \@@_punct_if_middle:NTF \g_@@_last_punct_tl
+          {
+            \xeCJK_get_punct_bounds:No \c_@@_left_tl \g_@@_last_punct_tl
+            \@@_punct_bound_rule:NN \c_@@_right_tl \g_@@_last_punct_tl
+            \xeCJK_class_group_end: \UL at stop \xeCJK_no_break:
+            \@@_punct_glue:NN \c_@@_left_tl  \g_@@_last_punct_tl
+          }
+          { \xeCJK_class_group_end: \UL at stop }
+        \@@_ulem_skip_punct_end:
+        \xeCJK_no_break:
+        \UL at start
+        \tex_ignorespaces:D
+      }
+      { \@@_ulem_FullLeft_and_Boundary: }
+  }
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_ulem_FullLeft_and_CJK:}
 % \changes{v3.2.3}{2013/06/04}
 % {修正全角左标点后下划线与 \tn{CJKunderdot} 连用时结果不正常的问题。}
 %    \begin{macrocode}
@@ -8758,6 +9490,7 @@
       {
         \xeCJK_FullLeft_and_Default:
         \@@_ulem_class_group_begin:
+        \xeCJK_select_font:
       }
       { \@@_ulem_FullLeft_and_CJK: }
   }
@@ -8764,7 +9497,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_ulem_FullRight_and_Default:}
+% \begin{macro}{\@@_ulem_FullRight_and_Default:}
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \@@_ulem_FullRight_and_Default:
   {
@@ -8783,9 +9516,9 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_ulem_FullRight_and_CJK:}
+% \begin{macro}{\@@_ulem_FullRight_and_Boundary:}
 %    \begin{macrocode}
-\cs_new_protected_nopar:Npn \@@_ulem_FullRight_and_CJK:
+\cs_new_protected_nopar:Npn \@@_ulem_FullRight_and_Boundary:
   {
     \xeCJK_if_ulem_patch:TF
       {
@@ -8794,42 +9527,49 @@
         \UL at stop
         \@@_punct_offset:NN \c_@@_right_tl \g_@@_last_punct_tl
         \@@_punct_glue:NN \c_@@_right_tl \g_@@_last_punct_tl
-        \@@_ulem_ccglue:
         \@@_ulem_skip_punct_end:
         \UL at start
-        \@@_ulem_class_group_begin:
+        \tex_ignorespaces:D
       }
-      { \@@_ulem_FullRight_and_CJK: }
+      { \@@_ulem_FullRight_and_Boundary: }
   }
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_ulem_punct_hskip:n}
+% \begin{macro}{\@@_ulem_FullRight_and_CJK:}
 %    \begin{macrocode}
-\cs_new_protected_nopar:Npn \@@_ulem_punct_hskip:n
+\cs_new_protected_nopar:Npn \@@_ulem_FullRight_and_CJK:
   {
     \xeCJK_if_ulem_patch:TF
-      { \xeCJK_ulem_hskip:n }
-      { \@@_ulem_punct_hskip:n }
+      {
+        \@@_punct_rule:NN \c_@@_right_tl \g_@@_last_punct_tl
+        \xeCJK_class_group_end:
+        \UL at stop
+        \@@_punct_offset:NN \c_@@_right_tl \g_@@_last_punct_tl
+        \@@_punct_glue:NN \c_@@_right_tl \g_@@_last_punct_tl
+        \@@_ulem_punct_ccglue:
+        \@@_ulem_skip_punct_end:
+        \UL at start
+        \@@_ulem_class_group_begin:
+        \xeCJK_select_font:
+      }
+      { \@@_ulem_FullRight_and_CJK: }
   }
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_ulem_punct_kern:n}
+% \begin{macro}{\@@_ulem_punct_hskip:n}
 %    \begin{macrocode}
-\cs_new_protected_nopar:Npn \@@_ulem_punct_kern:n #1
+\cs_new_protected_nopar:Npn \@@_ulem_punct_hskip:n
   {
     \xeCJK_if_ulem_patch:TF
-      {
-        \dim_compare:nNnF {#1} = \c_zero_dim
-          { \xeCJK_ulem_hskip:n {#1} }
-      }
-      { \@@_ulem_punct_kern:n {#1} }
+      { \xeCJK_ulem_hskip:n }
+      { \@@_ulem_punct_hskip:n }
   }
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_ulem_punct_breakable_kern:n}
+% \begin{macro}{\@@_ulem_punct_breakable_kern:n}
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \@@_ulem_punct_breakable_kern:n #1
   {
@@ -8838,6 +9578,7 @@
         \xeCJK_class_group_end:
         \UL at stop \xeCJK_ulem_hskip:n {#1} \UL at start
         \@@_ulem_class_group_begin:
+        \xeCJK_select_punct_font:
       }
       { \@@_ulem_punct_breakable_kern:n {#1} }
   }
@@ -8844,7 +9585,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_ulem_glue:n,\@@_ulem_ccglue:}
+% \begin{macro}{\@@_ulem_glue:n,\@@_ulem_ccglue:,\@@_ulem_punct_ccglue:}
 % 在下划线状态下的分别代替 \tn{CJKglue} 等。
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \@@_ulem_glue:n #1
@@ -8871,6 +9612,8 @@
   }
 \cs_new_protected_nopar:Npn \@@_ulem_ccglue:
   { { \skip_set_eq:NN \UL at skip \l_@@_ccglue_skip \UL at leaders } }
+\cs_new_protected_nopar:Npn \@@_ulem_punct_ccglue:
+  { \@@_punct_hskip:n { \l_@@_ccglue_skip } }
 %    \end{macrocode}
 % \end{macro}
 %
@@ -8878,7 +9621,7 @@
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \xeCJK_ulem_group_begin:
   {
-    \xeCJK_leave_vmode:
+    \mode_leave_vertical:
     \c_group_begin_token
   }
 \cs_new_protected_nopar:Npn \xeCJK_ulem_group_end:
@@ -8897,7 +9640,7 @@
 %    \begin{macrocode}
 \NewDocumentCommand \xeCJKfntefon { s t- s o }
   {
-    \xeCJK_leave_vmode:
+    \mode_leave_vertical:
     \xeCJK_ulem_boot:NNNn #1#2#3 {#4}
     \xeCJK_ulem_on:n
   }
@@ -9044,7 +9787,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\xeCJK_fntef_boot:nnNNNn}
+% \begin{macro}[int]{\xeCJK_fntef_boot:nnNNNn}
 % 处理参数问题。
 %    \begin{macrocode}
 \cs_new_protected:Npn \xeCJK_fntef_boot:nnNNNn #1#2#3#4#5#6
@@ -9051,9 +9794,9 @@
   {
     \bool_lazy_or:nnT {#3} {#5}
       { \bool_set_false:c { l_@@_#2_skip_bool } }
-    \IfBooleanT #4
+    \bool_if:NT #4
       { \bool_set_true:c { l_@@_#2_subtract_bool } }
-    \IfNoValueF {#6}
+    \tl_if_novalue:nF {#6}
       { \keys_set:nn { xeCJK / options / #1 } {#6} }
     \bool_set_eq:Nc \l_@@_ulem_skip_bool { l_@@_#2_skip_bool }
     \bool_set_eq:Nc \l_@@_ulem_hidden_bool { l_@@_#2_hidden_bool }
@@ -9064,15 +9807,15 @@
   {
     \bool_lazy_or:nnT {#1} {#3}
       { \bool_set_false:N \l_@@_ulem_skip_bool }
-    \IfBooleanT #2
+    \bool_if:NT #2
       { \bool_set_true:N \l_@@_ulem_subtract_bool }
-    \IfNoValueF {#4}
+    \tl_if_novalue:nF {#4}
       { \keys_set:nn { xeCJK / options / ulem } {#4} }
   }
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\xeCJK_fntef_initial:n}
+% \begin{macro}[int]{\xeCJK_fntef_initial:n}
 % 不支持下划线的嵌套使用。下划线嵌套使用时,里层的下划线会被放在盒子里,不能折行。
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \xeCJK_fntef_initial:n
@@ -9117,7 +9860,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[var,internal]{\l_@@_fntef_dim}
+% \begin{variable}{\l_@@_fntef_dim}
 % 记录下划线或者下划符号的深度,以便它们嵌套使用时能自动调整好距离。
 % \tn{ULdepth} 被 \pkg{ulem} 初始化为 \tn{maxdimen}。下划线嵌套时,\pkg{ulem}
 % 要使用它作计算,可能会溢出。为简便起见,\cs{l_@@_fntef_dim} 与 \tn{ULdepth}
@@ -9125,9 +9868,9 @@
 %    \begin{macrocode}
 \cs_new_eq:NN \l_@@_fntef_dim \ULdepth
 %    \end{macrocode}
-% \end{macro}
+% \end{variable}
 %
-% \begin{macro}[internal]{\xeCJK_fntef_sbox:n}
+% \begin{macro}[int]{\xeCJK_fntef_sbox:n}
 % 与 \cs{hcoffin_set:Nn} 和 \LaTeXe{} 的 \tn{sbox} 功能类似,确保颜色的正确。
 % 虽然 \texttt{coffin} 可以更方便的操作盒子,但速度要慢一点。并且,我们的需求
 % 也比较简单,就不用它了。
@@ -9145,18 +9888,6 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\xeCJK_leave_vmode:}
-% 功能与 \tn{leavevmode} 类似,但不会影响 \tn{everypar}。
-%    \begin{macrocode}
-\cs_new_protected_nopar:Npn \xeCJK_leave_vmode:
-  {
-    \if_mode_vertical:
-      \exp_after:wN \tex_indent:D
-    \fi:
-  }
-%    \end{macrocode}
-% \end{macro}
-%
 % 最合适的是用 \pkg{xtemplate} 宏包来实现,但是比较难于用 \tn{xeCJKsetup} 来统一
 % 设置,所以这里还是用土办法。
 %    \begin{macrocode}
@@ -9285,7 +10016,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\xeCJK_under_symbol:nnnnnn}
+% \begin{macro}[int]{\xeCJK_under_symbol:nnnnnn}
 % 当处在下划线中时,我们先断开下划线,在分组外设置下划符号。
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \xeCJK_under_symbol:nnnnnn
@@ -9308,7 +10039,7 @@
   }
 \cs_new_protected:Npn \@@_under_symbol_auxii:nnnnnn #1#2#3#4#5#6
   {
-    \xeCJK_leave_vmode:
+    \mode_leave_vertical:
     \group_begin:
       \xeCJK_under_symbol_initial:nnnnn {#1} {#2} {#3} {#4} {#5}
       \@@_under_symbol_text_format:c { l_@@_#2_text_format_tl }
@@ -9319,7 +10050,7 @@
   }
 \cs_new_protected:Npn \xeCJK_under_symbol_initial:nnnnn #1#2#3#4#5
   {
-    \IfNoValueF {#3}
+    \tl_if_novalue:nF {#3}
       { \keys_set:nn { xeCJK / options / #1 } {#3} }
     \xeCJK_fntef_sbox:n {#5}
     \bool_if:NTF \l_@@_fntef_bool
@@ -9348,7 +10079,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\xeCJK_make_under_symbol:n}
+% \begin{macro}[int]{\xeCJK_make_under_symbol:n}
 % 我们量取“一”的宽度作为汉字的宽度。
 %    \begin{macrocode}
 \cs_new_protected:Npn \xeCJK_make_under_symbol:n #1
@@ -9369,7 +10100,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_restore_shipout_CJKsymbol:}
+% \begin{macro}{\@@_restore_shipout_CJKsymbol:}
 % \changes{v3.2.3}{2013/06/04}{解决 \tn{CJKunderdot} 跨页使用时影响到页眉页脚的问题。}
 % \tn{CJKunderdot} 中对 \tn{CJKsymbol} 的修改会影响到页眉和页脚,需要小心处理。
 %    \begin{macrocode}
@@ -9394,7 +10125,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_under_CJKsymbol:N}
+% \begin{macro}{\@@_under_CJKsymbol:N}
 % 盒子放在汉字的左侧,比较容易处理状态转移的问题。
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \@@_under_CJKsymbol:N
@@ -9424,7 +10155,7 @@
   }
 \NewEnviron { CJKfilltwosides* } [ 2 ] [ c ]
   {
-    \xeCJK_leave_vmode:
+    \mode_leave_vertical:
     \cs_set_eq:NN \CJKglue \xeCJK_fntef_hfilll:
     \tl_set:Nn \arraystretch { 1 }
     \cs_if_free:NF \extrarowheight
@@ -9459,14 +10190,13 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\xeCJK_fntef_hfilll:}
+% \begin{macro}[int]{\xeCJK_fntef_hfilll:}
 % \pkg{colortbl} 将表格 |c| 列用于填充的 \tn{hfil} 改为了更高阶的 \texttt{fill},
 % 影响到了 \env{CJKfilltwosides*}。因此,我们也要用高阶的 \texttt{filll}。
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \xeCJK_fntef_hfilll:
   { \skip_horizontal:N \c_@@_filll_skip }
-\skip_new:N \c_@@_filll_skip
-\skip_set:Nn  \c_@@_filll_skip { \c_zero_dim plus 1 filll }
+\skip_const:Nn \c_@@_filll_skip { \c_zero_dim plus 1 filll }
 %    \end{macrocode}
 % \end{macro}
 %
@@ -9507,7 +10237,7 @@
 \lst at AddToHook { PreSet } { \bool_set_true:N \l_@@_listings_env_bool }
 %    \end{macrocode}
 %
-% \begin{macro}[internal]{\@@_listings_initial_hook:}
+% \begin{macro}{\@@_listings_initial_hook:}
 % \changes{v3.2.3}{2013/06/04}
 % {解决 \texttt{listings} 坏境中代码行号输出不正确的问题,并解决在其中跨页时对页眉
 %  和页脚的影响。}
@@ -9541,7 +10271,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_listings_toks_hook:}
+% \begin{macro}{\@@_listings_toks_hook:}
 % 采用不同的 \tn{XeTeXinterchartoks} 处理方式,输入的时候是将汉字加入到 \pkg{listings}
 % 的输出队列,实际输出的时候是普通文字。
 %    \begin{macrocode}
@@ -9565,7 +10295,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_backup_inter_class_toks:n}
+% \begin{macro}{\@@_backup_inter_class_toks:n}
 % 注意,给 \tn{XeTeXinterchartoks} 赋空值,会导致 \XeTeX 崩溃!
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \@@_backup_inter_class_toks:n #1
@@ -9586,7 +10316,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_listings_CJK_toks_hook:,\@@_listings_breaklines_toks:}
+% \begin{macro}{\@@_listings_CJK_toks_hook:,\@@_listings_breaklines_toks:}
 % 根据 \texttt{breaklines} 选项的使用与否,选择不同的处理方式。
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \@@_listings_CJK_toks_hook:
@@ -9624,7 +10354,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_listings_process_Default:nN}
+% \begin{macro}{\@@_listings_process_Default:nN}
 % \changes{v3.2.3}{2013/06/08}{在 \texttt{listings} 坏境中对 \tn{charcode} 大于
 % $255$ 的字符根据其 \tn{catcode} 区分 \texttt{letter} 和 \texttt{other}。}
 % \changes{v3.3.1}{2015/01/27}{对 \pkg{listings} 的字符扩展不影响到其符号表中的
@@ -9657,7 +10387,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_listings_process_CJK:nN}
+% \begin{macro}{\@@_listings_process_CJK:nN}
 % 对 CJK 字符类的处理。
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \@@_listings_process_CJK:nN #1#2
@@ -9669,7 +10399,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_listings_append:nN}
+% \begin{macro}{\@@_listings_append:nN}
 % 普通 CJK 字符的宽度为一般基本宽度的两倍,CM 类不增加宽度。这里有一个问题,
 % 对 CJK 字符类中的一些半角字符(例如半角日文假名)没有区分开。\pkg{listings} 通过
 % 重定义 \tn{lst at Append} 将代码写入外部文件,因此需要保留。
@@ -9682,7 +10412,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_listings_process_letter:nN,\@@_listings_process_other:nN}
+% \begin{macro}{\@@_listings_process_letter:nN,\@@_listings_process_other:nN}
 % 在 \texttt{letter} 类中区分汉字和西文字母。
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \@@_listings_process_letter:nN
@@ -9715,7 +10445,7 @@
 % \changes{v3.2.4}{2013/07/05}
 % {使 \pkg{listings} 的 \texttt{breaklines} 选项对 CJK 字符类可用,并保持标点符号的禁则。}
 %
-% \begin{macro}[internal]
+% \begin{macro}
 % {\@@_listings_process_breaklines_CJK:nN,
 %  \@@_listings_process_FullLeft:nN,
 %  \@@_listings_process_FullRight:nN}
@@ -9781,7 +10511,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\lst at AppendLetter,\lst at AppendOther}
+% \begin{macro}[int]{\lst at AppendLetter,\lst at AppendOther}
 %    \begin{macrocode}
 \cs_set_protected_nopar:Npn \lst at AppendLetter
   {
@@ -9808,7 +10538,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_listings_process_CM:nN}
+% \begin{macro}{\@@_listings_process_CM:nN}
 % \texttt{CM} 类作为 \texttt{letter} 处理,不用增加 \tn{lst at length}。
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \@@_listings_process_CM:nN
@@ -9821,7 +10551,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_listings_output_CM:}
+% \begin{macro}{\@@_listings_output_CM:}
 % 在使用 \texttt{columns=fixed} 选项时,\pkg{listings} 会在输出盒子里的每个字符
 % 之间加入 \tn{hss},这就破坏了 \XeTeX 将基本字和组合标识正确的组合起来。
 %    \begin{macrocode}
@@ -9840,7 +10570,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_listings_peek_active_loop:TF}
+% \begin{macro}{\@@_listings_peek_active_loop:TF}
 % \tn{lstinline} 通过判断参数中第一个字符是否是 \texttt{active} 类来区分
 % 它是否被用在其它宏的参数之中。如果这第一个字符不在 \pkg{listings} 预定义的
 % 符号表中,判断就会出问题。我们在这里通过一个循环跳过这些字符。
@@ -9863,7 +10593,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_listings_rescan:Nn,
+% \begin{macro}{\@@_listings_rescan:Nn,
 %  \@@_listings_inside_convert:nw,\@@_listings_inline_group:w}
 % 当 \tn{lstinline} 被使用在参数中时,\pkg{listings} 会使用一个循环逐个将
 % \tn{lstinline} 参数中的字符设置为活动字符。我们可以通过 \cs{tl_set_rescan:Nnn}
@@ -9897,7 +10627,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_listings_set_escape:}
+% \begin{macro}{\@@_listings_set_escape:}
 % 由于我们在上面的修改,需要保留 |\| 用于转义 \tn{lstinline} 参数中的某些 \TeX
 % 特殊字符,与原来宏包一致。
 %    \begin{macrocode}
@@ -9921,7 +10651,7 @@
 %
 %
 % \changes{v3.4.8}{2017/05/15}{转义 \tn{lstinline} 参数中的 $\texttt{\textbackslash}_{12}$。}
-% \begin{macro}[internal]{\@@_listings_escape_backslash:}
+% \begin{macro}{\@@_listings_escape_backslash:}
 % \tn{catcode} 为 $12$ 的 |\| 需要双写转义。
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npx \@@_listings_escape_backslash:
@@ -10020,7 +10750,7 @@
 \AtEndOfPackage { \@@_reload:N \g_@@_encname_clist }
 %    \end{macrocode}
 %
-% \begin{macro}[internal]{\ReloadXunicode}
+% \begin{macro}[int]{\ReloadXunicode}
 % 参数可以是多个编码,设置这些编码对应的命令。如果编码没有预先声明,则给出
 % 一个错误警告。
 %    \begin{macrocode}
@@ -10068,7 +10798,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\DeclareUTFmathsymbols}
+% \begin{macro}[int]{\DeclareUTFmathsymbols}
 % \changes{v3.2.8}{2013/12/04}{修正 \tn{UseMathAsText} 的功能,恢复 \tn{hbar}
 % 和增加以 \texttt{text} 打头的文本符号命令。}
 % 将文本符号定义为 \tn{protected} 宏后,为了与 \pkg{hyperref} 的书签功能兼容
@@ -10118,7 +10848,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal,pTF]{\@@_glyph_if_exist:n}
+% \begin{macro}[pTF]{\@@_glyph_if_exist:n}
 % 判断字符在当前字体中是否存在。
 %    \begin{macrocode}
 \prg_new_conditional:Npnn \@@_glyph_if_exist:n #1 { p , T , F , TF }
@@ -10129,7 +10859,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\UndeclareUTFcharacter}
+% \begin{macro}[int]{\UndeclareUTFcharacter}
 % 取消编码 |#1| 下的符号命令 |#3|。
 %    \begin{macrocode}
 \RenewDocumentCommand \UndeclareUTFcharacter { O { \UTFencname } m m }
@@ -10142,7 +10872,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\UndeclareUTFcomposite}
+% \begin{macro}[int]{\UndeclareUTFcomposite}
 % 取消编码 |#1| 下的复合符号命令 |#3{#4}|。
 %    \begin{macrocode}
 \RenewDocumentCommand \UndeclareUTFcomposite { O { \UTFencname } m m m }
@@ -10157,7 +10887,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_composite_cs:Nnn,\@@_composite_cs:nnn}
+% \begin{macro}{\@@_composite_cs:Nnn,\@@_composite_cs:nnn}
 %    \begin{macrocode}
 \cs_new:Npx \@@_composite_cs:Nnn #1#2#3
   { \cs_to_str:N \\ #2 \exp_not:N \token_to_str:N #1 - \exp_not:N \tl_to_str:n {#3} }
@@ -10166,7 +10896,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_if_csname:nTF}
+% \begin{macro}{\@@_if_csname:nTF}
 % 判断 |#1| 是否可以作为控制序列的名字。这是因为 \pkg{xunicide} 使用了下面的定义。
 % \begin{verbatim}
 %   \DeclareUTFcharacter[\UTFencname]{x0149}{'n}
@@ -10185,7 +10915,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\DeclareUTFcharacter}
+% \begin{macro}[int]{\DeclareUTFcharacter}
 % 定义编码 |#1| 下的符号命令 |#3|,其对应符号的 Unicode 是 |#2|。
 %    \begin{macrocode}
 \RenewDocumentCommand \DeclareUTFcharacter { O { \UTFencname } m m }
@@ -10202,7 +10932,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_restore_hbar:}
+% \begin{macro}{\@@_restore_hbar:}
 % 恢复 \tn{hbar} 为原本定义。
 %    \begin{macrocode}
 \cs_new_protected_nopar:Npn \@@_restore_hbar:
@@ -10219,7 +10949,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_declare_character:Nnn}
+% \begin{macro}{\@@_declare_character:Nnn}
 % 通过 \texttt{lowercase} 技巧,直接由 Unicode |#3| 得到编码 |#2| 下的符号命令
 % |#1| 对应的实际字符。\tn{DeclareUTFSymbol} 的参数格式与 \tn{DeclareTextSymbol}
 % 完全一致。
@@ -10240,7 +10970,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\DeclareUTFSymbol,\DeclareUTFCommand}
+% \begin{macro}[int]{\DeclareUTFSymbol,\DeclareUTFCommand}
 % \tn{DeclareUTFCommand} 只能用于定义不带参数的符号命令。
 %    \begin{macrocode}
 \NewDocumentCommand \DeclareUTFSymbol { m O { \UTFencname } m }
@@ -10259,7 +10989,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_provide_text_command_default:N}
+% \begin{macro}{\@@_provide_text_command_default:N}
 % 如果控制序列 |#1| 已经存在,但不是符号命令,\pkg{xunicode} 会将它定义为
 % \tn{UTFencname} 编码下的符号命令。但是编码被转换之后,再使用这些控制序列,
 % \pkg{NFSS} 就会报错。为此需要给出这些符号命令的默认定义,与原来的意义相同。
@@ -10285,7 +11015,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_declare_character:NNnn}
+% \begin{macro}{\@@_declare_character:NNnn}
 % 使用编码 |#4| 下的符号命令 |#2| 的时候先判断它对应的实际字符 |#1| 在当前字体中
 % 是否存在。如果不存在则转换到 \tn{DeclareTextSymbolDefault} 中设置的编码或者使用
 % \cs{Declare\-Text\-Command\-Default} 中设置的命令。
@@ -10303,7 +11033,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_check_slot:n}
+% \begin{macro}{\@@_check_slot:n}
 % \pkg{xunicode} 中使用的 Unicode 格式是诸如 |x0022| 的形式,这就需要一些转换。
 %    \begin{macrocode}
 \cs_new_nopar:Npn \@@_check_slot:n #1
@@ -10317,7 +11047,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\DeclareUTFcomposite}
+% \begin{macro}[int]{\DeclareUTFcomposite}
 % 设置编码 |#1| 下的符号命令 |#3| 与它的参数 |#4| 的复合对应的符号的 Unicode 是 |#2|。
 %    \begin{macrocode}
 \RenewDocumentCommand \DeclareUTFcomposite { O { \UTFencname } m m m }
@@ -10330,7 +11060,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_declare_composite:Nnnn}
+% \begin{macro}{\@@_declare_composite:Nnnn}
 % 这里使用 \cs{tex_afterassignment:D} 是因为 \pkg{xunicode} 有如下的定义。
 % \begin{verbatim}
 %   \DeclareUTFcomposite[\UTFencname]{x02E8\char"02E5}{\tonebar}{25}
@@ -10352,7 +11082,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\DeclareUTFCompositeCommand}
+% \begin{macro}[int]{\DeclareUTFCompositeCommand}
 % 设置编码 |#2| 下的符号命令 |#1| 与它的参数 |#3| 的复合对应结果是 |#4|。不能直接用
 % \cs{Declare\-Text\-Composite\-Command} 来定义,它与我们的机制冲突。
 %    \begin{macrocode}
@@ -10361,7 +11091,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\DeclareUTFCompositeSymbol}
+% \begin{macro}[int]{\DeclareUTFCompositeSymbol}
 % 设置编码 |#2| 下的符号命令 |#1| 与它的参数 |#3| 的复合对应结果是 |#4|。不能直接用
 % \cs{Declare\-Text\-Composite} 来定义,它与我们的机制冲突。
 %    \begin{macrocode}
@@ -10373,7 +11103,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\DeclareUTFComposite}
+% \begin{macro}[int]{\DeclareUTFComposite}
 % 将 |#1| 设置为编码 |#2| 下的带一个参数的复合符号命令。
 %    \begin{macrocode}
 \NewDocumentCommand \DeclareUTFComposite { m O { \UTFencname } }
@@ -10381,7 +11111,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\DeclareUTFEncodedAccent}
+% \begin{macro}[int]{\DeclareUTFEncodedAccent}
 % |#1| 是重音命令,|#2| 是编码,|#3| 是组合重音符号的 Unicode,|#4| 是基本重音符号
 % 的 Unicode。当 |#1| 的参数为空时,输出 |#4|,否则是 |#1| 的参数与 |#3| 的组合。
 %    \begin{macrocode}
@@ -10390,7 +11120,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\DeclareUTFEncodedAccents}
+% \begin{macro}[int]{\DeclareUTFEncodedAccents}
 % |#1| 是重音命令,|#2| 是编码,|#3| 和 |#4| 都是组合重音符号的 Unicode。
 % 输出 |#1| 与 |#3|、|#4| 的组合。
 %    \begin{macrocode}
@@ -10399,7 +11129,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\DeclareUTFEncodedSymbol}
+% \begin{macro}[int]{\DeclareUTFEncodedSymbol}
 % |#1| 是带参数的符号命令,|#2| 是编码,|#3| 是组合符号的 Unicode,|#4| 是基本符号的
 % Unicode。当 |#1| 的参数为空时,输出 |#4|,否则是 |#1| 的参数与 |#3| 的组合。
 %    \begin{macrocode}
@@ -10408,7 +11138,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\DeclareUTFEncodedCircle}
+% \begin{macro}[int]{\DeclareUTFEncodedCircle}
 % |#1| 是带参数的圆圈符号命令,|#2| 是编码,|#3| 是组合圆圈符号的 Unicode,|#4|
 % 是圆圈符号的 Unicode。当 |#1| 的参数为空时,输出 |#4|,否则是 |#1| 的参数与 |#4| 的组合。
 %    \begin{macrocode}
@@ -10417,7 +11147,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\DeclareEncodedCompositeCharacter}
+% \begin{macro}[int]{\DeclareEncodedCompositeCharacter}
 %    \begin{macrocode}
 \RenewDocumentCommand \DeclareEncodedCompositeCharacter { m m m m }
   { \DeclareUTFEncodedSymbol #2 [#1] { "#3 } { "0#4 } }
@@ -10424,7 +11154,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\DeclareEncodedCompositeAccents}
+% \begin{macro}[int]{\DeclareEncodedCompositeAccents}
 % \changes{v3.2.9}{2013/12/07}{修正 \pkg{xunicode} 中的错误定义。}
 %    \begin{macrocode}
 \RenewDocumentCommand \DeclareEncodedCompositeAccents { m m m m }
@@ -10432,7 +11162,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\DeclareUTFDoubleEncodedAccent}
+% \begin{macro}[int]{\DeclareUTFDoubleEncodedAccent}
 % \changes{v3.2.10}{2014/02/20}{改进 \tn{t} 等的定义方式。}
 %    \begin{macrocode}
 \NewDocumentCommand \DeclareUTFDoubleEncodedAccent { m O { \UTFencname } m m }
@@ -10440,7 +11170,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\DeclareUTFDoubleEncodedSymbol}
+% \begin{macro}[int]{\DeclareUTFDoubleEncodedSymbol}
 % \changes{v3.2.10}{2014/02/21}{改进 \tn{sliding} 等的定义方式。}
 %    \begin{macrocode}
 \NewDocumentCommand \DeclareUTFDoubleEncodedSymbol { m O { \UTFencname } m m }
@@ -10448,7 +11178,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_declare_composite:Nnn}
+% \begin{macro}{\@@_declare_composite:Nnn}
 % 通过 \texttt{lowercase} 技巧,直接由重音符号的 Unicode 得到实际字符。
 %    \begin{macrocode}
 \cs_new_protected:Npn \@@_declare_composite:Nnn #1#2#3
@@ -10456,7 +11186,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_text_composite:nnn}
+% \begin{macro}{\@@_text_composite:nnn}
 %    \begin{macrocode}
 \cs_new_protected:Npn \@@_text_composite:nnn #1#2#3
   {
@@ -10482,7 +11212,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_declare_encoded:NNnnn}
+% \begin{macro}{\@@_declare_encoded:NNnnn}
 % 通过 \texttt{lowercase} 技巧,直接由重音符号的 Unicode 得到实际字符。
 %    \begin{macrocode}
 \cs_new_protected:Npn \@@_declare_encoded:NNnnn #1#2#3#4#5
@@ -10504,7 +11234,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_text_combine:NnnNNn}
+% \begin{macro}{\@@_text_combine:NnnNNn}
 % 若重音命令 |#2| 与它的参数 |#6| 的复合已经由 \tn{DeclareUTFcomposite} 设置,
 % 并且在当前字体中存在该字符,则直接使用。否则使用组合命令。
 %    \begin{macrocode}
@@ -10529,7 +11259,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_combine_symbol:nnNNn}
+% \begin{macro}{\@@_combine_symbol:nnNNn}
 %     \begin{macrocode}
 \cs_new_protected:Npn \@@_combine_symbol:nnNNn
   { \@@_text_combine:NnnNNn \@@_add_symbol:nnNN }
@@ -10550,7 +11280,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_combine_accent:nnNNn,\@@_add_accent:nnNN}
+% \begin{macro}{\@@_combine_accent:nnNNn,\@@_add_accent:nnNN}
 % 若组合重音符号的 |#3| 和基本重音符号 |#4| 在当前字体中都不存在,则转换到
 % \cs{Declare\-Text\-Accent\-Default} 设置的编码或者使用
 % \tn{DeclareTextCommandDefault} 中设置的命令。\texttt{0.9999} 版以前的 \XeTeX
@@ -10582,7 +11312,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_combine_accents:nnNNn,\@@_add_accents:nnNN}
+% \begin{macro}{\@@_combine_accents:nnNNn,\@@_add_accents:nnNN}
 %    \begin{macrocode}
 \cs_new_protected:Npn \@@_combine_accents:nnNNn
   { \@@_text_combine:NnnNNn \@@_add_accents:nnNN }
@@ -10601,7 +11331,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_combine_circle:nnNNn,\@@_add_circle:nnNN,\@@_add_circle:nN}
+% \begin{macro}{\@@_combine_circle:nnNNn,\@@_add_circle:nnNN,\@@_add_circle:nN}
 % 对圆圈中的数字或者字母适当缩小,以适合圆圈的大小。只有字体中存在
 % \texttt{U+25EF} 时,才使用这里的设置,否则还还是 \LaTeX\ 中的设置。
 %    \begin{macrocode}
@@ -10648,7 +11378,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\settextcircledratio}
+% \begin{macro}[int]{\settextcircledratio}
 % 设置圆圈中文字的宽度与圆圈宽度的比例,预设为 \texttt{0.7}。
 %    \begin{macrocode}
 \NewDocumentCommand \settextcircledratio { m }
@@ -10658,7 +11388,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_combine_double_accent:nnNNn}
+% \begin{macro}{\@@_combine_double_accent:nnNNn}
 % 使 \tn{t} 等组合重音符号放在参数的第一个字母的右边。
 %    \begin{macrocode}
 \cs_new_protected:Npn \@@_combine_double_accent:nnNNn
@@ -10684,7 +11414,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_combine_double_symbol:nnNNn}
+% \begin{macro}{\@@_combine_double_symbol:nnNNn}
 % 使 \tn{sliding} 等组合重音符号放在参数的第一个字母的右边。
 %    \begin{macrocode}
 \cs_new_protected:Npn \@@_combine_double_symbol:nnNNn
@@ -10706,7 +11436,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_add_double_symbol:nN}
+% \begin{macro}{\@@_add_double_symbol:nN}
 % 如果参数的第一个记号是字母类、其它符号类或者由 \tn{chardef} 定义,则将组合符号
 % 放在它的右边,否则不作处理。
 %    \begin{macrocode}
@@ -10734,7 +11464,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\AtBeginUTFCommand,\AtEndUTFCommand}
+% \begin{macro}[int]{\AtBeginUTFCommand,\AtEndUTFCommand}
 % 设置在符号命令前后使用的钩子,可选参数用于指定单个符号命名。可以用 |#1|
 % 引用带参数的组合符号命令的参数或者符号命令对应的符号。
 % \changes{v3.2.6}{2013/08/15}{可以指定特定符号命令使用的钩子。}
@@ -10764,7 +11494,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_set_cmd_hook:nnn}
+% \begin{macro}{\@@_set_cmd_hook:nnn}
 %    \begin{macrocode}
 \cs_new_protected:Npn \@@_set_cmd_hook:nnn #1#2#3
   {
@@ -10783,7 +11513,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\@@_begin_hook:nn,\@@_end_hook:nn}
+% \begin{macro}{\@@_begin_hook:nn,\@@_end_hook:nn}
 %    \begin{macrocode}
 \cs_new_protected:Npn \@@_begin_hook:nn #1#2
   {
@@ -10802,7 +11532,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\DeclareUTFTIPACommand}
+% \begin{macro}[int]{\DeclareUTFTIPACommand}
 % \changes{v3.2.10}{2014/02/20}{检查 \tn{t} 和 \tn{sliding} 的参数是否以 \tn{textipa} 开头。}
 %    \begin{macrocode}
 \NewDocumentCommand \DeclareUTFTIPACommand { O { \UTFencname } m }

Modified: trunk/Master/texmf-dist/source/xelatex/xecjk/xeCJK.ins
===================================================================
--- trunk/Master/texmf-dist/source/xelatex/xecjk/xeCJK.ins	2018-01-30 21:05:58 UTC (rev 46496)
+++ trunk/Master/texmf-dist/source/xelatex/xecjk/xeCJK.ins	2018-01-30 21:06:15 UTC (rev 46497)
@@ -7,8 +7,8 @@
 %% xeCJK.dtx  (with options: `install')
 %% 
 %%     Copyright (C) 2007--2010 by Wenchang Sun <sunwch at nankai.edu.cn>
-%%     Copyright (C) 2009--2017 by Leo Liu <leoliu.pku at gmail.com>
-%%     Copyright (C) 2012--2017 by Qing Lee <sobenlee at gmail.com>
+%%     Copyright (C) 2009--2018 by Leo Liu <leoliu.pku at gmail.com>
+%%     Copyright (C) 2012--2018 by Qing Lee <sobenlee at gmail.com>
 %% ----------------------------------------------------------------------
 %% 
 %%     This work may be distributed and/or modified under the
@@ -33,8 +33,8 @@
 \preamble
 
     Copyright (C) 2007--2010 by Wenchang Sun <sunwch at nankai.edu.cn>
-    Copyright (C) 2009--2017 by Leo Liu <leoliu.pku at gmail.com>
-    Copyright (C) 2012--2017 by Qing Lee <sobenlee at gmail.com>
+    Copyright (C) 2009--2018 by Leo Liu <leoliu.pku at gmail.com>
+    Copyright (C) 2012--2018 by Qing Lee <sobenlee at gmail.com>
 ----------------------------------------------------------------------
 
     This work may be distributed and/or modified under the

Modified: trunk/Master/texmf-dist/tex/xelatex/xecjk/xeCJK-listings.sty
===================================================================
--- trunk/Master/texmf-dist/tex/xelatex/xecjk/xeCJK-listings.sty	2018-01-30 21:05:58 UTC (rev 46496)
+++ trunk/Master/texmf-dist/tex/xelatex/xecjk/xeCJK-listings.sty	2018-01-30 21:06:15 UTC (rev 46497)
@@ -7,8 +7,8 @@
 %% xeCJK.dtx  (with options: `listings')
 %% 
 %%     Copyright (C) 2007--2010 by Wenchang Sun <sunwch at nankai.edu.cn>
-%%     Copyright (C) 2009--2017 by Leo Liu <leoliu.pku at gmail.com>
-%%     Copyright (C) 2012--2017 by Qing Lee <sobenlee at gmail.com>
+%%     Copyright (C) 2009--2018 by Leo Liu <leoliu.pku at gmail.com>
+%%     Copyright (C) 2012--2018 by Qing Lee <sobenlee at gmail.com>
 %% ----------------------------------------------------------------------
 %% 
 %%     This work may be distributed and/or modified under the
@@ -29,10 +29,10 @@
 %% 
 \NeedsTeXFormat{LaTeX2e}
 \RequirePackage{expl3}
-\GetIdInfo$Id: xeCJK.dtx 5a18688 2017-11-22 19:12:51 +0800 Qing Lee <sobenlee at gmail.com> $
+\GetIdInfo$Id: xeCJK.dtx 54327e6 2018-01-28 19:10:14 +0800 Qing Lee <sobenlee at gmail.com> $
   {xeCJK patch file for listings}
 \ProvidesExplPackage{xeCJK-listings}
-  {\ExplFileDate}{3.5.1}{\ExplFileDescription}
+  {\ExplFileDate}{3.6.0}{\ExplFileDescription}
 \DeclareOption* { \PassOptionsToPackage { \CurrentOption } { xeCJK } }
 \ProcessOptions \scan_stop:
 \RequirePackage { xeCJK }

Modified: trunk/Master/texmf-dist/tex/xelatex/xecjk/xeCJK.cfg
===================================================================
--- trunk/Master/texmf-dist/tex/xelatex/xecjk/xeCJK.cfg	2018-01-30 21:05:58 UTC (rev 46496)
+++ trunk/Master/texmf-dist/tex/xelatex/xecjk/xeCJK.cfg	2018-01-30 21:06:15 UTC (rev 46497)
@@ -6,10 +6,10 @@
 %%
 %% xeCJK.dtx  (with options: `config')
 %% 
-\GetIdInfo$Id: xeCJK.dtx 5a18688 2017-11-22 19:12:51 +0800 Qing Lee <sobenlee at gmail.com> $
+\GetIdInfo$Id: xeCJK.dtx 54327e6 2018-01-28 19:10:14 +0800 Qing Lee <sobenlee at gmail.com> $
   {Configuration file for xeCJK package}
 \ProvidesExplFile{\ExplFileName.cfg}
-  {\ExplFileDate}{3.5.1}{\ExplFileDescription}
+  {\ExplFileDate}{3.6.0}{\ExplFileDescription}
 
 %% 
 %%

Modified: trunk/Master/texmf-dist/tex/xelatex/xecjk/xeCJK.sty
===================================================================
--- trunk/Master/texmf-dist/tex/xelatex/xecjk/xeCJK.sty	2018-01-30 21:05:58 UTC (rev 46496)
+++ trunk/Master/texmf-dist/tex/xelatex/xecjk/xeCJK.sty	2018-01-30 21:06:15 UTC (rev 46497)
@@ -7,8 +7,8 @@
 %% xeCJK.dtx  (with options: `package')
 %% 
 %%     Copyright (C) 2007--2010 by Wenchang Sun <sunwch at nankai.edu.cn>
-%%     Copyright (C) 2009--2017 by Leo Liu <leoliu.pku at gmail.com>
-%%     Copyright (C) 2012--2017 by Qing Lee <sobenlee at gmail.com>
+%%     Copyright (C) 2009--2018 by Leo Liu <leoliu.pku at gmail.com>
+%%     Copyright (C) 2012--2018 by Qing Lee <sobenlee at gmail.com>
 %% ----------------------------------------------------------------------
 %% 
 %%     This work may be distributed and/or modified under the
@@ -29,10 +29,10 @@
 %% 
 \NeedsTeXFormat{LaTeX2e}
 \RequirePackage{expl3}
-\GetIdInfo$Id: xeCJK.dtx 5a18688 2017-11-22 19:12:51 +0800 Qing Lee <sobenlee at gmail.com> $
+\GetIdInfo$Id: xeCJK.dtx 54327e6 2018-01-28 19:10:14 +0800 Qing Lee <sobenlee at gmail.com> $
   {Typesetting CJK scripts with XeLaTeX}
 \ProvidesExplPackage{\ExplFileName}
-  {\ExplFileDate}{3.5.1}{\ExplFileDescription}
+  {\ExplFileDate}{3.6.0}{\ExplFileDescription}
 \msg_new:nnn { xeCJK } { Require-XeTeX }
   {
     The~xeCJK~package~requires~XeTeX~to~function.\\\\
@@ -49,7 +49,7 @@
     using~your~TeX~package~manager~or~from~CTAN.\\
     \str_if_eq:nnT {#1} { expl3 } { Loading~xeCJK~will~abort! }
   }
-\@ifpackagelater { expl3 } { 2017/07/19 } { }
+\@ifpackagelater { expl3 } { 2017/12/16 } { }
   { \msg_critical:nnn { xeCJK } { l3-too-old } { expl3 } }
 \prg_new_conditional:Npnn \xeCJK_if_package_loaded:n #1 { p , T , F , TF }
   {
@@ -98,8 +98,10 @@
 \cs_new_protected_nopar:Npn \__xeCJK_warning:nxx  { \msg_warning:nnxx  { xeCJK } }
 \cs_new_protected_nopar:Npn \__xeCJK_warning:nxxx { \msg_warning:nnxxx { xeCJK } }
 \cs_new_protected_nopar:Npn \__xeCJK_info:nxx     { \msg_info:nnxx     { xeCJK } }
-\cs_new_protected_nopar:Npn \xeCJK_allow_break: { \tex_penalty:D \c_zero }
-\cs_new_protected_nopar:Npn \xeCJK_no_break: { \tex_penalty:D \c_ten_thousand }
+\cs_new_protected_nopar:Npn \xeCJK_allow_break:
+  { \tex_penalty:D \c_zero }
+\cs_new_protected_nopar:Npn \xeCJK_no_break:
+  { \tex_penalty:D \c_ten_thousand }
 \tl_new:N \g__xeCJK_at_end_preamble_hook_tl
 \tl_new:N \g__xeCJK_after_preamble_hook_tl
 \tl_new:N \g__xeCJK_after_end_preamble_hook_tl
@@ -150,8 +152,6 @@
   { \tl_put_right:Nn \l__xeCJK_shipout_hook_tl }
 \tl_new:N \l__xeCJK_shipout_hook_tl
 \bool_new:N \l__xeCJK_shipout_hook_bool
-\cs_new_nopar:Npn \xeCJK_reverse:nnn #1#2#3
-  { \str_if_eq_x:nnTF {#1} {#2} {#3} {#2} }
 \cs_new_protected_nopar:Npn \xeCJK_tl_remove_outer_braces:N #1
   { \tl_set:Nx #1 { \exp_args:NV \xeCJK_tl_remove_outer_braces:n #1 } }
 \cs_new:Npn \xeCJK_tl_remove_outer_braces:n #1
@@ -190,7 +190,7 @@
   }
 \tl_const:Nn \c_xeCJK_space_skip_tl
   {
-    \int_compare:nNnTF \g__xeCJK_spacefactor_int = \c_one_thousand
+    \int_compare:nNnTF \g__xeCJK_space_factor_int = \c_one_thousand
       {
         \skip_if_eq:nnTF \tex_spaceskip:D \c_zero_skip
           {
@@ -203,9 +203,12 @@
       {
         \skip_if_eq:nnTF \tex_spaceskip:D \c_zero_skip
           {
-            \int_compare:nNnTF \g__xeCJK_spacefactor_int < { 2000 }
-              { \__xeCJK_space_skip_scale:nnn { \tex_fontdimen:D \c_two \tex_font:D } }
+            \int_compare:nNnTF \g__xeCJK_space_factor_int < { 2000 }
               {
+                \__xeCJK_space_skip_scale:nnn
+                  { \tex_fontdimen:D \c_two \tex_font:D }
+              }
+              {
                 \skip_if_eq:nnTF \tex_xspaceskip:D \c_zero_skip
                   {
                     \__xeCJK_space_skip_scale:nnn
@@ -220,13 +223,16 @@
               { \tex_fontdimen:D \c_four  \tex_font:D }
           }
           {
-            \int_compare:nNnTF \g__xeCJK_spacefactor_int < { 2000 }
+            \int_compare:nNnTF \g__xeCJK_space_factor_int < { 2000 }
               { \__xeCJK_space_skip_scale:nnn { \tex_spaceskip:D } }
               {
                 \skip_if_eq:nnTF \tex_xspaceskip:D \c_zero_skip
                   {
                     \__xeCJK_space_skip_scale:nnn
-                      { \tex_spaceskip:D + \tex_fontdimen:D \c_seven \tex_font:D }
+                      {
+                        \tex_spaceskip:D +
+                        \tex_fontdimen:D \c_seven \tex_font:D
+                      }
                   }
                   { \tex_xspaceskip:D \use_none:nn }
               }
@@ -238,13 +244,13 @@
 \cs_new_nopar:Npn \__xeCJK_space_skip_scale:nnn #1#2#3
   {
     \dim_eval:n {#1}
-    plus \fp_eval:n { \g__xeCJK_spacefactor_int / 1000 } #2
+    plus \fp_eval:n { \g__xeCJK_space_factor_int / 1000 } #2
     minus
       \int_div_truncate:nn
-        { 1000 * \tex_number:D #3 } { \g__xeCJK_spacefactor_int } sp
+        { 1000 * \tex_number:D #3 } { \g__xeCJK_space_factor_int } sp
   }
-\int_new:N \g__xeCJK_spacefactor_int
-\int_gset_eq:NN \g__xeCJK_spacefactor_int \c_one_thousand
+\int_new:N \g__xeCJK_space_factor_int
+\int_gset_eq:NN \g__xeCJK_space_factor_int \c_one_thousand
 \cs_new_protected_nopar:Npn \xeCJK_glue_to_skip:nN #1#2
   {
     \group_begin:
@@ -271,7 +277,10 @@
     \fi:
   }
 \cs_new_protected:Npn \xeCJK_int_until_do:nn #1#2
-  { \__xeCJK_int_until_do:wn \use_none:n { \reverse_if:N \if_int_compare:w #1#2 } }
+  {
+    \__xeCJK_int_until_do:wn \use_none:n
+      { \reverse_if:N \if_int_compare:w #1#2 }
+  }
 \cs_new_protected:Npn \__xeCJK_int_until_do:wn \use_none:n #1
   { #1 \exp_after:wN \__xeCJK_int_until_do:wn \fi: \use_none:n {#1} }
 \int_new:N \l__xeCJK_begin_int
@@ -278,7 +287,7 @@
 \int_new:N \l__xeCJK_end_int
 \cs_new_protected:Npn \xeCJK_peek_catcode_ignore_spaces:NTF #1#2#3
   {
-    \cs_set_eq:NN \l__peek_search_token #1 \scan_stop:
+    \cs_set_eq:NN \l__xeCJK_peek_search_token #1 \scan_stop:
     \tl_set:Nn \__xeCJK_peek_catcode_true:w  { \group_align_safe_end: #2 }
     \tl_set:Nn \__xeCJK_peek_catcode_false:w { \group_align_safe_end: #3 }
     \bool_set_false:N \l__xeCJK_peek_ignore_spaces_bool
@@ -294,7 +303,7 @@
       \tex_romannumeral:D 0
     \else:
       \if_catcode:w
-        \exp_not:N \l_peek_token \exp_not:N \l__peek_search_token
+        \exp_not:N \l_peek_token \exp_not:N \l__xeCJK_peek_search_token
         \exp_after:wN \exp_after:wN
         \exp_after:wN \__xeCJK_peek_catcode_true:w
       \else:
@@ -303,6 +312,7 @@
       \fi:
     \fi:
   }
+\cs_new_eq:NN \l__xeCJK_peek_search_token ?
 \tl_new:N \__xeCJK_peek_catcode_true:w
 \tl_new:N \__xeCJK_peek_catcode_false:w
 \bool_new:N \l__xeCJK_peek_ignore_spaces_bool
@@ -357,16 +367,17 @@
 \group_end:
 \prg_new_conditional:Npnn \xeCJK_if_CJK_class:N #1 { p , T , F , TF }
   {
-    \if_cs_exist:w \__xeCJK_CJK_class_tl:n { \xeCJK_token_value_class:N #1 } \cs_end:
+    \if_cs_exist:w
+      \__xeCJK_CJK_class_tl:n { \xeCJK_token_value_class:N #1 }
+    \cs_end:
       \prg_return_true: \else: \prg_return_false: \fi:
   }
 \cs_new_nopar:Npn \__xeCJK_CJK_class_tl:n #1
   { c__xeCJK_CJK_class_ \int_eval:n {#1} _tl }
-\cs_generate_variant:Nn \__xeCJK_CJK_class_tl:n { c }
 \prg_new_conditional:Npnn \xeCJK_if_same_class:NN #1#2 { p , T , F , TF }
   {
-    \if_int_compare:w
-      \xeCJK_token_value_class:N #1 = \xeCJK_token_value_class:N #2 \exp_stop_f:
+    \if_int_compare:w \xeCJK_token_value_class:N #1 =
+                      \xeCJK_token_value_class:N #2 \exp_stop_f:
       \prg_return_true: \else: \prg_return_false: \fi:
   }
 \keys_define:nn { xeCJK / options }
@@ -376,8 +387,10 @@
     xeCJKactive / false .code:n = { \makexeCJKinactive } ,
     xeCJKactive      .default:n = { true }
   }
-\NewDocumentCommand \makexeCJKactive   { } { \xetex_interchartokenstate:D = \c_one  }
-\NewDocumentCommand \makexeCJKinactive { } { \xetex_interchartokenstate:D = \c_zero }
+\NewDocumentCommand \makexeCJKactive   { }
+  { \xetex_interchartokenstate:D = \c_one }
+\NewDocumentCommand \makexeCJKinactive { }
+  { \xetex_interchartokenstate:D = \c_zero }
 \char_set_catcode_ignore:n { "FEFF }
 \seq_new:N \g__xeCJK_class_seq
 \seq_new:N \g__xeCJK_new_class_seq
@@ -386,10 +399,12 @@
     \int_if_exist:cTF { \__xeCJK_class_csname:n {#1} }
       { \__xeCJK_error:nx { class-already-defined } {#1} }
       {
-        \exp_args:Nc \newXeTeXintercharclass { \__xeCJK_class_csname:n {#1} }
+        \exp_args:Nc \newXeTeXintercharclass
+          { \__xeCJK_class_csname:n {#1} }
         \clist_new:c { g__xeCJK_#1_range_clist }
         \seq_gput_right:Nn \g__xeCJK_class_seq {#1}
-        \seq_gput_right:Nv \g__xeCJK_new_class_seq { \__xeCJK_class_csname:n {#1} }
+        \seq_gput_right:Nv \g__xeCJK_new_class_seq
+          { \__xeCJK_class_csname:n {#1} }
       }
   }
 \cs_new_protected_nopar:Npn \xeCJK_save_class:nn #1#2
@@ -447,24 +462,32 @@
   }
 \clist_const:Nn \c__xeCJK_PR_chars_clist
   { "FE69 , "FF04 , "FFE1 , "FFE5 , "FFE6 }
-\clist_const:Nx \c__xeCJK_FullLeft_chars_clist
-  {
-    \c__xeCJK_OP_chars_clist ,
-    \c__xeCJK_PR_chars_clist
-  }
+\clist_new:N \c__xeCJK_FullLeft_chars_clist
+\clist_gconcat:NNN \c__xeCJK_FullLeft_chars_clist
+                   \c__xeCJK_OP_chars_clist
+                   \c__xeCJK_PR_chars_clist
 \clist_const:Nn \c__xeCJK_CL_chars_clist
   {
-    "00B7 , "2019 , "201D , "2013 , "2014 , "2025 , "2026 , "2027 ,
+    "00B7 , "2019 , "201D , "2013 , "2014 , "2025 , "2026 , "2027 , "2E3A ,
     "3001 , "3002 , "3009 , "300B , "300D , "300F , "3011 , "3015 , "3017 , "3019 ,
     "301B , "301E , "301F , "FE11 , "FE12 , "FE18 , "FE36 , "FE38 , "FE3A , "FE3C ,
     "FE3E , "FE40 , "FE42 , "FE44 , "FE48 , "FE50 , "FE52 , "FE5A , "FE5C , "FE5E ,
     "FF09 , "FF0C , "FF0E , "FF3D , "FF5D , "FF60 , "FF61 , "FF63 , "FF64
   }
-\clist_const:Nn \c__xeCJK_hyphens_chars_clist { "301C , "30A0 }
+\clist_const:Nn \c__xeCJK_hyphens_chars_clist
+  { "301C , "30A0 , "FF5E }
 \clist_const:Nn \c__xeCJK_iteration_marks_chars_clist
   { "3005 , "303B , "309D , "309E , "30FD , "30FE }
 \clist_const:Nn \c__xeCJK_NS_chars_clist
   { "30FB , "FE54 , "FE55 , "FF1A , "FF1B , "FF65 , "16FE0 }
+\AtEndOfPackage
+  {
+    \cs_set:Npn \__xeCJK_tmp:w #1
+      { \char_generate:nn {#1} { \c_twelve } }
+    \__xeCJK_add_special_punct:nn { middle }
+      { \clist_map_function:NN \c__xeCJK_hyphens_chars_clist \__xeCJK_tmp:w }
+    \cs_undefine:N \__xeCJK_tmp:w
+  }
 \clist_const:Nn \c__xeCJK_EX_chars_clist
   { "FE15 , "FE16 , "FE56 , "FE57 , "FF01 , "FF1F }
 \clist_const:Nn \c__xeCJK_IS_chars_clist { "FE10 , "FE13 , "FE14 }
@@ -478,14 +501,20 @@
     "FF70
   }
 \clist_const:Nn \c__xeCJK_PO_chars_clist { "FE6A , "FF05 , "FFE0 }
-\clist_const:Nx \c__xeCJK_FullRight_chars_clist
+\clist_new:N \c__xeCJK_FullRight_chars_clist
+\tl_map_inline:nn
   {
-    \c__xeCJK_CL_chars_clist ,
-    \c__xeCJK_NS_chars_clist ,
-    \c__xeCJK_EX_chars_clist ,
-    \c__xeCJK_IS_chars_clist ,
+    \c__xeCJK_CL_chars_clist
+    \c__xeCJK_NS_chars_clist
+    \c__xeCJK_EX_chars_clist
+    \c__xeCJK_IS_chars_clist
     \c__xeCJK_PO_chars_clist
+    \c__xeCJK_hyphens_chars_clist
   }
+  {
+    \clist_gconcat:NNN \c__xeCJK_FullRight_chars_clist
+                       \c__xeCJK_FullRight_chars_clist #1
+  }
 \clist_const:Nn \c__xeCJK_CJK_chars_clist
   {
     "2E80 -> "2EFF ,
@@ -537,7 +566,8 @@
     "A960 -> "A97F ,
     "D7B0 -> "D7FF
   }
-\cs_new_nopar:Npn \xeCJK_class_num:n #1 { \use:c { \__xeCJK_class_csname:n {#1} } }
+\cs_new_nopar:Npn \xeCJK_class_num:n #1
+  { \use:c { \__xeCJK_class_csname:n {#1} } }
 \NewDocumentCommand \xeCJKDeclareCharClass { s > { \TrimSpaces } m m }
   {
     \xeCJK_declare_char_class:nn {#2} {#3}
@@ -565,7 +595,6 @@
 \NewDocumentCommand \__xeCJK_set_char_class_aux:Nnw
   { m > { \SplitArgument { 1 } { -> } } m } { #1 #2 }
 \cs_generate_variant:Nn \clist_gconcat:NNN { cc }
-\cs_generate_variant:Nn \xeCJK_declare_char_class:nn { nc }
 \cs_new_protected_nopar:Npn \__xeCJK_check_num_range:nnNN #1#2#3#4
   {
     \bool_lazy_or:nnTF
@@ -576,8 +605,8 @@
         \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} } }
+        \int_set:Nn #3 { \int_min:nn {#1} { \tl_if_novalue:nTF {#2} {#1} {#2} } }
+        \int_set:Nn #4 { \int_max:nn {#1} { \tl_if_novalue:nTF {#2} {#1} {#2} } }
       }
   }
 \token_if_letter:NF ^^^^ac00
@@ -636,10 +665,16 @@
   }
 \xeCJKResetCharClass
 \cs_new_protected_nopar:Npn \xeCJK_inter_class_toks:nnn #1#2#3
-  { \xetex_interchartoks:D \xeCJK_class_num:n {#1} ~ \xeCJK_class_num:n {#2} = {#3} }
-\cs_generate_variant:Nn \xeCJK_inter_class_toks:nnn { nnc , nnx }
+  {
+    \xetex_interchartoks:D \xeCJK_class_num:n {#1} ~
+                           \xeCJK_class_num:n {#2} = {#3}
+  }
+\cs_generate_variant:Nn \xeCJK_inter_class_toks:nnn { nnx }
 \cs_new_nopar:Npn \xeCJK_get_inter_class_toks:nn #1#2
-  { \tex_the:D \xetex_interchartoks:D \xeCJK_class_num:n {#1} ~ \xeCJK_class_num:n {#2} }
+  {
+    \tex_the:D \xetex_interchartoks:D \xeCJK_class_num:n {#1} ~
+                                      \xeCJK_class_num:n {#2}
+  }
 \cs_new_protected_nopar:Npn \xeCJK_clear_inter_class_toks:nn #1#2
   { \xeCJK_inter_class_toks:nnn {#1} {#2} { \prg_do_nothing: } }
 \cs_new_protected_nopar:Npn \xeCJK_pre_inter_class_toks:nnn #1#2#3
@@ -653,13 +688,15 @@
     \xeCJK_inter_class_toks:nnx {#1} {#2}
       { \xeCJK_get_inter_class_toks:nn {#1} {#2} \exp_not:n {#3} }
   }
-\cs_generate_variant:Nn \xeCJK_app_inter_class_toks:nnn { nnc , nnx }
+\cs_generate_variant:Nn \xeCJK_app_inter_class_toks:nnn { nnx }
 \cs_new_protected_nopar:Npn \xeCJK_copy_inter_class_toks:nnnn #1#2#3#4
   {
-    \tl_set:Nx \l__xeCJK_tmp_tl { \xeCJK_get_inter_class_toks:nn {#3} {#4} }
+    \tl_set:Nx \l__xeCJK_tmp_tl
+      { \xeCJK_get_inter_class_toks:nn {#3} {#4} }
     \tl_if_empty:NTF \l__xeCJK_tmp_tl
       {
-        \tl_set:Nx \l__xeCJK_tmp_tl { \xeCJK_get_inter_class_toks:nn {#1} {#2} }
+        \tl_set:Nx \l__xeCJK_tmp_tl
+          { \xeCJK_get_inter_class_toks:nn {#1} {#2} }
         \tl_if_empty:NF \l__xeCJK_tmp_tl
           { \xeCJK_clear_inter_class_toks:nn {#1} {#2} }
       }
@@ -667,11 +704,13 @@
   }
 \cs_new_protected_nopar:Npn \xeCJK_replace_inter_class_toks:nnnn #1#2#3#4
   {
-    \tl_set:Nx \l__xeCJK_tmp_tl { \xeCJK_get_inter_class_toks:nn {#1} {#2} }
+    \tl_set:Nx \l__xeCJK_tmp_tl
+      { \xeCJK_get_inter_class_toks:nn {#1} {#2} }
     \tl_if_empty:NF \l__xeCJK_tmp_tl
       {
         \tl_replace_all:Nnn \l__xeCJK_tmp_tl {#3} {#4}
-        \xeCJK_inter_class_toks:nnx {#1} {#2} { \exp_not:o \l__xeCJK_tmp_tl }
+        \xeCJK_inter_class_toks:nnx {#1} {#2}
+          { \exp_not:o \l__xeCJK_tmp_tl }
       }
   }
 \cs_new_protected_nopar:Npn \xeCJK_clear_Boundary_and_CJK_toks:
@@ -682,8 +721,8 @@
       {
         \exp_not:o { \xeCJK_clear_Boundary_and_CJK_toks: }
         \xetex_interchartoks:D
-          \xeCJK_class_num:n { Boundary } ~ \xeCJK_class_num:n {#1}
-            = { \exp_not:N \prg_do_nothing: }
+          \xeCJK_class_num:n { Boundary } ~
+          \xeCJK_class_num:n {#1} = { \exp_not:N \prg_do_nothing: }
       }
   }
 \seq_new:N \g__xeCJK_base_class_seq
@@ -695,7 +734,9 @@
 \cs_new_protected_nopar:Npn \__xeCJK_save_CJK_class:n #1
   {
     \seq_gput_right:Nn \g__xeCJK_CJK_class_seq {#1}
-    \tl_const:cn { \__xeCJK_CJK_class_tl:c { \__xeCJK_class_csname:n {#1} } } {#1}
+    \tl_const:cn
+      { \__xeCJK_CJK_class_tl:n { \use:c { \__xeCJK_class_csname:n {#1} } } }
+      {#1}
     \__xeCJK_update_clear_toks:n {#1}
   }
 \clist_map_function:nN
@@ -704,7 +745,7 @@
   {
     \c_group_begin_token
     \bool_set_true:N \l__xeCJK_CJK_group_bool
-    \int_gset_eq:NN \g__xeCJK_spacefactor_int \c_one_thousand
+    \int_gset_eq:NN \g__xeCJK_space_factor_int \c_one_thousand
     \int_zero:N \xetex_dashbreakstate:D
   }
 \bool_new:N \l__xeCJK_CJK_group_bool
@@ -747,8 +788,10 @@
   }
 \clist_map_inline:nn { Default , HalfLeft }
   {
-    \xeCJK_inter_class_toks:nnn { Boundary } {#1} { \xeCJK_Boundary_and_Default: }
-    \xeCJK_app_inter_class_toks:nnn { CJK } {#1} { \CJKecglue }
+    \xeCJK_inter_class_toks:nnn { Boundary } {#1}
+      { \xeCJK_Boundary_and_Default: }
+    \xeCJK_app_inter_class_toks:nnn { CJK } {#1}
+      { \CJKecglue }
   }
 \cs_new_protected_nopar:Npn \xeCJK_Boundary_and_Default:
   { \xeCJK_check_for_ecglue: }
@@ -779,7 +822,9 @@
       {
         \xeCJK_if_last_node:nTF { CJK }
           {
-            \skip_if_eq:nnTF { \l__xeCJK_last_skip } { \c_xeCJK_space_skip_tl }
+            \skip_if_eq:nnTF
+              { \l__xeCJK_last_skip }
+              { \c_xeCJK_space_skip_tl }
               { \xeCJK_remove_node: \CJKecglue }
               { \skip_horizontal:N \l__xeCJK_last_skip }
           }
@@ -791,7 +836,7 @@
   {
     \xeCJK_inter_class_toks:nnn {#1} { Boundary }
       {
-        \int_gset_eq:NN \g__xeCJK_spacefactor_int \tex_spacefactor:D
+        \int_gset_eq:NN \g__xeCJK_space_factor_int \tex_spacefactor:D
         \peek_meaning_remove:NTF \tex_italiccorrection:D
           {
             \tex_italiccorrection:D
@@ -820,10 +865,11 @@
     \xeCJK_if_last_node:nT { CJK-space }
       { \xeCJK_remove_node: \xeCJK_space_or_xecglue: }
   }
-\cs_new_eq:NN \xeCJK_check_for_ecglue_normalsp: \__xeCJK_check_for_ecglue_normalsp:
+\cs_new_eq:NN \xeCJK_check_for_ecglue_normalsp:
+              \__xeCJK_check_for_ecglue_normalsp:
 \xeCJK_inter_class_toks:nnn { NormalSpace } { Boundary }
   {
-    \int_gset_eq:NN \g__xeCJK_spacefactor_int \tex_spacefactor:D
+    \int_gset_eq:NN \g__xeCJK_space_factor_int \tex_spacefactor:D
     \peek_meaning_remove:NTF \tex_italiccorrection:D
       {
         \tex_italiccorrection:D
@@ -845,22 +891,44 @@
   }
 \cs_new_protected_nopar:Npn \xeCJK_check_for_glue:
   {
-    \bool_lazy_or:nnTF
-      { \xeCJK_if_last_node_p:n { CJK } }
-      { \xeCJK_if_last_node_p:n { CJK-space } }
-      { \xeCJK_remove_node: \CJKglue }
+    \int_compare:nNnTF \etex_lastnodetype:D = \c_twelve
+      { \__xeCJK_check_for_glue_auxi: }
       {
-        \xeCJK_if_last_node:nTF { CJK-widow }
-          { \xeCJK_remove_node: \xeCJK_widow_penalty: \CJKglue }
-          {
-            \bool_lazy_or:nnTF
-              { \xeCJK_if_last_node_p:n { default } }
-              { \int_compare_p:nNn \etex_lastnodetype:D = \c_ten }
-              { \xeCJK_remove_node: \CJKecglue }
-              { \xeCJK_check_for_xglue: }
-          }
+        \int_compare:nNnTF \etex_lastnodetype:D = \c_ten
+          { \xeCJK_remove_node: \CJKecglue }
+          { \__xeCJK_check_for_glue_auxii: }
       }
   }
+\cs_new_protected_nopar:Npn \__xeCJK_check_for_glue_auxi:
+  {
+    \dim_case:nn { \tex_lastkern:D }
+      {
+        { \__xeCJK_node:n { CJK } }
+        { \xeCJK_remove_node: \CJKglue }
+        { \__xeCJK_node:n { CJK-space } }
+        { \xeCJK_remove_node: \CJKglue }
+        { \__xeCJK_node:n { CJK-widow } }
+        { \xeCJK_remove_node: \xeCJK_widow_penalty: \CJKglue }
+        { \__xeCJK_node:n { default } }
+        { \xeCJK_remove_node: \CJKecglue }
+      }
+  }
+\cs_new_protected_nopar:Npn \__xeCJK_check_for_glue_auxii:
+  {
+    \xeCJK_if_last_punct:TF
+      { \__xeCJK_check_for_glue_auxiii: }
+      { \xeCJK_check_for_xglue: }
+  }
+\cs_new_protected_nopar:Npn \__xeCJK_check_for_glue_auxiii:
+  {
+    \bool_if:NT \l__xeCJK_last_penalty_bool
+      {
+        \__xeCJK_add_offset_skip:N \l__xeCJK_last_skip
+        \tex_penalty:D \l__xeCJK_last_penalty_int
+      }
+    \skip_horizontal:N \l__xeCJK_last_skip
+    \tl_if_eq:NNF \l__xeCJK_aligni_tl \c__xeCJK_left_tl { \CJKglue }
+  }
 \cs_new_eq:NN \xeCJK_check_for_xglue: \prg_do_nothing:
 \cs_new_protected_nopar:Npn \__xeCJK_check_for_xglue:
   {
@@ -879,7 +947,9 @@
   }
 \cs_new_protected_nopar:Npn \__xeCJK_check_for_xglue_aux:
   {
-    \skip_if_eq:nnTF { \l__xeCJK_last_skip } { \c_xeCJK_space_skip_tl }
+    \skip_if_eq:nnTF
+      { \l__xeCJK_last_skip }
+      { \c_xeCJK_space_skip_tl }
       {
         \xeCJK_if_last_node:nTF { CJK }
           { \xeCJK_remove_node: \__xeCJK_ccglue_or_space: }
@@ -912,11 +982,9 @@
 \int_new:N \g__xeCJK_node_int
 \int_gset_eq:NN \g__xeCJK_node_int \c_ten
 \cs_new_protected_nopar:Npn \xeCJK_make_node:n #1
-  {
-    \exp_after:wN \__xeCJK_make_node:N
-    \cs:w c__xeCJK_#1_node_dim \exp_after:wN \cs_end:
-    \exp_after:wN \tex_spacefactor:D \int_use:N \tex_spacefactor:D \exp_stop_f:
-  }
+  { \exp_args:Nc \__xeCJK_make_node:N { c__xeCJK_#1_node_dim } }
+\cs_new_nopar:Npn \__xeCJK_node:n #1
+  { \use:c { c__xeCJK_#1_node_dim } }
 \cs_new_protected_nopar:Npn \__xeCJK_make_node:N #1
   {
     \tex_kern:D - #1
@@ -1093,8 +1161,10 @@
       { \group_align_safe_end: }
   }
 \cs_new_eq:NN \__xeCJK_ignore_space_end: \group_align_safe_end:
-\xeCJK_inter_class_toks:nnn { CJK } { CJK } { \xeCJK_CJK_and_CJK:N }
-\cs_new_protected_nopar:Npn \xeCJK_CJK_and_CJK:N #1 { \CJKglue \CJKsymbol {#1} }
+\xeCJK_inter_class_toks:nnn { CJK } { CJK }
+  { \xeCJK_CJK_and_CJK:N }
+\cs_new_protected_nopar:Npn \xeCJK_CJK_and_CJK:N
+  { \CJKglue \CJKsymbol }
 \xeCJK_inter_class_toks:nnn { FullLeft }  { CJK }
   { \xeCJK_FullLeft_and_CJK: \CJKsymbol }
 \xeCJK_inter_class_toks:nnn { FullRight } { CJK }
@@ -1105,7 +1175,8 @@
       {
         \xeCJK_inter_class_toks:nnx {#1} {##1}
           { \exp_not:c { xeCJK_Default_and_##1:nN } {#1} }
-        \xeCJK_inter_class_toks:nnc {##1} {#1} { xeCJK_##1_and_Default: }
+        \xeCJK_inter_class_toks:nnx {##1} {#1}
+          { \exp_not:c { xeCJK_##1_and_Default: } }
       }
   }
 \xeCJK_inter_class_toks:nnn { Boundary } { FullLeft }
@@ -1112,20 +1183,160 @@
   { \xeCJK_Boundary_and_FullLeft:N  }
 \xeCJK_inter_class_toks:nnn { Boundary } { FullRight }
   { \xeCJK_Boundary_and_FullRight:N  }
-\xeCJK_app_inter_class_toks:nnn { FullLeft } { Boundary } { \tex_ignorespaces:D }
+\xeCJK_inter_class_toks:nnn { FullLeft } { Boundary }
+  { \xeCJK_FullLeft_and_Boundary: }
 \xeCJK_inter_class_toks:nnn { FullRight } { Boundary }
   { \xeCJK_FullRight_and_Boundary: }
+\cs_new_protected_nopar:Npn \xeCJK_FullLeft_and_Boundary:
+  {
+    \__xeCJK_punct_if_middle:NTF \g__xeCJK_last_punct_tl
+      {
+        \__xeCJK_punct_bound_rule:NN \c__xeCJK_right_tl \g__xeCJK_last_punct_tl
+        \xeCJK_class_group_end:
+        \exp_after:wN \xeCJK_punct_node:N \g__xeCJK_last_punct_tl
+        \xeCJK_no_break:
+        \__xeCJK_punct_glue:NN \c__xeCJK_left_tl  \g__xeCJK_last_punct_tl
+      }
+      {
+        \xeCJK_class_group_end:
+        \exp_after:wN \xeCJK_punct_node:N \g__xeCJK_last_punct_tl
+        \xeCJK_no_break: \__xeCJK_zero_glue:
+      }
+    \tex_ignorespaces:D
+  }
 \cs_new_protected_nopar:Npn \xeCJK_FullRight_and_Boundary:
-  { \xeCJK_FullRight_and_Default: \tex_ignorespaces:D }
+  {
+    \__xeCJK_punct_rule:NN \c__xeCJK_right_tl \g__xeCJK_last_punct_tl
+    \xeCJK_class_group_end:
+    \__xeCJK_punct_offset:NN \c__xeCJK_right_tl \g__xeCJK_last_punct_tl
+    \exp_after:wN \xeCJK_punct_node:N \g__xeCJK_last_punct_tl
+    \__xeCJK_punct_glue:NN \c__xeCJK_right_tl \g__xeCJK_last_punct_tl
+    \tex_ignorespaces:D
+  }
+\cs_new_protected_nopar:Npn \xeCJK_punct_node:N #1
+  {
+    \__xeCJK_punct_bound_unitization:NN #1 \l__xeCJK_tmp_dim
+    \__xeCJK_make_node:N \l__xeCJK_tmp_dim
+    \dim_set:Nn \l__xeCJK_tmp_dim { `#1 sp }
+    \__xeCJK_make_node:N \l__xeCJK_tmp_dim
+  }
+\cs_new_protected_nopar:Npn \__xeCJK_punct_bound_unitization:NN #1#2
+  {
+    \dim_set:Nn #2
+      {
+        \dim_max:nn
+          { \c_zero_dim }
+          { \__xeCJK_use_punct_dim:nNN { bound } \c__xeCJK_right_tl #1 }
+      }
+    \dim_compare:nNnF {#2} < { 1pt }
+      { \dim_set:Nn #2 { -1pt * \dim_ratio:nn {#2} { \c_max_dim } } }
+  }
+\cs_new_protected_nopar:Npn \xeCJK_punct_bound_kern:N #1
+  {
+    \exp_after:wN \__xeCJK_punct_bound_kern:NN
+      \g__xeCJK_last_punct_tl #1
+  }
+\cs_new_protected_nopar:Npn \__xeCJK_punct_bound_kern:NN #1#2
+  {
+    \xeCJK_get_punct_bounds:NN \l__xeCJK_aligni_tl #1
+    \xeCJK_get_punct_kerning:NN #1 #2
+    \__xeCJK_punct_bound_unitization:NN #1 \l__xeCJK_tmp_dim
+    \skip_set:Nn \l__xeCJK_punct_kern_skip
+      { \__xeCJK_use_dim_or_skip:nNN { bound_kern } #1 #2 }
+    \dim_compare:nNnF \l__xeCJK_tmp_dim = \l__xeCJK_last_bound_dim
+      { \__xeCJK_punct_bound_kern_ratio:NN #1 #2 }
+    \__xeCJK_add_offset_skip:N \l__xeCJK_punct_kern_skip
+    \bool_if:NTF \l__xeCJK_last_penalty_bool
+      {
+        \tex_penalty:D \l__xeCJK_last_penalty_int
+        \skip_horizontal:N
+      }
+      { \__xeCJK_punct_bound_kern_aux:NNN #1 #2 }
+      \l__xeCJK_punct_kern_skip
+  }
+\cs_new_protected_nopar:Npn \__xeCJK_add_offset_skip:N #1
+  {
+    \tl_if_eq:NNF \l__xeCJK_aligni_tl \c__xeCJK_left_tl
+      {
+        \int_compare:nNnT \etex_lastnodetype:D = \c_eleven
+          {
+            \skip_add:Nn #1 { \tex_lastskip:D }
+            \tex_unskip:D
+          }
+      }
+  }
+\skip_new:N \l__xeCJK_punct_kern_skip
+\cs_new_protected_nopar:Npn \__xeCJK_punct_bound_kern_ratio:NN #1#2
+  {
+    \dim_set:Nn \l__xeCJK_bound_dim
+      { \__xeCJK_use_punct_dim:nNN { bound_width } #1 #2 }
+    \dim_compare:nNnT \l__xeCJK_bound_dim > \c_zero_dim
+      {
+        \dim_compare:nNnF \l__xeCJK_last_bound_dim > \c_zero_dim
+          {
+            \dim_set:Nn \l__xeCJK_last_bound_dim
+              {
+                - \l__xeCJK_last_bound_dim *
+                  \dim_ratio:nn { \c_max_dim } { 1pt }
+              }
+          }
+        \__xeCJK_punct_bound_kern_ratio_aux:N #2
+      }
+  }
+\cs_new_protected_nopar:Npn \__xeCJK_punct_bound_kern_ratio_aux:N #1
+  {
+    \skip_set:Nn \l__xeCJK_punct_kern_skip
+      {
+        \l__xeCJK_punct_kern_skip *
+        \dim_ratio:nn
+          {
+              \l__xeCJK_last_bound_dim
+            + \__xeCJK_use_punct_dim:nNN { bound } \c__xeCJK_left_tl #1
+          }
+          { \l__xeCJK_bound_dim }
+      }
+  }
+\cs_new_protected_nopar:Npn \__xeCJK_nobreak_hskip:N
+  { \xeCJK_no_break: \skip_horizontal:N }
+\cs_new_protected_nopar:Npn \__xeCJK_nobreak_hskip:n
+  { \xeCJK_no_break: \skip_horizontal:n }
+\cs_new_eq:NN \__xeCJK_punct_bound_kern:N \__xeCJK_nobreak_hskip:N
+\cs_new_protected_nopar:Npn \__xeCJK_punct_bound_breakable_kern:N
+  {
+    \tl_if_eq:NNTF \l__xeCJK_aligni_tl \c__xeCJK_right_tl
+      {
+        \tl_if_eq:NNTF \l__xeCJK_alignii_tl \c__xeCJK_left_tl
+          { \skip_horizontal:N }
+          { \__xeCJK_nobreak_hskip:N }
+      }
+      { \__xeCJK_nobreak_hskip:N }
+  }
+\cs_new_protected_nopar:Npn \__xeCJK_punct_bound_kern_aux:NNN #1#2
+  {
+    \str_if_eq:nnTF {#1} {#2}
+      { \__xeCJK_punct_bound_kern:N }
+      {
+        \__xeCJK_punct_if_long:NTF #1
+          { \skip_horizontal:N }
+          {
+            \__xeCJK_punct_if_long:NTF #2
+              { \skip_horizontal:N }
+              { \__xeCJK_punct_bound_kern:N }
+          }
+      }
+  }
 \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 } }
+      {
+        \xeCJK_inter_class_toks:nnx {#1} {##1}
+          { \exp_not:c { xeCJK_#1_and_##1:N } }
+      }
   }
 \cs_new_protected_nopar:Npn \__xeCJK_punct_bound_rule:NN #1#2
   {
     \tex_vrule:D
-      width - \__xeCJK_use_punct_dim:nnn { bound } {#1} {#2}
+      width - \__xeCJK_use_punct_dim:nNN { bound } #1 #2 ~
       depth  \c_zero_dim
       height \c_zero_dim \scan_stop:
   }
@@ -1132,29 +1343,48 @@
 \cs_new_protected_nopar:Npn \__xeCJK_punct_rule:NN #1#2
   {
     \tex_vrule:D
-      width  \__xeCJK_use_punct_dim:nnn { rule } {#1} {#2}
+      width  \__xeCJK_use_punct_dim:nNN { rule } #1 #2 ~
       depth  \c_zero_dim
       height \c_zero_dim \scan_stop:
   }
 \cs_new_protected_nopar:Npn \__xeCJK_punct_offset:NN #1#2
-  { \__xeCJK_punct_kern:n { - \__xeCJK_use_punct_dim:nnn { offset } {#1} {#2} } }
-\cs_new_protected_nopar:Npn \__xeCJK_punct_kern:n #1
   {
-    \dim_set:Nn \l__xeCJK_tmp_dim {#1}
+    \dim_set:Nn \l__xeCJK_tmp_dim
+      { - \__xeCJK_use_punct_dim:nNN { offset } #1 #2 }
     \dim_compare:nNnF \l__xeCJK_tmp_dim = \c_zero_dim
-      { \tex_kern:D \l__xeCJK_tmp_dim }
+      { \__xeCJK_punct_hskip:n { \l__xeCJK_tmp_dim } }
   }
 \cs_new_protected_nopar:Npn \__xeCJK_punct_glue:NN #1#2
-  {
-    \__xeCJK_punct_hskip:n
-      { \__xeCJK_use_dim_or_skip:nnn { glue } {#1} {#2} }
-  }
+  { \__xeCJK_punct_hskip:n { \__xeCJK_use_dim_or_skip:nNN { glue } #1 #2 } }
 \cs_new_eq:NN \__xeCJK_punct_hskip:n \skip_horizontal:n
 \cs_new_protected_nopar:Npn \__xeCJK_punct_kern:NN #1#2
   {
-    \__xeCJK_nobreak_hskip:n
-      { \__xeCJK_use_dim_or_skip:nnn { kern } {#1} {#2} }
+    \str_if_eq_x:nnTF {#1} {#2}
+      { \__xeCJK_punct_nobreak_kern:NN }
+      {
+        \__xeCJK_punct_if_long:NTF #1
+          { \__xeCJK_punct_breakable_kern:NN }
+          {
+            \__xeCJK_punct_if_long:NTF #2
+              { \__xeCJK_punct_breakable_kern:NN }
+              { \__xeCJK_punct_nobreak_kern:NN }
+          }
+      }
+    #1 #2
   }
+\cs_new_eq:NN \xeCJK_punct_kern:NN \__xeCJK_punct_kern:NN
+\cs_new_protected_nopar:Npn \__xeCJK_punct_nobreak_kern:NN #1#2
+  { \__xeCJK_nobreak_hskip:n { \__xeCJK_use_dim_or_skip:nNN { kern } #1 #2 } }
+\cs_new_protected_nopar:Npn \__xeCJK_punct_breakable_kern:NN #1#2
+  {
+    \exp_after:wN \__xeCJK_punct_if_right:NT #1
+      { \__xeCJK_punct_rule:NN \c__xeCJK_right_tl #1 }
+    \__xeCJK_punct_breakable_kern:n
+      { \__xeCJK_use_dim_or_skip:nNN { break_kern } #1 #2 }
+    \__xeCJK_punct_if_right:NF #2
+      { \__xeCJK_punct_rule:NN \c__xeCJK_left_tl #2 }
+  }
+\cs_new_eq:NN \__xeCJK_punct_breakable_kern:n \skip_horizontal:n
 \tl_new:N \g__xeCJK_last_punct_tl
 \cs_new_protected_nopar:Npn \xeCJK_FullLeft_and_CJK:
   {
@@ -1162,9 +1392,10 @@
       {
         \__xeCJK_punct_bound_rule:NN \c__xeCJK_right_tl \g__xeCJK_last_punct_tl
         \xeCJK_no_break:
-        \__xeCJK_punct_glue:NN \c__xeCJK_left_tl  \g__xeCJK_last_punct_tl
+        \__xeCJK_punct_glue:NN \c__xeCJK_left_tl \g__xeCJK_last_punct_tl
       }
-      { \xeCJK_no_break: }
+      { }
+    \__xeCJK_select_font:
   }
 \cs_new_protected_nopar:Npn \xeCJK_FullLeft_and_Default:
   {
@@ -1174,7 +1405,7 @@
         \xeCJK_class_group_end: \xeCJK_no_break:
         \__xeCJK_punct_glue:NN \c__xeCJK_left_tl  \g__xeCJK_last_punct_tl
       }
-      { \xeCJK_class_group_end: \xeCJK_no_break: \__xeCJK_zero_glue: }
+      { \xeCJK_class_group_end: }
   }
 \cs_new_protected_nopar:Npn \__xeCJK_zero_glue:
   { \skip_horizontal:N \c_zero_skip }
@@ -1183,6 +1414,7 @@
     \__xeCJK_punct_rule:NN \c__xeCJK_right_tl \g__xeCJK_last_punct_tl
     \__xeCJK_punct_offset:NN \c__xeCJK_right_tl \g__xeCJK_last_punct_tl
     \__xeCJK_punct_glue:NN \c__xeCJK_right_tl \g__xeCJK_last_punct_tl
+    \__xeCJK_select_font:
     \CJKglue
   }
 \cs_new_protected_nopar:Npn \xeCJK_FullRight_and_Default:
@@ -1194,187 +1426,339 @@
   }
 \cs_new_protected_nopar:Npn \xeCJK_Default_and_FullLeft:nN #1#2
   {
-    \xeCJK_get_punct_bounds:NN \c__xeCJK_left_tl {#2}
-    \__xeCJK_Default_and_FullLeft_glue:N {#2}
+    \xeCJK_get_punct_bounds:NN \c__xeCJK_left_tl #2
+    \__xeCJK_Default_and_FullLeft_glue:N #2
     \xeCJK_class_group_begin:
-    \xeCJK_select_font:
+    \xeCJK_select_punct_font:
     \xeCJK_clear_inter_class_toks:nn {#1} { FullLeft }
     \xeCJK_clear_Boundary_and_CJK_toks:
-    \tl_gset:Nx \g__xeCJK_last_punct_tl {#2}
-    \__xeCJK_punct_rule:NN \c__xeCJK_left_tl {#2}
-    \CJKpunctsymbol {#2}
+    \tl_gset:Nn \g__xeCJK_last_punct_tl {#2}
+    \__xeCJK_punct_rule:NN \c__xeCJK_left_tl #2
+    \CJKpunctsymbol #2
   }
 \cs_new_protected_nopar:Npn \__xeCJK_Default_and_FullLeft_glue:N #1
   {
-    \__xeCJK_punct_glue:NN \c__xeCJK_left_tl {#1}
-    \__xeCJK_punct_offset:NN \c__xeCJK_left_tl {#1}
+    \__xeCJK_punct_glue:NN \c__xeCJK_left_tl #1
+    \__xeCJK_punct_offset:NN \c__xeCJK_left_tl #1
   }
 \cs_new_protected_nopar:Npn \xeCJK_CJK_and_FullLeft:N #1
   {
-    \xeCJK_get_punct_bounds:NN \c__xeCJK_left_tl {#1}
-    \__xeCJK_CJK_and_FullLeft_glue:N {#1}
-    \tl_gset:Nx \g__xeCJK_last_punct_tl {#1}
-    \__xeCJK_punct_rule:NN \c__xeCJK_left_tl {#1}
-    \CJKpunctsymbol {#1}
+    \xeCJK_get_punct_bounds:NN \c__xeCJK_left_tl #1
+    \__xeCJK_CJK_and_FullLeft_glue:N #1
+    \tl_gset:Nn \g__xeCJK_last_punct_tl {#1}
+    \__xeCJK_punct_rule:NN \c__xeCJK_left_tl #1
+    \__xeCJK_select_punct_font:
+    \CJKpunctsymbol #1
   }
 \cs_new_protected_nopar:Npn \__xeCJK_CJK_and_FullLeft_glue:N #1
   {
     \CJKglue
-    \__xeCJK_punct_glue:NN \c__xeCJK_left_tl {#1}
-    \__xeCJK_punct_offset:NN \c__xeCJK_left_tl {#1}
+    \__xeCJK_punct_glue:NN \c__xeCJK_left_tl #1
+    \__xeCJK_punct_offset:NN \c__xeCJK_left_tl #1
   }
 \cs_new_protected_nopar:Npn \xeCJK_Boundary_and_FullLeft:N #1
   {
-    \xeCJK_get_punct_bounds:NN \c__xeCJK_left_tl {#1}
-    \__xeCJK_Boundary_and_FullLeft_glue:N {#1}
-    \__xeCJK_punct_offset:NN \c__xeCJK_left_tl {#1}
+    \xeCJK_get_punct_bounds:NN \c__xeCJK_left_tl #1
+    \__xeCJK_Boundary_and_FullLeft_glue:N #1
+    \__xeCJK_punct_offset:NN \c__xeCJK_left_tl #1
     \xeCJK_class_group_begin:
-    \xeCJK_select_font:
+    \xeCJK_select_punct_font:
     \xeCJK_clear_Boundary_and_CJK_toks:
-    \tl_gset:Nx \g__xeCJK_last_punct_tl {#1}
-    \__xeCJK_punct_rule:NN \c__xeCJK_left_tl {#1}
-    \CJKpunctsymbol {#1}
+    \tl_gset:Nn \g__xeCJK_last_punct_tl {#1}
+    \__xeCJK_punct_rule:NN \c__xeCJK_left_tl #1
+    \CJKpunctsymbol #1
   }
 \cs_new_protected_nopar:Npn \__xeCJK_Boundary_and_FullLeft_glue:N #1
   {
-    \int_case:nnTF { \etex_lastnodetype:D }
+    \tl_set_eq:NN \l__xeCJK_alignii_tl \c__xeCJK_left_tl
+    \group_begin: \exp_args:NNc \group_end: \cs_if_exist_use:NTF
+      { __xeCJK_bound_type_ \int_use:N \etex_lastnodetype:D _glue:Nn }
+      {#1}
+      { \use:n }
+      { \__xeCJK_punct_glue:NN \c__xeCJK_left_tl #1 }
+  }
+\tl_new:N \c__xeCJK_alignii_tl
+\cs_new_protected_nopar:cpn { __xeCJK_bound_type_ -1 _glue:Nn } #1#2
+  { \__xeCJK_zero_glue: }
+\cs_new_protected_nopar:cpn { __xeCJK_bound_type_  1 _glue:Nn } #1#2
+  {
+    \box_set_to_last:N \l__xeCJK_tmp_box
+    \int_compare:nNnTF \etex_lastnodetype:D = { -1 }
       {
-        { \c_one       }
-        {
-          \box_set_to_last:N \l__xeCJK_tmp_box
-          \bool_lazy_and:nnTF
-            { \int_compare_p:nNn \etex_lastnodetype:D = { -1 } }
-            { \dim_compare_p:nNn { \box_wd:N \l__xeCJK_tmp_box } = \tex_parindent:D }
-            { \box_use_clear:N \l__xeCJK_tmp_box \use_none:n }
-            { \box_use_clear:N \l__xeCJK_tmp_box \use:n }
-        }
-        { -1 } { \__xeCJK_zero_glue: \use_none:n }
-        { \c_eleven    }
-        {
-          \bool_lazy_or:nnTF
-            { ! ( \skip_if_finite_p:n { \tex_lastskip:D } ) }
-            { \skip_if_eq_p:nn { \tex_lastskip:D } { 1 sp } }
-            { \__xeCJK_zero_glue: \use_none:n }
-            {
-              \skip_if_eq:nnTF { \tex_lastskip:D } { \labelsep }
-                {
-                  \tex_unskip:D
-                  \bool_lazy_and:nnTF
-                    { \int_compare_p:nNn \etex_lastnodetype:D = \c_thirteen }
-                    { \int_compare_p:nNn \tex_lastpenalty:D = \c_zero }
-                    { \skip_horizontal:n { \labelsep } \use_none:n }
-                    { \skip_horizontal:n { \labelsep } \use:n }
-                }
-                { \use:n }
-            }
-        }
-        { \c_thirteen  }
-        {
-          \int_compare:nNnTF \tex_lastpenalty:D = \c_zero
-            {
-              \tex_unpenalty:D
-              \int_compare:nNnTF \etex_lastnodetype:D = \c_one
-                { \tex_penalty:D \c_zero \use_none:n }
-                { \tex_penalty:D \c_zero \use:n }
-            }
-            { \use:n }
-        }
+        \dim_compare:nNnTF
+          { \box_wd:N \l__xeCJK_tmp_box } = \tex_parindent:D
+          { \box_use_drop:N \l__xeCJK_tmp_box }
+          { \box_use_drop:N \l__xeCJK_tmp_box #2 }
       }
-      { { \__xeCJK_punct_glue:NN \c__xeCJK_left_tl {#1} } }
-      { \__xeCJK_punct_glue:NN \c__xeCJK_left_tl {#1} }
+      { \box_use_drop:N \l__xeCJK_tmp_box #2 }
   }
+\cs_new_protected_nopar:cpn { __xeCJK_bound_type_ 11 _glue:Nn } #1#2
+  {
+    \skip_if_finite:nTF { \tex_lastskip:D }
+      { \__xeCJK_bound_glue_auxi:Nn #1 {#2} }
+      { \__xeCJK_zero_glue: }
+  }
+\cs_new_protected_nopar:Npn \__xeCJK_bound_glue_auxi:Nn #1#2
+  {
+    \__xeCJK_if_last_punct_glue:TF
+      { \xeCJK_punct_bound_kern:N #1 }
+      { \__xeCJK_bound_glue_auxii:n {#2} }
+  }
+\cs_new_protected_nopar:Npn \__xeCJK_bound_glue_auxii:n #1
+  {
+    \skip_if_eq:nnTF { \l__xeCJK_last_skip } { 1sp }
+      { \__xeCJK_zero_glue: }
+      {
+        \skip_if_eq:nnTF { \l__xeCJK_last_skip } { \labelsep }
+          {
+            \tex_unskip:D
+            \int_compare:nNnTF \etex_lastnodetype:D = \c_thirteen
+              {
+                \int_compare:nNnTF \tex_lastpenalty:D = \c_zero
+                  { \skip_horizontal:N \l__xeCJK_last_skip }
+                  { \skip_horizontal:N \l__xeCJK_last_skip #1 }
+              }
+              { \skip_horizontal:N \l__xeCJK_last_skip #1 }
+          }
+          {#1}
+      }
+  }
+\cs_new_protected_nopar:cpn { __xeCJK_bound_type_ 12 _glue:Nn } #1#2
+  {
+    \xeCJK_if_last_node:nF { CJK }
+      { \xeCJK_if_last_node:nF { CJK-space } { \use_none:nn } }
+    \xeCJK_remove_node: \CJKglue
+    #2
+  }
+\cs_new_protected_nopar:cpn { __xeCJK_bound_type_ 13 _glue:Nn } #1#2
+  {
+    \__xeCJK_if_last_punct_penalty:TF
+      { \xeCJK_punct_bound_kern:N #1 }
+      {
+        \int_compare:nNnTF \tex_lastpenalty:D = \c_zero
+          {
+            \tex_unpenalty:D
+            \int_compare:nNnTF \etex_lastnodetype:D = \c_one
+              { \tex_penalty:D \c_zero }
+              { \tex_penalty:D \c_zero #2 }
+          }
+          {#2}
+      }
+  }
 \cs_new_protected_nopar:Npn \xeCJK_Default_and_FullRight:nN #1#2
   {
-    \xeCJK_get_punct_bounds:NN \c__xeCJK_right_tl {#2}
-    \__xeCJK_Default_and_FullRight_glue:N {#2}
+    \xeCJK_get_punct_bounds:NN \c__xeCJK_right_tl #2
+    \__xeCJK_Default_and_FullRight_glue:N #2
     \xeCJK_class_group_begin:
-    \xeCJK_select_font:
+    \xeCJK_select_punct_font:
     \xeCJK_clear_inter_class_toks:nn {#1} { FullRight }
     \xeCJK_clear_Boundary_and_CJK_toks:
-    \tl_gset:Nx \g__xeCJK_last_punct_tl {#2}
-    \xeCJK_FullRight_symbol:N {#2}
+    \tl_gset:Nn \g__xeCJK_last_punct_tl {#2}
+    \xeCJK_FullRight_symbol:N #2
   }
 \cs_new_protected_nopar:Npn \xeCJK_Boundary_and_FullRight:N #1
   {
-    \xeCJK_get_punct_bounds:NN \c__xeCJK_right_tl {#1}
-    \__xeCJK_Default_and_FullRight_glue:N {#1}
+    \xeCJK_get_punct_bounds:NN \c__xeCJK_right_tl #1
+    \xeCJK_if_last_punct:TF
+      {
+        \tl_set_eq:NN \l__xeCJK_alignii_tl \c__xeCJK_right_tl
+        \xeCJK_punct_bound_kern:N
+      }
+      { \__xeCJK_Default_and_FullRight_glue:N }
+      #1
     \xeCJK_class_group_begin:
-    \xeCJK_select_font:
+    \xeCJK_select_punct_font:
     \xeCJK_clear_Boundary_and_CJK_toks:
-    \tl_gset:Nx \g__xeCJK_last_punct_tl {#1}
-    \xeCJK_FullRight_symbol:N {#1}
+    \tl_gset:Nn \g__xeCJK_last_punct_tl {#1}
+    \xeCJK_FullRight_symbol:N #1
   }
 \cs_new_protected_nopar:Npn \xeCJK_CJK_and_FullRight:N #1
   {
-    \xeCJK_get_punct_bounds:NN \c__xeCJK_right_tl {#1}
-    \__xeCJK_CJK_and_FullRight_glue:N {#1}
-    \tl_gset:Nx \g__xeCJK_last_punct_tl {#1}
-    \xeCJK_FullRight_symbol:N {#1}
+    \xeCJK_get_punct_bounds:NN \c__xeCJK_right_tl #1
+    \__xeCJK_CJK_and_FullRight_glue:N #1
+    \tl_gset:Nn \g__xeCJK_last_punct_tl {#1}
+    \__xeCJK_select_punct_font:
+    \xeCJK_FullRight_symbol:N #1
   }
-\cs_new_protected_nopar:Npn \__xeCJK_CJK_and_FullRight_glue:N #1
+\cs_new_protected_nopar:Npn \xeCJK_if_last_punct:TF
   {
-    \__xeCJK_punct_if_long:NTF {#1}
-      { \CJKglue }
+    \bool_set_false:N \l__xeCJK_last_penalty_bool
+    \int_compare:nNnTF \etex_lastnodetype:D = \c_eleven
+      { \__xeCJK_if_last_punct_glue:TF }
       {
-        \__xeCJK_punct_if_middle:NTF {#1}
+        \int_compare:nNnTF \etex_lastnodetype:D = \c_thirteen
+         { \__xeCJK_if_last_punct_penalty:TF }
+         { \use_ii:nn }
+      }
+  }
+\cs_new_protected:Npn \__xeCJK_if_last_punct_glue:TF #1#2
+  {
+    \skip_set_eq:NN \l__xeCJK_last_skip \tex_lastskip:D
+    \tex_unskip:D
+    \int_compare:nNnTF \tex_lastpenalty:D = \c_ten_thousand
+      { \__xeCJK_if_last_punct_auxi:TF }
+      {
+        \xeCJK_if_last_node:TF
+          { \__xeCJK_if_last_punct_auxii:TF }
+          { \__xeCJK_if_last_punct_auxiii:TF }
+      }
+      {#1}
+      { \skip_horizontal:N \l__xeCJK_last_skip #2 }
+  }
+\cs_new_protected:Npn \__xeCJK_if_last_punct_penalty:TF #1#2
+  {
+    \int_set_eq:NN \l__xeCJK_last_penalty_int \tex_lastpenalty:D
+    \tex_unpenalty:D
+    \bool_set_true:N \l__xeCJK_last_penalty_bool
+    \int_compare:nNnTF \etex_lastnodetype:D = \c_eleven
+      {
+        \__xeCJK_if_last_punct_glue:TF
+          {#1}
+          { \tex_penalty:D \l__xeCJK_last_penalty_int #2 }
+      }
+      { \tex_penalty:D \l__xeCJK_last_penalty_int #2 }
+  }
+\cs_new_protected:Npn \__xeCJK_if_last_punct_auxi:TF #1#2
+  {
+    \tex_unpenalty:D
+    \bool_if:NF \l__xeCJK_last_penalty_bool
+      {
+        \bool_set_true:N \l__xeCJK_last_penalty_bool
+        \int_set_eq:NN \l__xeCJK_last_penalty_int \c_ten_thousand
+      }
+    \__xeCJK_if_last_punct_auxiv:TF
+      {#1}
+      { \xeCJK_no_break: #2 }
+  }
+\cs_new_protected:Npn \__xeCJK_if_last_punct_auxii:TF #1#2
+  {
+    \int_case:nnTF { \xetex_charclass:D \l__xeCJK_last_kern_dim }
+      {
+        { \xeCJK_class_num:n { FullRight } }
+        { \tl_set_eq:NN \l__xeCJK_aligni_tl \c__xeCJK_right_tl }
+        { \xeCJK_class_num:n { FullLeft } }
+        { \tl_set_eq:NN \l__xeCJK_aligni_tl \c__xeCJK_left_tl }
+      }
+      { \__xeCJK_if_last_punct_auxv:TF {#1} {#2} }
+      { \__xeCJK_make_node:N \l__xeCJK_last_kern_dim #2 }
+  }
+\cs_new_protected_nopar:Npn \__xeCJK_if_last_punct_auxiii:TF
+  {
+    \int_compare:nNnTF \etex_lastnodetype:D = \c_eleven
+      { \__xeCJK_if_last_punct_auxvi:TF }
+      { \use_ii:nn }
+  }
+\cs_new_protected_nopar:Npn \__xeCJK_if_last_punct_auxiv:TF
+  {
+    \xeCJK_if_last_node:TF
+      { \__xeCJK_if_last_punct_auxii:TF }
+      { \use_ii:nn }
+  }
+\cs_new_protected:Npn \__xeCJK_if_last_punct_auxv:TF #1#2
+  {
+    \dim_set_eq:NN \l__xeCJK_tmp_dim \l__xeCJK_last_kern_dim
+    \xeCJK_if_last_node:TF
+      {
+        \tl_gset:Nx \g__xeCJK_last_punct_tl
+          { \utex_char:D \l__xeCJK_tmp_dim }
+        \dim_set_eq:NN \l__xeCJK_last_bound_dim \l__xeCJK_last_kern_dim
+        #1
+      }
+      { \__xeCJK_make_node:N \l__xeCJK_tmp_dim #2 }
+  }
+\cs_new_protected:Npn \__xeCJK_if_last_punct_auxvi:TF #1#2
+  {
+    \int_gset_eq:NN \g__xeCJK_space_factor_int \tex_spacefactor:D
+    \skip_if_eq:nnTF
+      { \l__xeCJK_last_skip }
+      { \c_xeCJK_space_skip_tl }
+      {
+        \skip_set_eq:NN \l__xeCJK_tmp_skip \tex_lastskip:D
+        \tex_unskip:D
+        \int_compare:nNnTF \tex_lastpenalty:D = \c_ten_thousand
+          { \__xeCJK_if_last_punct_auxi:TF }
+          { \__xeCJK_if_last_punct_auxiv:TF }
+          { \skip_set_eq:NN \l__xeCJK_last_skip \l__xeCJK_tmp_skip #1 }
+          { \skip_horizontal:N \l__xeCJK_tmp_skip #2 }
+      }
+      {#2}
+  }
+\tl_new:N \l__xeCJK_aligni_tl
+\tl_new:N \l__xeCJK_alignii_tl
+\int_new:N \l__xeCJK_last_penalty_int
+\dim_new:N \l__xeCJK_last_bound_dim
+\bool_new:N \l__xeCJK_last_penalty_bool
+\cs_new_protected:Npn \xeCJK_if_last_node:TF #1#2
+  {
+    \int_compare:nNnTF \etex_lastnodetype:D = \c_twelve
+      {
+        \dim_set_eq:NN \l__xeCJK_last_kern_dim \tex_lastkern:D
+        \tex_unkern:D
+        \int_compare:nNnTF \etex_lastnodetype:D = \c_twelve
           {
-            \xeCJK_no_break:
-            \__xeCJK_punct_glue:NN \c__xeCJK_right_tl {#1}
-            \__xeCJK_punct_bound_rule:NN \c__xeCJK_left_tl {#1}
+            \dim_compare:nNnTF \tex_lastkern:D = { - \l__xeCJK_last_kern_dim }
+              { \tex_unkern:D #1 }
+              { \tex_kern:D \l__xeCJK_last_kern_dim #2 }
           }
-          { \xeCJK_no_break: }
+          { \tex_kern:D \l__xeCJK_last_kern_dim #2 }
       }
+      {#2}
   }
-\cs_new_eq:NN \__xeCJK_Default_and_FullRight_glue:N \__xeCJK_CJK_and_FullRight_glue:N
-\cs_new_protected_nopar:Npn \xeCJK_FullLeft_and_FullLeft:N #1
+\dim_new:N \l__xeCJK_last_kern_dim
+\cs_new_protected_nopar:Npn \__xeCJK_CJK_and_FullRight_glue:N #1
   {
-    \xeCJK_no_break:
-    \xeCJK_get_punct_bounds:NN \c__xeCJK_left_tl {#1}
-    \xeCJK_get_punct_kerning:oN \g__xeCJK_last_punct_tl {#1}
-    \__xeCJK_punct_kern:NN \g__xeCJK_last_punct_tl {#1}
-    \tl_gset:Nx \g__xeCJK_last_punct_tl {#1}
-    \CJKpunctsymbol {#1}
+    \__xeCJK_punct_if_long:NTF #1
+      { \xeCJK_allow_break: }
+      { \xeCJK_no_break: }
+    \__xeCJK_punct_if_middle:NT #1
+      {
+        \CJKglue
+        \__xeCJK_punct_glue:NN \c__xeCJK_right_tl #1
+        \__xeCJK_punct_bound_rule:NN \c__xeCJK_left_tl #1
+      }
   }
-\cs_new_protected_nopar:Npn \xeCJK_FullLeft_and_FullRight:N #1
+\cs_new_protected_nopar:Npn \__xeCJK_Default_and_FullRight_glue:N #1
   {
-    \xeCJK_no_break:
-    \xeCJK_get_punct_bounds:NN \c__xeCJK_right_tl {#1}
-    \xeCJK_get_punct_kerning:oN \g__xeCJK_last_punct_tl {#1}
-    \__xeCJK_punct_kern:NN \g__xeCJK_last_punct_tl {#1}
-    \tl_gset:Nx \g__xeCJK_last_punct_tl {#1}
-    \xeCJK_no_break:
-    \xeCJK_FullRight_symbol:N {#1}
+    \__xeCJK_punct_if_long:NTF #1
+      { \xeCJK_allow_break: }
+      { \xeCJK_no_break: }
+    \__xeCJK_punct_if_middle:NT #1
+      {
+        \__xeCJK_punct_glue:NN \c__xeCJK_right_tl #1
+        \__xeCJK_punct_bound_rule:NN \c__xeCJK_left_tl #1
+      }
   }
-\cs_new_protected_nopar:Npn \xeCJK_FullRight_and_FullLeft:N #1
+\cs_new_protected_nopar:Npn \xeCJK_FullLeft_and_FullLeft:N #1
   {
-    \xeCJK_get_punct_bounds:NN \c__xeCJK_left_tl {#1}
-    \xeCJK_get_punct_kerning:oN \g__xeCJK_last_punct_tl {#1}
-    \xeCJK_punct_kern:NN \g__xeCJK_last_punct_tl {#1}
-    \tl_gset:Nx \g__xeCJK_last_punct_tl {#1}
-    \CJKpunctsymbol {#1}
+    \xeCJK_get_punct_bounds:NN \c__xeCJK_left_tl #1
+    \xeCJK_get_punct_kerning:oN \g__xeCJK_last_punct_tl #1
+    \__xeCJK_punct_kern:NN \g__xeCJK_last_punct_tl #1
+    \tl_gset:Nn \g__xeCJK_last_punct_tl {#1}
+    \CJKpunctsymbol #1
   }
-\cs_new_protected_nopar:Npn \__xeCJK_punct_nobreak_kern:NN #1#2
+\cs_new_protected_nopar:Npn \xeCJK_FullLeft_and_FullRight:N #1
   {
-    \__xeCJK_punct_kern:NN #1#2
-    \xeCJK_no_break:
+    \xeCJK_get_punct_bounds:NN \c__xeCJK_right_tl #1
+    \xeCJK_get_punct_kerning:oN \g__xeCJK_last_punct_tl #1
+    \__xeCJK_punct_kern:NN \g__xeCJK_last_punct_tl #1
+    \tl_gset:Nn \g__xeCJK_last_punct_tl {#1}
+    \xeCJK_FullRight_symbol:N #1
   }
-\cs_new_eq:NN \xeCJK_punct_kern:NN \__xeCJK_punct_nobreak_kern:NN
-\cs_new_protected_nopar:Npn \__xeCJK_punct_breakable_kern:NN #1#2
+\cs_new_protected_nopar:Npn \xeCJK_FullRight_and_FullLeft:N #1
   {
-    \__xeCJK_punct_rule:NN \c__xeCJK_right_tl #1
-    \__xeCJK_punct_breakable_kern:n
-      { \__xeCJK_use_dim_or_skip:nnn { break_kern } {#1} {#2} }
-    \__xeCJK_punct_rule:NN \c__xeCJK_left_tl #2
+    \xeCJK_get_punct_bounds:NN \c__xeCJK_left_tl #1
+    \xeCJK_get_punct_kerning:oN \g__xeCJK_last_punct_tl #1
+    \xeCJK_punct_kern:NN \g__xeCJK_last_punct_tl #1
+    \tl_gset:Nn \g__xeCJK_last_punct_tl {#1}
+    \CJKpunctsymbol #1
   }
-\cs_new_eq:NN \__xeCJK_punct_breakable_kern:n \skip_horizontal:n
 \cs_new_protected_nopar:Npn \xeCJK_FullRight_and_FullRight:N #1
   {
-    \xeCJK_get_punct_bounds:NN \c__xeCJK_right_tl {#1}
-    \xeCJK_get_punct_kerning:oN \g__xeCJK_last_punct_tl {#1}
-    \__xeCJK_punct_kern:NN \g__xeCJK_last_punct_tl {#1}
-    \tl_gset:Nx \g__xeCJK_last_punct_tl {#1}
-    \xeCJK_no_break:
-    \xeCJK_FullRight_symbol:N {#1}
+    \xeCJK_get_punct_bounds:NN \c__xeCJK_right_tl #1
+    \xeCJK_get_punct_kerning:oN \g__xeCJK_last_punct_tl #1
+    \__xeCJK_punct_kern:NN \g__xeCJK_last_punct_tl #1
+    \tl_gset:Nn \g__xeCJK_last_punct_tl {#1}
+    \xeCJK_FullRight_symbol:N #1
   }
 \keys_define:nn { xeCJK / options }
   {
@@ -1383,10 +1767,14 @@
       {
         \cs_if_eq:NNF \xeCJK_FullRight_and_Boundary: \xeCJK_check_FullRight:
           {
-            \cs_set_eq:NN \__xeCJK_save_FullRight_check: \xeCJK_FullRight_and_Boundary:
-            \cs_set_eq:NN \__xeCJK_save_FullRight_symbol:N \xeCJK_FullRight_symbol:N
-            \cs_set_eq:NN \xeCJK_FullRight_and_Boundary: \xeCJK_check_FullRight:
-            \cs_set_eq:NN \xeCJK_FullRight_symbol:N \xeCJK_check_FullRight_symbol:Nw
+            \cs_set_eq:NN \__xeCJK_save_FullRight_check:
+                          \xeCJK_FullRight_and_Boundary:
+            \cs_set_eq:NN \__xeCJK_save_FullRight_symbol:N
+                          \xeCJK_FullRight_symbol:N
+            \cs_set_eq:NN \xeCJK_FullRight_and_Boundary:
+                          \xeCJK_check_FullRight:
+            \cs_set_eq:NN \xeCJK_FullRight_symbol:N
+                          \xeCJK_check_FullRight_symbol:Nw
           }
       } ,
     CheckFullRight / false .code:n =
@@ -1393,8 +1781,10 @@
       {
         \cs_if_eq:NNT \xeCJK_FullRight_and_Boundary: \xeCJK_check_FullRight:
           {
-            \cs_set_eq:NN \xeCJK_FullRight_and_Boundary: \__xeCJK_save_FullRight_check:
-            \cs_set_eq:NN \xeCJK_FullRight_symbol:N \__xeCJK_save_FullRight_symbol:N
+            \cs_set_eq:NN \xeCJK_FullRight_and_Boundary:
+                          \__xeCJK_save_FullRight_check:
+            \cs_set_eq:NN \xeCJK_FullRight_symbol:N
+                          \__xeCJK_save_FullRight_symbol:N
           }
       } ,
     CheckFullRight      .default:n = { true }
@@ -1402,20 +1792,28 @@
 \cs_new_nopar:Npn \xeCJK_FullRight_symbol:N { \CJKpunctsymbol }
 \cs_new_protected_nopar:Npn \xeCJK_check_FullRight:
   {
-    \xeCJK_get_punct_bounds:NN \c__xeCJK_right_tl \g__xeCJK_last_punct_tl
+    \xeCJK_get_punct_bounds:No \c__xeCJK_right_tl \g__xeCJK_last_punct_tl
     \__xeCJK_punct_rule:NN \c__xeCJK_right_tl \g__xeCJK_last_punct_tl
-    \__xeCJK_punct_offset:NN \c__xeCJK_right_tl \g__xeCJK_last_punct_tl
     \group_align_safe_begin:
     \tl_case:NoTF \l_peek_token
       { \l__xeCJK_no_break_cs_case_tl }
-      { \group_align_safe_end: \xeCJK_no_break: }
+      {
+        \group_align_safe_end:
+        \xeCJK_no_break:
+        \group_insert_after:N \xeCJK_no_break:
+      }
       { \group_align_safe_end: }
+    \__xeCJK_punct_offset:NN \c__xeCJK_right_tl \g__xeCJK_last_punct_tl
+    \exp_after:wN \xeCJK_punct_node:N \g__xeCJK_last_punct_tl
+    \xeCJK_class_group_end:
     \__xeCJK_punct_glue:NN \c__xeCJK_right_tl \g__xeCJK_last_punct_tl
-    \xeCJK_class_group_end:
   }
-\cs_generate_variant:Nn \tl_case:NnTF { No }
+\prg_generate_conditional_variant:Nnn \tl_case:Nn { No } { TF , F }
 \cs_new_protected_nopar:Npn \xeCJK_check_FullRight_symbol:Nw #1
-  { \xeCJK_peek_after_ignore_spaces:nw { \__xeCJK_save_FullRight_symbol:N {#1} } }
+  {
+    \xeCJK_peek_after_ignore_spaces:nw
+      { \__xeCJK_save_FullRight_symbol:N #1 }
+  }
 \cs_new_protected:Npn \xeCJK_cs_case_keys_define:nNNnn #1#2#3#4#5
   {
     \tl_new:N #2
@@ -1456,13 +1854,30 @@
         \bool_if:NTF \l__xeCJK_tmp_bool
           {
             \bool_set_false:N \l__xeCJK_tmp_bool
-            \skip_set_eq:NN \l__xeCJK_tmp_skip \tex_lastskip:D
+            \skip_set_eq:NN \l__xeCJK_last_skip \tex_lastskip:D
           }
-          { \skip_add:Nn \l__xeCJK_tmp_skip \tex_lastskip:D }
+          { \skip_add:Nn \l__xeCJK_last_skip \tex_lastskip:D }
         \tex_unskip:D
       }
+    \xeCJK_if_last_node:TF
+      {
+        \dim_set_eq:NN \l__xeCJK_tmp_dim \l__xeCJK_last_kern_dim
+        \xeCJK_if_last_node:TF
+          {
+            \int_compare:nNnT \etex_lastnodetype:D = \c_eleven
+              {
+                \exp_args:NNNo \tex_unskip:D \xeCJK_no_break:
+                \skip_horizontal:n { \skip_use:N \tex_lastskip:D }
+              }
+            \__xeCJK_make_node:N \l__xeCJK_last_kern_dim
+          }
+          { }
+        \__xeCJK_make_node:N \l__xeCJK_tmp_dim
+      }
+      { }
     \xeCJK_no_break:
-    \bool_if:NF \l__xeCJK_tmp_bool { \skip_horizontal:N \l__xeCJK_tmp_skip }
+    \bool_if:NF \l__xeCJK_tmp_bool
+      { \skip_horizontal:N \l__xeCJK_last_skip }
   }
 \keys_define:nn { xeCJK / options }
   {
@@ -1598,7 +2013,6 @@
       { \__xeCJK_check_single_end:N #1 #2#3 }
   }
 \tl_new:N \l__xeCJK_check_single_cs_case_tl
-\cs_generate_variant:Nn \tl_case:NnF { No }
 \cs_new_protected_nopar:Npn \xeCJK_check_single_env:nnNn #1#2#3#4
   {
     \str_case_x:noTF {#4}
@@ -1607,7 +2021,7 @@
       {#1}
     #3 {#4}
   }
-\cs_generate_variant:Nn \str_case_x:nnTF { no }
+\prg_generate_conditional_variant:Nnn \str_case_x:nn { no } { TF }
 \xeCJK_cs_case_keys_define:nNNnn { NewLineCS }
   \l__xeCJK_new_line_cs_case_tl \l__xeCJK_new_line_cs_seq
   { \use_ii:nnn }
@@ -1686,9 +2100,9 @@
       {
         \int_if_exist:cTF { \__xeCJK_class_csname:n { CJK/##1 } }
           {
-            \xeCJK_declare_char_class:nc
+            \xeCJK_declare_char_class:nn
               { CJK \bool_if:NF \l__xeCJK_sub_cancel_bool { /##1 } }
-              { g__xeCJK_CJK/##1_range_clist }
+              { \use:c { g__xeCJK_CJK/##1_range_clist } }
           }
           { \__xeCJK_error:nx { SubBlock-undefined } {##1} }
       }
@@ -1792,11 +2206,15 @@
       {
         \bool_set_true:N \l__xeCJK_punct_breakable_bool
         \cs_set_eq:NN \xeCJK_punct_kern:NN \__xeCJK_punct_breakable_kern:NN
+        \cs_set_eq:NN \__xeCJK_punct_bound_kern:N
+                      \__xeCJK_punct_bound_breakable_kern:N
       } ,
     AllowBreakBetweenPuncts / false .code:n =
       {
         \bool_set_false:N \l__xeCJK_punct_breakable_bool
-        \cs_set_eq:NN \xeCJK_punct_kern:NN \__xeCJK_punct_nobreak_kern:NN
+        \cs_set_eq:NN \xeCJK_punct_kern:NN \__xeCJK_punct_kern:NN
+        \cs_set_eq:NN \__xeCJK_punct_bound_kern:N
+                      \__xeCJK_nobreak_hskip:N
       } ,
     AllowBreakBetweenPuncts      .default:n = { true } ,
     KaiMingPunct  .code:n = { \__xeCJK_set_special_punct:nn { mixed_width } {#1} } ,
@@ -1815,13 +2233,13 @@
     RubberPunctSkip .choice: ,
     RubberPunctSkip      .default:n = { true } ,
     RubberPunctSkip / true  .code:n =
-      { \cs_set_eq:NN \__xeCJK_use_dim_or_skip:nnn \__xeCJK_use_punct_skip:nnn } ,
+      { \cs_set_eq:NN \__xeCJK_use_dim_or_skip:nNN \__xeCJK_use_punct_skip:nNN } ,
     RubberPunctSkip / plus  .code:n =
-      { \cs_set_eq:NN \__xeCJK_use_dim_or_skip:nnn \__xeCJK_use_punct_skip_plus:nnn } ,
+      { \cs_set_eq:NN \__xeCJK_use_dim_or_skip:nNN \__xeCJK_use_punct_skip_plus:nNN } ,
     RubberPunctSkip / minus .code:n =
-      { \cs_set_eq:NN \__xeCJK_use_dim_or_skip:nnn \__xeCJK_use_punct_skip_minus:nnn } ,
+      { \cs_set_eq:NN \__xeCJK_use_dim_or_skip:nNN \__xeCJK_use_punct_skip_minus:nNN } ,
     RubberPunctSkip / false .code:n =
-      { \cs_set_eq:NN \__xeCJK_use_dim_or_skip:nnn \__xeCJK_use_punct_dim:nnn }
+      { \cs_set_eq:NN \__xeCJK_use_dim_or_skip:nNN \__xeCJK_use_punct_dim:nNN }
   }
 \bool_new:N \l__xeCJK_punct_breakable_bool
 \clist_new:N \g__xeCJK_special_punct_clist
@@ -1862,7 +2280,8 @@
   }
 \prg_new_conditional:Npnn \__xeCJK_punct_if_right:N #1 { p , T , F , TF }
   {
-    \if_int_compare:w \xeCJK_token_value_class:N #1 = \xeCJK_class_num:n { FullRight }
+    \if_int_compare:w \xeCJK_token_value_class:N #1 =
+                      \xeCJK_class_num:n { FullRight }
       \prg_return_true: \else: \prg_return_false: \fi:
   }
 \clist_map_inline:Nn \g__xeCJK_special_punct_clist
@@ -1876,27 +2295,27 @@
   }
 \cs_new:Npn \__xeCJK_punct_csname:n #1
   { c__xeCJK_\l_xeCJK_current_font_tl/\l_xeCJK_punct_style_tl/#1/tl }
-\cs_new:Npn \__xeCJK_use_punct_dim:nn #1#2
+\cs_new:Npn \__xeCJK_use_punct_dim:nN #1#2
   { \use:c { \__xeCJK_punct_csname:n { dim/#1/#2 } } }
-\cs_new:Npn \__xeCJK_use_punct_dim:nnn #1#2#3
+\cs_new:Npn \__xeCJK_use_punct_dim:nNN #1#2#3
   { \use:c { \__xeCJK_punct_csname:n { dim/#1/#2/#3 } } }
-\cs_new:Npn \__xeCJK_use_punct_skip:nnn #1#2#3
+\cs_new:Npn \__xeCJK_use_punct_skip:nNN #1#2#3
   { \use:c { \__xeCJK_punct_csname:n { skip/#1/#2/#3 } } }
-\cs_new:Npn \__xeCJK_use_punct_skip_plus:nnn #1#2#3
+\cs_new:Npn \__xeCJK_use_punct_skip_plus:nNN #1#2#3
   { \use:c { \__xeCJK_punct_csname:n { skip/plus/#1/#2/#3 } } }
-\cs_new:Npn \__xeCJK_use_punct_skip_minus:nnn #1#2#3
+\cs_new:Npn \__xeCJK_use_punct_skip_minus:nNN #1#2#3
   { \use:c { \__xeCJK_punct_csname:n { skip/minus/#1/#2/#3 } } }
-\cs_new_protected:Npn \__xeCJK_save_punct_dim:nnn #1#2
+\cs_new_protected:Npn \__xeCJK_save_punct_dim:nNn #1#2
   { \__xeCJK_save_punct_width_aux:nnn { dim } { #1/#2 } }
-\cs_new_protected:Npn \__xeCJK_save_punct_dim:nnnn #1#2#3
+\cs_new_protected:Npn \__xeCJK_save_punct_dim:nNNn #1#2#3
   { \__xeCJK_save_punct_width_aux:nnn { dim } { #1/#2/#3 } }
-\cs_new_protected:Npn \__xeCJK_save_punct_skip:nnnn #1#2#3#4
+\cs_new_protected:Npn \__xeCJK_save_punct_skip:nNNn #1#2#3#4
   {
     \__xeCJK_save_punct_width_aux:nnn { skip } { #1/#2/#3 } {#4}
     \__xeCJK_save_punct_width_aux:nnn { skip } { plus/#1/#2/#3 } {#4}
     \__xeCJK_save_punct_width_aux:nnn { skip } { minus/#1/#2/#3 } {#4}
   }
-\cs_new_protected:Npn \__xeCJK_save_punct_skip:nnnnnn #1#2#3#4#5#6
+\cs_new_protected:Npn \__xeCJK_save_punct_skip:nNNnnn #1#2#3#4#5#6
   {
     \use:x
       {
@@ -1908,9 +2327,12 @@
   }
 \cs_new_protected:Npn \__xeCJK_save_punct_skip_aux:nnnn #1#2#3#4
   {
-    \__xeCJK_save_punct_width_aux:nnn { skip } {#1} { #2 ~ plus ~ #3 ~ minus ~ #4 ~ }
-    \__xeCJK_save_punct_width_aux:nnn { skip } { plus/#1 } { #2 ~ plus ~ #3 ~ }
-    \__xeCJK_save_punct_width_aux:nnn { skip } { minus/#1 } { #2 ~ minus ~ #4 ~ }
+    \__xeCJK_save_punct_width_aux:nnn { skip }
+      {#1}         { #2 ~ plus ~ #3 ~ minus ~ #4 ~ }
+    \__xeCJK_save_punct_width_aux:nnn { skip }
+      { plus/#1 }  { #2 ~ plus ~ #3 ~ }
+    \__xeCJK_save_punct_width_aux:nnn { skip }
+      { minus/#1 } { #2 ~ minus ~ #4 ~ }
   }
 \cs_new_protected:Npn \__xeCJK_save_punct_width_aux:nnn #1#2#3
   {
@@ -1917,7 +2339,7 @@
     \tl_const:cx { \__xeCJK_punct_csname:n { #1/#2 } }
                  { \use:c { #1_eval:n } {#3} }
   }
-\cs_new_eq:NN \__xeCJK_use_dim_or_skip:nnn \__xeCJK_use_punct_skip:nnn
+\cs_new_eq:NN \__xeCJK_use_dim_or_skip:nNN \__xeCJK_use_punct_skip:nNN
 \DeclareObjectType { xeCJK / punctuation } { \c_zero }
 \DeclareTemplateInterface { xeCJK / punctuation } { basic } { \c_zero }
   {
@@ -1995,32 +2417,37 @@
 \cs_new_protected_nopar:Npn \xeCJK_get_punct_bounds:NN #1#2
   {
     \tl_if_exist:cF { \__xeCJK_punct_csname:n { dim/glue/#1/#2 } }
+      { \__xeCJK_get_punct_bounds_aux:NN #1 #2 }
+  }
+\cs_new_protected_nopar:Npn \__xeCJK_get_punct_bounds_aux:NN #1#2
+  {
+    \tl_if_eq:NNTF \l_xeCJK_punct_style_tl \c__xeCJK_punct_style_plain_tl
       {
-        \tl_if_eq:NNTF \l_xeCJK_punct_style_tl \c__xeCJK_punct_style_plain_tl
+        \__xeCJK_save_punct_dim:nNNn { rule }   #1 #2 { \c_zero_dim }
+        \__xeCJK_save_punct_dim:nNNn { glue }   #1 #2 { \c_zero_dim }
+        \__xeCJK_save_punct_dim:nNNn { offset } #1 #2 { \c_zero_dim }
+        \__xeCJK_save_punct_dim:nNNn { bound } \c__xeCJK_left_tl  {#2} { \c_zero_dim }
+        \__xeCJK_save_punct_dim:nNNn { bound } \c__xeCJK_right_tl {#2} { \c_zero_dim }
+        \__xeCJK_save_punct_skip:nNNn { glue }  #1 #2 { \c_zero_skip }
+      }
+      {
+        { \xeCJK_select_punct_font: \xeCJK_calc_punct_dimen:N #2 }
+        \dim_set:Nn \l__xeCJK_bound_dim
+          { \__xeCJK_use_punct_dim:nNN { bound } #1 #2 }
+        \dim_set:Nn \l__xeCJK_reverse_bound_dim
           {
-            \__xeCJK_save_punct_dim:nnnn { rule }   {#1} {#2} { \c_zero_dim }
-            \__xeCJK_save_punct_dim:nnnn { glue }   {#1} {#2} { \c_zero_dim }
-            \__xeCJK_save_punct_dim:nnnn { offset } {#1} {#2} { \c_zero_dim }
-            \__xeCJK_save_punct_dim:nnnn { bound } \c__xeCJK_left_tl  {#2} { \c_zero_dim }
-            \__xeCJK_save_punct_dim:nnnn { bound } \c__xeCJK_right_tl {#2} { \c_zero_dim }
-            \__xeCJK_save_punct_skip:nnnn { glue }  {#1} {#2} { \c_zero_skip }
+            \tl_if_eq:NNTF #1 \c__xeCJK_left_tl
+              { \__xeCJK_use_punct_dim:nNN { bound } \c__xeCJK_right_tl }
+              { \__xeCJK_use_punct_dim:nNN { bound } \c__xeCJK_left_tl }
+              #2
           }
-          {
-            { \xeCJK_select_font: \xeCJK_calc_punct_dimen:o {#2} }
-            \dim_set:Nn \l__xeCJK_bound_dim
-              { \__xeCJK_use_punct_dim:nnn { bound } {#1} {#2} }
-            \dim_set:Nn \l__xeCJK_reverse_bound_dim
-              {
-                \__xeCJK_use_punct_dim:nnn { bound }
-                  { \xeCJK_reverse:nnn {#1} \c__xeCJK_left_tl \c__xeCJK_right_tl }
-                  {#2}
-              }
-            \UseInstance { xeCJK / punctuation } { \l_xeCJK_punct_style_tl }
-            \xeCJK_punct_margin_process:NN {#1} {#2}
-            \xeCJK_punct_offset_process:NN {#1} {#2}
-          }
+        \UseInstance { xeCJK / punctuation } { \l_xeCJK_punct_style_tl }
+        \xeCJK_punct_margin_process:NN #1 #2
+        \xeCJK_punct_offset_process:NN #1 #2
       }
   }
+\cs_new_protected_nopar:Npn \xeCJK_get_punct_bounds:No
+  { \exp_last_unbraced:NNo \xeCJK_get_punct_bounds:NN }
 \dim_new:N \l__xeCJK_bound_dim
 \dim_new:N \l__xeCJK_reverse_bound_dim
 \cs_new_protected_nopar:Npn \xeCJK_get_punct_kerning:NN #1#2
@@ -2029,18 +2456,22 @@
       {
         \tl_if_eq:NNTF \l_xeCJK_punct_style_tl \c__xeCJK_punct_style_plain_tl
           {
-            \__xeCJK_save_punct_dim:nnnn { kern } {#1} {#2} { \c_zero_dim }
-            \__xeCJK_save_punct_dim:nnnn { break_kern } {#1} {#2} { \c_zero_dim }
-            \__xeCJK_save_punct_skip:nnnn { kern } {#1} {#2} { \c_zero_skip }
-            \__xeCJK_save_punct_skip:nnnn { break_kern } {#1} {#2} { \c_zero_skip }
+            \__xeCJK_save_punct_dim:nNNn { kern } #1 #2 { \c_zero_dim }
+            \__xeCJK_save_punct_dim:nNNn { break_kern } #1 #2 { \c_zero_dim }
+            \__xeCJK_save_punct_dim:nNNn { bound_kern } #1 #2 { \c_zero_dim }
+            \__xeCJK_save_punct_dim:nNNn { bound_width } #1 #2 { \c_zero_dim }
+            \__xeCJK_save_punct_skip:nNNn { kern } #1 #2 { \c_zero_skip }
+            \__xeCJK_save_punct_skip:nNNn { break_kern } #1 #2 { \c_zero_skip }
+            \__xeCJK_save_punct_skip:nNNn { bound_kern } #1 #2 { \c_zero_skip }
           }
           {
             \UseInstance { xeCJK / punctuation } { \l_xeCJK_punct_style_tl }
-            \xeCJK_punct_kerning_process:NN {#1} {#2}
+            \xeCJK_punct_kerning_process:NN #1 #2
           }
       }
   }
-\cs_generate_variant:Nn \xeCJK_get_punct_kerning:NN { o }
+\cs_new_protected_nopar:Npn \xeCJK_get_punct_kerning:oN
+  { \exp_after:wN \xeCJK_get_punct_kerning:NN }
 \cs_new_protected_nopar:Npn \xeCJK_punct_margin_process:NN #1#2
   {
     \dim_set:Nn \l__xeCJK_tmp_dim
@@ -2047,24 +2478,14 @@
       {
         \bool_if:NTF \l__xeCJK_enabled_global_setting_bool
           {
-            \cs_if_exist_use:cTF { g__xeCJK_punct_width/#2/tl }
-              { \use_none:n }
+            \cs_if_exist_use:cF { g__xeCJK_punct_width/#2/tl }
               {
                 \tl_if_empty:NTF \g__xeCJK_punct_width_tl
-                  { \use:n }
-                  { \g__xeCJK_punct_width_tl \use_none:n }
+                  { \__xeCJK_calc_punct_width:N #2 }
+                  { \g__xeCJK_punct_width_tl }
               }
           }
-          { \use:n }
-          {
-            \__xeCJK_punct_if_middle:NTF {#2}
-              { \__xeCJK_punct_width_or_ratio:nN { middle } {#2} }
-              {
-                \__xeCJK_punct_if_mixed_width:NTF {#2}
-                  { \__xeCJK_punct_width_or_ratio:nN { mixed } {#2} }
-                  { \__xeCJK_punct_width_or_ratio:nN { fixed } {#2} }
-              }
-          }
+          { \__xeCJK_calc_punct_width:N #2 }
       }
     \dim_set:Nn \l__xeCJK_tmp_dim
       {
@@ -2073,10 +2494,10 @@
           {
             \dim_compare:nNnTF \l__xeCJK_tmp_dim < \c_max_dim
               {
-                \__xeCJK_punct_if_middle:NTF {#2}
+                \__xeCJK_punct_if_middle:NTF #2
                   {
-                    (
-                      \l__xeCJK_tmp_dim - ( \__xeCJK_use_punct_dim:nn { dimen } {#2} )
+                    (   \l__xeCJK_tmp_dim
+                      - ( \__xeCJK_use_punct_dim:nN { dimen } #2 )
                     ) / \c_two
                   }
                   {
@@ -2083,12 +2504,17 @@
                     \bool_if:NTF \l__xeCJK_optimize_margin_bool
                       {
                         \dim_max:nn
-                          { \dim_min:nn \l__xeCJK_bound_dim \l__xeCJK_reverse_bound_dim }
+                          {
+                            \dim_min:nn
+                              { \l__xeCJK_bound_dim }
+                              { \l__xeCJK_reverse_bound_dim }
+                          }
                       }
                       { \use:n }
                       {
-                        \l__xeCJK_tmp_dim - \l__xeCJK_reverse_bound_dim
-                        - ( \__xeCJK_use_punct_dim:nn { dimen } {#2} )
+                          \l__xeCJK_tmp_dim
+                        - \l__xeCJK_reverse_bound_dim
+                        - ( \__xeCJK_use_punct_dim:nN { dimen } #2 )
                       }
                   }
               }
@@ -2096,45 +2522,58 @@
                 \bool_if:NTF \l__xeCJK_optimize_margin_bool
                   { \dim_min:nn { \l__xeCJK_bound_dim } }
                   { \use:n }
-                  {
-                    \__xeCJK_punct_if_middle:NTF {#2}
-                      {
-                        \dim_compare:nNnTF \l__xeCJK_middle_margin_width_dim < \c_max_dim
-                          { \l__xeCJK_middle_margin_width_dim }
-                          {
-                            \fp_use:N \l__xeCJK_middle_margin_ratio_fp
-                            \etex_dimexpr:D
-                              ( \l__xeCJK_bound_dim + \l__xeCJK_reverse_bound_dim ) / \c_two
-                            \scan_stop:
-                          }
-                      }
-                      {
-                        \__xeCJK_punct_if_mixed_width:NTF {#2}
-                          { \__xeCJK_margin_width_or_ratio:n { mixed } }
-                          { \__xeCJK_margin_width_or_ratio:n { fixed } }
-                      }
-                  }
+                  { \__xeCJK_calc_margin_width:N #2 }
               }
           }
       }
-    \__xeCJK_save_punct_dim:nnnn { glue } {#1} {#2} { \l__xeCJK_tmp_dim }
-    \__xeCJK_save_punct_skip:nnnnnn { glue } {#1} {#2}
+    \__xeCJK_save_punct_dim:nNNn { glue } #1 #2 { \l__xeCJK_tmp_dim }
+    \__xeCJK_save_punct_skip:nNNnnn { glue } #1 #2
       { \l__xeCJK_tmp_dim }
       {
-        \__xeCJK_punct_if_middle:NTF {#2}
+        \__xeCJK_punct_if_middle:NTF #2
           {
-            ( \__xeCJK_use_punct_dim:nn { width } {#2} -
-              \__xeCJK_use_punct_dim:nn { dimen } {#2} ) / \c_two
+            ( \__xeCJK_use_punct_dim:nN { width } #2 -
+              \__xeCJK_use_punct_dim:nN { dimen } #2 ) / \c_two
             - \l__xeCJK_tmp_dim
           }
           { \l__xeCJK_bound_dim - \l__xeCJK_tmp_dim }
       }
       {
-        \__xeCJK_punct_if_middle:NTF {#2}
+        \__xeCJK_punct_if_middle:NTF #2
           { .5 \l__xeCJK_tmp_dim }
           { \l__xeCJK_tmp_dim - \l__xeCJK_reverse_bound_dim }
       }
   }
+\cs_new_nopar:Npn \__xeCJK_calc_punct_width:N #1
+  {
+    \__xeCJK_punct_if_middle:NTF #1
+      { \__xeCJK_punct_width_or_ratio:nN { middle } }
+      {
+        \__xeCJK_punct_if_mixed_width:NTF #1
+          { \__xeCJK_punct_width_or_ratio:nN { mixed } }
+          { \__xeCJK_punct_width_or_ratio:nN { fixed } }
+      }
+      #1
+  }
+\cs_new_nopar:Npn \__xeCJK_calc_margin_width:N #1
+  {
+    \__xeCJK_punct_if_middle:NTF #1
+      {
+        \dim_compare:nNnTF \l__xeCJK_middle_margin_width_dim < \c_max_dim
+          { \l__xeCJK_middle_margin_width_dim }
+          {
+            \fp_use:N \l__xeCJK_middle_margin_ratio_fp
+            \etex_dimexpr:D
+              ( \l__xeCJK_bound_dim + \l__xeCJK_reverse_bound_dim ) / \c_two
+            \scan_stop:
+          }
+      }
+      {
+        \__xeCJK_punct_if_mixed_width:NTF #1
+          { \__xeCJK_margin_width_or_ratio:n { mixed } }
+          { \__xeCJK_margin_width_or_ratio:n { fixed } }
+      }
+  }
 \cs_new_protected_nopar:Npn \xeCJK_punct_offset_process:NN #1#2
   {
     \dim_set:Nn \l__xeCJK_tmp_dim
@@ -2141,16 +2580,14 @@
       {
         \bool_if:NTF \l__xeCJK_enabled_global_setting_bool
           {
-            \cs_if_exist_use:cTF { g__xeCJK_punct_bound_width/#2/tl }
-              { \use_none:n }
+            \cs_if_exist_use:cF { g__xeCJK_punct_bound_width/#2/tl }
               {
                 \tl_if_empty:NTF \g__xeCJK_punct_bound_width_tl
-                  { \use:n }
-                  { \g__xeCJK_punct_bound_width_tl \use_none:n }
+                  { \__xeCJK_punct_width_or_ratio:nN { bound } #2 }
+                  { \g__xeCJK_punct_bound_width_tl }
               }
           }
-          { \use:n }
-          { \__xeCJK_punct_width_or_ratio:nN { bound } {#2} }
+          { \__xeCJK_punct_width_or_ratio:nN { bound } #2 }
       }
     \dim_set:Nn \l__xeCJK_tmp_dim
       {
@@ -2160,15 +2597,16 @@
           {
             \dim_compare:nNnTF \l__xeCJK_tmp_dim < \c_max_dim
               {
-                \__xeCJK_punct_if_middle:NTF {#2}
+                \__xeCJK_punct_if_middle:NTF #2
                   {
-                    \l__xeCJK_tmp_dim
-                    - ( \__xeCJK_use_punct_dim:nnn { glue } {#1} {#2} )
-                    - ( \__xeCJK_use_punct_dim:nn { dimen } {#2} )
+                      \l__xeCJK_tmp_dim
+                    - ( \__xeCJK_use_punct_dim:nNN { glue } #1 #2 )
+                    - ( \__xeCJK_use_punct_dim:nN { dimen } #2 )
                   }
                   {
-                    \l__xeCJK_tmp_dim - \l__xeCJK_reverse_bound_dim
-                    - ( \__xeCJK_use_punct_dim:nn { dimen } {#2} )
+                      \l__xeCJK_tmp_dim
+                    - \l__xeCJK_reverse_bound_dim
+                    - ( \__xeCJK_use_punct_dim:nN { dimen } #2 )
                   }
               }
               {
@@ -2179,8 +2617,9 @@
               }
           }
       }
-    \__xeCJK_save_punct_dim:nnnn { offset } {#1} {#2} { \l__xeCJK_tmp_dim }
-    \__xeCJK_save_punct_dim:nnnn { rule } {#1} {#2}
+    \__xeCJK_save_punct_dim:nNNn { offset } #1 #2
+      { \l__xeCJK_tmp_dim }
+    \__xeCJK_save_punct_dim:nNNn { rule }   #1 #2
       { - \l__xeCJK_bound_dim + \l__xeCJK_tmp_dim }
   }
 \cs_new_nopar:Npn \__xeCJK_punct_width_or_ratio:nN #1#2
@@ -2192,7 +2631,7 @@
           { \c_max_dim }
           {
             \fp_use:c { l__xeCJK_#1_punct_ratio_fp }
-            \etex_dimexpr:D \__xeCJK_use_punct_dim:nn { width } {#2} \scan_stop:
+            \etex_dimexpr:D \__xeCJK_use_punct_dim:nN { width } #2 \scan_stop:
           }
       }
   }
@@ -2210,13 +2649,13 @@
 \cs_new_protected_nopar:Npn \xeCJK_punct_kerning_process:NN #1#2
   {
     \dim_set:Nn \l__xeCJK_original_margin_dim
-      { \__xeCJK_original_kerning_margin:NN {#1} {#2} }
+      { \__xeCJK_original_kerning_margin:NN #1 #2 }
     \dim_set:Nn \l__xeCJK_minimum_bound_dim
-      { \__xeCJK_punct_min_bound:NN {#1} {#2} }
-    \__xeCJK_punct_if_long:NTF {#1}
+      { \__xeCJK_punct_min_bound:NN #1 #2 }
+    \__xeCJK_punct_if_long:NTF #1
       { \bool_set_false:N \l__xeCJK_enabled_kerning_bool }
       {
-        \__xeCJK_punct_if_long:NT {#2}
+        \__xeCJK_punct_if_long:NT #2
           { \bool_set_false:N \l__xeCJK_enabled_kerning_bool }
       }
     \dim_set:Nn \l__xeCJK_kerning_margin_dim
@@ -2223,38 +2662,82 @@
       {
         \bool_if:NTF \l__xeCJK_enabled_global_setting_bool
           {
-            \cs_if_exist_use:cTF { g__xeCJK_punct/kern/#1/#2/tl }
-              { \use_none:n }
-              { \use:n }
+            \cs_if_exist_use:cF { g__xeCJK_punct/kern/#1/#2/tl }
+              { \__xeCJK_punct_kerning_process_aux:NN #1 #2 }
           }
-          { \use:n }
+          { \__xeCJK_punct_kerning_process_aux:NN #1 #2 }
+      }
+    \__xeCJK_save_kerning:nnNN { kern } { bound }  #1 #2
+    \__xeCJK_save_punct_dim:nNNn { bound_width } #1 #2
+      { \l__xeCJK_kerning_margin_dim - \l__xeCJK_tmp_dim }
+    \__xeCJK_punct_if_right:NTF #1
+      {
+        \__xeCJK_punct_if_right:NTF #2
           {
-            \bool_if:NTF \l__xeCJK_enabled_kerning_bool
-              { \__xeCJK_calc_kerning_margin:NN {#1} {#2} }
-              { \l__xeCJK_original_margin_dim }
+            \__xeCJK_save_kerning:nNNNN
+              { bound_kern } \c__xeCJK_left_tl #2 #1 #2
+            \__xeCJK_save_kerning:nnnNN
+              { break_kern } { offset } { bound }
           }
+          {
+            \__xeCJK_save_kerning:nnNN
+              { break_kern } { offset } #1 #2
+            \__xeCJK_save_kerning_aux:nnNN
+              { bound_kern } { \l__xeCJK_kerning_margin_dim }
+          }
       }
-    \__xeCJK_save_kerning:nnNN { kern } { bound } {#1} {#2}
-    \__xeCJK_punct_if_right:NF {#2}
       {
-        \__xeCJK_punct_if_right:NT {#1}
-          { \__xeCJK_save_kerning:nnNN { break_kern } { offset } {#1} {#2} }
+        \__xeCJK_punct_if_right:NTF #2
+          {
+            \__xeCJK_save_kerning:nnNN
+              { bound_kern } { bound } #1 #2
+            \__xeCJK_save_kerning_aux:nnNN
+              { break_kern } { \l__xeCJK_tmp_dim }
+          }
+          {
+            \__xeCJK_save_kerning:nNNNN
+              { bound_kern } \c__xeCJK_right_tl #1 #1 #2
+            \__xeCJK_save_kerning:nnnNN
+              { break_kern } { bound } { offset }
+          }
       }
+      #1 #2
   }
+\cs_new_nopar:Npn \__xeCJK_punct_kerning_process_aux:NN #1#2
+  {
+    \bool_if:NTF \l__xeCJK_enabled_kerning_bool
+      { \__xeCJK_calc_kerning_margin:NN #1 #2 }
+      { \l__xeCJK_original_margin_dim }
+  }
 \dim_new:N \l__xeCJK_minimum_bound_dim
 \dim_new:N \l__xeCJK_kerning_margin_dim
 \dim_new:N \l__xeCJK_original_margin_dim
-\cs_new_protected_nopar:Npn \__xeCJK_save_kerning:nnNN #1#2#3#4
+\cs_new_protected_nopar:Npn \__xeCJK_save_kerning:nnNN #1#2
+  { \__xeCJK_save_kerning:nnnNN {#1} {#2} {#2} }
+\cs_new_protected_nopar:Npn \__xeCJK_save_kerning:nnnNN #1#2#3#4#5
   {
     \dim_set:Nn \l__xeCJK_tmp_dim
       {
-        \l__xeCJK_kerning_margin_dim
-        - ( \__xeCJK_use_punct_dim:nnn {#2} \c__xeCJK_right_tl {#3} )
-        - ( \__xeCJK_use_punct_dim:nnn {#2} \c__xeCJK_left_tl  {#4} )
+          \l__xeCJK_kerning_margin_dim
+        - ( \__xeCJK_use_punct_dim:nNN {#2} \c__xeCJK_right_tl #4 )
+        - ( \__xeCJK_use_punct_dim:nNN {#3} \c__xeCJK_left_tl  #5 )
       }
-    \__xeCJK_save_punct_dim:nnnn {#1} {#3} {#4} { \l__xeCJK_tmp_dim }
-    \__xeCJK_save_punct_skip:nnnnnn {#1} {#3} {#4}
-      { \l__xeCJK_tmp_dim }
+    \__xeCJK_save_kerning_aux:nnNN {#1} { \l__xeCJK_tmp_dim } #4 #5
+  }
+\cs_new_protected_nopar:Npn \__xeCJK_save_kerning:nNNNN #1#2#3#4#5
+  {
+    \dim_set:Nn \l__xeCJK_tmp_dim
+      {
+          \l__xeCJK_kerning_margin_dim
+        - ( \__xeCJK_use_punct_dim:nNN { bound } #2 #3 )
+      }
+    \__xeCJK_save_kerning_aux:nnNN {#1} { \l__xeCJK_tmp_dim } #4 #5
+  }
+\cs_new_protected_nopar:Npn \__xeCJK_save_kerning_aux:nnNN #1#2#3#4
+  {
+    \__xeCJK_save_punct_dim:nNNn {#1} #3 #4 {#2}
+    \__xeCJK_save_punct_skip:nNNnnn {#1} #3 #4
+      {#2}
       { \l__xeCJK_original_margin_dim - \l__xeCJK_kerning_margin_dim }
       { \l__xeCJK_kerning_margin_dim - \l__xeCJK_minimum_bound_dim }
   }
@@ -2262,12 +2745,11 @@
   {
     \dim_eval:n
       {
-        \__xeCJK_use_punct_dim:nnn
-          { \__xeCJK_punct_if_right:NTF {#1} { glue } { bound } }
-          { \c__xeCJK_right_tl } {#1} +
-        \__xeCJK_use_punct_dim:nnn
-          { \__xeCJK_punct_if_right:NTF {#2} { bound } { glue } }
-          { \c__xeCJK_left_tl  } {#2}
+        \__xeCJK_use_punct_dim:nNN
+          { \__xeCJK_punct_if_right:NTF #1 { glue } { bound } } \c__xeCJK_right_tl #1
+        +
+        \__xeCJK_use_punct_dim:nNN
+          { \__xeCJK_punct_if_right:NTF #2 { bound } { glue } } \c__xeCJK_left_tl #2
       }
   }
 \cs_new_nopar:Npn \__xeCJK_calc_kerning_margin:NN #1#2
@@ -2281,31 +2763,33 @@
             \bool_if:NTF \l__xeCJK_optimize_kerning_bool
               { \dim_max:nn { \l__xeCJK_minimum_bound_dim } }
               { \use:n }
+              { \__xeCJK_calc_kerning_margin_aux:NN #1 #2 }
+          }
+      }
+  }
+\cs_new_nopar:Npn \__xeCJK_calc_kerning_margin_aux:NN #1#2
+  {
+    \dim_compare:nNnTF \l__xeCJK_kerning_total_width_dim < \c_max_dim
+      { \__xeCJK_calc_kerning_margin:nNN \l__xeCJK_kerning_total_width_dim }
+      {
+        \fp_compare:nNnTF \l__xeCJK_kerning_total_ratio_fp ? \c_zero_fp
+          {
+            \xeCJK_if_same_class:NNTF #1 #2
+              { \__xeCJK_kerning_width_or_ratio:nNN { same } }
+              { \__xeCJK_kerning_width_or_ratio:nNN { different } }
+          }
+          {
+            \__xeCJK_calc_kerning_margin:nNN
               {
-                \dim_compare:nNnTF \l__xeCJK_kerning_total_width_dim < \c_max_dim
-                  { \__xeCJK_calc_kerning_margin:nNN \l__xeCJK_kerning_total_width_dim }
-                  {
-                    \fp_compare:nNnTF \l__xeCJK_kerning_total_ratio_fp ? \c_zero_fp
-                      {
-                        \xeCJK_if_same_class:NNTF {#1} {#2}
-                          { \__xeCJK_kerning_width_or_ratio:nNN { same } }
-                          { \__xeCJK_kerning_width_or_ratio:nNN { different } }
-                      }
-                      {
-                        \__xeCJK_calc_kerning_margin:nNN
-                          {
-                            \fp_use:N \l__xeCJK_kerning_total_ratio_fp
-                            \etex_dimexpr:D
-                              \__xeCJK_use_punct_dim:nn { width } {#1} +
-                              \__xeCJK_use_punct_dim:nn { width } {#2}
-                            \scan_stop:
-                          }
-                      }
-                  }
-                  {#1} {#2}
+                \fp_use:N \l__xeCJK_kerning_total_ratio_fp
+                \etex_dimexpr:D
+                  \__xeCJK_use_punct_dim:nN { width } #1 +
+                  \__xeCJK_use_punct_dim:nN { width } #2
+                \scan_stop:
               }
           }
       }
+      #1 #2
   }
 \cs_new_nopar:Npn \__xeCJK_kerning_width_or_ratio:nNN #1#2#3
   {
@@ -2327,13 +2811,13 @@
     \dim_max:nn
       {
         \dim_min:nn
-          { \__xeCJK_use_punct_dim:nnn { bound } \c__xeCJK_left_tl  {#1} }
-          { \__xeCJK_use_punct_dim:nnn { bound } \c__xeCJK_right_tl {#1} }
+          { \__xeCJK_use_punct_dim:nNN { bound } \c__xeCJK_left_tl  #1 }
+          { \__xeCJK_use_punct_dim:nNN { bound } \c__xeCJK_right_tl #1 }
       }
       {
         \dim_min:nn
-          { \__xeCJK_use_punct_dim:nnn { bound } \c__xeCJK_left_tl  {#2} }
-          { \__xeCJK_use_punct_dim:nnn { bound } \c__xeCJK_right_tl {#2} }
+          { \__xeCJK_use_punct_dim:nNN { bound } \c__xeCJK_left_tl  #2 }
+          { \__xeCJK_use_punct_dim:nNN { bound } \c__xeCJK_right_tl #2 }
       }
   }
 \cs_new_nopar:Npn \__xeCJK_calc_kerning_margin:nNN #1#2#3
@@ -2340,33 +2824,33 @@
   {
     \dim_eval:n
       {
-        (#1)
-        - ( \__xeCJK_use_punct_dim:nnn
-              { \__xeCJK_punct_if_right:NTF {#2} { bound } { glue } }
-              { \c__xeCJK_left_tl } {#2} )
-        - ( \__xeCJK_use_punct_dim:nnn
-              { \__xeCJK_punct_if_right:NTF {#3} { glue } { bound } }
-              { \c__xeCJK_right_tl } {#3} )
-        - ( \__xeCJK_use_punct_dim:nn { dimen } {#2} )
-        - ( \__xeCJK_use_punct_dim:nn { dimen } {#3} )
+          (#1)
+        - ( \__xeCJK_use_punct_dim:nNN
+              { \__xeCJK_punct_if_right:NTF #2 { bound } { glue } }
+              \c__xeCJK_left_tl #2 )
+        - ( \__xeCJK_use_punct_dim:nNN
+              { \__xeCJK_punct_if_right:NTF #3 { glue } { bound } }
+              \c__xeCJK_right_tl #3 )
+        - ( \__xeCJK_use_punct_dim:nN { dimen } #2 )
+        - ( \__xeCJK_use_punct_dim:nN { dimen } #3 )
       }
   }
 \cs_new_protected_nopar:Npn \xeCJK_calc_punct_dimen:N #1
   {
-    \__xeCJK_save_punct_dim:nnnn { bound } \c__xeCJK_left_tl {#1}
-      { \xeCJK_glyph_bounds:NN \c_one {#1} }
-    \__xeCJK_save_punct_dim:nnnn { bound } \c__xeCJK_right_tl {#1}
-      { \xeCJK_glyph_bounds:NN \c_three {#1} }
+    \__xeCJK_save_punct_dim:nNNn { bound } \c__xeCJK_left_tl #1
+      { \xeCJK_glyph_bounds:NN \c_one #1 }
+    \__xeCJK_save_punct_dim:nNNn { bound } \c__xeCJK_right_tl #1
+      { \xeCJK_glyph_bounds:NN \c_three #1 }
     \dim_set:Nn \l__xeCJK_tmp_dim
       {
-        ( \__xeCJK_use_punct_dim:nnn { bound } \c__xeCJK_left_tl  {#1} ) +
-        ( \__xeCJK_use_punct_dim:nnn { bound } \c__xeCJK_right_tl {#1} )
+        ( \__xeCJK_use_punct_dim:nNN { bound } \c__xeCJK_left_tl  #1 ) +
+        ( \__xeCJK_use_punct_dim:nNN { bound } \c__xeCJK_right_tl #1 )
       }
-    \__xeCJK_save_punct_dim:nnn { width } {#1}
-      { \etex_fontcharwd:D \tex_font:D \xeCJK_token_value_charcode:N #1 }
-    \__xeCJK_save_punct_dim:nnn { dimen } {#1}
-      { \__xeCJK_use_punct_dim:nn { width } {#1} - \l__xeCJK_tmp_dim }
-    \__xeCJK_punct_if_long:NT {#1}
+    \__xeCJK_save_punct_dim:nNn { width } #1
+      { \etex_fontcharwd:D \tex_font:D `#1 }
+    \__xeCJK_save_punct_dim:nNn { dimen } #1
+      { \__xeCJK_use_punct_dim:nN { width } #1 - \l__xeCJK_tmp_dim }
+    \__xeCJK_punct_if_long:NT #1
       {
         \dim_set:Nn \l__xeCJK_tmp_dim
           {
@@ -2375,16 +2859,12 @@
               { \c_zero_dim }
               { \dim_min:nn { - \l__xeCJK_tmp_dim } { \c_zero_dim } }
           }
-        \__xeCJK_save_punct_dim:nnnn { kern } {#1} {#1} { \l__xeCJK_tmp_dim }
-        \__xeCJK_save_punct_skip:nnnn { kern } {#1} {#1} { \l__xeCJK_tmp_dim }
+        \__xeCJK_save_punct_dim:nNNn  { kern } #1 #1 { \l__xeCJK_tmp_dim }
+        \__xeCJK_save_punct_skip:nNNn { kern } #1 #1 { \l__xeCJK_tmp_dim }
       }
   }
-\cs_generate_variant:Nn \xeCJK_calc_punct_dimen:N { o }
 \cs_new_nopar:Npn \xeCJK_glyph_bounds:NN #1#2
-  {
-    \dim_use:N \xetex_glyphbounds:D #1 ~
-    \xetex_charglyph:D \xeCJK_token_value_charcode:N #2 \exp_stop_f:
-  }
+  { \xetex_glyphbounds:D #1 ~ \xetex_charglyph:D `#2 \exp_stop_f: }
 \keys_define:nn { xeCJK / options }
   {
     PunctStyle .choice: ,
@@ -2476,13 +2956,13 @@
   }
 \cs_new_protected_nopar:Npn \xeCJK_fallback_test_glyph:N #1
   {
-    \xeCJK_glyph_if_exist:NTF {#1}
-      { \__xeCJK_fallback_save_CJKsymbol:N {#1} }
+    \xeCJK_glyph_if_exist:NTF #1
+      { \__xeCJK_fallback_save_CJKsymbol:N #1 }
       {
         \group_begin:
-          \xeCJK_aftergroup_reset_Boundary:N {#1}
+          \xeCJK_aftergroup_reset_Boundary:N #1
           \tl_set_eq:NN \l__xeCJK_fallback_family_tl \l_xeCJK_family_tl
-          \xeCJK_fallback_loop:Nn {#1} { \l_xeCJK_family_tl/FallBack }
+          \xeCJK_fallback_loop:No #1 { \l_xeCJK_family_tl/FallBack }
         \group_end:
       }
   }
@@ -2498,14 +2978,14 @@
 \tl_new:N \g__xeCJK_aftergroup_Boundary_tl
 \cs_new_protected_nopar:Npn \xeCJK_fallback_loop:Nn #1#2
   {
-    \xeCJK_family_if_exist:xTF {#2}
+    \xeCJK_family_if_exist:nTF {#2}
       {
-        \tl_set:Nx \l_xeCJK_family_tl {#2}
+        \tl_set:Nn \l_xeCJK_family_tl {#2}
         \tl_set_eq:NN \CJK at family \l__xeCJK_fontspec_family_tl
         \xeCJK_select_font:
-        \xeCJK_glyph_if_exist:NTF {#1}
-          { \__xeCJK_fallback_save_CJKsymbol:N {#1} }
-          { \xeCJK_fallback_loop:Nn {#1} { \l_xeCJK_family_tl/FallBack } }
+        \xeCJK_glyph_if_exist:NTF #1
+          { \__xeCJK_fallback_save_CJKsymbol:N #1 }
+          { \xeCJK_fallback_loop:No #1 { \l_xeCJK_family_tl/FallBack } }
       }
       {
         \str_if_eq_x:nnTF { \CJKfamilydefault } { \l__xeCJK_fallback_family_tl }
@@ -2513,14 +2993,15 @@
             \__xeCJK_warning:nxxx { missing-glyph }
               { \l_xeCJK_family_tl } {#1}
               { \int_to_Hex:n { `#1 } }
-            \__xeCJK_fallback_save_CJKsymbol:N {#1}
+            \__xeCJK_fallback_save_CJKsymbol:N #1
           }
           {
             \tl_set:Nx \l__xeCJK_fallback_family_tl { \CJKfamilydefault }
-            \xeCJK_fallback_loop:Nn {#1} { \l__xeCJK_fallback_family_tl }
+            \xeCJK_fallback_loop:Nn #1 { \l__xeCJK_fallback_family_tl }
           }
       }
   }
+\cs_generate_variant:Nn \xeCJK_fallback_loop:Nn { No }
 \__xeCJK_msg_new:nn { missing-glyph }
   {
     CJKfamily~`\__xeCJK_msg_family_map:n {#1}'~
@@ -2537,7 +3018,7 @@
   {
     \group_begin:
     \tl_set:Nn \l__xeCJK_fallback_family_tl {#1}
-    \prop_get:NVNF \g__xeCJK_family_font_name_prop
+    \prop_get:NoNF \g__xeCJK_family_font_name_prop
       \l__xeCJK_fallback_family_tl \l__xeCJK_font_name_tl
       { \tl_clear:N \l__xeCJK_font_name_tl }
     \clist_map_inline:nn {#3}
@@ -2821,13 +3302,13 @@
 \cs_new_protected_nopar:Npn \__xeCJK_copy_sub_family:n #1
   {
     \__xeCJK_check_family:V \l__xeCJK_sub_family_name_tl
-    \prop_get:NVNT \g__xeCJK_family_font_name_prop
+    \prop_get:NoNT \g__xeCJK_family_font_name_prop
       \l__xeCJK_family_name_tl \l__xeCJK_sub_font_name_tl
       {
         \prop_gput:NVV \g__xeCJK_family_font_name_prop
           \l__xeCJK_sub_family_name_tl \l__xeCJK_sub_font_name_tl
       }
-    \prop_get:NVNT \g__xeCJK_family_font_options_prop
+    \prop_get:NoNT \g__xeCJK_family_font_options_prop
       \l__xeCJK_family_name_tl \l__xeCJK_sub_font_options_clist
       {
         \clist_remove_all:Nn \l__xeCJK_sub_font_options_clist { #1 = * }
@@ -2867,19 +3348,37 @@
   }
 \cs_new_protected_nopar:Npn \__xeCJK_copy_family:xx #1#2
   { \use:x { \__xeCJK_copy_family:nn {#1} {#2} } }
+\cs_new_nopar:Npn \__xeCJK_font_csname:n #1
+  { xeCJK/#1/\f at series/\f at shape/\f at size }
 \tl_new:N \l_xeCJK_current_font_tl
-\tl_set:Nn \l_xeCJK_current_font_tl { \__xeCJK_font_csname:n { \CJK at family } }
-\cs_new_nopar:Npn \__xeCJK_font_csname:n #1 { xeCJK/#1/\f at series/\f at shape/\f at size }
+\tl_set:No \l_xeCJK_current_font_tl
+  { \__xeCJK_font_csname:n { \CJK at family } }
 \cs_new_protected_nopar:Npn \xeCJK_select_font:
   {
     \exp_args:Nc \cs_if_exist_use:NF { \l_xeCJK_current_font_tl }
       {
-        \__xeCJK_family_use:x { \l_xeCJK_family_tl }
+        \__xeCJK_family_use:n { \l_xeCJK_family_tl }
         \xeCJK_font_gset_to_current:c { \l_xeCJK_current_font_tl }
       }
   }
 \tl_new:N \l__xeCJK_current_coor_tl
 \cs_new_eq:NN \xeCJK at setfont \xeCJK_select_font:
+\cs_new_eq:NN \xeCJK_select_punct_font: \xeCJK_select_font:
+\cs_new_protected_nopar:Npn \__xeCJK_select_punct_font_aux:
+  {
+    \exp_args:Nc \cs_if_exist_use:NF { \l_xeCJK_current_punct_font_tl }
+      {
+        \__xeCJK_family_use:n { \l_xeCJK_punct_family_tl }
+        \xeCJK_font_gset_to_current:c { \l_xeCJK_current_punct_font_tl }
+      }
+  }
+\tl_new:N \CJK at punctfamily
+\tl_new:N \l_xeCJK_punct_family_tl
+\tl_new:N \l_xeCJK_current_punct_font_tl
+\tl_set:No \l_xeCJK_current_punct_font_tl
+  { \__xeCJK_font_csname:n { \CJK at punctfamily } }
+\cs_new_eq:NN \__xeCJK_select_font: \prg_do_nothing:
+\cs_new_eq:NN \__xeCJK_select_punct_font: \prg_do_nothing:
 \cs_new_protected_nopar:Npn \__xeCJK_switch_font:nn #1#2
   {
     \str_if_eq:nnF {#1} {#2}
@@ -2893,10 +3392,11 @@
 \__xeCJK_msg_new:nn { CJK-block } { Switch~from~block~`#1'~to~`#2'. }
 \cs_new_protected_nopar:Npn \xeCJK_select_font:n #1
   {
-    \exp_args:Nc \cs_if_exist_use:NF { \__xeCJK_font_csname:n { \CJK at family/#1 } }
+    \exp_args:Nc \cs_if_exist_use:NF
+      { \__xeCJK_font_csname:n { \CJK at family/#1 } }
       {
         \xeCJK_block_family:nn { \l_xeCJK_family_tl } {#1}
-        \__xeCJK_family_use:x { \l_xeCJK_family_tl/#1 }
+        \__xeCJK_family_use:n { \l_xeCJK_family_tl/#1 }
         \xeCJK_font_gset_to_current:c
           { \__xeCJK_font_csname:n { \CJK at family/#1 } }
       }
@@ -2913,73 +3413,113 @@
           }
       }
   }
-\cs_new_nopar:Npn \__xeCJK_family_csname:n #1 { xeCJK/family/#1 }
-\cs_new_nopar:Npn \__xeCJK_family_nfss_csname:n #1 { xeCJK/family/nfss/#1 }
-\cs_new_nopar:Npn \__xeCJK_family_use:x #1 { \use:c { \__xeCJK_family_nfss_csname:n {#1} } }
-\cs_new_protected_nopar:Npn \__xeCJK_gset_family_nfss_cs:xx #1#2
+\cs_new_nopar:Npn \__xeCJK_family_csname:n #1
+  { xeCJK/family/#1 }
+\cs_new_nopar:Npn \__xeCJK_family_nfss_csname:n #1
+  { xeCJK/family/nfss/#1 }
+\cs_new_nopar:Npn \__xeCJK_family_use:n #1
+  { \use:c { \__xeCJK_family_nfss_csname:n {#1} } }
+\cs_new_protected_nopar:Npn \__xeCJK_gset_family_nfss_cs:nn #1#2
   {
-    \prop_gput:Nxx \g__xeCJK_family_name_prop {#1} {#2}
-    \cs_gset_protected_nopar:cpx { \__xeCJK_family_nfss_csname:n {#1} }
+    \prop_gput:Nnn \g__xeCJK_family_name_prop {#1} {#2}
+    \cs_gset_protected_nopar:cpx
+      { \__xeCJK_family_nfss_csname:n {#1} }
       {
         \exp_not:N \fontencoding { \c__xeCJK_encoding_tl }
-        \tl_set:Nx \exp_not:N \f at family {#2}
+        \tl_set:Nn \exp_not:N \f at family {#2}
         \exp_not:N \selectfont
       }
   }
-\cs_generate_variant:Nn \prop_gput:Nnn { Nxx }
+\cs_generate_variant:Nn \__xeCJK_gset_family_nfss_cs:nn { xx }
 \prg_new_protected_conditional:Npnn \xeCJK_family_if_exist:n #1 { T , F , TF }
   {
-    \prop_get:NnNTF \g__xeCJK_family_name_prop {#1} \l__xeCJK_fontspec_family_tl
+    \prop_get:NnNTF \g__xeCJK_family_name_prop
+      {#1} \l__xeCJK_fontspec_family_tl
       { \prg_return_true: }
       {
-        \exp_args:Nc \cs_if_exist_use:NTF { \__xeCJK_family_csname:n {#1} }
-          { \prg_return_true: } { \prg_return_false: }
+        \cs_if_exist_use:cTF { \__xeCJK_family_csname:n {#1} }
+          { \prg_return_true: }
+          { \prg_return_false: }
       }
   }
-\cs_generate_variant:Nn \xeCJK_family_if_exist:nT  { x }
-\cs_generate_variant:Nn \xeCJK_family_if_exist:nF  { x }
-\cs_generate_variant:Nn \xeCJK_family_if_exist:nTF { x }
+\prg_generate_conditional_variant:Nnn \xeCJK_family_if_exist:n { x } { T , F , TF }
 \NewDocumentCommand \CJKfamily { t+ t- m }
   {
-    \xeCJK_if_blank_x:nTF {#3}
+    \xeCJK_family:NNx #1 #2 {#3}
+    \tex_ignorespaces:D
+  }
+\cs_new_protected_nopar:Npn \xeCJK_family:NNn #1#2#3
+  {
+    \tl_if_blank:nTF {#3}
       {
-        \IfBooleanF {#1} { \IfBooleanF {#2} { \use_none:nn } }
+        \bool_if:NF #1 { \bool_if:NF #2 { \use_none:nn } }
         \xeCJK_family_if_exist_use:x { \l_xeCJK_family_tl }
       }
       {
-        \IfBooleanTF {#2} { \xeCJK_family_if_exist_use:x {#3} }
+        \bool_if:NTF #2
+          { \xeCJK_family_if_exist_use:n {#3} }
           {
-            \xeCJK_family_if_exist:xTF {#3}
+            \xeCJK_family_if_exist:nTF {#3}
               {
-                \tl_set:Nx \l_xeCJK_family_tl {#3}
+                \tl_set:Nn \l_xeCJK_family_tl {#3}
                 \tl_set_eq:NN \CJK at family \l__xeCJK_fontspec_family_tl
-                \IfBooleanT {#1} { \__xeCJK_family_use:x {#3} }
+                \bool_if:NT #1 { \__xeCJK_family_use:n {#3} }
               }
-              { \__xeCJK_family_unknown_warning:x {#3} }
+              { \__xeCJK_family_unknown_warning:n {#3} }
           }
       }
-    \tex_ignorespaces:D
   }
+\cs_generate_variant:Nn \xeCJK_family:NNn { NNx }
 \cs_new_protected_nopar:Npn \xeCJK_switch_family:n #1
   {
-    \xeCJK_family_if_exist:xTF {#1}
+    \xeCJK_family_if_exist:nTF {#1}
       {
-        \tl_set:Nx \l_xeCJK_family_tl {#1}
+        \tl_set:Nn \l_xeCJK_family_tl {#1}
         \tl_set_eq:NN \CJK at family \l__xeCJK_fontspec_family_tl
       }
-      { \__xeCJK_family_unknown_warning:x {#1} }
+      { \__xeCJK_family_unknown_warning:n {#1} }
   }
+\cs_generate_variant:Nn \xeCJK_switch_family:n { x , o }
+\keys_define:nn { xeCJK / options }
+  {
+    PunctFamily .choice: ,
+    PunctFamily .value_required:n = { true } ,
+    PunctFamily / false   .code:n =
+      {
+        \tl_clear:N \l_xeCJK_punct_family_tl
+        \tl_clear:N \CJK at punctfamily
+        \xeCJK_cs_clear:N \__xeCJK_select_font:
+        \xeCJK_cs_clear:N \__xeCJK_select_punct_font:
+        \cs_set_eq:NN \xeCJK_select_punct_font: \xeCJK_select_font:
+      } ,
+    PunctFamily / unknown .code:n =
+      { \xeCJK_punct_family:x {#1} } ,
+  }
+\cs_new_protected_nopar:Npn \xeCJK_punct_family:n #1
+  {
+    \xeCJK_family_if_exist:nTF {#1}
+      {
+        \tl_set:Nn \l_xeCJK_punct_family_tl {#1}
+        \tl_set_eq:NN \CJK at punctfamily \l__xeCJK_fontspec_family_tl
+        \cs_set_eq:NN \__xeCJK_select_font: \xeCJK_select_font:
+        \cs_set_eq:NN \__xeCJK_select_punct_font: \__xeCJK_select_punct_font_aux:
+        \cs_set_eq:NN \xeCJK_select_punct_font: \__xeCJK_select_punct_font:
+      }
+      { \__xeCJK_family_unknown_warning:n {#1} }
+  }
+\cs_generate_variant:Nn \xeCJK_punct_family:n { x }
 \tl_new:N \l_xeCJK_family_tl
 \tl_new:N \CJK at family
 \cs_new_protected_nopar:Npn \__xeCJK_gobble_CJKfamily:
   { \cs_set_eq:NN \CJKfamily \__xeCJK_gobble_CJKfamily:wn }
 \DeclareExpandableDocumentCommand \__xeCJK_gobble_CJKfamily:wn { t+ t- m } {  }
-\cs_new_protected_nopar:Npn \xeCJK_family_if_exist_use:x #1
+\cs_new_protected_nopar:Npn \xeCJK_family_if_exist_use:n #1
   {
-    \xeCJK_family_if_exist:xTF {#1}
-      { \__xeCJK_family_use:x {#1} }
-      { \__xeCJK_family_unknown_warning:x {#1} }
+    \xeCJK_family_if_exist:nTF {#1}
+      { \__xeCJK_family_use:n {#1} }
+      { \__xeCJK_family_unknown_warning:n {#1} }
   }
+\cs_generate_variant:Nn \xeCJK_family_if_exist_use:n { x }
 \cs_new_protected_nopar:Npn \__xeCJK_family_unknown_warning:n #1
   {
     \prop_if_empty:NF \g__xeCJK_family_font_name_prop
@@ -3021,7 +3561,7 @@
   }
 \cs_new_protected:Npn \__xeCJK_pass_args:nnnn #1#2#3#4
   {
-    \IfNoValueTF {#2}
+    \tl_if_novalue:nTF {#2}
       { \__xeCJK_post_arg:w {#1} {#3} {#4} }
       {
         \use:x { #1 {#2} {#3} }
@@ -3065,8 +3605,10 @@
   }
 \NewDocumentCommand \newCJKfontfamily { o m o m }
   {
-    \tl_set:Nx \l__xeCJK_tmp_tl { \IfNoValueTF {#1} { \cs_to_str:N #2 } {#1} }
-    \cs_new_protected_nopar:Npx #2 { \xeCJK_switch_family:n { \l__xeCJK_tmp_tl } }
+    \tl_set:Nx \l__xeCJK_tmp_tl
+      { \tl_if_novalue:nTF {#1} { \cs_to_str:N #2 } {#1} }
+    \cs_new_protected_nopar:Npx #2
+      { \xeCJK_switch_family:n { \l__xeCJK_tmp_tl } }
     \__xeCJK_pass_args:nnnn
       { \xeCJK_set_family:nnn { \l__xeCJK_tmp_tl } } {#3} {#4}
       { }
@@ -3081,7 +3623,7 @@
   {
     \prop_get:NnNTF \g__xeCJK_fontspec_prop
       { CJKfontspec/#1/#2/id } \l_xeCJK_family_tl
-      { \xeCJK_switch_family:n { \l_xeCJK_family_tl } }
+      { \xeCJK_switch_family:o { \l_xeCJK_family_tl } }
       {
         \__xeCJK_fontspec:xnn
           { CJKfontspec ( \int_eval:n { \g__xeCJK_family_int + \c_one } ) }
@@ -3109,7 +3651,7 @@
 \cs_new_eq:NN \addCJKfontfeature \addCJKfontfeatures
 \cs_new_protected_nopar:Npn \xeCJK_add_font_features:Nnn #1#2#3
   {
-    \prop_get:NVNTF \g__xeCJK_family_font_name_prop
+    \prop_get:NoNTF \g__xeCJK_family_font_name_prop
       \l_xeCJK_family_tl \l__xeCJK_font_name_tl
       {
         \clist_set:Nn \l__xeCJK_add_font_features_clist {#3}
@@ -3133,7 +3675,7 @@
             \seq_map_function:NN
               \g__xeCJK_sub_key_seq \__xeCJK_add_sub_class_features:n
           }
-        \prop_get:NVNT \g__xeCJK_family_font_options_prop
+        \prop_get:NoNT \g__xeCJK_family_font_options_prop
           \l_xeCJK_family_tl \l__xeCJK_font_options_clist
           {
             \bool_lazy_or:nnT
@@ -3174,7 +3716,7 @@
               { \CJKfamilydefault/#1 } \l__xeCJK_sub_font_options_clist
           }
           {
-            \prop_get:NVN \g__xeCJK_family_font_options_prop
+            \prop_get:NoN \g__xeCJK_family_font_options_prop
               \l_xeCJK_family_tl \l__xeCJK_sub_font_options_clist
             \tl_set_eq:NN \l__xeCJK_sub_font_name_tl \l__xeCJK_font_name_tl
           }
@@ -3190,8 +3732,8 @@
           }
       }
   }
-\cs_generate_variant:Nn \prop_get:NnN   { Nx }
-\cs_generate_variant:Nn \prop_get:NnNTF { Nx }
+\cs_generate_variant:Nn \prop_get:NnN { Nx }
+\prg_generate_conditional_variant:Nnn \prop_get:NnN { Nx } { TF }
 \keys_define:nn { xeCJK / options }
   { LoadFandol .bool_gset:N = \g__xeCJK_fandol_bool }
 \cs_new_protected_nopar:Npn \__xeCJK_load_fandol:
@@ -3255,7 +3797,7 @@
         \__xeCJK_warning:nxx { CJKfamilydefault-undefined }
           { \l__xeCJK_tmp_tl } { \CJKfamilydefault }
       }
-    \xeCJK_switch_family:n { \CJKfamilydefault }
+    \xeCJK_switch_family:x { \CJKfamilydefault }
     \bool_if:NT \g__xeCJK_math_bool { \xeCJK_set_mathfont: }
   }
 \__xeCJK_msg_new:nn { no-CJKfamily }
@@ -3298,14 +3840,19 @@
   }
 \cs_new_protected_nopar:Npn \__xeCJK_set_mathfont_aux:
   {
-    \tl_const:Nx \c__xeCJK_math_family_tl { \l__xeCJK_fontspec_family_tl }
-    \xeCJK_declare_mathfont:nn { \c__xeCJK_math_tl } { \c__xeCJK_math_family_tl }
-    \int_const:Nn \c_xeCJK_math_fam_int { \use:c { sym \c__xeCJK_math_tl } }
-    \clist_concat:NNN \g__xeCJK_math_chars_clist
+    \tl_const:Nx \c__xeCJK_math_family_tl
+      { \l__xeCJK_fontspec_family_tl }
+    \xeCJK_declare_mathfont:xx
+      { \c__xeCJK_math_tl }
+      { \c__xeCJK_math_family_tl }
+    \int_const:Nn \c_xeCJK_math_fam_int
+      { \use:c { sym \c__xeCJK_math_tl } }
+    \clist_gconcat:NNN \g__xeCJK_math_chars_clist
       \g__xeCJK_CJK_range_clist \g__xeCJK_FullLeft_range_clist
-    \clist_concat:NNN \g__xeCJK_math_chars_clist
+    \clist_gconcat:NNN \g__xeCJK_math_chars_clist
       \g__xeCJK_math_chars_clist \g__xeCJK_FullRight_range_clist
-    \xeCJK_gset_mathcode:Nn \g__xeCJK_math_chars_clist { \c_xeCJK_math_fam_int }
+    \xeCJK_gset_mathcode:Nn \g__xeCJK_math_chars_clist
+      { \c_xeCJK_math_fam_int }
     \xeCJK_set_mathfont_block:
   }
 \clist_new:N \g__xeCJK_math_chars_clist
@@ -3323,12 +3870,14 @@
   {
     \xeCJK_block_family:nn { \c__xeCJK_math_tl } {#1}
     \prop_get:NoNTF \g__xeCJK_fam_prop
-      { \l__xeCJK_fontspec_family_tl } \l__xeCJK_tmp_tl
+      \l__xeCJK_fontspec_family_tl \l__xeCJK_tmp_tl
       { \int_set:Nn \l__xeCJK_fam_int { \l__xeCJK_tmp_tl } }
       {
-        \xeCJK_declare_mathfont:nn
-          { \c__xeCJK_math_tl / #1 } { \l__xeCJK_fontspec_family_tl }
-        \__xeCJK_set_mathfont_block_aux:cn { sym \c__xeCJK_math_tl / #1 } {#1}
+        \xeCJK_declare_mathfont:xx
+          { \c__xeCJK_math_tl / #1 }
+          { \l__xeCJK_fontspec_family_tl }
+        \__xeCJK_set_mathfont_block_aux:cn
+          { sym \c__xeCJK_math_tl / #1 } {#1}
       }
     \xeCJK_gset_mathcode:cn { g__xeCJK_CJK/#1_range_clist } { \l__xeCJK_fam_int }
   }
@@ -3350,8 +3899,10 @@
         \SetSymbolFont {#1} { bold } { \c__xeCJK_encoding_tl }
           {#2} { \bfdefault } { \updefault }
       }
-    \prop_gput:Nxx \g__xeCJK_fam_prop {#2} { \exp_not:c { sym #1 } }
+    \prop_gput:Nnx \g__xeCJK_fam_prop {#2} { \exp_not:c { sym #1 } }
   }
+\cs_generate_variant:Nn \prop_gput:Nnn { Nnx }
+\cs_generate_variant:Nn \xeCJK_declare_mathfont:nn { xx }
 \cs_new_protected:Npn \xeCJK_declare_symbol_font:nnnnn #1
   { \__xeCJK_declare_symbol_font:cnnnn { sym #1 } }
 \cs_new_protected:Npn \__xeCJK_declare_symbol_font:Nnnnn #1
@@ -3474,8 +4025,6 @@
   { \xeCJK_no_break: \skip_horizontal:N \l__xeCJK_ccglue_skip }
 \cs_new_protected_nopar:Npn \__xeCJK_nobreak_ecglue:
   { \xeCJK_no_break: \skip_horizontal:N \l__xeCJK_ecglue_skip }
-\cs_new_protected_nopar:Npn \__xeCJK_nobreak_hskip:n
-  { \xeCJK_no_break: \skip_horizontal:n }
 \cs_new_protected_nopar:Npn \__xeCJK_reset_shipout_skip:
   {
     \cs_set_eq:NN \__xeCJK_shipout_CJKglue:   \CJKglue
@@ -3493,8 +4042,8 @@
             \cs_set_eq:NN \CJKglue \__xeCJK_shipout_CJKglue:
             \cs_set_eq:NN \CJKecglue \__xeCJK_shipout_CJKecglue:
             \cs_set_eq:NN \__xeCJK_punct_hskip:n \__xeCJK_shipout_punct_hskip:n
-            \cs_set_eq:NN
-              \__xeCJK_punct_breakable_kern:n \__xeCJK_shipout_punct_breakable_kern:n
+            \cs_set_eq:NN \__xeCJK_punct_breakable_kern:n
+                          \__xeCJK_shipout_punct_breakable_kern:n
             \l__xeCJK_reset_shipout_skip_hook_tl
           }
       }
@@ -3596,10 +4145,10 @@
       }
       {
         \tl_set:Nx \l__xeCJK_current_coor_tl { \CJK at family/\curr at fontshape }
-        \prop_get:NVNTF \g__xeCJK_scale_family_prop
+        \prop_get:NoNTF \g__xeCJK_scale_family_prop
           \l__xeCJK_current_coor_tl \l_xeCJK_family_tl
           {
-            \xeCJK_switch_family:n { \l_xeCJK_family_tl }
+            \xeCJK_switch_family:o { \l_xeCJK_family_tl }
             \skip_zero:N \l__xeCJK_verb_exspace_skip
           }
           {
@@ -3676,7 +4225,8 @@
   } }
 \cs_new_protected_nopar:Npn \xeCJK_set_visible_space_font:
   {
-    \tl_set:Nx \l__xeCJK_current_coor_tl { xeCJK/space/\curr at fontshape/\f at size }
+    \tl_set:Nx \l__xeCJK_current_coor_tl
+      { xeCJK/space/\curr at fontshape/\f at size }
     \exp_after:wN \__xeCJK_set_visible_space_size:n
     \exp_after:wN { \dim_use:N \tex_fontdimen:D \c_two \tex_font:D }
     \xeCJK_font_gset_to_current:c { \l__xeCJK_current_coor_tl }
@@ -3759,7 +4309,7 @@
   }
 \__xeCJK_msg_new:nn { key-unknown }
   {
-    Sorry,~but~\l__keys_module_tl \ does~not~have~a~key~called~`#1'.\\\\
+    Sorry,~but~xeCJK/options~does~not~have~a~key~called~`#1'.\\\\
     The~key~`#1'~is~being~ignored.
   }
 \cs_new_nopar:Npn \CJKsymbol      #1 {#1}
@@ -3787,8 +4337,8 @@
     WidowPenalty    = \c_ten_thousand ,
     NoBreakCS       = { \footnote \footnotemark \nobreak } ,
     KaiMingPunct    = { ^^^^3002 ^^^^ff0e ^^^^ff1f ^^^^ff01 } ,
-    LongPunct       = { ^^^^2014 ^^^^2025 ^^^^2026 } ,
-    MiddlePunct     = { ^^^^2013 ^^^^2014 ^^^^2027 ^^^^00b7 ^^^^30fb ^^^^ff65 } ,
+    LongPunct       = { ^^^^2014 ^^^^2e3a ^^^^2025 ^^^^2026 } ,
+    MiddlePunct     = { ^^^^2013 ^^^^2014 ^^^^2e3a ^^^^2027 ^^^^00b7 ^^^^30fb ^^^^ff65 } ,
     AllowBreakBetweenPuncts = false
   }
 \defaultCJKfontfeatures { Script = CJK }
@@ -3863,7 +4413,7 @@
           \cs_set_protected_nopar:Npx \hbar
             { {
               \mathchar
-                \int_eval:n { \symlegacymaths * \c_two_hundred_fifty_six + '26 } ~
+                \int_eval:n { \symlegacymaths * 256 + '26 } ~
               \mkern -9mu h
             } }
         \fi:
@@ -4040,7 +4590,7 @@
 \cs_new_protected_nopar:Npn \xeCJK at fontfamily #1
   {
     \str_if_eq:nnTF {#1} { \familydefault }
-      { \xeCJK_switch_family:n { \CJKfamilydefault } }
+      { \xeCJK_switch_family:x { \CJKfamilydefault } }
       { \__xeCJK_update_family_aux: }
   }
 \cs_new_protected_nopar:Npn \__xeCJK_update_family_aux:
@@ -4047,10 +4597,10 @@
   {
     \str_case_x:nn { \f at family }
       {
-        { \rmdefault }     { \xeCJK_switch_family:n { \CJKrmdefault } }
-        { \sfdefault }     { \xeCJK_switch_family:n { \CJKsfdefault } }
-        { \ttdefault }     { \xeCJK_switch_family:n { \CJKttdefault } }
-        { \familydefault } { \xeCJK_switch_family:n { \CJKfamilydefault } }
+        { \rmdefault }     { \xeCJK_switch_family:x { \CJKrmdefault } }
+        { \sfdefault }     { \xeCJK_switch_family:x { \CJKsfdefault } }
+        { \ttdefault }     { \xeCJK_switch_family:x { \CJKttdefault } }
+        { \familydefault } { \xeCJK_switch_family:x { \CJKfamilydefault } }
       }
   }
 \cs_new_eq:NN \xeCJK at fix@penalty \fix at penalty
@@ -4059,37 +4609,46 @@
 \cs_new_protected_nopar:Npn \xeCJK at italiccorr
   {
     \int_compare:nNnTF \xetex_interchartokenstate:D > \c_zero
+      { \xeCJK_italic_correction: }
+      { \@@italiccorr }
+  }
+\cs_new_protected_nopar:Npn \xeCJK_italic_correction:
+  {
+    \int_compare:nNnT \etex_lastnodetype:D = \c_twelve
+      { \__xeCJK_italic_correction: }
+  }
+\cs_new_protected_nopar:Npn \__xeCJK_italic_correction:
+  {
+    \dim_case:nnF { \tex_lastkern:D }
       {
-        \xeCJK_if_last_node:nTF { default }
-          {
-            \xeCJK_remove_node: \@@italiccorr
-            { \xeCJK_make_node:n { default } }
-          }
-          {
-            \xeCJK_if_last_node:nTF { CJK }
-              {
-                \xeCJK_remove_node: \@@italiccorr
-                { \xeCJK_make_node:n { CJK } } \use:n
-              }
-              {
-                \xeCJK_if_last_node:nTF { CJK-space }
-                  {
-                    \xeCJK_remove_node: \@@italiccorr
-                    { \xeCJK_make_node:n { CJK-space } } \use:n
-                  }
-                  { \@@italiccorr \use_none:n }
-              }
-              {
-                              \exp_after:wN \exp_after:wN \exp_after:wN
-                \exp_after:wN \exp_after:wN \exp_after:wN \exp_after:wN
-                \exp_after:wN \exp_after:wN \exp_after:wN \exp_after:wN
-                \exp_after:wN \exp_after:wN \exp_after:wN \exp_after:wN
-                \xeCJK_ignore_spaces:w
-              }
-          }
+        { \__xeCJK_node:n { default } }
+        {
+          \xeCJK_remove_node: \tex_italiccorrection:D
+          \xeCJK_make_node:n { default }
+        }
+        { \__xeCJK_node:n { CJK } }
+        {
+          \xeCJK_remove_node: \tex_italiccorrection:D
+          \xeCJK_make_node:n { CJK }
+          \__xeCJK_italic_correction_aux:
+        }
+        { \__xeCJK_node:n { CJK-space } }
+        {
+          \xeCJK_remove_node: \tex_italiccorrection:D
+          \xeCJK_make_node:n { CJK-space }
+          \__xeCJK_italic_correction_aux:
+        }
       }
-      { \@@italiccorr }
+      { \tex_italiccorrection:D }
   }
+\cs_new_protected_nopar:Npn \__xeCJK_italic_correction_aux:
+  {
+                  \exp_after:wN \exp_after:wN \exp_after:wN
+    \exp_after:wN \exp_after:wN \exp_after:wN \exp_after:wN
+    \exp_after:wN \exp_after:wN \exp_after:wN \exp_after:wN
+    \exp_after:wN \exp_after:wN \exp_after:wN \exp_after:wN
+    \xeCJK_ignore_spaces:w
+  }
 \cs_new_eq:NN \g__xeCJK_xetex_allocator_int \xe at alloc@intercharclass
 \__xeCJK_after_end_preamble:n
   {
@@ -4098,7 +4657,9 @@
       { \g__xeCJK_xetex_allocator_int }
       {
         \int_step_inline:nnnn
-          { \c__xeCJK_class_begin_int + \c_one } \c_one \g__xeCJK_xetex_allocator_int
+          { \c__xeCJK_class_begin_int + \c_one }
+          { \c_one }
+          { \g__xeCJK_xetex_allocator_int }
           {
             \seq_if_in:NnF \g__xeCJK_new_class_seq {#1}
               { \__xeCJK_set_others_toks:n {#1} }

Modified: trunk/Master/texmf-dist/tex/xelatex/xecjk/xeCJKfntef.sty
===================================================================
--- trunk/Master/texmf-dist/tex/xelatex/xecjk/xeCJKfntef.sty	2018-01-30 21:05:58 UTC (rev 46496)
+++ trunk/Master/texmf-dist/tex/xelatex/xecjk/xeCJKfntef.sty	2018-01-30 21:06:15 UTC (rev 46497)
@@ -7,8 +7,8 @@
 %% xeCJK.dtx  (with options: `fntef')
 %% 
 %%     Copyright (C) 2007--2010 by Wenchang Sun <sunwch at nankai.edu.cn>
-%%     Copyright (C) 2009--2017 by Leo Liu <leoliu.pku at gmail.com>
-%%     Copyright (C) 2012--2017 by Qing Lee <sobenlee at gmail.com>
+%%     Copyright (C) 2009--2018 by Leo Liu <leoliu.pku at gmail.com>
+%%     Copyright (C) 2012--2018 by Qing Lee <sobenlee at gmail.com>
 %% ----------------------------------------------------------------------
 %% 
 %%     This work may be distributed and/or modified under the
@@ -29,10 +29,10 @@
 %% 
 \NeedsTeXFormat{LaTeX2e}
 \RequirePackage{expl3}
-\GetIdInfo$Id: xeCJK.dtx 5a18688 2017-11-22 19:12:51 +0800 Qing Lee <sobenlee at gmail.com> $
+\GetIdInfo$Id: xeCJK.dtx 54327e6 2018-01-28 19:10:14 +0800 Qing Lee <sobenlee at gmail.com> $
   {xeCJK font effect}
 \ProvidesExplPackage{xeCJKfntef}
-  {\ExplFileDate}{3.5.1}{\ExplFileDescription}
+  {\ExplFileDate}{3.6.0}{\ExplFileDescription}
 \PassOptionsToPackage { normalem } { ulem }
 \DeclareOption* { \PassOptionsToPackage { \CurrentOption } { ulem } }
 \ProcessOptions \scan_stop:
@@ -70,7 +70,6 @@
         \cs_set_eq:NN \__xeCJK_ulem_hskip_aux:n \xeCJK_ulem_hskip:n
       }
       {
-        \xeCJK_swap_cs:NN \__xeCJK_punct_kern:n  \__xeCJK_ulem_punct_kern:n
         \xeCJK_swap_cs:NN \__xeCJK_punct_hskip:n \__xeCJK_ulem_punct_hskip:n
         \xeCJK_cs_clear:N \__xeCJK_ulem_skip_punct_begin:
         \xeCJK_cs_clear:N \__xeCJK_ulem_skip_punct_end:
@@ -96,6 +95,8 @@
       { \__xeCJK_ulem_glue:n \l__xeCJK_ecglue_skip }
     \cs_set_protected_nopar:Npn \xeCJK_space_glue:
       { \__xeCJK_ulem_glue:n \l__xeCJK_space_skip }
+    \cs_set_eq:NN \xeCJK_punct_node:N \use_none:n
+    \cs_set_eq:NN \xeCJK_if_last_punct:TF \use_ii:nn
     \keys_set:nn { xeCJK / options }
       { CheckFullRight = false , xCJKecglue = false }
   }
@@ -247,7 +248,7 @@
     \int_compare:nNnTF \etex_lastnodetype:D = \c_twelve
       { \__xeCJK_ulem_right_skip_kern: }
       { \__xeCJK_ulem_right_skip_glue: }
-    \box_use_clear:N \l__xeCJK_tmp_box
+    \box_use_drop:N \l__xeCJK_tmp_box
   }
 \cs_new_protected_nopar:Npn \__xeCJK_ulem_right_skip_kern:
   {
@@ -303,18 +304,20 @@
 \cs_new_protected_nopar:Npn \__xeCJK_ulem_skip_putbox:
   {
     \tl_if_empty:NF \UL at start
-      { \box_use_clear:N \UL at box }
+      { \box_use_drop:N \UL at box }
   }
 \cs_new_protected_nopar:Npn \__xeCJK_ulem_initial:
   {
     \__xeCJK_ulem_swap_cs:NN
-    \xeCJK_FullLeft_and_Default:  \__xeCJK_ulem_FullLeft_and_Default:
-    \xeCJK_FullLeft_and_CJK:      \__xeCJK_ulem_FullLeft_and_CJK:
-    \xeCJK_FullRight_and_Default: \__xeCJK_ulem_FullRight_and_Default:
-    \xeCJK_FullRight_and_CJK:     \__xeCJK_ulem_FullRight_and_CJK:
-    \xeCJK_CJK_and_CJK:N          \__xeCJK_ulem_CJK_and_CJK:N
-    \xeCJK_CJK_and_Boundary:w     \__xeCJK_ulem_CJK_and_Boundary:w
-    \xeCJK at fix@penalty            \__xeCJK_ulem_fix_penalty:
+    \xeCJK_FullLeft_and_Default:   \__xeCJK_ulem_FullLeft_and_Default:
+    \xeCJK_FullLeft_and_CJK:       \__xeCJK_ulem_FullLeft_and_CJK:
+    \xeCJK_FullLeft_and_Boundary:  \__xeCJK_ulem_FullLeft_and_Boundary:
+    \xeCJK_FullRight_and_Default:  \__xeCJK_ulem_FullRight_and_Default:
+    \xeCJK_FullRight_and_CJK:      \__xeCJK_ulem_FullRight_and_CJK:
+    \xeCJK_FullRight_and_Boundary: \__xeCJK_ulem_FullRight_and_Boundary:
+    \xeCJK_CJK_and_CJK:N           \__xeCJK_ulem_CJK_and_CJK:N
+    \xeCJK_CJK_and_Boundary:w      \__xeCJK_ulem_CJK_and_Boundary:w
+    \xeCJK at fix@penalty             \__xeCJK_ulem_fix_penalty:
     \__xeCJK_punct_breakable_kern:n       \__xeCJK_ulem_punct_breakable_kern:n
     \__xeCJK_Default_and_FullLeft_glue:N  \__xeCJK_ulem_Default_and_FullLeft_glue:N
     \__xeCJK_Default_and_FullRight_glue:N \__xeCJK_ulem_Default_and_FullRight_glue:N
@@ -392,6 +395,7 @@
         \xeCJK_class_group_end:
         \UL at stop \__xeCJK_ulem_ccglue: \UL at start
         \__xeCJK_ulem_class_group_begin:
+        \xeCJK_select_font:
         \CJKsymbol
       }
       { \__xeCJK_ulem_CJK_and_CJK:N }
@@ -400,7 +404,6 @@
   {
     \xeCJK_class_group_begin:
     \xeCJK_clear_Boundary_and_CJK_toks:
-    \xeCJK_select_font:
   }
 \cs_new_protected_nopar:Npn \__xeCJK_ulem_between_CJK_blocks:nnN #1#2
   {
@@ -425,8 +428,8 @@
       {
         \UL at stop
         \__xeCJK_ulem_skip_punct_begin:
-        \__xeCJK_punct_glue:NN \c__xeCJK_left_tl {#1}
-        \__xeCJK_punct_offset:NN \c__xeCJK_left_tl {#1}
+        \__xeCJK_punct_glue:NN \c__xeCJK_left_tl #1
+        \__xeCJK_punct_offset:NN \c__xeCJK_left_tl #1
         \UL at start
       }
       { \__xeCJK_ulem_Default_and_FullLeft_glue:N #1 }
@@ -437,7 +440,7 @@
       {
         \UL at stop
         \__xeCJK_ulem_skip_punct_begin:
-        \__xeCJK_punct_glue:NN \c__xeCJK_left_tl {#1}
+        \__xeCJK_punct_glue:NN \c__xeCJK_left_tl #1
         \UL at start
       }
       { \__xeCJK_ulem_Boundary_and_FullLeft_glue:N #1 }
@@ -449,11 +452,12 @@
         \xeCJK_class_group_end:
         \UL at stop
         \__xeCJK_ulem_skip_punct_begin:
-        \__xeCJK_ulem_ccglue:
-        \__xeCJK_punct_glue:NN \c__xeCJK_left_tl {#1}
-        \__xeCJK_punct_offset:NN \c__xeCJK_left_tl {#1}
+        \__xeCJK_ulem_punct_ccglue:
+        \__xeCJK_punct_glue:NN \c__xeCJK_left_tl #1
+        \__xeCJK_punct_offset:NN \c__xeCJK_left_tl #1
         \UL at start
         \__xeCJK_ulem_class_group_begin:
+        \xeCJK_select_punct_font:
       }
       { \__xeCJK_ulem_CJK_and_FullLeft_glue:N #1 }
   }
@@ -463,16 +467,13 @@
       {
         \UL at stop
         \__xeCJK_ulem_skip_punct_begin:
-        \__xeCJK_punct_if_long:NTF {#1}
-          { \__xeCJK_ulem_ccglue: }
+        \__xeCJK_punct_if_long:NTF #1
+          { \xeCJK_allow_break: }
+          { \xeCJK_no_break: }
+        \__xeCJK_punct_if_middle:NT #1
           {
-            \__xeCJK_punct_if_middle:NTF {#1}
-              {
-                \xeCJK_no_break:
-                \__xeCJK_punct_glue:NN \c__xeCJK_right_tl {#1}
-                \__xeCJK_punct_bound_rule:NN \c__xeCJK_left_tl {#1}
-              }
-              { \xeCJK_no_break: }
+            \__xeCJK_punct_glue:NN \c__xeCJK_right_tl #1
+            \__xeCJK_punct_bound_rule:NN \c__xeCJK_left_tl #1
           }
         \UL at start
       }
@@ -483,8 +484,20 @@
     \xeCJK_if_ulem_patch:TF
       {
         \xeCJK_class_group_end:
-        \__xeCJK_Default_and_FullRight_glue:N {#1}
+        \UL at stop
+        \__xeCJK_ulem_skip_punct_begin:
+        \__xeCJK_punct_if_long:NTF #1
+          { \xeCJK_allow_break: }
+          { \xeCJK_no_break: }
+        \__xeCJK_punct_if_middle:NT #1
+          {
+            \__xeCJK_ulem_punct_ccglue:
+            \__xeCJK_punct_glue:NN \c__xeCJK_right_tl #1
+            \__xeCJK_punct_bound_rule:NN \c__xeCJK_left_tl #1
+          }
+        \UL at start
         \__xeCJK_ulem_class_group_begin:
+        \xeCJK_select_punct_font:
       }
       { \__xeCJK_ulem_CJK_and_FullRight_glue:N #1 }
   }
@@ -494,7 +507,7 @@
       {
         \__xeCJK_punct_if_middle:NTF \g__xeCJK_last_punct_tl
           {
-            \xeCJK_get_punct_bounds:NN \c__xeCJK_left_tl \g__xeCJK_last_punct_tl
+            \xeCJK_get_punct_bounds:No \c__xeCJK_left_tl \g__xeCJK_last_punct_tl
             \__xeCJK_punct_bound_rule:NN \c__xeCJK_right_tl \g__xeCJK_last_punct_tl
             \xeCJK_class_group_end: \UL at stop \xeCJK_no_break:
             \__xeCJK_punct_glue:NN \c__xeCJK_left_tl  \g__xeCJK_last_punct_tl
@@ -506,6 +519,25 @@
       }
       { \__xeCJK_ulem_FullLeft_and_Default: }
   }
+\cs_new_protected_nopar:Npn \__xeCJK_ulem_FullLeft_and_Boundary:
+  {
+    \xeCJK_if_ulem_patch:TF
+      {
+        \__xeCJK_punct_if_middle:NTF \g__xeCJK_last_punct_tl
+          {
+            \xeCJK_get_punct_bounds:No \c__xeCJK_left_tl \g__xeCJK_last_punct_tl
+            \__xeCJK_punct_bound_rule:NN \c__xeCJK_right_tl \g__xeCJK_last_punct_tl
+            \xeCJK_class_group_end: \UL at stop \xeCJK_no_break:
+            \__xeCJK_punct_glue:NN \c__xeCJK_left_tl  \g__xeCJK_last_punct_tl
+          }
+          { \xeCJK_class_group_end: \UL at stop }
+        \__xeCJK_ulem_skip_punct_end:
+        \xeCJK_no_break:
+        \UL at start
+        \tex_ignorespaces:D
+      }
+      { \__xeCJK_ulem_FullLeft_and_Boundary: }
+  }
 \cs_new_protected_nopar:Npn \__xeCJK_ulem_FullLeft_and_CJK:
   {
     \xeCJK_if_ulem_patch:TF
@@ -512,6 +544,7 @@
       {
         \xeCJK_FullLeft_and_Default:
         \__xeCJK_ulem_class_group_begin:
+        \xeCJK_select_font:
       }
       { \__xeCJK_ulem_FullLeft_and_CJK: }
   }
@@ -529,6 +562,21 @@
       }
       { \__xeCJK_ulem_FullRight_and_Default: }
   }
+\cs_new_protected_nopar:Npn \__xeCJK_ulem_FullRight_and_Boundary:
+  {
+    \xeCJK_if_ulem_patch:TF
+      {
+        \__xeCJK_punct_rule:NN \c__xeCJK_right_tl \g__xeCJK_last_punct_tl
+        \xeCJK_class_group_end:
+        \UL at stop
+        \__xeCJK_punct_offset:NN \c__xeCJK_right_tl \g__xeCJK_last_punct_tl
+        \__xeCJK_punct_glue:NN \c__xeCJK_right_tl \g__xeCJK_last_punct_tl
+        \__xeCJK_ulem_skip_punct_end:
+        \UL at start
+        \tex_ignorespaces:D
+      }
+      { \__xeCJK_ulem_FullRight_and_Boundary: }
+  }
 \cs_new_protected_nopar:Npn \__xeCJK_ulem_FullRight_and_CJK:
   {
     \xeCJK_if_ulem_patch:TF
@@ -538,10 +586,11 @@
         \UL at stop
         \__xeCJK_punct_offset:NN \c__xeCJK_right_tl \g__xeCJK_last_punct_tl
         \__xeCJK_punct_glue:NN \c__xeCJK_right_tl \g__xeCJK_last_punct_tl
-        \__xeCJK_ulem_ccglue:
+        \__xeCJK_ulem_punct_ccglue:
         \__xeCJK_ulem_skip_punct_end:
         \UL at start
         \__xeCJK_ulem_class_group_begin:
+        \xeCJK_select_font:
       }
       { \__xeCJK_ulem_FullRight_and_CJK: }
   }
@@ -551,15 +600,6 @@
       { \xeCJK_ulem_hskip:n }
       { \__xeCJK_ulem_punct_hskip:n }
   }
-\cs_new_protected_nopar:Npn \__xeCJK_ulem_punct_kern:n #1
-  {
-    \xeCJK_if_ulem_patch:TF
-      {
-        \dim_compare:nNnF {#1} = \c_zero_dim
-          { \xeCJK_ulem_hskip:n {#1} }
-      }
-      { \__xeCJK_ulem_punct_kern:n {#1} }
-  }
 \cs_new_protected_nopar:Npn \__xeCJK_ulem_punct_breakable_kern:n #1
   {
     \xeCJK_if_ulem_patch:TF
@@ -567,6 +607,7 @@
         \xeCJK_class_group_end:
         \UL at stop \xeCJK_ulem_hskip:n {#1} \UL at start
         \__xeCJK_ulem_class_group_begin:
+        \xeCJK_select_punct_font:
       }
       { \__xeCJK_ulem_punct_breakable_kern:n {#1} }
   }
@@ -594,9 +635,11 @@
   }
 \cs_new_protected_nopar:Npn \__xeCJK_ulem_ccglue:
   { { \skip_set_eq:NN \UL at skip \l__xeCJK_ccglue_skip \UL at leaders } }
+\cs_new_protected_nopar:Npn \__xeCJK_ulem_punct_ccglue:
+  { \__xeCJK_punct_hskip:n { \l__xeCJK_ccglue_skip } }
 \cs_new_protected_nopar:Npn \xeCJK_ulem_group_begin:
   {
-    \xeCJK_leave_vmode:
+    \mode_leave_vertical:
     \c_group_begin_token
   }
 \cs_new_protected_nopar:Npn \xeCJK_ulem_group_end:
@@ -608,7 +651,7 @@
   { \__xeCJK_ulem_on:n { \xeCJK_ulem_left: #1 \xeCJK_ulem_right: } }
 \NewDocumentCommand \xeCJKfntefon { s t- s o }
   {
-    \xeCJK_leave_vmode:
+    \mode_leave_vertical:
     \xeCJK_ulem_boot:NNNn #1#2#3 {#4}
     \xeCJK_ulem_on:n
   }
@@ -725,9 +768,9 @@
   {
     \bool_lazy_or:nnT {#3} {#5}
       { \bool_set_false:c { l__xeCJK_#2_skip_bool } }
-    \IfBooleanT #4
+    \bool_if:NT #4
       { \bool_set_true:c { l__xeCJK_#2_subtract_bool } }
-    \IfNoValueF {#6}
+    \tl_if_novalue:nF {#6}
       { \keys_set:nn { xeCJK / options / #1 } {#6} }
     \bool_set_eq:Nc \l__xeCJK_ulem_skip_bool { l__xeCJK_#2_skip_bool }
     \bool_set_eq:Nc \l__xeCJK_ulem_hidden_bool { l__xeCJK_#2_hidden_bool }
@@ -738,9 +781,9 @@
   {
     \bool_lazy_or:nnT {#1} {#3}
       { \bool_set_false:N \l__xeCJK_ulem_skip_bool }
-    \IfBooleanT #2
+    \bool_if:NT #2
       { \bool_set_true:N \l__xeCJK_ulem_subtract_bool }
-    \IfNoValueF {#4}
+    \tl_if_novalue:nF {#4}
       { \keys_set:nn { xeCJK / options / ulem } {#4} }
   }
 \cs_new_protected_nopar:Npn \xeCJK_fntef_initial:n
@@ -793,12 +836,6 @@
         \color_group_end:
       }
   }
-\cs_new_protected_nopar:Npn \xeCJK_leave_vmode:
-  {
-    \if_mode_vertical:
-      \exp_after:wN \tex_indent:D
-    \fi:
-  }
 \keys_define:nn { xeCJK / options }
   {
     underdot / symbol          .tl_set:N = \l__xeCJK_udot_symbol_tl ,
@@ -930,7 +967,7 @@
   }
 \cs_new_protected:Npn \__xeCJK_under_symbol_auxii:nnnnnn #1#2#3#4#5#6
   {
-    \xeCJK_leave_vmode:
+    \mode_leave_vertical:
     \group_begin:
       \xeCJK_under_symbol_initial:nnnnn {#1} {#2} {#3} {#4} {#5}
       \__xeCJK_under_symbol_text_format:c { l__xeCJK_#2_text_format_tl }
@@ -941,7 +978,7 @@
   }
 \cs_new_protected:Npn \xeCJK_under_symbol_initial:nnnnn #1#2#3#4#5
   {
-    \IfNoValueF {#3}
+    \tl_if_novalue:nF {#3}
       { \keys_set:nn { xeCJK / options / #1 } {#3} }
     \xeCJK_fntef_sbox:n {#5}
     \bool_if:NTF \l__xeCJK_fntef_bool
@@ -1016,7 +1053,7 @@
   }
 \NewEnviron { CJKfilltwosides* } [ 2 ] [ c ]
   {
-    \xeCJK_leave_vmode:
+    \mode_leave_vertical:
     \cs_set_eq:NN \CJKglue \xeCJK_fntef_hfilll:
     \tl_set:Nn \arraystretch { 1 }
     \cs_if_free:NF \extrarowheight
@@ -1050,8 +1087,7 @@
   }
 \cs_new_protected_nopar:Npn \xeCJK_fntef_hfilll:
   { \skip_horizontal:N \c__xeCJK_filll_skip }
-\skip_new:N \c__xeCJK_filll_skip
-\skip_set:Nn  \c__xeCJK_filll_skip { \c_zero_dim plus 1 filll }
+\skip_const:Nn \c__xeCJK_filll_skip { \c_zero_dim plus 1 filll }
 %% 
 %%     This package consists of the files xeCJK.dtx,
 %%                                        full-stop.map,

Modified: trunk/Master/texmf-dist/tex/xelatex/xecjk/xunicode-addon.sty
===================================================================
--- trunk/Master/texmf-dist/tex/xelatex/xecjk/xunicode-addon.sty	2018-01-30 21:05:58 UTC (rev 46496)
+++ trunk/Master/texmf-dist/tex/xelatex/xecjk/xunicode-addon.sty	2018-01-30 21:06:15 UTC (rev 46497)
@@ -7,8 +7,8 @@
 %% xeCJK.dtx  (with options: `xunicode')
 %% 
 %%     Copyright (C) 2007--2010 by Wenchang Sun <sunwch at nankai.edu.cn>
-%%     Copyright (C) 2009--2017 by Leo Liu <leoliu.pku at gmail.com>
-%%     Copyright (C) 2012--2017 by Qing Lee <sobenlee at gmail.com>
+%%     Copyright (C) 2009--2018 by Leo Liu <leoliu.pku at gmail.com>
+%%     Copyright (C) 2012--2018 by Qing Lee <sobenlee at gmail.com>
 %% ----------------------------------------------------------------------
 %% 
 %%     This work may be distributed and/or modified under the
@@ -29,10 +29,10 @@
 %% 
 \NeedsTeXFormat{LaTeX2e}
 \RequirePackage{expl3}
-\GetIdInfo$Id: xeCJK.dtx 5a18688 2017-11-22 19:12:51 +0800 Qing Lee <sobenlee at gmail.com> $
+\GetIdInfo$Id: xeCJK.dtx 54327e6 2018-01-28 19:10:14 +0800 Qing Lee <sobenlee at gmail.com> $
   {addon file for xunicode}
 \ProvidesExplPackage{xunicode-addon}
-  {\ExplFileDate}{3.5.1}{\ExplFileDescription}
+  {\ExplFileDate}{3.6.0}{\ExplFileDescription}
 \bool_lazy_or:nnF
   { \sys_if_engine_xetex_p: }
   { \sys_if_engine_luatex_p: }

Modified: trunk/Master/texmf-dist/tex/xelatex/xecjk/xunicode-extra.def
===================================================================
--- trunk/Master/texmf-dist/tex/xelatex/xecjk/xunicode-extra.def	2018-01-30 21:05:58 UTC (rev 46496)
+++ trunk/Master/texmf-dist/tex/xelatex/xecjk/xunicode-extra.def	2018-01-30 21:06:15 UTC (rev 46497)
@@ -7,8 +7,8 @@
 %% xeCJK.dtx  (with options: `xunextra')
 %% 
 %%     Copyright (C) 2007--2010 by Wenchang Sun <sunwch at nankai.edu.cn>
-%%     Copyright (C) 2009--2017 by Leo Liu <leoliu.pku at gmail.com>
-%%     Copyright (C) 2012--2017 by Qing Lee <sobenlee at gmail.com>
+%%     Copyright (C) 2009--2018 by Leo Liu <leoliu.pku at gmail.com>
+%%     Copyright (C) 2012--2018 by Qing Lee <sobenlee at gmail.com>
 %% ----------------------------------------------------------------------
 %% 
 %%     This work may be distributed and/or modified under the
@@ -27,10 +27,10 @@
 %% 
 %% ----------------------------------------------------------------------
 %% 
-\GetIdInfo$Id: xeCJK.dtx 5a18688 2017-11-22 19:12:51 +0800 Qing Lee <sobenlee at gmail.com> $
+\GetIdInfo$Id: xeCJK.dtx 54327e6 2018-01-28 19:10:14 +0800 Qing Lee <sobenlee at gmail.com> $
   {extra definition for xunicode}
 \ProvidesExplFile{xunicode-extra.def}
-  {\ExplFileDate}{3.5.1}{\ExplFileDescription}
+  {\ExplFileDate}{3.6.0}{\ExplFileDescription}
 \DeclareUTFComposite\textsuperscript
 \DeclareUTFComposite\textsubscript
 \DeclareUTFEncodedAccent\textsbleftarrow{"20EE}{"20FF}



More information about the tex-live-commits mailing list