% Copyright 2026 Open-Guji (https://github.com/open-guji)
%
% Licensed under the Apache License, Version 2.0 (the "License");
% you may not use this file except in compliance with the License.
% You may obtain a copy of the License at
%
%     http://www.apache.org/licenses/LICENSE-2.0
%
% Unless required by applicable law or agreed to in writing, software
% distributed under the License is distributed on an "AS IS" BASIS,
% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
% See the License for the specific language governing permissions and
% limitations under the License.

\RequirePackage{core/luatex-cn-core-base}
\ProvidesExplPackage{core/luatex-cn-core-content}{2026/02/26} {0.3.1}{Content layout and border support}

% Centralized variables for content border settings
\bool_new:N \l__luatexcn_content_border_bool
\tl_new:N \l__luatexcn_content_border_thickness_tl
\tl_new:N \l__luatexcn_content_border_padding_top_tl
\tl_new:N \l__luatexcn_content_border_padding_bottom_tl

\bool_new:N \l__luatexcn_content_outer_border_bool
\tl_new:N \l__luatexcn_content_outer_border_thickness_tl
\tl_new:N \l__luatexcn_content_outer_border_sep_tl
\tl_new:N \l__luatexcn_content_border_color_tl

% Grid layout variables
\int_new:N \l__luatexcn_content_n_char_per_col_int
\tl_new:N \l__luatexcn_content_grid_width_tl
\tl_new:N \l__luatexcn_content_grid_height_tl
\tl_new:N \l__luatexcn_content_valign_tl
\tl_new:N \l__luatexcn_content_font_size_tl
\int_new:N \l__luatexcn_content_n_column_int
\tl_new:N \l__luatexcn_content_page_columns_tl
\tl_new:N \l__luatexcn_content_height_tl
\tl_new:N \l__luatexcn_content_col_spacing_tl
\tl_new:N \l__luatexcn_content_line_spacing_tl
\tl_new:N \l__luatexcn_content_banxin_ratio_tl

\tl_new:N \l__luatexcn_content_border_rgb_tl
\tl_new:N \l__luatexcn_content_background_rgb_tl
\tl_new:N \l__luatexcn_content_font_rgb_tl
\tl_new:N \l__luatexcn_content_font_color_tl
\tl_new:N \l__luatexcn_content_font_name_tl
\tl_new:N \l__luatexcn_content_font_features_tl
\tl_new:N \l__luatexcn_content_layout_mode_tl
\tl_new:N \l__luatexcn_content_inter_cell_gap_tl
\tl_new:N \l__luatexcn_content_cell_height_tl
\tl_new:N \l__luatexcn_content_cell_width_tl
\tl_new:N \l__luatexcn_content_cell_gap_tl

% Flexible column width and spacing (Free Mode)
\bool_new:N \l__luatexcn_content_auto_col_width_bool
\tl_new:N \l__luatexcn_content_col_spacing_top_tl
\tl_new:N \l__luatexcn_content_col_spacing_bottom_tl
\tl_new:N \l__luatexcn_content_para_spacing_tl

% Digital mode: async vbox + obeylines (each newline = column break)
\bool_new:N \l__luatexcn_content_digital_mode_bool

\keys_define:nn { luatexcn / content }
  {
    border .bool_set:N = \l__luatexcn_content_border_bool,
    border .initial:n = false,

    border-thickness .tl_set:N = \l__luatexcn_content_border_thickness_tl,
    border-thickness .initial:n = {0.4pt},

    border-padding-top .tl_set:N = \l__luatexcn_content_border_padding_top_tl,
    border-padding-top .initial:n = {0pt},

    border-padding-bottom .tl_set:N = \l__luatexcn_content_border_padding_bottom_tl,
    border-padding-bottom .initial:n = {0pt},

    outer-border .bool_set:N = \l__luatexcn_content_outer_border_bool,
    outer-border .initial:n = false,

    outer-border-thickness .tl_set:N = \l__luatexcn_content_outer_border_thickness_tl,
    outer-border-thickness .initial:n = {1pt},

    outer-border-sep .tl_set:N = \l__luatexcn_content_outer_border_sep_tl,
    outer-border-sep .initial:n = {2pt},

    border-color .tl_set:N = \l__luatexcn_content_border_color_tl,
    border-color .initial:n = {black},

    % Number of characters per column (used for auto-calculating grid-height)
    n-char-per-col .int_set:N = \l__luatexcn_content_n_char_per_col_int,
    n-char-per-col .initial:n = 0,

    % Grid layout settings
    grid-width .tl_set:N = \l__luatexcn_content_grid_width_tl,
    grid-width .initial:n = {1.5em},

    grid-height .tl_set:N = \l__luatexcn_content_grid_height_tl,
    grid-height .initial:n = {1.2em},

    vertical-align .tl_set:N = \l__luatexcn_content_valign_tl,
    vertical-align .initial:n = {center},

    font-size .tl_set:N = \l__luatexcn_content_font_size_tl,
    font-size .initial:n = {12pt},

    font .tl_set:N = \l__luatexcn_content_font_name_tl,
    font .initial:n = {},

    font-features .tl_set:N = \l__luatexcn_content_font_features_tl,
    font-features .initial:n = {},

    font-color .tl_set:N = \l__luatexcn_content_font_color_tl,
    font-color .initial:n = {},

    n-column .int_set:N = \l__luatexcn_content_n_column_int,
    n-column .initial:n = 8,

    page-columns .tl_set:N = \l__luatexcn_content_page_columns_tl,
    page-columns .initial:n = {},

    height .tl_set:N = \l__luatexcn_content_height_tl,
    height .initial:n = ,

    spacing-col .tl_set:N = \l__luatexcn_content_col_spacing_tl,
    spacing-col .initial:n = {},

    line-spacing .tl_set:N = \l__luatexcn_content_line_spacing_tl,
    line-spacing .initial:n = { \baselineskip },

    banxin-ratio .tl_set:N = \l__luatexcn_content_banxin_ratio_tl,
    banxin-ratio .initial:n = {0.7},

    layout-mode .tl_set:N = \l__luatexcn_content_layout_mode_tl,
    layout-mode .initial:n = {grid},

    inter-cell-gap .tl_set:N = \l__luatexcn_content_inter_cell_gap_tl,
    inter-cell-gap .initial:n = {0pt},

    cell-height .tl_set:N = \l__luatexcn_content_cell_height_tl,
    cell-height .initial:n = {},

    cell-width .tl_set:N = \l__luatexcn_content_cell_width_tl,
    cell-width .initial:n = {},

    cell-gap .tl_set:N = \l__luatexcn_content_cell_gap_tl,
    cell-gap .initial:n = {},

    % 灵活列宽和间距参数（Free Mode）
    auto-column-width .bool_set:N = \l__luatexcn_content_auto_col_width_bool,
    auto-column-width .initial:n = false,

    column-spacing-top .tl_set:N = \l__luatexcn_content_col_spacing_top_tl,
    column-spacing-top .initial:n = {0pt},

    column-spacing-bottom .tl_set:N = \l__luatexcn_content_col_spacing_bottom_tl,
    column-spacing-bottom .initial:n = {0pt},

    column-spacing .meta:n = {
      column-spacing-top = #1,
      column-spacing-bottom = #1
    },

    paragraph-spacing .tl_set:N = \l__luatexcn_content_para_spacing_tl,
    paragraph-spacing .initial:n = {0pt},

    % Digital mode: when true, BodyText uses async vbox + obeylines
    % (each source newline = one column break in PDF output)
    digital-mode .bool_set:N = \l__luatexcn_content_digital_mode_bool,
    digital-mode .initial:n = false,

    unknown .code:n = { \keys_set:nx { luatexcn / page } { \l_keys_key_tl = { #1 } } },
  }

% Internal: Sync content parameters to Lua (idempotent, can be called multiple times)
\cs_new_protected:Npn \__luatexcn_content_sync_to_lua:
  {
    % Convert colors to normalized RGB strings for Lua
    \__luatexcn_color_to_rgb:NN \l__luatexcn_content_border_color_tl \l__luatexcn_content_border_rgb_tl
    \__luatexcn_color_to_rgb_or_nil:NN \l__luatexcn_page_background_color_tl \l__luatexcn_content_background_rgb_tl
    \__luatexcn_color_to_rgb_or_nil:NN \l__luatexcn_content_font_color_tl \l__luatexcn_content_font_rgb_tl

    % Sync params to Lua (idempotent)
    \lua_now:e {
      require('core.luatex-cn-core-content').sync_params({
        border_on~=~\bool_if:NTF~\l__luatexcn_content_border_bool~{true}~{false},
        outer_border_on~=~\bool_if:NTF~\l__luatexcn_content_outer_border_bool~{true}~{false},
        border_thickness~=~[=[\luaescapestring{\l__luatexcn_content_border_thickness_tl}]=],
        outer_border_thickness~=~[=[\luaescapestring{\l__luatexcn_content_outer_border_thickness_tl}]=],
        outer_border_sep~=~[=[\luaescapestring{\l__luatexcn_content_outer_border_sep_tl}]=],
        border_padding_top~=~[=[\luaescapestring{\l__luatexcn_content_border_padding_top_tl}]=],
        border_padding_bottom~=~[=[\luaescapestring{\l__luatexcn_content_border_padding_bottom_tl}]=],
        n_column~=~\int_use:N~\l__luatexcn_content_n_column_int,
        grid_width~=~[=[\luaescapestring{\l__luatexcn_content_grid_width_tl}]=],
        grid_height~=~[=[\luaescapestring{\l__luatexcn_content_grid_height_tl}]=],
        page_columns~=~[=[\luaescapestring{\l__luatexcn_content_page_columns_tl}]=],
        vertical_align~=~[=[\luaescapestring{\l__luatexcn_content_valign_tl}]=],
        border_color~=~[=[\l__luatexcn_content_border_rgb_tl]=],
        background_color~=~[=[\l__luatexcn_content_background_rgb_tl]=],
        font_color~=~[=[\l__luatexcn_content_font_rgb_tl]=],
        font_size~=~[=[\luaescapestring{\l__luatexcn_content_font_size_tl}]=],
        layout_mode~=~[=[\luaescapestring{\l__luatexcn_content_layout_mode_tl}]=],
        inter_cell_gap~=~[=[\luaescapestring{\l__luatexcn_content_inter_cell_gap_tl}]=],
        cell_height~=~[=[\luaescapestring{\l__luatexcn_content_cell_height_tl}]=],
        cell_width~=~[=[\luaescapestring{\l__luatexcn_content_cell_width_tl}]=],
        cell_gap~=~[=[\luaescapestring{\l__luatexcn_content_cell_gap_tl}]=]
      })
    }
  }

\NewDocumentCommand{\contentSetup}{ m }
  {
    \keys_set:nn { luatexcn / content } { #1 }
  }

% Internal function to call Lua for Grid Layout
\cs_new_protected:Npn \__luatexcn_content_process_grid:n #1
  {
    % Put content into a vbox to preserve structure (paragraphs)
    \vbox_set:Nn \l_tmpa_box
      {
        \dim_set_eq:NN \hsize \c_max_dim
        \dim_set_eq:NN \hfuzz \c_max_dim
        \int_set:Nn \hbadness { 10000 }
        \dim_zero:N \parindent
        #1
      }
    \skip_zero:N \topskip

    % Final sync (catches any overrides from environment optional args)
    \__luatexcn_content_sync_to_lua:

    % Initialize content style (once per content block)
    \lua_now:n { require('core.luatex-cn-core-content').init_style() }

    % Process content
    \lua_now:e {
      core.process(\int_value:w \l_tmpa_box, {
        height~=~[=[\luaescapestring{\l__luatexcn_content_height_tl}]=],
        col_limit~=~\int_use:N~\l__luatexcn_content_n_char_per_col_int
      })
    }
  }

% Define Main Command with Key-Value interface
% Syntax: \Content[key=val]{text}
\NewDocumentCommand{\Content}{ O{} +m }
  {
    \nointerlineskip
    \par\noindent
    \group_begin:
    \keys_set:nn { luatexcn / content } { #1 }
    % Apply page geometry
    \luatexcn_apply_geometry:
    % Auto-layout for guji-style (computes grid dimensions if needed)
    \__luatexcn_content_auto_layout:
    % Common setup (font, skips, height fallback)
    \__luatexcn_content_setup_font:
    \__luatexcn_content_zero_skips:
    \__luatexcn_content_calc_height:
    % Apply background color if set
    \__luatexcn_content_apply_background:
    % Override \clearpage/\newpage for grid processing (scoped to this group)
    \__luatexcn_page_override_pagebreak:
    \__luatexcn_content_process_grid:n { #2 }
    \group_end:
  }

% BodyText environment — uses async vbox capture (\vbox_set:Nw) so that
% digital-mode obeylines can take effect during token reading.
% When digital-mode is false, behaves identically to the old sync approach.
\NewDocumentEnvironment{BodyText}{ O{} }
  {
    \nointerlineskip
    \par\noindent
    \group_begin:
    \keys_set:nn { luatexcn / content } { #1 }
    % Apply page geometry
    \luatexcn_apply_geometry:
    % Auto-layout for guji-style (computes grid dimensions if needed)
    \__luatexcn_content_auto_layout:
    % Common setup (font, skips, height fallback)
    \__luatexcn_content_setup_font:
    \__luatexcn_content_zero_skips:
    \__luatexcn_content_calc_height:
    % Apply background color if set
    \__luatexcn_content_apply_background:
    % Override \clearpage/\newpage for grid processing (scoped to this group)
    \__luatexcn_page_override_pagebreak:

    % Begin async vbox capture
    \vbox_set:Nw \l_tmpa_box
      \dim_set_eq:NN \hsize \c_max_dim
      \dim_set_eq:NN \hfuzz \c_max_dim
      \int_set:Nn \hbadness { 10000 }
      \dim_zero:N \parindent

      % Digital mode: activate obeylines for newline-as-column-break
      \bool_if:NT \l__luatexcn_content_digital_mode_bool
        { \luatexcndigitalobeylines }
  }
  {
    \vbox_set_end:
    \skip_zero:N \topskip

    % Final sync (catches any overrides from environment optional args)
    \__luatexcn_content_sync_to_lua:

    % Initialize content style (once per content block)
    \lua_now:n { require('core.luatex-cn-core-content').init_style() }

    % Process content through three-stage pipeline
    \lua_now:e {
      core.process( \int_value:w \l_tmpa_box , {
        height~=~[=[\luaescapestring{\l__luatexcn_content_height_tl}]=],
        col_limit~=~\int_use:N~\l__luatexcn_content_n_char_per_col_int
      })
    }
    \group_end:
  }

% Space Command - inserts ideographic space characters (U+3000) for vertical layout
% Syntax: \Space or \Space[n]  (n = number of grid cells, default 1)
\NewDocumentCommand{\Space}{ O{1} }
  {
    \prg_replicate:nn { #1 } { \char"3000\relax }
    \ignorespaces
  }

% MissingChar Command - inserts a white square (U+25A1 □) as placeholder for missing/illegible characters
% Syntax: \MissingChar or \MissingChar[n]  (n = number of missing characters, default 1)
\NewDocumentCommand{\MissingChar}{ O{1} }
  {
    \prg_replicate:nn { #1 } { \char"25A1\relax }
    \ignorespaces
  }

% ============================================================================
% Shared Helper Functions for Content Environments
% ============================================================================

% Helper: Setup font if font is specified
% Uses RawFeature={+vert,+vrt2} as default for CJK vertical typesetting
\cs_new_protected:Npn \__luatexcn_content_setup_font:
  {
    \tl_if_empty:NF \l__luatexcn_content_font_name_tl
      {
        \tl_if_empty:NTF \l__luatexcn_content_font_features_tl
          {
            \exp_args:NV \setmainfont \l__luatexcn_content_font_name_tl [RawFeature={+vert,+vrt2}]
          }
          {
            \exp_args:NV \setmainfont \l__luatexcn_content_font_name_tl [\l__luatexcn_content_font_features_tl]
          }
      }
    \fontsize{\l__luatexcn_content_font_size_tl}{\l__luatexcn_content_line_spacing_tl}\selectfont
  }

% Helper: Zero out vertical skips for exact grid layout
\cs_new_protected:Npn \__luatexcn_content_zero_skips:
  {
    \dim_zero:N \topskip
    \dim_zero:N \parskip
    \dim_zero:N \partopsep
    \dim_zero:N \topsep
    \dim_zero:N \itemsep
    \dim_zero:N \parsep
    \dim_zero:N \baselineskip
    \dim_zero:N \lineskip
    \dim_zero:N \lineskiplimit
    \dim_zero:N \parindent
  }

% Helper: Calculate height from page dimensions if not set
\cs_new_protected:Npn \__luatexcn_content_calc_height:
  {
    \tl_if_empty:NT \l__luatexcn_content_height_tl
      {
        \tl_set:Nx \l__luatexcn_content_height_tl
          {
            \dim_eval:n
              {
                \l__luatexcn_page_paper_height_tl
                - \l__luatexcn_page_margin_top_tl
                - \l__luatexcn_page_margin_bottom_tl
              }
          }
      }
  }

% Helper: Apply background color if specified
\cs_new_protected:Npn \__luatexcn_content_apply_background:
  {
    \tl_if_empty:NF \l__luatexcn_page_background_color_tl
      {
        \tl_if_in:NnTF \l__luatexcn_page_background_color_tl { , }
          { \pagecolor [RGB] { \l__luatexcn_page_background_color_tl } }
          { \pagecolor { \l__luatexcn_page_background_color_tl } }
      }
  }

% Helper: Auto-layout calculation for guji-style content
% Calls Lua to compute grid dimensions based on page and content parameters
% Only runs if n-column > 0 (guji-style with banxin)
\cs_new_protected:Npn \__luatexcn_content_auto_layout:
  {
    \int_compare:nNnT { \l__luatexcn_content_n_column_int } > { 0 }
      {
        \lua_now:e {
          require('core.luatex-cn-core-content').guji_auto_layout({
            border_on~=~\bool_if:NTF~\l__luatexcn_content_border_bool~{true}~{false},
            outer_border_on~=~\bool_if:NTF~\l__luatexcn_content_outer_border_bool~{true}~{false},
            border_thickness~=~"\l__luatexcn_content_border_thickness_tl",
            outer_border_thickness~=~"\l__luatexcn_content_outer_border_thickness_tl",
            outer_border_sep~=~"\l__luatexcn_content_outer_border_sep_tl",
            border_padding_top~=~"\l__luatexcn_content_border_padding_top_tl",
            border_padding_bottom~=~"\l__luatexcn_content_border_padding_bottom_tl",
            n_column~=~\int_use:N~\l__luatexcn_content_n_column_int,
            n_char_per_col~=~\int_use:N~\l__luatexcn_content_n_char_per_col_int,
            grid_height~=~"\l__luatexcn_content_grid_height_tl",
            banxin_ratio~=~"\l__luatexcn_content_banxin_ratio_tl",
          })
        }
      }
  }

\ExplSyntaxOff

% Digital obeylines helper: make ^^M (newline) produce column breaks.
% MUST be defined outside ExplSyntax context because the \lowercase trick
% requires ~ to have catcode 13 (active), not catcode 10 (space).
\def\luatexcndigitalobeylines{%
  \obeylines
  \begingroup\lccode`\~=`\^^M
  \lowercase{\endgroup\def~{\luatexcnRestoreTempIndent\leavevmode\par\penalty-10005\relax}}%
}

% ============================================================
% Chinese aliases / 中文别名
% ============================================================
% Simplified Chinese / 简体
\NewCommandCopy{\空格}{\Space}
\NewCommandCopy{\缺字}{\MissingChar}
\NewCommandCopy{\内容设置}{\contentSetup}
\NewCommandCopy{\内容}{\Content}
\NewEnvironmentCopy{正文}{BodyText}
% Traditional Chinese / 繁体
\NewCommandCopy{\內容設置}{\contentSetup}
\NewCommandCopy{\內容}{\Content}

% ============================================================
% Chinese key aliases / 中文 Key 别名
% ============================================================
\ExplSyntaxOn
\keys_define:nn { luatexcn / content }
  {
    % 简体
    边框 .bool_set:N = \l__luatexcn_content_border_bool,
    边框粗细 .tl_set:N = \l__luatexcn_content_border_thickness_tl,
    边框上填充 .tl_set:N = \l__luatexcn_content_border_padding_top_tl,
    边框下填充 .tl_set:N = \l__luatexcn_content_border_padding_bottom_tl,
    外框 .bool_set:N = \l__luatexcn_content_outer_border_bool,
    外框粗细 .tl_set:N = \l__luatexcn_content_outer_border_thickness_tl,
    外框间距 .tl_set:N = \l__luatexcn_content_outer_border_sep_tl,
    边框色 .tl_set:N = \l__luatexcn_content_border_color_tl,
    每行字数 .int_set:N = \l__luatexcn_content_n_char_per_col_int,
    格宽 .tl_set:N = \l__luatexcn_content_grid_width_tl,
    格高 .tl_set:N = \l__luatexcn_content_grid_height_tl,
    纵对齐 .tl_set:N = \l__luatexcn_content_valign_tl,
    字号 .tl_set:N = \l__luatexcn_content_font_size_tl,
    字体 .tl_set:N = \l__luatexcn_content_font_name_tl,
    字体特性 .tl_set:N = \l__luatexcn_content_font_features_tl,
    字体颜色 .tl_set:N = \l__luatexcn_content_font_color_tl,
    行数 .int_set:N = \l__luatexcn_content_n_column_int,
    版面行数 .tl_set:N = \l__luatexcn_content_page_columns_tl,
    高度 .tl_set:N = \l__luatexcn_content_height_tl,
    列间距 .tl_set:N = \l__luatexcn_content_col_spacing_tl,
    行距 .tl_set:N = \l__luatexcn_content_line_spacing_tl,
    版心比例 .tl_set:N = \l__luatexcn_content_banxin_ratio_tl,
    布局模式 .tl_set:N = \l__luatexcn_content_layout_mode_tl,
    字间隙 .tl_set:N = \l__luatexcn_content_inter_cell_gap_tl,
    字格高 .tl_set:N = \l__luatexcn_content_cell_height_tl,
    字格宽 .tl_set:N = \l__luatexcn_content_cell_width_tl,
    字格间距 .tl_set:N = \l__luatexcn_content_cell_gap_tl,
    自动列宽 .bool_set:N = \l__luatexcn_content_auto_col_width_bool,
    列上间距 .tl_set:N = \l__luatexcn_content_col_spacing_top_tl,
    列下间距 .tl_set:N = \l__luatexcn_content_col_spacing_bottom_tl,
    列间距 .meta:n = { column-spacing-top = #1, column-spacing-bottom = #1 },
    段间距 .tl_set:N = \l__luatexcn_content_para_spacing_tl,
    数字模式 .bool_set:N = \l__luatexcn_content_digital_mode_bool,
    % 繁体（与简体不同形的）
    邊框 .bool_set:N = \l__luatexcn_content_border_bool,
    邊框粗細 .tl_set:N = \l__luatexcn_content_border_thickness_tl,
    外框粗細 .tl_set:N = \l__luatexcn_content_outer_border_thickness_tl,
    外框間距 .tl_set:N = \l__luatexcn_content_outer_border_sep_tl,
    邊框色 .tl_set:N = \l__luatexcn_content_border_color_tl,
    每行字數 .int_set:N = \l__luatexcn_content_n_char_per_col_int,
    格寬 .tl_set:N = \l__luatexcn_content_grid_width_tl,
    縱對齊 .tl_set:N = \l__luatexcn_content_valign_tl,
    字號 .tl_set:N = \l__luatexcn_content_font_size_tl,
    字體 .tl_set:N = \l__luatexcn_content_font_name_tl,
    字體特性 .tl_set:N = \l__luatexcn_content_font_features_tl,
    字體顏色 .tl_set:N = \l__luatexcn_content_font_color_tl,
    行數 .int_set:N = \l__luatexcn_content_n_column_int,
    版面行數 .tl_set:N = \l__luatexcn_content_page_columns_tl,
    列間距 .tl_set:N = \l__luatexcn_content_col_spacing_tl,
    佈局模式 .tl_set:N = \l__luatexcn_content_layout_mode_tl,
    字間隙 .tl_set:N = \l__luatexcn_content_inter_cell_gap_tl,
    字格寬 .tl_set:N = \l__luatexcn_content_cell_width_tl,
    字格間距 .tl_set:N = \l__luatexcn_content_cell_gap_tl,
    自動列寬 .bool_set:N = \l__luatexcn_content_auto_col_width_bool,
    列上間距 .tl_set:N = \l__luatexcn_content_col_spacing_top_tl,
    列下間距 .tl_set:N = \l__luatexcn_content_col_spacing_bottom_tl,
    列間距 .meta:n = { column-spacing-top = #1, column-spacing-bottom = #1 },
    段間距 .tl_set:N = \l__luatexcn_content_para_spacing_tl,
    數字模式 .bool_set:N = \l__luatexcn_content_digital_mode_bool,
  }
\ExplSyntaxOff

\endinput
