Skip to content
Snippets Groups Projects
njuthesis.dtx 194 KiB
Newer Older
%    \begin{macrocode}
\msg_new:nnn { njuthesis } { unsupported-engine }
  {
    The~ njuthesis~ class~ requires~ either~
Yu Xiong's avatar
Yu Xiong committed
    XeTeX~ or~ LuaTeX. \\
    "#1"~ is~ not~ supported~ at~ present.~
    You~ must~ change~ your~ typesetting~ engine~
Yu Xiong's avatar
Yu Xiong committed
    to~ "xelatex"~ or~ "lualatex".
\bool_lazy_or:nnF
  { \sys_if_engine_xetex_p:  }
  { \sys_if_engine_luatex_p: }
  { \msg_fatal:nnx { njuthesis } { unsupported-engine } { \c_sys_engine_str } }
%    \end{macrocode}
%
% \subsection{定义变量}
%
% 在变量名称中,|l| 代表局部变量,|g| 代表全局变量,|c| 代表常量,最后一个下划线后的内容指示其类型。
Yu Xiong's avatar
Yu Xiong committed
% \begin{variable}{\l_@@_tmp_box,\l_@@_tmp_clist,\l_@@_tmp_dim,
%   \l_@@_tmp_int,\l_@@_tmp_seq,\l_@@_tmp_tl}
% 临时变量。
%    \begin{macrocode}
Yu Xiong's avatar
Yu Xiong committed
\box_new:N   \l_@@_tmp_box
\clist_new:N \l_@@_tmp_clist
\dim_new:N   \l_@@_tmp_dim
Yu Xiong's avatar
Yu Xiong committed
\int_new:N   \l_@@_tmp_int
\seq_new:N   \l_@@_tmp_seq
\tl_new:N    \l_@@_tmp_tl
% \end{variable}
Yu Xiong's avatar
Yu Xiong committed
% \begin{variable}{\g_@@_info_degree_int,
%   \l_@@_info_diploma_int,\g_@@_info_type_int}
% 用于存储学位类型的变量。
Yu Xiong's avatar
Yu Xiong committed
\int_new:N \g_@@_info_degree_int
\int_new:N \l_@@_info_diploma_int
Yu Xiong's avatar
Yu Xiong committed
\int_new:N \g_@@_info_type_int
%    \end{macrocode}
% \end{variable}
%
% \begin{variable}{\l_@@_info_degree_tl,\l_@@_info_diploma_tl,
%   \l_@@_info_type_tl}
% 用于存储学位名称的变量。
%    \begin{macrocode}
\tl_new:N  \l_@@_info_degree_tl
\tl_new:N  \l_@@_info_diploma_tl
\tl_new:N  \l_@@_info_type_tl
% \end{variable}
% \begin{variable}{\g_@@_twoside_bool}
% 定义用于判断是否使用双面模式的变量,初始值为使用双面模式。
%    \begin{macrocode}
\bool_new:N \g_@@_twoside_bool
\bool_set_true:N \g_@@_twoside_bool
%    \end{macrocode}
% \end{variable}
%
% \begin{variable}{\l_@@_second_supv_bool}
% 定义用于判断是否有第二导师的变量。
%    \begin{macrocode}
\bool_new:N \l_@@_second_supv_bool
%    \end{macrocode}
% \end{variable}
%
% \begin{variable}{\l_@@_info_supv_full_tl,\l_@@_info_supv_full_en_tl}
% 用于存储导师姓名加职称的变量。
\tl_new:N \l_@@_info_supv_full_tl
\tl_new:N \l_@@_info_supv_full_en_tl
% \end{variable}
% \begin{variable}{\g_@@_latin_font_tl,\g_@@_cjk_font_tl}
% 用于存储所使用字体名称的全局变量。
%    \begin{macrocode}
\tl_new:N \g_@@_latin_font_tl
\tl_new:N \g_@@_cjk_font_tl
%    \end{macrocode}
% \end{variable}
%
% \begin{variable}{\g_@@_config_tl}
% 保存配置文件名称。默认为空。
%    \begin{macrocode}
\tl_new:N \g_@@_config_tl
%    \end{macrocode}
% \end{variable}
%
% \begin{variable}{\g_@@_theorem_type_clist}
% 定理类型。
%    \begin{macrocode}
\clist_new:N \g_@@_theorem_type_clist
%    \end{macrocode}
% \end{variable}
%
% \begin{variable}{\l_@@_info_sm_date_tl,\l_@@_info_sm_date_en_tl,
% \l_@@_info_df_date_tl}
% 用于存储格式化后的论文提交日期和答辩日期的变量。
%    \begin{macrocode}
\tl_new:N \l_@@_info_sm_date_tl
\tl_new:N \l_@@_info_sm_date_en_tl
\tl_new:N \l_@@_info_df_date_tl
%    \end{macrocode}
% \end{variable}
%
% \begin{variable}{\c_@@_today_tl}
% 编译当天日期,格式为 |yyyy-mm-dd|。
%    \begin{macrocode}
\tl_const:Nx \c_@@_today_tl
  {
    \int_to_arabic:n { \c_sys_year_int  } -
    \int_to_arabic:n { \c_sys_month_int } -
    \int_to_arabic:n { \c_sys_day_int   }
  }
%    \end{macrocode}
% \end{variable}
%
Yu Xiong's avatar
Yu Xiong committed
% \begin{variable}{\c_@@_name_type_clist,\c_@@_name_type_en_clist,
%   \c_@@_name_degree_clist,\c_@@_name_degree_en_clist}
% 论文类型与学位类型。
%    \begin{macrocode}
\clist_const:Nn \c_@@_name_type_clist
  { 毕业论文, 毕业设计 }
\clist_const:Nn \c_@@_name_type_en_clist
  { THESIS, DESIGN }
\clist_const:Nn \c_@@_name_degree_clist
  { 学士, 硕士, 硕士专业, 博士 }
\clist_const:Nn \c_@@_name_degree_en_clist
  { Bachelor, Master, Master, Doctor~of~Philosophy }
%    \end{macrocode}
% \end{variable}
%
% \begin{variable}{\c_@@_month_en_clist}
% 英文月份名称。
%    \begin{macrocode}
\clist_const:Nn \c_@@_month_en_clist
  {
    January, February, March, April, May, June,
    July, August, September, October, November, December
  }
%    \end{macrocode}
% \end{variable}
%
% \subsection{内部函数}
% \begin{macro}{\@@_quad:,\@@_qquad:}
% 等价于 \LaTeXe{} 中的 \tn{quad} 和 \tn{qquad}。
Yu Xiong's avatar
Yu Xiong committed
%    \begin{macrocode}
\cs_new:Nn \@@_quad:  { \skip_horizontal:n { 1 em } }
\cs_new:Nn \@@_qquad: { \skip_horizontal:n { 2 em } }
Yu Xiong's avatar
Yu Xiong committed
%    \end{macrocode}
Yu Xiong's avatar
Yu Xiong committed
%
% \begin{macro}{\@@_vskip:,\@@_hskip:}
% 生成一个较小的 skip。
Yu Xiong's avatar
Yu Xiong committed
%    \begin{macrocode}
\cs_new:Nn \@@_vskip: { \skip_vertical:n   { 1   ex } }
\cs_new:Nn \@@_hskip: { \skip_horizontal:n { 0.3 em } }
Yu Xiong's avatar
Yu Xiong committed
%    \end{macrocode}
% \end{macro}
% \begin{macro}{\@@_vskip:N}
% 类似于 \hologo{LaTeX2e} 中的 \tn{vspace*},
% 从上一个页面元素底部开始生成 |skip|。
%    \begin{macrocode}
\cs_new_protected:Npn \@@_vskip:N #1
  {
    \hrule height \c_zero_dim
    \nobreak
    \skip_vertical:n { - \baselineskip - \lineskip }
    \skip_vertical:N #1
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_define_name:nn,\@@_define_name:nnn}
% 用来定义默认名称的辅助函数。
%    \begin{macrocode}
\cs_new_protected:Npn \@@_define_name:nn #1#2
  { \tl_const:cn { c_@@_name_ #1    _tl } { #2 } }
\cs_new_protected:Npn \@@_define_name:nnn #1#2#3
  {
    \tl_const:cn { c_@@_name_ #1    _tl } { #2 }
    \tl_const:cn { c_@@_name_ #1 _en_tl } { #3 }
% \begin{macro}{\@@_add_theorem_type:nnn}
% 添加已知的定理环境类型。
%    \begin{macrocode}
\cs_new_protected:Npn \@@_add_theorem_type:nnn #1#2#3
  {
    \clist_gput_right:Nn \g_@@_theorem_type_clist { #1 }
    \@@_define_name:nnn { #1 } { #2 } { #3 }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_name:n,\@@_info:n}
Yu Xiong's avatar
Yu Xiong committed
% 根据变量名调用名称和内容信息。
\cs_new:Npn \@@_name:n #1 { \tl_use:c { c_@@_name_ #1 _tl } }
\cs_new:Npn \@@_info:n #1 { \tl_use:c { l_@@_info_ #1 _tl } }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_ulined_center_box:nn}
% 带有下划线的水平盒子。
% \begin{arguments}
%   \item 宽度,|dim| 型变量
%   \item 内容,可带有格式
% \end{arguments}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_ulined_center_box:nn #1#2
    \mode_leave_vertical:
    \rule [ -0.5 ex ] { #1 } { 0.4 pt }
    \skip_horizontal:n { -#1 }
    \hbox_to_wd:nn { #1 } { \hfil #2 \hfil }
%    \end{macrocode}
% \end{macro}
%
Yu Xiong's avatar
Yu Xiong committed
% \begin{macro}{\@@_spread_box:nnn,\@@_spread_box:nnV,\@@_spread_box:nnx}
% 分散对齐的水平盒子。
% \begin{arguments}
%   \item 宽度,|dim| 型变量
%   \item 格式
%   \item 内容,不可带有格式
% \end{arguments}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_spread_box:nnn #1#2#3
    \mode_leave_vertical:
    \hbox_to_wd:nn { #1 }
Yu Xiong's avatar
Yu Xiong committed
      { #2 \tl_map_inline:nn { #3 } { ##1 \hfil } \unskip }
Yu Xiong's avatar
Yu Xiong committed
\cs_generate_variant:Nn \@@_spread_box:nnn { nnV }
\cs_generate_variant:Nn \@@_spread_box:nnn { nnx }
% \end{macro}
% \begin{macro}{\@@_multiline_box:nnnn}
% 多行固定长度的下划线内容,用于国家图书馆封面。
% \begin{arguments}
%   \item 用于循环的 |int| 型变量
%   \item 内容,|clist| 型变量
%   \item 行数
%   \item 宽度,|dim| 型变量
% \end{arguments}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_multiline_box:nnnn #1#2#3#4
    \int_set:Nn #1 { 1 }
    \int_do_until:nn { #1 > #3 }
      {
        \@@_ulined_center_box:nn { #4 }
          { \clist_item:Nn #2 { #1 } } \\
        \int_incr:N #1
      }
  }
%    \end{macrocode}
% \end{macro}
% \begin{macro}{\@@_put_inempty_seg:nnn}
% 将固定长度的非空字符串插入 |clist|。
% \begin{arguments}
%   \item 原始字符串
%   \item 起始位置
%   \item 长度
% \end{arguments}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_put_inempty_seg:nnn #1#2#3
  {
    \tl_set:Nx \l_@@_tmp_tl
      { \tl_range:Nnn { #1 } { #2 } { #2 + #3 - 1 } }
    \tl_if_empty:NF \l_@@_tmp_tl
      { \clist_put_right:NV \l_@@_tmp_clist { \l_@@_tmp_tl } }
  }
%    \end{macrocode}
% \end{macro}
% \begin{macro}{\@@_uline:n}
% 指定宽度的下划线。
% \begin{arguments}
%   \item 宽度,|dim| 型变量
% \end{arguments}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_uline:n #1
  {
    \mode_leave_vertical:
    \rule [ -0.5 ex ] { #1 } { 0.4 pt }
    \skip_horizontal:n { -#1 }
  }
%    \end{macrocode}
% \end{macro}
% \begin{macro}{\@@_uuline:n}
% 指定宽度的双层下划线。
% \begin{arguments}
%   \item 宽度,|dim| 型变量
% \end{arguments}
Yu Xiong's avatar
Yu Xiong committed
%    \begin{macrocode}
\cs_new_protected:Npn \@@_uuline:n #1
  {
    \mode_leave_vertical:
    \rule [ -0.5 ex ] { #1 } { 0.4 pt }
    \skip_horizontal:n { -#1 }
    \rule [ -0.6 ex ] { #1 } { 0.4 pt }
    \skip_horizontal:n { -#1 }
  }
Yu Xiong's avatar
Yu Xiong committed
%    \end{macrocode}
% \end{macro}
Yu Xiong's avatar
Yu Xiong committed
%
% \begin{macro}{\@@_get_text_width:Nn,\@@_get_text_width:NV}
% 获取文本宽度。
% \begin{arguments}
%   \item 存储宽度的 |dim| 型变量
%   \item 文本
% \end{arguments}
% 将内容放入 \tn{hbox} 后读取其宽度,存入 |dim| 型变量。
Yu Xiong's avatar
Yu Xiong committed
%    \begin{macrocode}
\cs_new:Npn \@@_get_text_width:Nn #1#2
  {
Yu Xiong's avatar
Yu Xiong committed
    \hbox_set:Nn \l_@@_tmp_box {#2}
    \dim_set:Nn #1 { \box_wd:N \l_@@_tmp_box }
  }
\cs_generate_variant:Nn \@@_get_text_width:Nn { NV }
Yu Xiong's avatar
Yu Xiong committed
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_full_uline:Nn,\@@_full_uline:NV}
% 横跨整页的下划线。
% \begin{arguments}
%   \item 宽度,|dim| 型变量
%   \item 文本
% \end{arguments}
% 先使用 \cs{@@_get_text_width:Nn} 获取文本内容宽度,该宽度存储在调用的 |dim| 型变量中。随后输出文本内容。
%    \begin{macrocode}
\cs_new_protected:Npn \@@_full_uline:Nn #1#2
  {
    \@@_get_text_width:Nn #1 { #2 }
    \dim_set:Nn #1 { \textwidth - #1 } #2
  }
\cs_generate_variant:Nn \@@_full_uline:Nn { NV }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_uline_entry:Nnnn}
% 生成占整页宽度的下划线条目。
% \begin{arguments}
%   \item 宽度,|dim| 型变量
%   \item 文本
%   \item 文本
%   \item 分隔符
% \end{arguments}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_uline_entry:Nnnn #1#2#3#4
  {
    \@@_full_uline:NV #1 { \@@_name:n { #2 } #4 }
    \@@_ulined_center_box:nn { #1 } { \@@_info:n { #3 } }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_uline_bientry:Nnnn}
% 生成占半页宽度的下划线条目。
% \begin{arguments}
%   \item 宽度,|dim| 型变量
%   \item 文本
%   \item 文本
%   \item 分隔符
% \end{arguments}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_uline_bientry:Nnnn #1#2#3#4
  {
    \@@_full_uline:NV #1 { \@@_name:n { #2 } #4 }
    \dim_sub:Nn #1 { \textwidth / 2 }
    \@@_ulined_center_box:nn { #1 } { \@@_info:n { #3 } }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_date:www,\@@_date_en:www}
% 将形如 |yyyy-mm-dd| 的 ISO 日期格式字符串转化为日期表示。该格式符合国际标准 ISO 8601 以及国内标准 GB/T 7408--2005《数据元和交换格式 信息交换 日期和时间表示法》。
% \begin{arguments}
%   \item 年份
%   \item 月份
%   \item 日期
% \end{arguments}
% 中文日期表示通过封装 \pkg{zhnumber} 的内部函数实现;英文日期表示通过用于研究生英文封面。其中,变量类型 |w| 表明参数符合特定语法格式。
%    \begin{macrocode}
\cs_new:Npn \@@_date:www #1-#2-#3 \q_stop
  { \__zhnum_date_aux:nnn { #1 } { #2 } { #3 } }
\cs_new:Npn \@@_date_en:www #1-#2-#3 \q_stop
  { \clist_item:Nn \c_@@_month_en_clist { #2 } ~#3 , ~#1  }
%    \end{macrocode}
% \end{macro}
%
% \subsubsection{封面相关}
% \changes{v0.16}{2022/03/10}{将封面和摘要内部函数定义移动到前部。}
%
% \begin{macro}{\@@_split_title:n,\@@_split_title:V}
% 分割标题。
%    \begin{macrocode}
\cs_new_protected:Npn \@@_split_title:n #1
  {
    \tl_if_in:nnTF { #1 } { \\ }
      {
%    \end{macrocode}
% 从 |\\| 进行分割,存入 |clist|。
%    \begin{macrocode}
        \seq_set_split:Nnn \l_@@_tmp_seq { \\ } { #1 }
        \clist_set_from_seq:NN \l_@@_tmp_clist \l_@@_tmp_seq
      }
      {
%    \end{macrocode}
% 如果没找到换行符,则手动从固定宽度的位置进行分割。
%    \begin{macrocode}
        \@@_put_inempty_seg:nnn { #1 } { 1  } { 15 }
        \@@_put_inempty_seg:nnn { #1 } { 16 } { 15 }
        \@@_put_inempty_seg:nnn { #1 } { 31 } { 15 }
      }
  }
\cs_generate_variant:Nn \@@_split_title:n { V }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_multiline_title:nnn}
% 生成多行标题。
% \begin{arguments}
%   \item 内容格式
%   \item 名称盒子宽度,|dim| 型变量
%   \item 内容盒子宽度,|dim| 型变量
% \end{arguments}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_multiline_title:nnn #1#2#3
  {
    \@@_split_title:V \l_@@_info_title_tl
    \@@_spread_box:nnV { #2 } { \kaishu } \c_@@_name_title_tl
    \@@_hskip:
    \clist_map_inline:Nn \l_@@_tmp_clist
      { \@@_ulined_center_box:nn { #3 } { #1 ##1 } \@@_vskip: }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_cover_entry:nnn}
% 生成单项信息条目。
% \begin{arguments}
%   \item 条目名称
%   \item 名称盒子宽度,|dim| 型变量
%   \item 内容盒子宽度,|dim| 型变量
% \end{arguments}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_cover_entry:nnn #1#2#3
  {
    \@@_spread_box:nnx { #2 } { \kaishu } { \@@_name:n { #1 } }
    \@@_hskip:
    \@@_ulined_center_box:nn { #3 }       { \@@_info:n { #1 } }
    \@@_vskip:
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_cover_bientry:nnnn}
% 生成两项信息条目,仅用于本科生封面。
% \begin{arguments}
%   \item 左侧条目名称
%   \item 右侧条目名称
%   \item 名称盒子宽度,|dim| 型变量
%   \item 内容盒子宽度,|dim| 型变量
% \end{arguments}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_cover_bientry:nnnn #1#2#3#4
  {
    \@@_spread_box:nnx { #3 } { \kaishu } { \@@_name:n { #1 } }
    \@@_hskip:
    \@@_ulined_center_box:nn { #4 }       { \@@_info:n { #1 } }
    \skip_horizontal:n { 0.5 em }
    \@@_spread_box:nnx { #3 } { \kaishu } { \@@_name:n { #2 } }
    \@@_hskip:
    \@@_ulined_center_box:nn { #4 }       { \@@_info:n { #2 } }
    \@@_vskip:
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_cover_supv_entry:nnn}
% 生成两项导师信息条目,仅用于本科生封面。
% \begin{arguments}
%   \item 条目名称
%   \item 长内容盒子宽度,|dim| 型变量
%   \item 短内容盒子宽度,|dim| 型变量
% \end{arguments}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_cover_supv_entry:nnn #1#2#3
  {
    \@@_spread_box:nnx { #2 } { \kaishu } { \@@_name:n { #1 } }
    \@@_hskip:
    \@@_ulined_center_box:nn { #3 }
      { \clist_item:cn { l_@@_info_ #1 _clist } { 1 } }
    \skip_horizontal:n { 0.5 em }
    \@@_spread_box:nnV { #2 } { \kaishu } \c_@@_name_supv_ttl_tl
    \@@_hskip:
    \@@_ulined_center_box:nn { #3 }
      { \clist_item:cn { l_@@_info_ #1 _clist } { 2 } }
    \@@_vskip:
  }
%    \end{macrocode}
% \end{macro}
%
% \subsubsection{摘要相关}
%
% \begin{macro}{\@@_abs_bookmark:nn,\@@_abs_bookmark:Vn}
% \changes{v0.14}{2021/12/21}{将摘要插入目录。}
% 生成摘要的目录条目。
%    \begin{macrocode}
\cs_new_protected:Npn \@@_abs_bookmark:nn #1#2
  {
    \phantomsection
    \bool_if:NTF \g_@@_abs_in_toc_bool
      { \@@_add_tocline:n { #1 } }
      { \pdfbookmark [0] { #1 } { #2 } }
    \@@_chapter_header:n { #1 }
  }
\cs_generate_variant:Nn \@@_abs_bookmark:nn { Vn }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_abs_title:n,\@@_abs_title:V}
% 摘要标题双层下划线格式。
% \begin{arguments}
%   \item 宽度,|dim| 型变量
% \end{arguments}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_abs_title:n #1
  {
    \@@_get_text_width:Nn \l_@@_tmp_dim {#1}
    \@@_uuline:n { \l_@@_tmp_dim } #1
  }
\cs_generate_variant:Nn \@@_abs_title:n { V }
%    \end{macrocode}
% \end{macro}
%
% \subsection{页面对象}
% \changes{v0.15}{2022/01/24}{使用 \pkg{xtemplate} 重构封面。}
%
% 本模板使用 \pkg{xtemplate} 提供的面向对象方法简化封面和摘要的绘制过程。
%
% 以下分别从页面元素(element)和页面整体(page)的层次进行了抽象。当我们把页面部件考虑为一个对象时,它天然地只具备有限数量的属性:内容、格式、边距、对齐方式等。而具体的页面是这些对象的实例的集合,附加边距、行距等属性,创建页面只需传入一个列表调用各个 Instance 即可。通过 \pkg{xtemplate} 提供的功能,我们可以根据这些属性创建模板(template),进而能大量构建具有\emph{相似行为}的实例(instance)。这种做法能充分分离内容和样式,极大优化代码的可读性。
%
% 声明对象类型。此类对象不需要参数。
%    \begin{macrocode}
\DeclareObjectType { nju } { \c_zero_int }
%    \end{macrocode}
%
% 定义元素模板。
%    \begin{macrocode}
%<@@=njuelem>
%    \end{macrocode}
%
% 声明页面元素模板接口。
% 元素是一个页面的基本组成单位,包括文段、图片等等。一个抽象的元素应当具备以下属性:
% \begin{description}
%   \item[\opt{content}] 内容,即剥离样式的元素本身
%   \item[\opt{format}] 格式,例如字号、字体
%   \item[\opt{bottom-skip}] 下间距,即与下一个元素的距离
%   \item[\opt{align}] 对齐方式,包括左对齐、右对齐、居中、正常段落
% \end{description}
%    \begin{macrocode}
\DeclareTemplateInterface { nju } { element } { \c_zero_int }
  {
    content     : tokenlist = \c_empty_tl,
    format      : tokenlist = \c_empty_tl,
    bottom-skip : skip      = \c_zero_skip,
    align       : choice { l, r, c, n } = c
  }
%    \end{macrocode}
%
% 声明页面元素模板代码。涉及的变量将被自动创建。
%    \begin{macrocode}
\DeclareTemplateCode { nju } { element } { \c_zero_int }
  {
    content     = \l_@@_content_tl,
    format      = \l_@@_format_tl,
    bottom-skip = \l_@@_bottom_skip,
    align =
      {
        l =
          { \tl_set_eq:NN \l_@@_begin_align_tl \flushleft
            \tl_set_eq:NN \l_@@_end_align_tl   \endflushleft  },
        r =
          { \tl_set_eq:NN \l_@@_begin_align_tl \flushright
            \tl_set_eq:NN \l_@@_end_align_tl   \endflushright },
        c =
          { \tl_set_eq:NN \l_@@_begin_align_tl \center
            \tl_set_eq:NN \l_@@_end_align_tl   \endcenter     },
        n =
          { \tl_clear:N   \l_@@_begin_align_tl
            \tl_clear:N   \l_@@_end_align_tl                  }
      }
  }
  {
    \AssignTemplateKeys
    \group_begin:
      \l_@@_begin_align_tl
      \l_@@_format_tl
      \l_@@_content_tl \par
      \l_@@_end_align_tl
    \group_end:
    \__nju_vskip:N \l_@@_bottom_skip
  }
%    \end{macrocode}
%
% 定义页面模板。
%    \begin{macrocode}
%<@@=njupage>
%    \end{macrocode}
%
% 声明页面模板接口。
% 页面是元素的集合。一个抽象的页面应当具备以下属性:
% \begin{description}
%   \item[\opt{element}] 包含的元素,这里使用的是名称列表
%   \item[\opt{prefix}] 元素名称前缀
%   \item[\opt{format}] 格式,例如行距
%   \item[\opt{top-skip}] 上间距,即与页面顶部的距离
%   \item[\opt{bottom-skip}] 下间距,即与页面底部的距离
% \end{description}
%    \begin{macrocode}
\DeclareTemplateInterface { nju } { page } { \c_zero_int }
  {
    element     : commalist = \c_empty_clist,
    prefix      : tokenlist = \c_empty_tl,
    format      : tokenlist = \c_empty_tl,
    top-skip    : skip      = \c_zero_skip,
    bottom-skip : skip      = \c_zero_skip
  }
%    \end{macrocode}
%
% 声明页面模板代码。
%    \begin{macrocode}
\DeclareTemplateCode { nju } { page } { \c_zero_int }
  {
    element     = \l_@@_element_clist,
    prefix      = \l_@@_prefix_tl,
    format      = \l_@@_format_tl,
    top-skip    = \l_@@_top_skip,
    bottom-skip = \l_@@_bottom_skip
  }
  {
    \AssignTemplateKeys
    \newpage
    \__nju_vskip:N \l_@@_top_skip
    \group_begin:
      \l_@@_format_tl
      \clist_map_inline:Nn \l_@@_element_clist
        { \UseInstance { nju } { \l_@@_prefix_tl ##1 } }
    \group_end:
    \__nju_vskip:N \l_@@_bottom_skip
  }
%<@@=nju>
%    \end{macrocode}
%
% \begin{macro}{\@@_declare_element:nn,\@@_declare_page:nn}
% 封装 \pkg{xtemplate} 提供的函数,简化创建实例的过程。
% \begin{arguments}
%   \item 实例名称
%   \item 参数列表
% \end{arguments}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_declare_element:nn #1#2
  { \DeclareInstance { nju } {#1} { element } {#2} }
\cs_new_protected:Npn \@@_declare_page:nn    #1#2
  { \DeclareInstance { nju } {#1} { page    } {#2} }
%    \end{macrocode}
% \end{macro}
%
%
% \subsection{模板选项}
% \changes{v0.11}{2021/11/15}{进行了效率优化。}
Yu Xiong's avatar
Yu Xiong committed
%    \begin{macrocode}
\keys_define:nn { nju }
Yu Xiong's avatar
Yu Xiong committed
%    \end{macrocode}
%
% \begin{macro}{degree}
% 学位类型,默认为本科。
Yu Xiong's avatar
Yu Xiong committed
%    \begin{macrocode}
    degree            .choices:nn = { ug, mg, mf, phd }
Yu Xiong's avatar
Yu Xiong committed
      { \int_gset_eq:NN \g_@@_info_degree_int \l_keys_choice_int },
    degree            .initial:n  = ug,
Yu Xiong's avatar
Yu Xiong committed
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{type}
% 论文类型,默认为毕业论文。
Yu Xiong's avatar
Yu Xiong committed
%    \begin{macrocode}
    type              .choices:nn = { thesis, design }
Yu Xiong's avatar
Yu Xiong committed
      { \int_gset_eq:NN \g_@@_info_type_int   \l_keys_choice_int },
    type              .initial:n    = thesis,
Yu Xiong's avatar
Yu Xiong committed
%    \end{macrocode}
% \end{macro}
Yu Xiong's avatar
Yu Xiong committed
% \begin{macro}{nlcover}
Yu Xiong's avatar
Yu Xiong committed
% 是否需要国家图书馆封面(仅对研究生有效,默认关闭)。
% \footnote{nl 代表 National Library。}
Yu Xiong's avatar
Yu Xiong committed
%    \begin{macrocode}
    nlcover          .bool_set:N  = \g_@@_nlcover_bool,
    nlcover           .initial:n  = false,
Yu Xiong's avatar
Yu Xiong committed
%    \end{macrocode}
% \end{macro}
% \begin{macro}{decl-page}
% \changes{v0.13}{2021/12/15}{新增诚信承诺书选项。}
% \changes{v0.14}{2022/01/06}{修改选项名称。}
% 是否需要诚信承诺书或原创性声明(默认关闭)。
% \footnote{原创性声明的英文翻译为 Declaration of Originality,为了使选项表义更清晰同时缩减名称长度,将其修改为“声明页”这一名称。}
%    \begin{macrocode}
    decl-page        .bool_set:N  = \g_@@_orig_decl_bool,
    decl-page         .initial:n  = false,
%    \end{macrocode}
% \end{macro}
%
Yu Xiong's avatar
Yu Xiong committed
% \begin{macro}{draft}
% \changes{v0.13}{2021/12/15}{新增草稿模式选项。}
% 是否开启草稿模式(默认关闭)。
%    \begin{macrocode}
    draft           .bool_gset:N  = \g_@@_draft_bool,
    draft             .initial:n  = false,
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{oneside,twoside}
% \changes{v0.14}{2022/01/14}{新增单双面模式选项。}
% 单双面模式(默认为双面)。
%    \begin{macrocode}
    oneside   .value_forbidden:n  = true,
    twoside   .value_forbidden:n  = true,
Yu Xiong's avatar
Yu Xiong committed
    oneside .bool_gset_inverse:N  = \g_@@_twoside_bool,
Yu Xiong's avatar
Yu Xiong committed
%    \end{macrocode}
% \end{macro}
% \begin{macro}{latin-font,cjk-font}
% \changes{v0.14}{2021/12/12}{简化字体选项名称。}
% 字体选项。
Yu Xiong's avatar
Yu Xiong committed
%    \begin{macrocode}
    latin-font        .choices:nn =
      { gyre, mac, win, none }
      { \tl_set_eq:NN \g_@@_latin_font_tl \l_keys_choice_tl },
    cjk-font          .choices:nn =
      { fandol, founder, mac, noto, source, win, none }
      { \tl_set_eq:NN \g_@@_cjk_font_tl   \l_keys_choice_tl },
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{config}
% \changes{v0.16}{2022/02/23}{新增 \opt{config} 选项。}
% 配置文件路径。
%    \begin{macrocode}
    config             .tl_set:N  = \g_@@_config_clist
Yu Xiong's avatar
Yu Xiong committed
%    \end{macrocode}
Yu Xiong's avatar
Yu Xiong committed
% \end{macro}
% 在定义完全部设置以后从导言区输入参数。
Yu Xiong's avatar
Yu Xiong committed
%    \begin{macrocode}
\ProcessKeysOptions { nju }
%    \end{macrocode}
% \subsection{用户接口}
%
% \begin{macro}{bib,info,style}
% 定义元(meta)键值对。
%    \begin{macrocode}
\keys_define:nn { nju }
  {
    bib   .meta:nn = { nju / bib   } { #1 },
    info  .meta:nn = { nju / info  } { #1 },
    style .meta:nn = { nju / style } { #1 }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\njusetup}
% \changes{v0.6}{2021/09/10}{改用键值对输入信息。}
% 定义用于设置信息的命令。
%    \begin{macrocode}
\NewDocumentCommand \njusetup { m } { \keys_set:nn { nju } { #1 } }
%    \end{macrocode}
% \end{macro}
%
%
Yu Xiong's avatar
Yu Xiong committed
% \subsection{载入宏包}
%
% \changes{v0.12}{2021/12/07}{重新组织宏包载入顺序。}
% 将选项传入 \cls{ctexbook} 文档类。
\PassOptionsToClass
  {
    a4paper,
    UTF8,
    scheme = chinese,
Yu Xiong's avatar
Yu Xiong committed
%    \end{macrocode}
% 传入单双面模式选项。
%    \begin{macrocode}
    \bool_if:NTF \g_@@_twoside_bool { twoside, } { oneside, }
%    \end{macrocode}
% 开启草稿模式后传入 |draft| 选项。
%    \begin{macrocode}
    \bool_if:NT \g_@@_draft_bool { draft, }
%    \end{macrocode}
% 关于行距,\hologo{LaTeX} 默认1.2行距,MS Word 默认行距是1.3,要求1.5倍
% Word 行距,故 $1.5\times\frac{1.3}{1.2} = 1.625$
Yu Xiong's avatar
Yu Xiong committed
%    \begin{macrocode}
    linespread = 1.625,
%    \end{macrocode}
% 默认不载入任何字体,供本模板自行设置。
%    \begin{macrocode}
%    \end{macrocode}
% 正文字体设置为小四号。
%    \begin{macrocode}
  }
  { ctexbook }
Yu Xiong's avatar
Yu Xiong committed
%    \end{macrocode}
%
% 传入各宏包选项。
%    \begin{macrocode}
\clist_map_inline:nn
  {
    { no-math           } { fontspec     },
    { perpage           } { footmisc     },
    { amsmath, thmmarks } { ntheorem     },
    { hyphens           } { url          },
    { warnings-off={ mathtools-colon, mathtools-overbracket } }
                          { unicode-math },
    { capitalise, nameinlink, noabbrev }
  }
  { \PassOptionsToPackage #1 }
%    \end{macrocode}
%
% 使用\pkg{ctexbook}作为基础文档类。
Yu Xiong's avatar
Yu Xiong committed
%    \begin{macrocode}
\LoadClass { ctexbook } [ 2018/04/01 ]
Yu Xiong's avatar
Yu Xiong committed
%    \end{macrocode}
%
% \changes{v0.14}{2021/12/21}{移除内置的 \pkg{hologo}。}
Yu Xiong's avatar
Yu Xiong committed
% \changes{v0.15}{2022/01/17}{移除内置的 \pkg{url}。}
Yu Xiong's avatar
Yu Xiong committed
% 载入各种宏包。
Yu Xiong's avatar
Yu Xiong committed
%    \begin{macrocode}
Yu Xiong's avatar
Yu Xiong committed
\RequirePackage
  {
    geometry,
    fancyhdr,
    setspace,
%    \end{macrocode}
% \changes{v0.13}{2021/12/13}{使用 \pkg{ntheorem} 创建定理环境,删除
%   \pkg{amsthm} 和 \pkg{thmtools}。}
% 数学相关的宏包。其中,\pkg{amsmath} 必须在 \pkg{unicode-math} 前加载。
% \pkg{unicode-math} 指定了 \hologo{XeTeX} 和 \hologo{LuaTeX} 下所使用的
% 数学字体。用于配置数学环境的 \pkg{mathtools} 会与 \pkg{unicode-math}
% 发生冲突,此处手动消除其警告。
Yu Xiong's avatar
Yu Xiong committed
%    \begin{macrocode}
    amsmath,
    mathtools,
    unicode-math,
Yu Xiong's avatar
Yu Xiong committed
%    \end{macrocode}
% \changes{v0.14}{2021/12/21}{移除内置的 \pkg{multirow}、\pkg{subcaption}
%   和 \pkg{wrapfig}。}
% 图片与表格。
Yu Xiong's avatar
Yu Xiong committed
%    \begin{macrocode}
    booktabs,
    caption,
    graphicx,
Yu Xiong's avatar
Yu Xiong committed
%    \end{macrocode}
    enumitem,
%    \end{macrocode}
% 按以下顺序加载两个关于引用的包。
% \pkg{hyperref} 覆写了大量命令,因此需要在其他包最后载入。
% 仅有 \pkg{cleveref} 需要在 \pkg{hyperref} 后载入,否则会报错。
%    \begin{macrocode}
    hyperref,
    cleveref
  }
% 在双面模式下,使用 \pkg{emptypage} 清除空白页的页眉、页脚和页码。
%    \begin{macrocode}
\bool_if:NT \g_@@_twoside_bool { \RequirePackage{ emptypage } }
%    \end{macrocode}
%
% \changes{v0.13}{2021/12/13}{删除会与 \pkg{ntheorem} 冲突的 \pkg{microtype}。}
% \begin{macro}{\njuline}
% 针对编译引擎,使用不同的宏包构建可以对中文正常换行的下划线命令。
Yu Xiong's avatar
Yu Xiong committed
%    \begin{macrocode}
\sys_if_engine_xetex:T
  {
    \RequirePackage{xeCJKfntef}
    \NewDocumentCommand \njuline { m } { \CJKunderline{#1} }
  }
Yu Xiong's avatar
Yu Xiong committed
%    \end{macrocode}
% \pkg{lua-ul} 中需要在结尾使用 \tn{null} 保护尾部空白。
\sys_if_engine_luatex:T
  {
    \RequirePackage{lua-ul}
    \NewDocumentCommand \njuline { m } { \underLine{#1} \null }
  }
% \end{macro}
% \begin{macro}{\@@_check_package:nnn}
% 检查过时宏包。
%    \begin{macrocode}
\msg_new:nnn { njuthesis } { package-too-old }
  {
    Package~ "#1"~ is~ too~ old.\\
Yu Xiong's avatar
Yu Xiong committed
    The~ njuthesis~ class~ only~ supports~ "#1"~ with~
    a~ version higher~ than~ v#2.\\
    Please~ update~ an~ up-to-date~ version~ of~ it~
Yu Xiong's avatar
Yu Xiong committed
    using~ your TeX~ package~ manager~ or~ from~ CTAN.
  }
\cs_new_protected:Npn \@@_check_package:nnn #1#2#3
  {
    \@ifpackagelater {#1} {#2}
      { } { \msg_error:nnnn { njuthesis } { package-too-old } {#1} {#3} }
  }
%    \end{macrocode}
% \end{macro}
%
% 检查绘制下划线所需的 \pkg{luatexja} 包版本。该宏包在 2021-09-18 的更新
% 解决了下划线中断问题,然而在 2021-10-24 的更新才提供了正确的内嵌日期。
%    \begin{macrocode}
\sys_if_engine_luatex:T
  { \@@_check_package:nnn { luatexja } { 2021/10/24 } { 20211024.0 } }
%    \end{macrocode}
%
% \subsection{配置文件}
%
% \cls{njuthesis} 包含\emph{本科生}和\emph{研究生}两套区别较大的模板配置。
% 出于定义的简洁起见,本模板将其拆分为两个单独的参数配置文件,
% 编译过程中将会根据设置的学位信息,载入相应默认配置。
% 注意,尽管在手册的实现细节部分封面、摘要、常量等位于靠后部分,
% 拆分后实际上是在此处载入运行的,务必要注意逻辑顺序。
%
% 下文中,本科生模板配置对应 \agrd{definition-ug},用于生成
% \file{njuthesis-undergraduate.def};研究生模板配置对应
% \agrd{definition-g},用于生成 \file{njuthesis-graduate.def}。
%    \begin{macrocode}
Yu Xiong's avatar
Yu Xiong committed
\int_compare:nTF { \g_@@_info_degree_int == 1 }
  { \file_input:n { njuthesis-undergraduate.def } }
  { \file_input:n { njuthesis-     graduate.def } }
%    \end{macrocode}
%
% 载入用户设置,可用于对模板做额外修改。
%    \begin{macrocode}
\msg_new:nnn { njuthesis } { load-config }
  { I~ am~ loading~ config~ file~ "#1". }
\clist_if_empty:NF \g_@@_config_clist
  {
    \msg_info:nnx { njuthesis } { load-config } { \g_@@_config_tl }
    \clist_map_inline:Nn \g_@@_config_clist { \file_input:n { #1 } }
% \subsection{个人信息}
% \changes{v0.11}{2021/10/01}{将个人信息变量名改为小写字母加连字符的形式。}
% \changes{v0.15}{2022/01/22}{将个人信息设置移到载入宏包后。}
%
%    \begin{macrocode}
\keys_define:nn { nju / info }
  {
%    \end{macrocode}
%
% \begin{macro}{info/title,info/title*}
% \changes{v0.13}{2021/12/12}{简化多行标题的输入方式。}
% \changes{v0.14}{2022/01/14}{将标题断行控制符修改为 |\\|。}
% 题目。中文题目可使用 |\\| 手动断行。以下标注星号(|*|)的皆为对应的英文字段。
%    \begin{macrocode}
    title               .tl_set:N = \l_@@_info_title_tl,
    title              .initial:n = { 空 },
    title*              .tl_set:N = \l_@@_info_title_en_tl,
%    \end{macrocode}
% \end{macro}