Skip to content
Snippets Groups Projects
njuthesis.dtx 311 KiB
Newer Older
Yu Xiong's avatar
Yu Xiong committed
            \tex_hfil:D \scalebox
              { \dim_to_decimal_in_unit:nn {#2} {#1} }
              [ 1.0 ] { #3 #4 } \tex_hfil:D
          { #3 \tl_map_inline:nn {#4} { ##1 \tex_hfil:D } \tex_unskip:D }
% \begin{macro}{\@@_box_spread:NNn,\@@_box_spread:NNv,\@@_box_spread:NNe}
%    \begin{macrocode}
\cs_new:Npn \@@_box_spread:NNn { \@@_box_spread:NNNn \l_@@_tmp_skip }
\cs_generate_variant:Nn \@@_box_spread:NNn { NNv }
\cs_generate_variant:Nn \@@_box_spread:NNn { NNe }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_box_spread_name:Nn,\@@_box_spread_name:NNn}
% 以上盒子只用来打印 \cs[no-index]{c_@@_name_\meta{描述}_tl} 变量。
%    \begin{macrocode}
\cs_new_protected:Npn \@@_box_spread_name:Nn #1#2
  { \@@_box_spread:NNv #1 \c_empty_tl { c_@@_name_ #2 _tl } }
\cs_new_protected:Npn \@@_box_spread_name:NNn #1#2#3
  { \@@_box_spread:NNv #1 #2 { c_@@_name_ #3 _tl } }
% \end{macro}
% \begin{macro}{\@@_box_center:Nn}
% 居中对齐的水平盒子。
%    \begin{macrocode}
\cs_new_protected:Npn \@@_box_center:Nn #1#2
  {
    \mode_leave_vertical:
Yu Xiong's avatar
Yu Xiong committed
    \hbox_to_wd:nn {#1} { \tex_hfil:D #2 \tex_hfil:D }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_box_multiline:NNNNn}
% 多行固定长度的下划线内容。
% \begin{arguments}
%   \item 用于存储条目数量的 |int| 型变量
%   \item 用于存储条目内容的 |tl| 型变量
%   \item 内容,|clist| 型变量
%   \item 宽度,|dim| 型变量
%   \item 条目数量
% \end{arguments}
% 默认绘制 4 条下划线。在评阅者人数超过 4 人时添加额外的条目。
%    \begin{macrocode}
\cs_new_protected:Npn \@@_box_multiline:NNNNn #1#2#3#4#5
    \int_set:Nn #1 { \int_max:nn { \clist_count:N #3 } {#5} }
    \int_step_inline:nn {#1}
%    \end{macrocode}
% \changes{v1.1}{2022/11/29}{修复空返回值在国家图书馆封面引发的死循环。}
% 防止空的返回值引发死循环。
%    \begin{macrocode}
        \clist_pop:NNF #3 #2 { \tl_set_eq:NN #2 \c_empty_tl }
        \@@_box_ulined:NN #4 #2 \tex_par:D
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_box_multiline:NNn}
%    \begin{macrocode}
\cs_new:Npn \@@_box_multiline:NNn
  { \@@_box_multiline:NNNNn \l_@@_tmpa_int \l_@@_tmpa_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 [ \c_@@_ruledpi_dim ] {#1} { \c_@@_rulehti_dim }
    \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 [ \c_@@_ruledpii_dim  ] {#1} { \c_@@_rulehtii_dim }
    \skip_horizontal:n { -#1 }
    \rule [ \c_@@_ruledpiii_dim ] {#1} { \c_@@_rulehtii_dim }
    \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_width:Nn,\@@_get_width:NV,\@@_get_width:Nv}
% ^^A 来自 fduthesis
% 获取文本宽度。
% \begin{arguments}
%   \item 存储宽度的 |dim| 型变量
%   \item 文本
% \end{arguments}
% 将内容放入 \tn{hbox} 后读取其宽度,存入 |dim| 型变量。
Yu Xiong's avatar
Yu Xiong committed
%    \begin{macrocode}
\cs_new:Npn \@@_get_width:Nn #1#2
    \hbox_set:Nn \l_@@_tmpa_box {#2}
    \dim_set:Nn #1 { \box_wd:N \l_@@_tmpa_box }
\cs_generate_variant:Nn \@@_get_width:Nn { NV }
\cs_generate_variant:Nn \@@_get_width:Nn { Nv }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_get_max_width:NN}
% ^^A 来自 fduthesis,调整了实现方法
% 获取多个文本中的最大宽度,并存入 |dim| 型变量。
% 本模板中此函数仅用于处理 |info| 类型文本变量,
% 出现在博士后模板封面信息表。
% \begin{arguments}
%   \item |dim| 型变量
%   \item 文本 |clist|
% \end{arguments}
% 当 \cs{l_@@_tmp_clist} 非空时,弹出最后一个元素赋给 \cs{l_@@_tmpa_tl},
% 获取其长度后与 |#1| 进行比较,二者中较大的那一个将成为 |#1| 的新值。
% 不断循环,直至 \cs{l_@@_tmp_clist} 为空。
%    \begin{macrocode}
\cs_new:Npn \@@_get_max_width:NN #1#2
  {
    \clist_map_inline:Nn #2
      {
        \@@_get_width:Nv \l_@@_tmpa_dim { g_@@_info_ ##1 _tl }
        \dim_gset:Nn #1 { \dim_max:nn {#1} { \l_@@_tmpa_dim } }
      }
  }
Yu Xiong's avatar
Yu Xiong committed
%    \end{macrocode}
% \begin{macro}{\@@_get_width_print:Nn,\@@_get_width_print:Nv,
% 横跨整页的下划线。
% \begin{arguments}
%   \item 宽度,|dim| 型变量
%   \item 文本
% \end{arguments}
% 先使用 \cs{@@_get_width:Nn} 获取文本内容到右边距的宽度,
atxy's avatar
atxy committed
% 该宽度存储在调用的 |dim| 型变量中。随后输出文本内容。
%    \begin{macrocode}
\cs_new_protected:Npn \@@_get_width_print:Nn #1#2
    \dim_set:Nn #1 { \textwidth - #1 } #2
\cs_generate_variant:Nn \@@_get_width_print:Nn { Nv }
\cs_generate_variant:Nn \@@_get_width_print:Nn { Ne }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_uline_entry:Nn}
% 生成占整页宽度的下划线条目。
% \begin{arguments}
%   \item 宽度,|dim| 型变量
%   \item 文本
% \end{arguments}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_uline_entry:Nn #1#2
    \@@_get_width_print:Nv #1 { c_@@_name_ #2 _tl }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_uline_bientry:Nn}
% 生成占半页宽度的下划线条目。
% \begin{arguments}
%   \item 宽度,|dim| 型变量
%   \item 文本
% \end{arguments}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_uline_bientry:Nn #1#2
    \@@_get_width_print:Nv #1 { c_@@_name_ #2 _tl }
    \dim_sub:Nn #1 { \textwidth / 2 }
  }
%    \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} 的内部函数实现,默认使用阿拉伯数字表示,
% 可以通过该宏包提供的 |\zhnumsetup{time=Chinese}| 来使用中文数字;
% 英文日期字样用于研究生英文封面,格式为 \meta{月份缩写}~\meta{日},~\meta{年}。
% 其中,变量类型 |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_@@_name_month_en_clist {#2} ~#3 , ~#1  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_date:nn}
\cs_new:Npn \@@_date:nn { \@@_date:nnn { } }
% \begin{macro}{\@@_date:nnn}
% \changes{v0.20}{2022/05/24}{可在日期项留空以使用空白的年月日字样。}
% 用于日期格式转化的辅助命令。在传入的字符串为空时生成空白字样。
%    \begin{macrocode}
\cs_new:Npn \@@_date:nnn #1#2#3
    \tl_set:cx { g_@@_info_ #2 date #1 _tl }
        \tl_if_empty:nTF {#3}
          { \@@_name:n { blankdate #1 } }
          { \use:c { @@_date #1 :www } #3 \q_stop }
% \begin{macro}{\@@_at_begin_document:n}
% 封装 \LaTeX{} 的钩子管理机制,等效于 \tn{AtBeginDocument}。
%    \begin{macrocode}
\cs_new_protected:Npn \@@_at_begin_document:n #1
  { \hook_gput_next_code:nn { begin document } {#1} }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_cs_clear:N}
%    \begin{macrocode}
\cs_new:Npn \@@_cs_clear:N #1 { \cs_set_eq:NN #1 \tex_relax:D }
%    \end{macrocode}
% \end{macro}
%
atXY's avatar
atXY committed
% \begin{macro}{\@@_msg:nn}
% 简化提示信息的创建。
%    \begin{macrocode}
atXY's avatar
atXY committed
\cs_new:Npn \@@_msg:nn { \msg_new:nnn { njuthesis } }
%    \end{macrocode}
atXY's avatar
atXY committed
% \end{macro}
%
% \subsubsection{封面相关}
% \changes{v0.16}{2022/03/10}{将封面和摘要内部函数定义移动到前部。}
% \begin{macro}{\@@_loop_until:nnn}
atxy's avatar
atxy committed
% 等效于 plain \TeX 的 \tn{loop} 循环,原始结构为\\
% \tn{loop}\meta{循环体1}\meta{终止条件}\meta{循环体2}\tn{repeat}。
% \begin{arguments}
%   \item |bool| 表达式
%   \item 循环体1
%   \item 循环体2
% \end{arguments}
% 两个代码块交替执行,如果在\meta{循环体1}结束后满足条件,则退出循环。
%    \begin{macrocode}
\cs_new:Npn \@@_loop_until:nnn #1#2#3
  { #2 \bool_if:nF {#1} { #3 \@@_loop_until:nnn {#1} {#2} {#3} } }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_breakpar_loop:n}
% \changes{v1.0}{2022/08/01}{使用 \LaTeX3 语法重构下划线断行算法。}
% 带下划线的自然断行算法。
% \begin{arguments}
%   \item 循环体2的额外语句
% \end{arguments}
%    \begin{macrocode}
\cs_new:Npn \@@_breakpar_loop:n #1
  {
Yu Xiong's avatar
Yu Xiong committed
    \dim_set:Nn \l_@@_tmpa_dim
      { - \tex_prevdepth:D - \c_@@_ruledpi_dim - \c_@@_rulehti_dim }
    \hbox_gset:Nn \l_@@_tmpc_box { }
    \@@_loop_until:nnn { \box_if_empty_p:N \l_@@_tmpb_box }
      {
        \box_gset_to_last:N \l_@@_tmpb_box
        \tex_unskip:D \tex_unpenalty:D
      }
      {
        \hbox_gset:Nn \l_@@_tmpc_box
          {
            \vbox_top:n
              {
                \box_use_drop:N \l_@@_tmpb_box
Yu Xiong's avatar
Yu Xiong committed
                \__kernel_kern:n { \l_@@_tmpa_dim }
                \tex_hrule:D
              }
            \tex_penalty:D
            \l_@@_tmpa_box
            \hbox_unpack_drop:N \l_@@_tmpc_box
          }
        #1
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_breakpar_print:nn}
% 打印带下划线的自然断行算法结果。
%    \begin{macrocode}
\cs_new:Npn \@@_breakpar_print:nn #1#2
  {
    \group_begin:
      \tex_noindent:D #1
      \hbox_unpack_drop:N \l_@@_tmpc_box #2
      \tex_par:D
    \group_end:
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_uline_title:}
% \changes{v0.17}{2022/04/09}{优化封面标题的断行方式。}
Yu Xiong's avatar
Yu Xiong committed
% 多行带下划线标题。使用 \TeX 原生断行算法实现。
%    \begin{macrocode}
\cs_new_protected:Npn \@@_uline_title:
    \vbox_set:Nn \l_@@_tmpa_box
      {
        \skip_set:Nn \tex_leftskip:D { .5 em plus 1 fill }
        \skip_set_eq:NN \tex_rightskip:D \tex_leftskip:D
        \g_@@_info_title_tl \tex_par:D
%    \end{macrocode}
% 使用循环寻找断行点,存入已被清空的 3 号盒子。
%    \begin{macrocode}
        \@@_breakpar_loop:n { }
%    \end{macrocode}
% 输出绘制好的标题。
%    \begin{macrocode}
    \@@_breakpar_print:nn { } { }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_cover_entry_title:NNNN,\@@_cover_entry_title:NNN}
% 生成普通封面页的标题条目,包括标签和标题本身。
% \begin{arguments}
%   \item 分隔符
%   \item 名称盒子宽度,|dim| 型变量
%   \item 内容盒子宽度,|dim| 型变量
% \end{arguments}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_cover_entry_title:NNNN #1#2#3#4
    \tl_if_empty:NT \g_@@_info_title_tl
      { \msg_error:nn { njuthesis } { missing-title } }
    \@@_box_spread_name:NNn #2 #4 { title }
    \mode_leave_vertical: #1
Yu Xiong's avatar
Yu Xiong committed
% 这里需要存储 \tn{prevdepth} 的值,以使 \tn{parbox} 后行距正确。
% \footnote{\url{https://tex.stackexchange.com/q/34971/}}
%    \begin{macrocode}
        \c_@@_fmt_covertitle_tl \@@_uline_title:
        \dim_gset_eq:NN \l_@@_tmpa_dim \tex_prevdepth:D
    \tex_par:D
    \dim_set_eq:NN \tex_prevdepth:D \l_@@_tmpa_dim
\cs_new:Npn \@@_cover_entry_title:NNN { \@@_cover_entry_title:NNNN \@@_hskip: }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_cover_entry:NNNNn,\@@_cover_entry:NNNn}
% 生成单项信息条目。
% \begin{arguments}
%   \item 分隔符
%   \item 名称盒子宽度,|dim| 型变量
%   \item 内容盒子宽度,|dim| 型变量
%   \item 条目名称
% \end{arguments}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_cover_entry:NNNNn #1#2#3#4#5
    \@@_box_spread_name:NNn #2 #4 {#5} #1
    \@@_box_ulined_info:Nn  #3    {#5} \tex_par:D
\cs_new:Npn \@@_cover_entry:NNNn { \@@_cover_entry:NNNNn \@@_hskip: }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_cover_entry:NNNnn}
% 生成两项信息条目,仅用于本科生封面。
% \begin{arguments}
%   \item 名称盒子宽度,|dim| 型变量
%   \item 内容盒子宽度,|dim| 型变量
%   \item 左侧条目名称
%   \item 右侧条目名称
% \end{arguments}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_cover_entry:NNNnn #1#2#3#4#5
    \@@_box_spread_name:NNn #1 #3 {#4} \@@_hskip:
    \@@_box_ulined_info:Nn  #2    {#4} \@@_hskip:
    \@@_box_spread_name:NNn #1 #3 {#5} \@@_hskip:
    \@@_box_ulined_info:Nn  #2    {#5} \tex_par:D
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_cover_entry_supv:NNNn}
% 生成两项导师信息条目,仅用于本科生封面。
% \begin{arguments}
%   \item 长内容盒子宽度,|dim| 型变量
%   \item 短内容盒子宽度,|dim| 型变量
%   \item 条目名称
% \end{arguments}
% |clist| 最后一个元素为空时弹出的是无法展开的 \cs{q_novalue_tl},
% 需要简单处理以使用分散对齐的盒子。
%    \begin{macrocode}
\cs_new_protected:Npn \@@_cover_entry_supv:NNNn #1#2#3#4
  {
    \tl_set:Nn \l_@@_tmpa_tl
      { \clist_item:cn { g_@@_info_ #4 _clist } { 1 } }
    \tl_set:Nn \l_@@_tmpb_tl
      { \clist_item:cn { g_@@_info_ #4 _clist } { 2 } }
    \@@_box_spread_name:NNn #1 #3 {#4}        \@@_hskip:
    \@@_box_ulined:NN #2 \l_@@_tmpa_tl        \@@_hskip:
    \@@_box_spread_name:NNn #1 #3 { supvtitle } \@@_hskip:
    \@@_box_ulined:NN #2 \l_@@_tmpb_tl        \tex_par:D
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_emblem:N,\@@_name:N}
% 封装图片绘制命令,参数为图片宽度。
atxy's avatar
atxy committed
% 此时 \tn{njuemblem}、\tn{njuname} 和相关长度都是没有定义的。
%    \begin{macrocode}
\cs_new_protected:Npn \@@_emblem:N #1
  { \njuemblem [ \c_@@_fmt_emblemcolor_tl ] {#1} { ! } }
\cs_new_protected:Npn \@@_name:N   #1
  { \njuname   [ \c_@@_fmt_namecolor_tl   ] {#1} { ! } }
%    \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
    \@@_bookmark:Nnn \g_@@_abs_showentry_bool {#1} {#2}
  }
\cs_generate_variant:Nn \@@_abs_bookmark:nn { Vn }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_abs_title:N}
% 摘要标题双层下划线格式。
% \begin{arguments}
%   \item 宽度,|dim| 型变量
% \end{arguments}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_abs_title:N #1
    \@@_get_width:NV \l_@@_tmpa_dim #1
    \@@_uuline:N     \l_@@_tmpa_dim #1
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_uline_list:NNn,\@@_uline_list:NNe}
% \changes{v0.16}{2022/03/25}{使用原生断行算法处理摘要页标题。}
% 用于研究生摘要页面的多行标题。
% \begin{arguments}
%   \item 内容
%   \item 左边距
%   \item 标签
% \end{arguments}
% 这里转化了使用 plain \TeX{} 语法实现的的断行算法。
% \footnote{\url{https://tex.stackexchange.com/q/637861/},对其语法的简要介绍可以参考
% \href{http://petr.olsak.net/ftp/olsak/optex/tex-nutshell.pdf}{\file{tex-nutshell.pdf}}。}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_uline_list:NNn #1#2#3
    \vbox_set:Nn \l_@@_tmpa_box
      {
        \@@_get_width:Nn \tex_hangindent:D {#3}
        \int_set:Nn \tex_hangafter:D { -1 }
        \skip_set_eq:NN \tex_leftskip:D #2
        \skip_set_eq:NN \tex_rightskip:D \tex_leftskip:D
        \tex_noindent:D #1 \tex_par:D
%    \end{macrocode}
% 使用循环寻找断行点,存入已被清空的 3 号盒子,同时统计行数。
%    \begin{macrocode}
        \int_gzero:N \l_@@_tmpa_int
        \@@_breakpar_loop:n { \int_gincr:N \l_@@_tmpa_int }
%    \end{macrocode}
% 在使用自然断行算法后,研究生的摘要标题可选择 \opt{strict}
% 或者 \opt{natural} 模式。前者会为少于两行的标题补齐下划线,
% 并对多于三行的标题生成警告信息。这里通过行数处理边界条件,
% 包括标题未填写、标题只有一行,以及标题过长的情况。
%    \begin{macrocode}
        \bool_if:NT \g_@@_abs_title_strict_bool
            \int_case:nnF { \l_@@_tmpa_int }
              {
                { 0 } { \msg_error:nn { njuthesis } { missing-title } }
                { 1 } { \@@_uline:n { \textwidth } }
                { 2 } { }
              }
              { \msg_warning:nn { njuthesis } { abs-title-too-long } }
\cs_generate_variant:Nn \@@_uline_list:NNn { NNe }
%    \end{macrocode}
% \end{macro}
%
% \subsection{页面对象}
% \changes{v0.15}{2022/01/24}{使用 \pkg{xtemplate} 重构封面。}
%
% 本模板使用 \pkg{xtemplate} 提供的面向对象方法简化封面和摘要的绘制过程。
%
Yu Xiong's avatar
Yu Xiong committed
% 以下分别从页面元素(element)和页面整体(page)的层次进行了抽象。
% 当我们把页面部件考虑为一个对象时,它天然地只具备有限数量的属性:
% 内容、格式、边距、对齐方式等。而具体的页面是这些对象的实例的集合,
% 附加边距、行距等属性,创建页面只需传入一个列表调用各个 Instance
% 即可。通过 \pkg{xtemplate} 提供的功能,我们可以根据这些属性创建模板
% (template),进而能大量构建具有\emph{相似行为}的实例(instance)。
% 这种做法能充分分离内容和样式,极大优化代码的可读性。
%
% 声明对象类型。此类对象不需要参数。
%    \begin{macrocode}
\DeclareObjectType { nju } { \c_zero_int }
%    \end{macrocode}
%
% \subsubsection{元素模板}
% \changes{v1.0}{2022/08/02}{修正元素对象的底部间距设置。}
%    \begin{macrocode}
%<@@=njuelem>
%    \end{macrocode}
%
% \begin{macro}{\@@_align:}
% 声明元素模板接口。
Yu Xiong's avatar
Yu Xiong committed
% 元素是一个页面的基本组成单位,包括文段、图片等等。
% 一个抽象的元素应当具备以下属性:
% \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 = { \cs_set_eq:NN \@@_align: \raggedright },
        r = { \cs_set_eq:NN \@@_align: \raggedleft  },
        c = { \cs_set_eq:NN \@@_align: \centering   },
        n = { \cs_set:Nn    \@@_align: { }          }
      }
  }
  {
    \AssignTemplateKeys
    \group_begin:
      \@@_align:
      \l_@@_format_tl \l_@@_content_tl \tex_par:D
    \group_end:
    \__nju_vskip:N \l_@@_bottom_skip
  }
%    \end{macrocode}
% \end{macro}
% \subsubsection{页面模板}
% \changes{v1.0}{2022/07/27}{为页面模板添加书签选项。}
%
%    \begin{macrocode}
%<@@=njupage>
%    \end{macrocode}
%
% \begin{macro}{\exp_args:NVV}
%    \begin{macrocode}
\exp_args_generate:n { NVV }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_bookmark:nn}
% 声明页面模板接口。
% 页面是元素的集合。一个抽象的页面应当具备以下属性:
% \begin{description}
%   \item[\opt{element}] 包含的元素,这里使用的是名称列表
%   \item[\opt{prefix}] 元素名称前缀
%   \item[\opt{format}] 格式,例如行距
%   \item[\opt{top-skip}] 上间距,即与页面顶部的距离
%   \item[\opt{bottom-skip}] 下间距,即与页面底部的距离
%   \item[\opt{bm-text}] PDF 书签名称
%   \item[\opt{bm-name}] PDF 书签锚点名
%   \item[\opt{bookmark}] 添加书签的类型,分别为目录条目、仅 PDF 书签、不显示。
% \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,
    bm-text     : tokenlist = \c_empty_tl,
    bm-name     : tokenlist = \c_empty_tl,
    bookmark    : choice { toc, pdf, none } = none
  }
%    \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,
    bm-text     = \l_@@_bm_text_tl,
    bm-name     = \l_@@_bm_name_tl,
    bookmark    =
      {
        toc  = { \cs_set_eq:NN \@@_bookmark:nn \__nju_bookmark_toc:nn },
        pdf  = { \cs_set_eq:NN \@@_bookmark:nn \__nju_bookmark_pdf:nn },
        none = { \cs_set:Nn    \@@_bookmark:nn { } }
    \clearpage
    \thispagestyle { empty }
atxy's avatar
atxy committed
% 由于起始位置没有内容,\tn{vspace*} 会使第一个元素的位置与上边距有一定距离。
%    \begin{macrocode}
    \__nju_vskip:N \l_@@_top_skip
    \exp_args:NVV \@@_bookmark:nn
      \l_@@_bm_text_tl \l_@@_bm_name_tl
    \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
  }
%    \end{macrocode}
% \end{macro}
% \subsubsection{外部接口}
%
% \begin{macro}{\@@_declare_element:nn,\@@_declare_page:nn}
% 封装 \pkg{xtemplate} 提供的函数,简化创建实例的过程。
% \begin{arguments}
%   \item 实例名称
%   \item 参数列表
% \end{arguments}
%    \begin{macrocode}
\cs_new:Npn \@@_declare_element:nn #1#2
  { \DeclareInstance { nju } {#1} { element } {#2} }
\cs_new:Npn \@@_declare_page:nn    #1#2
  { \DeclareInstance { nju } {#1} { page    } {#2} }
%    \end{macrocode}
% \end{macro}
%
%
atXY's avatar
atXY committed
% \subsection{提示信息}
% \changes{v1.1}{2022/12/03}{整理提示信息。}
%
% 本节集中定义模板中的错误信息。
%    \begin{macrocode}
\@@_msg:nn { abs-title-too-long }
  {
    Your~ title~ seems~ too~ long~ to~ fit~ in~ two~ lines.\\
    I~ have~ drawn~ additional~ lines~ to~ contain~ it,~
    which~ will~ probably~ make~ your~ abstract~ page~
    look~ slightly~ different~ from~ the~ standard.~
    You~ can~ use~ the~ "abstract/title-style"~ key~
    to~ disable~ this~ message.
  }
\@@_msg:nn { empty-theorem-type }
  {
    Empty~ theorem~ list~ to~ define.\\
    The~ key~ "theorem/type"~ should~ not~ be~ left~ empty.
  }
\@@_msg:nn { load-config  }
  { I~ am~ loading~ config~ file~ "#1". }
\@@_msg:nn { missing-image }
  {
    You~ have~ not~ selected~ local~ files~
    for~ emblem~ and~ name~ images.\\
    It~ seems~ that~ you~ haven't~ fill~ in~ both~
    "image/nju-emblem"~ and~ "image/nju-name",~ therefore~
atXY's avatar
atXY committed
    I~ am~ using~ the~ package~ "njuvisual"~ instead,~
    which~ may~ slow~ down~ the~ compilation.
  }
\@@_msg:nn { missing-ntheorem }
  {
    "ntheorem"~ package~ not~ detected.\\
    The~ functionality~ of~ built-in~ theorem~ settings~
    requires~ loading~ the~ class~ with~ "ntheorem"~ option~
    set~ to~ "true".
  }
\@@_msg:nn { missing-symbol }
  {
    \string\mdwhtsquare\ and~ \string\checkmark\ are~ not~
    contained~ in~ the~ standard~ amsmath~ package.~ You~
    should~ redefine~ them~ with~ other~ packages~ loaded~
    to~ properly~ generate~ the~ declaration~ page.
  }
atXY's avatar
atXY committed
\@@_msg:nn { missing-title }
  {
    Thesis~ title~ should~ not~ be~ left~ blank.\\
    Please~ check~ whether~ you~ have~ fill~ in~
    both Chinese~ and~ English~ titles.
  }
\@@_msg:nn { no-small-caps }
  {
    I~ am~ using~ TeX~ Gyre~ Termes~ as~ default~ Roman~ font.\\
    This~ is~ because~ the~ "Times~ New~ Roman"~ font~ in~ your~
    system~ does~ not~ embed~ glyphs~ for~ small~ capitals.~
    You~ can~ ignore~ this~ warning~ if~ you~ do~ not~ need~
    \string\textsc.~ For~ more~ information,~
    please~ refer~ to~ section~ 3.2.6~ of~ the~ documentation.
  }
\@@_msg:nn { package-too-old }
  {
    Package~ "#1"~ is~ too~ old.\\
    The~ "njuthesis"~ class only~ supports~ "#1"~ with~
    a~ version~ higher~ than~ v#2.~
    Please~ update~ an~ up-to-date~ version~ of~ it~
    using~ your~ TeX~ package~ manager~ or~ from~ CTAN.
  }
\@@_msg:nn { package-conflict }
  {
    The~ "#2"~ package~ is~ incompatible~ with~ "#1".\\
    I~ have~ loaded~ "#1"~ by~ default.~ Maybe~ You~ should~
    refer~ to~ section~ 4~ of~ the~ documentation.
  }
\@@_msg:nn { missing-stzhongs }
  {
    "STZHONGS.TTF"~ is~ not~ found~ in~ your~ system.\\
    The~ font~ is~ normally~ distributed~ with~ MS~ Windows.~
    I~ have~ used~ songti~ for~ substitution.
  }
atXY's avatar
atXY committed
%    \end{macrocode}
%
%
% \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}{type}
% \changes{v1.0}{2022/06/30}{修改类型选项。}
% 学位,默认为学士。
Yu Xiong's avatar
Yu Xiong committed
%    \begin{macrocode}
    type               .choices:nn = { bachelor, master, doctor, postdoc }
      { \int_gset_eq:NN \g_@@_info_type_int \l_keys_choice_int },
    type               .initial:n  = bachelor,
Yu Xiong's avatar
Yu Xiong committed
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{degree}
% \changes{v1.0}{2022/06/30}{修改学位选项。}
% 研究生的学位类型,默认为学术学位。
Yu Xiong's avatar
Yu Xiong committed
%    \begin{macrocode}
    degree              .choice:,
    degree / academic     .code:n  =
      { \bool_set_true:N  \g_@@_opt_academic_bool },
    degree / professional .code:n  =
      { \bool_set_false:N \g_@@_opt_academic_bool },
    degree             .initial:n  = academic,
Yu Xiong's avatar
Yu Xiong committed
%    \end{macrocode}
% \end{macro}
% \begin{macro}{nl-cover,\g_@@_opt_nlcover_bool}
% 是否需要国家图书馆封面。本选项仅用于研究生模板,默认关闭。
% \footnote{nl 代表 National Library。}
Yu Xiong's avatar
Yu Xiong committed
%    \begin{macrocode}
    nl-cover          .bool_set:N  = \g_@@_opt_nlcover_bool,
    nl-cover           .initial:n  = false,
Yu Xiong's avatar
Yu Xiong committed
%    \end{macrocode}
% \end{macro}
% \begin{macro}{decl-page,\g_@@_opt_decl_bool}
% \changes{v0.13}{2021/12/15}{新增诚信承诺书选项。}
% \changes{v0.14}{2022/01/06}{修改选项名称。}
% 是否需要诚信承诺书或原创性声明(默认关闭)。
Yu Xiong's avatar
Yu Xiong committed
% \footnote{原创性声明的英文翻译为 Declaration of Originality,
% 为了使选项表义更清晰同时缩减名称长度,将其修改为“声明页”这一名称。}
%    \begin{macrocode}
    decl-page         .bool_set:N  = \g_@@_opt_decl_bool,
    decl-page          .initial:n  = false,
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{draft,\g_@@_opt_draft_bool}
Yu Xiong's avatar
Yu Xiong committed
% \changes{v0.13}{2021/12/15}{新增草稿模式选项。}
% 是否开启草稿模式(默认关闭)。
%    \begin{macrocode}
    draft            .bool_gset:N  = \g_@@_opt_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,
    oneside  .bool_gset_inverse:N  = \g_@@_opt_twoside_bool,
Yu Xiong's avatar
Yu Xiong committed
%    \end{macrocode}
% \end{macro}
% \begin{macro}{anonymous}
Yu Xiong's avatar
Yu Xiong committed
% \changes{v0.18}{2022/04/19}{新增盲审模式选项。}
% \changes{v1.0}{2022/07/05}{重命名盲审模式选项。}
% \begin{macro}{\g_@@_opt_anon_bool}
Yu Xiong's avatar
Yu Xiong committed
% 盲审模式。
%    \begin{macrocode}
    anonymous         .bool_set:N  = \g_@@_opt_anon_bool,
    anonymous          .initial:n  = false,
Yu Xiong's avatar
Yu Xiong committed
%    \end{macrocode}
% \end{macro}
% \end{macro}
Yu Xiong's avatar
Yu Xiong committed
%
% \begin{macro}{latin-font,cjk-font}
% \changes{v0.14}{2021/12/12}{简化字体选项名称。}
% 中英文字体选项。\opt{fandol} 和 \opt{gyre} 是等价的。
Yu Xiong's avatar
Yu Xiong committed
%    \begin{macrocode}
    latin-font         .choices:nn =
      { fandol, gyre, mac, macoffice, win, none }
      { \tl_set:Nn \g_@@_font_latin_tl {#1} },
    cjk-font           .choices:nn =
      { fandol, founder, mac, macoffice, noto, source, win, none }
      { \tl_set:Nn \g_@@_font_cjk_tl   {#1} },
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{latin-font,cjk-font}
% \changes{v1.4}{2023/12/15}{增加统一的字体选项名称。}
% 中英文字体选项。\opt{fontset} 这个名称和 \pkg{ctex} 是一致的。
%    \begin{macrocode}
    fontset            .choices:nn =
      { fandol, mac, macoffice, win, none }
      { \keys_set:nn { nju } { latin-font = #1, cjk-font = #1 } },
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{math-font}
% \changes{v1.0}{2022/07/02}{增加数学字库选择功能。}
% 数学字体选项。
% 由 \pkg{unicode-math} 指定 \XeTeX 和 \LuaTeX 下使用的数学字体。
%    \begin{macrocode}
    math-font          .choices:nn =
        asana, cambria, fira, garamond, lm, libertinus, newcm,
        stix, bonum, dejavu, pagella, schola, termes, xits, none
      { \tl_set_eq:NN \g_@@_font_math_tl  \l_keys_choice_tl },
    math-font          .initial:n  = xits,
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{font-path}
% 独立字体文件的路径。
%    \begin{macrocode}
    font-path             .code:n  =
      {
        \bool_set_true:N \g_@@_font_path_bool
        \tl_set_eq:NN \g_@@_font_path_tl \l_keys_value_tl
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{zihao}
% \changes{v1.0}{2022/07/04}{新增字号选项。}
% \begin{macro}{\g_@@_opt_zihao_tl}
% 字号。默认为小四号。
%    \begin{macrocode}
    zihao               .tl_set:N  = \g_@@_opt_zihao_tl,
    zihao              .initial:n  = -4,
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}{linespread}
% \changes{v1.0}{2022/07/04}{新增行距选项。}
% \begin{macro}{\g_@@_opt_linespread_tl}
Yu Xiong's avatar
Yu Xiong committed
% 行距。\LaTeX 默认1.2行距,MS Word 默认行距是1.3,要求1.5倍
% Word 行距,故默认值为 $1.5\times\frac{1.3}{1.2} = 1.625$。
% 更详细的说明请参考 \pkg{zhlineskip} 宏包文档。
%    \begin{macrocode}
    linespread          .tl_set:N  = \g_@@_opt_linespread_tl,
    linespread         .initial:n  = 1.625,
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}{config,\g_@@_config_clist}
% \changes{v0.16}{2022/02/23}{新增 \opt{config} 选项。}
% 配置文件路径。
%    \begin{macrocode}
    config           .clist_set:N  = \g_@@_config_clist
Yu Xiong's avatar
Yu Xiong committed
%    \end{macrocode}
Yu Xiong's avatar
Yu Xiong committed
% \end{macro}
% \begin{macro}{\g_@@_name_optional_pkg_clist}
%    \begin{macrocode}
\clist_new:N \g_@@_name_optional_pkg_clist
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_define_pkg_keys:nnn}
% \begin{arguments}
%   \item 宏包名
%   \item 简写名称,一般为宏包使用的名空间
%   \item 是否默认载入
% \end{arguments}
% 定义是否载入宏包的的文档类选项,以及相应的载入命令。
%    \begin{macrocode}
\cs_new_protected:Npn \@@_define_pkg_keys:nnn #1#2#3
        #1 .bool_gset:c = { g_@@_opt_load_ #2 _bool },
        #1   .initial:n = #3
    \cs_new_protected:cpn { @@_loadpkg_ #2 : }
        \bool_if:cT { g_@@_opt_load_ #2 _bool }
    \clist_put_right:Nn \g_@@_name_optional_pkg_clist {#1}
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{
%   biblatex,
%   cleveref,
%   enumitem,
%   footmisc,
%   \g_@@_opt_load_blx_bool,
%   \g_@@_opt_load_cref_bool,