\documentclass{ctexart}
\usepackage{tabular2}
\usepackage{paralist}
\usepackage[colorlinks, linkcolor=blue]{hyperref}
\usepackage{todo}

\ExplSyntaxOn
\cs_new:Npn \ttt #1
  { 
    \str_set:Ne \l_tmpa_str { \tl_to_str:n {#1} }     % 将 #1 转换为字符串
    \regex_replace_once:nnN { \s+\Z } {} \l_tmpa_str  % 去掉字符串末尾的空白字符
    \exp_args:NV \texttt \l_tmpa_str                  % 输出内容
  }
\ExplSyntaxOff
\begin{document}

\title{tabular2 宏包示例文档}
\author{Ms\_yam}
\date{\today}
\maketitle

\begin{abstract}
  本文档是 tabular2 宏包文档的扩展文档，用于展示 tabular2 的一些基本功能。
  同时，本文档也是 tabular2 的功能测试文档。
  文档本身只作功能介绍及效果展示，并未直接展示生成表格的相关代码，
  需要用户自行查看原代码\footnote{现在编辑器和 PDF 阅读器均支持反向查看，要找对应的源代码几乎无难度。}。
  查看原代码之前，应当先阅读 tabular2 宏包文档的用户部分。
\end{abstract}

{\small \tableofcontents}

\section{行列名设置}

\subsection{行与列的名称}

为了便于定位，本宏包的中表格的行与列均可以设置名称。
任意一行或一列可以有多个名称，但一个行名或列名则只能映射到唯一的一行或一列；
然而这种映射却是动态的，如某个行名可以在某个位置指向第 $1$ 行，而在另外一个位置指向第 $2$ 行。

命令 \ttt{\rowname} 与 \ttt{\colname} 专用用来指定行名与列名，它们会自动扩展表格的大小（行数或列数）。
如果您习惯了 \ttt{Excel} 的 \ttt{A1} 样式，则可以通过 \ttt{\excelcolname} 命令来初始化列名
\footnote{它不会影响表格的大小。}。

虽然我们可以使用纯数字\footnote{在内部实现中，所有名称都按凭据表处理（包括纯数字）。}来设置行名与列名，
但这样的名称不能用于定位\footnote{在定位时，会优先尝试将坐标解释为数字坐标，失败后才会按名称查找映射表。}。
因此，除非要设置表头，否则建议使用字母来设置行名与列名。

\subsection{表头与行列名}

表格有行表头与列表头；默认以行名为行表头；以列名为列表头。
如果某一个行设置了多个行名，则以最后一个行名为准；列表头类似。

当显示指定行表头或列表头时，则不受行列名的影响。

默认情况下，只输出列表头，不输出行表头。
表头是否输出则可通过相关设置来调整。\todo{更新如何设置表头是否输出；}

\subsection{示例}

由于宏包内部使用两种完全不同的输出方式，为保证测试的覆盖程度，
本文档中的所有输出示例，默认均以全边框形式及三线表形式两种方式输出。

以下是第一个示例，用于观察行名与列名设置效果：（包含行列表头）

\begin{xtable}[rowheader=true]
  \excelcolname
  \colname (3) { AA, BB, CC }
  \colname {5}
  \rowname [|] { 2 | A }
  \cell  (2,5) { data }
\end{xtable}
\rendertable [border]   \rule{2em}{0pt} 
\rendertable [booktabs] \par

说明：
\begin{compactitem}
  \item 此时行名的映射为：\ttt{<2>} = $1$（无效），\ttt{<A>} = $2$；
        如果这时输入行坐标 $2$，它会识别为第 $2$ 行而非第 $1$ 行；
  \item 对于列名同理，虽然列名 \ttt{<5>} = $1$，但因其不生效，所以最终修改的是第 $5$ 列；
  \item 行列名中所有字符均会被当作字符串处理，如 \ttt{\footnote} 等；
  \item 对于表头来说，以上设置却是有生效的；\footnote{脚注等命令，需要通过显示设置行列表头才生效。}
\end{compactitem}


\section{数据录入环境}

\ttt{data} 环境是主要的数据输入环境。目前该环境支持标准输入、CSV输入及JSON输入三种输入方式。
同时，CSV与JSON还可以考虑支持文件输入。\todo{解决文件输入；}
\subsection{标准输入}

标准输入是本宏包设计的主要输入方式。
该输入方式采用硬解码，处理速度有较明显优势，且直接支持行列表头输入，应当作为常规表格的首选输入方式。

\subsubsection{默认效果}

以下是全部使用默认值（逗号分隔，\ttt{\\} 换行）的标准输入的效果：\par

\begin{xtable}
  \begin{data}
    Name, Sex,    Age\footnote{这是一条演示用的脚注。}\\
    John, man,    18\\  
    Leon,    ,    20\\
    \\
    Lily, woman,  21\\  
    ,\\
  \end{data}
\end{xtable}
\rendertable [border]   \rule{2em}{0pt} 
\rendertable [booktabs] \par

其中：首行为表头（兼列名），中间的行会保留，尾随的空行不会。
如果某行在某列之后无需设置，则允许省略该行的后续分隔符。

\subsubsection{特殊字符}

标准输入不对特殊字符进行任何预处理，所有输入均为凭据表，直接由 \TeX 的默认方式处理。
但由于 “\ttt{\\}” 用于分隔表格行，因此单元格换行需要使用 “\ttt{\newline}”。
如果单元格本身带逗号，则可通过指定其它分隔符（选项 “\ttt{sep}” ）来规避。\par

\begin{xtable}
  \begin{data}[sep={=}]
    AA        = BB          = CC \\
    A\{[\#\$  = a\newline b = "\_ \\  
    B\}]\%\&  = c\\d        = ,\textbackslash\\
  \end{data}
\end{xtable}
\rendertable [border]   \rule{2em}{0pt} 
\rendertable [booktabs] \par

\subsection{CSV 输入}

CSV输入与标准输入非常相似，但是它会处理特殊字符\footnote{目的是尽量匹配 CSV 的输入语法。}；
代价则是处理速度会更慢。
在没有特殊字符的情况下，应当尽量使用标准输入。

\subsubsection{默认效果}

通过指定 “\ttt{format=csv}” 选项（或直接使用 “\ttt{csv}” ），可让本环境按 CSV 格式处理输入。\par

\begin{xtable}
  \begin{data}[csv]
    AA , BB , CC\footnote{} \\
    A  , ab , 1 \\
    B  , cd , 2 \\
                \\
      ,    , 3\footnote{暂时只有内容才支持脚注。}
  \end{data}
\end{xtable}
\rendertable [border]   \rule{2em}{0pt} 
\rendertable [booktabs] \par

虽然本宏包也支持指定分隔符，但是仍建议遵守约定，使用默认值 “\ttt{,}”。

\subsubsection{特殊字符【待处理】} \todo{处理CSV中的特殊字符}

\subsection{JSON 输入}

JSON 的输入方式与标准输入迥异，不需要使用 “\ttt{\\}” 来换行。
其属性名为列名，属性值为表体。

\subsubsection{默认效果}

通过指定 “\ttt{format}” 选项为 “\ttt{json}”，可让本环境处理 JSON 格式。\par

\begin{xtable}
  \begin{data}[format=json]
    [
      { "A" : "a",  "B" : true,  "C" : 1 },
      { "A" : "bc", "C" : 2 },
      { "A" : "d",  "C" : 3, "D\footnote{}" : "不支持 TeX 命令" },
      {},
      { "A" : "e",  "B" : false, "C" : 4 }
    ]
  \end{data}
\end{xtable}
\rendertable [border]   \rule{2em}{0pt} 
\rendertable [booktabs] \par


\subsubsection{特殊字符【待处理】} \todo{处理JSON中的特殊字符}

\subsection{输入位置}

可以通过 “\ttt{loc}” 选项来指定输入的起点位置
\footnote{这可能会造成位置之前的列没有列名，在这种情况下其表头空。}；
如果未指定，则默认为“\{1, 1\}”。

对于标准输入（含 CSV 格式），输入过程中，会根据输入列名依次更新列名映射。
如本例中的 “\ttt{Name}”，它原本映射到第 $1$ 列，但输入后映射到第 $3$ 列
\footnote{虽然映射更新了，但它不会影响之前的录入，如单元格 “\ttt{(1,1)}”。}。

\begin{xtable}
  \rowname{Name}
  \cell(1,Name){Old}
  \begin{data}[loc={2,3}]
    Name, Sex,    Age\\
    John, man,    18\\  
    Leon,    ,    20\\
    Lily, woman,  21\\  
  \end{data}
\end{xtable}
\rendertable [border]   \rule{2em}{0pt} 
\rendertable [booktabs] \par

但对于 JSON 格式，则是完全不一样的逻辑：
如果列名已经存在，则直接填充数据；如果列名不存在，则在指定位置及之后依次添加新列名。
\footnote{JSON 中的属性名可以是无序的，所以列名顺序设计上是独立的；而 CSV 有序，因此会自动更新表头。}

\begin{xtable}
  \begin{data}
    Sex, Name\\
    man, John 
  \end{data}
  \begin{data}[loc={2,3}, format=json]
    [
      { "Name": "John", "Sex": "man",   "Age": 18 },
      { "Name": "Leon", "Sex": "",      "Age": 20 },
      { "Name": "Lily", "Sex": "woman", "Age": 21 },
    ]
  \end{data}
\end{xtable}
\rendertable [border]   \rule{2em}{0pt} 
\rendertable [booktabs] \par


\subsection{无表头输入}

在标准输入与 CSV 输入中，默认第一行为表头兼列名；
但这可能通过将 “\ttt{header}” 选项设为 “\ttt{false}” 来将第一行当作数据行处理。

\begin{xtable}
  \excelcolname
  \begin{data}[header=false]
    Name, Sex,    Age\\
    John, man,    18\\  
    Leon,    ,    20\\
    Lily, woman,  21\\  
  \end{data}
\end{xtable}
\rendertable [border]   \rule{2em}{0pt} 
\rendertable [booktabs] \par

如果同时还需要将表格设置为无表头，则需要在 \ttt{xtable} 层级中设置
\footnote{此时，\ttt{data} 层级默认会继承这个设置，除非手动指定。}。

\begin{xtable}[header=false]
  \begin{data}
    Name, Sex,    Age\\
    John, man,    18\\  
    Leon,    ,    20\\
    Lily, woman,  21\\  
  \end{data}
\end{xtable}
\rendertable [border]   \rule{2em}{0pt} 
\rendertable [booktabs] \par

如果即想把第一行当作列名又想不输出表头，则需要组合设置该选项。

\begin{xtable}[header=false]
  \begin{data}[header=true]
    Name, Sex,    Age\\
    John, man,    18\\  
    Leon,    ,    20\\
    Lily, woman,  21\\  
  \end{data}
\end{xtable}
\rendertable [border]   \rule{2em}{0pt} 
\rendertable [booktabs] \par

对于JSON格式，由于属性名始终为列名，因此在 \ttt{data} 层级不支持这个选项。
但在 \ttt{xtable} 层级中，仍然支持。

\begin{xtable}[header=false]
  \begin{data}[format=json]
    [
      { "Name": "John", "Sex": "man",   "Age": 18 },
      { "Name": "Leon", "Sex": "",      "Age": 20 },
      { "Name": "Lily", "Sex": "woman", "Age": 21 },
    ]
  \end{data}
\end{xtable}
\rendertable [border]   \rule{2em}{0pt} 
\rendertable [booktabs] \par



\subsection{文件导入【待实现】}

\ttt{\file} 是的功能与 \ttt{data} 的功能几乎一致，除了其是以文件作为输入及换行有特殊处理。
\todo{实现文件导入功能}

\section{行列与单元格输入}

本宏包支持按行、按列及按单元格输入，且这三种方式可以组合使用。
输入过程中如果数据超出现有行列数，则会自动添加行列。

此部分是本宏包与传统表格之间的最大差异点之一。


\subsection{按行输入}

按行输入（命令 \ttt{\row}）用于横向添加数据，可以指定起点位置，默认为新行第一列。以下是按行输入的示例：

\begin{xtable}
  \begin{data}
    Name, Sex,    Age\\
    John, man,    18\\ 
  \end{data}
  \row[|]{Lily||20}
  \row(4){Jacob, man, 21}
  \row(3,2){woman, 18, newcol}
\end{xtable}
\rendertable [border]   \rule{2em}{0pt} 
\rendertable [booktabs] \par

\subsection{按列输入}


按行输入（命令 \ttt{\col}）用于纵向添加数据，可以指定起点位置，默认为新列第一行。以下是按列输入的示例：

\begin{xtable}
  \begin{data}
    Name, Sex,    Age\\
    John, man,    18\\ 
  \end{data}
  \col(newcol)[|]{A|B}
  \col{1,2}
  \col(2,Sex){woman, man}
\end{xtable}
\rendertable [border]   \rule{2em}{0pt} 
\rendertable [booktabs] \par

\subsection{按单元格输入}

按单元格输入（命令 \ttt{\cell}）用于向指定位置添加数据。以下是按单元格输入的示例：

\begin{xtable}
  \begin{data}
    Name, Sex,    Age\\
    John, man,    18\\ 
  \end{data}
  \cell(3,newcol){newdata}
  \end{xtable}
\rendertable [border]   \rule{2em}{0pt} 
\rendertable [booktabs] \par


\section{保存与加载}

为了提高数据复用率，本宏包提供了表格保存与加载功能。

\subsection{保存表格}

以下是一个基础表，使用保存命令 \ttt{\savetable}，然后再修改了部分内容。（保存数据没有直接体现）

\begin{xtable}
  \begin{data}[format=json]
    [
      { "Name": "John", "Sex": "man",   "Age": 18 },
      { "Name": "Leon", "Sex": "",      "Age": 20 },
      { "Name": "Lily", "Sex": "woman", "Age": 21 },
    ]
  \end{data}
  \savetable{abc} % 注意保存的节点
  \cell(2,Name){Unsaved}
\end{xtable}
\rendertable [border]   \rule{2em}{0pt} 
\rendertable [booktabs] \par

请注意，在保存命令之后做的修改不会影响已经保存的表格。

\subsection{加载表格}

以下是复用的表格数据：

\begin{xtable}
  \col(Name){A,B,C,D} % 被 \loadtable 覆盖
  \loadtable{abc} 
  \cell(2,Sex){man}
\end{xtable}
\rendertable [border]   \rule{2em}{0pt} 
\rendertable [booktabs] \par

注，加载表格会覆盖之前的设置，因此通常把它放置到第一行。

\section{格式设置}

\subsection{宽度与高度}

表格会依次计算各列所需的宽度，然后再计算各行所需的高度
\footnote{这里所说的高度是行的最顶端面到底端的距离，实现中包括 \LaTeX 中的高度与深度两个值。}，
以包容所有单元格。

\begin{xtable}
  \begin{data}
    AA, BB, CC, EE\\
    abcde, 12, dd, abccba\\
    ab, 12, xy, abc\\
  \end{data}
  \cell(2,BB){19\\20}
  \savetable{abc}
\end{xtable}
\rendertable [border]   \rule{2em}{0pt} 
\rendertable [booktabs] \par

但可以通过 \ttt{\rowheight} 与 \ttt{\colwidth} 来指定额外的规则或固定尺寸。

\begin{xtable}
  \loadtable{abc}
  \rowheight[same]{auto}
  \colwidth[auto][0.45\textwidth]{same,same,fill,1em}
\end{xtable}
\rendertable [border]   \rule{2em}{0pt} 
\rendertable [booktabs] \par

注：如果宽度设置过小，会导致数据超出单元格。\todo{如果盒子高度过小，会怎么样。}

\subsection{对齐方式}

表格默认是左右居中，上下底对齐的；
也可通用 \ttt{\rowalign} 与 \ttt{\colalign} 来指定对齐方式。

\begin{xtable}
  \loadtable{abc}
  \rowheight[same]{auto}
  \rowalign[b]{b,m,m}
  \colalign{l,c,c,r}
\end{xtable}
\rendertable [border]   \rule{2em}{0pt} 
\rendertable [booktabs] \par


\section{合并单元格}

\TeX{} 应当避免使用合并单元格，但为了满足某些需求，本宏包提供了合并单元格的功能。

\begin{xtable}
  \begin{data}
    Name, Sex,    Age, remark\\
    John, man,    18, test\\ 
    John,     ,     , test\\ 
    John,     ,     , test\\ 
    John,     ,     , test\\ 
    John, man,    18, test\\ 
  \end{data}
  \cell(2,Sex){man\\18\footnote{这是一个合并单元格。}}
  \mergecell(2,Sex)(4,Age)[br]
\end{xtable}

\rendertable [border]   \rule{2em}{0pt} 
\rendertable [booktabs] \par

合并单元格的齐还需要进一步研究。\todo{优化合并单元格的对齐。}

\section{渲染输出}

目前支持四种样式的输出：
\begin{compactitem}
  \item 直接按行打印内容（纯盒子）;
  \item 三线表（如上述示例中右边的效果）；；
  \item 栅格模式（不支持合并单元格）；
  \item 全边框模式（如上述示例中左边的效果）；
\end{compactitem}

\subsection{直接打印}

可以使用 \ttt{\printtable} 直接打印表格。
每行数据使用一个水平盒子输出，且不绘制任何边框。因此它可以自动换页，其效果如下：

\printtable*[2em]

\subsection{三线表}

由于三线表在 \TeX{} 中比较受欢迎，因此本宏包添加了支持。
除了传统的三条线外，本宏包还支持添加额外的水平分隔线，效果如下：

\rendertable [booktabs, rule={1,4}] \par

\subsection{栅格模式}

本渲染模式下，允许用户指定要绘制哪些水平线与垂直线，但它只支持整条线。其效果如下：

\rendertable [grid, rule={ \hrule{0.7pt,midrule,1=dash,4=bottomrule}, \vrule{0.7pt,3=dotted} }] \par

\subsection{全边框模式}

本模板会输出所有边框，包括合并单元格的边框。示例在前面已经讲了，这里就不举例了。

\todos

\end{document}
