%%
%% This is file `xeCJK.sty',
%% generated with the docstrip utility.
%%
%% The original source files were:
%%
%% xeCJK.dtx  (with options: `package')
%% 
%%     Copyright (C) 2007--2010 by Wenchang Sun <sunwch@nankai.edu.cn>
%%     Copyright (C) 2009--2022 by Leo Liu <leoliu.pku@gmail.com>
%%     Copyright (C) 2012--2022 by Qing Lee <sobenlee@gmail.com>
%% ----------------------------------------------------------------------
%% 
%%     This work may be distributed and/or modified under the
%%     conditions of the LaTeX Project Public License, either
%%     version 1.3c of this license or (at your option) any later
%%     version. This version of this license is in
%%        http://www.latex-project.org/lppl/lppl-1-3c.txt
%%     and the latest version of this license is in
%%        http://www.latex-project.org/lppl.txt
%%     and version 1.3 or later is part of all distributions of
%%     LaTeX version 2005/12/01 or later.
%% 
%%     This work has the LPPL maintenance status "maintained".
%% 
%%     The Current Maintainers of this work are Leo Liu and Qing Lee.
%% 
%% ----------------------------------------------------------------------
%% 
\NeedsTeXFormat{LaTeX2e}
\RequirePackage{expl3}
\GetIdInfo$Id: xeCJK.dtx c4ccfae 2022-08-05 21:02:32 +0800 Qing Lee <sobenlee@gmail.com> $
  {Typesetting CJK scripts with XeLaTeX}
\ProvidesExplPackage{\ExplFileName}
  {\ExplFileDate}{3.9.1}{\ExplFileDescription}
\msg_new:nnn { xeCJK } { Require-XeTeX }
  {
    The~xeCJK~package~requires~XeTeX~to~function.\\\\
    You~must~change~your~typesetting~engine~to~"xelatex" \\
    instead~of~plain~"latex"~or~"pdflatex"~or~"lualatex".\\
    Loading~xeCJK~will~abort!
  }
\sys_if_engine_xetex:F { \msg_critical:nn { xeCJK } { Require-XeTeX } }
\msg_new:nnn { xeCJK } { l3-too-old }
  {
    Support~package~`#1'~too~old. \\\\
    Please~update~an~up~to~date~version~of~the~bundles\\\\
    `l3kernel'~and~`l3packages'\\\\
    using~your~TeX~package~manager~or~from~CTAN.\\
    \str_if_eq:nnT {#1} { expl3 } { Loading~xeCJK~will~abort! }
  }
\@ifpackagelater { expl3 } { 2020/02/08 } { }
  { \msg_critical:nnn { xeCJK } { l3-too-old } { expl3 } }
\RequirePackage { ctexhook }
\prg_new_conditional:Npnn \xeCJK_if_package_loaded:n #1 { p , T , F , TF }
  {
    \tl_if_exist:cTF { ver@ #1 . \c__xeCJK_package_ext_tl }
      { \prg_return_true: } { \prg_return_false: }
  }
\tl_const:Nx \c__xeCJK_package_ext_tl { \@pkgextension }
\msg_new:nnn { xeCJK } { after-package }
  {
    The~`#1'~package~and~xeCJK~are~incompatible.\\\\
    Please~load~it~after~xeCJK.
  }
\clist_map_inline:nn { CJKnumb }
  {
    \xeCJK_if_package_loaded:nT {#1}
      { \msg_error:nnn { xeCJK } { after-package } {#1} }
  }
\clist_map_inline:nn
  { CJKulem , CJKvert , CJKpunct , CJKutf8 , CJK }
  { \ctex_disable_package:n {#1} }
\ctex_if_format_at_least:nTF { 2020/10/01 }
  { \ctex_replace_package:nn { CJKfntef } { xeCJKfntef } }
  { \ctex_disable_package:n { CJKfntef } }
\cs_if_exist:NF \NewDocumentCommand
  { \RequirePackage { xparse } }
\RequirePackage { xtemplate }
\tl_new:N \l__xeCJK_tmp_tl
\int_new:N \l__xeCJK_tmp_int
\box_new:N \l__xeCJK_tmp_box
\dim_new:N \l__xeCJK_tmp_dim
\bool_new:N \l__xeCJK_tmp_bool
\skip_new:N \l__xeCJK_tmp_skip
\clist_new:N \l__xeCJK_tmp_clist
\cs_new_protected:Npn \__xeCJK_msg_new:nn   { \msg_new:nnn       { xeCJK } }
\cs_new_protected:Npn \__xeCJK_msg_new:nnn  { \msg_new:nnnn      { xeCJK } }
\cs_new_protected:Npn \__xeCJK_error:n      { \msg_error:nn      { xeCJK } }
\cs_new_protected:Npn \__xeCJK_error:nx     { \msg_error:nnx     { xeCJK } }
\cs_new_protected:Npn \__xeCJK_warning:n    { \msg_warning:nn    { xeCJK } }
\cs_new_protected:Npn \__xeCJK_warning:nx   { \msg_warning:nnx   { xeCJK } }
\cs_new_protected:Npn \__xeCJK_warning:nxx  { \msg_warning:nnxx  { xeCJK } }
\cs_new_protected:Npn \__xeCJK_warning:nxxx { \msg_warning:nnxxx { xeCJK } }
\cs_new_protected:Npn \__xeCJK_info:nxx     { \msg_info:nnxx     { xeCJK } }
\cs_new_protected:Npn \xeCJK_allow_break:
  { \tex_penalty:D \c_zero_int }
\cs_new_protected:Npn \xeCJK_no_break:
  { \tex_penalty:D \c__xeCJK_nobreak_penalty_int }
\int_const:Nn \c__xeCJK_nobreak_penalty_int { 10 000 }
\AtBeginDocument           { \xeCJK@document@hook }
\ctex_at_end_preamble:n    { \xeCJK@document@left@hook }
\ctex_after_end_preamble:n { \xeCJK@document@right@hook }
\cs_new_protected:Npn \xeCJK@document@hook
  { \tl_use:N \g__xeCJK_after_preamble_hook_tl }
\cs_new_protected:Npn \xeCJK@document@left@hook
  { \tl_use:N \g__xeCJK_at_end_preamble_hook_tl }
\cs_new_protected:Npn \xeCJK@document@right@hook
  { \tl_use:N \g__xeCJK_after_end_preamble_hook_tl }
\cs_new_protected:Npn \__xeCJK_at_end_preamble:n
  { \tl_gput_right:Nn \g__xeCJK_at_end_preamble_hook_tl }
\cs_new_protected:Npn \__xeCJK_after_preamble:n
  { \tl_gput_right:Nn \g__xeCJK_after_preamble_hook_tl }
\cs_new_protected:Npn \__xeCJK_after_end_preamble:n
  { \tl_gput_right:Nn \g__xeCJK_after_end_preamble_hook_tl }
\cs_new_protected:Npn \__xeCJK_package_hook:nn
  { \ctex_at_end_package:nn }
\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
\__xeCJK_after_preamble:n
  { \tl_put_right:Nn \@begindvi { \xeCJK@first@begindvi } }
\cs_new_protected:Npn \xeCJK@first@begindvi
  {
    \xeCJKShipoutHook
    \cs_if_exist:NTF \@begindvi
      { \tl_gput_right:Nn }
      { \tl_const:Nn }
    \@begindvi { \xeCJKShipoutHook }
  }
\NewDocumentCommand \xeCJKShipoutHook { }
  {
    \bool_if:NF \l__xeCJK_shipout_hook_bool
      {
        \bool_set_true:N \l__xeCJK_shipout_hook_bool
        \tl_use:N \l__xeCJK_shipout_hook_tl
      }
  }
\cs_new_protected:Npn \xeCJK_add_to_shipout:n
  { \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:Npn \xeCJK_tl_remove_outer_braces:n #1
  {
    \exp_last_unbraced:Ne
    \__xeCJK_tl_remove_outer_braces:w { \tl_trim_spaces:n {#1} } \s_stop
  }
\cs_new:Npn \__xeCJK_tl_remove_outer_braces:w #1 \s_stop
  {
    \tl_if_single:nTF {#1}
      {
        \tl_if_head_is_N_type:nTF {#1}
          { \tl_trim_spaces:n }
          { \xeCJK_tl_remove_outer_braces:n }
      }
      { \tl_trim_spaces:n }
      {#1}
  }
\cs_new_protected:Npn \xeCJK_cs_clear:N #1
  { \cs_set_eq:NN #1 \prg_do_nothing: }
\cs_new_protected:Npn \xeCJK_cs_gclear:N #1
  { \cs_gset_eq:NN #1 \prg_do_nothing: }
\cs_new_protected:Npn \xeCJK_swap_cs:NN #1#2
  {
    \cs_set_eq:NN \__xeCJK_swap_cs_aux:w #1
    \cs_set_eq:NN #1 #2
    \cs_set_eq:NN #2 \__xeCJK_swap_cs_aux:w
    \cs_undefine:N \__xeCJK_swap_cs_aux:w
  }
\cs_new_protected:Npn \xeCJK_font_gset_to_current:N
  { \exp_after:wN \__xeCJK_font_gset_to_current_aux:NN \tex_the:D \tex_font:D }
\cs_new_protected:Npn \__xeCJK_font_gset_to_current_aux:NN #1#2
  { \cs_if_eq:NNF #1 \tex_nullfont:D { \cs_gset_eq:NN #2#1 } }
\prg_new_conditional:Npnn \xeCJK_glyph_if_exist:N #1 { p , T , F , TF }
  {
    \tex_iffontchar:D \tex_font:D `#1 \exp_stop_f:
      \prg_return_true: \else: \prg_return_false: \fi:
  }
\tl_const:Nn \c_xeCJK_space_skip_tl
  {
    \int_compare:nNnTF \g__xeCJK_space_factor_int = { 1000 }
      {
        \skip_if_eq:nnTF \tex_spaceskip:D \c_zero_skip
          {
                    \tex_fontdimen:D 2 ~ \tex_font:D
              plus  \tex_fontdimen:D 3 ~ \tex_font:D
              minus \tex_fontdimen:D 4 ~ \tex_font:D
          }
          { \tex_spaceskip:D }
      }
      {
        \skip_if_eq:nnTF \tex_spaceskip:D \c_zero_skip
          {
            \int_compare:nNnTF \g__xeCJK_space_factor_int < { 2000 }
              {
                \__xeCJK_space_skip_scale:nnn
                  { \tex_fontdimen:D 2 ~ \tex_font:D }
              }
              {
                \skip_if_eq:nnTF \tex_xspaceskip:D \c_zero_skip
                  {
                    \__xeCJK_space_skip_scale:nnn
                      {
                        \tex_fontdimen:D 2 ~ \tex_font:D +
                        \tex_fontdimen:D 7 ~ \tex_font:D
                      }
                  }
                  { \tex_xspaceskip:D  \use_none:nn }
              }
              { \tex_fontdimen:D 3 ~ \tex_font:D }
              { \tex_fontdimen:D 4 ~ \tex_font:D }
          }
          {
            \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 7 ~ \tex_font:D
                      }
                  }
                  { \tex_xspaceskip:D \use_none:nn }
              }
              { \tex_gluestretch:D \tex_spaceskip:D }
              { \tex_glueshrink:D  \tex_spaceskip:D }
          }
      }
  }
\cs_new:Npn \__xeCJK_space_skip_scale:nnn #1#2#3
  {
    \dim_eval:n {#1}
    plus \fp_eval:n { \g__xeCJK_space_factor_int / 1000 } #2
    minus
      \int_div_truncate:nn
        { 1000 * \int_value:w #3 } { \g__xeCJK_space_factor_int } sp
  }
\int_new:N \g__xeCJK_space_factor_int
\cs_new_protected:Npn \xeCJK_reset_space_factor:
  { \int_gset:Nn \g__xeCJK_space_factor_int { 1000 } }
\xeCJK_reset_space_factor:
\cs_new_protected:Npn \xeCJK_glue_to_skip:nN #1#2
  {
    \group_begin:
      \hbox_set:Nw \l__xeCJK_tmp_box #1 \scan_stop:
      \__xeCJK_if_last_glue:TF
        {
          \exp_args:NNNo \hbox_set_end:
          \skip_set:Nn #2 { \skip_use:N \tex_lastskip:D }
        }
        {
          \exp_args:NNNo \hbox_set_end:
          \skip_set:Nn #2 { \dim_use:N \box_wd:N \l__xeCJK_tmp_box }
        }
    \exp_args:NNNo \group_end:
    \skip_set:Nn #2 { \skip_use:N #2 }
  }
\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 }
  }
\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
\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__xeCJK_peek_search_token #1 \scan_stop:
    \cs_set_protected:Npx \__xeCJK_peek_catcode_true:w
      { \exp_not:N \group_align_safe_end: \exp_not:n {#2} }
    \cs_set_protected:Npx \__xeCJK_peek_catcode_false:w
      { \exp_not:N \group_align_safe_end: \exp_not:n {#3} }
    \bool_set_false:N \l__xeCJK_peek_ignore_spaces_bool
    \group_align_safe_begin:
    \peek_after:Nw \__xeCJK_peek_catcode_ignore_spaces_branches:w
  }
\cs_new_protected:Npn \__xeCJK_peek_catcode_ignore_spaces_branches:w
  {
    \if_meaning:w \l_peek_token \c_space_token
      \bool_set_true:N \l__xeCJK_peek_ignore_spaces_bool
      \exp_after:wN \peek_after:Nw
      \exp_after:wN \__xeCJK_peek_catcode_ignore_spaces_branches:w
      \tex_romannumeral:D 0
    \else:
      \if_catcode:w
        \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:
        \exp_after:wN \exp_after:wN
        \exp_after:wN \__xeCJK_peek_catcode_false:w
      \fi:
    \fi:
  }
\cs_new_eq:NN \l__xeCJK_peek_search_token ?
\cs_new_eq:NN \__xeCJK_peek_catcode_true:w  \prg_do_nothing:
\cs_new_eq:NN \__xeCJK_peek_catcode_false:w \prg_do_nothing:
\bool_new:N \l__xeCJK_peek_ignore_spaces_bool
\cs_new:Npn \xeCJK_token_value_class:N #1
  { \tex_XeTeXcharclass:D \xeCJK_token_value_charcode:N #1 }
\cs_new:Npn \xeCJK_token_value_charcode:N #1
  { \exp_after:wN \__xeCJK_get_charcode:w \token_to_meaning:N #1 \q_stop }
\group_begin:
  \cs_set:Npn \__xeCJK_tmp:w #1 ~ #2 ~ #3#4#5 \q_stop
    {
      \tl_if_empty:nTF { #4#5 }
        {
          \cs_new:Npn \__xeCJK_get_charcode:w ##1 ~ ##2 ~ ##3 \q_stop
            { \int_eval:n { `##3 } }
        }
        {
          \tl_if_empty:nTF {#5}
            {
              \cs_new:Npn \__xeCJK_get_charcode:w ##1 ~ ##2 ~ ##3##4 \q_stop
                {
                  \int_eval:n
                    {
                      \tl_if_empty:nTF { ##4 }
                        { `##3 }
                        { ( `##3 - "D800 ) * "400 + ( `##4 - "DC00 ) + "10000 }
                    }
                }
            }
            {
              \cs_new:Npn \__xeCJK_get_charcode:w ##1 ~ ##2 ~ ##3##4 \q_stop
                { \int_eval:n { \tl_if_empty:nTF { ##4 } { `##3 } { "20000 } } }
            }
        }
    }
  \exp_after:wN \__xeCJK_tmp:w \token_to_meaning:N ^^^^^20000 { } \q_stop
\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:
      \prg_return_true: \else: \prg_return_false: \fi:
  }
\cs_new:Npn \__xeCJK_CJK_class_tl:n #1
  { c__xeCJK_CJK_class_ \int_eval:n {#1} _tl }
\prg_new_conditional:Npnn \xeCJK_if_same_class:NN #1#2 { p , T , F , TF }
  {
    \if_int_compare:w \xeCJK_token_value_class:N #1 =
                      \xeCJK_token_value_class:N #2 \exp_stop_f:
      \prg_return_true: \else: \prg_return_false: \fi:
  }
\cs_new_protected:Npn \xeCJK_make_boundary:
  { \bool_if:NT \l__xeCJK_CJK_group_bool { \scan_stop: } }
\keys_define:nn { xeCJK / options }
  {
    xeCJKactive .choice: ,
    xeCJKactive / true  .code:n = { \makexeCJKactive   } ,
    xeCJKactive / false .code:n = { \makexeCJKinactive } ,
    xeCJKactive      .default:n = { true }
  }
\NewDocumentCommand \makexeCJKactive   { }
  { \tex_XeTeXinterchartokenstate:D = \c_one_int }
\NewDocumentCommand \makexeCJKinactive { }
  { \tex_XeTeXinterchartokenstate:D = \c_zero_int }
\char_set_catcode_ignore:n { "FEFF }
\seq_new:N \g__xeCJK_class_seq
\seq_new:N \g__xeCJK_new_class_seq
\cs_new_protected:Npn \xeCJK_new_class:n #1
  {
    \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} }
        \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} }
      }
  }
\cs_new_protected:Npn \xeCJK_save_class:nn #1#2
  {
    \int_if_exist:cTF { \__xeCJK_class_csname:n {#1} }
      { \__xeCJK_error:nx { class-already-defined } {#1} }
      {
        \int_const:cn { \__xeCJK_class_csname:n {#1} } {#2}
        \clist_new:c { g__xeCJK_#1_range_clist }
        \seq_gput_right:Nn \g__xeCJK_class_seq {#1}
      }
  }
\cs_new:Npn \__xeCJK_class_csname:n #1 { c__xeCJK_#1_class_int }
\cs_new_eq:cN { \__xeCJK_class_csname:n { Others } } \l__xeCJK_tmp_int
\__xeCJK_msg_new:nn { class-already-defined }
  {
    XeTeX~character~class~`#1'~has~been~already~defined.\\\\
    Please~take~another~name. \\
  }
\xeCJK_save_class:nn { Default } { 0 }
\str_const:Nx \c__xeCJK_xetex_version_str
  { \int_use:N \tex_XeTeXversion:D \tex_XeTeXrevision:D }
\fp_compare:nNnTF { \c__xeCJK_xetex_version_str } > { 0.99993 }
  { \xeCJK_save_class:nn { Boundary } { 4095 } }
  { \xeCJK_save_class:nn { Boundary } { 255 } }
\int_compare:nNnTF { \tex_XeTeXcharclass:D "4E00 } = \c_one_int
  {
    \xeCJK_save_class:nn { CJK }       { 1 }
    \xeCJK_save_class:nn { FullLeft }  { 2 }
    \xeCJK_save_class:nn { FullRight } { 3 }
    \int_const:Nn \c__xeCJK_class_begin_int { 3 }
  }
  {
    \xeCJK_new_class:n { CJK }
    \xeCJK_new_class:n { FullLeft }
    \xeCJK_new_class:n { FullRight }
    \int_const:Nn \c__xeCJK_class_begin_int { 0 }
  }
\xeCJK_new_class:n { HalfLeft }
\xeCJK_new_class:n { HalfRight }
\xeCJK_new_class:n { NormalSpace }
\xeCJK_new_class:n { CM }
\xeCJK_new_class:n { HangulJamo }
\clist_const:Nn \c__xeCJK_HalfLeft_chars_clist
  { "28  , "5B , "60 , "7B , "2329 }
\clist_const:Nn \c__xeCJK_HalfRight_chars_clist
  { "21 , "22 , "25 , "27 , "29 , "2C , "2E , "3A , "3B , "3F , "5D , "7D , "232A }
\clist_const:Nn \c__xeCJK_NormalSpace_chars_clist { "2D , "2F , "5C }
\clist_const:Nn \c__xeCJK_OP_chars_clist
  {
    "2018 , "201C ,
    "3008 , "300A , "300C , "300E , "3010 , "3014 , "3016 , "3018 , "301A , "301D ,
    "FE17 , "FE35 , "FE37 , "FE39 , "FE3B , "FE3D , "FE3F , "FE41 , "FE43 , "FE47 ,
    "FE59 , "FE5B , "FE5D , "FF08 , "FF3B , "FF5B , "FF5F , "FF62
  }
\clist_const:Nn \c__xeCJK_PR_chars_clist
  { "FE69 , "FF04 , "FFE1 , "FFE5 , "FFE6 }
\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 , "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 , "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} { 12 } }
    \__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 }
\clist_const:Nn \c__xeCJK_CJ_chars_clist
  {
    "3041 , "3043 , "3045 , "3047 , "3049 , "3063 , "3083 , "3085 , "3087 , "308E ,
    "3095 , "3096 , "30A1 , "30A3 , "30A5 , "30A7 , "30A9 , "30C3 , "30E3 , "30E5 ,
    "30E7 , "30EE , "30F5 , "30F6 , "30FC , "31F0 , "31F1 , "31F2 , "31F3 , "31F4 ,
    "31F5 , "31F6 , "31F7 , "31F8 , "31F9 , "31FA , "31FB , "31FC , "31FD , "31FE ,
    "31FF , "FF67 , "FF68 , "FF69 , "FF6A , "FF6B , "FF6C , "FF6D , "FF6E , "FF6F ,
    "FF70
  }
\clist_const:Nn \c__xeCJK_PO_chars_clist { "FE6A , "FF05 , "FFE0 }
\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_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
  {
    "02EA -> "02EB ,
    "2E80 -> "2EFF ,
    "2F00 -> "2FDF ,
    "2FF0 -> "2FFF ,
    "3000 -> "303F ,
    "3040 -> "309F ,
    "30A0 -> "30FF ,
    "3100 -> "312F ,
    "3130 -> "318F ,
    "3190 -> "319F ,
    "31A0 -> "31BF ,
    "31C0 -> "31EF ,
    "31F0 -> "31FF ,
    "3200 -> "32FF ,
    "3300 -> "33FF ,
    "3400 -> "4DBF ,
    "4DC0 -> "4DFF ,
    "4E00 -> "9FFF ,
    "A000 -> "A48F ,
    "A490 -> "A4CF ,
    "AC00 -> "D7AF ,
    "F900 -> "FAFF ,
    "FE10 -> "FE1F ,
    "FE30 -> "FE4F ,
    "FF00 -> "FFEF ,
    "16FE0 -> "16FFF ,
    "17000 -> "187FF ,
    "18800 -> "18AFF ,
    "18B00 -> "18CFF ,
    "18D00 -> "18D7F ,
    "1AFF0 -> "1AFFF ,
    "1B000 -> "1B0FF ,
    "1B100 -> "1B12F ,
    "1B130 -> "1B16F ,
    "1B170 -> "1B2FF ,
    "1F200 -> "1F2FF ,
    "20000 -> "2A6DF ,
    "2A700 -> "2B73F ,
    "2B740 -> "2B81F ,
    "2B820 -> "2CEAF ,
    "2CEB0 -> "2EBEF ,
    "2F800 -> "2FA1F ,
    "30000 -> "3134F
  }
\clist_const:Nn \c__xeCJK_CM_chars_clist
  {
    "302A -> "302F ,
    "3099 -> "309A ,
    "FE00 -> "FE0F ,
    "E0100 -> "E01EF
  }
\clist_const:Nn \c__xeCJK_HangulJamo_chars_clist
  {
    "1100 -> "11FF ,
    "A960 -> "A97F ,
    "D7B0 -> "D7FF
  }
\cs_new: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}
    \IfBooleanT {#1} { \xeCJKResetPunctClass }
  }
\cs_new_protected:Npn \xeCJK_declare_char_class:nn #1#2
  {
    \clist_set:Nx \l__xeCJK_tmp_clist {#2}
    \xeCJK_declare_char_class:nN {#1} \l__xeCJK_tmp_clist
  }
\cs_new_protected:Npn \xeCJK_declare_char_class:nN #1#2
  {
    \clist_gconcat:ccN
      { g__xeCJK_#1_range_clist } { g__xeCJK_#1_range_clist } #2
    \clist_map_inline:Nn #2
      {
        \str_if_eq:nnF {##1} { -> }
          {
            \__xeCJK_set_char_class_aux:Nnw \xeCJK_set_char_class:nnn {##1}
              { \xeCJK_class_num:n {#1} }
          }
      }
    \xeCJK_set_char_class:nnn { "3099 } { "309A } { \xeCJK_class_num:n { CM } }
  }
\NewDocumentCommand \__xeCJK_set_char_class_aux:Nnw
  { m > { \SplitArgument { 1 } { -> } } m } { #1 #2 }
\cs_generate_variant:Nn \clist_gconcat:NNN { cc }
\cs_new_protected:Npn \__xeCJK_check_num_range:nnNN #1#2#3#4
  {
    \bool_lazy_or:nnTF
      { \tl_if_blank_p:n {#1} }
      { \tl_if_blank_p:n {#2} }
      {
        \int_set:Nn #3 { \tl_if_blank:nTF {#1} {#2} {#1} }
        \int_set_eq:NN #3 #4
      }
      {
        \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
  {
    \int_set:Nn \l__xeCJK_begin_int { "AC00 }
    \int_set:Nn \l__xeCJK_end_int   { "D7A3 }
    \xeCJK_int_until_do:nn { \l__xeCJK_begin_int > \l__xeCJK_end_int }
      {
        \char_set_catcode_letter:n { \l__xeCJK_begin_int }
        \int_incr:N \l__xeCJK_begin_int
      }
  }
\cs_new_protected:Npn \xeCJK_set_char_class:nnn #1#2#3
  {
    \__xeCJK_check_num_range:nnNN {#1} {#2} \l__xeCJK_begin_int \l__xeCJK_end_int
    \int_set:Nn \l__xeCJK_tmp_int {#3}
    \xeCJK_int_until_do:nn { \l__xeCJK_begin_int > \l__xeCJK_end_int }
      {
        \tex_XeTeXcharclass:D \l__xeCJK_begin_int = \l__xeCJK_tmp_int
        \int_incr:N \l__xeCJK_begin_int
      }
  }
\cs_new_protected:Npn \__xeCJK_set_char_class_eq:nn #1#2
  {
    \int_set:Nn \l__xeCJK_tmp_int { \xeCJK_class_num:n {#2} }
    \clist_map_inline:cn { c__xeCJK_#1_chars_clist }
      { \tex_XeTeXcharclass:D ##1 = \l__xeCJK_tmp_int }
  }
\NewDocumentCommand \normalspacedchars { m }
  {
    \tl_map_inline:nn {#1}
      { \tex_XeTeXcharclass:D `##1 = \xeCJK_class_num:n { NormalSpace } }
  }
\NewDocumentCommand \xeCJKResetPunctClass { }
  {
    \clist_gclear:N \g__xeCJK_HalfLeft_range_clist
    \clist_gclear:N \g__xeCJK_HalfRight_range_clist
    \clist_gclear:N \g__xeCJK_FullLeft_range_clist
    \clist_gclear:N \g__xeCJK_FullRight_range_clist
    \xeCJK_declare_char_class:nN { HalfLeft  } \c__xeCJK_HalfLeft_chars_clist
    \xeCJK_declare_char_class:nN { HalfRight } \c__xeCJK_HalfRight_chars_clist
    \xeCJK_declare_char_class:nN { FullLeft  } \c__xeCJK_FullLeft_chars_clist
    \xeCJK_declare_char_class:nN { FullRight } \c__xeCJK_FullRight_chars_clist
  }
\NewDocumentCommand \xeCJKResetCharClass { }
  {
    \clist_gclear:N \g__xeCJK_CJK_range_clist
    \clist_gclear:N \g__xeCJK_NormalSpace_range_clist
    \clist_gclear:N \g__xeCJK_CM_range_clist
    \clist_gclear:N \g__xeCJK_HangulJamo_range_clist
    \xeCJK_declare_char_class:nN { CJK } \c__xeCJK_CJK_chars_clist
    \xeCJK_declare_char_class:nN { NormalSpace } \c__xeCJK_NormalSpace_chars_clist
    \xeCJK_declare_char_class:nN { CM } \c__xeCJK_CM_chars_clist
    \xeCJK_declare_char_class:nN { HangulJamo } \c__xeCJK_HangulJamo_chars_clist
    \xeCJKResetPunctClass
  }
\xeCJKResetCharClass
\cs_new_protected:Npn \xeCJK_inter_class_toks:nnn #1#2#3
  {
    \tex_XeTeXinterchartoks:D \xeCJK_class_num:n {#1} ~
                              \xeCJK_class_num:n {#2} = {#3}
  }
\cs_generate_variant:Nn \xeCJK_inter_class_toks:nnn { nne }
\cs_new:Npn \xeCJK_get_inter_class_toks:nn #1#2
  {
    \tex_the:D \tex_XeTeXinterchartoks:D \xeCJK_class_num:n {#1} ~
                                         \xeCJK_class_num:n {#2}
  }
\cs_new_protected:Npn \xeCJK_clear_inter_class_toks:nn #1#2
  { \xeCJK_inter_class_toks:nnn {#1} {#2} { \prg_do_nothing: } }
\cs_new_protected:Npn \xeCJK_pre_inter_class_toks:nnn #1#2#3
  {
    \xeCJK_inter_class_toks:nne {#1} {#2}
      { \exp_not:n {#3} \xeCJK_get_inter_class_toks:nn {#1} {#2} }
  }
\cs_generate_variant:Nn \xeCJK_pre_inter_class_toks:nnn { nne }
\cs_new_protected:Npn \xeCJK_app_inter_class_toks:nnn #1#2#3
  {
    \xeCJK_inter_class_toks:nne {#1} {#2}
      { \xeCJK_get_inter_class_toks:nn {#1} {#2} \exp_not:n {#3} }
  }
\cs_generate_variant:Nn \xeCJK_app_inter_class_toks:nnn { nne }
\cs_new_protected: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_if_empty:NTF \l__xeCJK_tmp_tl
      {
        \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} }
      }
      { \xeCJK_inter_class_toks:nne {#1} {#2} { \exp_not:o \l__xeCJK_tmp_tl } }
  }
\cs_new_protected: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_if_empty:NF \l__xeCJK_tmp_tl
      {
        \tl_replace_all:Nnn \l__xeCJK_tmp_tl {#3} {#4}
        \xeCJK_inter_class_toks:nne {#1} {#2}
          { \exp_not:o \l__xeCJK_tmp_tl }
      }
  }
\cs_new_protected:Npn \xeCJK_clear_Boundary_and_CJK_toks:
  { }
\cs_new_protected:Npn \__xeCJK_update_clear_toks:n #1
  {
    \cs_gset_protected:Npx \xeCJK_clear_Boundary_and_CJK_toks:
      {
        \exp_not:o { \xeCJK_clear_Boundary_and_CJK_toks: }
        \tex_XeTeXinterchartoks:D
          \xeCJK_class_num:n { Boundary } ~
          \xeCJK_class_num:n {#1} = { \exp_not:N \prg_do_nothing: }
      }
  }
\seq_new:N \g__xeCJK_base_class_seq
\seq_gset_eq:NN \g__xeCJK_base_class_seq \g__xeCJK_class_seq
\seq_new:N \g__xeCJK_non_CJK_class_seq
\seq_gset_from_clist:Nn \g__xeCJK_non_CJK_class_seq
  { Default , HalfLeft , HalfRight , NormalSpace , Boundary }
\seq_new:N \g__xeCJK_CJK_class_seq
\cs_new_protected:Npn \__xeCJK_save_CJK_class:n #1
  {
    \seq_gput_right:Nn \g__xeCJK_CJK_class_seq {#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
  { CJK , FullLeft , FullRight , CM , HangulJamo } \__xeCJK_save_CJK_class:n
\cs_new_protected:Npn \xeCJK_class_group_begin:
  {
    \c_group_begin_token
    \bool_set_true:N \l__xeCJK_CJK_group_bool
    \xeCJK_reset_space_factor:
    \int_zero:N \tex_XeTeXdashbreakstate:D
  }
\bool_new:N \l__xeCJK_CJK_group_bool
\cs_new_eq:NN \xeCJK_class_group_end: \c_group_end_token
\AtEndOfPackage
  {
    \seq_map_inline:Nn \g__xeCJK_class_seq
      {
        \str_if_eq:nnTF {#1} { CM }
          { \xeCJK_copy_inter_class_toks:nnnn { CM } {#1} { CJK } { CJK } }
          {
            \xeCJK_copy_inter_class_toks:nnnn { CM } {#1} { CJK } {#1}
            \str_if_eq:nnF {#1} { CJK }
              { \xeCJK_copy_inter_class_toks:nnnn {#1} { CM } {#1} { CJK } }
          }
      }
  }
\AtEndOfPackage
  {
    \seq_map_inline:Nn \g__xeCJK_class_seq
      {
        \str_if_eq:nnF {#1} { HangulJamo }
          {
            \xeCJK_copy_inter_class_toks:nnnn { HangulJamo } {#1} { CJK } {#1}
            \xeCJK_copy_inter_class_toks:nnnn {#1} { HangulJamo } {#1} { CJK }
          }
      }
  }
\clist_map_inline:nn { Default , HalfLeft , HalfRight , NormalSpace }
  {
    \xeCJK_inter_class_toks:nnn {#1} { CJK }
      {
        \xeCJK_class_group_begin:
        \xeCJK_select_font:
        \xeCJK_clear_inter_class_toks:nn {#1} { CJK }
        \xeCJK_clear_Boundary_and_CJK_toks:
        \xeCJK_fallback_symbol:NN
        \CJKsymbol
      }
    \xeCJK_inter_class_toks:nnn { CJK } {#1} { \xeCJK_class_group_end: }
  }
\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 }
  }
\cs_new_protected:Npn \xeCJK_Boundary_and_Default:
  { \xeCJK_check_for_ecglue: }
\cs_new_protected:Npn \__xeCJK_check_for_xecglue:
  {
    \__xeCJK_if_last_glue:TF
      { \__xeCJK_replace_space: }
      { \__xeCJK_check_for_ecglue: }
  }
\cs_new_protected:Npn \__xeCJK_check_for_ecglue:
  {
    \xeCJK_if_last_node:nTF { CJK }
      { \use_i:nn }
      { \xeCJK_if_last_node:nTF { CJK-widow } }
      { \xeCJK_remove_node: \CJKecglue }
      {
        \xeCJK_if_last_node:nT { CJK-space }
          { \xeCJK_remove_node: \xeCJK_space_or_xecglue: }
      }
  }
\cs_new_eq:NN \xeCJK_check_for_ecglue: \__xeCJK_check_for_ecglue:
\cs_new_protected:Npn \__xeCJK_replace_space:
  {
    \skip_set_eq:NN \l__xeCJK_last_skip \tex_lastskip:D
    \tex_unskip:D
    \xeCJK_if_last_node:nTF { CJK-space }
      { \xeCJK_remove_node: \CJKecglue }
      {
        \xeCJK_if_last_node:nTF { CJK }
          {
            \skip_if_eq:nnTF
              { \l__xeCJK_last_skip }
              { \c_xeCJK_space_skip_tl }
              { \xeCJK_remove_node: \CJKecglue }
              { \skip_horizontal:N \l__xeCJK_last_skip }
          }
          { \skip_horizontal:N \l__xeCJK_last_skip }
      }
  }
\skip_new:N \l__xeCJK_last_skip
\clist_map_inline:nn { Default , HalfRight }
  {
    \xeCJK_inter_class_toks:nnn {#1} { Boundary }
      {
        \int_gset_eq:NN \g__xeCJK_space_factor_int \tex_spacefactor:D
        \peek_meaning_remove:NTF \tex_italiccorrection:D
          {
            \tex_italiccorrection:D
            { \xeCJK_make_node:n { default } }
          }
          {
            \token_if_space:NTF \l_peek_token
              { \xeCJK_make_space_node: }
              { { \xeCJK_make_node:n { default } } }
          }
      }
    \xeCJK_pre_inter_class_toks:nnn {#1} { CJK } { \CJKecglue }
  }
\xeCJK_inter_class_toks:nnn { Boundary } { NormalSpace }
  { \xeCJK_Boundary_and_NormalSp: }
\cs_new_protected:Npn \xeCJK_Boundary_and_NormalSp:
  { \xeCJK_check_for_ecglue_normalsp: }
\cs_new_protected:Npn \__xeCJK_check_for_xecglue_normalsp:
  {
    \__xeCJK_if_last_glue:TF
      { \__xeCJK_replace_space: }
      { \__xeCJK_check_for_ecglue_normalsp: }
  }
\cs_new_protected:Npn \__xeCJK_check_for_ecglue_normalsp:
  {
    \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:
\xeCJK_inter_class_toks:nnn { NormalSpace } { Boundary }
  {
    \int_gset_eq:NN \g__xeCJK_space_factor_int \tex_spacefactor:D
    \peek_meaning_remove:NTF \tex_italiccorrection:D
      {
        \tex_italiccorrection:D
        { \xeCJK_make_node:n { normalspace } }
      }
      {
        \token_if_space:NTF \l_peek_token
          { \xeCJK_make_space_node: }
          { { \xeCJK_make_node:n { normalspace } } }
      }
  }
\xeCJK_inter_class_toks:nnn { Boundary } { CJK }
  {
    \xeCJK_check_for_glue:
    \xeCJK_class_group_begin:
    \xeCJK_clear_Boundary_and_CJK_toks:
    \xeCJK_select_font:
    \xeCJK_fallback_symbol:NN
    \CJKsymbol
  }
\cs_new_protected:Npn \xeCJK_check_for_glue:
  {
    \__xeCJK_if_last_kern:TF
      { \__xeCJK_check_for_glue_auxi: }
      {
        \__xeCJK_if_last_math:TF
          { \xeCJK_remove_node: \CJKecglue }
          { \__xeCJK_check_for_glue_auxii: }
      }
  }
\cs_new_protected: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: \__xeCJK_ccglue_or_space: }
        { \__xeCJK_node:n { CJK-widow } }
        { \xeCJK_remove_node: \xeCJK_widow_penalty: \CJKglue }
        { \__xeCJK_node:n { default } }
        { \xeCJK_remove_node: \CJKecglue }
      }
  }
\cs_new_protected:Npn \__xeCJK_check_for_glue_auxii:
  {
    \xeCJK_if_last_punct:TF
      { \__xeCJK_check_for_glue_auxiii: }
      { \xeCJK_check_for_xglue: }
  }
\cs_new_protected:Npn \__xeCJK_check_for_glue_auxiii:
  {
    \bool_if:NT \l__xeCJK_last_penalty_bool
      { \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:Npn \__xeCJK_check_for_xglue:
  {
    \__xeCJK_if_last_glue:TF
      {
        \skip_set_eq:NN \l__xeCJK_last_skip \tex_lastskip:D
        \tex_unskip:D
        \xeCJK_if_last_node:nTF { CJK-space }
          { \xeCJK_remove_node: \__xeCJK_ccglue_or_space: }
          {
            \xeCJK_if_last_node:nTF { default-space }
              { \xeCJK_remove_node: \CJKecglue }
              { \__xeCJK_check_for_xglue_aux: }
          }
      }
  }
\cs_new_protected:Npn \__xeCJK_check_for_xglue_aux:
  {
    \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: }
          {
            \xeCJK_if_last_node:nTF { default }
              { \xeCJK_remove_node: \CJKecglue }
              {
                \__xeCJK_if_last_math:TF
                  { \CJKecglue }
                  { \skip_horizontal:N \l__xeCJK_last_skip }
              }
          }
      }
      { \skip_horizontal:N \l__xeCJK_last_skip }
  }
\cs_new_protected:Npn \__xeCJK_ccglue_or_space:
  { \CJKglue }
\group_begin:
\cs_set:Npn \__xeCJK_tmp:nn #1
  {
    \exp_args:Ncc \__xeCJK_tmp_aux:NNn
      { __xeCJK_if_last_ #1 : }
      { c__xeCJK_ #1 _node }
  }
\cs_set:Npn \__xeCJK_tmp_aux:NNn #1#2#3
  {
    \int_const:Nn #2 {#3}
    \prg_new_conditional:Npnn #1 { T , F , TF }
      {
        \if_int_compare:w \tex_lastnodetype:D = #2
          \prg_return_true: \else: \prg_return_false: \fi:
      }
  }
\__xeCJK_tmp:nn { none }    { -1 }
\__xeCJK_tmp:nn { hlist }   {  1 }
\__xeCJK_tmp:nn { math }    { 10 }
\__xeCJK_tmp:nn { glue }    { 11 }
\__xeCJK_tmp:nn { kern }    { 12 }
\__xeCJK_tmp:nn { penalty } { 13 }
\group_end:
 \prg_new_conditional:Npnn \xeCJK_if_last_node:n #1 { p , T , F , TF }
  {
    \if_dim:w
      \cs_if_exist_use:cTF { c__xeCJK_#1_node_dim }
        { = \tex_lastkern:D }
        { \use:c { c__xeCJK_#1_node_skip } = \tex_lastskip:D }
      \prg_return_true: \else: \prg_return_false: \fi:
  }
\cs_new_protected:Npn \xeCJK_declare_node:n #1
  {
    \int_gincr:N \g__xeCJK_node_int
    \dim_if_exist:cTF { c__xeCJK_#1_node_dim }
      { \dim_gset:cn } { \dim_const:cn }
      { c__xeCJK_#1_node_dim } { \g__xeCJK_node_int sp }
  }
\cs_new_protected:Npn \xeCJK_declare_glue_node:n #1
  {
    \int_gincr:N \g__xeCJK_node_int
    \skip_if_exist:cTF { c__xeCJK_#1_node_skip }
      { \skip_gset:cn } { \skip_const:cn }
      { c__xeCJK_#1_node_skip } { \g__xeCJK_node_int sp }
  }
\int_new:N \g__xeCJK_node_int
\int_gset:Nn \g__xeCJK_node_int { 10 }
\cs_new_protected:Npn \xeCJK_make_node:n #1
  { \exp_args:Nc \__xeCJK_make_node:N { c__xeCJK_#1_node_dim } }
\cs_new:Npn \__xeCJK_node:n #1
  { \use:c { c__xeCJK_#1_node_dim } }
\cs_new:Npn \__xeCJK_gule_node:n #1
  { \use:c { c__xeCJK_#1_node_skip } }
\cs_new_protected:Npn \__xeCJK_make_node:N #1
  {
    \tex_kern:D - #1
    \tex_kern:D   #1
  }
\cs_new_protected:Npn \xeCJK_remove_node:
  {
    \__xeCJK_if_last_kern:TF
      { \tex_unkern:D \tex_unkern:D }
      {
        \__xeCJK_if_last_glue:T
          { \tex_unskip:D \tex_unskip:D }
      }
  }
\xeCJK_declare_node:n { CJK }
\xeCJK_declare_node:n { CJK-space }
\xeCJK_declare_node:n { default }
\xeCJK_declare_node:n { CJK-widow }
\xeCJK_declare_node:n { normalspace }
\xeCJK_declare_glue_node:n { default-space }
\cs_new_eq:NN \xeCJK_make_space_node: \prg_do_nothing:
\cs_new_protected:Npx \__xeCJK_make_space_node:
  {
    \tex_hskip:D - \__xeCJK_gule_node:n { default-space }
    \tex_hskip:D   \__xeCJK_gule_node:n { default-space }
  }
\keys_define:nn { xeCJK / options }
  {
    CJKglue .code:n =
      {
        \cs_set_protected:Npn \CJKglue {#1}
        \xeCJK_glue_to_skip:nN {#1} \l__xeCJK_ccglue_skip
      }
  }
\skip_new:N \l__xeCJK_ccglue_skip
\keys_define:nn { xeCJK / options }
  {
    CJKecglue            .code:n =
      {
        \cs_set_protected:Npn \CJKecglue {#1}
        \xeCJK_glue_to_skip:nN {#1} \l__xeCJK_ecglue_skip
      } ,
    xCJKecglue .choice: ,
    xCJKecglue / true    .code:n =
      {
        \bool_set_true:N  \l__xeCJK_xecglue_bool
        \cs_set_eq:NN \xeCJK_space_or_xecglue: \CJKecglue
        \cs_set_eq:NN \xeCJK_make_space_node: \__xeCJK_make_space_node:
        \cs_set_eq:NN \xeCJK_check_for_xglue: \__xeCJK_check_for_xglue:
        \cs_set_eq:NN \xeCJK_check_for_ecglue: \__xeCJK_check_for_xecglue:
        \cs_set_eq:NN
          \xeCJK_check_for_ecglue_normalsp:
          \__xeCJK_check_for_xecglue_normalsp:
      } ,
    xCJKecglue / false   .code:n =
      {
        \bool_set_false:N \l__xeCJK_xecglue_bool
        \cs_set_eq:NN \xeCJK_space_or_xecglue: \xeCJK_space_glue:
        \xeCJK_cs_clear:N \xeCJK_make_space_node:
        \xeCJK_cs_clear:N \xeCJK_check_for_xglue:
        \cs_set_eq:NN \xeCJK_check_for_ecglue: \__xeCJK_check_for_ecglue:
        \cs_set_eq:NN
          \xeCJK_check_for_ecglue_normalsp:
          \__xeCJK_check_for_ecglue_normalsp:
      } ,
    xCJKecglue / unknown .code:n =
      {
        \bool_set_true:N  \l__xeCJK_xecglue_bool
        \cs_set_protected:Npn \CJKecglue {#1}
        \xeCJK_glue_to_skip:nN {#1} \l__xeCJK_ecglue_skip
        \cs_set_eq:NN \xeCJK_space_or_xecglue: \CJKecglue
        \cs_set_eq:NN \xeCJK_make_space_node: \__xeCJK_make_space_node:
        \cs_set_eq:NN \xeCJK_check_for_xglue: \__xeCJK_check_for_xglue:
        \cs_set_eq:NN \xeCJK_check_for_ecglue: \__xeCJK_check_for_xecglue:
        \cs_set_eq:NN
          \xeCJK_check_for_ecglue_normalsp:
          \__xeCJK_check_for_xecglue_normalsp:
      } ,
    xCJKecglue        .default:n = { true }
  }
\cs_new_eq:NN \xeCJK_space_glue: \c_space_tl
\skip_new:N \l__xeCJK_ecglue_skip
\bool_new:N \l__xeCJK_xecglue_bool
\keys_define:nn { xeCJK / options }
  {
    CJKspace .choice: ,
    CJKspace / true  .code:n =
      {
        \bool_set_true:N \l__xeCJK_reserve_space_bool
        \cs_set_protected:Npn \__xeCJK_ccglue_or_space:
          { \xeCJK_space_glue: }
      } ,
    CJKspace / false .code:n =
      {
        \bool_set_false:N \l__xeCJK_reserve_space_bool
        \cs_set_protected:Npn \__xeCJK_ccglue_or_space:
          { \CJKglue }
      } ,
    CJKspace      .default:n = { true } ,
    space            .meta:n = { CJKspace = true  } ,
    nospace          .meta:n = { CJKspace = false }
  }
\bool_new:N \l__xeCJK_reserve_space_bool
\xeCJK_inter_class_toks:nnn { CJK } { Boundary } { \xeCJK_CJK_and_Boundary:w }
\cs_new_protected:Npn \xeCJK_CJK_and_Boundary:w
  {
    \xeCJK_peek_catcode_ignore_spaces:NTF \c_math_toggle_token
      {
        \bool_if:NTF \l__xeCJK_peek_ignore_spaces_bool
          { \xeCJK_class_group_end: \xeCJK_space_or_xecglue: }
          { \xeCJK_class_group_end: \CJKecglue }
      }
      {
        \group_align_safe_begin:
        \bool_if:NTF \l__xeCJK_peek_ignore_spaces_bool
          {
            \token_if_macro:NTF \l_peek_token
              { \__xeCJK_boundary_reserve_space: }
              { \__xeCJK_boundary_group_end:n { CJK-space } }
          }
          {
            \token_if_eq_meaning:NNTF \l_peek_token \scan_stop:
              { \__xeCJK_CJK_and_Boundary_relax:N }
              { \__xeCJK_boundary_group_end:n { CJK } }
          }
      }
  }
\cs_new_protected:Npn \__xeCJK_boundary_reserve_space:
  {
    \__xeCJK_boundary_group_end:n { CJK-space }
    \xeCJK_space_or_xecglue:
  }
\cs_new_protected:Npn \__xeCJK_CJK_and_Boundary_relax:N #1
  {
    \__xeCJK_boundary_group_end:n { CJK }
    \token_if_eq_meaning:NNTF #1 \scan_stop:
      {#1} { \cs_set_eq:NN #1 \scan_stop: #1 }
  }
\cs_new_protected:Npn \__xeCJK_boundary_group_end:n #1
  {
    \group_align_safe_end:
    \xeCJK_class_group_end:
    { \xeCJK_make_node:n {#1} }
  }
\cs_new_protected:Npn \xeCJK_ignore_spaces:w
  {
    \xeCJK_peek_catcode_ignore_spaces:NTF \c_math_toggle_token
      {
        \bool_if:NTF \l__xeCJK_peek_ignore_spaces_bool
          { \xeCJK_space_or_xecglue: } { \CJKecglue }
      }
      {
        \bool_if:NT \l__xeCJK_peek_ignore_spaces_bool
          {
            \dim_case:nn { \tex_lastkern:D }
              {
                { \__xeCJK_node:n { CJK } }
                { \xeCJK_remove_node: \xeCJK_make_node:n { CJK-space } }
                { \__xeCJK_node:n { default } }
                { \xeCJK_remove_node: \xeCJK_make_space_node: }
              }
            \group_align_safe_begin:
            \token_if_macro:NTF \l_peek_token
              { \__xeCJK_reserve_space_aux: }
              { \group_align_safe_end: }
          }
      }
  }
\cs_new_protected:Npn \__xeCJK_reserve_space_aux:
  {
    \group_align_safe_end:
    \xeCJK_space_or_xecglue:
  }
\xeCJK_inter_class_toks:nnn { CJK } { CJK }
  { \xeCJK_CJK_and_CJK:N }
\cs_new_protected:Npn \xeCJK_CJK_and_CJK:N
  {
    \CJKglue
    \xeCJK_fallback_symbol:NN
    \CJKsymbol
  }
\xeCJK_inter_class_toks:nnn { FullLeft }  { CJK }
  {
    \xeCJK_FullLeft_and_CJK:
    \xeCJK_fallback_symbol:NN
    \CJKsymbol
  }
\xeCJK_inter_class_toks:nnn { FullRight } { CJK }
  {
    \xeCJK_FullRight_and_CJK:
    \xeCJK_fallback_symbol:NN
    \CJKsymbol
  }
\seq_map_inline:Nn \g__xeCJK_non_CJK_class_seq
  {
    \clist_map_inline:nn { FullLeft , FullRight }
      {
        \xeCJK_inter_class_toks:nne {#1} {##1}
          { \exp_not:c { xeCJK_Default_and_##1:nN } {#1} }
        \xeCJK_inter_class_toks:nne {##1} {#1}
          { \exp_not:c { xeCJK_##1_and_Default: } }
      }
  }
\xeCJK_inter_class_toks:nnn { Boundary } { FullLeft }
  { \xeCJK_Boundary_and_FullLeft:N  }
\xeCJK_inter_class_toks:nnn { Boundary } { FullRight }
  { \xeCJK_Boundary_and_FullRight:N  }
\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:Npn \xeCJK_FullLeft_and_Boundary:
  {
    \__xeCJK_punct_if_middle:NTF \g__xeCJK_last_punct_tl
      {
        \__xeCJK_punct_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_nobreak_zero_glue:
      }
    \tex_ignorespaces:D
  }
\cs_new_protected:Npn \xeCJK_FullRight_and_Boundary:
  {
    \__xeCJK_punct_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_punct_glue:NN \c__xeCJK_right_tl \g__xeCJK_last_punct_tl
    \tex_ignorespaces:D
  }
\cs_new_protected: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: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:Npn \xeCJK_punct_bound_kern:N #1
  {
    \exp_after:wN \__xeCJK_punct_bound_kern:NN
      \g__xeCJK_last_punct_tl #1
  }
\cs_new_protected: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 }
    \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
  }
\skip_new:N \l__xeCJK_punct_kern_skip
\cs_new_protected: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: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:Npn \__xeCJK_nobreak_hskip:N
  { \xeCJK_no_break: \skip_horizontal:N }
\cs_new_protected: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: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:Npn \__xeCJK_punct_bound_kern_aux:NNN #1#2
  {
    \str_if_eq:nnTF {#1} {#2}
      { \__xeCJK_nobreak_hskip: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:nne {#1} {##1}
          { \exp_not:c { xeCJK_#1_and_##1:N } }
      }
  }
\cs_new_protected:Npn \__xeCJK_punct_bound_rule:NN #1#2
  {
    \tex_vrule:D
      width - \__xeCJK_use_punct_dim:nNN { bound } #1 #2 ~
      depth  \c_zero_dim
      height \c_zero_dim \scan_stop:
  }
\cs_new_protected:Npn \__xeCJK_punct_rule:NN #1#2
  {
    \tex_vrule:D
      width  \__xeCJK_use_punct_dim:nNN { rule } #1 #2 ~
      depth  \c_zero_dim
      height \c_zero_dim \scan_stop:
  }
\cs_new_protected:Npn \__xeCJK_punct_glue:NN #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:Npn \__xeCJK_punct_kern:NN #1#2
  {
    \str_if_eq:eeTF {#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:Npn \__xeCJK_punct_nobreak_kern:NN #1#2
  { \__xeCJK_nobreak_hskip:n { \__xeCJK_use_dim_or_skip:nNN { kern } #1 #2 } }
\cs_new_protected: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 { bound_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:Npn \xeCJK_FullLeft_and_CJK:
  {
    \__xeCJK_punct_if_middle:NTF \g__xeCJK_last_punct_tl
      {
        \__xeCJK_punct_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_select_font:
  }
\cs_new_protected:Npn \xeCJK_FullLeft_and_Default:
  {
    \__xeCJK_punct_if_middle:NTF \g__xeCJK_last_punct_tl
      {
        \__xeCJK_punct_rule:NN \c__xeCJK_right_tl \g__xeCJK_last_punct_tl
        \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_nobreak_zero_glue:
      }
  }
\cs_new_protected:Npn \__xeCJK_nobreak_zero_glue:
  {
    \tex_penalty:D \c__xeCJK_nobreak_penalty_int
    \skip_horizontal:N \c_zero_skip
  }
\cs_new_protected:Npn \__xeCJK_zero_glue:
  { \skip_horizontal:N \c_zero_skip }
\cs_new_protected:Npn \xeCJK_FullRight_and_CJK:
  {
    \__xeCJK_punct_rule: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:Npn \xeCJK_FullRight_and_Default:
  {
    \__xeCJK_punct_rule:NN \c__xeCJK_right_tl \g__xeCJK_last_punct_tl
    \xeCJK_class_group_end:
    \__xeCJK_punct_glue:NN \c__xeCJK_right_tl \g__xeCJK_last_punct_tl
  }
\cs_new_protected: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_class_group_begin:
    \xeCJK_select_punct_font:
    \xeCJK_clear_inter_class_toks:nn {#1} { FullLeft }
    \xeCJK_clear_Boundary_and_CJK_toks:
    \tl_gset:Nn \g__xeCJK_last_punct_tl {#2}
    \__xeCJK_punct_rule:NN \c__xeCJK_left_tl #2
    \xeCJK_fallback_punct_symbol:NN
    \CJKpunctsymbol #2
  }
\cs_new_protected:Npn \__xeCJK_Default_and_FullLeft_glue:N #1
  { \__xeCJK_punct_glue:NN \c__xeCJK_left_tl #1 }
\cs_new_protected: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:Nn \g__xeCJK_last_punct_tl {#1}
    \__xeCJK_punct_rule:NN \c__xeCJK_left_tl #1
    \__xeCJK_select_punct_font:
    \xeCJK_fallback_punct_symbol:NN
    \CJKpunctsymbol #1
  }
\cs_new_protected:Npn \__xeCJK_CJK_and_FullLeft_glue:N #1
  {
    \CJKglue
    \__xeCJK_punct_glue:NN \c__xeCJK_left_tl #1
  }
\cs_new_protected: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_class_group_begin:
    \xeCJK_select_punct_font:
    \xeCJK_clear_Boundary_and_CJK_toks:
    \tl_gset:Nn \g__xeCJK_last_punct_tl {#1}
    \__xeCJK_punct_rule:NN \c__xeCJK_left_tl #1
    \xeCJK_fallback_punct_symbol:NN
    \CJKpunctsymbol #1
  }
\cs_new_protected:Npn \__xeCJK_Boundary_and_FullLeft_glue:N #1
  {
    \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 \tex_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:cpn { __xeCJK_bound_type_ -1 _glue:Nn } #1#2
  { \__xeCJK_zero_glue: }
\cs_new_protected:cpn { __xeCJK_bound_type_  1 _glue:Nn } #1
  {
    \int_do_while:nNnn \tex_lastnodetype:D = \c__xeCJK_hlist_node
      { \__xeCJK_bound_hbox_auxi: }
    \__xeCJK_if_last_none:TF
      {
        \dim_case:nnF { \box_wd:N \l__xeCJK_indent_box }
          {
            { \tex_parindent:D } { \__xeCJK_bound_hbox_auxii:nn }
            { \c_zero_dim }      { \use_i:nn }
          }
          { \use:nn }
      }
      { \use:nn }
      { \hbox_unpack_drop:N \l__xeCJK_indent_box }
  }
\cs_new_protected:Npn \__xeCJK_bound_hbox_auxi:
  {
    \box_set_to_last:N \l__xeCJK_tmp_box
    \hbox_set:Nn \l__xeCJK_indent_box
      {
        \box_use:N \l__xeCJK_tmp_box
        \hbox_unpack:N \l__xeCJK_indent_box
      }
  }
\cs_new_protected:Npn \__xeCJK_bound_hbox_auxii:nn
  {
    \dim_compare:nNnTF
      { \box_ht:N \l__xeCJK_tmp_box } = \c_zero_dim
      { \use_i:nn }
      { \use:nn }
  }
\box_new:N \l__xeCJK_indent_box
\cs_new_protected: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: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:Npn \__xeCJK_bound_glue_auxii:n #1
  {
    \skip_set_eq:NN \l__xeCJK_last_skip \tex_lastskip:D
    \skip_if_eq:nnTF { \l__xeCJK_last_skip } { 1sp }
      { \__xeCJK_zero_glue: }
      {
        \skip_if_eq:nnTF { \l__xeCJK_last_skip } { \labelsep }
          {
            \tex_unskip:D
            \__xeCJK_if_last_penalty:TF
              {
                \int_compare:nNnTF \tex_lastpenalty:D = \c_zero_int
                  { \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: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: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_int
          {
            \tex_unpenalty:D
            \__xeCJK_if_last_hlist:TF
              { \tex_penalty:D \c_zero_int }
              { \tex_penalty:D \c_zero_int #2 }
          }
          {#2}
      }
  }
\cs_new_protected: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_class_group_begin:
    \xeCJK_select_punct_font:
    \xeCJK_clear_inter_class_toks:nn {#1} { FullRight }
    \xeCJK_clear_Boundary_and_CJK_toks:
    \tl_gset:Nn \g__xeCJK_last_punct_tl {#2}
    \xeCJK_FullRight_symbol:N #2
  }
\cs_new_protected:Npn \xeCJK_Boundary_and_FullRight: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_punct_font:
    \xeCJK_clear_Boundary_and_CJK_toks:
    \tl_gset:Nn \g__xeCJK_last_punct_tl {#1}
    \xeCJK_FullRight_symbol:N #1
  }
\cs_new_protected: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:Nn \g__xeCJK_last_punct_tl {#1}
    \__xeCJK_select_punct_font:
    \xeCJK_FullRight_symbol:N #1
  }
\cs_new_protected:Npn \xeCJK_if_last_punct:TF
  {
    \bool_set_false:N \l__xeCJK_last_penalty_bool
    \__xeCJK_if_last_glue:TF
      { \__xeCJK_if_last_punct_glue:TF }
      {
        \__xeCJK_if_last_penalty:TF
         { \__xeCJK_if_last_punct_penalty:TF }
         { \use_ii:nn }
      }
  }
\cs_new_protected:Npn \__xeCJK_if_last_punct_glue:TF
  {
    \prop_get:NoNTF \g__xeCJK_punct_skip_prop
      { \skip_use:N \tex_lastskip:D } \l__xeCJK_tmp_tl
      { \__xeCJK_if_last_punct_glue_auxi:TF }
      { \__xeCJK_if_last_punct_glue_auxii:TF }
  }
\cs_new_protected:Npn \__xeCJK_if_last_punct_glue_auxi:TF
  {
    \skip_set_eq:NN \l__xeCJK_last_skip \tex_lastskip:D
    \tex_unskip:D
    \int_compare:nNnTF \tex_lastpenalty:D = \c__xeCJK_nobreak_penalty_int
      { \__xeCJK_if_last_punct_auxi:TF { \use_i:nn } }
      {
        \xeCJK_if_last_node:TF
          { \__xeCJK_if_last_punct_auxii:TF { \use_i:nn } }
          { \use:n }
      }
      { \skip_horizontal:N \l__xeCJK_last_skip \use_ii:nn }
  }
\cs_new_protected:Npn \__xeCJK_if_last_punct_glue_auxii:TF
  {
    \group_begin:
      \g__xeCJK_space_factor_int \tex_spacefactor:D
      \skip_if_eq:nnTF { \tex_lastskip:D } { \c_xeCJK_space_skip_tl }
        { \group_end: \__xeCJK_if_last_punct_glue_auxiii:TF }
        { \group_end: \use_ii:nn }
  }
\cs_new_protected:Npn \__xeCJK_if_last_punct_glue_auxiii:TF
  {
    \skip_set_eq:NN \l__xeCJK_tmp_skip \tex_lastskip:D
    \tex_unskip:D
    \__xeCJK_if_last_glue:TF
      {
        \prop_get:NoNTF \g__xeCJK_punct_skip_prop
          { \skip_use:N \tex_lastskip:D } \l__xeCJK_tmp_tl
          { \__xeCJK_if_last_punct_glue_auxi:TF { \use_i:nn } }
          { \use:n }
      }
      { \use:n }
      { \skip_horizontal:N \l__xeCJK_tmp_skip \use_ii:nn }
  }
\cs_new_protected:Npn \__xeCJK_if_last_punct_penalty:TF
  {
    \int_set_eq:NN \l__xeCJK_last_penalty_int \tex_lastpenalty:D
    \tex_unpenalty:D
    \bool_set_true:N \l__xeCJK_last_penalty_bool
    \__xeCJK_if_last_glue:TF
      { \__xeCJK_if_last_punct_glue:TF { \use_i:nn } }
      { \use:n }
      { \__xeCJK_last_punct_penalty_false:nn }
  }
\cs_new_protected:Npn \__xeCJK_last_punct_penalty_false:nn #1#2
  {
    \bool_set_false:N \l__xeCJK_last_penalty_bool
    \tex_penalty:D \l__xeCJK_last_penalty_int
    #2
  }
\cs_new_protected:Npn \__xeCJK_if_last_punct_auxi:TF
  {
    \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__xeCJK_nobreak_penalty_int
      }
    \xeCJK_if_last_node:TF
      { \__xeCJK_if_last_punct_auxii:TF { \use_i:nn } }
      { \use:n }
      { \xeCJK_no_break: \use_ii:nn }
  }
\cs_new_protected:Npn \__xeCJK_if_last_punct_auxii:TF
  {
    \dim_compare:nNnTF \l__xeCJK_last_kern_dim > \c_zero_dim
      { \__xeCJK_if_last_punct_auxiii:TF }
      { \__xeCJK_make_node:N \l__xeCJK_last_kern_dim \use_ii:nn }
  }
\cs_new_protected:Npn \__xeCJK_if_last_punct_auxiii:TF
  {
    \int_case:nnTF { \tex_XeTeXcharclass: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_auxiv:TF }
      { \use_ii:nn }
  }
\cs_new_protected:Npn \__xeCJK_if_last_punct_auxiv:TF
  {
    \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
          { \tex_Uchar:D \l__xeCJK_tmp_dim }
        \dim_set_eq:NN \l__xeCJK_last_bound_dim \l__xeCJK_last_kern_dim
        \use_i:nn
      }
      { \__xeCJK_make_node:N \l__xeCJK_tmp_dim \use_ii:nn }
  }
\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
  {
    \__xeCJK_if_last_kern:TF
      {
        \dim_set_eq:NN \l__xeCJK_last_kern_dim \tex_lastkern:D
        \tex_unkern:D
        \__xeCJK_if_last_kern:TF
          {
            \dim_compare:nNnTF \tex_lastkern:D = { - \l__xeCJK_last_kern_dim }
              { \tex_unkern:D #1 }
              { \tex_kern:D \l__xeCJK_last_kern_dim #2 }
          }
          { \tex_kern:D \l__xeCJK_last_kern_dim #2 }
      }
      {#2}
  }
\dim_new:N \l__xeCJK_last_kern_dim
\cs_new_protected:Npn \__xeCJK_CJK_and_FullRight_glue:N #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_rule:NN \c__xeCJK_left_tl #1
      }
  }
\cs_new_protected:Npn \__xeCJK_Default_and_FullRight_glue: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_rule:NN \c__xeCJK_left_tl #1
      }
  }
\cs_new_protected: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:Nn \g__xeCJK_last_punct_tl {#1}
    \xeCJK_fallback_punct_symbol:NN
    \CJKpunctsymbol #1
  }
\cs_new_protected:Npn \xeCJK_FullLeft_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:Nn \g__xeCJK_last_punct_tl {#1}
    \xeCJK_FullRight_symbol:N #1
  }
\cs_new_protected:Npn \xeCJK_FullRight_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:Nn \g__xeCJK_last_punct_tl {#1}
    \xeCJK_fallback_punct_symbol:NN
    \CJKpunctsymbol #1
  }
\cs_new_protected: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:Nn \g__xeCJK_last_punct_tl {#1}
    \xeCJK_FullRight_symbol:N #1
  }
\keys_define:nn { xeCJK / options }
  {
    CheckFullRight .choice: ,
    CheckFullRight / true  .code:n =
      {
        \cs_if_eq:NNF \xeCJK_FullRight_and_Boundary: \xeCJK_check_FullRight:
          {
            \cs_set_eq:NN \__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 =
      {
        \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
          }
      } ,
    CheckFullRight      .default:n = { true }
  }
\cs_new_protected:Npn \xeCJK_FullRight_symbol:N
  {
    \xeCJK_fallback_punct_symbol:NN
    \CJKpunctsymbol
  }
\cs_new_protected:Npn \xeCJK_check_FullRight:
  {
    \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
    \group_align_safe_begin:
    \token_case_meaning:NoTF \l_peek_token
      { \l__xeCJK_no_break_cs_case_tl }
      {
        \group_align_safe_end:
        \xeCJK_no_break:
        \group_insert_after:N \xeCJK_no_break:
      }
      { \group_align_safe_end: }
    \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
  }
\prg_generate_conditional_variant:Nnn \token_case_meaning:Nn { No } { TF , F }
\cs_new_protected:Npn \xeCJK_check_FullRight_symbol:Nw #1
  { \peek_remove_spaces:n { \__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
    \seq_new:N #3
    \keys_define:nn { xeCJK / options }
      {
        #1  .code:n =
          {
            \seq_set_split:Nnn #3 { } {##1}
            \__xeCJK_update_cs_case_tl:NNnn #2#3 {#4} {#5}
          } ,
        #1+ .code:n =
          {
            \tl_map_inline:nn {##1}
              { \seq_if_in:NnF #3 {####1} { \seq_put_right:Nn #3 {####1} } }
            \__xeCJK_update_cs_case_tl:NNnn #2#3 {#4} {#5}
          } ,
        #1- .code:n =
          {
            \tl_map_inline:nn {##1} { \seq_remove_all:Nn #3 {####1} }
            \__xeCJK_update_cs_case_tl:NNnn #2#3 {#4} {#5}
          }
      }
  }
\cs_new_protected:Npn \__xeCJK_update_cs_case_tl:NNnn #1#2#3#4
  {
    \tl_clear:N #1
    \seq_map_inline:Nn #2 { \tl_put_right:Nn #1 { {##1} {#3} } }
    #4
  }
\xeCJK_cs_case_keys_define:nNNnn { NoBreakCS }
  \l__xeCJK_no_break_cs_case_tl \l__xeCJK_no_break_cs_seq { } { }
\NewDocumentCommand \xeCJKnobreak { }
  {
    \bool_set_true:N \l__xeCJK_tmp_bool
    \int_while_do:nNnn \tex_lastnodetype:D = \c__xeCJK_glue_node
      {
        \bool_if:NTF \l__xeCJK_tmp_bool
          {
            \bool_set_false:N \l__xeCJK_tmp_bool
            \skip_set_eq:NN \l__xeCJK_last_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
          {
            \__xeCJK_if_last_glue:TF
              {
                \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_last_skip }
  }
\keys_define:nn { xeCJK / options }
  {
    CheckSingle .choice: ,
    CheckSingle / true  .code:n =
      {
        \cs_if_eq:NNF \xeCJK_CJK_and_CJK:N \xeCJK_check_single:Nw
          {
            \cs_set_eq:NN \__xeCJK_check_single_save:N \xeCJK_CJK_and_CJK:N
            \cs_set_eq:NN \xeCJK_CJK_and_CJK:N \xeCJK_check_single:Nw
          }
      } ,
    CheckSingle / false .code:n =
      {
        \cs_if_eq:NNT \xeCJK_CJK_and_CJK:N \xeCJK_check_single:Nw
          { \cs_set_eq:NN  \xeCJK_CJK_and_CJK:N \__xeCJK_check_single_save:N }
      } ,
    CheckSingle      .default:n = { true } ,
    CJKchecksingle      .meta:n = { CheckSingle = true }
  }
\keys_define:nn { xeCJK / options }
  {
    WidowPenalty .int_set:N = \l__xeCJK_widow_penalty_int ,
    WidowPenalty .default:n = { 10 000 }
  }
\cs_new_protected:Npn \xeCJK_widow_penalty:
  { \tex_penalty:D \l__xeCJK_widow_penalty_int }
\cs_new_protected:Npn \xeCJK_check_single:Nw #1
  {
    \group_align_safe_begin:
    \peek_catcode:NTF \c_catcode_letter_token
      { \xeCJK_check_single:NNw #1 }
      {
        \token_if_other:NTF \l_peek_token
          { \xeCJK_check_single:NNw }
          { \__xeCJK_check_single_end:N }
        #1
      }
  }
\cs_new_protected:Npn \__xeCJK_check_single_end:N
  {
    \group_align_safe_end:
    \__xeCJK_check_single_save:N
  }
\cs_new_protected:Npn \xeCJK_check_single:NNw #1#2
  {
    \xeCJK_peek_catcode_ignore_spaces:NTF \c_catcode_letter_token
      {
        \bool_if:NTF \l__xeCJK_peek_ignore_spaces_bool
          {
            \bool_if:NTF \l__xeCJK_reserve_space_bool
              { \__xeCJK_check_single_end:N #1 #2 ~ }
              { \__xeCJK_check_single_space:NN #1#2 }
          }
          { \__xeCJK_check_single_end:N #1 #2 }
      }
      {
        \token_if_other:NTF \l_peek_token
          {
            \bool_if:NTF \l__xeCJK_peek_ignore_spaces_bool
              { \__xeCJK_check_single_space:NN }
              { \__xeCJK_check_single_end:N }
          }
          {
            \bool_if:NTF \l__xeCJK_peek_ignore_spaces_bool
              { \__xeCJK_check_single_aux:nNNw { ~ } }
              { \__xeCJK_check_single_aux:nNNw { } }
          }
        #1 #2
      }
  }
\cs_new_protected:Npn \__xeCJK_check_single_aux:nNNw #1#2#3
  {
    \token_if_cs:NTF \l_peek_token
      { \xeCJK_check_single_cs:NNn }
      { \xeCJK_check_single_end:NNnw }
    #2 #3 {#1}
  }
\cs_new_protected:Npn \__xeCJK_check_single_end_aux:NNn #1#2#3
  { \__xeCJK_check_single_end:N #1 #2 #3 }
\cs_new_eq:NN \xeCJK_check_single_end:NNnw \__xeCJK_check_single_end_aux:NNn
\cs_new_protected:Npn \__xeCJK_check_single_end_equation:NNnw
  {
    \token_if_math_toggle:NTF \l_peek_token
      { \xeCJK_check_single_equation:NNnNw }
      { \__xeCJK_check_single_end_aux:NNn }
  }
\keys_define:nn { xeCJK / options }
  {
    PlainEquation .choice: ,
    PlainEquation / true  .code:n =
      {
        \cs_set_eq:NN \xeCJK_check_single_end:NNnw
                      \__xeCJK_check_single_end_equation:NNnw
      } ,
    PlainEquation / false .code:n =
      {
        \cs_set_eq:NN \xeCJK_check_single_end:NNnw
                      \__xeCJK_check_single_end_aux:NNn
      } ,
    PlainEquation      .default:n = { true } ,
  }
\cs_new_protected:Npn \__xeCJK_check_single_space:NN #1#2
  {
    \xeCJK_if_CJK_class:NTF #2
      {
        \xeCJK_if_CJK_class:NTF \l_peek_token
          { \__xeCJK_check_single_end:N #1 #2 }
          { \__xeCJK_check_single_end:N #1 #2 ~ }
      }
      { \__xeCJK_check_single_end:N #1 #2 ~ }
  }
\cs_new_protected:Npn \xeCJK_check_single_equation:NNnNw #1#2#3#4
  {
    \peek_catcode:NTF \c_math_toggle_token
      {
        \xeCJK_widow_penalty: \__xeCJK_check_single_end:N #1
        \xeCJK_make_node:n { CJK-widow } #2 #4
      }
      { \__xeCJK_check_single_end:N #1 #2#3#4 }
  }
\cs_new_protected:Npn \xeCJK_check_single_cs:NNn #1#2#3
  {
    \token_case_meaning:NoF \l_peek_token
      { \l__xeCJK_check_single_cs_case_tl }
      { \use_iii:nnn }
      { \xeCJK_check_single_env:nnNn }
      {
        \xeCJK_widow_penalty:
        \__xeCJK_check_single_end:N #1
        \xeCJK_make_node:n { CJK-widow } #2#3
      }
      { \__xeCJK_check_single_end:N #1 #2#3 }
  }
\tl_new:N \l__xeCJK_check_single_cs_case_tl
\cs_new_protected:Npn \xeCJK_check_single_env:nnNn #1#2#3#4
  {
    \str_case_e:noTF {#4}
      { \l__xeCJK_inline_env_case_tl }
      {#2}
      {#1}
    #3 {#4}
  }
\prg_generate_conditional_variant:Nnn \str_case_e: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 }
  {
    \tl_concat:NNN \l__xeCJK_check_single_cs_case_tl
      \l__xeCJK_new_line_cs_case_tl \l__xeCJK_env_cs_case_tl
  }
\xeCJK_cs_case_keys_define:nNNnn { EnvCS }
  \l__xeCJK_env_cs_case_tl \l__xeCJK_env_cs_seq
  { \use:n }
  {
    \tl_concat:NNN \l__xeCJK_check_single_cs_case_tl
      \l__xeCJK_new_line_cs_case_tl \l__xeCJK_env_cs_case_tl
  }
\keys_define:nn { xeCJK / options }
  {
    InlineEnv       .code:n =
      {
        \seq_set_from_clist:Nn \l__xeCJK_inline_env_seq {#1}
        \__xeCJK_update_inline_env_case_tl:
      } ,
    InlineEnv+      .code:n =
      {
        \clist_map_inline:nn {#1}
          {
            \seq_if_in:NnF \l__xeCJK_inline_env_seq {##1}
              { \seq_put_right:Nn \l__xeCJK_inline_env_seq {##1} }
          }
        \__xeCJK_update_inline_env_case_tl:
      } ,
    InlineEnv-      .code:n =
      {
        \clist_map_inline:nn {#1}
          { \seq_remove_all:Nn \l__xeCJK_inline_env_seq {##1} }
        \__xeCJK_update_inline_env_case_tl:
      }
  }
\seq_new:N \l__xeCJK_inline_env_seq
\cs_new_protected:Npn \__xeCJK_update_inline_env_case_tl:
  {
    \tl_clear:N \l__xeCJK_inline_env_case_tl
    \seq_map_inline:Nn \l__xeCJK_inline_env_seq
      { \tl_put_right:Nn \l__xeCJK_inline_env_case_tl { {##1} { } } }
  }
\tl_new:N \l__xeCJK_inline_env_case_tl
\seq_new:N \g__xeCJK_CJK_sub_class_seq
\NewDocumentCommand \xeCJKDeclareSubCJKBlock
  { s > { \TrimSpaces } m m }
  {
    \xeCJK_declare_sub_char_class:nen { CJK } {#2} {#3}
    \IfBooleanT {#1} { \xeCJKResetPunctClass }
  }
\@onlypreamble \xeCJKDeclareSubCJKBlock
\bool_new:N \l__xeCJK_sub_cancel_bool
\NewDocumentCommand \xeCJKCancelSubCJKBlock { s m }
  {
    \bool_if:NF \l__xeCJK_sub_cancel_bool
      {
        \bool_set_true:N \l__xeCJK_sub_cancel_bool
        \__xeCJK_sub_restore_or_cancel:e {#2}
        \IfBooleanT {#1} { \xeCJKResetPunctClass }
      }
  }
\NewDocumentCommand \xeCJKRestoreSubCJKBlock { s m }
  {
    \bool_if:NT \l__xeCJK_sub_cancel_bool
      {
        \bool_set_false:N \l__xeCJK_sub_cancel_bool
        \__xeCJK_sub_restore_or_cancel:e {#2}
        \IfBooleanT {#1} { \xeCJKResetPunctClass }
      }
  }
\cs_new_protected:Npn \__xeCJK_sub_restore_or_cancel:n #1
  {
    \clist_map_inline:nn {#1}
      {
        \int_if_exist:cTF { \__xeCJK_class_csname:n { CJK/##1 } }
          {
            \xeCJK_declare_char_class:nn
              { CJK \bool_if:NF \l__xeCJK_sub_cancel_bool { /##1 } }
              { \use:c { g__xeCJK_CJK/##1_range_clist } }
          }
          { \__xeCJK_error:nx { SubBlock-undefined } {##1} }
      }
  }
\cs_generate_variant:Nn \__xeCJK_sub_restore_or_cancel:n { e }
\__xeCJK_msg_new:nn { SubBlock-undefined }
  {
    The~CJK~sub~block~`#1'~is~undefined.\\\\
    Try~to~use~\token_to_str:N \xeCJKDeclareSubCJKBlock \
    to~declare~it.
  }
\cs_new_protected:Npn \xeCJK_declare_sub_char_class:nnn #1#2#3
  {
    \int_if_exist:cF { \__xeCJK_class_csname:n { #1/#2 } }
      {
        \xeCJK_new_class:n { #1/#2 }
        \__xeCJK_set_sub_class_toks:nn {#1} {#2}
        \xeCJK_new_sub_key:n {#2}
      }
    \xeCJK_declare_char_class:nn { #1/#2 } {#3}
  }
\cs_generate_variant:Nn \xeCJK_declare_sub_char_class:nnn { ne }
\cs_new_protected:Npn \__xeCJK_set_sub_class_toks:nn #1#2
  {
    \seq_map_inline:Nn \g__xeCJK_base_class_seq
      {
        \xeCJK_copy_inter_class_toks:nnnn { #1/#2 } {##1} {#1}  {##1}
        \xeCJK_copy_inter_class_toks:nnnn {##1} { #1/#2 } {##1} {#1}
        \str_if_eq:nnTF {##1} { CJK }
          {
            \xeCJK_pre_inter_class_toks:nnn {##1} { #1/#2 }
              { \__xeCJK_switch_font:nn {#1} {#2} }
          }
          {
            \xeCJK_replace_inter_class_toks:nnnn {##1} { #1/#2 }
              { \xeCJK_fallback_symbol:NN }
              {
                \__xeCJK_switch_font:nn {#1} {#2}
                \xeCJK_fallback_symbol:NN
              }
          }
      }
    \xeCJK_copy_inter_class_toks:nnnn { #1/#2 } { #1/#2 } {#1} {#1}
    \seq_map_inline:Nn \g__xeCJK_CJK_sub_class_seq
      {
        \xeCJK_copy_inter_class_toks:nnnn { #1/#2  } { #1/##1 } {#1} {#1}
        \xeCJK_copy_inter_class_toks:nnnn { #1/##1 } { #1/#2  } {#1} {#1}
        \xeCJK_pre_inter_class_toks:nnn { #1/#2 } { #1/##1 }
          { \__xeCJK_switch_font:nn {#2} {##1} }
        \xeCJK_pre_inter_class_toks:nnn { #1/##1 } { #1/#2 }
          { \__xeCJK_switch_font:nn {##1} {#2} }
      }
    \seq_gput_right:Nn \g__xeCJK_CJK_sub_class_seq {#2}
    \__xeCJK_save_CJK_class:n { #1/#2 }
    \clist_map_inline:nn { CJK , FullLeft , FullRight , HangulJamo }
      {
        \xeCJK_pre_inter_class_toks:nnn { #1/#2 } {##1}
          { \__xeCJK_switch_font:nn {#2} {#1} }
      }
  }
\cs_if_exist:NF \tex_XeTeXglyphbounds:D
  {
    \__xeCJK_msg_new:nn { XeTeX-too-old }
      {
        \token_to_str:N \tex_XeTeXglyphbounds:D \ is~not~defined.\\
        CJK~punctuation~kerning~will~not~be~available.\\\\
        You~have~to~update~XeTeX~to~the~version~0.9995.0~or~later.
      }
    \__xeCJK_error:n { XeTeX-too-old }
    \AtEndOfPackage
      {
        \keys_define:nn { xeCJK / options }
          {
            PunctStyle .code:n =
              { \__xeCJK_error:nx { punct-style-unknown } {#1} }
          }
        \seq_gclear:N \g__xeCJK_punct_style_seq
        \__xeCJK_set_punct_style:n { plain }
      }
  }
\NewDocumentCommand \xeCJKsetwidth { s m m }
  {
    \IfBooleanTF {#1}
      {
        \tl_map_inline:en {#2}
          { \tl_gset:cn { g__xeCJK_punct_bound_width/##1/tl } {#3} }
      }
      {
        \tl_map_inline:en {#2}
          { \tl_gset:cn { g__xeCJK_punct_width/##1/tl } {#3} }
      }
  }
\@onlypreamble \xeCJKsetwidth
\cs_generate_variant:Nn \tl_map_inline:nn { e }
\NewDocumentCommand \xeCJKsetkern { m m m }
  { \tl_gset:cn { g__xeCJK_punct/kern/#1/#2/tl } {#3} }
\@onlypreamble \xeCJKsetkern
\tl_const:Nn \c__xeCJK_left_tl  { left }
\tl_const:Nn \c__xeCJK_right_tl { right }
\keys_define:nn { xeCJK / options }
  {
    AllowBreakBetweenPuncts .choice: ,
    AllowBreakBetweenPuncts / true  .code:n =
      {
        \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_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} } ,
    KaiMingPunct+ .code:n = { \__xeCJK_add_special_punct:nn { mixed_width } {#1} } ,
    KaiMingPunct- .code:n = { \__xeCJK_sub_special_punct:nn { mixed_width } {#1} } ,
    LongPunct     .code:n = { \__xeCJK_set_special_punct:nn { long } {#1} } ,
    LongPunct+    .code:n = { \__xeCJK_add_special_punct:nn { long } {#1} } ,
    LongPunct-    .code:n = { \__xeCJK_sub_special_punct:nn { long } {#1} } ,
    MiddlePunct   .code:n = { \__xeCJK_set_special_punct:nn { middle } {#1} } ,
    MiddlePunct+  .code:n = { \__xeCJK_add_special_punct:nn { middle } {#1} } ,
    MiddlePunct-  .code:n = { \__xeCJK_sub_special_punct:nn { middle } {#1} } ,
    PunctWidth      .tl_gset:N = \g__xeCJK_punct_width_tl ,
    PunctBoundWidth .tl_gset:N = \g__xeCJK_punct_bound_width_tl ,
    PunctWidth      .value_required:n = true ,
    PunctBoundWidth .value_required:n = true ,
    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 } ,
    RubberPunctSkip / plus  .code:n =
      { \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 } ,
    RubberPunctSkip / false .code:n =
      { \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
\clist_gset:Nn \g__xeCJK_special_punct_clist { mixed_width , long , middle }
\cs_new:Npn \__xeCJK_special_punct_seq:n #1 { g__xeCJK_special_punct_#1_seq }
\cs_new:Npn \__xeCJK_special_punct_tl:nN #1#2 { g__xeCJK_special_punct_#1_#2_tl }
\clist_map_inline:Nn \g__xeCJK_special_punct_clist
  { \seq_new:c { \__xeCJK_special_punct_seq:n {#1} } }
\cs_new_protected:Npn \__xeCJK_set_special_punct:nn #1#2
  {
    \seq_map_inline:cn { \__xeCJK_special_punct_seq:n {#1} }
      { \cs_undefine:c { \__xeCJK_special_punct_tl:nN {#1} {##1} } }
    \seq_gclear:c { \__xeCJK_special_punct_seq:n {#1} }
    \tl_map_inline:en {#2}
      {
        \tl_new:c { \__xeCJK_special_punct_tl:nN {#1} {##1} }
        \seq_gput_right:cn { \__xeCJK_special_punct_seq:n {#1} } {##1}
      }
  }
\cs_new_protected:Npn \__xeCJK_add_special_punct:nn #1#2
  {
    \tl_map_inline:en {#2}
      {
        \seq_if_in:cnF { \__xeCJK_special_punct_seq:n {#1} } {##1}
          {
            \tl_new:c { \__xeCJK_special_punct_tl:nN {#1} {##1} }
            \seq_gput_right:cn { \__xeCJK_special_punct_seq:n {#1} } {##1}
          }
      }
  }
\cs_new_protected:Npn \__xeCJK_sub_special_punct:nn #1#2
  {
    \tl_map_inline:en {#2}
      {
        \cs_undefine:c { \__xeCJK_special_punct_tl:nN {#1} {##1} }
        \seq_gremove_all:cn { \__xeCJK_special_punct_seq:n {#1} } {##1}
      }
  }
\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 }
      \prg_return_true: \else: \prg_return_false: \fi:
  }
\clist_map_inline:Nn \g__xeCJK_special_punct_clist
  {
    \exp_args:Nc
    \prg_new_conditional:Npnn { __xeCJK_punct_if_#1:N } ##1 { p , T , F , TF }
      {
        \if_cs_exist:w \__xeCJK_special_punct_tl:nN {#1} {##1} \cs_end:
          \prg_return_true: \else: \prg_return_false: \fi:
      }
  }
\cs_new:Npn \__xeCJK_punct_csname:n #1
  { c__xeCJK_\l_xeCJK_current_punct_font_tl/\l_xeCJK_punct_style_tl/#1/tl }
\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
  { \use:c { \__xeCJK_punct_csname:n { dim/#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
  { \use:c { \__xeCJK_punct_csname:n { skip/plus/#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
  { \__xeCJK_save_punct_width_aux:nnnn { dim } {#1} { #1/#2 } }
\cs_new_protected:Npn \__xeCJK_save_punct_dim:nNNn #1#2#3
  { \__xeCJK_save_punct_width_aux:nnnn { dim } {#1} { #1/#2/#3 } }
\cs_new_protected:Npn \__xeCJK_save_punct_skip:nNNn #1#2#3#4
  {
    \__xeCJK_save_punct_width_aux:nnnn { skip } {#1} { #1/#2/#3 } {#4}
    \__xeCJK_save_punct_width_aux:nnnn { skip } {#1} { plus/#1/#2/#3 } {#4}
    \__xeCJK_save_punct_width_aux:nnnn { skip } {#1} { minus/#1/#2/#3 } {#4}
  }
\cs_new_protected:Npn \__xeCJK_save_punct_skip:nNNnnn #1#2#3#4#5#6
  {
    \exp_last_unbraced:Ne
    \__xeCJK_save_punct_skip_aux:nnnnn
      {
        {#1}
        { #1/#2/#3 }
        { \dim_eval:n {#4} }
        { \dim_max:nn { \c_zero_dim } {#5} }
        { \dim_max:nn { \c_zero_dim } {#6} }
      }
  }
\cs_new_protected:Npn \__xeCJK_save_punct_skip_aux:nnnnn #1#2#3#4#5
  {
    \__xeCJK_save_punct_width_aux:nnnn { skip } {#1}
      {#2}         { #3 ~ plus ~ #4 ~ minus ~ #5 ~ }
    \__xeCJK_save_punct_width_aux:nnnn { skip } {#1}
      { plus/#2 }  { #3 ~ plus ~ #4 ~ }
    \__xeCJK_save_punct_width_aux:nnnn { skip } {#1}
      { minus/#2 } { #3 ~ minus ~ #5 ~ }
  }
\cs_new_protected:Npn \__xeCJK_save_punct_width_aux:nnnn #1#2#3#4
  {
    \__xeCJK_save_punct_width_aux:cen
      { \__xeCJK_punct_csname:n { #1/#3 } }
      { \use:c { #1_eval:n } {#4} }
      {#2}
  }
\cs_new_protected:Npn \__xeCJK_save_punct_width_aux:Nnn #1#2#3
  {
    \tl_const:Nn #1 {#2}
    \str_if_eq:nnT {#3} { glue }
      { \prop_gput:Nnn \g__xeCJK_punct_skip_prop {#2} { } }
  }
\prop_new:N \g__xeCJK_punct_skip_prop
\prop_gput:Non \g__xeCJK_punct_skip_prop { \skip_use:N \c_zero_skip } { }
\cs_generate_variant:Nn \__xeCJK_save_punct_width_aux:Nnn { ce }
\cs_new_eq:NN \__xeCJK_use_dim_or_skip:nNN \__xeCJK_use_punct_skip:nNN
\DeclareObjectType { xeCJK / punctuation } { 0 }
\DeclareTemplateInterface { xeCJK / punctuation } { basic } { 0 }
  {
    enabled-global-setting  : boolean = true ,
    fixed-punct-width       : length  = \c_max_dim ,
    fixed-punct-ratio       : real    = \c_one_fp ,
    mixed-punct-width       : length  = \KeyValue { fixed-punct-width } ,
    mixed-punct-ratio       : real    = \KeyValue { fixed-punct-ratio } ,
    middle-punct-width      : length  = \KeyValue { fixed-punct-width } ,
    middle-punct-ratio      : real    = \KeyValue { fixed-punct-ratio } ,
    fixed-margin-width      : length  = \c_max_dim ,
    fixed-margin-ratio      : real    = \c_one_fp ,
    mixed-margin-width      : length  = \KeyValue { fixed-margin-width } ,
    mixed-margin-ratio      : real    = \KeyValue { fixed-margin-ratio } ,
    middle-margin-width     : length  = \KeyValue { fixed-margin-width } ,
    middle-margin-ratio     : real    = \KeyValue { fixed-margin-ratio } ,
    bound-punct-width       : length  = \c_max_dim ,
    bound-punct-ratio       : real    = \c_nan_fp ,
    bound-margin-width      : length  = \c_max_dim ,
    bound-margin-ratio      : real    = \c_zero_fp ,
    enabled-hanging         : boolean = false ,
    add-min-bound-to-margin : boolean = false ,
    optimize-margin         : boolean = false ,
    margin-minimum          : length  = \c_zero_dim ,
    enabled-kerning         : boolean = true ,
    min-bound-to-kerning    : boolean = false ,
    kerning-total-width     : length  = \c_max_dim ,
    kerning-total-ratio     : real    = 0.75 ,
    optimize-kerning        : boolean = false ,
    same-align-margin       : length  = \c_max_dim ,
    same-align-ratio        : real    = \c_nan_fp ,
    different-align-margin  : length  = \c_max_dim ,
    different-align-ratio   : real    = \c_nan_fp ,
    kerning-margin-width    : length  = \c_max_dim ,
    kerning-margin-ratio    : real    = \c_one_fp ,
    kerning-margin-minimum  : length  = \c_zero_dim
  }
\DeclareTemplateCode { xeCJK / punctuation } { basic } { 0 }
  {
    enabled-global-setting  = \l__xeCJK_enabled_global_setting_bool ,
    fixed-punct-width       = \l__xeCJK_fixed_punct_width_dim ,
    fixed-punct-ratio       = \l__xeCJK_fixed_punct_ratio_fp ,
    mixed-punct-width       = \l__xeCJK_mixed_punct_width_dim ,
    mixed-punct-ratio       = \l__xeCJK_mixed_punct_ratio_fp ,
    middle-punct-width      = \l__xeCJK_middle_punct_width_dim ,
    middle-punct-ratio      = \l__xeCJK_middle_punct_ratio_fp ,
    fixed-margin-width      = \l__xeCJK_fixed_margin_width_dim ,
    fixed-margin-ratio      = \l__xeCJK_fixed_margin_ratio_fp ,
    mixed-margin-width      = \l__xeCJK_mixed_margin_width_dim ,
    mixed-margin-ratio      = \l__xeCJK_mixed_margin_ratio_fp ,
    middle-margin-width     = \l__xeCJK_middle_margin_width_dim ,
    middle-margin-ratio     = \l__xeCJK_middle_margin_ratio_fp ,
    bound-punct-width       = \l__xeCJK_bound_punct_width_dim ,
    bound-punct-ratio       = \l__xeCJK_bound_punct_ratio_fp ,
    bound-margin-width      = \l__xeCJK_bound_margin_width_dim ,
    bound-margin-ratio      = \l__xeCJK_bound_margin_ratio_fp ,
    enabled-hanging         = \l__xeCJK_enabled_hanging_bool ,
    add-min-bound-to-margin = \l__xeCJK_add_min_bound_to_margin_bool ,
    optimize-margin         = \l__xeCJK_optimize_margin_bool ,
    margin-minimum          = \l__xeCJK_margin_minimum_dim ,
    enabled-kerning         = \l__xeCJK_enabled_kerning_bool ,
    min-bound-to-kerning    = \l__xeCJK_min_bound_to_kerning_bool ,
    kerning-total-width     = \l__xeCJK_kerning_total_width_dim ,
    kerning-total-ratio     = \l__xeCJK_kerning_total_ratio_fp ,
    optimize-kerning        = \l__xeCJK_optimize_kerning_bool ,
    same-align-margin       = \l__xeCJK_same_align_margin_dim ,
    same-align-ratio        = \l__xeCJK_same_align_ratio_fp ,
    different-align-margin  = \l__xeCJK_different_align_margin_dim ,
    different-align-ratio   = \l__xeCJK_different_align_ratio_fp ,
    kerning-margin-width    = \l__xeCJK_kerning_margin_width_dim ,
    kerning-margin-ratio    = \l__xeCJK_kerning_margin_ratio_fp ,
    kerning-margin-minimum  = \l__xeCJK_kerning_margin_minimum_dim
  }
  { \AssignTemplateKeys }
\cs_new_protected: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:Npn \__xeCJK_get_punct_bounds_aux:NN
  {
    \tl_if_eq:NNTF \l_xeCJK_punct_style_tl \c__xeCJK_punct_style_plain_tl
      { \__xeCJK_save_punct_margin_plain:NN }
      { \__xeCJK_save_punct_margin:NN }
  }
\cs_new_protected:Npn \xeCJK_get_punct_bounds:No
  { \exp_last_unbraced:NNo \xeCJK_get_punct_bounds:NN }
\cs_new_protected:Npn \__xeCJK_save_punct_margin_plain:NN #1#2
  {
    \__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 { margin } #1 #2 { \c_zero_dim }
    \__xeCJK_save_punct_dim:nNNn { rule } \c__xeCJK_left_tl  {#2} { \c_zero_dim }
    \__xeCJK_save_punct_dim:nNNn { rule } \c__xeCJK_right_tl {#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 }
  }
\cs_new_protected:Npn \__xeCJK_save_punct_margin:NN #1#2
  {
    \group_begin:
      \xeCJK_select_punct_font:
      \xeCJK_fallback_punct_symbol:NN
      \xeCJK_calc_punct_dimen:N #2
    \group_end:
    \dim_set:Nn \l__xeCJK_bound_dim
      { \__xeCJK_use_punct_dim:nNN { bound } #1 #2 }
    \tl_if_eq:NNTF #1 \c__xeCJK_right_tl
      { \tl_set_eq:NN \l__xeCJK_reverse_tl \c__xeCJK_left_tl }
      { \tl_set_eq:NN \l__xeCJK_reverse_tl \c__xeCJK_right_tl }
    \dim_set:Nn \l__xeCJK_reverse_bound_dim
      { \__xeCJK_use_punct_dim:nNN { bound } \l__xeCJK_reverse_tl #2 }
    \UseInstance { xeCJK / punctuation } { \l_xeCJK_punct_style_tl }
    \xeCJK_punct_margin_process:NN #1 #2
    \xeCJK_punct_offset_process:NN #1 #2
    \__xeCJK_punct_if_long:NT #2
      { \__xeCJK_long_punct_kerning:N #2 }
  }
\tl_new:N \l__xeCJK_reverse_tl
\dim_new:N \l__xeCJK_bound_dim
\dim_new:N \l__xeCJK_reverse_bound_dim
\cs_new_protected:Npn \__xeCJK_long_punct_kerning:N #1
  {
    \dim_set:Nn \l__xeCJK_tmp_dim
      {
        \dim_max:nn
          { \l__xeCJK_bound_dim + \l__xeCJK_reverse_bound_dim }
          { \c_zero_dim }
      }
    \__xeCJK_save_punct_dim:nNNn { bound_width } #1 #1 { \l__xeCJK_tmp_dim }
    \dim_set:Nn \l__xeCJK_tmp_dim
      {
        \str_case:nnTF {#1}
          { { ^^^^2025 } { } { ^^^^2026 } { } }
          { \c_zero_dim }
          { -\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 }
    \dim_add:Nn \l__xeCJK_tmp_dim
      { \dim_max:nn { \l__xeCJK_bound_dim } { \c_zero_dim } }
    \__xeCJK_save_punct_dim:nNNn  { bound_kern } #1 #1 { \l__xeCJK_tmp_dim }
    \__xeCJK_save_punct_skip:nNNn { bound_kern } #1 #1 { \l__xeCJK_tmp_dim }
  }
\cs_new_protected:Npn \xeCJK_get_punct_kerning:NN #1#2
  {
    \tl_if_exist:cF { \__xeCJK_punct_csname:n { dim/kern/#1/#2 } }
      {
        \tl_if_eq:NNTF \l_xeCJK_punct_style_tl \c__xeCJK_punct_style_plain_tl
          { \__xeCJK_save_punct_kerning_plain:NN }
          { \__xeCJK_save_punct_kerning:NN }
          #1 #2
      }
  }
\cs_new_protected:Npn \xeCJK_get_punct_kerning:oN
  { \exp_after:wN \xeCJK_get_punct_kerning:NN }
\cs_new_protected:Npn \__xeCJK_save_punct_kerning_plain:NN #1#2
  {
    \__xeCJK_save_punct_dim:nNNn { 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 { bound_kern } #1 #2 { \c_zero_skip }
  }
\cs_new_protected:Npn \__xeCJK_save_punct_kerning:NN
  {
    \UseInstance { xeCJK / punctuation } { \l_xeCJK_punct_style_tl }
    \xeCJK_punct_kerning_process:NN
  }
\cs_new_protected:Npn \xeCJK_punct_margin_process:NN #1#2
  {
    \dim_set:Nn \l__xeCJK_tmp_dim
      {
        \bool_if:NTF \l__xeCJK_enabled_global_setting_bool
          {
            \cs_if_exist_use:cF { g__xeCJK_punct_width/#2/tl }
              {
                \tl_if_empty:NTF \g__xeCJK_punct_width_tl
                  { \__xeCJK_calc_punct_width:N #2 }
                  { \g__xeCJK_punct_width_tl }
              }
          }
          { \__xeCJK_calc_punct_width:N #2 }
      }
    \dim_set:Nn \l__xeCJK_margin_dim
      {
        \dim_max:nn
          { \l__xeCJK_margin_minimum_dim }
          {
            \dim_compare:nNnTF \l__xeCJK_tmp_dim < \c_max_dim
              {
                \__xeCJK_punct_if_middle:NTF #2
                  {
                    (   \l__xeCJK_tmp_dim
                      - ( \__xeCJK_use_punct_dim:nN { dimen } #2 )
                    ) / 2
                  }
                  {
                    \bool_if:NTF \l__xeCJK_optimize_margin_bool
                      {
                        \dim_max:nn
                          {
                            \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 )
                      }
                  }
              }
              {
                \bool_if:NTF \l__xeCJK_optimize_margin_bool
                  { \dim_min:nn { \l__xeCJK_bound_dim } }
                  { \use:n }
                  { \__xeCJK_calc_margin_width:N #2 }
              }
          }
      }
    \__xeCJK_save_punct_dim:nNNn { margin } #1 #2 { \l__xeCJK_margin_dim }
  }
\dim_new:N \l__xeCJK_margin_dim
\cs_new: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: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 }
          {
            \__xeCJK_dim_ratio:Nn \l__xeCJK_middle_margin_ratio_fp
              { ( \l__xeCJK_bound_dim + \l__xeCJK_reverse_bound_dim ) / 2 }
          }
      }
      {
        \__xeCJK_punct_if_mixed_width:NTF #1
          { \__xeCJK_margin_width_or_ratio:n { mixed } }
          { \__xeCJK_margin_width_or_ratio:n { fixed } }
      }
  }
\cs_new:Npn \__xeCJK_dim_ratio:Nn #1#2
  { \fp_to_dim:n { #1 \dim_to_fp:n {#2} } }
\cs_generate_variant:Nn \__xeCJK_dim_ratio:Nn { c }
\cs_new_protected:Npn \xeCJK_punct_offset_process:NN #1#2
  {
    \dim_set:Nn \l__xeCJK_tmp_dim
      {
        \bool_if:NTF \l__xeCJK_enabled_global_setting_bool
          {
            \cs_if_exist_use:cF { g__xeCJK_punct_bound_width/#2/tl }
              {
                \tl_if_empty:NTF \g__xeCJK_punct_bound_width_tl
                  { \__xeCJK_punct_width_or_ratio:nN { bound } #2 }
                  { \g__xeCJK_punct_bound_width_tl }
              }
          }
          { \__xeCJK_punct_width_or_ratio:nN { bound } #2 }
      }
    \dim_set:Nn \l__xeCJK_tmp_dim
      {
        \bool_if:NTF \l__xeCJK_enabled_hanging_bool
          { \use:n }
          { \dim_max:nn { \l__xeCJK_margin_minimum_dim } }
          {
            \dim_compare:nNnTF \l__xeCJK_tmp_dim < \c_max_dim
              {
                \__xeCJK_punct_if_middle:NTF #2
                  {
                      \l__xeCJK_tmp_dim
                    - \l__xeCJK_margin_dim
                    - ( \__xeCJK_use_punct_dim:nN { dimen } #2 )
                  }
                  {
                      \l__xeCJK_tmp_dim
                    - \l__xeCJK_reverse_bound_dim
                    - ( \__xeCJK_use_punct_dim:nN { dimen } #2 )
                  }
              }
              {
                \bool_if:NTF \l__xeCJK_optimize_margin_bool
                  { \dim_min:nn { \l__xeCJK_bound_dim } }
                  { \use:n }
                  { \__xeCJK_margin_width_or_ratio:n { bound } }
              }
          }
      }
    \__xeCJK_save_punct_dim:nNNn { offset } #1 #2
      { \l__xeCJK_tmp_dim }
    \__xeCJK_save_punct_dim:nNNn { rule } #1 #2
      { \l__xeCJK_tmp_dim - \l__xeCJK_bound_dim }
    \__xeCJK_save_punct_dim:nNNn { rule } \l__xeCJK_reverse_tl #2
      { \l__xeCJK_tmp_dim - \l__xeCJK_reverse_bound_dim }
    \__xeCJK_save_punct_dim:nNNn { glue } #1 #2
      { \l__xeCJK_margin_dim - \l__xeCJK_tmp_dim }
    \__xeCJK_save_punct_skip:nNNnnn { glue } #1 #2
      { \l__xeCJK_margin_dim - \l__xeCJK_tmp_dim }
      {
        \__xeCJK_punct_if_middle:NTF #2
          {
            ( \__xeCJK_use_punct_dim:nN { width } #2 -
              \__xeCJK_use_punct_dim:nN { dimen } #2 ) / 2
            - \l__xeCJK_margin_dim
          }
          { \l__xeCJK_bound_dim - \l__xeCJK_margin_dim }
      }
      {
        \__xeCJK_punct_if_middle:NTF #2
          { \l__xeCJK_margin_dim / 2 }
          { \l__xeCJK_margin_dim - \l__xeCJK_reverse_bound_dim }
      }
  }
\cs_new:Npn \__xeCJK_punct_width_or_ratio:nN #1#2
  {
    \dim_compare:nNnTF { \use:c { l__xeCJK_#1_punct_width_dim } } < \c_max_dim
      { \use:c { l__xeCJK_#1_punct_width_dim } }
      {
        \fp_if_nan:nTF { \use:c { l__xeCJK_#1_punct_ratio_fp } }
          { \c_max_dim }
          {
            \__xeCJK_dim_ratio:cn
              { l__xeCJK_#1_punct_ratio_fp }
              { \__xeCJK_use_punct_dim:nN { width } #2 }
          }
      }
  }
\cs_new:Npn \__xeCJK_margin_width_or_ratio:n #1
  {
    \dim_compare:nNnTF { \use:c { l__xeCJK_#1_margin_width_dim } } < \c_max_dim
      { \use:c { l__xeCJK_#1_margin_width_dim } }
      {
        \__xeCJK_dim_ratio:cn
          { l__xeCJK_#1_margin_ratio_fp }
          { \l__xeCJK_bound_dim }
      }
    \bool_if:NT \l__xeCJK_add_min_bound_to_margin_bool
      { + \dim_min:nn \l__xeCJK_bound_dim \l__xeCJK_reverse_bound_dim }
  }
\cs_new_protected:Npn \xeCJK_punct_kerning_process:NN #1#2
  {
    \dim_set:Nn \l__xeCJK_margin_dim
      { \__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
      { \bool_set_false:N \l__xeCJK_enabled_kerning_bool }
      {
        \__xeCJK_punct_if_long:NT #2
          { \bool_set_false:N \l__xeCJK_enabled_kerning_bool }
      }
    \dim_set:Nn \l__xeCJK_kerning_margin_dim
      {
        \bool_if:NTF \l__xeCJK_enabled_global_setting_bool
          {
            \cs_if_exist_use:cF { g__xeCJK_punct/kern/#1/#2/tl }
              { \__xeCJK_punct_kerning_process_aux:NN #1 #2 }
          }
          { \__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
          { \__xeCJK_save_kerning:nnnNN { bound_kern } { offset } { bound } }
          { \__xeCJK_save_kerning:nnNN  { bound_kern } { offset } }
      }
      {
        \__xeCJK_punct_if_right:NTF #2
          { \__xeCJK_save_kerning:nnNN  { bound_kern } { bound } }
          { \__xeCJK_save_kerning:nnnNN { bound_kern } { bound } { offset } }
      }
      #1 #2
  }
\cs_new: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_margin_dim }
  }
\dim_new:N \l__xeCJK_minimum_bound_dim
\dim_new:N \l__xeCJK_kerning_margin_dim
\cs_new_protected:Npn \__xeCJK_save_kerning:nnNN #1#2
  { \__xeCJK_save_kerning:nnnNN {#1} {#2} {#2} }
\cs_new_protected: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 #4 )
        - ( \__xeCJK_use_punct_dim:nNN {#3} \c__xeCJK_left_tl  #5 )
      }
    \__xeCJK_save_punct_dim:nNNn {#1} #4 #5 { \l__xeCJK_tmp_dim }
    \__xeCJK_save_punct_skip:nNNnnn {#1} #4 #5
      { \l__xeCJK_tmp_dim }
      { \l__xeCJK_margin_dim - \l__xeCJK_kerning_margin_dim }
      { \l__xeCJK_kerning_margin_dim - \l__xeCJK_minimum_bound_dim }
  }
\cs_new:Npn \__xeCJK_original_kerning_margin:NN #1#2
  {
    \dim_eval:n
      {
        \__xeCJK_use_punct_dim:nNN
          { \__xeCJK_punct_if_right:NTF #1 { margin } { bound } } \c__xeCJK_right_tl #1
        +
        \__xeCJK_use_punct_dim:nNN
          { \__xeCJK_punct_if_right:NTF #2 { bound } { margin } } \c__xeCJK_left_tl #2
      }
  }
\cs_new:Npn \__xeCJK_calc_kerning_margin:NN #1#2
  {
    \dim_max:nn
      { \l__xeCJK_kerning_margin_minimum_dim }
      {
        \bool_if:NTF \l__xeCJK_min_bound_to_kerning_bool
          { \l__xeCJK_minimum_bound_dim }
          {
            \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: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_if_nan:nTF { \l__xeCJK_kerning_total_ratio_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
              {
                \__xeCJK_dim_ratio:Nn \l__xeCJK_kerning_total_ratio_fp
                  {
                    \__xeCJK_use_punct_dim:nN { width } #1 +
                    \__xeCJK_use_punct_dim:nN { width } #2
                  }
              }
          }
      }
      #1 #2
  }
\cs_new:Npn \__xeCJK_kerning_width_or_ratio:nNN #1#2#3
  {
    \dim_compare:nNnTF { \use:c { l__xeCJK_#1_align_margin_dim } } < \c_max_dim
      { \use:c { l__xeCJK_#1_align_margin_dim } }
      {
        \fp_if_nan:nTF { \use:c { l__xeCJK_#1_align_ratio_fp } }
          {
            \dim_compare:nNnTF \l__xeCJK_kerning_margin_width_dim < \c_max_dim
              { \l__xeCJK_kerning_margin_width_dim \use_none:n }
              { \__xeCJK_dim_ratio:Nn \l__xeCJK_kerning_margin_ratio_fp }
          }
          { \__xeCJK_dim_ratio:cn { l__xeCJK_#1_align_ratio_fp } }
          { \l__xeCJK_margin_dim }
      }
  }
\cs_new:Npn \__xeCJK_punct_min_bound:NN #1#2
  {
    \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 }
      }
      {
        \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 }
      }
  }
\cs_new:Npn \__xeCJK_calc_kerning_margin:nNN #1#2#3
  {
    \dim_eval:n
      {
          (#1)
        - ( \__xeCJK_use_punct_dim:nNN
              { \__xeCJK_punct_if_right:NTF #2 { bound } { margin } }
              \c__xeCJK_left_tl #2 )
        - ( \__xeCJK_use_punct_dim:nNN
              { \__xeCJK_punct_if_right:NTF #3 { margin } { bound } }
              \c__xeCJK_right_tl #3 )
        - ( \__xeCJK_use_punct_dim:nN { dimen } #2 )
        - ( \__xeCJK_use_punct_dim:nN { dimen } #3 )
      }
  }
\cs_new_protected:Npn \xeCJK_calc_punct_dimen:N #1
  {
    \__xeCJK_save_punct_dim:nNNn { bound } \c__xeCJK_left_tl #1
      { \xeCJK_glyph_bounds:NN 1 #1 }
    \__xeCJK_save_punct_dim:nNNn { bound } \c__xeCJK_right_tl #1
      { \xeCJK_glyph_bounds:NN 3 #1 }
    \__xeCJK_save_punct_dim:nNn { width } #1
      { \tex_fontcharwd:D \tex_font:D `#1 }
    \__xeCJK_save_punct_dim:nNn { dimen } #1
      {
        ( \__xeCJK_use_punct_dim:nN { width } #1 )                 -
        ( \__xeCJK_use_punct_dim:nNN { bound } \c__xeCJK_left_tl  #1 ) -
        ( \__xeCJK_use_punct_dim:nNN { bound } \c__xeCJK_right_tl #1 )
      }
  }
\cs_new:Npn \xeCJK_glyph_bounds:NN #1#2
  { \tex_XeTeXglyphbounds:D #1 ~ \tex_XeTeXcharglyph:D `#2 \exp_stop_f: }
\keys_define:nn { xeCJK / options }
  { PunctStyle .code:n = \exp_args:Nx \__xeCJK_set_punct_style:n {#1} }
\cs_new_protected:Npn \__xeCJK_set_punct_style:n #1
  {
    \IfInstanceExistTF { xeCJK / punctuation } {#1}
      { \tl_set:Nn \l_xeCJK_punct_style_tl {#1} }
      {
        \prop_get:NnNF \c__xeCJK_punct_style_alias_prop
          {#1} \l_xeCJK_punct_style_tl
          { \__xeCJK_error:nx { punct-style-unknown } {#1} }
      }
  }
\prop_const_from_keyval:Nn \c__xeCJK_punct_style_alias_prop
  {
    halfwidth     = banjiao ,
    fullwidth     = quanjiao ,
    mixedwidth    = kaiming ,
    marginkerning = hangmobanjiao ,
    plain         = plain
  }
\tl_new:N \l_xeCJK_punct_style_tl
\tl_const:Nn \c__xeCJK_punct_style_plain_tl { plain }
\__xeCJK_msg_new:nn { punct-style-unknown }
  {
    Punctuation~style~"#1"~is~unknown. \\\\
    The~available~styles~are~listed~as~follow.\\\\
    "plain,~\seq_use:Nnnn \g__xeCJK_punct_style_seq
      { ~and~ } { ,~ } { ,~and~ }".\\
  }
\cs_new_protected:Npn \__xeCJK_trim_spaces:n #1
  {
    \tl_set:Nx \ProcessedArgument
      { \exp_args:Ne \tl_trim_spaces:n {#1} }
  }
\NewDocumentCommand \xeCJKDeclarePunctStyle
  { > { \__xeCJK_trim_spaces:n } m m }
  {
    \IfInstanceExistTF { xeCJK / punctuation } {#1}
      { \__xeCJK_warning:nx { punct-style-already-defined } {#1} }
      { \seq_gput_right:Nn \g__xeCJK_punct_style_seq {#1} }
    \DeclareInstance { xeCJK / punctuation } {#1} { basic } {#2}
  }
\seq_new:N \g__xeCJK_punct_style_seq
\__xeCJK_msg_new:nn { punct-style-already-defined }
  {
    Punctuation~style~"#1"~is~already~defined!. \\\\
    The~existing~style~of~"#1"~will~be~overwritten.\\
  }
\@onlypreamble \xeCJKDeclarePunctStyle
\NewDocumentCommand \xeCJKEditPunctStyle
  { > { \__xeCJK_trim_spaces:n } m m }
  {
    \IfInstanceExistTF { xeCJK / punctuation } {#1}
      { \EditInstance { xeCJK / punctuation } {#1} {#2} }
      { \__xeCJK_error:nx { punct-style-unknown } {#1} }
  }
\@onlypreamble \xeCJKEditPunctStyle
\xeCJKDeclarePunctStyle { quanjiao } { }
\xeCJKDeclarePunctStyle { hangmobanjiao } { enabled-kerning = false }
\xeCJKDeclarePunctStyle { banjiao }
  {
    fixed-punct-ratio   = 0.5  ,
    optimize-margin     = true ,
    kerning-total-ratio = 0.5  ,
    optimize-kerning    = true
  }
\xeCJKDeclarePunctStyle { kaiming }
  {
    fixed-punct-ratio   = 0.5  ,
    mixed-punct-ratio   = 0.8  ,
    optimize-margin     = true ,
    kerning-total-ratio = 0.5  ,
    optimize-kerning    = true
  }
\xeCJKDeclarePunctStyle { CCT }
  {
    fixed-punct-ratio   = 0.7  ,
    optimize-margin     = true ,
    kerning-total-ratio = 0.6  ,
    optimize-kerning    = true
  }
\keys_define:nn { xeCJK / options }
  {
    AutoFallBack .choice: ,
    AutoFallBack / true  .code:n =
      {
        \cs_set_eq:NN \xeCJK_fallback_symbol:NN
                      \__xeCJK_fallback_symbol:NN
        \cs_set_eq:NN \xeCJK_fallback_punct_symbol:NN
                      \__xeCJK_fallback_punct_symbol:NN
        \cs_set_eq:NN \xeCJK_clear_fallback_font:
                      \__xeCJK_clear_fallback_font:
      } ,
    AutoFallBack / false .code:n =
      {
        \xeCJK_cs_clear:N \xeCJK_fallback_symbol:NN
        \xeCJK_cs_clear:N \xeCJK_fallback_punct_symbol:NN
        \xeCJK_cs_clear:N \xeCJK_clear_fallback_font:
      } ,
    AutoFallBack      .default:n = { true } ,
    fallback             .meta:n = { AutoFallBack = true }
  }
\cs_new_protected:Npn \__xeCJK_fallback_symbol:NN #1#2
  {
    \xeCJK_reset_fallback_font:
    \xeCJK_glyph_if_exist:NF #2
      { \__xeCJK_fallback_symbol_aux:NN }
    #1#2
  }
\cs_new_protected:Npn \__xeCJK_fallback_punct_symbol:NN #1#2
  {
    \xeCJK_reset_fallback_font:
    \xeCJK_glyph_if_exist:NF #2
      { \__xeCJK_fallback_punct_symbol_aux:NN }
    #1#2
  }
\cs_new_eq:NN \xeCJK_fallback_symbol:NN       \prg_do_nothing:
\cs_new_eq:NN \xeCJK_fallback_punct_symbol:NN \prg_do_nothing:
\cs_new_protected:Npn \__xeCJK_fallback_symbol_aux:NN
  {
    \__xeCJK_fallback_symbol_aux:nnNN
      { \CJK@family }
      { \l_xeCJK_family_tl }
  }
\cs_new_protected:Npn \__xeCJK_fallback_punct_symbol_aux:NN
  {
    \__xeCJK_fallback_symbol_aux:nnNN
      { \CJK@punctfamily }
      { \l_xeCJK_punct_family_tl }
  }
\cs_new_protected:Npn \__xeCJK_fallback_symbol_aux:nnNN
  {
    \cs_set_protected:Npx \xeCJK_reset_fallback_font:
      {
        \tex_the:D \tex_font:D
        \xeCJK_clear_fallback_font:
      }
    \exp_args:Nee \__xeCJK_fallback_loop:nnNN
  }
\cs_new_protected:Npn \__xeCJK_clear_fallback_font:
  { \cs_set_eq:NN \xeCJK_reset_fallback_font: \prg_do_nothing: }
\cs_new_eq:NN \xeCJK_reset_fallback_font: \prg_do_nothing:
\cs_new_eq:NN \xeCJK_clear_fallback_font: \prg_do_nothing:
\cs_new_protected:Npn \__xeCJK_fallback_loop:nnNN
  {
    \cs_set_eq:NN \__xeCJK_fallback_loop:TF \use_i:nn
    \__xeCJK_fallback_loop:nnnNN { FallBack }
  }
\cs_new_protected:Npn \__xeCJK_fallback_loop:nnnNN #1#2#3
  {
    \xeCJK_select_fallback_font:nnn {#1} {#2} {#3}
    \__xeCJK_fallback_loop:TF
      { \__xeCJK_fallback_loop_aux:nnnNN }
      { \__xeCJK_fallback_missing_glyph:nnnNN }
      {#1} {#2} {#3}
  }
\cs_new_protected:Npn \__xeCJK_fallback_loop_aux:nnnNN #1#2#3#4#5
  {
    \xeCJK_glyph_if_exist:NF #5
      { \__xeCJK_fallback_loop:nnnNN { #1/FallBack } {#2} {#3} }
    #4#5
  }
\cs_new_protected:Npn \__xeCJK_fallback_missing_glyph:nnnNN #1#2#3#4#5
  {
    \__xeCJK_warning:nxxx { missing-glyph } {#1} {#2} {#5}
    #4#5
  }
\cs_new_protected:Npn \xeCJK_select_fallback_font:nnn #1#2
  {
    \__xeCJK_select_fallback_font:cnnn
      { \__xeCJK_font_csname:n { #2/#1 } } {#1} {#2}
  }
\cs_new_protected:Npn \__xeCJK_select_fallback_font:Nnnn #1
  {
    \cs_if_exist:NF #1
      { \__xeCJK_fallback_font_initial:NNnnn }
    #1 \use_none:nnn
  }
\cs_generate_variant:Nn \__xeCJK_select_fallback_font:Nnnn { c }
\cs_new_protected:Npn \__xeCJK_fallback_font_initial:NNnnn #1#2#3#4#5
  {
    \xeCJK_family_if_exist:nTF { #5/#3 }
      { \__xeCJK_font_initial:Nn #1 { #5/#3 } }
      { \__xeCJK_fallback_font_initial_auxi:Nnnn #1 {#5} {#3} {#4} }
    #1
  }
\cs_new_protected:Npn \__xeCJK_fallback_font_initial_auxi:Nnnn #1
  {
    \exp_args:NNe \__xeCJK_fallback_font_initial_auxii:Nnnnn
      #1 { \CJKfamilydefault }
  }
\cs_new_protected:Npn \__xeCJK_fallback_font_initial_auxii:Nnnnn #1#2#3
  {
    \str_if_eq:nnTF {#2} {#3}
      { \__xeCJK_fallback_loop_end:Nnnn }
      { \__xeCJK_fallback_font_initial_auxiii:Nnnn }
      #1 {#2}
  }
\cs_new_protected:Npn \__xeCJK_fallback_font_initial_auxiii:Nnnn #1#2
  {
    \xeCJK_family_if_exist:nTF {#2}
      { \__xeCJK_fallback_font_initial_auxiv:Nnnn }
      { \__xeCJK_fallback_loop_end:Nnnn }
      #1 {#2}
  }
\cs_new_protected:Npn \__xeCJK_fallback_font_initial_auxiv:Nnnn #1#2#3#4
  {
    \__xeCJK_font_initial:Nn #1 {#2}
    \exp_args:Nc \__xeCJK_fallback_font_initial_auxiii:Nnnn
      { \__xeCJK_font_csname:n { #4/#3/FallBack } }
      { #2/FallBack } { #3/FallBack } {#4}
  }
\cs_new_eq:NN \__xeCJK_fallback_loop:TF \use_i:nn
\cs_new_protected:Npn \__xeCJK_fallback_loop_end:Nnnn #1#2#3#4
  { \cs_gset_eq:NN #1 \__xeCJK_fallback_loop_end: }
\cs_new_protected:Npn \__xeCJK_fallback_loop_end:
  { \cs_set_eq:NN \__xeCJK_fallback_loop:TF \use_ii:nn }
\__xeCJK_msg_new:nn { missing-glyph }
  {
    CJKfamily~`\__xeCJK_msg_family_map:n {#2}'~(#1)~
    does~not~contain~glyph~`#3'~
    ( U + \int_to_Hex:n { `#3 } )~\msg_line_context:.
  }
\NewDocumentCommand \setCJKfallbackfamilyfont { m o m }
  {
    \__xeCJK_pass_args:nnnn
      { \xeCJK_set_family_fallback:nnn {#1} } {#2} {#3}
      { }
  }
\cs_new_protected:Npn \xeCJK_set_family_fallback:nnn #1#2#3
  {
    \group_begin:
    \tl_set:Nn \l__xeCJK_fallback_family_tl {#1}
    \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_set:Nn \l__xeCJK_public_options_clist {#2}
    \clist_map_function:nN {#3} \__xeCJK_set_family_fallback:n
    \group_end:
  }
\cs_new_protected:Npn \__xeCJK_set_family_fallback:n #1
  {
    \tl_put_right:Nn \l__xeCJK_fallback_family_tl { /FallBack }
    \__xeCJK_get_sub_features:on \l__xeCJK_fallback_family_tl {#1}
    \clist_concat:NNN \l__xeCJK_sub_font_options_clist
      \l__xeCJK_public_options_clist
      \l__xeCJK_sub_font_options_clist
    \exp_args:Nooo \xeCJK_set_family:nnn
      \l__xeCJK_fallback_family_tl
      \l__xeCJK_sub_font_options_clist
      \l__xeCJK_sub_font_name_tl
  }
\tl_new:N \l__xeCJK_fallback_family_tl
\clist_new:N \l__xeCJK_public_options_clist
\bool_new:N \g__xeCJK_auto_fake_bold_bool
\bool_new:N \g__xeCJK_auto_fake_slant_bool
\fp_new:N \g__xeCJK_embolden_factor_fp
\fp_new:N \g__xeCJK_slant_factor_fp
\keys_define:nn { xeCJK / options }
  {
    AutoFakeBold .choices:nn = { true , false }
      { \use:c { bool_gset_ \l_keys_choice_tl :N } \g__xeCJK_auto_fake_bold_bool } ,
    AutoFakeBold / unknown .code:n =
      {
        \bool_gset_true:N  \g__xeCJK_auto_fake_bold_bool
        \fp_gset:Nn \g__xeCJK_embolden_factor_fp { \l_keys_value_tl }
      } ,
    AutoFakeBold  .default:n  = { true } ,
    AutoFakeSlant .choices:nn = { true , false }
      { \use:c { bool_gset_ \l_keys_choice_tl :N } \g__xeCJK_auto_fake_slant_bool } ,
    AutoFakeSlant / unknown  .code:n =
      {
        \bool_gset_true:N  \g__xeCJK_auto_fake_slant_bool
        \fp_gset:Nn \g__xeCJK_slant_factor_fp { \l_keys_value_tl }
      } ,
    AutoFakeSlant  .default:n = { true } ,
    EmboldenFactor .fp_gset:N = \g__xeCJK_embolden_factor_fp ,
    SlantFactor    .fp_gset:N = \g__xeCJK_slant_factor_fp ,
    BoldFont  .meta:n = { AutoFakeBold  = true } ,
    boldfont  .meta:n = { AutoFakeBold  = true } ,
    SlantFont .meta:n = { AutoFakeSlant = true } ,
    slantfont .meta:n = { AutoFakeSlant = true }
  }
\seq_new:N \g__xeCJK_sub_key_seq
\cs_new_protected:Npn \xeCJK_new_sub_key:n #1
  {
    \seq_gput_right:Nn \g__xeCJK_sub_key_seq {#1}
    \keys_define:nn { xeCJK / features }
      {
        #1 .code:n =
          {
            \tl_if_blank:nTF {##1}
              {
                \prop_clear:N \l__xeCJK_sub_key_prop
                \tl_set:Nx \l__xeCJK_sub_family_name_tl
                  { \l__xeCJK_family_name_tl /#1 }
                \clist_remove_all:Nn \l__xeCJK_font_options_clist {#1}
              }
              {
                \tl_clear:N \l__xeCJK_sub_family_name_tl
                \str_if_eq:nnTF {##1} { * }
                  { \prop_put:Nnn \l__xeCJK_sub_key_prop {#1} { \q_no_value } }
                  { \__xeCJK_get_sub_features:nn {#1} {##1} }
              }
          } ,
        #1 .default:n = { }
      }
  }
\cs_new_protected:Npn \__xeCJK_get_sub_features:nn #1#2
  {
    \tl_set:Nx \l__xeCJK_tmp_tl { \xeCJK_tl_remove_outer_braces:n {#2} }
    \clist_clear:N \l__xeCJK_sub_font_options_clist
    \exp_after:wN \__xeCJK_get_sub_features:w \l__xeCJK_tmp_tl
      \q_mark [ \q_nil ] \q_mark \q_stop
    \tl_if_empty:NTF \l__xeCJK_sub_font_name_tl
      { \tl_set_eq:NN \l__xeCJK_sub_font_name_tl \l__xeCJK_font_name_tl }
      { \tl_replace_all:Nno \l__xeCJK_sub_font_name_tl { * } \l__xeCJK_font_name_tl }
    \prop_put:Nne \l__xeCJK_sub_key_prop {#1}
      {
        { \exp_not:o \l__xeCJK_sub_font_options_clist }
        { \exp_not:o \l__xeCJK_sub_font_name_tl }
      }
  }
\cs_new_protected:Npn \__xeCJK_get_sub_features:w #1 [#2] #3 \q_mark #4 \q_stop
  {
    \quark_if_nil:nTF {#2}
      { \tl_set_eq:NN \l__xeCJK_sub_font_name_tl \l__xeCJK_tmp_tl }
      {
        \tl_set:Nx \l__xeCJK_sub_font_name_tl
          { \xeCJK_tl_remove_outer_braces:n {#3} }
        \tl_if_empty:NTF \l__xeCJK_sub_font_name_tl
          { \tl_set_eq:NN \l__xeCJK_sub_font_name_tl \l__xeCJK_tmp_tl }
          { \clist_set:Nn \l__xeCJK_sub_font_options_clist {#2} }
      }
  }
\tl_new:N \l__xeCJK_sub_family_name_tl
\tl_new:N \l__xeCJK_sub_font_name_tl
\clist_new:N \l__xeCJK_sub_font_options_clist
\cs_generate_variant:Nn \__xeCJK_get_sub_features:nn { o }
\cs_generate_variant:Nn \tl_replace_all:Nnn { Nno }
\xeCJK_new_sub_key:n { FallBack }
\keys_define:nn { xeCJK / features }
  {
    BoldFont   .tl_set:N = \l__xeCJK_font_name_bf_tl ,
    ItalicFont .tl_set:N = \l__xeCJK_font_name_it_tl
  }
\keys_define:nn { xeCJK / features }
  {
    AutoFakeBold  .choice: ,
    AutoFakeBold / true    .code:n =
      {
        \bool_set_true:N \l__xeCJK_auto_fake_bold_bool
        \fp_set_eq:NN \l__xeCJK_embolden_factor_fp \g__xeCJK_embolden_factor_fp
      } ,
    AutoFakeBold / false   .code:n =
      { \bool_set_false:N \l__xeCJK_auto_fake_bold_bool } ,
    AutoFakeBold / unknown .code:n =
      {
        \bool_set_true:N \l__xeCJK_auto_fake_bold_bool
        \fp_set:Nn \l__xeCJK_embolden_factor_fp { \l_keys_value_tl }
      } ,
    AutoFakeBold .default:n  = { true } ,
    AutoFakeSlant  .choice: ,
    AutoFakeSlant / true    .code:n =
      {
        \bool_set_true:N \l__xeCJK_auto_fake_slant_bool
        \fp_set_eq:NN \l__xeCJK_slant_factor_fp \g__xeCJK_slant_factor_fp
      } ,
    AutoFakeSlant / false   .code:n =
      { \bool_set_false:N \l__xeCJK_auto_fake_slant_bool } ,
    AutoFakeSlant / unknown .code:n =
      {
        \bool_set_true:N \l__xeCJK_auto_fake_slant_bool
        \fp_set:Nn \l__xeCJK_slant_factor_fp { \l_keys_value_tl }
      } ,
    AutoFakeSlant .default:n  = { true }
  }
\cs_new_protected:Npn \__xeCJK_set_family_initial:
  {
    \int_gincr:N \g__xeCJK_family_int
    \prop_clear:N \l__xeCJK_sub_key_prop
    \tl_clear:N \l__xeCJK_font_name_bf_tl
    \tl_clear:N \l__xeCJK_font_name_it_tl
    \tl_clear:N \l__xeCJK_sub_family_name_tl
    \clist_clear:N \l__xeCJK_fontspec_options_clist
    \bool_set_eq:NN \l__xeCJK_auto_fake_bold_bool  \g__xeCJK_auto_fake_bold_bool
    \bool_set_eq:NN \l__xeCJK_auto_fake_slant_bool \g__xeCJK_auto_fake_slant_bool
    \fp_set_eq:NN \l__xeCJK_embolden_factor_fp \g__xeCJK_embolden_factor_fp
    \fp_set_eq:NN \l__xeCJK_slant_factor_fp    \g__xeCJK_slant_factor_fp
  }
\int_new:N \g__xeCJK_family_int
\prop_new:N \l__xeCJK_sub_key_prop
\clist_new:N \l__xeCJK_fontspec_options_clist
\bool_new:N \l__xeCJK_auto_fake_bold_bool
\bool_new:N \l__xeCJK_auto_fake_slant_bool
\fp_new:N \l__xeCJK_embolden_factor_fp
\fp_new:N \l__xeCJK_slant_factor_fp
\cs_new_protected:Npn \xeCJK_set_family:nnn #1#2#3
  {
    \group_begin:
    \__xeCJK_set_family_initial:
    \tl_set:Nn \l__xeCJK_family_name_tl {#1}
    \clist_set:Nn \l__xeCJK_font_options_clist {#2}
    \tl_set:Nn \l__xeCJK_font_name_tl {#3}
    \clist_concat:NNN \l__xeCJK_font_options_clist
      \g__xeCJK_default_features_clist \l__xeCJK_font_options_clist
    \keys_set_known:noN { xeCJK / features }
      \l__xeCJK_font_options_clist \l__xeCJK_fontspec_options_clist
    \__xeCJK_binding_sub_family:
    \__xeCJK_parse_font_shape:
    \__xeCJK_check_family:o \l__xeCJK_family_name_tl
    \__xeCJK_gset_family_cs:n { \l__xeCJK_family_name_tl }
    \__xeCJK_save_family_info:
    \__xeCJK_set_sub_block_family:
    \group_end:
  }
\tl_new:N \l__xeCJK_family_name_tl
\tl_new:N \l__xeCJK_font_name_tl
\clist_new:N \l__xeCJK_font_options_clist
\cs_generate_variant:Nn \xeCJK_set_family:nnn { e , o }
\cs_new_protected:Npn \__xeCJK_binding_sub_family:
  {
    \tl_if_empty:NF \l__xeCJK_sub_family_name_tl
      { \tl_set_eq:NN \l__xeCJK_family_name_tl \l__xeCJK_sub_family_name_tl }
  }
\cs_new_protected:Npn \__xeCJK_gset_family_cs:n #1
  {
    \cs_gset_protected:cpx { \__xeCJK_family_csname:n {#1} }
      {
        \group_begin:
        \exp_not:n { \cs_set_eq:NN \xeCJK@fontfamily \use_none:n }
        \exp_not:n { \fontspec_gset_family:Nnn \g__xeCJK_fontspec_family_tl }
          { \exp_not:o \l__xeCJK_fontspec_options_clist }
          { \exp_not:o \l__xeCJK_font_name_tl }
        \__xeCJK_gset_family_nfss_cs:no
          {#1} { \exp_not:N \g__xeCJK_fontspec_family_tl }
        \group_end:
        \tl_set_eq:NN \exp_not:N \l__xeCJK_fontspec_family_tl
                      \exp_not:N \g__xeCJK_fontspec_family_tl
      }
  }
\tl_new:N \g__xeCJK_fontspec_family_tl
\tl_new:N \l__xeCJK_fontspec_family_tl
\cs_new_protected:Npn \__xeCJK_check_family:n #1
  {
    \prop_gpop:NnNT \g__xeCJK_family_font_name_prop {#1} \l__xeCJK_tmp_tl
      {
        \prop_gpop:NnNT \g__xeCJK_family_name_prop {#1} \l__xeCJK_tmp_tl
          {
            \cs_undefine:c { \__xeCJK_family_csname:n {#1} }
            \cs_undefine:c { \__xeCJK_family_nfss_csname:n {#1} }
          }
        \__xeCJK_warning:nxx { CJKfamily-redef } {#1} { \l__xeCJK_tmp_tl }
      }
  }
\cs_generate_variant:Nn \__xeCJK_check_family:n { o }
\__xeCJK_msg_new:nn { CJKfamily-redef }
  { Redefining~CJKfamily~`\__xeCJK_msg_family_map:n {#1}'~(#2). }
\cs_new_protected:Npn \__xeCJK_parse_font_shape:
  {
    \tl_if_blank:oTF \l__xeCJK_font_name_bf_tl
      {
        \bool_if:NT \l__xeCJK_auto_fake_bold_bool
          {
            \clist_put_right:Nx \l__xeCJK_fontspec_options_clist
              { AutoFakeBold = { \fp_use:N \l__xeCJK_embolden_factor_fp } }
          }
      }
      {
        \clist_put_right:Nx \l__xeCJK_fontspec_options_clist
          { BoldFont = { \exp_not:o \l__xeCJK_font_name_bf_tl } }
      }
    \tl_if_blank:oTF \l__xeCJK_font_name_it_tl
      {
        \bool_if:NT \l__xeCJK_auto_fake_slant_bool
          {
            \clist_put_right:Nx \l__xeCJK_fontspec_options_clist
              { AutoFakeSlant = { \fp_use:N \l__xeCJK_slant_factor_fp } }
          }
      }
      {
        \clist_put_right:Nx \l__xeCJK_fontspec_options_clist
          { ItalicFont = { \exp_not:o \l__xeCJK_font_name_it_tl } }
      }
  }
\prop_new:N \g__xeCJK_family_name_prop
\prop_new:N \g__xeCJK_family_font_name_prop
\prop_new:N \g__xeCJK_family_font_options_prop
\cs_new_protected:Npn \__xeCJK_save_family_info:
  {
    \exp_args:Nooo \__xeCJK_save_family_info:nnn
      \l__xeCJK_family_name_tl
      \l__xeCJK_font_name_tl
      \l__xeCJK_font_options_clist
  }
\cs_new_protected:Npn \__xeCJK_save_family_info:nnn #1#2#3
  {
    \prop_gput:Nnn \g__xeCJK_family_font_name_prop    {#1} {#2}
    \prop_gput:Nnn \g__xeCJK_family_font_options_prop {#1} {#3}
  }
\cs_new_protected:Npn \__xeCJK_set_sub_block_family:
  {
    \prop_if_empty:NF \l__xeCJK_sub_key_prop
      {
        \prop_map_function:NN
          \l__xeCJK_sub_key_prop
          \__xeCJK_set_sub_block_family:nn
      }
  }
\cs_new_protected:Npn \__xeCJK_set_sub_block_family:nn #1#2
  {
    \tl_set:Nx \l__xeCJK_sub_family_name_tl { \l__xeCJK_family_name_tl/#1 }
    \quark_if_no_value:nTF {#2}
      { \__xeCJK_copy_sub_family:n {#1} }
      { \xeCJK_set_family:onn \l__xeCJK_sub_family_name_tl #2 }
  }
\cs_new_protected:Npn \__xeCJK_copy_sub_family:n #1
  {
    \__xeCJK_check_family:o \l__xeCJK_sub_family_name_tl
    \prop_get:NoNT \g__xeCJK_family_font_name_prop
      \l__xeCJK_family_name_tl \l__xeCJK_sub_font_name_tl
      {
        \prop_gput:Noo \g__xeCJK_family_font_name_prop
          \l__xeCJK_sub_family_name_tl \l__xeCJK_sub_font_name_tl
      }
    \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 = * }
        \prop_gput:Noo \g__xeCJK_family_font_options_prop
          \l__xeCJK_sub_family_name_tl \l__xeCJK_sub_font_options_clist
      }
    \cs_gset_protected:cpx
      { \__xeCJK_family_csname:n { \l__xeCJK_sub_family_name_tl } }
      {
        \xeCJK_family_if_exist:eT { \l__xeCJK_family_name_tl }
          {
            \__xeCJK_gset_family_nfss_cs:no
              { \l__xeCJK_sub_family_name_tl }
              { \exp_not:N \l__xeCJK_fontspec_family_tl }
          }
      }
  }
\cs_new_protected:Npn \__xeCJK_copy_family:nn #1#2
  {
    \xeCJK_family_if_exist:nT {#2}
      {
        \prop_gput:Nno \g__xeCJK_family_name_prop
          {#1} \l__xeCJK_fontspec_family_tl
        \tl_map_inline:nn
          {
            \g__xeCJK_family_font_name_prop
            \g__xeCJK_family_font_options_prop
          }
          {
            \prop_get:NnNT ##1 {#2} \l__xeCJK_tmp_tl
              { \prop_gput:Nno ##1 {#1} \l__xeCJK_tmp_tl }
          }
        \cs_gset_eq:cc
          { \__xeCJK_family_nfss_csname:n {#1} }
          { \__xeCJK_family_nfss_csname:n {#2} }
      }
  }
\cs_generate_variant:Nn \__xeCJK_copy_family:nn { ee }
\cs_new:Npn \__xeCJK_font_csname:n #1
  { xeCJK/#1/\f@series/\f@shape/\f@size }
\tl_new:N \l_xeCJK_current_font_tl
\tl_set:No \l_xeCJK_current_font_tl
  { \__xeCJK_font_csname:n { \CJK@family } }
\cs_new_protected:Npn \xeCJK_select_font:
  {
    \__xeCJK_select_font:cn
      { \l_xeCJK_current_font_tl }
      { \l_xeCJK_family_tl }
  }
\cs_new_protected:Npn \__xeCJK_select_font:Nn #1#2
  {
    \xeCJK_clear_fallback_font:
    \cs_if_exist:NF #1 { \__xeCJK_font_initial:Nn #1 {#2} }
    #1
  }
\cs_generate_variant:Nn \__xeCJK_select_font:Nn { c }
\tl_new:N \l__xeCJK_current_coor_tl
\cs_new_eq:NN \xeCJK@setfont \xeCJK_select_font:
\cs_new_protected:Npn \__xeCJK_font_initial:Nn #1#2
  {
    \group_begin:
      \__xeCJK_font_initial_hook:
      \__xeCJK_family_use:n {#2}
      \xeCJK_font_gset_to_current:N #1
    \group_end:
  }
\cs_new_protected:Npn \__xeCJK_font_initial_hook:
  { \tl_use:N \g__xeCJK_font_initial_hook_tl }
\cs_new_protected:Npn \__xeCJK_gadd_font_initial_hook:n
  { \tl_gput_right:Nn \g__xeCJK_font_initial_hook_tl }
\tl_new:N \g__xeCJK_font_initial_hook_tl
\cs_new_eq:NN \xeCJK_select_punct_font: \xeCJK_select_font:
\cs_new_protected:Npn \__xeCJK_select_punct_font_aux:
  {
    \__xeCJK_select_font:cn
      { \l_xeCJK_current_punct_font_tl }
      { \l_xeCJK_punct_family_tl }
  }
\tl_new:N \CJK@punctfamily
\tl_new:N \l_xeCJK_punct_family_tl
\tl_new:N \l_xeCJK_current_punct_font_tl
\tl_set:Nn \CJK@punctfamily { \CJK@family }
\tl_set:Nn \l_xeCJK_punct_family_tl { \l_xeCJK_family_tl }
\tl_set:No \l_xeCJK_current_punct_font_tl
  { \__xeCJK_font_csname:n { \CJK@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:Npn \__xeCJK_switch_font:nn #1#2
  {
    \str_if_eq:nnF {#1} {#2}
      {
        \__xeCJK_info:nxx { CJK-block } {#1} {#2}
        \str_if_eq:nnTF {#2} { CJK }
          { \xeCJK_select_font: }
          { \xeCJK_select_font:n {#2} }
      }
  }
\__xeCJK_msg_new:nn { CJK-block } { Switch~from~block~`#1'~to~`#2'. }
\cs_new_protected:Npn \xeCJK_select_font:n #1
  {
    \__xeCJK_select_font:cnn
      { \__xeCJK_font_csname:n { \CJK@family/#1 } }
      { \l_xeCJK_family_tl }
      {#1}
  }
\cs_new_protected:Npn \__xeCJK_select_font:Nnn #1#2#3
  {
    \xeCJK_clear_fallback_font:
    \cs_if_exist:NF #1
      { \__xeCJK_block_font_initial:Nnn #1 {#2} {#3} }
    #1
  }
\cs_generate_variant:Nn \__xeCJK_select_font:Nnn { c }
\cs_new_protected:Npn \__xeCJK_block_font_initial:Nnn #1#2#3
  {
    \xeCJK_block_family:nn {#2} {#3}
    \__xeCJK_font_initial:Nn #1 { #2/#3 }
  }
\cs_new_protected:Npn \xeCJK_block_family:nn #1#2
  {
    \xeCJK_family_if_exist:eF { #1/#2 }
      {
        \__xeCJK_copy_family:ee { #1/#2 }
          {
            \cs_if_exist:cTF
              { \__xeCJK_family_csname:n { \CJKfamilydefault/#2 } }
              { \CJKfamilydefault/#2 } {#1}
          }
      }
  }
\cs_new:Npn \__xeCJK_family_csname:n #1
  { xeCJK/family/#1 }
\cs_new:Npn \__xeCJK_family_nfss_csname:n #1
  { xeCJK/family/nfss/#1 }
\cs_new_protected:Npn \__xeCJK_family_use:n #1
  { \use:c { \__xeCJK_family_nfss_csname:n {#1} } }
\cs_new_protected:Npn \__xeCJK_gset_family_nfss_cs:nn #1#2
  {
    \prop_gput:Nnn \g__xeCJK_family_name_prop {#1} {#2}
    \cs_gset_protected:cpx
      { \__xeCJK_family_nfss_csname:n {#1} }
      { \__xeCJK_nfss_family:nn { \c__xeCJK_encoding_tl } {#2} }
  }
\cs_generate_variant:Nn \__xeCJK_gset_family_nfss_cs:nn { no }
\cs_if_exist:NTF \fontseriesforce
  {
    \cs_new_protected:Npn \__xeCJK_nfss_family:nn #1#2
      {
        \fontencoding {#1}
        \str_if_eq:eeF { \f@series } { \bfdefault }
          {
            \str_case_e:nn { \f@family }
              {
                { \rmdefault } { \__xeCJK_nfss_series:n { rm } }
                { \sfdefault } { \__xeCJK_nfss_series:n { sf } }
                { \ttdefault } { \__xeCJK_nfss_series:n { tt } }
              }
          }
        \fontfamily {#2}
        \selectfont
      }
    \cs_new_protected:Npn \__xeCJK_nfss_series:n #1
      {
        \str_if_eq:eeT { \f@series } { \use:c { bfseries@#1 } }
          { \fontseriesforce { \bfdefault } }
      }
  }
  {
    \cs_new_protected:Npn \__xeCJK_nfss_family:nn #1#2
      {
        \fontencoding {#1}
        \tl_set:Nn \f@family {#2}
        \selectfont
      }
  }
\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
      { \prg_return_true: }
      {
        \exp_args:Ne \cs_if_exist_use:cTF
          { \__xeCJK_family_csname:n {#1} }
          { \prg_return_true: }
          { \prg_return_false: }
      }
  }
\prg_generate_conditional_variant:Nnn \xeCJK_family_if_exist:n { e } { T , F , TF }
\NewDocumentCommand \CJKfamily { t+ t- m }
  {
    \xeCJK_family:NNe #1 #2 {#3}
    \tex_ignorespaces:D
  }
\cs_new_protected:Npn \xeCJK_family:NNn #1#2#3
  {
    \tl_if_blank:nTF {#3}
      {
        \bool_if:NF #1 { \bool_if:NF #2 { \use_none:nn } }
        \xeCJK_family_if_exist_use:e { \l_xeCJK_family_tl }
      }
      {
        \bool_if:NTF #2
          { \xeCJK_family_if_exist_use:n {#3} }
          {
            \xeCJK_family_if_exist:nTF {#3}
              {
                \tl_set:Nn \l_xeCJK_family_tl {#3}
                \tl_set_eq:NN \CJK@family \l__xeCJK_fontspec_family_tl
                \bool_if:NT #1 { \__xeCJK_family_use:n {#3} }
              }
              { \__xeCJK_family_unknown_warning:n {#3} }
          }
      }
  }
\cs_generate_variant:Nn \xeCJK_family:NNn { NNe }
\cs_new_protected:Npn \xeCJK_switch_family:n #1
  {
    \xeCJK_family_if_exist:nTF {#1}
      {
        \tl_set:Nn \l_xeCJK_family_tl {#1}
        \tl_set_eq:NN \CJK@family \l__xeCJK_fontspec_family_tl
      }
      { \__xeCJK_family_unknown_warning:n {#1} }
  }
\cs_generate_variant:Nn \xeCJK_switch_family:n { e , o }
\keys_define:nn { xeCJK / options }
  {
    PunctFamily .choice: ,
    PunctFamily .value_required:n = { true } ,
    PunctFamily / false   .code:n =
      {
        \tl_set:Nn \CJK@punctfamily { \CJK@family }
        \tl_set:Nn \l_xeCJK_punct_family_tl { \l_xeCJK_family_tl }
        \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:e {#1} } ,
  }
\cs_new_protected: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@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 { e }
\tl_new:N \l_xeCJK_family_tl
\tl_new:N \CJK@family
\cs_new_protected:Npn \__xeCJK_gobble_CJKfamily:
  { \cs_set_eq:NN \CJKfamily \__xeCJK_gobble_CJKfamily:wn }
\NewExpandableDocumentCommand \__xeCJK_gobble_CJKfamily:wn { t+ t- m } {  }
\cs_new_protected:Npn \xeCJK_family_if_exist_use:n #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 { e }
\cs_new_protected:Npn \__xeCJK_family_unknown_warning:n #1
  {
    \prop_if_empty:NF \g__xeCJK_family_font_name_prop
      {
        \seq_if_in:NnF \g__xeCJK_unknown_family_seq {#1}
          {
            \seq_gput_right:Nn \g__xeCJK_unknown_family_seq {#1}
            \__xeCJK_warning:nx { CJKfamily-Unknown } {#1}
          }
      }
  }
\seq_new:N \g__xeCJK_unknown_family_seq
\__xeCJK_msg_new:nn { CJKfamily-Unknown }
  {
    Unknown~CJK~family~`\__xeCJK_msg_family_map:n {#1}'~is~being~ignored.\\\\
    Try~to~use~`\__xeCJK_msg_def_family_map:n {#1}'~to~define~it.
  }
\cs_new:Npn \__xeCJK_msg_def_family_map:n #1
  {
    \str_case_e:nnF {#1}
      {
        \CJKrmdefault { \token_to_str:N \setCJKmainfont }
        \CJKsfdefault { \token_to_str:N \setCJKsansfont }
        \CJKttdefault { \token_to_str:N \setCJKmonofont }
      }
      { \token_to_str:N \setCJKfamilyfont \{ #1 \} }
    [<...>]\{<...>\}
  }
\cs_new:Npn \__xeCJK_msg_family_map:n #1
  {
    \str_case_e:nnF {#1}
      {
        \CJKrmdefault { \token_to_str:N \CJKrmdefault }
        \CJKsfdefault { \token_to_str:N \CJKsfdefault }
        \CJKttdefault { \token_to_str:N \CJKttdefault }
      }
      {#1}
  }
\cs_new_protected:Npn \__xeCJK_pass_args:nnnn #1#2#3#4
  {
    \tl_if_novalue:nTF {#2}
      { \__xeCJK_post_arg:w {#1} {#3} {#4} }
      {
        \use:e { #1 {#2} {#3} }
        #4
      }
  }
\NewDocumentCommand \__xeCJK_post_arg:w { m m m O { } }
  {
    \use:e { #1 {#4} {#2} }
    #3
  }
\NewDocumentCommand \setCJKmainfont { o m }
  {
    \__xeCJK_pass_args:nnnn
      { \xeCJK_set_family:nnn { \CJKrmdefault } } {#1} {#2}
      { \__xeCJK_preamble_family:n { \CJKrmdefault } }
  }
\cs_new_eq:NN \setCJKromanfont \setCJKmainfont
\NewDocumentCommand \setCJKsansfont { o m }
  {
    \__xeCJK_pass_args:nnnn
      { \xeCJK_set_family:nnn { \CJKsfdefault } } {#1} {#2}
      { \__xeCJK_preamble_family:n { \CJKsfdefault } }
  }
\NewDocumentCommand \setCJKmonofont { o m }
  {
    \__xeCJK_pass_args:nnnn
      { \xeCJK_set_family:nnn { \CJKttdefault } } {#1} {#2}
      { \__xeCJK_preamble_family:n { \CJKttdefault } }
  }
\@onlypreamble \setCJKmainfont
\@onlypreamble \setCJKmathfont
\@onlypreamble \setCJKsansfont
\@onlypreamble \setCJKmonofont
\@onlypreamble \setCJKromanfont
\cs_new_protected:Npn \__xeCJK_preamble_family:n #1
  { \str_if_eq:eeT {#1} { \CJKfamilydefault } { \normalfont } }
\NewDocumentCommand \setCJKfamilyfont { m o m }
  {
    \__xeCJK_pass_args:nnnn
      { \xeCJK_set_family:nnn {#1} } {#2} {#3}
      { }
  }
\NewDocumentCommand \newCJKfontfamily { o m o m }
  {
    \tl_set:Nx \l__xeCJK_tmp_tl
      { \tl_if_novalue:nTF {#1} { \cs_to_str:N #2 } {#1} }
    \cs_new_protected:Npx #2
      { \xeCJK_switch_family:n { \l__xeCJK_tmp_tl } }
    \__xeCJK_pass_args:nnnn
      { \xeCJK_set_family:nnn { \l__xeCJK_tmp_tl } } {#3} {#4}
      { }
  }
\NewDocumentCommand \CJKfontspec { o m }
  {
    \__xeCJK_pass_args:nnnn
      { \xeCJK_fontspec:nn } {#1} {#2}
      { \tex_ignorespaces:D }
  }
\cs_new_protected:Npn \xeCJK_fontspec:nn #1#2
  {
    \prop_get:NnNTF \g__xeCJK_fontspec_prop
      { CJKfontspec/#1/#2/id } \l_xeCJK_family_tl
      { \xeCJK_switch_family:o { \l_xeCJK_family_tl } }
      {
        \__xeCJK_fontspec:enn
          { CJKfontspec ( \int_eval:n { \g__xeCJK_family_int + 1 } ) }
          {#1} {#2}
      }
  }
\cs_new_protected:Npn \__xeCJK_fontspec:nnn #1#2#3
  {
    \prop_gput:Nnn \g__xeCJK_fontspec_prop { CJKfontspec/#2/#3/id } {#1}
    \xeCJK_set_family:nnn {#1} {#2} {#3}
    \xeCJK_switch_family:n {#1}
  }
\cs_generate_variant:Nn \xeCJK_fontspec:nn { oo }
\cs_generate_variant:Nn \__xeCJK_fontspec:nnn { e }
\prop_new:N \g__xeCJK_fontspec_prop
\clist_new:N \g__xeCJK_default_features_clist
\NewDocumentCommand \defaultCJKfontfeatures { m }
  { \clist_gset:Nn \g__xeCJK_default_features_clist {#1} }
\@onlypreamble \defaultCJKfontfeatures
\NewDocumentCommand \addCJKfontfeatures { s O { } m }
  {
    \xeCJK_add_font_features:Nee #1 {#2} {#3}
    \tex_ignorespaces:D
  }
\cs_new_eq:NN \addCJKfontfeature \addCJKfontfeatures
\cs_new_protected:Npn \xeCJK_add_font_features:Nnn #1#2#3
  {
    \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}
        \seq_map_inline:Nn \g__xeCJK_sub_key_seq
          { \clist_remove_all:Nn \l__xeCJK_add_font_features_clist {##1} }
        \seq_clear:N \l__xeCJK_sub_key_seq
        \clist_clear:N \l__xeCJK_add_block_features_clist
        \clist_map_function:nN {#2} \__xeCJK_add_sub_features:n
        \bool_lazy_and:nnT
          {#1}
          { \seq_if_empty_p:N \l__xeCJK_sub_key_seq }
          {
            \seq_map_function:NN
              \g__xeCJK_sub_key_seq \__xeCJK_add_sub_class_features:n
          }
        \prop_get:NoNT \g__xeCJK_family_font_options_prop
          \l_xeCJK_family_tl \l__xeCJK_font_options_clist
          {
            \bool_lazy_or:nnT
              { \seq_if_empty_p:N \l__xeCJK_sub_key_seq }
              {#1}
              {
                \clist_concat:NNN \l__xeCJK_font_options_clist
                  \l__xeCJK_font_options_clist \l__xeCJK_add_font_features_clist
              }
            \clist_concat:NNN \l__xeCJK_font_options_clist
              \l__xeCJK_font_options_clist \l__xeCJK_add_block_features_clist
          }
        \xeCJK_fontspec:oo \l__xeCJK_font_options_clist \l__xeCJK_font_name_tl
      }
      { \__xeCJK_warning:n { addCJKfontfeature-ignored } }
  }
\cs_new_protected:Npn \__xeCJK_add_sub_features:n #1
  {
    \seq_if_in:NnTF \g__xeCJK_sub_key_seq {#1}
      {
        \seq_put_right:Nn \l__xeCJK_sub_key_seq {#1}
        \__xeCJK_add_sub_class_features:n {#1}
      }
      { \__xeCJK_warning:nx { SubBlock-undefined } {#1} }
  }
\clist_new:N \l__xeCJK_add_font_features_clist
\clist_new:N \l__xeCJK_add_block_features_clist
\cs_generate_variant:Nn \xeCJK_add_font_features:Nnn { Nee , Nne }
\__xeCJK_msg_new:nn { addCJKfontfeature-ignored }
  {
    \token_to_str:N \addCJKfontfeature (s)~ignored.\\\\
    It~cannot~be~used~with~a~font~that~wasn't~selected~by~xeCJK.
  }
\cs_new_protected:Npn \__xeCJK_add_sub_class_features:n #1
  {
    \prop_get:NoNTF \g__xeCJK_family_font_name_prop
      { \l_xeCJK_family_tl/#1 } \l__xeCJK_sub_font_name_tl
      {
        \prop_get:NoN \g__xeCJK_family_font_options_prop
          { \l_xeCJK_family_tl/#1 } \l__xeCJK_sub_font_options_clist
      }
      {
        \prop_get:NeNTF \g__xeCJK_family_font_name_prop
          { \CJKfamilydefault/#1 } \l__xeCJK_sub_font_name_tl
          {
            \prop_get:NeN \g__xeCJK_family_font_options_prop
              { \CJKfamilydefault/#1 } \l__xeCJK_sub_font_options_clist
          }
          {
            \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
          }
      }
    \clist_concat:NNN \l__xeCJK_sub_font_options_clist
      \l__xeCJK_sub_font_options_clist \l__xeCJK_add_font_features_clist
    \clist_put_right:Nx \l__xeCJK_add_block_features_clist
      {
        #1 =
          {
            [ \exp_not:o \l__xeCJK_sub_font_options_clist ]
            { \exp_not:o \l__xeCJK_sub_font_name_tl }
          }
      }
  }
\cs_generate_variant:Nn \prop_get:NnN { Ne }
\prg_generate_conditional_variant:Nnn \prop_get:NnN { Ne } { T , TF }
\keys_define:nn { xeCJK / options }
  { LoadFandol .bool_gset:N = \g__xeCJK_fandol_bool }
\cs_new_protected:Npn \__xeCJK_load_fandol:
  {
    \xeCJK_set_family:enn { \CJKrmdefault }
      { Extension = .otf , BoldFont = FandolSong-Bold , ItalicFont = FandolKai-Regular }
      { FandolSong-Regular }
    \xeCJK_set_family:enn { \CJKsfdefault }
      { Extension = .otf , BoldFont = FandolHei-Bold } { FandolHei-Regular }
    \xeCJK_set_family:enn { \CJKttdefault }
      { Extension = .otf } { FandolFang-Regular }
  }
\__xeCJK_at_end_preamble:n
  {
    \tl_if_eq:NNT \CJKfamilydefault \l__xeCJK_family_default_init_tl
      {
        \group_begin:
        \cs_set_eq:NN \__xeCJK_family_default_wrap:n \exp_not:n
        \tl_gset:Nx \CJKfamilydefault
          {
            \str_case:onF { \familydefault }
              {
                { \rmdefault } { \exp_not:N \CJKrmdefault }
                { \sfdefault } { \exp_not:N \CJKsfdefault }
                { \ttdefault } { \exp_not:N \CJKttdefault }
              }
              { \CJKfamilydefault }
          }
        \group_end:
      }
    \prop_if_empty:NTF \g__xeCJK_family_font_name_prop
      {
        \bool_if:NTF \g__xeCJK_fandol_bool
          {
            \__xeCJK_warning:n { fandol }
            \__xeCJK_load_fandol:
            \xeCJK_ensure_default_family:
          }
          { \__xeCJK_warning:nx { no-CJKfamily } { \CJKfamilydefault } }
      }
      { \xeCJK_ensure_default_family: }
  }
\cs_new_protected:Npn \xeCJK_ensure_default_family:
  {
    \xeCJK_family_if_exist:eF { \CJKfamilydefault }
      {
        \tl_set_eq:NN \l__xeCJK_tmp_tl \CJKfamilydefault
        \str_if_eq:eeTF { \CJKfamilydefault } { \CJKrmdefault }
          { \use:n }
          {
            \xeCJK_family_if_exist:eTF { \CJKrmdefault }
              { \tl_gset:Nn \CJKfamilydefault { \CJKrmdefault } }
          }
          {
            \prop_map_inline:Nn \g__xeCJK_family_font_name_prop
              {
                \prop_map_break:n
                  { \tl_gset_rescan:Nnn \CJKfamilydefault { } { ##1 } }
              }
          }
        \__xeCJK_warning:nxx { CJKfamilydefault-undefined }
          { \l__xeCJK_tmp_tl } { \CJKfamilydefault }
      }
    \xeCJK_switch_family:e { \CJKfamilydefault }
    \bool_if:NT \g__xeCJK_math_bool { \xeCJK_set_mathfont: }
  }
\__xeCJK_msg_new:nn { no-CJKfamily }
  {
    It~seems~that~you~have~not~declare~a~CJKfamily.\\
    If~you~want~to~use~xeCJK~in~the~right~way,~you~should~use\\\\
    `\__xeCJK_msg_def_family_map:n {#1}'\\\\
    in~the~preamble~to~declare~the~default~CJKfamily.\\
  }
\__xeCJK_msg_new:nn { CJKfamilydefault-undefined }
  {
    Undefined~CJK~default~family~`\__xeCJK_msg_family_map:n {#1}'~
    has~been~replaced~by~`\__xeCJK_msg_family_map:n {#2}'.\\\\
    Try~to~use~`\__xeCJK_msg_def_family_map:n {#1}'~to~define~it.
  }
\__xeCJK_msg_new:nn { fandol }
  {
    Fandol~is~being~set~as~the~default~font~for~CJK~text.\\
    Please~make~sure~it~has~been~properly~installed.
  }
\keys_define:nn { xeCJK / options } { CJKmath .bool_gset:N = \g__xeCJK_math_bool }
\NewDocumentCommand \setCJKmathfont { o m }
  {
    \__xeCJK_pass_args:nnnn
      { \xeCJK_set_family:nnn { \c__xeCJK_math_tl } } {#1} {#2}
      { }
  }
\tl_const:Nn \c__xeCJK_math_tl { CJKmath }
\cs_new_protected:Npn \xeCJK_set_mathfont:
  {
    \cs_if_exist_use:N \__xeCJK_save_um_char:
    \xeCJK_family_if_exist:eTF { \c__xeCJK_math_tl }
      { \__xeCJK_set_mathfont_aux: }
      {
        \xeCJK_family_if_exist:eT { \CJKfamilydefault }
          {
            \__xeCJK_copy_family:ee { \c__xeCJK_math_tl } { \CJKfamilydefault }
            \__xeCJK_set_mathfont_aux:
          }
      }
    \cs_if_exist_use:N \__xeCJK_restore_um_char:
  }
\cs_new_protected:Npn \__xeCJK_set_mathfont_aux:
  {
    \tl_const:Nx \c__xeCJK_math_family_tl
      { \l__xeCJK_fontspec_family_tl }
    \xeCJK_declare_mathfont:ee
      { \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_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_set_mathfont_block:
  }
\clist_new:N \g__xeCJK_math_chars_clist
\prop_new:N \g__xeCJK_fam_prop
\cs_new_protected:Npn \xeCJK_set_mathfont_block:
  {
    \seq_if_empty:NF \g__xeCJK_CJK_sub_class_seq
      {
        \seq_map_function:NN
          \g__xeCJK_CJK_sub_class_seq
          \xeCJK_set_mathfont_block:n
      }
  }
\cs_new_protected:Npn \xeCJK_set_mathfont_block:n #1
  {
    \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
      { \int_set:Nn \l__xeCJK_fam_int { \l__xeCJK_tmp_tl } }
      {
        \xeCJK_declare_mathfont:ee
          { \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 }
  }
\cs_new_protected:Npn \__xeCJK_set_mathfont_block_aux:Nn #1#2
  {
    \int_set_eq:NN \l__xeCJK_fam_int #1
    \prop_gput:Nnn \g__xeCJK_block_fam_prop {#2} {#1}
  }
\int_new:N \l__xeCJK_fam_int
\prop_new:N \g__xeCJK_block_fam_prop
\cs_generate_variant:Nn \__xeCJK_set_mathfont_block_aux:Nn { c }
\cs_new_protected:Npn \xeCJK_declare_mathfont:nn #1#2
  {
    \xeCJK_declare_symbol_font:nnnnn {#1} { \c__xeCJK_encoding_tl }
      {#2} { \mddefault } { \shapedefault }
    \cs_if_free:cF
      { \c__xeCJK_encoding_tl/#2/\bfdefault/\shapedefault }
      {
        \SetSymbolFont {#1} { bold } { \c__xeCJK_encoding_tl }
          {#2} { \bfdefault } { \shapedefault }
      }
    \prop_gput:Nne \g__xeCJK_fam_prop {#2} { \exp_not:c { sym #1 } }
  }
\cs_generate_variant:Nn \prop_put:Nnn { Nne }
\cs_generate_variant:Nn \prop_gput:Nnn { Nne }
\cs_generate_variant:Nn \xeCJK_declare_mathfont:nn { ee }
\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
  {
    \xeCJK_new_fam:N #1
    \xeCJK_new_symbol_font:Nnnnn #1
  }
\cs_generate_variant:Nn \__xeCJK_declare_symbol_font:Nnnnn { c }
\cs_new_protected:Npn \xeCJK_new_fam:N #1
  {
    \int_compare:nNnTF
      { \g__xeCJK_fam_allocation_int } > { \g__xeCJK_fam_bottom_int }
      {
        \int_set_eq:NN \allocationnumber \g__xeCJK_fam_allocation_int
        \int_const:Nn #1 { \allocationnumber }
        \iow_log:x
          {
            \token_to_str:N #1 =
            \token_to_str:N \mathgroup \int_use:N \allocationnumber
          }
        \int_gdecr:N \g__xeCJK_fam_allocation_int
      }
      { \__xeCJK_error:n { fam-exhausted } }
  }
\tex_countdef:D \g__xeCJK_fam_bottom_int = 18 ~
\int_new:N \g__xeCJK_fam_allocation_int
\int_gset:Nn \g__xeCJK_fam_allocation_int { 255 }
\__xeCJK_msg_new:nn { fam-exhausted }
  { No~room~for~a~new~fam. }
\cs_new_protected:Npn \xeCJK_new_symbol_font:Nnnnn #1#2#3#4#5
  { \__xeCJK_new_symbol_font:Nc #1 { #2/#3/#4/#5 } }
\cs_new_protected:Npn \__xeCJK_new_symbol_font:NN #1#2
  {
    \tl_put_right:Nn \group@list { \group@elt #1 #2 }
    \cs_set:Npn \version@elt ##1
      { \tl_put_right:Nn ##1 { \getanddefine@fonts #1 #2 } }
    \version@list
  }
\cs_generate_variant:Nn \__xeCJK_new_symbol_font:NN { Nc }
\cs_new_protected:Npn \xeCJK_gset_mathcode:Nn #1#2
  {
    \clist_map_inline:Nn #1
      {
        \__xeCJK_set_char_class_aux:Nnw \xeCJK_gset_mathcode:nnnn { ##1 }
          { 0 } {#2}
      }
  }
\cs_generate_variant:Nn \xeCJK_gset_mathcode:Nn { c }
\cs_new_protected:Npn \xeCJK_gset_mathcode:nnnn #1#2#3#4
  {
    \__xeCJK_check_num_range:nnNN {#1} {#2} \l__xeCJK_begin_int \l__xeCJK_end_int
    \xeCJK_int_until_do:nn { \l__xeCJK_begin_int > \l__xeCJK_end_int }
      {
        \xeCJK_gset_mathcode:Nnn \l__xeCJK_begin_int {#3} {#4}
        \int_incr:N \l__xeCJK_begin_int
      }
  }
\cs_new_protected:Npn \xeCJK_gset_mathcode:Nnn #1#2#3
  { \tex_global:D \tex_Umathcode:D #1 = #2 ~ #3 ~ #1 }
\int_new:N \l__xeCJK_verb_case_int
\keys_define:nn { xeCJK / options }
  {
    Verb .choices:nn =
      { true , env+ , env , false }
      { \int_set_eq:NN \l__xeCJK_verb_case_int \l_keys_choice_int } ,
    Verb  .default:n = { env }
  }
\cs_new_protected:Npn \__xeCJK_verb_font_hook:
  {
    \if_case:w \l__xeCJK_verb_case_int
    \or:
      \__xeCJK_nobreak_skip_zero:
    \or:
      \int_compare:nNnTF \tex_currentgrouptype:D = { 14 }
        { \xeCJKVerbAddon }
        { \__xeCJK_nobreak_skip: }
    \or:
      \int_compare:nNnTF \tex_currentgrouptype:D = { 14 }
        { \xeCJKVerbAddon }
        { \__xeCJK_nobreak_skip_zero: }
    \fi:
  }
\__xeCJK_after_preamble:n
  {
    \cs_set_protected:Npx \verbatim@font
      { \exp_not:o { \verbatim@font } \__xeCJK_verb_font_hook: }
  }
\cs_new_protected:Npn \__xeCJK_nobreak_skip_zero:
  {
    \__xeCJK_reset_shipout_skip:
    \cs_set_eq:NN \__xeCJK_shipout_check_for_glue: \xeCJK_check_for_glue:
    \cs_set_eq:NN \__xeCJK_shipout_boundary:w \xeCJK_CJK_and_Boundary:w
    \tl_put_right:Nn \l__xeCJK_reset_shipout_skip_hook_tl
      {
        \cs_set_eq:NN \xeCJK_check_for_glue: \__xeCJK_shipout_check_for_glue:
        \cs_set_eq:NN \xeCJK_CJK_and_Boundary:w \__xeCJK_shipout_boundary:w
      }
    \xeCJK_cs_clear:N \CJKglue
    \xeCJK_cs_clear:N \CJKecglue
    \xeCJK_cs_clear:N \xeCJK_check_for_glue:
    \cs_set_eq:NN \xeCJK_CJK_and_Boundary:w \xeCJK_class_group_end:
    \cs_set_eq:NN \__xeCJK_punct_hskip:n \__xeCJK_nobreak_hskip:n
    \cs_set_eq:NN \__xeCJK_punct_breakable_kern:n \__xeCJK_nobreak_hskip:n
  }
\cs_new_protected:Npn \__xeCJK_nobreak_skip:
  {
    \__xeCJK_reset_shipout_skip:
    \xeCJK_glue_to_skip:nN { \CJKglue } \l__xeCJK_ccglue_skip
    \skip_if_eq:nnTF { \l__xeCJK_ccglue_skip } { \c_zero_skip }
      { \xeCJK_cs_clear:N \CJKglue }
      { \cs_set_eq:NN \CJKglue \__xeCJK_nobreak_ccglue: }
    \xeCJK_glue_to_skip:nN { \CJKecglue } \l__xeCJK_ecglue_skip
    \skip_if_eq:nnTF { \l__xeCJK_ecglue_skip } { \c_zero_skip }
      { \xeCJK_cs_clear:N \CJKecglue }
      { \cs_set_eq:NN \CJKecglue \__xeCJK_nobreak_ecglue: }
    \cs_set_eq:NN \__xeCJK_punct_hskip:n \__xeCJK_nobreak_hskip:n
    \cs_set_eq:NN \__xeCJK_punct_breakable_kern:n \__xeCJK_nobreak_hskip:n
  }
\cs_new_protected:Npn \__xeCJK_nobreak_ccglue:
  { \xeCJK_no_break: \skip_horizontal:N \l__xeCJK_ccglue_skip }
\cs_new_protected:Npn \__xeCJK_nobreak_ecglue:
  { \xeCJK_no_break: \skip_horizontal:N \l__xeCJK_ecglue_skip }
\cs_new_protected:Npn \__xeCJK_reset_shipout_skip:
  {
    \cs_set_eq:NN \__xeCJK_shipout_CJKglue:   \CJKglue
    \cs_set_eq:NN \__xeCJK_shipout_CJKecglue: \CJKecglue
    \cs_set_eq:NN \__xeCJK_shipout_punct_hskip:n \__xeCJK_punct_hskip:n
    \cs_set_eq:NN
      \__xeCJK_shipout_punct_breakable_kern:n \__xeCJK_punct_breakable_kern:n
    \tl_set:Nx \l__xeCJK_off_verb_addon_tl
      {
        \bool_if:NTF \l__xeCJK_xecglue_bool
          { \keys_set:nn { xeCJK / options } { xCJKecglue = true } }
          { \keys_set:nn { xeCJK / options } { xCJKecglue = false } }
        \exp_not:n
          {
            \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
            \l__xeCJK_reset_shipout_skip_hook_tl
          }
      }
    \xeCJK_add_to_shipout:n { \l__xeCJK_off_verb_addon_tl }
    \keys_set:nn { xeCJK / options } { xCJKecglue = false }
  }
\tl_new:N \l__xeCJK_reset_shipout_skip_hook_tl
\NewDocumentCommand \xeCJKVerbAddon { }
  {
    \int_compare:nNnF \tex_currentgrouplevel:D = \c_zero_int
      {
        \bool_if:NF \l__xeCJK_listings_env_bool
          {
            \dim_compare:nNnTF
              { \tex_fontdimen:D 2 ~ \tex_font:D } =
              { \tex_fontcharwd:D \tex_font:D \c__xeCJK_mono_letter_int }
              {
                \__xeCJK_set_verb_exspace:
                \__xeCJK_verb_addon:
              }
              {
                \int_if_odd:nTF { \l__xeCJK_verb_case_int }
                  { \__xeCJK_nobreak_skip_zero: }
                  { \__xeCJK_nobreak_skip: }
              }
          }
      }
  }
\int_const:Nn \c__xeCJK_mono_letter_int { 77 }
\bool_new:N \l__xeCJK_listings_env_bool
\NewDocumentCommand \xeCJKOffVerbAddon { }
  { \tl_use:N \l__xeCJK_off_verb_addon_tl }
\tl_new:N \l__xeCJK_off_verb_addon_tl
\cs_new_protected:Npn \__xeCJK_verb_addon:
  {
    \bool_if:NF \l__xeCJK_verb_addon_bool
      { \__xeCJK_verb_addon_action: }
    \skip_if_eq:nnTF { \l__xeCJK_verb_exspace_skip } { \c_zero_skip }
      {
        \xeCJK_cs_clear:N \CJKglue
        \xeCJK_cs_clear:N \CJKecglue
      }
      {
        \skip_set_eq:NN \l__xeCJK_ccglue_skip \l__xeCJK_verb_exspace_skip
        \skip_set:Nn \l__xeCJK_ecglue_skip { \l__xeCJK_verb_exspace_skip / 2 }
        \cs_set_eq:NN \CJKglue   \__xeCJK_nobreak_ccglue:
        \cs_set_eq:NN \CJKecglue \__xeCJK_nobreak_ecglue:
      }
    \cs_set_eq:NN \xeCJK_check_for_glue: \CJKecglue
    \cs_set_eq:NN \xeCJK_CJK_and_Boundary:w \__xeCJK_verb_CJK_and_Boundary:w
  }
\cs_new_protected:Npn \__xeCJK_verb_addon_action:
  {
    \bool_set_true:N \l__xeCJK_verb_addon_bool
    \__xeCJK_set_char_class_eq:nn { FullLeft }    { CJK }
    \__xeCJK_set_char_class_eq:nn { FullRight }   { CJK }
    \__xeCJK_set_char_class_eq:nn { HalfLeft }    { Default }
    \__xeCJK_set_char_class_eq:nn { HalfRight }   { Default }
    \__xeCJK_set_char_class_eq:nn { NormalSpace } { Default }
    \cs_set_eq:NN \__xeCJK_shipout_CJKglue:   \CJKglue
    \cs_set_eq:NN \__xeCJK_shipout_CJKecglue: \CJKecglue
    \cs_set_eq:NN \__xeCJK_shipout_check_for_glue: \xeCJK_check_for_glue:
    \cs_set_eq:NN \__xeCJK_shipout_boundary:w \xeCJK_CJK_and_Boundary:w
    \cs_set_protected:Npx \xeCJKOffVerbAddon
      {
        \__xeCJK_reset_char_class:n { FullLeft }
        \__xeCJK_reset_char_class:n { FullRight }
        \__xeCJK_reset_char_class:n { HalfLeft }
        \__xeCJK_reset_char_class:n { HalfLeft }
        \__xeCJK_reset_char_class:n { NormalSpace }
        \bool_if:NTF \l__xeCJK_xecglue_bool
          { \keys_set:nn { xeCJK / options } { xCJKecglue = true } }
          { \keys_set:nn { xeCJK / options } { xCJKecglue = false } }
        \exp_not:n
          {
            \cs_set_eq:NN \CJKglue   \__xeCJK_shipout_CJKglue:
            \cs_set_eq:NN \CJKecglue \__xeCJK_shipout_CJKecglue:
            \cs_set_eq:NN \xeCJK_check_for_glue: \__xeCJK_shipout_check_for_glue:
            \cs_set_eq:NN \xeCJK_CJK_and_Boundary:w \__xeCJK_shipout_boundary:w
          }
      }
    \xeCJK_add_to_shipout:n { \xeCJKOffVerbAddon }
    \keys_set:nn { xeCJK / options } { xCJKecglue = false }
  }
\cs_new_protected:Npn \__xeCJK_verb_CJK_and_Boundary:w
  { \xeCJK_class_group_end: \CJKecglue }
\cs_new_protected:Npn \__xeCJK_reset_char_class:n #1
  {
    \int_set:Nn \l__xeCJK_tmp_int { \xeCJK_class_num:n {#1} }
    \clist_map_inline:cn { c__xeCJK_#1_chars_clist }
      { \tex_XeTeXcharclass:D ##1 = \l__xeCJK_tmp_int }
  }
\bool_new:N \l__xeCJK_verb_addon_bool
\cs_new_eq:NN \CJKfixedspacing \xeCJKVerbAddon
\cs_new_protected:Npn \__xeCJK_set_verb_exspace:
  {
    \tl_if_exist:cTF { xeCJK/verb/\CJK@family/\curr@fontshape/\f@size }
      {
        \skip_set:Nn \l__xeCJK_verb_exspace_skip
          { \use:c { xeCJK/verb/\CJK@family/\curr@fontshape/\f@size } }
      }
      {
        \tl_set:Nx \l__xeCJK_current_coor_tl { \CJK@family/\curr@fontshape }
        \prop_get:NoNTF \g__xeCJK_scale_family_prop
          \l__xeCJK_current_coor_tl \l_xeCJK_family_tl
          {
            \xeCJK_switch_family:o { \l_xeCJK_family_tl }
            \skip_zero:N \l__xeCJK_verb_exspace_skip
          }
          {
            \group_begin: \xeCJK_select_font: \exp_args:NNo \group_end:
            \__xeCJK_set_verb_exspace:n
              { \dim_use:N \tex_fontcharwd:D \tex_font:D "4E00 ~ }
          }
      }
  }
\skip_new:N \l__xeCJK_verb_exspace_skip
\cs_new_protected:Npn \__xeCJK_set_verb_exspace:n #1
  {
    \skip_set:Nn \l__xeCJK_verb_exspace_skip
      { 2 \tex_fontdimen:D 2 ~ \tex_font:D - #1 }
    \dim_compare:nNnTF \l__xeCJK_verb_exspace_skip < \c_zero_dim
      {
        \skip_zero:N \l__xeCJK_verb_exspace_skip
        \exp_args:Nee \__xeCJK_set_verb_scale:nn
          { \dim_to_fp:n { 2 \tex_fontdimen:D 2 ~ \tex_font:D } }
          { \dim_to_fp:n {#1} }
      }
      {
        \tl_const:cx { xeCJK/verb/\CJK@family/\curr@fontshape/\f@size }
          { \skip_use:N \l__xeCJK_verb_exspace_skip }
      }
  }
\cs_new_protected:Npn \__xeCJK_set_verb_scale:nn #1#2
  {
    \fp_set:Nn \l__xeCJK_scale_factor_fp { #1 / #2 }
    \__xeCJK_warning:nxx { scale-factor }
      { \fp_eval:n { trunc ( \l__xeCJK_scale_factor_fp , 4 ) } }
      { \fp_eval:n {  ceil ( #2 / #1 , 4 ) } }
    \xeCJK_add_font_features:Nne \c_true_bool
      { } { Scale = { \fp_use:N \l__xeCJK_scale_factor_fp } }
    \prop_gput:Noo \g__xeCJK_scale_family_prop
      \l__xeCJK_current_coor_tl \l_xeCJK_family_tl
  }
\__xeCJK_msg_new:nn { scale-factor }
  {
    `\token_to_str:N \xeCJKVerbAddon'~may~not~work~properly.\\\\
    You~may~set~`Scale=#1'~to~CJKfamily~
    `\__xeCJK_msg_family_map:n { \l_xeCJK_family_tl }',\\
    or~set~`Scale=#2'~to~family~
    `\str_if_eq:eeTF \f@family \ttdefault
      { \token_to_str:N \ttdefault } { \f@family }'.
  }
\fp_new:N \l__xeCJK_scale_factor_fp
\prop_new:N \g__xeCJK_scale_family_prop
\cs_new_protected:Npn \xeCJK_setup_visible_space:
  {
    \xeCJK_make_boundary:
    \xeCJK_glyph_if_exist:NTF { ^^^^2423 }
      { \tl_set:Nn \l__xeCJK_visible_space_tl { ^^^^2423 } }
      {
        \int_compare:nNnTF { \tex_XeTeXfonttype:D \tex_font:D } = \c_zero_int
          {
            \tl_set:Nx \l__xeCJK_visible_space_tl
              {
                \str_if_eq:eeTF { \f@family } { \ttdefault }
                    { \c_catcode_other_space_tl }
                    { \exp_not:N \textvisiblespace }
              }
          }
          { \__xeCJK_visible_space_fallback: }
      }
    \cs_set_eq:NN \@xobeysp \l__xeCJK_visible_space_tl
  }
\tl_new:N \l__xeCJK_visible_space_tl
\cs_set_eq:NN \@setupverbvisiblespace \xeCJK_setup_visible_space:
\cs_new_protected:Npn \__xeCJK_visible_space_fallback:
  {
    \exp_args:Nc \__xeCJK_visible_space_fallback_auxi:N
      { xeCJK/space/\curr@fontshape/\f@size }
  }
\cs_new_protected:Npn \__xeCJK_visible_space_fallback_auxi:N #1
  {
    \cs_if_exist:NF #1
      { \__xeCJK_visible_space_fallback_auxii:N #1 }
    \tl_set:Nn \l__xeCJK_visible_space_tl {#1}
  }
\cs_new_protected:Npn \__xeCJK_visible_space_fallback_auxii:N #1
  {
    \group_begin:
      \exp_args:No \__xeCJK_set_visible_space_size:n
        { \dim_use:N \tex_fontdimen:D 2 ~ \tex_font:D }
      \cs_new_protected:Npx #1
        { \group_begin: \tex_the:D \tex_font:D ^^^^2423 \group_end: }
    \group_end:
  }
\cs_new_protected:Npn \__xeCJK_set_visible_space_size:n #1
  {
    \fontencoding { \UnicodeEncodingName }
    \tl_set:Nn \f@family { lmtt }
    \selectfont
    \dim_compare:nNnF {#1} = { \tex_fontdimen:D 2 ~ \tex_font:D }
      {
        \fontsize
          {
            \dim_eval:n
              {
                \f@size pt *
                \dim_ratio:nn {#1} { \tex_fontdimen:D 2 ~ \tex_font:D }
              }
          }
          { \f@baselineskip }
        \selectfont
      }
  }
\keys_define:nn { xeCJK / options }
  {
    LocalConfig .choice: ,
    LocalConfig / false   .code:n =
      { \bool_gset_false:N \g__xeCJK_config_bool } ,
    LocalConfig / true    .code:n =
      {
        \bool_gset_true:N \g__xeCJK_config_bool
        \tl_gset:Nn \g__xeCJK_config_name_tl { xeCJK }
      } ,
    LocalConfig / unknown .code:n =
      {
        \bool_gset_true:N \g__xeCJK_config_bool
        \tl_gset:Nx \g__xeCJK_config_name_tl { xeCJK - \l_keys_value_tl }
      } ,
    LocalConfig        .default:n = { true }
  }
\tl_new:N \g__xeCJK_config_name_tl
\bool_new:N \g__xeCJK_config_bool
\keys_define:nn { xeCJK / options }
  {
    CJKnumber         .code:n =
      { \__xeCJK_warning:nxx { option-deprecated } { \l_keys_key_tl } { CJKnumb } } ,
    indentfirst       .code:n =
      { \__xeCJK_warning:nxx { option-deprecated } { \l_keys_key_tl } { indentfirst } } ,
    normalindentfirst .code:n =
      { \__xeCJK_warning:nxx { option-deprecated } { \l_keys_key_tl } { } }
  }
\__xeCJK_msg_new:nn { option-deprecated }
  {
    The~`#1'~option~is~deprecated.\\
    \tl_if_empty:nF {#2}
      { You~may~load~the~package~`#2'~after~xeCJK~to~use~its~function.\\ }
  }
\keys_define:nn { xeCJK / options }
  {
    quiet .code:n =
      {
        \msg_redirect_module:nnn { xeCJK } { warning } { info }
        \msg_redirect_module:nnn { xeCJK } { info }    { none }
        \xeCJK_if_package_loaded:nF { fontspec }
          { \PassOptionsToPackage { quiet } { fontspec } }
      } ,
    silent .code:n =
      {
        \msg_redirect_module:nnn { xeCJK } { warning } { none }
        \msg_redirect_module:nnn { xeCJK } { info }    { none }
        \xeCJK_if_package_loaded:nF { fontspec }
          { \PassOptionsToPackage { silent } { fontspec } }
      } ,
    unknown .code:n =
      {
        \xeCJK_if_package_loaded:nTF { fontspec }
          { \__xeCJK_error:nx { key-unknown } { \l_keys_key_tl } }
          { \PassOptionsToPackage { \l_keys_key_tl } { fontspec } }
      }
  }
\__xeCJK_msg_new:nn { key-unknown }
  {
    Sorry,~but~xeCJK/options~does~not~have~a~key~called~`#1'.\\\\
    The~key~`#1'~is~being~ignored.
  }
\cs_new_eq:NN \CJKsymbol      \use:n
\cs_new_eq:NN \CJKpunctsymbol \use:n
\keys_set:nn { xeCJK / options }
  {
    CJKglue         = { \skip_horizontal:n { \c_zero_dim plus 0.08 \tex_baselineskip:D } } ,
    CJKecglue       = { ~ } ,
    xCJKecglue      = false ,
    CheckSingle     = false ,
    PlainEquation   = false ,
    CheckFullRight  = false ,
    CJKspace        = false ,
    CJKmath         = false ,
    xeCJKactive     = true  ,
    LocalConfig     = true  ,
    LoadFandol      = true  ,
    RubberPunctSkip = true  ,
    Verb            = env   ,
    EmboldenFactor  = 4     ,
    SlantFactor     = 0.167 ,
    PunctStyle      = quanjiao ,
    NewLineCS       = { \par \[ } ,
    EnvCS           = { \begin \end } ,
    WidowPenalty    = { 10 000 } ,
    NoBreakCS       = { \footnote \footnotemark \nobreak } ,
    KaiMingPunct    = { ^^^^3002 ^^^^ff0e ^^^^ff1f ^^^^ff01 } ,
    LongPunct       = { ^^^^2014 ^^^^2e3a ^^^^2025 ^^^^2026 } ,
    MiddlePunct     = { ^^^^2013 ^^^^2014 ^^^^2e3a ^^^^2027 ^^^^00b7 ^^^^30fb ^^^^ff65 } ,
    AllowBreakBetweenPuncts = false
  }
\defaultCJKfontfeatures { Script = CJK }
\xeCJKsetwidth { ^^^^2013 } { 0.5 em }
\cs_if_exist:NTF \ProcessKeyOptions
  { \ProcessKeyOptions [ xeCJK / options ] }
  {
    \RequirePackage { l3keys2e }
    \ProcessKeysOptions { xeCJK / options }
  }
\RequirePackage { fontspec } [ 2020/02/03 ]
\tl_const:Nx \c__xeCJK_encoding_tl { \g_fontspec_encoding_tl }
\keys_define:nn { xeCJK / options }
  {
    LocalConfig .code:n =
      { \__xeCJK_warning:nx { option-invalid } { \l_keys_key_tl } }
  }
\__xeCJK_msg_new:nn { option-invalid }
  {
    The~`#1'~option~can~only~be~set~in~the~optional~argument~to~the\\
    \token_to_str:N \usepackage \ command~when~xeCJK~is~being~loaded.\\\\
    Please~do~not~set~it~via~the~\token_to_str:N \xeCJKsetup \ command.
  }
\tl_if_exist:NF \CJKrmdefault { \tl_gset:Nn \CJKrmdefault { rm } }
\tl_if_exist:NF \CJKsfdefault { \tl_gset:Nn \CJKsfdefault { sf } }
\tl_if_exist:NF \CJKttdefault { \tl_gset:Nn \CJKttdefault { tt } }
\tl_new:N \l__xeCJK_family_default_init_tl
\cs_new_eq:NN \__xeCJK_family_default_wrap:n \use:n
\tl_set:Nx \l__xeCJK_family_default_init_tl
  {
    \exp_not:N \__xeCJK_family_default_wrap:n
      {
        \tl_if_exist:NTF \CJKfamilydefault
          { \exp_not:o \CJKfamilydefault }
          { \exp_not:N \CJKrmdefault }
      }
  }
\tl_gset_eq:NN \CJKfamilydefault \l__xeCJK_family_default_init_tl
\NewDocumentCommand \xeCJKsetup { +m }
  {
    \keys_set:nn { xeCJK / options } {#1}
    \tex_ignorespaces:D
  }
\NewDocumentCommand \xeCJKsetemboldenfactor { m }
  { \xeCJKsetup { EmboldenFactor = {#1} } }
\NewDocumentCommand \xeCJKsetslantfactor { m }
  { \xeCJKsetup { SlantFactor = {#1} } }
\NewDocumentCommand \punctstyle { m } { \xeCJKsetup { PunctStyle = {#1} } }
\NewDocumentCommand \xeCJKplainchr { } { \xeCJKsetup { PunctStyle = plain } }
\NewDocumentCommand \CJKsetecglue { m } { \xeCJKsetup { CJKecglue = {#1} } }
\cs_new_eq:NN \xeCJKsetecglue \CJKsetecglue
\NewDocumentCommand \CJKspace   { } { \xeCJKsetup { CJKspace = true } }
\NewDocumentCommand \CJKnospace { } { \xeCJKsetup { CJKspace = false } }
\NewDocumentCommand \xeCJKallowbreakbetweenpuncts { }
  { \xeCJKsetup { AllowBreakBetweenPuncts = true } }
\NewDocumentCommand \xeCJKnobreakbetweenpuncts { }
  { \xeCJKsetup { AllowBreakBetweenPuncts = false } }
\NewDocumentCommand \xeCJKenablefallback { }
  { \xeCJKsetup { AutoFallBack = true } }
\NewDocumentCommand \xeCJKdisablefallback { }
  { \xeCJKsetup { AutoFallBack = false } }
\NewDocumentCommand \xeCJKsetcharclass { m m m }
  {
    \xeCJK_set_char_class:nnn {#1} {#2} {#3}
    \xeCJKResetPunctClass
  }
\cs_new_protected:Npn \xeCJK@update@fam
  {
    \addto@hook \everymath
      {
        \__xeCJK_update_main_fam:
        \__xeCJK_update_block_fam:
      }
  }
\cs_new_protected:Npn \__xeCJK_update_main_fam:
  {
    \group_begin:
      \xeCJK_select_font:
      \exp_last_unbraced:NNNo \group_end:
    \tex_textfont:D \c_xeCJK_math_fam_int \tex_the:D \tex_font:D
  }
\cs_new_protected:Npn \__xeCJK_update_block_fam:
  {
    \prop_if_empty:NF \g__xeCJK_block_fam_prop
      {
        \prop_map_function:NN
          \g__xeCJK_block_fam_prop
          \__xeCJK_update_block_fam:nn
      }
  }
\cs_new_protected:Npn \__xeCJK_update_block_fam:nn #1#2
  {
    \int_set:Nn \l__xeCJK_fam_int {#2}
    \group_begin:
      \xeCJK_select_font:n {#1}
      \exp_last_unbraced:NNNo \group_end:
    \tex_textfont:D \l__xeCJK_fam_int \tex_the:D \tex_font:D
  }
\__xeCJK_after_end_preamble:n
  {
    \bool_lazy_and:nnT
      { \g__xeCJK_math_bool }
      { \cs_if_exist_p:N \Url@MathSetup }
      { \tl_put_right:Nn \Url@MathSetup { \xeCJK@update@fam } }
  }
\cs_new_protected:Npn \__xeCJK_math_robust:N #1
  {
    \group_begin: \exp_args:NcNc \group_end:
      { __xeCJK_math_robust_aux:NN } #1 { \cs_to_str:N #1 ~ }
  }
\cs_new_protected:Npn \__xeCJK_math_robust_aux:NN #1#2
  {
    \exp_args:Ne \str_case:nnTF { \cs_replacement_spec:N #1 }
      {
        { \x@protect #1 \protect #2 } { }
        { \protect #2 } { }
      }
      { \__xeCJK_math_robust:NN #1#2 }
      { \__xeCJK_math_robust:NN #1#1 }
  }
\cs_new_protected:Npn \__xeCJK_math_robust:NN #1#2
  {
    \str_if_eq:eeTF { \cs_argument_spec:N #2 } { }
      {
        \exp_args:No \tl_if_head_eq_meaning:nNTF {#2} \scan_stop:
          {
            \cs_gset_protected:Npx #1
              { \tl_tail:N #2 }
          }
          {
            \cs_if_eq:NNTF #1 \ensuremath
              {
                \cs_gset_protected:Npx #1
                  { \exp_not:o {#2} }
              }
              {
                \__xeCJK_warning:nxx { robust-failure }
                  { \token_to_str:N #1 } { \token_to_meaning:N #2 }
              }
          }
      }
      {
        \__xeCJK_warning:nxx { robust-failure }
          { \token_to_str:N #1 } { \token_to_meaning:N #2 }
      }
  }
\__xeCJK_msg_new:nnn { robust-failure }
  { xeCJK~can~not~make~`#1'~robust. }
  {
    The~current~meaning~of~`#1'~is:\\
    \iow_indent:n {#2}
  }
\cs_if_eq:NNTF \( \math
  {
    \__xeCJK_math_robust:N \(
    \cs_set_eq:NN \math \(
  }
  {
    \__xeCJK_math_robust:N \(
    \__xeCJK_math_robust:N \math
  }
\cs_if_eq:NNTF \) \endmath
  {
    \__xeCJK_math_robust:N \)
    \cs_set_eq:NN \endmath \)
  }
  {
    \__xeCJK_math_robust:N \)
    \__xeCJK_math_robust:N \endmath
  }
\__xeCJK_math_robust:N \ensuremath
\ctex_if_format_at_least:nTF { 2020/10/01 }
  {
    \cs_set_eq:NN \xeCJK@family \xeCJK_switch_family:e
    \ctex_gadd_ltxhook:nn { rmfamily }   { \xeCJK@family { \CJKrmdefault } }
    \ctex_gadd_ltxhook:nn { sffamily }   { \xeCJK@family { \CJKsfdefault } }
    \ctex_gadd_ltxhook:nn { ttfamily }   { \xeCJK@family { \CJKttdefault } }
    \ctex_gadd_ltxhook:nn { normalfont } { \xeCJK@family { \CJKfamilydefault } }
  }
  {
    \cs_if_exist:NTF \@rmfamilyhook
      {
        \cs_set_eq:NN \xeCJK@family \xeCJK_switch_family:e
        \g@addto@macro \@rmfamilyhook { \xeCJK@family { \CJKrmdefault } }
        \g@addto@macro \@sffamilyhook { \xeCJK@family { \CJKsfdefault } }
        \g@addto@macro \@ttfamilyhook { \xeCJK@family { \CJKttdefault } }
        \exp_args:Nc \g@addto@macro
          {
            \cs_if_exist:NTF \@defaultfamilyhook
              { @defaultfamilyhook } { normalfont ~ }
          }
          { \xeCJK@family { \CJKfamilydefault } }
      }
      {
        \RenewDocumentCommand \fontfamily { m }
          {
            \tl_set:Nx \f@family {#1}
            \xeCJK@fontfamily {#1}
          }
        \cs_new_protected:Npn \xeCJK@fontfamily #1
          {
            \str_if_eq:nnTF {#1} { \familydefault }
              { \xeCJK_switch_family:e { \CJKfamilydefault } }
              { \__xeCJK_update_family_aux: }
          }
        \cs_new_protected:Npn \__xeCJK_update_family_aux:
          {
            \str_case_e:nn { \f@family }
              {
                { \rmdefault }     { \xeCJK_switch_family:e { \CJKrmdefault } }
                { \sfdefault }     { \xeCJK_switch_family:e { \CJKsfdefault } }
                { \ttdefault }     { \xeCJK_switch_family:e { \CJKttdefault } }
                { \familydefault } { \xeCJK_switch_family:e { \CJKfamilydefault } }
              }
          }
      }
  }
\cs_new_eq:NN \xeCJK@fix@penalty \fix@penalty
\tl_replace_once:Nnn \xeCJK@fix@penalty { \@@italiccorr } { \xeCJK@italiccorr }
\tl_replace_once:Nnn \sw@slant          { \fix@penalty }  { \xeCJK@fix@penalty }
\cs_new_protected:Npn \xeCJK@italiccorr
  {
    \int_compare:nNnTF \tex_XeTeXinterchartokenstate:D > \c_zero_int
      { \xeCJK_italic_correction: }
      { \@@italiccorr }
  }
\cs_new_protected:Npn \xeCJK_italic_correction:
  { \__xeCJK_if_last_kern:T { \__xeCJK_italic_correction: } }
\cs_new_protected:Npn \__xeCJK_italic_correction:
  {
    \dim_case:nnF { \tex_lastkern:D }
      {
        { \__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:
        }
      }
      { \tex_italiccorrection:D }
  }
\cs_new_protected: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@alloc@intercharclass
\__xeCJK_after_end_preamble:n
  {
    \int_compare:nNnF
      { \c__xeCJK_class_begin_int + \seq_count:N \g__xeCJK_new_class_seq } =
      { \g__xeCJK_xetex_allocator_int }
      {
        \int_step_inline:nnn
          { \c__xeCJK_class_begin_int + 1 }
          { \g__xeCJK_xetex_allocator_int }
          {
            \seq_if_in:NnF \g__xeCJK_new_class_seq {#1}
              { \__xeCJK_set_others_toks:n {#1} }
          }
      }
  }
\cs_new_protected:Npn \__xeCJK_set_others_toks:n #1
  {
    \int_set:cn { \__xeCJK_class_csname:n { Others } } {#1}
    \seq_map_inline:Nn \g__xeCJK_CJK_class_seq
      {
        \xeCJK_copy_inter_class_toks:nnnn {##1} { Others } {##1} { NormalSpace }
        \xeCJK_copy_inter_class_toks:nnnn { Others } {##1} { NormalSpace } {##1}
        \xeCJK_app_inter_class_toks:nne {##1} { Others }
          { \xeCJK_get_inter_class_toks:nn { Default } { Others } }
        \xeCJK_pre_inter_class_toks:nne { Others } {##1}
          { \xeCJK_get_inter_class_toks:nn { Others } { Default } }
        \tl_if_blank:eT
          { \xeCJK_get_inter_class_toks:nn { Others } { Boundary } }
          {
            \xeCJK_copy_inter_class_toks:nnnn
              { Others } { Boundary } { Default } { Boundary }
          }
        \tl_if_blank:eT
          { \xeCJK_get_inter_class_toks:nn { Boundary } { Others } }
          {
            \xeCJK_copy_inter_class_toks:nnnn
              { Boundary } { Others } { Boundary } { Default }
          }
      }
  }
\cs_new_protected:Npn \__xeCJK_inactive_group_begin:
  { \group_begin: \makexeCJKinactive }
\cs_new_eq:NN \__xeCJK_inactive_group_end: \group_end:
\prop_const_from_keyval:Nn \c__xeCJK_ambiguous_char_prop
  {
    "00B7 = \textperiodcentered \textcentereddot \textcdot ,
    "2013 = \textendash ,
    "2014 = \textemdash ,
    "2018 = \textquoteleft \textgrq ,
    "2019 = \textquoteright ,
    "201C = \textquotedblleft \textgrqq ,
    "201D = \textquotedblright ,
    "2025 = \texthdotfor ,
    "2026 = \textellipsis ,
    "2027 = \texthyphenationpoint ,
    "2E3A = \texttwoemdash
  }
\__xeCJK_at_end_preamble:n { \__xeCJK_patch_text_command: }
\cs_new_protected:Npn \__xeCJK_patch_text_command:
  {
    \xeCJK_if_package_loaded:nTF { xunicode }
      { \__xeCJK_patch_xunicode_ambiguous_char: }
      {
        \exp_args:Ne \__xeCJK_patch_tuenc_ambiguous_char:n
          { \UnicodeEncodingName }
        \__xeCJK_patch_tuenc_accent:
        \__xeCJK_patch_tuenc_composite:
      }
  }
\cs_new_protected:Npn \__xeCJK_patch_xunicode_ambiguous_char:
  {
    \RequirePackage { xunicode-addon }
    \prop_map_inline:Nn \c__xeCJK_ambiguous_char_prop
      {
        \tl_map_inline:nn { ##2 }
          {
            \xunadd_set_begin_hook:nn { ####1 }
              { \__xeCJK_inactive_group_begin: }
            \xunadd_set_end_hook:nn { ####1 }
              { \__xeCJK_inactive_group_end: }
          }
      }
    \xunadd_append_begin_hook:n { \xeCJK_make_boundary: }
  }
\cs_new_protected:Npn \__xeCJK_patch_tuenc_ambiguous_char:n #1
  {
    \prop_map_inline:Nn \c__xeCJK_ambiguous_char_prop
      {
        \tl_map_inline:nn { ##2 }
          {
            \cs_if_exist:NF ####1
              { \DeclareTextSymbol ####1 {#1} { ##1 } }
            \__xeCJK_patch_ambiguous_char:nN {#1} ####1
          }
      }
  }
\cs_new_protected:Npn \__xeCJK_patch_ambiguous_char:nN #1#2
  {
    \exp_args:Ne \__xeCJK_patch_ambiguous_char:nn
      { #1 \token_to_str:N #2 }
      { #1 - #2 }
  }
\cs_new_protected:Npx \__xeCJK_patch_ambiguous_char:nNn #1#2#3
  {
    \exp_not:N \exp_args:Ne
    \exp_not:N \__xeCJK_patch_ambiguous_char:nn
      {
        \c_backslash_str #1
        \exp_not:N \token_to_str:N #2 -
        \exp_not:N \token_to_str:N #3
      }
      { #1 - #2#3 }
  }
\cs_new_protected:Npn \__xeCJK_patch_ambiguous_char:nn #1#2
  {
    \cs_if_free:cF {#1}
      { \exp_args:Nc \__xeCJK_patch_ambiguous_char:Nn {#1} {#2} }
  }
\cs_new_protected:Npn \__xeCJK_patch_ambiguous_char:Nn #1#2
  {
    \token_if_chardef:NTF #1
      {
        \prop_gput:Nne \c__xeCJK_ambiguous_slot_prop {#2}
          { \int_eval:n {#1} }
        \cs_set_protected:Npx #1
          { \__xeCJK_ambiguous_char:n { \tex_Uchar:D #1 } }
      }
      {
        \prop_gput:Nne \c__xeCJK_ambiguous_slot_prop {#2}
          { \int_eval:n { \exp_after:wN ` #1 } }
        \cs_set_protected:Npx #1
          { \__xeCJK_ambiguous_char:n { \exp_not:o {#1} } }
      }
  }
\cs_new_protected:Npn \__xeCJK_ambiguous_char:n #1
  {
    \int_compare:nNnTF \tex_XeTeXinterchartokenstate:D > \c_zero_int
      { \__xeCJK_inactive_group_begin: #1 \__xeCJK_inactive_group_end: }
      {#1}
  }
\prop_new:N \c__xeCJK_ambiguous_slot_prop
\cs_new_protected:Npn \xeCJK_text_composite_patch:
  {
    \str_if_eq:eeT { \f@encoding } { \UnicodeEncodingName }
      { \xeCJK_make_boundary: }
  }
\cs_new_protected:Npn \__xeCJK_patch_tuenc_composite:
  {
    \cs_set_nopar:Npn \@text@composite@x
      {
        \xeCJK_text_composite_patch:
        \cs_if_exist_use:NF
      }
  }
\group_begin:
\char_set_catcode_other:n { "A0 }
\cs_new_protected:Npn \__xeCJK_patch_tuenc_accent:
  {
    \cs_set_protected_nopar:Npn \add@unicode@accent ##1 ##2
      {
        \tl_if_blank:nTF { ##2 } { ^^a0 } { ##2 }
        \tex_Uchar:D \tex_numexpr:D ##1 \scan_stop:
      }
  }
\group_end:
\prop_const_from_keyval:Nn \c__xeCJK_middle_dot_prop
  {
    T2A = \cyrchvcrs ,
    T2B = \cyrchldsc ,
    T2C = \cyrabhha ,
    X2  = \cyrchldsc ,
    TS1 = \textperiodcentered ,
    LY1 = \textperiodcentered ,
    T1  = \r u ,
    T4  = \B t ,
    T5  = \` \ecircumflex
  }
\__xeCJK_at_end_preamble:n { \__xeCJK_patch_middle_dot: }
\cs_new_protected:Npn \__xeCJK_patch_middle_dot:
  {
    \prop_map_inline:Nn \c__xeCJK_middle_dot_prop
      { \__xeCJK_patch_middle_dot:nw { ##1 } ##2 \q_stop }
    \__xeCJK_patch_ambiguous_char:nNn { T5 } \` { \^ - e }
  }
\cs_new_protected:Npn \__xeCJK_patch_middle_dot:nw #1#2#3 \q_stop
  {
    \tl_if_empty:nTF {#3}
      { \__xeCJK_patch_ambiguous_char:nN {#1} #2 }
      { \__xeCJK_patch_ambiguous_char:nNn {#1} #2 {#3} }
  }
\__xeCJK_package_hook:nn { pifont }
  {
    \RenewDocumentCommand \Pifont { m }
      { \makexeCJKinactive \usefont { U } {#1} { m } { n } }
  }
\__xeCJK_package_hook:nn { unicode-math }
  {
    \prop_const_from_keyval:Nn \c__xeCJK_um_ambiguous_char_prop
      {
        "00B7 = \cdotp ,
        "2025 = \enleadertwodots ,
        "2026 = \unicodeellipsis
      }
    \cs_new_protected:Npn \__xeCJK_save_um_char:
      {
        \cs_set_protected:Npx \__xeCJK_restore_um_char:
          {
            \prop_map_function:NN
              \c__xeCJK_um_ambiguous_char_prop
              \__xeCJK_restore_um_char_aux:nn
          }
      }
    \cs_new_eq:NN \__xeCJK_restore_um_char: \prg_do_nothing:
    \cs_new:Npn \__xeCJK_restore_um_char_aux:nn #1#2
      {
        \__xeCJK_gset_mathcodenum:nn
          { \int_value:w #1 }
          { \int_value:w \tex_Umathcodenum:D #1 }
      }
    \cs_new_protected:Npn \__xeCJK_gset_mathcodenum:nn #1#2
      {
        \int_compare:nNnF { \tex_Umathcodenum:D #1 } = {#2}
          { \tex_global:D \tex_Umathcodenum:D #1 = #2 ~ }
      }
  }
\cs_new_protected:Npn \__xeCJK_patch_microtype_get_slot:
  {
    \cs_new_eq:NN \xeCJK@original@get@slot \MT@get@slot@
    \cs_set_eq:NN \MT@get@slot@ \xeCJK@microtype@get@slot
    \cs_set_eq:NN \MT@warn@unknown@once \use_none:n
  }
\cs_new_protected_nopar:Npn \xeCJK@microtype@get@slot
  {
    \int_compare:nNnT \MT@char < \c_zero_int
      { \__xeCJK_get_ambiguous_slot: }
    \xeCJK@original@get@slot
  }
\cs_new_protected:Npn \__xeCJK_get_ambiguous_slot:
  {
    \prop_get:NeNT \c__xeCJK_ambiguous_slot_prop
      { \MT@encoding - \tex_the:D \MT@toks } \l__xeCJK_tmp_tl
      { \cs_set_eq:NN \MT@char \l__xeCJK_tmp_tl }
  }
\cs_new_protected:Npn \xeCJK@microtype@restore@pickupfont
  { \__xeCJK_gadd_font_initial_hook:n { \MT@ltx@pickupfont } }
\__xeCJK_package_hook:nn { microtype }
  {
    \cs_if_free:NF \MT@get@slot@
      { \__xeCJK_patch_microtype_get_slot: }
    \MT@addto@setup { \xeCJK@microtype@restore@pickupfont }
  }
\__xeCJK_package_hook:nn { hyperref }
  {
    \pdfstringdefDisableCommands
      {
        \__xeCJK_gobble_CJKfamily:
        \xeCJK_cs_clear:N \__xeCJK_inactive_group_begin:
        \xeCJK_cs_clear:N \__xeCJK_inactive_group_end:
        \xeCJK_cs_clear:N \makexeCJKinactive
        \xeCJK_cs_clear:N \xeCJK_text_composite_patch:
      }
  }
\__xeCJK_package_hook:nn { cprotect }
  {
    \cs_if_free:NF \icprotect
      { \exp_after:wN \tex_let:D \cs:w cprotect \cs_end: \icprotect }
  }
\__xeCJK_package_hook:nn { listings }
  { \RequirePackage { xeCJK-listings } }
\__xeCJK_package_hook:nn { everysel }
  {
    \cs_if_exist:NF \@EverySelectfont@Legacy
      { \cs_undefine:c { ver@CJK . \c__xeCJK_package_ext_tl } }
  }
\ctex_at_begin_package:nn { CJKnumb }
  {
    \tl_new:N \l__xeCJK_CJK_version_tl
    \tl_set_eq:Nc \l__xeCJK_CJK_version_tl { ver@CJK . \c__xeCJK_package_ext_tl }
    \tl_set:cn { ver@CJK . \c__xeCJK_package_ext_tl } { 9999/99/99 }
    \cs_new_protected:Npn \CJKaddEncHook #1#2
      {
        \str_if_eq:nnT {#1} { \CJK@UnicodeEnc }
          {
            \group_begin:
              \cs_set_eq:NN \Unicode \xeCJK_unicode_char:nn
              \cs_set_eq:NN \def \xdef
              #2
            \group_end:
            \str_gset:Nn \CJK@tenthousand    { ^^^^4e07 }
            \str_gset:Nn \CJK@hundredmillion { ^^^^4ebf }
            \tl_if_exist:NF \CJK@UnicodeEnc
              { \tl_const:Nn \CJK@UnicodeEnc { UTF8 } }
            \cs_if_exist:NF \Unicode
              { \cs_new_eq:NN \Unicode \xeCJK_unicode_char:nn }
          }
      }
    \cs_new:Npn \xeCJK_unicode_char:nn #1#2
      { \tex_Uchar:D \tex_numexpr:D (#1) * 256 + (#2) \scan_stop: }
  }
\ctex_at_end_package:nn { CJKnumb }
  { \tl_set_eq:cN { ver@CJK . \c__xeCJK_package_ext_tl } \l__xeCJK_CJK_version_tl }
\bool_if:NT \g__xeCJK_config_bool
  {
    \ExplSyntaxOff
    \file_input:n { \g__xeCJK_config_name_tl .cfg }
    \ExplSyntaxOn
  }
%% 
%%     This package consists of the files xeCJK.dtx,
%%                                        full-stop.map,
%%                                        fullwidth-stop.map,
%%                                        han-simp.map,
%%                                        han-trad.map,
%%                  and the derived files xeCJK.pdf,
%%                                        xeCJK.sty,
%%                                        xeCJK.cfg,
%%                                        xeCJK.ins,
%%                                        xeCJKfntef.sty,
%%                                        xeCJK-listings.sty,
%%                                        xunicode-addon.sty,
%%                                        xunicode-extra.def,
%%                                        xeCJK-example-autofake.tex,
%%                                        xeCJK-example-fallback.tex,
%%                                        xeCJK-example-subCJKblock.tex,
%%                                        xeCJK-example-CJKecglue.tex,
%%                                        xeCJK-example-checksingle.tex,
%%                                        xeCJK-example-CJKfntef.tex,
%%                                        xeCJK-example-punctstyle.tex,
%%                                        xeCJK-example-verbatim.tex,
%%                                        xeCJK-example-CM.tex,
%%                                        xeCJK-example-listings.tex,
%%                                        xeCJK-example-mathblock.tex,
%%                                        xunicode-symbols.tex,
%%                                        xunicode-commands.tex,
%%                                        xunicode-combine-marks.tex,
%%                                        xunicode-symbols.pdf,
%%                                        full-stop.tec,
%%                                        fullwidth-stop.tec,
%%                                        han-simp.tec,
%%                                        han-trad.tec, and
%%                                        README.md.
%%
%% End of file `xeCJK.sty'.
