如何手动创建tabularx的X宽度?

如何手动创建tabularx的X宽度?

我想创建一个自动计算发票的环境。

表格可以完美地适应页面(当然)tabularx。但是,计算出错了,因为 LaTeX 多次创建表格以找出列宽X

计算工作很顺利,tabular但表格是否完全适合页面则取决于字体设置。

因此,我尝试手动计算一个变量列的宽度,但效果并不理想。有没有更好的方法可以解决这个问题?

\documentclass{scrlttr2}

\usepackage{tgheros}
\renewcommand\familydefault{\sfdefault}

\RequirePackage{xspace}

% =====================================
% correct number displaying

\usepackage[mode=text, reset-text-series = false, separate-uncertainty, group-digits=integer, group-minimum-digits = 4]{siunitx}

\sisetup{group-separator = {\,}, input-decimal-markers={,.}, output-decimal-marker = {,}}

\newcommand{\EURsimple}[1]{
    \sisetup{minimum-decimal-digits=2, group-separator = {.}, output-decimal-marker = {,}}
    #1
    \sisetup{minimum-decimal-digits=0, group-separator = {\,}, output-decimal-marker = {,}} 
}

\newcommand{\EUR}[1]{
    \num[minimum-decimal-digits=2, group-separator = {.}, output-decimal-marker = {,}]{#1}\,€
}

% =====================================
% automatic item numbering

\newcommand{\startnumberedlist}{\newcounter{numberedlistcounter}}
\newcommand{\numberedlistitem}{\stepcounter{numberedlistcounter}\thenumberedlistcounter}

% =====================================
% calculation

\RequirePackage{calculator}

\newcommand{\resetinvoicesum}{\ADD{0}{0}{\invoicesum}}
\newcommand{\addtoinvoicesum}[1]{\ADD{\invoicesum}{#1}{\invoicesum}\GLOBALCOPY{\invoicesum}{\invoicesum}}

% =====================================
% trying to calculate the needed width of the variable width column

\newlength{\invoicedescwidth}
\setlength{\invoicedescwidth}{\textwidth}

\newlength{\invoicerestwidth}
\settowidth{\invoicerestwidth}{\textbf{Pos. Anzahl Rechnungsbetrag Gesamtpreis}}

\addtolength{\invoicedescwidth}{-\invoicerestwidth}
\addtolength{\invoicedescwidth}{-15ex}  % <= this is not variable enough for different fonts. 

% =====================================
% invoice environment

\RequirePackage{booktabs}

\newenvironment{invoice}[1][]{\startnumberedlist\resetinvoicesum\par\noindent
    \centering\begin{tabular}{rp{\invoicedescwidth}S[table-format=5.1]S[table-format=5.4]r}%
        \toprule[0.7pt]\textbf{Pos.} & \textbf{Beschreibung} & \textbf{Anzahl} & \textbf{Einzelpreis} & \textbf{Gesamtpreis} \\ \toprule[0.7pt]}
    {\midrule[0.7pt] 
        &&& \textbf{Rechnungsbetrag} & \textbf{\EUR{\invoicesum}} \\ \cmidrule[0.7pt]{4-5} \end{tabular}%
}

% invoiceitem, #1: description, #2: count, #3: price per item
\newcommand{\invoiceitem}[3]{
    \numberedlistitem & #1 & #2 & \EURsimple{#3} & \MULTIPLY{#2}{#3}{\tempsum} \ROUND[2]{\tempsum}{\tempsum} \addtoinvoicesum{\tempsum} \EUR{\tempsum}\\} % price calculation and sum up all prices


\usepackage{Blindtext}

\begin{document}
    \begin{letter}{recipient}
        \opening{Hello}
        
        \Blindtext[1]
        
        \begin{invoice}
            \invoiceitem{Arbeitszeit in h}{800}{15}
            \invoiceitem{Schrauben}{2134}{0.1}
            \invoiceitem{Strommenge in kWh}{303.5}{0.3175}
            \invoiceitem{Position C}{2}{1123.1}
        \end{invoice}
        
    \end{letter}
\end{document}

附言:我也尝试过,tabularray但是这似乎无法正确地将小数点处的数字排列起来Ssiunitx

答案1

一些计算

\newlength{\invoicedescwidth}
\setlength{\invoicedescwidth}{\textwidth}
\newlength{\invoicerestwidth}
\settowidth{\invoicerestwidth}{\textbf{Pos. Anzahl Rechnungsbetrag Gesamtpreis}} 
\addtolength{\invoicedescwidth}{-\invoicerestwidth}
\addtolength{\invoicedescwidth}{-15ex}  % <= this is not variable enough for different fonts.

是可疑的。例如,你为什么不把文本部分转换为\textsf?为什么字符串中有空格Pos. Anzahl Rechnungsbetrag Gesamtpreis?从哪里来的15ex?我建议你用

% manual calculation of required column width
\newlength\mylenA
\newlength\invoicedescwidth
\settowidth\mylenA{% some text, some numbers, no spaces, all \textsf
  \textsf{\textbf{Pos.Gesamtpreis}22\,124,511.123,3175}}
\setlength\invoicedescwidth{%
  \dimexpr\textwidth-\mylenA-8\tabcolsep\relax}

在此处输入图片描述

\documentclass{scrlttr2}

\usepackage{tgheros}
\renewcommand\familydefault{\sfdefault}

\usepackage{xspace}
\usepackage{booktabs}
\usepackage{blindtext}

% correct number displaying
\usepackage[mode=text, 
            reset-text-series = false, 
            separate-uncertainty, 
            group-digits=integer, 
            group-minimum-digits = 4
           ]{siunitx}
\sisetup{group-separator = {\,}, 
         input-decimal-markers={,.}, 
         output-decimal-marker = {,}}

\newcommand{\EURsimple}[1]{
    \sisetup{minimum-decimal-digits=2, 
             group-separator = {.}, 
             output-decimal-marker = {,}}
    #1
    \sisetup{minimum-decimal-digits=0, 
             group-separator = {\,}, 
             output-decimal-marker = {,}} 
}

\newcommand{\EUR}[1]{
    \num[minimum-decimal-digits=2, 
         group-separator = {.}, 
         output-decimal-marker = {,}]{#1}\,€%
}

% automatic item numbering
\newcounter{numberedlistcounter}
\newcommand{\resetnumberedlist}{%
   \setcounter{numberedlistcounter}{0}}
\newcommand{\numberedlistitem}{%
   \stepcounter{numberedlistcounter}%
   \thenumberedlistcounter}
   
% manual calculation of required column width
\newlength\mylenA
\newlength\invoicedescwidth
\settowidth\mylenA{%   
  \textsf{\textbf{Pos.Gesamtpreis}22\,124,511.123,3175}}
\setlength\invoicedescwidth{%
  \dimexpr\textwidth-\mylenA-8\tabcolsep\relax}

% calculations
\RequirePackage{calculator}
\newcommand{\resetinvoicesum}{\ADD{0}{0}{\invoicesum}}
\newcommand{\addtoinvoicesum}[1]{\ADD{\invoicesum}{#1}{\invoicesum}\GLOBALCOPY{\invoicesum}{\invoicesum}}

% invoice environment
\newenvironment{invoice}{%
    \resetnumberedlist\resetinvoicesum
    \medskip\par\noindent
    \begin{tabular}{@{}
       l
       >{\raggedright}p{\invoicedescwidth}
       S[table-format=5.1]
       S[table-format=5.4]
       r @{}}%
       \toprule[0.7pt]
       \textbf{Pos.} & 
       \textbf{Beschreibung} & 
       {\textbf{Anzahl}} & 
       {\textbf{Einzelpreis}} & 
       \textbf{Gesamtpreis} \\ 
       \midrule[0.7pt]}{%
       \bottomrule[0.7pt] 
       \addlinespace
       \multicolumn{5}{r@{}}{%
         \textbf{Rechnungsbetrag}\hspace{0.75em}% 
         \textbf{\EUR{\invoicesum}}} \\ 
       \addlinespace
       \end{tabular}
     \par
}

\newcommand{\invoiceitem}[3]{
    \numberedlistitem & 
    #1 & #2 & 
    \EURsimple{#3} & 
    \MULTIPLY{#2}{#3}{\tempsum} 
    \ROUND[2]{\tempsum}{\tempsum} 
    \addtoinvoicesum{\tempsum} 
    \EUR{\tempsum}\\} 


\begin{document}
    \begin{letter}{recipient}
    \opening{Hello}
    \Blindtext[1]
        
    \begin{invoice}
        \invoiceitem{Arbeitszeit in h}{800}{15}
        \invoiceitem{Schrauben}{2134}{0.1}
        \invoiceitem{Strommenge in kWh}{303.5}{0.3175}
        \invoiceitem{Position C}{2}{1123.1}
          
    \end{invoice}   
    \end{letter}
    
\end{document}

附录解决 OP 的后续评论之一:如果您使用非默认的无衬线字体 - 尤其是使用可变宽度数字的字体 - 我建议您延迟计算长度参数\mylenA\invoicedescwidth直到之后\begin{document}并将代码块更改为

\newlength\mylenA
\newlength\invoicedescwidth
\settowidth\mylenA{%   
  \textsf{\textbf{Pos.Gesamtpreis}%
     \num[group-separator={\,}]{22222,2}%
     \num[group-separator={.}]{22222,2222}}}
\setlength\invoicedescwidth{%
  \dimexpr\textwidth-\mylenA-8\tabcolsep\relax}

答案2

环境{NiceTabular}提供了使用与 类似的列的nicematrix能力。与 不同,不会在运行 LaTeX 期间多次编译临时框中的表格,而是将信息写入文件(并且需要运行几次 LaTeX)。Xtabularxtabularxnicematrixaux

\documentclass{scrlttr2}
\usepackage{nicematrix}

\usepackage{tgheros}
\renewcommand\familydefault{\sfdefault}

\RequirePackage{xspace}

% =====================================
% correct number displaying

\usepackage[mode=text, reset-text-series = false, separate-uncertainty, group-digits=integer, group-minimum-digits = 4]{siunitx}

\sisetup{group-separator = {\,}, input-decimal-markers={,.}, output-decimal-marker = {,}}

\newcommand{\EURsimple}[1]{
    \sisetup{minimum-decimal-digits=2, group-separator = {.}, output-decimal-marker = {,}}
    #1
    \sisetup{minimum-decimal-digits=0, group-separator = {\,}, output-decimal-marker = {,}} 
}

\newcommand{\EUR}[1]{
    \num[minimum-decimal-digits=2, group-separator = {.}, output-decimal-marker = {,}]{#1}\,€
}

% =====================================
% automatic item numbering

\newcommand{\startnumberedlist}{\newcounter{numberedlistcounter}}
\newcommand{\numberedlistitem}{\stepcounter{numberedlistcounter}\thenumberedlistcounter}

% =====================================
% calculation

\RequirePackage{calculator}

\newcommand{\resetinvoicesum}{\ADD{0}{0}{\invoicesum}}
\newcommand{\addtoinvoicesum}[1]{\ADD{\invoicesum}{#1}{\invoicesum}\GLOBALCOPY{\invoicesum}{\invoicesum}}


% =====================================
% invoice environment

\RequirePackage{booktabs}

\newenvironment{invoice}[1][]{\startnumberedlist\resetinvoicesum\par\noindent
    \centering\begin{NiceTabular}{rX[l]S[table-format=5.1]S[table-format=5.4]r}%
        \toprule[0.7pt]\textbf{Pos.} & \textbf{Beschreibung} & \textbf{Anzahl} & \textbf{Einzelpreis} & \textbf{Gesamtpreis} \\ \toprule[0.7pt]}
    {\midrule[0.7pt] 
        &&& \textbf{Rechnungsbetrag} & \textbf{\EUR{\invoicesum}}\\ \cmidrule[0.7pt]{4-5} \end{NiceTabular}%
}

% invoiceitem, #1: description, #2: count, #3: price per item
\newcommand{\invoiceitem}[3]{
    \numberedlistitem & #1 & #2 & \EURsimple{#3} & \MULTIPLY{#2}{#3}{\tempsum} \ROUND[2]{\tempsum}{\tempsum} \addtoinvoicesum{\tempsum} \EUR{\tempsum}\\} % price calculation and sum up all prices


\usepackage{Blindtext}

\begin{document}
    \begin{letter}{recipient}
        \opening{Hello}
        
        \Blindtext[1]
        
        \begin{invoice}
            \invoiceitem{Arbeitszeit in h}{800}{15}
            \invoiceitem{Schrauben}{2134}{0.1}
            \invoiceitem{Strommenge in kWh and some other long text}{303.5}{0.3175}
            \invoiceitem{Position C}{2}{1123.1}
        \end{invoice}
        
    \end{letter}
\end{document}

您需要几个编译(几个工具,例如latexmk将为您完成这些编译)。

上述代码的输出

相关内容