% ^^A ================================== 元注释 ================================== % \iffalse meta-comment % % 文件:tabular2.dtx % % 版权 (C) 2023-2025 By Ms_yam % % 它可以在 LaTeX 项目公共许可(LPPL)1.3c 及之后的任意版本(随你的意见)下分发或修改。 % 这个许可的最新版本在如下文件中: % % https://www.latex-project.org/lppl.txt % % 本文件的 LPPL 维护状态为 "maintained"。 % % 本文件的当前维护者是 Ms_yam。 % % \fi % % ^^A ================================== 安装信息 ================================== % \iffalse %<*install> \begingroup \input docstrip.tex \keepsilent \askforoverwritefalse \preamble \endpreamble \postamble \endpostamble \generate{ \file{tabular2.ins} {\from{\jobname.dtx}{install}} \file{tabular2.sty} {\from{\jobname.dtx}{package}} \let\MetaPrefix\relax \def\MetaPrefix{} \nopreamble\nopostamble \file{README.md} {\from{\jobname.dtx}{readme}} } \endgroup % % \fi % % ^^A ================================== 文档驱动 ================================== % \iffalse %<*driver> \documentclass[show-notes]{l3doc} \usepackage{tabular2} % ^^A 添加中文支持及设置超级链接 \usepackage[UTF8,heading]{ctex} \hypersetup{ colorlinks, linkcolor=blue, hyperindex, pdfstartview=FitH, plainpages=false, backref, } \usepackage{enumitem} \setlist{noitemsep,topsep=0pt,parsep=0pt,partopsep=0pt} % ^^A 引用待办包 \usepackage[nothing]{todo} % ^^A 汉化 l3doc 的部分定义 \RenewDocumentEnvironment {texnote} {} { \endgraf \vspace{0.5em} % ^^A 3mm => 0.5em \small\textbf{\TeX{} 黑客笔记:} % ^^A \TeX~hackers~note: } { \vspace{0.5em} } % ^^A 3mm => 0.5em \ExplSyntaxOn \NewDocumentCommand {\ttt} { m } { \texttt{\tl_to_str:n{#1}} } \ExplSyntaxOff % ^^A 索引排除 \DoNotIndex{ \+, \-, \., \\, \ , \c, \d, \s, \A, \Z } \DoNotIndex{ \begin, \end, \par, \baselineskip, \noindent, \rule, \textwidth, \hsize, \hspace, \parindent, \vfill, \vskip, \centering, \raggedleft, \raggedright } \DoNotIndex{ \NeedsTeXFormat, \RequirePackage, \ProvidesExplPackage, \ProcessKeyOptions, \NewDocumentCommand, \NewDocumentEnvironment, \ExplSyntaxOff, \IfBooleanF } \DoNotIndex{ \group_begin:, \group_end: } \DoNotIndex{ \cs_new:Nn, \cs_set_eq:NN, \cs_gset_eq:NN, \cs_if_free:NT, \cs_if_exist:NTF, \cs_generate_variant:Nn } \DoNotIndex{ \prg_new_conditional:Nnn, \prg_generate_conditional_variant:Nnn, \prg_replicate:nn, \prg_return_true:, \prg_return_false: } \DoNotIndex{ \msg_new:nnn, \msg_error:nnn, \msg_error:nnV } \DoNotIndex{ \bool_new:N, \bool_gset_eq:NN, \bool_set_eq:NN, \bool_gset_true:N, \bool_set_true:N, \bool_set_false:N, \bool_if:NT, \bool_if:NF, \bool_if:NTF, \bool_if:nTF, \bool_show:N, \bool_until_do:nn, \c_true_bool } \DoNotIndex{ \tl_new:N, \tl_const:Nn, \tl_clear:N, \tl_set_eq:NN, \tl_set:Nn, \tl_set:Ne, \tl_if_empty:NT, \tl_if_empty:NF, \tl_if_empty:NTF, \tl_if_empty:nT, \tl_if_empty:nF, \tl_if_empty:nTF, \tl_if_eq:NnTF, \tl_if_eq:VnT, \tl_if_in:NnT, \tl_put_right:Nn, \tl_replace_all:Nnn, \tl_to_str:N, \tl_to_str:n } \DoNotIndex{ \str_new:N, \str_const:Nn, \str_const:Ne, \str_clear:N, \str_put_right:Nn, \str_if_eq:nnT, \str_if_eq:VnT, \str_if_eq:nnTF, \str_if_eq:VnTF, \str_if_in:NnF, \str_if_in:nnTF, \str_case:Vn, \str_case:nnF, \str_case:VnF, \str_case_e:nnF, \str_map_inline:nn } \DoNotIndex{ \char_generate:nn } \DoNotIndex{ \regex_const:Nn, \regex_extract_all:NnN, \regex_extract_all:NnNF, \regex_extract_all:nnNF, \regex_match:nnTF } \DoNotIndex{ \int_new:N, \int_zero:N, \int_gzero:N, \int_incr:N, \int_gincr:N, \int_set:Nn, \int_gset:Nn, \int_set_eq:NN, \int_gset_eq:NN, \int_if_zero:nF, \int_case:nn, \int_compare:nNnT, \int_compare:nNnF, \int_compare:nNnTF, \int_step_inline:nn,\int_step_inline:nnn, \int_max:nn, \int_use:N, \int_to_Alph:n,\int_show:N } \DoNotIndex{ \dim_new:N, \dim_const:Nn, \dim_zero:N, \dim_set_eq:NN, \dim_gset:Nn, \dim_set:Nn, \dim_add:Nn, \dim_sub:Nn, \dim_max:nn, \dim_eval:n, \dim_compare:nNnT, \dim_compare:nNnTF, \dim_use:N } \DoNotIndex{ \seq_new:N, \seq_gclear:N, \seq_clear:N, \seq_set_eq:NN, \seq_set_split:Nnn, \seq_set_split:NVn, \seq_set_split:NnV, \seq_if_empty_p:N, \seq_if_empty:NT, \seq_if_empty:NTF, \seq_count:N, \seq_gput_left:Nn, \seq_gput_left:NV, \seq_gput_right:Nn, \seq_gput_right:Ne, \seq_gput_right:NV, \seq_gpop_left:NN, \seq_pop_left:NN, \seq_put_right:Nn, \seq_put_right:NV, \seq_gset_item:Nnn, \seq_gset_item:Nne, \seq_gset_item:NnV, \seq_set_item:Nnn, \seq_get_left:NN, \seq_get_right:NN, \seq_item:Nn, \seq_map_function:NN, \seq_map_inline:Nn, \seq_show:N, \seq_log:N } \DoNotIndex{ \prop_new:N, \prop_gclear:N, \prop_clear:N, \prop_gset_eq:NN, \prop_if_empty:NTF, \prop_if_in:NVTF, \prop_gput:Nnn, \prop_gput:Nne, \prop_gput:Nen, \prop_gput:NVn, \prop_gput:NVe, \prop_put:Nnn, \prop_gpop:NnN, \prop_gpop:NeN, \prop_get:NnN, \prop_get:NVN, \prop_get:NnNF, \prop_get:NnNTF, \prop_map_inline:Nn, \prop_show:N, \prop_log:N } \DoNotIndex{ \scan_new:N } \DoNotIndex{ \clist_new:N, \clist_if_in:nVTF, \clist_item:Nn, \clist_item:nn, \clist_map_inline:nn } \DoNotIndex{ \keys_define:nn, \keys_set:nn, \l_keys_choice_tl } \DoNotIndex{ \box_new:N, \box_ht:N, \box_dp:N, \box_set_dp:Nn, \box_wd:N, \box_use:N, \hbox:n, \hbox_set:Nn, \vbox_set:Nn, \vbox_set_top:Nn, \vbox_set_to_ht:Nnn } \DoNotIndex{ \color_stroke:n, \color_stroke:e } \DoNotIndex{ \draw_begin:, \draw_end:, \draw_box_use:N, \draw_box_use:Nn, \draw_linewidth:n, \draw_set_linewidth:n, \draw_set_linewidth:e, \draw_set_linewidth:V, \draw_dash_pattern:nn, \draw_set_dash_pattern:nn, \draw_set_dash_pattern:en, \draw_set_dash_pattern:Vn, \draw_path_moveto:n, \draw_path_lineto:n, \draw_path_rectangle:nn, \draw_path_use_clear:n } \begin{document} \DocInput{\jobname.dtx} \end{document} % % \fi % % ^^A ================================ RADME ME ================================ % \iffalse %<*readme> ^^A README # 基本说明 Description 本宏包是一个实验性的表格排版解决方案,采用 `expl3` 语法重新设计, 完全摆脱了传统的底层表格命令依赖。 This package is an experimental table typesetting solution built with `expl3` syntax, completely independent of traditional low-level table commands. # 许可 License 版权 Copyright (C) 2023-2025 By Ms_yam 本宏包可以在 LaTeX 项目公共许可(LPPL)1.3c 及之后的任意版本(随你的意见)下分发或修改。 这个许可的最新版本在如下文件中: https://www.latex-project.org/lppl.txt It may be distributed and/or modified under the conditions of the LaTeX Project Public License (LPPL), either version 1.3c of this license or (at your option) any later version. The latest version of this license is in the file https://www.latex-project.org/lppl.txt 本文件的 LPPL 维护状态为 "maintained"。 This file has LPPL maintenance status "maintained". 本文件的当前维护者是 Ms_yam。 The current maintainer of this file is Ms_yam. # 编译 Compilation 本宏包设计为单文件包,你可以直接使用以下命令编译: This package is designed as a single-file package and can be compiled directly with the following commands: ```sh xelatex tabular2.dtx ``` # 备注 Remark * 目前属于开发阶段,变更比较频繁,部分文档暂不提供英文版,敬请谅解; * 英文版本描述由 AI 翻译,如有差异,请以中文版本为准。 * It is currently in the development stage, the change is frequent, some documents will not be provided in the English version, please understand. * The English version description is translated by AI. In case of any discrepancies, please refer to the Chinese version as the standard. % % \fi % % ^^A ================================== 文档内容 ================================== % % \title{^^A % tabular2\thanks{原本计划叫 \pkg{xtable}/\pkg{xtabular},但因与其它宏冲突取消。 ^^A % 后受 R 的 \pkg{ggplot2} 的启发,用于表示这是一个全新的 tabular。} : ^^A % 一个实验性的表格排版方案^^A % } % % \author{Ms\_yam ~(\href{mailto:Ms_yam@163.com}{Ms\_yam@163.com})^^A % } % \date{\zhdigits*{2025}年\zhnumber{08}月\zhnumber{21}日} % % \maketitle % % ^^A ---------------------------------- 文档部分 ---------------------------------- % % \begin{documentation} % % % \section{用户文档} % % 本宏包是一个实验性的表格排版解决方案,它重新定义了表格的输入输出方式。 % 代码层上采用 \pkg{expl3} 语法,完全摆脱了传统的底层表格命令依赖 % \footnote{但仍然使用了部分底层命令,如 \ttt{\hsize} 等。如有更好的 \pkg{expl3} 实现方法,欢迎指正。}。 % % 与传统表格不同,本模块将表格的输入与输出拆开,前者使用设置表格数据,后者确认渲染条件。 % % % \subsection{数据输入} % % % \begin{function}{xtable} % \begin{syntax} % \ttt{\begin}\{xtable\} \oarg{选项} % ~~嵌套的环境与命令... % \ttt{\end}\{xtable\} % \end{syntax} % 一个表格输入环境,它本身不能直接输入,也不会渲染输出表格。 % 它会先初始化一个空表,然后执行嵌套的数据输入环境与命令, % 最后做一些必要的计算。 % \begin{texnote} % 所有表格数据,必须通过本环境嵌套的环境与命令输入。另见 \env{data} 等环境。 % \end{texnote} % \end{function} % % \begin{function}{data} % \begin{syntax} % \ttt{\begin}\{data\} \oarg{选项} % ~~A,~B,~C |\\|\\ % ~~1,~2,~3... % \ttt{\end}\{data\} % \end{syntax} % 一个输入实际数据的环境。它必须嵌套在 \env{xtable} 环境中。 % \begin{texnote} % 本宏包只定义了其在 \env{xtable} 环境是的作用,对环境外不作干涉。【下同】 % \end{texnote} % \end{function} % % 标准输入以 “|\\|” 为行分隔符,以指定的分隔符(默认为 “|,|”)为字段(列)分隔符,不做任何其它特殊处理。 % 由于其简单易解码,处理速度快,应此推荐作为主要输入方式。 % % 以下是一个类似之前的表格输入方式的示例:\label{标准输入} % % \begin{minipage} {0.6\textwidth} % \begin{verbatim} % \begin{xtable} % % loc 用于指定基准位置,sep 用于指定分隔符 % \begin{data}[loc={2,1}, sep={&}] % Name & Sex & Age\\ % John & man & 20 % \end{data} % \end{xtable} % % \end{verbatim} % \end{minipage} % \begin{minipage} {0.29\textwidth} % \begin{xtable} % \begin{data}[loc={2,1}, sep={&}] % Name & Sex & Age\\ % John & man & 20 % \end{data} % \end{xtable} % \rendertable % \end{minipage} % % % CSV 作为最传统的输入方式,本宏包也添加了支持\footnote{标准输入其实就是 % 一个简化版的 CSV 格式,但其以不支持特殊字符为代价,显著提升了处理效率。}。 % 由于 \TeX 读取普通显示字符时,换行符会被忽略,因此在输入时也使用 “|\\|” 作为换行。 % \todo{处理特殊字符。} % % 此外,本宏包还有限地支持 JSON 数据源,但其必须理论上可以转换为简单的二维表格。 % 以下是一个 JSON 的输入示例。\label{JSON 格式} % % \begin{minipage} {0.6\textwidth} % \begin{verbatim} % % \begin{xtable} % % format 用于指定输入的格式 % \begin{data}[format=json] % [ % { % "Name": "张三", % "Age": 30, % "City": "北京" % }, % { % "Name": "李四", % "Age": 25, % "City": "上海" % } % ] % \end{data} % \end{xtable} % \end{verbatim} % \end{minipage} % \begin{minipage} {0.29\textwidth} % \begin{xtable} % \begin{data}[format=json] % [ % { % "Name": "张三", % "Age": 30, % "City": "北京" % }, % { % "Name": "李四", % "Age": 25, % "City": "上海" % } % ] % \end{data} % \end{xtable} % \rendertable % \end{minipage} % % % \begin{function}{\cell} % \begin{syntax} % \cs{cell}(\meta{x},\meta{y})\Arg{内容} % \end{syntax} % 设置指定单元格的内容。 % \begin{texnote} % \meta{x}、\meta{y} 坐标支持行名/列名或数据坐标。【下同】 % \end{texnote} % \end{function} % % % \begin{function}{\excelcolname} % \begin{syntax} % \cs{excelcolname} \oarg{n} % \end{syntax} % 将指定的前 \meta{n} 列的列名设置为指定的名称。 % \end{function} % % % \begin{function}{\savetable, \loadtable} % \begin{syntax} % \cs{savetable} \Arg{名称} % \cs{loadtable} \Arg{名称} % \end{syntax} % 将当前的表格数据保存到指定 \meta{名称} 中, % 或加载之前保存的名为 \meta{名称} 的表 % \footnote{它会替代当前表的全部内容,所以应当在输入其它数据之前加载。}。 % \end{function} % % % \begin{function}{\rowheight, \colwidth} % \begin{syntax} % \cs{rowheight}\oarg{全局样式} \Arg{样式列表} % \cs{colwidth} \oarg{全局样式} \oarg{总宽度} \Arg{样式列表} % \end{syntax} % 设置表格的行高与列宽样式,样式支持 |auto|、|same|、|fill|(仅列宽)、 % |samefill|(仅列宽) 及指定数值。 % \meta{样式列表} 若干个 \meta{[索引=]样式} 组成,并以逗号分隔。 % 如果省略索引,则索引为 “前一个索引 |+1|”。 % \begin{texnote} % 样式 |auto| 表示自然尺寸;|same| 则表示同组所有行/列的值相同 % \footnote{同时兼容所有行/列的自然尺寸,即同组自然尺寸的最大值。}; % |fill| 则会根据总宽度来补偿,即为 “自然尺寸+补偿值” % \footnote{补偿值的目的是用于保证总宽度,即为控制总宽度,需要使用缩放这些列的宽度。}; % |samefill| 等于 |same|\footnote{但与纯 $same$ 不同组,即它们的宽度是另外一个值。} + |fill|。 % 所有 |fill| 的补偿值是一样的。 % \end{texnote} % \end{function} % % % \begin{function}{\rowalign, \colalign} % \begin{syntax} % \cs{rowalign} \oarg{全局样式} \Arg{样式列表} % \cs{colalign} \oarg{全局样式} \Arg{样式列表} % \end{syntax} % 设置表格的行与列对齐方式;行对齐(垂直对齐)样式支持 |t|、|m|、|b|, % 列对齐(水平对齐)样式支持 |l|、|c|、|r|。设置方式与 \cs{rowheight} 类似。 % \begin{texnote} % 当 \meta{样式列表} 不含索引时,可以省略逗号,即类似 “|llcrl|” 一样输入。 % \end{texnote} % \end{function} % % % \subsection{渲染输出} % % \begin{function}{\printtable} % \begin{syntax} % \cs{printtable}[*] \oarg{缩进量} % \end{syntax} % 逐行打印表格,输出的表格不带格式(如边框与背景色),但会处理基本的对齐。 % \begin{texnote} % 逐行输出,每行是一个水平盒子,因此可以正常分页。 % 另外,此命令是本宏包中渲染表格最快的命令。 % \end{texnote} % \end{function} % % % 以下章节 \ref{JSON 格式} 中的 JSON 输入示例的输出效果:(缩进量为 |4em|)\\ % \printtable[4em] % % 由于使用了水平盒子 |+ \\|,其副作用是在嵌套环境中可能报错 “There's no line here to end.”。 % 要在类似环境中使用本命令输出表格,请使用星号版本,区别在于它不会输出 |\\|。 % % \begin{verbatim} % \begin{table}[!htp] % \centering % \caption{嵌套的 \cs{printtable}} % \parbox[t]{\textwidth}{\printtable*} % \label{tab:嵌套的printtable} % \end{table} % \end{verbatim} % 以上示例展示如何在表格浮动体中使用 \cs{printtable},其效果如表 \ref{tab:嵌套的printtable}。 % % \begin{table}[!htp] % \centering % \caption{嵌套的 \cs{printtable}} % \parbox[t]{\textwidth}{\printtable*} % \label{tab:嵌套的printtable} % \end{table} % % % \begin{function}{\rendertable, \rowborder, \colborder} % \begin{syntax} % \cs{rendertable} [booktabs] \oarg{中间的线} % \cs{rendertable} [grid] % ~~\{ % ~~~~\cs{rowborder} \oarg{默认值} \Arg{边框列表} % ~~~~\cs{colborder} \oarg{默认值} \Arg{边框列表} % ~~\} % \end{syntax} % 按预定义的渲染方式渲染表格。功能开发中,目前参数支持有限。\todo{更新命令说明} % 前面介绍输入时显示的表格就是使用本命令(无参数)渲染的。 % \begin{texnote} % 在渲染逻辑上,它是通过 \pkg{l3draw} 绘制的,即它是一个整体。 % 可以直接嵌套在其它环境中(如表 \ref{tab:rendertable[booktabs]}、 % 表 \ref{tab:rendertable[grid]}),但它不会分页。 % \end{texnote} % \end{function} % % \begin{xtable} % \begin{data}[loc={2,1},sep={&}] % Name & Sex & Age\\ % John & man & 18\\ % Leon & man & 20\\ % Lily & woman & 21 % \end{data} % \cell(1,Sex){man\\woman\\unknown} % \cell(1,3){18\~{}25} % \colwidth[a]{} % \rowalign[m]{} % \colalign{l,c,r} % \end{xtable} % % % \begin{table}[!htp] % \begin{minipage}{0.45\textwidth} % \centering % \caption{\cs{rendertable}\texttt{[booktabs]}} % \rendertable[booktabs][1] % \label{tab:rendertable[booktabs]} % \end{minipage} % \begin{minipage}{0.45\textwidth} % \centering % \caption{\cs{rendertable}\texttt{[grid]}} % \rendertable[grid] % { % \rowborder{0.7pt,midrule,3=dash,bottomrule} % \colborder{0.7pt,2=dotted} % } % \label{tab:rendertable[grid]} % \end{minipage} % \end{table} % % \subsection{选项参数} % % \subsubsection{宏包选项} % % % \begin{function}{xtable/package/rowsep, xtable/package/colsep} % \begin{syntax} % colsep = {0.5em}, rowsep = {0.6ex} % \end{syntax} % 设置表格的行间/列间间距。 % \end{function} % % % \begin{function}{xtable/package/margin} % \begin{syntax} % margin = \{0.4em, 0.6ex\} % \end{syntax} % 设置表格的单元格的边距。 % \end{function} % % % \begin{function}{xtable/package/vspace} % \begin{syntax} % vspace = \{0.5ex, 0ex\} % \end{syntax} % 设置表格的上下边距。暂时只有顶边距生效。\todo{更新实现与说明} % \end{function} % % % \begin{function}{xtable/package/minwidth, xtable/package/lineskip} % \begin{syntax} % minwidth = {1.5em}, lineskip = {3ex} % \end{syntax} % 设置单元格的最小宽度及行基线间距。 % \end{function} % % % \subsubsection{设置命令} % % \begin{function}{\linepatternset} % \begin{syntax} % \cs{linepatternset} \Arg{名称} \Arg{定义} % \end{syntax} % 将指定 \meta{名称} 的线型映射到指定 \meta{定义}。 % 线型定义的方式由一系列长度值组成,即 |{线, 空, ...}|。 % \end{function} % % \begin{function}{\linewdset} % \begin{syntax} % \cs{linewdset} \Arg{名称} \Arg{尺寸} % \end{syntax} % 将指定 \meta{名称} 的线宽映射到指定 \meta{尺寸}。 % \end{function} % % \begin{function}{\linestyleset} % \begin{syntax} % \cs{linestyleset} \Arg{名称} \Arg{线型} \Arg{线宽} \Arg{颜色} % \end{syntax} % 将指定 \meta{名称} 的线样式映射到指定 \meta{线型}、\meta{线宽} 及 \meta{颜色}。 % \meta{线宽} 可以是之前定义的名称或字面值。 % \end{function} % % \subsection{调试} % % 本小节的命令主要用于输出一些表格的内部状态数据,以供调试使用。 % % \begin{function}{\showtable} % \begin{syntax} % \cs{showtable} % \end{syntax} % 以文本形式显示当前表格的数据。效果如下: % \end{function} % % \showtable % % \begin{function}{\logtable} % \begin{syntax} % \cs{logtable} % \end{syntax} % 在日志中显示表格的核心数据。 % \end{function} % % % \section{关键逻辑} % % \subsection{单元格大小} % % \subsubsection{间距} % % 对于无边框打印表格,列间距由 \cs{rowsep} 设置,表格四周无空隙。 % 行间距由 \LaTeX 自行按普通行处理。 % % 对于有边框的渲染表格,单元格的边距由 \cs{margin} 确认。 % 相当于行列间距有 $2$ 倍的边距),表格四周有单倍的边距。 % % \subsubsection{宽度} % % 宽度的计算步骤如下: % \begin{enumerate} % \item 计算所有列的自然宽度(该列中所有单元格的自然宽度的最大值); % \item 将固定宽度的列的宽度设置为指定值; % \item 将 |same| 与 |samefill| 的宽度设置为同组最大值; % \item 计算当前总宽度与目标总宽度的差异,计算补偿值; % \item 将所有 |fill| 与 |samefill| 的列宽度加上补偿值。 % \end{enumerate} % % 注:列宽度不包含边距,但总宽度有计算。 % % % \subsubsection{高度} % % 高度的计算步骤如下: % \begin{enumerate} % \item 计算所有单元格的首行填充值(用于平衡居中对齐时的基线问题); % \item 计算所有行的自然高度与深度(该行中所有单元格的自然高度与浓度的最大值); % \item 将固定高度的列的高度设置为(指定值-深度); % \item 将 |same| 的高度与深度设置为同组最大值。 % \end{enumerate} % % 图 \ref{fig:表格渲染逻辑} 展示了基本的计算逻辑。\\ % % \begin{figure}[htp] % \centering % \ExplSyntaxOn % \draw_begin: % \draw_set_linewidth:n {1pt} ^^A 边框 % \draw_path_rectangle:nn {0,0} {2cm,1cm} % \draw_path_rectangle:nn {2cm,0cm} {3cm,1cm} % \draw_path_rectangle:nn {0cm,1cm} {2cm,1cm} % \draw_path_rectangle:nn {2cm,1cm} {3cm,1cm} % \draw_path_use_clear:n { stroke } % \color_fill:n { black!40 } ^^A 最终尺寸 % \draw_path_rectangle:nn {0cm + 2pt, 0cm + 2pt} {2cm - 4pt, 1cm - 4pt} % \draw_path_rectangle:nn {2cm + 2pt, 0cm + 2pt} {3cm - 4pt, 1cm - 4pt} % \draw_path_rectangle:nn {0cm + 2pt, 1cm + 2pt} {2cm - 4pt, 1cm - 4pt} % \draw_path_rectangle:nn {2cm + 2pt, 1cm + 2pt} {3cm - 4pt, 1cm - 4pt} % \draw_path_use_clear:n { fill } % \color_fill:n { yellow!80!black } ^^A 自然尺寸 % \draw_path_rectangle:nn {0cm + 5pt, 0cm + 3pt} {2cm - 10pt, 1cm - 6pt} % \draw_path_rectangle:nn {2cm + 5pt, 0cm + 3pt} {3cm - 10pt, 1cm - 6pt} % \draw_path_rectangle:nn {0cm + 5pt, 1cm + 3pt} {2cm - 10pt, 1cm - 6pt} % \draw_path_rectangle:nn {2cm + 5pt, 1cm + 3pt} {3cm - 10pt, 1cm - 6pt} % \draw_path_use_clear:n { fill } % \color_fill:n { green!50!black } ^^A 实际内容 % \draw_path_rectangle:nn {0cm + 10pt, 0cm + 6pt} {2cm - 20pt, 1cm - 12pt} % \draw_path_rectangle:nn {2cm + 2pt, 0cm + 7pt} {3cm - 40pt, 1cm - 14pt} % \draw_path_rectangle:nn {0cm + 15pt, 1cm + 11pt} {2cm - 30pt, 1cm - 13pt} % \draw_path_rectangle:nn {2cm + 2pt, 1cm + 6pt} {3cm - 60pt, 1cm - 8pt} % \draw_path_use_clear:n { fill } % \color_select:n {black} ^^A 对齐方式 % \hbox_set:Nn \l_tmpa_box {居中对齐} % \draw_box_use:Nn \l_tmpa_box % {1cm - (\box_wd:N \l_tmpa_box)/2, 2.2cm} % \hbox_set:Nn \l_tmpa_box {左对齐} % \draw_box_use:Nn \l_tmpa_box % {3.5cm - (\box_wd:N \l_tmpa_box)/2, 2.2cm} % \hbox_set:Nn \l_tmpa_box {上下居中} % \draw_box_use:Nn \l_tmpa_box % {-0.4 - \box_wd:N \l_tmpa_box, 0.5cm - (\box_ht:N \l_tmpa_box)/2} % \hbox_set:Nn \l_tmpa_box {顶对齐} % \draw_box_use:Nn \l_tmpa_box % {-0.4 - \box_wd:N \l_tmpa_box, 1.5cm - (\box_ht:N \l_tmpa_box)/2} % \vbox_set:Nn \l_tmpa_box ^^A 图例 % { % \hbox:n % { % \draw_begin: % \draw_set_linewidth:n {1pt} % \draw_path_rectangle:nn {0,0} {0.5cm-1pt, 0.3cm-1pt} % \draw_path_use_clear:n { stroke } % \draw_end: % \ 线宽与间隙(白色) % } % \hbox:n % { \color_fill:n { black!40 } \rule{0.5cm}{0.3cm}~最终尺寸} % \hbox:n % { \color_fill:n { yellow!80!black } \rule{0.5cm}{0.3cm}~自然尺寸 } % \hbox:n % { \color_fill:n { green!50!black } \rule{0.5cm}{0.3cm}~实际内容 } % } % \draw_box_use:Nn \l_tmpa_box {5.5cm, 0cm} % \draw_end: % \ExplSyntaxOff % \caption{表格渲染逻辑} \label{fig:表格渲染逻辑} % \end{figure} % % % % \end{documentation} % % % ^^A ---------------------------------- 实现部分 ---------------------------------- % % \newpage % % \begin{implementation} % % \section{基础定义} % % 本宏包的开发测试环境使用的 \LaTeX2e 的版本为 <2023-11-01> , % L3 编程层的版本为 <2024-02-20>。 % % \begin{macrocode} %<*package> %<@@=xtable> \NeedsTeXFormat{LaTeX2e}[2023-11-01] \ProvidesExplPackage{tabular2}{2025-08-21}{0.2} {A new table implementation base on expl3} \RequirePackage{l3draw}[2024-02-20] % \end{macrocode} % % \subsection{通用变体} % % 定义一些必要的系统函数的变体。 % \begin{macrocode} \cs_generate_variant:Nn \msg_error:nnn {nnV} \cs_generate_variant:Nn \str_const:Nn {Ne} \cs_generate_variant:Nn \seq_set_split:Nnn {NVn} \cs_generate_variant:Nn \seq_set_item:Nnn {NnV, Nne} \cs_generate_variant:Nn \seq_gset_item:Nnn {NnV, Nne} \cs_generate_variant:Nn \prop_gput:Nnn {Nne, Nen, Nee, NeV, NVe, NVV} \cs_generate_variant:Nn \prop_gpop:NnN {NeN} \cs_generate_variant:Nn \color_stroke:n {e} \cs_generate_variant:Nn \regex_extract_all:NnN {NVN} % \end{macrocode} % % % \subsection{\pkg{l3draw} 函数} % % 在 2025-06-30 发布的新版 \pkg{l3draw} 中,将部分设置函数的函数名添加了 |set| 部分。 % 但考虑到大部分用户可能并未升级到此版本,故增加此部分以兼容旧版本。 % \begin{macrocode} \cs_if_free:NT \draw_set_linewidth:n { \cs_gset_eq:NN \draw_set_linewidth:n \draw_linewidth:n } \cs_if_free:NT \draw_set_dash_pattern:nn { \cs_gset_eq:NN \draw_set_dash_pattern:nn \draw_dash_pattern:nn } % \end{macrocode} % % 定义 \pkg{l3draw} 函数的变体。 % \begin{macrocode} \cs_generate_variant:Nn \draw_set_linewidth:n {V, e} \cs_generate_variant:Nn \draw_set_dash_pattern:nn {Vn,en} % \end{macrocode} % % % \subsection{消息定义} % % % 常见的错误消息。 % \begin{macrocode} \msg_new:nnn {xtable} {unknown_row_name} {未知的行名称:<#1>} \msg_new:nnn {xtable} {unknown_col_name} {未知的列名称:<#1>} \msg_new:nnn {xtable} {unknown_input} {未知的输入 <#1>} \msg_new:nnn {xtable} {unknown_format} {未知的格式 <#1>} \msg_new:nnn {xtable} {unknown_cell} {未知的单元格数据 @<#1>} \msg_new:nnn {xtable} {outside_table} {<#1> 应当在 xtable 的输入环境中使用} \msg_new:nnn {xtable} {outside_render} {<#1> 应当在 xtable 的输出环境中使用} \msg_new:nnn {xtable} {unsaved_table} {未保存的表:<#1>} % \end{macrocode} % % % \subsection{通用变量} % % \subsubsection{全局常量} % % \begin{variable}{\c_@@_space_str, \c_@@_escape_str, \c_@@_lbrace_str, \c_@@_rbrace_str} % 定义一些不易直接输入的字符常量。 % \begin{macrocode} \str_const:Ne \c_@@_space_str { \char_generate:nn {32} {10} } % \str_const:Ne \c_@@_escape_str { \char_generate:nn {92} {12} } % \ \str_const:Ne \c_@@_lbrace_str { \char_generate:nn {123} {12} } % { \str_const:Ne \c_@@_rbrace_str { \char_generate:nn {125} {12} } % } % \end{macrocode} % \end{variable} % % \begin{variable}{\c_@@_std_ht_dim, \c_@@_std_dp_dim} % 单行文字的标准高度与深度。 % \begin{macrocode} \dim_const:Nn \c_@@_std_ht_dim {1.91ex} % ≈汉字的高度,字母高度≈{1.67ex} \dim_const:Nn \c_@@_std_dp_dim {0.48ex} % ≈字母的深度 % \end{macrocode} % \end{variable} % % % \begin{variable}{\s_@@_mark} % 定义内部标记。 % \begin{macrocode} \scan_new:N \s_@@_mark % \end{macrocode} % \end{variable} % % % \subsubsection{专用变量} % % 本小节定义一些专用的变量,以供缓存或函数之间传递特定的数据。 % 使用这些变量的函数应当在文档中声明。 % % % \begin{variable}{\l_@@_row_loc_int, \l_@@_col_loc_int} % 用于存储行列坐标信息。 % \begin{macrocode} \int_new:N \l_@@_row_loc_int \int_new:N \l_@@_col_loc_int % \end{macrocode} % \end{variable} % % % \begin{variable}{\l_@@_x_dim, \l_@@_y_dim} % 用于存储绘图坐标信息。 % \begin{macrocode} \dim_new:N \l_@@_x_dim \dim_new:N \l_@@_y_dim % \end{macrocode} % \end{variable} % % % \begin{variable}{\l_@@_wd_dim, \l_@@_ht_dim, \l_@@_dp_dim, \l_@@_fill_dim} % 用于存储宽高深等尺寸信息。 % \begin{macrocode} \dim_new:N \l_@@_wd_dim \dim_new:N \l_@@_ht_dim \dim_new:N \l_@@_dp_dim \dim_new:N \l_@@_fill_dim % \end{macrocode} % \end{variable} % % % \begin{variable}{\l_@@_line_dim} % 用于存储线宽信息。 % \begin{macrocode} \dim_new:N \l_@@_line_dim % \end{macrocode} % \end{variable} % % % \begin{variable}{\l_@@_style_tl, \l_@@_data_tl} % 用于存储样式与数据的凭据表。 % \begin{macrocode} \tl_new:N \l_@@_style_tl \tl_new:N \l_@@_data_tl % \end{macrocode} % \end{variable} % % \begin{variable}{\l_@@_row_align_tl, \l_@@_col_align_tl, \l_@@_pattern_tl, \l_@@_color_tl} % 用于存储各项样式的凭据表。 % \begin{macrocode} \tl_new:N \l_@@_row_align_tl \tl_new:N \l_@@_col_align_tl \tl_new:N \l_@@_pattern_tl \tl_new:N \l_@@_color_tl % \end{macrocode} % \end{variable} % % % \begin{variable}{\l_@@_shared_seq} % 用于函数之间共享数据的属性表。 % \begin{macrocode} \seq_new:N \l_@@_shared_seq % \end{macrocode} % \end{variable} % % % \begin{variable}{\l_@@_cell_box} % 用于存储单元格内容的盒子。 % \begin{macrocode} \box_new:N \l_@@_cell_box % \end{macrocode} % \end{variable} % % % \subsubsection{缓存变量} % % 本小节定义一些缓存的变量,以供函数内保存数据,而不受调用函数的影响。 % 使用这些变量的函数应当在文档中声明。 % % \begin{variable}{\l_@@_cachea_int, \l_@@_cacheb_int} % 用于缓存整数。 % \begin{macrocode} \int_new:N \l_@@_cachea_int \int_new:N \l_@@_cacheb_int % \end{macrocode} % \end{variable} % % % \begin{variable}{\l_@@_cachea_dim, \l_@@_cacheb_dim} % 用于缓存尺寸信息。 % \begin{macrocode} \dim_new:N \l_@@_cachea_dim \dim_new:N \l_@@_cacheb_dim % \end{macrocode} % \end{variable} % % % \begin{variable}{\l_@@_cachea_tl, \l_@@_cacheb_tl} % 用于缓存的凭据表。 % \begin{macrocode} \tl_new:N \l_@@_cachea_tl \tl_new:N \l_@@_cacheb_tl % \end{macrocode} % \end{variable} % % % \begin{variable}{\l_@@_cachea_seq, \l_@@_cacheb_seq} % 用于缓存的属性表。 % \begin{macrocode} \seq_new:N \l_@@_cachea_seq \seq_new:N \l_@@_cacheb_seq % \end{macrocode} % \end{variable} % % % \subsubsection{临时变量} % % 本小节定义一些临时变量\footnote{不使用系统提供的临时变量,以防止和其它宏包冲突。}, % 以供函数内部使用\footnote{不应当使用这些变量在函数之间传递数据。}。 % 可以随意使用这些变量而无需当在文档中声明 % \footnote{应当假定调用任何函数都会修改这些变量的值。}。 % % \begin{variable}{\l_@@_tmpa_int, \l_@@_tmpb_int} % 整数变量。 % \begin{macrocode} \int_new:N \l_@@_tmpa_int \int_new:N \l_@@_tmpb_int % \end{macrocode} % \end{variable} % % % \begin{variable}{\l_@@_tmpa_dim, \l_@@_tmpb_dim} % 长度变量。 % \begin{macrocode} \dim_new:N \l_@@_tmpa_dim \dim_new:N \l_@@_tmpb_dim % \end{macrocode} % \end{variable} % % % \begin{variable}{\l_@@_tmpa_tl, \l_@@_tmpb_tl, \l_@@_tmpa_str, \l_@@_tmpb_str} % 凭据表与字符串。 % \begin{macrocode} \tl_new:N \l_@@_tmpa_tl \tl_new:N \l_@@_tmpb_tl \str_new:N \l_@@_tmpa_str \str_new:N \l_@@_tmpb_str % \end{macrocode} % \end{variable} % % % \begin{variable}{\l_@@_tmpa_seq, \l_@@_tmpb_seq, \l_@@_tmpa_prop, \l_@@_tmpb_prop} % 序列与属性表。 % \begin{macrocode} \seq_new:N \l_@@_tmpa_seq \seq_new:N \l_@@_tmpb_seq \prop_new:N \l_@@_tmpa_prop \prop_new:N \l_@@_tmpb_prop % \end{macrocode} % \end{variable} % % % \begin{variable}{\l_@@_tmpa_box, \l_@@_tmpb_box} % 盒子对象。 % \begin{macrocode} \box_new:N \l_@@_tmpa_box \box_new:N \l_@@_tmpb_box % \end{macrocode} % \end{variable} % % % \subsection{通用选项} % % \subsubsection{宏包选项} % % \begin{variable}{\g_@@_row_sep_dim, \g_@@_col_sep_dim, \g_@@_row_margin_dim, % \g_@@_col_margin_dim, \g_@@_above_space_dim, \g_@@_below_space_dim} % 表格间隙与边空。 % \begin{macrocode} \dim_new:N \g_@@_row_sep_dim % 行与行的最小间距(通常无边框线) \dim_new:N \g_@@_col_sep_dim \dim_new:N \g_@@_row_margin_dim % 行的最小边距(通常有边框线) \dim_new:N \g_@@_col_margin_dim \dim_new:N \g_@@_above_space_dim % 表格上下边距 \dim_new:N \g_@@_below_space_dim \dim_new:N \g_@@_cell_wd_min_dim % 单元格的最小宽度 \dim_new:N \g_@@_cell_lineskip_dim % 多行单元格的行距 % \end{macrocode} % \end{variable} % % % \begin{variable}{xtable/package} % 定义宏包选项。 % \begin{macrocode} \keys_define:nn { xtable / package } { rowsep .dim_gset:N = \g_@@_row_sep_dim, rowsep .initial:n = {0.6ex}, colsep .dim_gset:N = \g_@@_col_sep_dim, colsep .initial:n = {0.5em}, margin .code:n = { \dim_gset:Nn \g_@@_row_margin_dim { \clist_item:nn {#1} {2} } \dim_gset:Nn \g_@@_col_margin_dim { \clist_item:nn {#1} {1} } }, margin .initial:n = {0.4em, 0.6ex}, vspace .code:n = { \dim_gset:Nn \g_@@_above_space_dim { \clist_item:nn {#1} {1} } \dim_gset:Nn \g_@@_below_space_dim { \clist_item:nn {#1} {2} } }, vspace .initial:n = {0.5ex, -1ex}, minwidth .dim_gset:N = \g_@@_cell_wd_min_dim, minwidth .initial:n = {1.5em}, lineskip .dim_gset:N = \g_@@_cell_lineskip_dim, lineskip .initial:n = {3ex} } \ProcessKeyOptions [ xtable / package ] % \end{macrocode} % \end{variable} % % % \subsubsection{全局线样式} % % \begin{variable}{\g_@@_line_pattern_prop, \g_@@_line_wd_prop, \g_@@_line_style_prop} % 表格线型、线宽与样式。 % 样式的值为\{线型, 线宽, 颜色\}(\cs{s_@@_mark} 分隔)。 % \begin{macrocode} \prop_new:N \g_@@_line_pattern_prop \prop_new:N \g_@@_line_wd_prop \prop_new:N \g_@@_line_style_prop % \end{macrocode} % \end{variable} % % % % \begin{macro}{\linepatternset, \linewdset, \linestyleset} % 设置边框线的线宽、线型与样式。 % \begin{macrocode} \NewDocumentCommand {\linepatternset} { m m } { \prop_gput:Nnn \g_@@_line_pattern_prop {#1} {#2} } \NewDocumentCommand {\linewdset} { m m } { \prop_gput:Nnn \g_@@_line_wd_prop {#1} {#2} } \NewDocumentCommand {\linestyleset} { m m m m } { \prop_get:NnNF \g_@@_line_pattern_prop {#2} \l_@@_tmpa_tl { \tl_set:Nn \l_@@_tmpa_tl {#2} } \prop_get:NnNF \g_@@_line_wd_prop {#3} \l_@@_tmpb_tl { \tl_set:Nn \l_@@_tmpb_tl {#3} } \prop_gput:Nne \g_@@_line_style_prop {#1} {\l_@@_tmpa_tl \s_@@_mark \l_@@_tmpb_tl \s_@@_mark #4} } \linepatternset {soild} {} \linepatternset {dotted}{0.6em, 0.15em} \linepatternset {dash} {0.6em, 0.15em, 0.05em, 0.15em} \linewdset {normal} {1pt} \linewdset {thick} {1.5pt} \linewdset {thin} {0.75pt} \linewdset {empty} {0pt} \linewdset {toprule} {0.08em} \linewdset {midrule} {0.05em} \linewdset {cmidrule} {0.03em} \linewdset {bottomrule} {0.08em} \linestyleset {normal} {soild} {normal} {black} \linestyleset {dotted} {dotted} {normal} {black} \linestyleset {dash} {dash} {normal} {black} \linestyleset {bold} {soild} {thick} {black} % \end{macrocode} % \end{macro} % % % \subsection{通用函数} % % % \subsubsection{常规设置} % % \begin{macro}{\@@_init_seq:Nnn, \@@_ginit_seq:Nnn} % 初始化序列 |#1|,使其元素个数为 |#2|,且元素内容均为 |#3|。 % \begin{macrocode} \cs_new:Nn \@@_init_seq:Nnn { \seq_clear:N #1 \prg_replicate:nn {#2} { \seq_put_right:Nn #1 {#3} } } \cs_new:Nn \@@_ginit_seq:Nnn { \seq_gclear:N #1 \prg_replicate:nn {#2} { \seq_gput_right:Nn #1 {#3} } } \cs_generate_variant:Nn \@@_init_seq:Nnn {Nne} % \end{macrocode} % \end{macro} % % % \begin{macro}{\@@_int_set_max:Nn, \@@_int_gset_max:Nn} % 将变量(|#1|)的值与指定值(|#2|)中的最大值赋值给变量。 % \begin{macrocode} \cs_new:Nn \@@_int_set_max:Nn { \int_set:Nn #1 { \int_max:nn {#1} {#2} } } \cs_new:Nn \@@_int_gset_max:Nn { \int_gset:Nn #1 { \int_max:nn {#1} {#2} } } % \end{macrocode} % \end{macro} % % % \begin{macro}{\@@_dim_set_max:NN, \@@_dim_set_max:Nn} % 将变量(|#1|)的值与指定值(|#2|)中的最大值赋值给变量。 % \begin{macrocode} \cs_new:Nn \@@_dim_set_max:Nn { \dim_set:Nn #1 { \dim_max:nn {#1} {#2} } } \cs_new:Nn \@@_dim_set_max:NN { \dim_set:Nn #1 { \dim_max:nn {#1} {#2} } } % \end{macrocode} % \end{macro} % % % \subsubsection{线样式相关} % % \begin{macro}{\@@_set_line_pattern:n, \@@_set_line_wd:n, \@@_set_line_style:n} % 设置当前线型、线宽及样式。 % \begin{macrocode} \cs_new:Nn \@@_set_line_pattern:n { \prop_get:NnNTF \g_@@_line_pattern_prop {#1} \l_@@_tmpa_tl { \draw_set_dash_pattern:Vn \l_@@_tmpa_tl {0pt} } { \draw_set_dash_pattern:nn {#1} {0pt} } } \cs_new:Nn \@@_set_line_wd:n { \prop_get:NnNTF \g_@@_line_wd_prop {#1} \l_@@_tmpa_tl { \draw_set_linewidth:V \l_@@_tmpa_tl } { \draw_set_linewidth:n {#1} } } \cs_new:Nn \@@_set_line_style:n { \prop_get:NnNTF \g_@@_line_style_prop {#1} \l_@@_tmpa_tl { \seq_set_split:NnV \l_@@_tmpa_seq {\s_@@_mark} \l_@@_tmpa_tl \draw_set_dash_pattern:en { \seq_item:Nn \l_@@_tmpa_seq {1} } {0pt} \draw_set_linewidth:e { \seq_item:Nn \l_@@_tmpa_seq {2} } \color_stroke:e { \seq_item:Nn \l_@@_tmpa_seq {3} } } { \@@_set_line_pattern:n {soild} \@@_set_line_wd:n {#1} \color_stroke:e {black} } } \cs_generate_variant:Nn \@@_set_line_style:n {V} % \end{macrocode} % \end{macro} % % % \begin{macro}{\@@_parse_line_style:n} % 解析当前线的样式,并将结果分别保存在 \cs{l_@@_type_tl}、 % \cs{l_@@_line_dim} 与 \cs{l_@@_color_tl} 中。 % \begin{macrocode} \cs_new:Nn \@@_parse_line_style:n { \prop_get:NnNTF \g_@@_line_style_prop {#1} \l_@@_tmpa_tl { \seq_set_split:NnV \l_@@_tmpa_seq {,} \l_@@_tmpa_tl \tl_set:Ne \l_@@_pattern_tl { \seq_item:Nn \l_@@_tmpa_seq {1} } \dim_set:Nn \l_@@_line_dim { \seq_item:Nn \l_@@_tmpa_seq {2} } \tl_set:Ne \l_@@_color_tl { \seq_item:Nn \l_@@_tmpa_seq {3} } } { \prop_get:NnNTF \g_@@_line_wd_prop {#1} \l_@@_tmpa_tl { \dim_set:Nn \l_@@_line_dim { \l_@@_tmpa_tl } } { \dim_set:Nn \l_@@_line_dim {#1} } \tl_if_empty:NT \l_@@_pattern_tl { \tl_set:Nn \l_@@_pattern_tl { soild } } \tl_if_empty:NT \l_@@_color_tl { \tl_set:Nn \l_@@_color_tl { black } } } } \cs_generate_variant:Nn \@@_parse_line_style:n {V} % \end{macrocode} % \end{macro} % % % \section{表格核心} % % % 在表格的内部存储中,统一使用数字坐标\footnote{数据从 $1$ 开始,行/列标题(不是表格标题)的坐标为 $0$。}为索引(键); % 行名与列名仅用于输入接口。 % % % \subsection{核心存储} % % 本小节定义一些用于存储表格数据与格式的变量。 % % % \subsubsection{行号与列名} % % \begin{variable}{\g_@@_row_name_prop, \g_@@_col_name_prop} % 存储行号与列名与其数字坐标的映射关系(name -> number)。 % 唯二不使用数字坐标为索引的变量。 % \begin{macrocode} \prop_new:N \g_@@_row_name_prop \prop_new:N \g_@@_col_name_prop % \end{macrocode} % \end{variable} % % % \subsubsection{单元格内容} % % % \begin{variable}{\c_@@_cell_content_tl, \c_@@_cell_formula_tl, \c_@@_cell_merged_tl, \c_@@_cell_empty_tl} % 定义单元格的类型。 % \begin{macrocode} \tl_const:Nn \c_@@_cell_content_tl {content} \tl_const:Nn \c_@@_cell_formula_tl {formula} \tl_const:Nn \c_@@_cell_merged_tl {merged} \tl_const:Nn \c_@@_cell_empty_tl {empty} % \end{macrocode} % \end{variable} % % % % \begin{variable}{\g_@@_has_header_bool, \g_@@_row_count_int, \g_@@_col_count_int, \g_@@_cell_data_prop} % 定义存储的单元格内容的属性表及最大行列数。\\ % 单元格内容:row,col -> \{type, content, formula\}(分隔符为 \cs{s_@@_mark})。 % \begin{macrocode} \bool_new:N \g_@@_has_header_bool \int_new:N \g_@@_row_count_int \int_new:N \g_@@_col_count_int \prop_new:N \g_@@_cell_data_prop % 不记录 header 单元格 % \end{macrocode} % \end{variable} % % % \begin{variable}{\g_@@_row_summary_prop, \g_@@_col_formula_prop} % 行汇总与列公式。\\ % 行汇总定义:row -> \{formula_code, range\};\\ % 列公式定义:col -> \{formula_code, dependencies\}。 % \begin{macrocode} \prop_new:N \g_@@_row_summary_prop \prop_new:N \g_@@_col_formula_prop % \end{macrocode} % \end{variable} % % % \subsubsection{单元格样式} % % 与单元格内容不同,单元格样式会记录 Header \footnote{ % Header 的内容由 col name 存储,所以不重复记录。} 信息。 % % % \begin{variable}{\g_@@_row_ht_style_seq, \g_@@_col_wd_style_seq, \g_@@_row_align_seq, \g_@@_col_align_seq} % 基本的行列样式信息。 % \begin{macrocode} \seq_new:N \g_@@_row_ht_style_seq \seq_new:N \g_@@_col_wd_style_seq \seq_new:N \g_@@_row_align_seq \seq_new:N \g_@@_col_align_seq % \end{macrocode} % \end{variable} % % % \subsubsection{合并单元格} % % % \begin{variable}{\g_@@_merge_count_int, \g_@@_merge_info_prop, \g_@@_merge_ref_prop} % 合并单元格数量、合并单元格信息表及引用表。\\ % 信息表:merge_id -> \{start_row, start_col, end_row, end_col, content\};\\ % 引用表:row,col -> merge_id。 % \begin{macrocode} \int_new:N \g_@@_merge_count_int \prop_new:N \g_@@_merge_info_prop \prop_new:N \g_@@_merge_ref_prop % \end{macrocode} % \end{variable} % % % \subsubsection{中间变量} % % \begin{variable}{\g_@@_col_header_seq} % 用于存储列标题的序列,在输入完成后需要刷新此变量的值。 % \begin{macrocode} \seq_new:N \g_@@_col_header_seq % \end{macrocode} % \end{variable} % % % \begin{variable}{\g_@@_row_sep_seq, \g_@@_col_sep_seq} % 用于存储间隙信息,在渲染前需要首先刷新此变量的值。 % \begin{macrocode} \seq_new:N \g_@@_row_sep_seq \seq_new:N \g_@@_col_sep_seq % \end{macrocode} % \end{variable} % % % \begin{variable}{\g_@@_row_ht_seq, \g_@@_row_dp_seq, \g_@@_col_wd_seq} % 用于行/列的存储高深宽信息,在渲染前需要刷新此变量的值。 % 如果列宽有使用填充样式,则需要刷新列间隙后再刷新列宽。 % \begin{macrocode} \seq_new:N \g_@@_row_ht_seq \seq_new:N \g_@@_row_dp_seq \seq_new:N \g_@@_col_wd_seq % \end{macrocode} % \end{variable} % % % \begin{variable}{\g_@@_cell_ht_prop, \g_@@_cell_vfill_prop} % 单元格填充值(补偿首行字高)及填充后高度,在渲染前需要刷新此变量的值。 % \begin{macrocode} \prop_new:N \g_@@_cell_ht_prop \prop_new:N \g_@@_cell_vfill_prop % \end{macrocode} % \end{variable} % % % % \begin{variable}{\g_@@_row_loc_seq, \g_@@_col_loc_seq} % 用于存储表格的行列坐标(图形坐标)信息。 % 需要考虑是否有 |Header|,需要在渲染条件确认后再刷新。 % \begin{macrocode} \seq_new:N \g_@@_row_loc_seq \seq_new:N \g_@@_col_loc_seq % \end{macrocode} % \end{variable} % % % \begin{variable}{\g_@@_row_border_seq, \g_@@_col_border_seq} % 用于存储行列边框线。需要考虑是否有 |Header|。 % \begin{macrocode} \seq_new:N \g_@@_row_border_seq \seq_new:N \g_@@_col_border_seq % \end{macrocode} % \end{variable} % % % \subsection{行列定位} % % % \subsubsection{行列名设置} % % % \begin{macro}{\@@_set_excel_col_names:n} % 设置 Excel 风格的列名,|#1| 为终止列数。 % \begin{macrocode} \cs_new:Nn \@@_set_excel_col_names:n { \int_step_inline:nn {#1} { \prop_gput:Nen \g_@@_col_name_prop { \int_to_Alph:n {##1} } {##1} } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_set_row_name:nn, \@@_set_col_name:nn} % 设置行名/列名: |#1| 为行名/列名,|#2| 为数字列坐标。 % \begin{macrocode} \cs_new:Nn \@@_set_row_name:nN { \prop_gput:Nne \g_@@_row_name_prop {#1} {\int_use:N #2} \@@_int_gset_max:Nn \g_@@_row_count_int {#2} } \cs_new:Nn \@@_set_col_name:nN { \prop_gput:Nne \g_@@_col_name_prop {#1} {\int_use:N #2} \@@_int_gset_max:Nn \g_@@_col_count_int {#2} } \cs_generate_variant:Nn \@@_set_row_name:nN {VN} \cs_generate_variant:Nn \@@_set_col_name:nN {VN} % \end{macrocode} % \end{macro} % % % \subsubsection{行列名解析} % % % \begin{macro}{\@@_get_row_loc:nN, \@@_get_col_loc:nN} % 返回指定行名、列名对应的数字坐标。 % \begin{macrocode} \cs_new:Nn \@@_get_row_loc:nN { \regex_match:nnTF {^-?[0-9]+$} {#1} { \int_set:Nn #2 {#1} } { \prop_get:NnNTF \g_@@_row_name_prop {#1} \l_@@_tmpa_tl { \int_set:Nn #2 {\l_@@_tmpa_tl} } { \msg_error:nnn {xtable} {unknown_row_name} {#1} } } } \cs_new:Nn \@@_get_col_loc:nN { \regex_match:nnTF {^-?[0-9]+$} {#1} { \int_set:Nn #2 {#1} } { \prop_get:NnNTF \g_@@_col_name_prop {#1} \l_@@_tmpa_tl { \int_set:Nn #2 {\l_@@_tmpa_tl} } { \msg_error:nnn {xtable} {unknown_col_name} {#1} } } } \cs_generate_variant:Nn \@@_get_row_loc:nN {VN} \cs_generate_variant:Nn \@@_get_col_loc:nN {VN} % \end{macrocode} % \end{macro} % % % \begin{macro}{\@@_get_row_loc:nNTF, \@@_get_col_loc:nNTF} % 返回指定行名、列名(不支持数字坐标)对应的数字坐标。 % \begin{macrocode} \prg_new_conditional:Nnn \@@_get_row_loc:nN { T , F, TF } { \prop_get:NnNTF \g_@@_row_name_prop {#1} \l_@@_tmpa_tl { \int_set:Nn #2 {\l_@@_tmpa_tl} \prg_return_true: } { \prg_return_false: } } \prg_new_conditional:Nnn \@@_get_col_loc:nN { T , F, TF } { \prop_get:NnNTF \g_@@_col_name_prop {#1} \l_@@_tmpa_tl { \int_set:Nn #2 {\l_@@_tmpa_tl} \prg_return_true: } { \prg_return_false: } } \prg_generate_conditional_variant:Nnn \@@_get_row_loc:nN {VN} {T, F, TF} \prg_generate_conditional_variant:Nnn \@@_get_col_loc:nN {VN} {T, F, TF} % \end{macrocode} % \end{macro} % % % \begin{macro}{\@@_parse_row_loc:n, \@@_parse_col_loc:n} % 解析坐标(支持数字和名称混合),|#1|:行名/列名。 % 解析的坐标保存在 \cs{l_@@_row_loc_int} 或 \cs{l_@@_col_loc_int} 中。 % \begin{macrocode} \cs_new:Nn \@@_parse_row_loc:n { \@@_get_row_loc:nN {#1} \l_@@_row_loc_int } \cs_new:Nn \@@_parse_col_loc:n { \@@_get_col_loc:nN {#1} \l_@@_col_loc_int } % \end{macrocode} % \end{macro} % % % \begin{macro}{\@@_parse_coord:nn, \@@_parse_coord:n} % 解析坐标(支持数字和名称混合),|#1|:行名,|#2|:列名。 % 解析的坐标保存在 \cs{l_@@_row_loc_int} 与 \cs{l_@@_col_loc_int} 中。 % \begin{macrocode} \cs_new:Nn \@@_parse_coord:nn { \@@_get_row_loc:nN {#1} \l_@@_row_loc_int \@@_get_col_loc:nN {#2} \l_@@_col_loc_int } \cs_generate_variant:Nn \@@_parse_coord:nn {ee} % \end{macrocode} % 使用逗号分隔列表作为参数输入的版本。 % \begin{macrocode} \cs_new:Nn \@@_parse_coord:n { \@@_parse_coord:ee { \clist_item:nn {#1} {1} } { \clist_item:nn {#1} {2} } } % \end{macrocode} % \end{macro} % % % \subsection{内容处理} % % % \subsubsection{初始化与保存} % % % \begin{macro}{\@@_table_init:} % 将初始化为空表。 % \begin{macrocode} \cs_new:Nn \@@_table_init: { \int_gzero:N \g_@@_row_count_int \int_gzero:N \g_@@_col_count_int \int_gzero:N \g_@@_merge_count_int \prop_gclear:N \g_@@_row_name_prop \prop_gclear:N \g_@@_col_name_prop \prop_gclear:N \g_@@_cell_data_prop \prop_gclear:N \g_@@_row_summary_prop \prop_gclear:N \g_@@_col_formula_prop \seq_gclear:N \g_@@_row_ht_style_seq \seq_gclear:N \g_@@_col_wd_style_seq \seq_gclear:N \g_@@_row_align_seq \seq_gclear:N \g_@@_col_align_seq \prop_gclear:N \g_@@_merge_info_prop \prop_gclear:N \g_@@_merge_ref_prop % \end{macrocode} % \begin{macrocode} \prop_gput:Nnn \g_@@_row_name_prop {header} {0} \prop_gput:Nnn \g_@@_row_name_prop {title} {0} \bool_gset_true:N \g_@@_has_header_bool } % \end{macrocode} % \end{macro} % % % \begin{macro}{\@@_table_save:n} % 保存表格。 % \begin{macrocode} \cs_new:Nn \@@_table_save:n { \bool_set_eq:NN \g_@@_has_header_bool \g_@@_has_header_#1_bool \int_gset_eq:NN \g_@@_row_count_int \g_@@_row_count_#1_int \int_gset_eq:NN \g_@@_col_count_int \g_@@_col_count_#1_int \int_gset_eq:NN \g_@@_merge_count_int \g_@@_merge_count_#1_int \prop_gset_eq:NN \g_@@_row_name_prop \g_@@_row_name_#1_prop \prop_gset_eq:NN \g_@@_col_name_prop \g_@@_col_name_#1_prop \prop_gset_eq:NN \g_@@_cell_data_prop \g_@@_cell_data_#1_prop \prop_gset_eq:NN \g_@@_row_summary_prop \g_@@_row_summary_#1_prop \prop_gset_eq:NN \g_@@_col_formula_prop \g_@@_col_formula_#1_prop \seq_set_eq:NN \g_@@_row_ht_style_seq \g_@@_row_ht_style_#1_seq \seq_set_eq:NN \g_@@_col_wd_style_seq \g_@@_col_wd_style_#1_seq \seq_set_eq:NN \g_@@_row_align_seq \g_@@_row_align_#1_seq \seq_set_eq:NN \g_@@_col_align_seq \g_@@_col_align_#1_seq \prop_gset_eq:NN \g_@@_merge_info_prop \g_@@_merge_info_#1_prop \prop_gset_eq:NN \g_@@_merge_ref_prop \g_@@_merge_ref_#1_prop } % \end{macrocode} % \end{macro} % % % \begin{macro}{\@@_table_restore:n} % 恢复表格。 % \begin{macrocode} \cs_new:Nn \@@_table_restore:n { \cs_if_exist:NTF \g_@@_row_count_#1_int { \bool_set_eq:NN \g_@@_has_header_#1_bool \g_@@_has_header_bool \int_gset_eq:NN \g_@@_row_count_#1_int \g_@@_row_count_int \int_gset_eq:NN \g_@@_col_count_#1_int \g_@@_col_count_int \int_gset_eq:NN \g_@@_merge_count_#1_int \g_@@_merge_count_int \prop_gset_eq:NN \g_@@_row_name_#1_prop \g_@@_row_name_prop \prop_gset_eq:NN \g_@@_col_name_#1_prop \g_@@_col_name_prop \prop_gset_eq:NN \g_@@_cell_data_#1_prop \g_@@_cell_data_prop \prop_gset_eq:NN \g_@@_row_summary_#1_prop \g_@@_row_summary_prop \prop_gset_eq:NN \g_@@_col_formula_#1_prop \g_@@_col_formula_prop \seq_set_eq:NN \g_@@_row_ht_style_#1_seq \g_@@_row_ht_style_seq \seq_set_eq:NN \g_@@_col_wd_style_#1_seq \g_@@_col_wd_style_seq \seq_set_eq:NN \g_@@_row_align_#1_seq \g_@@_row_align_seq \seq_set_eq:NN \g_@@_col_align_#1_seq \g_@@_col_align_seq \prop_gset_eq:NN \g_@@_merge_info_#1_prop \g_@@_merge_info_prop \prop_gset_eq:NN \g_@@_merge_ref_#1_prop \g_@@_merge_ref_prop } { \msg_error:nnn {xtable} {unsaved_table} {#1} } } % \end{macrocode} % \end{macro} % % % \subsubsection{单元格数据} % % % \begin{macro}{\@@_set_cell:nnnnn} % 设置单元格数据。|#1#2| 数字坐标,|#3| 类型,|#4| 内容,|#5| 公式。 % \begin{macrocode} \cs_new:Nn \@@_set_cell:nnnnn { \prop_gput:Nnn \g_@@_cell_data_prop {#1, #2} {#3 \s_@@_mark #4 \s_@@_mark #5} \@@_int_gset_max:Nn \g_@@_row_count_int {#1} \@@_int_gset_max:Nn \g_@@_col_count_int {#2} } \cs_generate_variant:Nn \@@_set_cell:nnnnn {eeVnn} % \end{macrocode} % \end{macro} % % % \begin{macro}{\@@_set_cell:NNn, \@@_set_cell:n} % 设置单元格内容。|#1#2| 数字坐标,|#3| 内容。 % \begin{macrocode} \cs_new:Nn \@@_set_cell:NNn { \tl_if_empty:nTF {#3} { \prop_gpop:NeN \g_@@_cell_data_prop { \int_use:N #1 , \int_use:N #2 } \l_@@_tmpa_tl } { \@@_set_cell:eeVnn { \int_use:N #1 } { \int_use:N #2 } \c_@@_cell_content_tl {#3} {} } } % \end{macrocode} % 设置单元格内容。|#1|: 内容,坐标使用 \cs{l_@@_row_loc_int} 与 \cs{l_@@_col_loc_int}。 % \begin{macrocode} \cs_new:Nn \@@_set_cell:n { \@@_set_cell:NNn \l_@@_row_loc_int \l_@@_col_loc_int {#1} } \cs_generate_variant:Nn \@@_set_cell:NNn {NNV} % \end{macrocode} % \end{macro} % % % % \begin{macro}{\@@_get_cell:nnN, \@@_parse_cell:nn} % 获取表格内容。|#1#2| 数字坐标,|#3| 存储内容的变量。支持获取 Header 内容。 % \begin{macrocode} \cs_new:Nn \@@_get_cell:nnN { \prop_get:NnNTF \g_@@_cell_data_prop {#1, #2} \l_@@_tmpa_tl { \seq_set_split:NnV \l_@@_tmpa_seq {\s_@@_mark} \l_@@_tmpa_tl \str_case_e:nnF { \seq_item:Nn \l_@@_tmpa_seq {1} } { { \c_@@_cell_content_tl } % 内容 { \tl_set:Ne #3 { \seq_item:Nn \l_@@_tmpa_seq {2} } } { \c_@@_cell_formula_tl } % 公式 { \tl_set:Ne #3 { \seq_item:Nn \l_@@_tmpa_seq {2} } } { \c_@@_cell_merged_tl } % 合并 { \tl_set:Nn #3 {} } { \c_@@_cell_empty_tl } % 空 { \tl_set:Nn #3 {} } } { \msg_error:nnV {xtable} {unknown_cell} {#1,#2}} } { \str_if_eq:nnTF {#1} {0} { \tl_set:Ne #3 {\seq_item:Nn \g_@@_col_header_seq {#2}} } { \tl_set:Nn #3 {} } % 空单元格 } } % \end{macrocode} % 获取表格内容。|#1#2| 数字坐标,结果保存在 \cs{l_@@_data_tl} 中。 % \begin{macrocode} \cs_new:Nn \@@_parse_cell:nn { \@@_get_cell:nnN {#1} {#2} \l_@@_data_tl } \cs_generate_variant:Nn {\@@_get_cell:nnN} {eeN} \cs_generate_variant:Nn {\@@_parse_cell:nn} {ee} % \end{macrocode} % \end{macro} % % % \subsection{格式处理} % % % \subsubsection{样式设置} % % \begin{macro}{\@@_set_row_style:NNn, \@@_set_col_style:NNn} % 设置行的样式。|#1|: 样式名,|#2|: 样式内容,|#3|: 默认值。 % 本命令会修改变量 \cs{l_@@_row_loc_int} 的值。 % \begin{macrocode} \cs_new:Nn \@@_set_row_style:NNn { \bool_if:NTF \g_@@_has_header_bool { \int_set:Nn \l_@@_tmpa_int {1} \int_set:Nn \l_@@_row_loc_int {0} } { \int_set:Nn \l_@@_tmpa_int {0} \int_set:Nn \l_@@_row_loc_int {1} } \@@_int_gset_max:Nn \g_@@_row_count_int { \seq_count:N #1 - \l_@@_tmpa_int } \@@_ginit_seq:Nnn #1 {\g_@@_row_count_int + 1} {#3} \seq_map_inline:Nn #2 { \str_if_in:nnTF {##1} {=} { \seq_set_split:Nnn \l_@@_tmpa_seq {=} {##1} \seq_get_left:NN \l_@@_tmpa_seq \l_@@_tmpa_tl \seq_get_right:NN \l_@@_tmpa_seq \l_@@_tmpb_tl \@@_get_row_loc:VN \l_@@_tmpa_tl \l_@@_row_loc_int } { \tl_set:Nn \l_@@_tmpb_tl {##1} } \seq_gset_item:NnV #1 {\l_@@_row_loc_int + 1} \l_@@_tmpb_tl \int_incr:N \l_@@_row_loc_int } } % \end{macrocode} % 设置列的样式。|#1|: 样式名,|#2|: 样式内容,|#3|: 默认值。 % 本命令会修改变量 \cs{l_@@_col_loc_int} 的值。 % \begin{macrocode} \cs_new:Nn \@@_set_col_style:NNn { \int_set:Nn \l_@@_col_loc_int {1} \@@_int_gset_max:Nn \g_@@_col_count_int {\seq_count:N #1} \@@_ginit_seq:Nnn #1 {\g_@@_col_count_int} {#3} \seq_map_inline:Nn #2 { \str_if_in:nnTF {##1} {=} { \seq_set_split:Nnn \l_@@_tmpa_seq {=} {##1} \seq_get_left:NN \l_@@_tmpa_seq \l_@@_tmpa_tl \seq_get_right:NN \l_@@_tmpa_seq \l_@@_tmpb_tl \@@_get_col_loc:VN \l_@@_tmpa_tl \l_@@_col_loc_int } { \tl_set:Nn \l_@@_tmpb_tl {##1} } \seq_gset_item:NnV #1 {\l_@@_col_loc_int} \l_@@_tmpb_tl \int_incr:N \l_@@_col_loc_int } } % \end{macrocode} % \end{macro} % % % % \begin{macro}{\@@_set_row_ht_style:Nn, \@@_set_col_wd_style:Nnn} % 设置行高的样式。|#1|: 样式内容,|#2|: 默认值。 % \begin{macrocode} \cs_new:Nn \@@_set_row_ht_style:Nn { \@@_set_row_style:NNn \g_@@_row_ht_style_seq #1 {#2} \int_step_inline:nn { \seq_count:N \g_@@_row_ht_style_seq } { \tl_set:Ne \l_@@_tmpa_tl { \seq_item:Nn \g_@@_row_ht_style_seq {##1} } \tl_replace_all:Nnn \l_@@_tmpa_tl {auto} {a} \tl_replace_all:Nnn \l_@@_tmpa_tl {same} {s} \seq_gset_item:NnV \g_@@_row_ht_style_seq {##1} \l_@@_tmpa_tl } } % \end{macrocode} % 设置列宽的样式。|#1|: 样式内容,|#2|: 默认值,|#3|: 总宽。 % \begin{macrocode} \cs_new:Nn \@@_set_col_wd_style:Nnn { \@@_set_col_style:NNn \g_@@_col_wd_style_seq #1 {#2} \int_step_inline:nn { \seq_count:N \g_@@_col_wd_style_seq } { \tl_set:Ne \l_@@_tmpa_tl { \seq_item:Nn \g_@@_col_wd_style_seq {##1} } \tl_replace_all:Nnn \l_@@_tmpa_tl {auto} {a} \tl_replace_all:Nnn \l_@@_tmpa_tl {same} {s} \tl_replace_all:Nnn \l_@@_tmpa_tl {fill} {f} \seq_gset_item:NnV \g_@@_col_wd_style_seq {##1} \l_@@_tmpa_tl } \seq_gput_right:Nn \g_@@_col_wd_style_seq {#3} } % \end{macrocode} % \end{macro} % % % \begin{macro}{\@@_set_row_align:Nn, \@@_set_col_align:Nn} % 设置行高样式。 % \begin{macrocode} \cs_new:Nn \@@_set_row_align:Nn { \@@_set_row_style:NNn \g_@@_row_align_seq #1 {#2} } \cs_new:Nn \@@_set_col_align:Nn { \@@_set_col_style:NNn \g_@@_col_align_seq #1 {#2} } % \end{macrocode} % \end{macro} % % % \begin{macro}{\@@_insure_style:} % 确保样式有设置好。本命令会修改变量 \cs{l_@@_shared_seq} 的值。 % \begin{macrocode} \cs_new:Nn \@@_insure_style: { \seq_clear:N \l_@@_shared_seq \seq_if_empty:NT \g_@@_row_ht_style_seq { \@@_set_row_ht_style:Nn \l_@@_shared_seq {a} } \seq_if_empty:NT \g_@@_col_wd_style_seq { \@@_set_col_wd_style:Nnn \l_@@_shared_seq {a} {\textwidth} } \seq_if_empty:NT \g_@@_row_align_seq { \@@_set_row_align:Nn \l_@@_shared_seq {b} } \seq_if_empty:NT \g_@@_col_align_seq { \@@_set_col_align:Nn \l_@@_shared_seq {c} } } % \end{macrocode} % \end{macro} % % % % \subsubsection{样式查询} % % \begin{macro}{\@@_get_row_ht_style:nN, \@@_get_col_wd_style:nN} % 获取行高的样式。|#1|: 数字坐标,|#2|: 存储的变量。 % \begin{macrocode} \cs_new:Nn \@@_get_row_ht_style:nN { \tl_set:Ne #2 { \seq_item:Nn \g_@@_row_ht_style_seq {#1 + 1} } } % \end{macrocode} % 获取列宽的样式。|#1|: 数字坐标,|#2|: 存储的变量。 % \begin{macrocode} \cs_new:Nn \@@_get_col_wd_style:nN { \tl_set:Ne #2 { \seq_item:Nn \g_@@_col_wd_style_seq {#1} } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_parse_row_ht_style:n, \@@_parse_col_wd_style:n} % 解析行高/列宽的样式。|#1|: 数字坐标,结果保存在 \cs{l_@@_style_tl} 中。 % \begin{macrocode} \cs_new:Nn \@@_parse_row_ht_style:n { \@@_get_row_ht_style:nN {#1} \l_@@_style_tl } \cs_new:Nn \@@_parse_col_wd_style:n { \@@_get_col_wd_style:nN {#1} \l_@@_style_tl } % \end{macrocode} % \end{macro} % % % % \begin{macro}{\@@_get_row_align:Nn, \@@_get_col_align:Nn} % 获取行列对齐方式。|#1|: 数字坐标,|#2|: 存储的变量。 % \begin{macrocode} \cs_new:Nn \@@_get_row_align:nN { \tl_set:Ne #2 { \seq_item:Nn \g_@@_row_align_seq {#1 + 1} } } \cs_new:Nn \@@_get_col_align:nN { \tl_set:Ne #2 { \seq_item:Nn \g_@@_col_align_seq {#1} } } % \end{macrocode} % \end{macro} % % % \begin{macro}{\@@_parse_row_align:n, \@@_parse_col_align:n} % 解析行高/列宽的样式。|#1|: 数字坐标,结果保存在 % \cs{l_@@_row_align_tl} 与 \cs{l_@@_col_align_tl} 中。 % \begin{macrocode} \cs_new:Nn \@@_parse_row_align:n { \@@_get_row_align:nN {#1} \l_@@_row_align_tl } \cs_new:Nn \@@_parse_col_align:n { \@@_get_col_align:nN {#1} \l_@@_col_align_tl } % \end{macrocode} % \end{macro} % % % \subsection{调试} % % % \begin{macro}{\showtable} % 显示表存储的内容。\todo{显示更多内容} % \begin{macrocode} \NewDocumentCommand \showtable {} { % \end{macrocode} % 输出列标题。 % \begin{macrocode} \prop_if_empty:NTF \g_@@_cell_data_prop {当前表格内容为空。} {当前表格内容如下:\\No.} \seq_map_inline:Nn \g_@@_col_header_seq { ,~\tl_to_str:n {##1} } % \end{macrocode} % 输出内容。 % \begin{macrocode} \int_step_inline:nn {\g_@@_row_count_int} { ;\\##1 \int_step_inline:nn {\g_@@_col_count_int} { \@@_get_cell:nnN {##1} {####1} \l_@@_tmpa_tl ,~\l_@@_tmpa_tl } }。 } % \end{macrocode} % \end{macro} % % % % \begin{macro}{\logtable} % 在日志中显示表格的核心数据。 % \begin{macrocode} \NewDocumentCommand \logtable {} { \int_show:N \g_@@_row_count_int \int_show:N \g_@@_col_count_int \int_show:N \g_@@_merge_count_int \prop_show:N \g_@@_row_name_prop \prop_show:N \g_@@_col_name_prop \prop_show:N \g_@@_cell_data_prop \prop_show:N \g_@@_row_summary_prop \prop_show:N \g_@@_col_formula_prop \seq_show:N \g_@@_row_ht_style_seq \seq_show:N \g_@@_col_wd_style_seq \seq_show:N \g_@@_row_align_seq \seq_show:N \g_@@_col_align_seq \prop_show:N \g_@@_merge_info_prop \prop_show:N \g_@@_merge_ref_prop \bool_show:N \g_@@_has_header_bool } % \end{macrocode} % \end{macro} % % % \section{中间计算} % % % \subsection{列标题} % % \begin{macro}{\@@_process_header:} % 处理表格列标题。 % \begin{macrocode} \cs_new:Nn \@@_process_header: { \prop_clear:N \l_@@_tmpa_prop \seq_gclear:N \g_@@_col_header_seq \prop_map_inline:Nn \g_@@_col_name_prop { \prop_put:Nnn \l_@@_tmpa_prop {##2} {##1} } \int_step_inline:nn {\g_@@_col_count_int} { \prop_get:NnNTF \l_@@_tmpa_prop {##1} \l_@@_tmpa_tl { \seq_gput_right:NV \g_@@_col_header_seq \l_@@_tmpa_tl } { \seq_gput_right:Nn \g_@@_col_header_seq {<##1>} } } } % \end{macrocode} % \end{macro} % % % \subsection{行列尺寸} % % % \subsubsection{边距设置} % % % \begin{macro}{\@@_set_row_sep:nn, \@@_set_col_sep:nn} % 设置列边距(内容到内容的距离)。|#1|:最左边与最右边的边距,|#2|:中间的边距。 % \begin{macrocode} \cs_new:Nn \@@_set_row_sep:nn { \seq_gclear:N \g_@@_row_sep_seq \prg_replicate:nn {\g_@@_row_count_int} { \seq_gput_right:Nn \g_@@_row_sep_seq {#2} } \seq_gput_left:Nn \g_@@_row_sep_seq {#1} \seq_gput_right:Nn \g_@@_row_sep_seq {#1} } \cs_new:Nn \@@_set_col_sep:nn { \seq_gclear:N \g_@@_col_sep_seq \prg_replicate:nn {\g_@@_col_count_int - 1} { \seq_gput_right:Nn \g_@@_col_sep_seq {#2} } \seq_gput_left:Nn \g_@@_col_sep_seq {#1} \seq_gput_right:Nn \g_@@_col_sep_seq {#1} } \cs_generate_variant:Nn \@@_set_row_sep:nn { ne, en, ee } \cs_generate_variant:Nn \@@_set_col_sep:nn { ne, en, ee } % \end{macrocode} % \end{macro} % % % \subsubsection{宽度计算} % % % \begin{macro}{\@@_measure_cell_wd:n} % 测量单元格内容(|#1|)的自然宽度,结果保存在 \cs{l_@@_wd_dim} 中。 % 顺便会更新首行的高度差\footnote{高度与 \cs{c_@@_ht_dim} 的差距值(为正), % 如果高度更大,则为零},结果保存在 \cs{l_@@_ht_dim} 中。 % \begin{macrocode} \cs_new:Nn \@@_measure_cell_wd:n { \dim_zero:N \l_@@_ht_dim \dim_set_eq:NN \l_@@_wd_dim \g_@@_cell_wd_min_dim \tl_if_empty:nF {#1} { \bool_set_true:N \l_@@_tmpa_bool \seq_set_split:Nnn \l_@@_tmpa_seq {\\} {#1} \seq_map_inline:Nn \l_@@_tmpa_seq { \hbox_set:Nn \l_@@_tmpa_box {##1} \@@_dim_set_max:Nn \l_@@_wd_dim { \box_wd:N \l_@@_tmpa_box } \bool_if:NT \l_@@_tmpa_bool { \@@_dim_set_max:Nn \l_@@_ht_dim { \c_@@_std_ht_dim - \box_ht:N \l_@@_tmpa_box } \bool_set_false:N \l_@@_tmpa_bool } } } } \cs_generate_variant:Nn \@@_measure_cell_wd:n { V, e } % \end{macrocode} % \end{macro} % % % \begin{macro}{\@@_measure_col_wd:n} % 测量表格的列(|#1|)的宽度,结果保存在 \cs{l_@@_wd_dim} 中。 % 同时会记录首行高度差(使用 \cs{l_@@_ht_dim} 变量中转)。 % \footnote{本命令需要确认 \cs{l_@@_tmpa_dim} 不会被调用的函数修改。} % \begin{macrocode} \cs_new:Nn \@@_measure_col_wd:n { \dim_zero:N \l_@@_tmpa_dim \bool_if:NT \g_@@_has_header_bool { \@@_measure_cell_wd:e { \seq_item:Nn \g_@@_col_header_seq {#1} } \dim_set_eq:NN \l_@@_tmpa_dim \l_@@_wd_dim \prop_gput:Nne \g_@@_cell_vfill_prop {0,#1} {\dim_use:N \l_@@_ht_dim} } \int_step_inline:nn {\g_@@_row_count_int} { \@@_get_cell:nnN {##1} {#1} \l_@@_tmpa_tl \@@_measure_cell_wd:V \l_@@_tmpa_tl \@@_dim_set_max:NN \l_@@_tmpa_dim \l_@@_wd_dim \prop_gput:Nne \g_@@_cell_vfill_prop {##1,#1} {\dim_use:N \l_@@_ht_dim} } \dim_set_eq:NN \l_@@_wd_dim \l_@@_tmpa_dim } % \end{macrocode} % \end{macro} % % % \begin{macro}{\@@_calc_col_wd:} % 计算表格各列的宽度,如果有需要调整的列宽,必须刷新出列边距,否则计算会失真。 % 本命令会大量修改通用变量的值,调用时需要注意。 % \begin{macrocode} \cs_new:Nn \@@_calc_col_wd: { \dim_zero:N \l_@@_cachea_dim \dim_zero:N \l_@@_cacheb_dim \seq_clear:N \l_@@_cachea_seq \seq_clear:N \l_@@_cacheb_seq \seq_gclear:N \g_@@_col_wd_seq \prop_gclear:N \g_@@_cell_vfill_prop \int_step_inline:nn {\g_@@_col_count_int} % 自然宽度循环 { \@@_measure_col_wd:n {##1} \@@_parse_col_wd_style:n {##1} \clist_if_in:nVTF {a,s,f,sf} \l_@@_style_tl { \seq_gput_right:Ne \g_@@_col_wd_seq { \dim_use:N \l_@@_wd_dim } \tl_if_eq:VnT \l_@@_style_tl {s} { \seq_put_right:Nn \l_@@_cachea_seq {##1} \@@_dim_set_max:NN \l_@@_cachea_dim \l_@@_wd_dim } \tl_if_eq:VnT \l_@@_style_tl {sf} { \seq_put_right:Nn \l_@@_cacheb_seq {##1} \@@_dim_set_max:NN \l_@@_cacheb_dim \l_@@_wd_dim } } { \seq_gput_right:NV \g_@@_col_wd_seq \l_@@_style_tl } } \seq_map_inline:Nn \l_@@_cachea_seq % 相同宽度循环 { \seq_gset_item:Nne \g_@@_col_wd_seq {##1} { \dim_use:N \l_@@_cachea_dim } } \seq_map_inline:Nn \l_@@_cacheb_seq { \seq_gset_item:Nne \g_@@_col_wd_seq {##1} { \dim_use:N \l_@@_cacheb_dim } } \int_zero:N \l_@@_cachea_int % 填充循环 \seq_clear:N \l_@@_cachea_seq \dim_set:Nn \l_@@_wd_dim { \seq_item:Nn \g_@@_col_wd_style_seq {-1} } \seq_map_inline:Nn \g_@@_col_sep_seq { \dim_sub:Nn \l_@@_wd_dim {##1} } \int_step_inline:nn {\g_@@_col_count_int} { \dim_sub:Nn \l_@@_wd_dim { \seq_item:Nn \g_@@_col_wd_seq {##1} } \@@_parse_col_wd_style:n {##1} \tl_if_in:NnT \l_@@_style_tl {f} { \int_incr:N \l_@@_cachea_int \seq_put_right:Nn \l_@@_cachea_seq {##1} } } \int_if_zero:nF { \l_@@_cachea_int } { \dim_set:Nn \l_@@_cachea_dim { \l_@@_wd_dim / \l_@@_cachea_int } } \seq_map_inline:Nn \l_@@_cachea_seq { \dim_set:Nn \l_@@_wd_dim { \seq_item:Nn \g_@@_col_wd_seq {##1} } \dim_add:Nn \l_@@_wd_dim { \l_@@_cachea_dim } \seq_gset_item:Nnn \g_@@_col_wd_seq {##1} { \dim_use:N \l_@@_wd_dim } } } % \end{macrocode} % \end{macro} % % % \subsubsection{高度计算} % % % % \begin{macro}{\@@_measure_cell_ht:n} % 测量单元格内容在限制宽度内的高度尺寸。|#1|: 垂直对齐方式,|#2|: 宽度,|#3| 内容。 % 结果保存在 \cs{l_@@_ht_dim} 和 \cs{l_@@_dp_dim} 中。 % \begin{macrocode} \cs_new:Nn \@@_measure_cell_ht:nnn { \dim_set_eq:NN \l_@@_ht_dim \c_@@_std_ht_dim \dim_set_eq:NN \l_@@_dp_dim \c_@@_std_dp_dim \tl_if_empty:nF {#3} { \str_if_eq:nnTF {#1} {t} { \vbox_set_top:Nn } { \vbox_set:Nn } \l_@@_tmpa_box { \baselineskip=\g_@@_cell_lineskip_dim \hsize=#2 \parindent=0pt \noindent #3 } \@@_dim_set_max:Nn \l_@@_ht_dim { \box_ht:N \l_@@_tmpa_box + \l_@@_fill_dim } \@@_dim_set_max:Nn \l_@@_dp_dim { \box_dp:N \l_@@_tmpa_box } } } \cs_generate_variant:Nn \@@_measure_cell_ht:nnn { VeV } % \end{macrocode} % \end{macro} % % % \begin{macro}{\@@_measure_row_ht:n} % 测量表格的行(|#1|)的高度,结果保存在 \cs{l_@@_ht_dim} 与 \cs{l_@@_dp_dim} 中, % 同时会缓存高度数据备用。 % 本函数会修改变量 \cs{l_@@_row_align_tl} 与 \cs{l_@@_fill_dim} 的值。 % \footnote{本命令需要确认 \cs{l_@@_tmpa_dim} 与 \cs{l_@@_tmpb_dim} 不会被调用的函数修改。} % \begin{macrocode} \cs_new:Nn \@@_measure_row_ht:n { \dim_zero:N \l_@@_tmpa_dim \dim_zero:N \l_@@_tmpb_dim \@@_parse_row_align:n {#1} \int_step_inline:nn {\g_@@_col_count_int} { \@@_parse_cell_fill:nn {#1} {##1} \@@_get_cell:nnN {#1} {##1} \l_@@_tmpa_tl \@@_measure_cell_ht:VeV \l_@@_row_align_tl { \seq_item:Nn \g_@@_col_wd_seq {##1} } \l_@@_tmpa_tl \prop_gput:Nne \g_@@_cell_ht_prop {#1,##1} { \dim_use:N \l_@@_ht_dim } \@@_dim_set_max:NN \l_@@_tmpa_dim \l_@@_ht_dim \@@_dim_set_max:NN \l_@@_tmpb_dim \l_@@_dp_dim } \dim_set_eq:NN \l_@@_ht_dim \l_@@_tmpa_dim \dim_set_eq:NN \l_@@_dp_dim \l_@@_tmpb_dim } % \end{macrocode} % \end{macro} % % % \begin{macro}{\@@_calc_row_ht:} % 计算表格各行的高度。本命令会大量修改通用变量的值,调用时需要注意。 % \begin{macrocode} \cs_new:Nn \@@_calc_row_ht: { \dim_zero:N \l_@@_cachea_dim \dim_zero:N \l_@@_cacheb_dim \seq_clear:N \l_@@_cachea_seq \seq_gclear:N \g_@@_row_ht_seq \seq_gclear:N \g_@@_row_ht_seq \prop_gclear:N \g_@@_cell_ht_prop \int_step_inline:nnn {0} {\g_@@_row_count_int} % 自然高度循环 { \@@_parse_row_ht_style:n {##1} \@@_measure_row_ht:n {##1} \clist_if_in:nVTF {a,s} \l_@@_style_tl { \seq_gput_right:Ne \g_@@_row_ht_seq { \dim_use:N \l_@@_ht_dim } \seq_gput_right:Ne \g_@@_row_dp_seq { \dim_use:N \l_@@_dp_dim } \tl_if_eq:VnT \l_@@_style_tl {s} { \seq_put_right:Nn \l_@@_cachea_seq {##1} \@@_dim_set_max:NN \l_@@_cachea_dim \l_@@_ht_dim \@@_dim_set_max:NN \l_@@_cacheb_dim \l_@@_dp_dim } } { \seq_gput_right:Ne \g_@@_row_ht_seq { \dim_eval:n { \l_@@_style_tl - \l_@@_dp_dim } } \seq_gput_right:Ne \g_@@_row_dp_seq { \dim_use:N \l_@@_dp_dim } } } \seq_map_inline:Nn \l_@@_cachea_seq % 相同高度循环 { \seq_gset_item:Nne \g_@@_row_ht_seq {##1+1} { \dim_use:N \l_@@_cachea_dim } \seq_gset_item:Nne \g_@@_row_dp_seq {##1+1} { \dim_use:N \l_@@_cacheb_dim } } } % \end{macrocode} % \end{macro} % % % \subsubsection{查询尺寸} % % \begin{macro}{\@@_parse_cell_final_size:nn} % 查询单元格的排版尺寸。|#1#2| 为行列数字坐标。 % 结果保存在 \cs{l_@@_wd_dim}、\cs{l_@@_ht_dim} 及 \cs{l_@@_dp_dim} 中。 % \begin{macrocode} \cs_new:Nn \@@_parse_cell_final_size:nn { \dim_set:Nn \l_@@_wd_dim { \seq_item:Nn \g_@@_col_wd_seq {#2} } \dim_set:Nn \l_@@_ht_dim { \seq_item:Nn \g_@@_row_ht_seq {#1+1} } \dim_set:Nn \l_@@_dp_dim { \seq_item:Nn \g_@@_row_dp_seq {#1+1} } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_parse_cell_fill:nn} % 查询单元格的首先高度填充值。|#1#2| 为行列数字坐标。 % 结果保存在 \cs{l_@@_fill_dim} 中。 % \begin{macrocode} \cs_new:Nn \@@_parse_cell_fill:nn { \prop_get:NnNF \g_@@_cell_vfill_prop {#1,#2} \l_@@_tmpa_tl { \tl_set:Nn \l_@@_tmpa_tl {0pt}} \dim_set:Nn \l_@@_fill_dim {\l_@@_tmpa_tl} } % \end{macrocode} % \end{macro} % % % \begin{macro}{\@@_parse_cell_ht:nn} % 查询单元格的实际高度。|#1#2| 为行列数字坐标。 % 结果保存在 \cs{l_@@_ht_dim} 中。 % \begin{macrocode} \cs_new:Nn \@@_parse_cell_ht:nn { \prop_get:NnN \g_@@_cell_ht_prop {#1,#2} \l_@@_tmpa_tl \dim_set:Nn \l_@@_ht_dim {\l_@@_tmpa_tl} } % \end{macrocode} % \end{macro} % % \subsubsection{刷新坐标} % % \begin{macro}{\@@_calc_coord:} % 计算表格的(边框线所处位置的)实际输出坐标,其受有无 |Header| 的影响。 % 本命令使用 \cs{l_@@_wd_dim} 与 \cs{l_@@_ht_dim} 变量。 % \begin{macrocode} \cs_new:Nn \@@_calc_coord: { \dim_zero:N \l_@@_wd_dim \seq_gclear:N \g_@@_col_loc_seq \seq_gput_right:NV \g_@@_col_loc_seq \l_@@_wd_dim \dim_add:Nn \l_@@_wd_dim { (\seq_item:Nn \g_@@_col_sep_seq {1})/2 } \int_step_inline:nn {\g_@@_col_count_int} { \dim_add:Nn \l_@@_wd_dim { (\seq_item:Nn \g_@@_col_sep_seq {##1})/2 } \dim_add:Nn \l_@@_wd_dim { \seq_item:Nn \g_@@_col_wd_seq {##1} } \dim_add:Nn \l_@@_wd_dim { (\seq_item:Nn \g_@@_col_sep_seq {##1+1})/2 } \seq_gput_right:NV \g_@@_col_loc_seq \l_@@_wd_dim } \dim_add:Nn \l_@@_wd_dim { (\seq_item:Nn \g_@@_col_sep_seq {-1})/2 } \seq_gset_item:NnV \g_@@_col_loc_seq {-1} \l_@@_wd_dim % \end{macrocode} % 为方便计算,$Y$ 坐标采用负坐标。 % \begin{macrocode} \dim_zero:N \l_@@_ht_dim \seq_gclear:N \g_@@_row_loc_seq \seq_gput_right:NV \g_@@_row_loc_seq \l_@@_ht_dim \bool_if:NTF \g_@@_has_header_bool { \int_set:Nn \l_@@_tmpa_int {0} } { \int_set:Nn \l_@@_tmpa_int {1} } \dim_sub:Nn \l_@@_ht_dim { \seq_item:Nn \g_@@_row_sep_seq {1} } \int_step_inline:nnn {\l_@@_tmpa_int} {\g_@@_row_count_int} { \int_compare:nNnF {##1} = {\l_@@_tmpa_int} { \dim_sub:Nn \l_@@_ht_dim { (\seq_item:Nn \g_@@_row_sep_seq {##1+1})/2 } } \dim_sub:Nn \l_@@_ht_dim { \seq_item:Nn \g_@@_row_ht_seq {##1+1} } \dim_sub:Nn \l_@@_ht_dim { \seq_item:Nn \g_@@_row_dp_seq {##1+1} } \dim_sub:Nn \l_@@_ht_dim { (\seq_item:Nn \g_@@_row_sep_seq {##1+2})/2 } \seq_gput_right:NV \g_@@_row_loc_seq \l_@@_ht_dim } \dim_sub:Nn \l_@@_ht_dim { (\seq_item:Nn \g_@@_row_sep_seq {-1})/2 } \seq_gset_item:NnV \g_@@_row_loc_seq {-1} \l_@@_ht_dim } % \end{macrocode} % \end{macro} % % % % \subsubsection{待删除} % % \begin{macro}{\@@_measure_cell_size:} % 测量单元格内容的尺寸。先计算自然宽度。\todo{移除此命令及并调整关联命令。} % \begin{macrocode} \cs_new:Nn \@@_measure_cell_size: { \seq_gclear:N \g_@@_col_wd_seq \int_step_inline:nn {\g_@@_col_count_int} { \dim_zero:N \l_@@_tmpa_dim \bool_if:NT \g_@@_has_header_bool { \@@_measure_cell_wd:e { \seq_item:Nn \g_@@_col_header_seq {##1} } \dim_set_eq:NN \l_@@_tmpa_dim \l_@@_wd_dim } \int_step_inline:nn {\g_@@_row_count_int} { \@@_get_cell:nnN {####1} {##1} \l_@@_tmpa_tl \@@_measure_cell_wd:V \l_@@_tmpa_tl \@@_dim_set_max:NN \l_@@_tmpa_dim \l_@@_wd_dim } \seq_gput_right:Ne \g_@@_col_wd_seq { \dim_use:N \l_@@_tmpa_dim } } % \end{macrocode} % 统计行高度。 % \begin{macrocode} \seq_gclear:N \g_@@_row_ht_seq \bool_if:NTF \g_@@_has_header_bool { \int_set:Nn \l_@@_tmpa_int {0} } { \int_set:Nn \l_@@_tmpa_int {1} } \int_step_inline:nnn {\l_@@_tmpa_int} {\g_@@_row_count_int} { \dim_zero:N \l_@@_tmpa_dim \int_step_inline:nn {\g_@@_col_count_int} { \@@_get_cell:nnN {##1} {####1} \l_@@_tmpa_tl \@@_measure_cell_ht:eV {\seq_item:Nn \g_@@_col_wd_seq {####1} } \l_@@_tmpa_tl \@@_dim_set_max:NN \l_@@_tmpa_dim \l_@@_ht_dim } \seq_gput_right:Ne \g_@@_row_ht_seq { \dim_use:N \l_@@_tmpa_dim } } } % \end{macrocode} % \end{macro} % % % % \section{输入接口} % % % \subsection{变量与选项} % % % \subsubsection{常量定义} % % % \begin{variable} {\c_@@_csv_row_regex, \c_@@_csv_cell_regex} % 用于解析 CSV 中的行的正则表达式。 % 其中单元格表达式每个匹配生成三个项目,它们分别是所有匹配,常规字段值,引号字段值。 % \begin{macrocode} \regex_const:Nn \c_@@_csv_row_regex { ((?:[^"\c{\\}]+|"(?:""|[^"])+")+?)(?:\c{\\}|$) } \regex_const:Nn \c_@@_csv_cell_regex { (?:([^"\c{\\},]+)|"((?:""|[^"])+)")(?:,|\c{\\}|$) } % \end{macrocode} % \end{variable} % % % \begin{variable} {\c_@@_json_cell_regex} % 用于解析 JSON 中的属性值的正则表达式。 % 每个匹配生成四个项目,它们分别是所有匹配,名称,非字符值(数值或真假),字符值。 % \begin{macrocode} \regex_const:Nn \c_@@_json_cell_regex { "((?:[^"\\]|\\.)+)" \s*:\s* (?: ([\+\-]?(?:\d*\.)?\d+|true|false) | "((?:[^"\\]|\\.)+)" ) \s*[,]? } % \end{macrocode} % \end{variable} % % % \begin{variable} {\c_@@_row_ht_regex, \c_@@_col_wd_regex} % 用于判定某项内容是否为行高样式/列宽样式。 % \begin{macrocode} \regex_const:Nn \c_@@_row_ht_regex { \A(?:.*=)?(?: (auto|same|a|s)| ([0-9.]+(?:pt|ex|em|mm|cm|in|cc|dd|pc)) )\Z } \regex_const:Nn \c_@@_col_wd_regex { \A(?:.*=)?(?: (auto|same|fill|samefill|a|s|f|sf)| ([0-9.]+(?:pt|ex|em|mm|cm|in|cc|dd|pc)) )\Z } % \end{macrocode} % \end{variable} % % % % \subsubsection{状态参数} % % \begin{variable} {\l_@@_input_bool} % 当前是否处于输入环境。 % \begin{macrocode} \bool_new:N \l_@@_input_bool \bool_set_false:N \l_@@_input_bool % \end{macrocode} % \end{variable} % % \begin{macro}{\@@_check_input_env:n} % \begin{macrocode} \cs_new:Nn \@@_check_input_env:n { \bool_if:NF \l_@@_input_bool { \msg_error:nnn {xtable} {outside_table} {#1} } } % \end{macrocode} % \end{macro} % % \begin{variable} {\l_@@_parse_status_int, \l_@@_escape_status_bool, \l_@@_quote_status_bool} % 用于保存解析过程中的状态。 % \begin{macrocode} \int_new:N \l_@@_parse_status_int \bool_new:N \l_@@_escape_status_bool \bool_new:N \l_@@_quote_status_bool % \end{macrocode} % \end{variable} % % % \subsubsection{局部变量} % % \begin{variable} {\l_@@_row_input_seq, \l_@@_cell_input_seq} % 用于保存原始的输入数据。 % \begin{macrocode} \seq_new:N \l_@@_row_input_seq \seq_new:N \l_@@_cell_input_seq % \end{macrocode} % \end{variable} % % % \subsubsection{局部选项} % % % \begin{variable}{\l_@@_input_format_tl, \l_@@_input_separator_tl, \l_@@_input_has_header_bool} % 输入选项变量。 % \begin{macrocode} \tl_new:N \l_@@_input_format_tl \tl_new:N \l_@@_input_separator_tl \bool_new:N \l_@@_input_has_header_bool % \end{macrocode} % \end{variable} % % % % \begin{macro}{xtable/input} % 输入选项。 % \begin{macrocode} \keys_define:nn { xtable / input } { format .choices:nn = { inner, csv, json } { \tl_set_eq:NN \l_@@_input_format_tl \l_keys_choice_tl }, format .initial:n = {inner}, title .bool_set:N = \l_@@_input_has_header_bool, header .bool_set:N = \l_@@_input_has_header_bool, header .initial:n = {true}, sep .tl_set:N = \l_@@_input_separator_tl, sep .initial:n = {,}, loc .code:n = {\@@_parse_coord:n {#1}}, loc .initial:n = {1,1} } % \end{macrocode} % \end{macro} % % % \subsection{数据解析} % % \subsubsection{标准格式} % % % \begin{macro}{\@@_parse_input:nnn} % 解析标准输入的表格数据(|#1|),并将其添加到指定位置。 % 起始行由 \cs{l_@@_row_loc_int} 指定;起始列由 \cs{l_@@_col_loc_int} 指定。 % \begin{macrocode} \cs_new:Nn \@@_parse_input:n { \seq_set_split:Nnn \l_@@_row_input_seq {\\} {#1} \int_set_eq:NN \l_@@_tmpa_int \l_@@_row_loc_int \seq_map_inline:Nn \l_@@_row_input_seq { \int_set_eq:NN \l_@@_tmpb_int \l_@@_col_loc_int \seq_set_split:NVn \l_@@_cell_input_seq \l_@@_input_separator_tl {##1} \bool_if:NTF \l_@@_input_has_header_bool { % 填充标题 \seq_map_inline:Nn \l_@@_cell_input_seq { \@@_set_col_name:nN {####1} \l_@@_tmpb_int \int_incr:N \l_@@_tmpb_int } \bool_set_false:N \l_@@_input_has_header_bool } { % 填充数据 \seq_map_inline:Nn \l_@@_cell_input_seq { \@@_set_cell:NNn \l_@@_tmpa_int \l_@@_tmpb_int {####1} \int_incr:N \l_@@_tmpb_int } \int_incr:N \l_@@_tmpa_int } } } % \end{macrocode} % \end{macro} % % % \subsubsection{CSV 数据} % % % 解析 CSV 数据使用的思路是,先用正则表达式解析出行,再用正则表达式解析每个字段。 % % % \begin{macro}{\@@_parse_csv:n} % 解析 CSV 数据(|#1|),并将其添加到指定位置。 % 起始行由 \cs{l_@@_row_loc_int} 指定;起始列由 \cs{l_@@_col_loc_int} 指定。 % \begin{macrocode} \cs_new:Nn \@@_parse_csv:n { \seq_clear:N \l_@@_row_input_seq \regex_extract_all:NnN \c_@@_csv_row_regex {#1} \l_@@_row_input_seq \int_set_eq:NN \l_@@_tmpa_int \l_@@_row_loc_int \seq_map_inline:Nn \l_@@_row_input_seq { \int_set_eq:NN \l_@@_tmpb_int \l_@@_col_loc_int \seq_clear:N \l_@@_cell_input_seq \regex_extract_all:NnN \c_@@_csv_cell_regex {##1} \l_@@_cell_input_seq \bool_if:NTF \l_@@_input_has_header_bool { % 填充标题 \seq_map_inline:Nn \l_@@_cell_input_seq { \@@_set_col_name:nN {####1} \l_@@_tmpb_int \int_incr:N \l_@@_tmpb_int } \bool_set_false:N \l_@@_input_has_header_bool } { % 填充数据 \seq_map_inline:Nn \l_@@_cell_input_seq { \@@_set_cell:NNn \l_@@_tmpa_int \l_@@_tmpb_int {####1} \int_incr:N \l_@@_tmpb_int } \int_incr:N \l_@@_tmpa_int } } } % \end{macrocode} % \end{macro} % % % % \subsubsection{JSON 数据} % % 本宏包解析 JSON 数据的思路:先将行依次解析到一个中转序列(逐字符处理) % 再依次解析每一行数据(正则表达式)。 % % 逐字符解析完整 JSON ,并将解析状态分为:初始模式、行间模式、行内模式。 % 整个解析过程都在这三个状态中切换。 % % \begin{macro}{\@@_parse_json_auxa:n} % 拆分行时用到的辅助函数,处理初始模式。 % 当前字符(|#1|)为 |[|,则表示数据开始,切换到行间模式; % 当前字符为 |{|,则表示一行数据开始,直接切换到行内模式。 % \begin{macrocode} \cs_new:Nn \@@_parse_json_auxa:n { \str_case_e:nnF {#1} { {\c_@@_space_str} {} % 空白,跳过 {[} % 数组开始 { \int_set:Nn \l_@@_parse_status_int {1} } {\c_@@_lbrace_str} % 行开始 { \int_set:Nn \l_@@_parse_status_int {2} \str_clear:N \l_@@_tmpa_str } } { \msg_error:nnn {xtable} {unknown_input} {#1} } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_parse_json_auxb:n} % 拆分行时用到的辅助函数,处理行间模式。 % 当前字符(|#1|)为 |]|,则表示数据结束,切换回初始模式; % 当前字符为 |{|,则表示一行数据开始,切换到行内模式。 % 忽略逗号与空白。 % \begin{macrocode} \cs_new:Nn \@@_parse_json_auxb:n { \str_case_e:nnF {#1} { {\c_@@_space_str} {} % 空白,跳过 {,} {} % 行分隔,跳过 {]} % 数组结束 { \int_set:Nn \l_@@_parse_status_int {0} } {\c_@@_lbrace_str} % 行开始 { \int_set:Nn \l_@@_parse_status_int {2} \str_clear:N \l_@@_tmpa_str } } { \msg_error:nnn {xtable} {unknown_input} {#1} } } % \end{macrocode} % \end{macro} % \begin{macro}{\@@_parse_json_auxc:n} % 拆分行时用到的辅助函数,处理行内模式。|#1| 为当前字符。 % 行内模式又有两个子状态:转义态与引用态。 % 如果当前状态为转义态,则恢复为非转义态;否则,可以用 |\| 切换为转义态。 % 如果当前状态为引用态(且非转义态),则可用 |"| 切换为非引用态; % 否则,可以使用 |"| 切换为引用态,或使用 |}| 结束当前行,并进入行间模式。 % \begin{macrocode} \cs_new:Nn \@@_parse_json_auxc:n { \bool_if:NTF \l_@@_escape_status_bool % 转义状态 { \bool_set_false:N \l_@@_escape_status_bool } { \str_if_eq:VnTF \c_@@_escape_str {#1} % 即将转义 { \bool_set_true:N \l_@@_escape_status_bool } { \bool_if:NTF \l_@@_quote_status_bool % 引用状态 { \str_if_eq:nnT {"} {#1} % 结束引用 { \bool_set_false:N \l_@@_quote_status_bool } } { \str_if_eq:nnTF {"} {#1} % 即将引用 { \bool_set_true:N \l_@@_quote_status_bool } { \str_if_eq:VnT \c_@@_rbrace_str {#1} % 行结束 { \int_set:Nn \l_@@_parse_status_int {1} \seq_put_right:NV \l_@@_row_input_seq \l_@@_tmpa_str } } } } } \str_put_right:Nn \l_@@_tmpa_str {#1} } % \end{macrocode} % \end{macro} % % % \begin{macro}{\@@_parse_json_row:n} % 解析 JSON 中的单行数据(|#1|),并将其添加表中。 % 使用 \cs{l_@@_row_loc_int} 与 \cs{l_@@_col_loc_int} 来传递下一位置的坐标。 % 使用缓存变量 \cs{l_@@_cachea_tl} 与 \cs{l_@@_cacheb_tl}。 % \begin{macrocode} \cs_new:Nn \@@_parse_json_row:n { \regex_extract_all:NnN \c_@@_json_cell_regex {#1} \l_@@_cell_input_seq \bool_until_do:nn { \seq_if_empty_p:N \l_@@_cell_input_seq } { \seq_pop_left:NN \l_@@_cell_input_seq \l_@@_tmpa_tl % 丢弃 \seq_pop_left:NN \l_@@_cell_input_seq \l_@@_cachea_tl \seq_pop_left:NN \l_@@_cell_input_seq \l_@@_tmpa_tl \seq_pop_left:NN \l_@@_cell_input_seq \l_@@_tmpb_tl \tl_if_empty:NTF \l_@@_tmpa_tl { \tl_set_eq:NN \l_@@_cacheb_tl \l_@@_tmpb_tl } { \tl_set_eq:NN \l_@@_cacheb_tl \l_@@_tmpa_tl } \@@_get_col_loc:VNF \l_@@_cachea_tl \l_@@_tmpa_int { \@@_set_col_name:VN \l_@@_cachea_tl \l_@@_col_loc_int \int_set_eq:NN \l_@@_tmpa_int \l_@@_col_loc_int \int_incr:N \l_@@_col_loc_int } \@@_set_cell:NNV \l_@@_row_loc_int \l_@@_tmpa_int \l_@@_cacheb_tl } \int_incr:N \l_@@_row_loc_int } % \end{macrocode} % \end{macro} % % % \begin{macro}{\@@_parse_json:nnn} % 解析 JSON 数据(|#1|),并将其添加到指定位置。 % 起始行由 \cs{l_@@_row_loc_int} 指定;列位置则由列名指定。 % 如果列名不存在,先依次将列名映射到指定位置(\cs{l_@@_col_loc_int} )及其后。 % \begin{macrocode} \cs_new:Nn \@@_parse_json:n { \int_zero:N \l_@@_parse_status_int \bool_set_false:N \l_@@_escape_status_bool \bool_set_false:N \l_@@_qute_status_bool \str_clear:N \l_@@_tmpa_str \seq_clear:N \l_@@_row_input_seq \str_map_inline:nn {#1} % 解析 JSON 中的行 { \int_case:nn { \l_@@_parse_status_int } { {0} { \@@_parse_json_auxa:n {##1} } % 开始 {1} { \@@_parse_json_auxb:n {##1} } % 行间状态 {2} { \@@_parse_json_auxc:n {##1} } % 行内状态 } } \seq_map_function:NN \l_@@_row_input_seq \@@_parse_json_row:n } \cs_generate_variant:Nn \@@_parse_json:nnn {een} % \end{macrocode} % \end{macro} % % \subsection{数据录入} % % % \begin{macro}{\loadtable, \savetable} % 保存与加载表格。 % \begin{macrocode} \NewDocumentCommand {\loadtable} { m } { \@@_check_input_env:n {\loadtable} \@@_table_restore:n {#1} } \NewDocumentCommand {\savetable} { m } { \@@_check_input_env:n {\savetable} \@@_table_save:n {#1} } % \end{macrocode} % \end{macro} % % \begin{macro}{\excelcolname} % 设置 Excel 风格列名。 % \begin{macrocode} \NewDocumentCommand {\excelcolname} { O{26} } { \@@_check_input_env:n {\excelcolname} \@@_set_excel_col_names:n {#1} } % \end{macrocode} % \end{macro} % % % \begin{macro}{\cell, \@@_cell:} % 用于输入单元格的数据。 % \begin{macrocode} \NewDocumentCommand {\@@_cell:} { r() m} { \@@_parse_coord:n {#1} \@@_set_cell:n {#2} } % \end{macrocode} % \end{macro} % % % \begin{environment}{data, @@_data:} % 用于输入表格内容的环境。 % \begin{macrocode} \NewDocumentEnvironment{@@_data:}{O{} +b} { \keys_set:nn { xtable/input } {#1} \str_case:VnF \l_@@_input_format_tl { {inner} { \@@_parse_input:n {#2} } {csv} { \@@_parse_csv:n {#2} } {json} { \@@_parse_json:n {#2} } } { \msg_error:nnV {xtable} {unknown_format} \l_@@_input_format_tl } } {} % \end{macrocode} % \end{environment} % % % \begin{environment}{xtable} % 用于输入表格的环境。 % \begin{macrocode} \NewDocumentEnvironment{xtable}{ O{} } { \@@_table_init: \keys_set:nn { xtable/input } {#1} \bool_gset_eq:NN \g_@@_has_header_bool \l_@@_input_has_header_bool \cs_set_eq:NN \data \@@_data: \cs_set_eq:NN \enddata \end_@@_data: \cs_set_eq:NN \cell \@@_cell: \bool_set_true:N \l_@@_input_bool } { \bool_set_false:N \l_@@_input_bool \@@_insure_style: \@@_process_header: } % \end{macrocode} % \end{environment} % % % \subsection{格式设置} % % % \begin{macro}{\rowheight, \colwidth} % 设置行高与列宽。 % \begin{macrocode} \NewDocumentCommand {\rowheight} { O{auto} m } { \@@_check_input_env:n {\rowheight} \tl_if_empty:nTF {#2} { \seq_clear:N \l_@@_shared_seq } { \seq_set_split:Nnn \l_@@_shared_seq {,} {#2} } \regex_extract_all:NnNF \c_@@_row_ht_regex {#1} \l_@@_tmpa_seq { \msg_error:nnn {xtable} {unknown_format} {#1} } \seq_map_inline:Nn \l_@@_shared_seq { \regex_extract_all:NnNF \c_@@_row_ht_regex {##1} \l_@@_tmpa_seq { \msg_error:nnn {xtable} {unknown_format} {##1} } } \@@_set_row_ht_style:Nn \l_@@_shared_seq {#1} } % \end{macrocode} % \begin{macrocode} \NewDocumentCommand {\colwidth} { O{auto} O{\textwidth} m } { \@@_check_input_env:n {\colwidth} \tl_if_empty:nTF {#3} { \seq_clear:N \l_@@_shared_seq } { \seq_set_split:Nnn \l_@@_shared_seq {,} {#3} } \regex_extract_all:NnNF \c_@@_col_wd_regex {#1} \l_@@_tmpa_seq { \msg_error:nnn {xtable} {unknown_format} {#1} } \seq_map_inline:Nn \l_@@_shared_seq { \regex_extract_all:NnNF \c_@@_col_wd_regex {##1} \l_@@_tmpa_seq { \msg_error:nnn {xtable} {unknown_format} {##1} } } \@@_set_col_wd_style:Nnn \l_@@_shared_seq {#1} {#2} } % \end{macrocode} % \end{macro} % % % \begin{macro}{\rowalign, \colalign} % 设置行与列的对齐方式。 % \begin{macrocode} \NewDocumentCommand {\rowalign} { O{b} m } { \@@_check_input_env:n {\rowalign} \regex_extract_all:nnNF {^(?:(?:.*=)?[tmb],?)*$} {#1#2} \l_@@_tmpa_seq { \msg_error:nnn {xtable} {unknown_format} {[#1],{#2}} } \str_if_in:nnTF {#2} {=} { \seq_set_split:Nnn \l_@@_shared_seq {,} {#2} } { \tl_set:Nn \l_@@_tmpa_tl {#2} \tl_replace_all:Nnn \l_@@_tmpa_tl {,} {} \seq_set_split:NnV \l_@@_shared_seq {} \l_@@_tmpa_tl } \@@_set_row_align:Nn \l_@@_shared_seq {#1} } % \end{macrocode} % \begin{macrocode} \NewDocumentCommand {\colalign} { O{c} m } { \@@_check_input_env:n {\colalign} \regex_extract_all:nnNF {^(?:(?:.*=)?[lcr],?)*$} {#1#2} \l_@@_tmpa_seq { \msg_error:nnn {xtable} {unknown_format} {[#1],{#2}} } \str_if_in:nnTF {#2} {=} { \seq_set_split:Nnn \l_@@_shared_seq {,} {#2} } { \tl_set:Nn \l_@@_tmpa_tl {#2} \tl_replace_all:Nnn \l_@@_tmpa_tl {,} {} \seq_set_split:NnV \l_@@_shared_seq {} \l_@@_tmpa_tl } \@@_set_col_align:Nn \l_@@_shared_seq {#1} } % \end{macrocode} % \end{macro} % % % % \section{渲染输出} % % % \subsection{通用内容} % % \subsubsection{局部变量} % % % \begin{variable}{\l_@@_expand_start_dim, \l_@@_expand_end_dim} % 两个偏移值变量。 % \begin{macrocode} \dim_new:N \l_@@_expand_start_dim \dim_new:N \l_@@_expand_end_dim % \end{macrocode} % \end{variable} % % % % \subsubsection{渲染函数} % % \begin{macro}{\@@_draw_hline:n} % 绘制表格指定的水平线。使用 \cs{l_@@_expand_start_dim} 与 % \cs{l_@@_expand_end_dim} 扩展边界。 % \begin{macrocode} \cs_new:Nn \@@_draw_hline:n { \draw_path_moveto:n { \seq_item:Nn \g_@@_col_loc_seq {1} - \l_@@_expand_start_dim, \seq_item:Nn \g_@@_row_loc_seq {#1} } \draw_path_lineto:n { \seq_item:Nn \g_@@_col_loc_seq {-1} + \l_@@_expand_end_dim, \seq_item:Nn \g_@@_row_loc_seq {#1} } } % \end{macrocode} % \end{macro} % % % \begin{macro}{\@@_draw_vline:n} % 绘制表格指定的垂直线。使用 \cs{l_@@_expand_start_dim} 与 % \cs{l_@@_expand_end_dim} 扩展边界。 % \begin{macrocode} \cs_new:Nn \@@_draw_vline:n { \draw_path_moveto:n { \seq_item:Nn \g_@@_col_loc_seq {#1}, \seq_item:Nn \g_@@_row_loc_seq {1} + \l_@@_expand_start_dim } \draw_path_lineto:n { \seq_item:Nn \g_@@_col_loc_seq {#1}, \seq_item:Nn \g_@@_row_loc_seq {-1} - \l_@@_expand_end_dim } } % \end{macrocode} % \end{macro} % % % % \subsection{打印表格} % % \begin{macro}{\@@_print_data:nnn} % 使用垂直盒子打印输出指定数据。|#1|: 垂直对齐方式, |#2|: 水平对齐方式,|#3|: 内容。 % \begin{macrocode} \cs_new:Nn \@@_print_data:nnn { \str_if_in:nnTF{tmb}{#1} { \tl_set:Nn \l_@@_tmpb_tl {#1} } { \tl_set:Nn \l_@@_tmpb_tl {b} } \str_case:nnF{#2} { {l} { \tl_set:Nn \l_@@_tmpa_tl {\raggedright} } {c} { \tl_set:Nn \l_@@_tmpa_tl {\centering} } {r} { \tl_set:Nn \l_@@_tmpa_tl {\raggedleft} } } { \tl_set:Nn \l_@@_tmpa_tl {} } \tl_if_empty:nT {#3} { \tl_put_right:Nn \l_@@_tmpa_tl {\rule{1pt}{0pt}} } \str_case:Vn \l_@@_tmpb_tl { {t} { \vbox_set_top:Nn \l_@@_tmpa_box { \baselineskip=\g_@@_cell_lineskip_dim \hsize=\l_@@_wd_dim \parindent=0pt \l_@@_tmpa_tl #3 \par } } % \end{macrocode} % 如果是居中对齐,则额外使用 \cs{l_@@_cachea_dim} 与 \cs{l_@@_fill_dim} % 传递单元格高度及填充量。 % \begin{macrocode} {m} { %<*backup> \vbox_set:Nn \l_@@_tmpa_box { \baselineskip=\g_@@_cell_lineskip_dim \hsize=\l_@@_wd_dim \parindent=0pt \l_@@_tmpa_tl #3 \par } \dim_set:Nn \l_@@_tmpb_dim {\box_ht:N \l_@@_tmpa_box} \dim_compare:nNnTF {\c_@@_std_ht_dim} > {\l_@@_tmpb_dim} { \dim_set:Nn \l_@@_tmpa_dim { (\l_@@_ht_dim + \c_@@_std_ht_dim)/2 - \l_@@_tmpb_dim } } { \dim_set:Nn \l_@@_tmpa_dim {(\l_@@_ht_dim - \l_@@_tmpb_dim)/2} } % \dim_set:Nn \l_@@_tmpa_dim { (\l_@@_ht_dim - \l_@@_cachea_dim)/2 + \l_@@_fill_dim } \vbox_set_to_ht:Nnn \l_@@_tmpa_box {\l_@@_ht_dim} { \baselineskip=\g_@@_cell_lineskip_dim \hsize=\l_@@_wd_dim \parindent=0pt \vskip \l_@@_tmpa_dim \l_@@_tmpa_tl #3 \vfill \par } } {b} { \vbox_set:Nn \l_@@_tmpa_box { \baselineskip=\g_@@_cell_lineskip_dim \hsize=\l_@@_wd_dim \parindent=0pt \l_@@_tmpa_tl #3 \par } } } \dim_compare:nNnT {\box_dp:N \l_@@_tmpa_box} < {\l_@@_dp_dim} { \box_set_dp:Nn \l_@@_tmpa_box {\l_@@_dp_dim} } \box_use:N \l_@@_tmpa_box } \cs_generate_variant:Nn \@@_print_data:nnn {VVV} % \end{macrocode} % \end{macro} % % % \begin{macro}{\printtable} % 打印表格。 % \begin{macrocode} \NewDocumentCommand \printtable { s O{0em} } { \@@_set_col_sep:ne {0pt} { \dim_use:N \g_@@_col_sep_dim } \@@_calc_col_wd: \@@_calc_row_ht: \bool_if:NTF \g_@@_has_header_bool { \int_set:Nn \l_@@_cachea_int {1} } { \int_set:Nn \l_@@_cachea_int {0} } \int_step_inline:nnn {1-\l_@@_cachea_int} {\g_@@_row_count_int} { \hbox:n { \dim_zero:N \l_@@_tmpa_dim \int_step_inline:nn {\g_@@_col_count_int} { \int_compare:nNnTF {####1} = {1} { \hspace{#2} } { \hspace{\g_@@_col_sep_dim} } \@@_parse_cell_fill:nn {##1}{####1} \@@_parse_cell_ht:nn {##1} {####1} \dim_set_eq:NN \l_@@_cachea_dim \l_@@_ht_dim \@@_parse_cell:nn {##1} {####1} \@@_parse_cell_final_size:nn {##1} {####1} \@@_parse_row_align:n {##1} \@@_parse_col_align:n {####1} \@@_print_data:VVV \l_@@_row_align_tl \l_@@_col_align_tl \l_@@_data_tl } } \IfBooleanF {#1} { \int_compare:nNnT {##1} < {\g_@@_row_count_int} {\\} } } } % \end{macrocode} % \end{macro} % % % \subsection{渲染表格} % % % \subsubsection{渲染内容} % % % \begin{macro}{\@@_render_data:nnn} % 使用垂直盒子渲染指定数据。|#1|: 垂直对齐方式, |#2|: 水平对齐方式,|#3|: 内容。 % 渲染好的盒子存储在 \cs{l_@@_cell_box} 中。 % \begin{macrocode} \cs_new:Nn \@@_render_data:nnn { \str_case:nnF{#2} { {l} { \tl_set:Nn \l_@@_tmpa_tl {\raggedright} } {c} { \tl_set:Nn \l_@@_tmpa_tl {\centering} } {r} { \tl_set:Nn \l_@@_tmpa_tl {\raggedleft} } } { \tl_set:Nn \l_@@_tmpa_tl {} } \tl_if_empty:nTF {#3} { \tl_set:Nn \l_@@_tmpb_tl {\rule{1pt}{0pt}} } { \tl_set:Nn \l_@@_tmpb_tl {#3} } \str_if_eq:nnTF {#1} {t} { \vbox_set_top:Nn } { \vbox_set:Nn } \l_@@_cell_box { \baselineskip=3ex \hsize=\l_@@_wd_dim \parindent=0pt \l_@@_tmpa_tl \l_@@_tmpb_tl \par } } \cs_generate_variant:Nn \@@_render_data:nnn {VVV} % \end{macrocode} % \end{macro} % % % \begin{macro}{\@@_draw_cell:nn} % 绘制指定单元格,使用 \cs{l_@@_x_dim} 与 \cs{l_@@_y_dim} 的坐标信息。 % \begin{macrocode} \cs_new:Nn \@@_draw_cell:nn { \@@_parse_cell_fill:nn {#1}{#2} \@@_parse_cell:nn {#1} {#2} \@@_parse_cell_final_size:nn {#1} {#2} \@@_parse_row_align:n {#1} \@@_parse_col_align:n {#2} \@@_render_data:VVV \l_@@_row_align_tl \l_@@_col_align_tl \l_@@_data_tl \tl_if_eq:NnTF \l_@@_row_align_tl {m} { \dim_set:Nn \l_@@_tmpb_dim { \box_ht:N \l_@@_cell_box + \l_@@_fill_dim } \dim_set:Nn \l_@@_tmpa_dim { \l_@@_y_dim - \l_@@_tmpb_dim } \dim_sub:Nn \l_@@_tmpa_dim { (\l_@@_ht_dim - \l_@@_tmpb_dim) / 2 } } { \dim_set:Nn \l_@@_tmpa_dim { \l_@@_y_dim - \l_@@_ht_dim } } \draw_box_use:Nn \l_@@_cell_box {\l_@@_x_dim, \l_@@_tmpa_dim } } % \end{macrocode} % \end{macro} % % % \begin{macro}{\@@_draw_cells:} % 绘制所有单元格,使用 \cs{l_@@_x_dim} 与 \cs{l_@@_y_dim} 的坐标信息。 % \begin{macrocode} \cs_new:Nn \@@_draw_cells: { \dim_set:Nn \l_@@_y_dim {-\c_@@_std_dp_dim / 3} % 补偿 \bool_if:NTF \g_@@_has_header_bool { \int_set:Nn \l_@@_tmpa_int {0} } { \int_set:Nn \l_@@_tmpa_int {1} \dim_sub:Nn \l_@@_y_dim { \seq_item:Nn \g_@@_row_sep_seq {1} } \dim_add:Nn \l_@@_y_dim { \seq_item:Nn \g_@@_row_sep_seq {2} } } \int_step_inline:nnn {\l_@@_tmpa_int} {\g_@@_row_count_int} { \dim_sub:Nn \l_@@_y_dim { \seq_item:Nn \g_@@_row_sep_seq {##1+1} } \dim_zero:N \l_@@_x_dim \int_step_inline:nn {\g_@@_col_count_int} { \dim_add:Nn \l_@@_x_dim { \seq_item:Nn \g_@@_col_sep_seq {####1} } \@@_draw_cell:nn {##1} {####1} \dim_add:Nn \l_@@_x_dim { \l_@@_wd_dim } } \dim_sub:Nn \l_@@_y_dim { \l_@@_ht_dim + \l_@@_dp_dim } } } % \end{macrocode} % \end{macro} % % % \subsubsection{渲染边框-三线表} % % % \begin{macro}{\@@_draw_booktabs:n} % 绘制三线表的边框线。 % \begin{macrocode} \cs_new:Nn \@@_draw_booktabs:n { \dim_zero:N \l_@@_expand_start_dim \dim_zero:N \l_@@_expand_end_dim \@@_set_line_style:n {toprule} \@@_draw_hline:n {1} \draw_path_use_clear:n { stroke } \@@_set_line_style:n {midrule} \@@_draw_hline:n {2} \draw_path_use_clear:n { stroke } \@@_set_line_style:n {bottomrule} \@@_draw_hline:n {-1} \draw_path_use_clear:n { stroke } \@@_set_line_style:n {cmidrule} \clist_map_inline:nn {#1} { \@@_draw_hline:n {##1+2} } \draw_path_use_clear:n { stroke } \@@_rendertable_post: } % \end{macrocode} % \end{macro} % % % \subsubsection{渲染边框-网格线} % % % \begin{macro}{\@@_row_line:nn} % 设置网格线风格的行网格线。|#1| 为默认值,|#2| 为线列表。 % \begin{macrocode} \NewDocumentCommand {\@@_row_line:nn} { O{} m } { \tl_set:Nn \l_@@_style_tl {#1} \tl_if_empty:nTF {#2} { \seq_clear:N \l_@@_shared_seq } { \seq_set_split:Nnn \l_@@_shared_seq {,} {#2} \seq_get_left:NN \l_@@_shared_seq \l_@@_tmpa_tl \str_if_in:NnF \l_@@_tmpa_tl {=} { \seq_pop_left:NN \l_@@_shared_seq \l_@@_style_tl } } \@@_set_row_style:NNn \g_@@_row_border_seq \l_@@_shared_seq {#1} \bool_if:NF \g_@@_has_header_bool { \seq_gpop_left:NN \g_@@_row_border_seq \l_@@_tmpa_tl } \seq_gput_left:NV \g_@@_row_border_seq \l_@@_style_tl } % \end{macrocode} % \end{macro} % % % \begin{macro}{\@@_col_line:nn} % 设置网格线风格的列网格线。|#1| 为默认值,|#2| 为线列表。 % \begin{macrocode} \NewDocumentCommand {\@@_col_line:nn} { O{} m } { \tl_set:Nn \l_@@_style_tl {#1} \tl_if_empty:nTF {#2} { \seq_clear:N \l_@@_shared_seq } { \seq_set_split:Nnn \l_@@_shared_seq {,} {#2} \seq_get_left:NN \l_@@_shared_seq \l_@@_tmpa_tl \str_if_in:NnF \l_@@_tmpa_tl {=} { \seq_pop_left:NN \l_@@_shared_seq \l_@@_style_tl } } \@@_set_col_style:NNn \g_@@_col_border_seq \l_@@_shared_seq {#1} \seq_gput_left:NV \g_@@_col_border_seq \l_@@_style_tl } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_calc_grid_expand:N} % 计算网格线的扩展长度\footnote{用于平衡线宽造成的转角过渡问题。}。 % \begin{macrocode} \cs_new:Nn \@@_calc_grid_expand:N { \seq_if_empty:NTF #1 { \seq_set_split:Nnn \l_@@_tmpa_seq {,} {0pt} } { \seq_set_eq:NN \l_@@_tmpa_seq #1 } \seq_get_left:NN \l_@@_tmpa_seq \l_@@_style_tl \tl_if_empty:NTF \l_@@_style_tl { \dim_zero:N \l_@@_expand_start_dim } { \@@_parse_line_style:V \l_@@_style_tl \dim_set:Nn \l_@@_expand_start_dim { \l_@@_line_dim/2 } } \seq_get_right:NN \l_@@_tmpa_seq \l_@@_style_tl \tl_if_empty:NTF \l_@@_style_tl { \dim_zero:N \l_@@_expand_end_dim } { \@@_parse_line_style:V \l_@@_style_tl \dim_set:Nn \l_@@_expand_end_dim { \l_@@_line_dim/2 } } } % \end{macrocode} % \end{macro} % % % \begin{macro}{\@@_draw_grid:nn} % 绘制网格线型表格。 % \begin{macrocode} \cs_new:Nn \@@_draw_grid:nn { \group_begin: \cs_set_eq:NN \rowborder \@@_row_line:nn \cs_set_eq:NN \colborder \@@_col_line:nn #2 \group_end: \@@_calc_grid_expand:N \g_@@_row_border_seq \int_step_inline:nn {\seq_count:N \g_@@_col_loc_seq} { \tl_set:Ne \l_@@_style_tl { \seq_item:Nn \g_@@_col_border_seq {##1} } \tl_if_empty:NF \l_@@_style_tl { \@@_set_line_style:V \l_@@_style_tl \@@_draw_vline:n {##1} \draw_path_use_clear:n { stroke } } } \@@_calc_grid_expand:N \g_@@_col_border_seq \int_step_inline:nn {\seq_count:N \g_@@_row_loc_seq} { \tl_set:Ne \l_@@_style_tl { \seq_item:Nn \g_@@_row_border_seq {##1} } \tl_if_empty:NF \l_@@_style_tl { \@@_set_line_style:V \l_@@_style_tl \@@_draw_hline:n {##1} \draw_path_use_clear:n { stroke } } } \@@_rendertable_post: } % \end{macrocode} % \end{macro} % % % \subsubsection{渲染边框-框架} % % \begin{macro}{\@@_draw_cell_border:nn} % 绘制指定单元格边框。 % \begin{macrocode} \cs_new:Nn \@@_draw_cell_border:nn { \@@_parse_cell_final_size:nn {#1} {#2} \dim_set:Nn \l_@@_tmpa_dim {\l_@@_wd_dim + \g_@@_col_margin_dim * 2} \dim_set:Nn \l_@@_tmpb_dim {\l_@@_ht_dim + \l_@@_dp_dim + \g_@@_row_margin_dim * 2} \draw_path_rectangle:nn {\l_@@_x_dim, \l_@@_y_dim - \l_@@_tmpb_dim } {\l_@@_tmpa_dim, \l_@@_tmpb_dim } \draw_path_use_clear:n { draw } } % \end{macrocode} % \end{macro} % % % \begin{macro}{\@@_draw_cell_border:} % 绘制单元格边框。 % \begin{macrocode} \cs_new:Nn \@@_draw_cell_borders: { \dim_zero:N \l_@@_y_dim \int_step_inline:nnn {0} {\g_@@_row_count_int} { \dim_zero:N \l_@@_x_dim \int_step_inline:nn {\g_@@_col_count_int} { \@@_draw_cell_border:nn {##1} {####1} \dim_set:Nn \l_@@_x_dim { \l_@@_x_dim + \l_@@_wd_dim + \g_@@_col_margin_dim * 2 } } \dim_set:Nn \l_@@_y_dim { \l_@@_y_dim - \l_@@_ht_dim - \l_@@_dp_dim - \g_@@_row_margin_dim * 2 } } \@@_rendertable_post: } % \end{macrocode} % \end{macro} % % % \subsubsection{渲染表格} % % \begin{macro}{\@@_set_table_sep:n} % 处理表格,计算各类表格尺寸数据。 % \begin{macrocode} \cs_new:Nn \@@_set_table_sep:n { \@@_set_row_sep:ee { \dim_use:N \g_@@_row_margin_dim } { \dim_eval:n { \g_@@_row_margin_dim * 2 } } \@@_set_col_sep:ee { \bool_if:nTF {#1} { \dim_use:N \g_@@_col_margin_dim } { 0pt } } { \dim_eval:n { \g_@@_col_margin_dim * 2 } } } % \end{macrocode} % \end{macro} % % % \begin{macro}{\@@_rendertable_prepare:, \@@_rendertable_post:} % 渲染表格的准备函数与善后的函数。 % 其中,准备函数由 \cs{rendertable} 直接调用,而善后函数则由所有绘制表格的函数调用。 % \begin{macrocode} \cs_new:Nn \@@_rendertable_prepare: { \@@_set_table_sep:n {\c_true_bool} \@@_calc_col_wd: \@@_calc_row_ht: \@@_calc_coord: \draw_begin: \vbox_set_to_ht:Nnn \l_@@_tmpa_box {\g_@@_above_space_dim} {} \draw_box_use:N \l_@@_tmpa_box } \cs_new:Nn \@@_rendertable_post: { \@@_draw_cells: \draw_end: } % \end{macrocode} % \end{macro} % % % \begin{macro}{\rendertable} % 绘制表格。 % \begin{macrocode} \NewDocumentCommand \rendertable { O{} O{} } { \@@_rendertable_prepare: \str_case:nnF {#1} { {booktabs} { \@@_draw_booktabs:n {#2} } {grid} { \@@_draw_grid:nn {#2} } } { \@@_draw_cell_borders: } } % \end{macrocode} % \end{macro} % % % \begin{macrocode} % % \end{macrocode} % % % \end{implementation} % % \PrintChanges % \PrintIndex % \todos % \endinput