Ubuntu20.04+TeXLive2022平台。
在基于l3draw用Expl3开发chinesechess中国象棋排版宏包中已发布于CTAN: https://www.ctan.org/pkg/chinesechess,现需对setcchessman*环境打谱过程的棋谱进行记录输出。
初步的想法是通过在环境中将打谱过程记录在\l__cchess_manual_clist
列表中,在环境结束时用\refstepcounter{setman}
递增计数器,并根据环境的
label选项添加
label,然后将棋谱列表中的数据写入由
label选项值和计数器值拼接成的文件中,如
test011.man`。
再设计一个命令\printman
,将label
值作为其参数传入,然后根据label
值和\ref{label}
结果再拼接出文件名,如test011.man
。读取这个文件,将棋谱正常输出即可。
目前,存入文件测试通过,读取文件也测试通过,并且直接使用\ref
也可以实现正确的交叉引用。
但是,在读取文件前,却无法根据label
值和\ref{label}
结果拼接出文件名,目前查到的是无法得到正确的\ref{label}
结果的原因。
期望网友能够分析一下该MWE,给一个解决方案,以期能完善chinesechess中国象棋排版宏包。该功能的MWE如下:
\documentclass{ctexart}
\usepackage{xparse}
\ExplSyntaxOn
% \label命令变体
\cs_set_eq:NN \__cchess_setman_label:n \label
\cs_generate_variant:Nn \__cchess_setman_label:n { x }
% 是否输出棋谱
\bool_new:N \l__cchess_with_setman_bool
% 棋谱文字说明列表(如车一进二等)
\clist_new:N \l__cchess_manual_clist
% 打谱环境棋谱标签
\tl_new:N \l__cchess_setman_label_tl
\tl_new:N \l__cchess_setman_label_num_tl
% 打谱环境用计数器
\newcounter{setman}
\coffin_new:N \l__cchess_manual_coffin
% key_value选项设计
\keys_define:nn { cchess }
{
% 棋盘背景图片
label .tl_gset:N = \l__cchess_setman_label_tl ,
label .initial:n = {} ,
}
% 打谱排版环境用户接口
\NewDocumentEnvironment{ setcchessman* }{ O{} +b }
{
\group_begin:
\bool_set_true:N \l__cchess_with_setman_bool
\keys_set:nn { cchess } { #1 }
\__cchess_setcchessman_pre_setup:n { #2 }
}{
\__cchess_setcchessman_post_setup:
\group_end:
}
% 棋谱输出用户接口
% #1 棋谱label
\NewDocumentCommand{\printman}{ m }
{
\__cchess_setman_print:n { #1 }
}
% 打谱环境前处理函数
% #1 打谱命令
\cs_new:Npn \__cchess_setcchessman_pre_setup:n #1
{
\hcoffin_set:Nn \l__cchess_manual_coffin
{ 这是一个棋盘 }
\clist_put_right:Nn \l__cchess_manual_clist { 车九进一 }
\clist_put_right:Nn \l__cchess_manual_clist { 马3退2 }
}
% 打谱环境后处理函数
\cs_new:Nn \__cchess_setcchessman_post_setup:
{
% 输出结果盒子容器
\coffin_typeset:Nnnnn \l__cchess_manual_coffin
{ l }{ b } { 0pt } { 0pt }
% 星号环境需要输出打谱记录
\bool_if:NT \l__cchess_with_setman_bool
{
% 递增计数器
\refstepcounter{setman}
% 设置label标签
\__cchess_setman_label:x { \l__cchess_setman_label_tl }
% 构造文件名
\iow_open:Nn \g_tmpa_iow { \l__cchess_setman_label_tl\thesetman .man }
% 遍历打谱记录列表,输出打谱记录
\bool_until_do:nn { \clist_if_empty_p:N \l__cchess_manual_clist }
{
\clist_pop:NN \l__cchess_manual_clist \l_tmpa_tl
\iow_now:Nx \g_tmpa_iow { \l_tmpa_tl }
}
\iow_close:N \g_tmpa_iow
}
}
% 棋谱输出
\cs_new:Npn \__cchess_setman_print:n #1
{
% 根据棋谱label构建文件名
% 此处无法构建文件名
\tl_set:Nf \l_tmpa_tl { #1 \ref { #1 } .man }
% \tl_show:N \l_tmpa_tl
\ior_open:NnTF \g_tmpb_ior {test011.man}
{
\ior_str_map_inline:Nn
% \ior_map_inline:Nn
\g_tmpb_ior
{
##1\par
}
}
{ \msg_error:nnn { csv } { file-not-found } { test011.man } }
\iow_close:N \g_tmpa_ior
}
% 文件不存在错误提示
\msg_new:nnn { cchess } { file-not-found } { File~`#1'~not~found. }
\ExplSyntaxOff
\begin{document}
天圆地方大战的棋谱如棋谱\ref{test01}所示。
\begin{setcchessman*}[label=test01]
% 打谱命令
\end{setcchessman*}
\bigskip
这是一个棋谱
\printman{ test01 }
\bigskip
\bigskip
昏天黑地大战的棋谱如棋谱\ref{test02}所示。
\begin{setcchessman*}[label=test02]
% 打谱命令
\end{setcchessman*}
\end{document}
有两个问题,
\ref
不能被完全展开,因此不能用于 f
、x
、e
等展开类型中,否则会出错,必须使用底层的宏来得到 ref;\printman{ test01 }
,时,一般两侧的空格是不需要的,使用 \NewDocumentCommand { >{\TrimSpaces} m }
在传参时将其去除。改动的地方只有两个:
\makeatletter
% 棋谱输出用户接口
% #1 棋谱label
\NewDocumentCommand{\printman}{ >{\TrimSpaces} m } % 去掉两侧空格
{
\__cchess_setman_print:n { #1 }
}
\cs_set:Npn \use_i:nnnnn #1#2#3#4#5 {#1} % LaTeX3 并未定义 \use_i:nnnnn
% 棋谱输出
\cs_new:Npn \__cchess_setman_print:n #1
{
\cs_if_exist:cTF { r@#1 }
{
% 这一步是得到 ref,它保存在 \r@#1 中。\r@#1 有两项,当使用 hyperref 时,
% \r@#1 有 5 项,这里使用 \empty 统一解决
\tl_set:Nx \l_tmpa_tl
{ #1 \exp_args:NNc \exp_after:wN \use_i:nnnnn { r@ #1 } \c_empty_tl \c_empty_tl \c_empty_tl .man }
\ior_open:NnTF \g_tmpb_ior { \l_tmpa_tl }
{
\ior_str_map_inline:Nn
% \ior_map_inline:Nn
\g_tmpb_ior
{ ##1\par }
}
{ \msg_error:nnx { csv } { file-not-found } { \l_tmpa_tl } }
\iow_close:N \g_tmpa_ior
}
{ \G@refundefinedtrue }% 引用未定义
}
完整代码:
\documentclass{ctexart}
\usepackage{xparse}
%\usepackage{hyperref}
%\usepackage{nameref,cleveref}
\makeatletter
\ExplSyntaxOn
% \label命令变体
\cs_new_protected_nopar:Npn \__cchess_setman_label:n { \label }
\cs_generate_variant:Nn \__cchess_setman_label:n { x }
\cs_set:Npn \use_i:nnnnn #1#2#3#4#5 {#1}
% 是否输出棋谱
\bool_new:N \l__cchess_with_setman_bool
% 棋谱文字说明列表(如车一进二等)
\clist_new:N \l__cchess_manual_clist
% 打谱环境棋谱标签
\tl_new:N \l__cchess_setman_label_tl
\tl_new:N \l__cchess_setman_label_num_tl
% 打谱环境用计数器
\newcounter{setman}
\coffin_new:N \l__cchess_manual_coffin
% key_value选项设计
\keys_define:nn { cchess }
{
% 棋盘背景图片
label .tl_gset:N = \l__cchess_setman_label_tl ,
label .initial:n = {} ,
}
% 打谱排版环境用户接口
\NewDocumentEnvironment{ setcchessman* }{ O{} +b }
{
\group_begin:
\bool_set_true:N \l__cchess_with_setman_bool
\keys_set:nn { cchess } { #1 }
\__cchess_setcchessman_pre_setup:n { #2 }
}{
\__cchess_setcchessman_post_setup:
\group_end:
}
% 棋谱输出用户接口
% #1 棋谱label
\NewDocumentCommand{\printman}{ >{\TrimSpaces} m }
{
\__cchess_setman_print:n { #1 }
}
% 打谱环境前处理函数
% #1 打谱命令
\cs_new:Npn \__cchess_setcchessman_pre_setup:n #1
{
\hcoffin_set:Nn \l__cchess_manual_coffin
{ 这是一个棋盘 }
\clist_put_right:Nn \l__cchess_manual_clist { 车九进一 }
\clist_put_right:Nn \l__cchess_manual_clist { 马3退2 }
}
% 打谱环境后处理函数
\cs_new:Nn \__cchess_setcchessman_post_setup:
{
% 输出结果盒子容器
\coffin_typeset:Nnnnn \l__cchess_manual_coffin
{ l }{ b } { 0pt } { 0pt }
% 星号环境需要输出打谱记录
\bool_if:NT \l__cchess_with_setman_bool
{
% 递增计数器
\refstepcounter{setman}
% 设置label标签
\__cchess_setman_label:x { \l__cchess_setman_label_tl }
% 构造文件名
\iow_open:Nn \g_tmpa_iow { \l__cchess_setman_label_tl\thesetman .man }
% 遍历打谱记录列表,输出打谱记录
\bool_until_do:nn { \clist_if_empty_p:N \l__cchess_manual_clist }
{
\clist_pop:NN \l__cchess_manual_clist \l_tmpa_tl
\iow_now:Nx \g_tmpa_iow { \l_tmpa_tl }
}
\iow_close:N \g_tmpa_iow
}
}
% 棋谱输出
\cs_new:Npn \__cchess_setman_print:n #1
{
% 根据棋谱label构建文件名
% 此处无法构建文件名
\cs_if_exist:cTF { r@#1 }
{
\tl_set:Nx \l_tmpa_tl
{ #1 \exp_args:NNc \exp_after:wN \use_i:nnnnn { r@ #1 } \c_empty_tl \c_empty_tl \c_empty_tl .man }
\ior_open:NnTF \g_tmpb_ior { \l_tmpa_tl }
{
\ior_str_map_inline:Nn
% \ior_map_inline:Nn
\g_tmpb_ior
{ ##1\par }
}
{ \msg_error:nnx { csv } { file-not-found } { \l_tmpa_tl } }
\iow_close:N \g_tmpa_ior
}
{ \G@refundefinedtrue }% 引用未定义
}
% 文件不存在错误提示
\msg_new:nnn { cchess } { file-not-found } { File~`#1'~not~found. }
\ExplSyntaxOff
\begin{document}
天圆地方大战的棋谱如棋谱 \ref{test01} 所示。
\begin{setcchessman*}[label=test01]
% 打谱命令
\end{setcchessman*}
\bigskip
这是一个棋谱
\printman{ test01 }
\bigskip
\bigskip
昏天黑地大战的棋谱如棋谱 \ref{test02} 所示。
\begin{setcchessman*}[label=test02]
% 打谱命令
\end{setcchessman*}
\end{document}
写入临时文件时,可以在文件名前加上 \jobname
(\c_sys_jobname_str
)与其它主文件的辅助文件区分开来。
非常感谢,采用该方案解决了这个问题:
其它细节问题,我再时行必要的完善。