找到解决方案后,longtable
我发现我需要,longtabu
因为我想使用X
列。
但是,使用longtabu
总和会出错,显示正确值的两倍。使用列时,X
它会添加更多内容。
这是因为该表被编译多次,因此也多次运行添加部分吗? 还有其他方法可以解决这个问题吗?expl3
或者我只需要使用longtable
然后手动计算X
列以使表填满整个textwidth
?
\documentclass[parskip=full]{scrlttr2}
\renewcommand\familydefault{\sfdefault}
\usepackage[defaultsans, scale=0.9]{opensans}
\usepackage[check-declarations]{expl3}
\ExplSyntaxOn
\NewDocumentCommand{\newtime}{m}
{
\fp_new:c { g_timecalc_time_#1_fp }
}
\newtime{default} % initialize one
\NewDocumentCommand{\resettime}{O{default}}{
\fp_gset:cn { g_timecalc_time_#1_fp } { \fpeval{0} }
}
% \addtime* will also show the added time
\NewDocumentCommand{\addtime}{sO{default}mm}
{
\timecalc_time_add:nnn { #2 } { #3 } { #4 }
% with \addtime* also show what's been added
\IfBooleanT{#1}{#3\,\textup{#4}}
}
\NewDocumentCommand{\strtime}{O{default}m}
{
\timecalc_time_str:nn { #1 } { #2 }
}
\int_new:N \l__timecalc_time_days_int
\int_new:N \l__timecalc_time_hours_int
\int_new:N \l__timecalc_time_minutes_int
\cs_new_protected:Nn \timecalc_time_add:nnn
{
\str_case:nnF { #3 }
{
{h}{ \fp_gadd:cn { g_timecalc_time_#1_fp } { #2 * 60 } }
{d}{ \fp_gadd:cn { g_timecalc_time_#1_fp } { #2 * 60 * 24 } }
{min}{ \fp_gadd:cn { g_timecalc_time_#1_fp } { #2 } }
}
{\ERROR}
}
\cs_new_protected:Nn \int_div_mod:nnn {
\int_set:Nn #3
{ \int_div_truncate:nn { #1 } { #2 } }
\int_set:Nn #1
{ \int_mod:nn { #1 } { #2 } }
}
\cs_new_protected:Nn \timecalc_time_str:nn
{
% round to an integral number of minutes
\int_set:Nn \l__timecalc_time_minutes_int
{ \fp_eval:n { round( \fp_use:c { g_timecalc_time_#1_fp }, 0 ) } }
\bool_if:nTF
{
\str_if_eq_p:nn{#2}{d}
}
{%yes
% compute and remove the number of days
\int_div_mod:nnn{\l__timecalc_time_minutes_int}{1440}{\l__timecalc_time_days_int}
% now print the days
\int_compare:nTF { \l__timecalc_time_days_int > 0 }
{ \int_eval:n { \l__timecalc_time_days_int }\,\textup{d}\; }
{% no days
\unkern
}
}{} % no
\bool_if:nTF
{
\str_if_eq_p:nn{#2}{h} ||
\str_if_eq_p:nn{#2}{d}
}
{%yes
% compute and remove the number of hours
\int_div_mod:nnn{\l__timecalc_time_minutes_int}{60}{\l__timecalc_time_hours_int}
% now print the hours
\int_compare:nTF { \l__timecalc_time_hours_int > 0 }
{
\int_eval:n { \l__timecalc_time_hours_int }\,\textup{h}\;
}
{% no hours
\unkern
}
}{} % no
% now print the minutes
\int_compare:nTF { \l__timecalc_time_minutes_int > 0 }
{
\int_eval:n { \l__timecalc_time_minutes_int }\,\textup{min}
}
{% no minutes
0\,min%\unkern
}
}
\ExplSyntaxOff
\usepackage{longtable}[=v4.13]
\usepackage{tabu}%
\begin{document}
\begin{longtable}{r}
\textbf{time}
\endhead
\addtime*{30}{min}\\
\addtime*{4}{h} \\
\end{longtable}
This leads to a complete time of \strtime{h}.
\resettime
Current time: \strtime{h}.
\begin{longtabu}{r}%
\textbf{time}
\endhead
\addtime*{30}{min}\\
\addtime*{4}{h} \\
\end{longtabu}%
This leads to a complete time of \strtime{h}.
\resettime
Current time: \strtime{h}.
\begin{longtabu}{X}%
\textbf{time}
\endhead
\addtime*{30}{min}\\
\addtime*{4}{h} \\
\end{longtabu}%
This leads to a complete time of \strtime{h}.
\end{document}
答案1
编辑以统一编码风格并使用 OP 的初始代码
\documentclass[parskip=full]{scrlttr2}
\renewcommand\familydefault{\sfdefault}
\usepackage[defaultsans, scale=0.9]{opensans}
\usepackage[check-declarations]{expl3}
% \newcommand\tabudoonlyatlast{%
% \ifx\tabu@savecounters\relax\expandafter\@gobble\else\expandafter\@firstofone\fi
% }
\ExplSyntaxOn
% WARNING! THIS USES \tex_relax:D WHICH IS FORBIDDEN (D = DoNotUse)
% But I have no idea what should take its place.
% Not "\relax"? that would be way too lenient.
\makeatletter
\NewDocumentCommand{\mrCarnivoreprotect}{m}{
\tl_if_eq:NNTF { \tabu@savecounters } { \tex_relax:D }
{ }
{ #1 }
}
\makeatother
\NewDocumentCommand{\newtime}{m}
{
\fp_new:c { g_timecalc_time_#1_fp }
}
\newtime{default} % initialize one
\NewDocumentCommand{\resettime}{O{default}}{
\fp_gset:cn { g_timecalc_time_#1_fp } { \fpeval{0} }
}
% \addtime* will also show the added time
\NewDocumentCommand{\addtime}{sO{default}mm}
{
\timecalc_time_add:nnn { #2 } { #3 } { #4 }
% with \addtime* also show what's been added
\IfBooleanT{#1}{#3\,\textup{#4}}
}
\NewDocumentCommand{\strtime}{O{default}m}
{
\timecalc_time_str:nn { #1 } { #2 }
}
\int_new:N \l__timecalc_time_days_int
\int_new:N \l__timecalc_time_hours_int
\int_new:N \l__timecalc_time_minutes_int
\cs_new_protected:Nn \timecalc_time_add:nnn
{
\str_case:nnF { #3 }
{
{h}{ \fp_gadd:cn { g_timecalc_time_#1_fp } { #2 * 60 } }
{d}{ \fp_gadd:cn { g_timecalc_time_#1_fp } { #2 * 60 * 24 } }
{min}{ \fp_gadd:cn { g_timecalc_time_#1_fp } { #2 } }
}
{\ERROR}
}
\cs_new_protected:Nn \int_div_mod:nnn {
\int_set:Nn #3
{ \int_div_truncate:nn { #1 } { #2 } }
\int_set:Nn #1
{ \int_mod:nn { #1 } { #2 } }
}
\cs_new_protected:Nn \timecalc_time_str:nn
{
% round to an integral number of minutes
\int_set:Nn \l__timecalc_time_minutes_int
{ \fp_eval:n { round( \fp_use:c { g_timecalc_time_#1_fp }, 0 ) } }
\bool_if:nTF
{
\str_if_eq_p:nn{#2}{d}
}
{%yes
% compute and remove the number of days
\int_div_mod:nnn{\l__timecalc_time_minutes_int}{1440}{\l__timecalc_time_days_int}
% now print the days
\int_compare:nTF { \l__timecalc_time_days_int > 0 }
{ \int_eval:n { \l__timecalc_time_days_int }\,\textup{d}\; }
{% no days
\unkern
}
}{} % no
\bool_if:nTF
{
\str_if_eq_p:nn{#2}{h} ||
\str_if_eq_p:nn{#2}{d}
}
{%yes
% compute and remove the number of hours
\int_div_mod:nnn{\l__timecalc_time_minutes_int}{60}{\l__timecalc_time_hours_int}
% now print the hours
\int_compare:nTF { \l__timecalc_time_hours_int > 0 }
{
\int_eval:n { \l__timecalc_time_hours_int }\,\textup{h}\;
}
{% no hours
\unkern
}
}{} % no
% now print the minutes
\int_compare:nTF { \l__timecalc_time_minutes_int > 0 }
{
\int_eval:n { \l__timecalc_time_minutes_int }\,\textup{min}
}
{% no minutes
0\,min%\unkern
}
}
\ExplSyntaxOff
\usepackage{longtable}[=v4.13]
\usepackage{tabu}%
\begin{document}
\begin{longtable}{r}
\textbf{time}
\endhead
\addtime*{30}{min}\\
\addtime*{4}{h}\\
\end{longtable}
This leads to a complete time of \strtime{h}.
\resettime
Current time: \strtime{h}.
\begin{longtabu}{r}%
\textbf{time}
\endhead
\mrCarnivoreprotect{\addtime*{30}{min}}\\
\mrCarnivoreprotect{\addtime*{4}{h}}\\
\end{longtabu}%
This leads to a complete time of \strtime{h}.
\resettime
Current time: \strtime{h}.
\begin{longtabu}{X}%
\textbf{time}
\endhead
\mrCarnivoreprotect{\addtime*{30}{min}}\\
\mrCarnivoreprotect{\addtime*{4}{h}}\\
\end{longtabu}%
This leads to a complete time of \strtime{h}.
\end{document}
生产
原始版本
tabu 会进行多次传递(或者可能会),可能 2 次或 3 次,甚至更多(不确定)。我快速查看了它的代码,但没有找到可以查询的条件。但是看起来好像在tabu 初步使用后\tabu@savecounters
设置为\relax
,并在最终传递时重置(可能是退出组)。因此这样。
\documentclass[parskip=full]{scrlttr2}
\renewcommand\familydefault{\sfdefault}
\usepackage[defaultsans, scale=0.9]{opensans}
\usepackage{longtable}[=v4.13]
\usepackage{tabu}%
\makeatletter
\newcommand\tabudoonlyatlast{%
\ifx\tabu@savecounters\relax\expandafter\@gobble\else\expandafter\@firstofone\fi
}
\makeatother
\begin{document}
\def\x{0}
\def\stepx{\xdef\x{\the\numexpr\x+1}}
Value of x: \x.
\begin{longtabu}{r}%
\textbf{time}
\endhead
initial x = \x\\
final x = \tabudoonlyatlast{\stepx}\x
\end{longtabu}
Value of x: \x.
\end{document}
话虽如此,我们因此在最终排版过程中修改了输出,并且我们可能会弄乱禁忌测量(例如当我们从 9 变为 10 时)。因此,为了确保万无一失,它应该与在固定宽度框中呈现的变量一起使用。
答案2
回答评论中的问题:longtable
在命令中存储辅助文件中每个表的列宽(使用罗马数字):
\gdef \LT@i {\LT@entry
{1}{17.00003pt}\LT@entry
{1}{27.27783pt}\LT@entry
{1}{32.55562pt}\LT@entry
{1}{50.88902pt}}
\gdef \LT@ii {\LT@entry
{1}{59.45284pt}\LT@entry
{1}{91.35826pt}\LT@entry
{1}{80.13902pt}}
您可以通过处理此命令来检索列大小,例如在 longtable 之前。您可以自行决定如何处理这些信息。在这里,我仅显示它并绘制规则(请注意,信息仅在第二次编译时存在,因此您需要进行一些测试并回退到第一次编译)。
\documentclass{article}
\usepackage{longtable,tikz}
\makeatletter
\ExplSyntaxOn
\newcommand\shownextlongtablewidth
{
\group_begin:
\int_zero:N\l_tmpa_int
\tl_if_exist:cT {LT@\int_to_roman:n{\c@LT@tables+1}}
{
\par\centering % to center the rules like the longtable
\cs_set:Npn \LT@entry##1##2
{
\int_incr:N\l_tmpa_int
\typeout{Width~of~column~\int_use:N\l_tmpa_int\c_space_tl~
in~table~\int_eval:n{\c@LT@tables+1}~is~##2}
\tikz{\draw[red,<->](0,0)--++(##2,0)}
}
\tl_use:c{LT@\int_to_roman:n{\c@LT@tables+1}}%
\par
}
\group_end:
}
\makeatother
\ExplSyntaxOff
\begin{document}
\shownextlongtablewidth
\begin{longtable}{lcl|l}
a&abc & duck & blubblub
\end{longtable}
\shownextlongtablewidth
\begin{longtable}{l@{\hspace{1cm}}p{3cm}l}
12345&abc & some long word \\
b & xxxxxxx &123
\end{longtable}
\end{document}
日志文件将会显示
Width of column 1 in table 1 is 17.00003pt
Width of column 2 in table 1 is 27.27783pt
Width of column 3 in table 1 is 32.55562pt
Width of column 4 in table 1 is 50.88902pt
Width of column 1 in table 2 is 59.45284pt
Width of column 2 in table 2 is 91.35826pt
Width of column 3 in table 2 is 80.13902pt
答案3
我建议你使用板状的封装并使用如下代码。
在序言中:
\usepackage{xltabular} \newcolumntype{R}{>{\raggedleft\arraybackslash}X}
在文档正文中:
\begin{xltabular}{\textwidth}{R} \textbf{time} \endhead \addtime*{30}{min}\\ \addtime*{4}{h} n \\ \end{xltabular}
其中
\addtime
是您发布的查询中定义的 L3 语法命令之一。
我忍不住要说,如果该表实际上只包含三行,那么longtable
and/orxltabular
包的机制在这里就没有很好地部署。