当表格对齐不能解决问题时该怎么办?
我设计了一种将数据输入到几乎类似表格的列表中的方法(可能效率不高或方法“不正确”,但使用了来自 egreg 的一些有用的 LaTeX3 答案)。
主要问题是
- 我的“表格”可能会在表格页脚栏出现的地方中断,如示例中所示。
- 此外,它可以在页脚栏信息之后、标题之前中断。
这样,标题或页脚和标题就会通过分页符与表格分隔开。我知道我可以用 minipages 换行,但我需要可拆分的文本,因为其中一些参数列表非常长。
标准
- 除最后两项外,每一项均可分页,而最后两项必须与表格的页脚和标题保持一致。
- 每个项目的描述应采用段落格式(如我在示例中所演示的那样)
jkodescriptionlist
并\jkoitem
具有灵活数量的参数(只有前两个参数左对齐)。
我想要的是定义jkoitem
应与表的页脚栏保持在一起的最小行数。(就此而言,中断也不应该在表的标题栏之后立即发生。)如果需要在表格页脚或标题的位置进行分页,则会发生分页,但它会将其中的一些项目带到下一页。
笔记:我能够通过使用\\*
对应于\vadjust{\nobreak\vskip<length>}
我还愿意接受有关如何更好地实施这整个事情的建议。我认为解决方案与胶水有关。正如 cfr 所建议的,longtable 环境可能会起作用,但我不知道如何使用 longtable 实现此布局而不会使源代码变得一团糟。更新:我忘记了之前问过一个关于表格和 LaTeX3 的问题,这可能在这里有用:https://tex.stackexchange.com/a/243627/13552^. 我记得我当时只是因为时间限制而放弃了,而且我永远无法实现长表版本。
代码
我愿意接受建议
\documentclass{article}
\usepackage{fontspec}
\usepackage{lipsum}
\usepackage{caption}
\usepackage{xparse}
\usepackage{booktabs}
\usepackage{tikz}
\newcommand{\jkotabcap}[1]{\captionof{table}{#1}\bigskip}
\ExplSyntaxOn
\DeclareDocumentEnvironment{jkodescriptionlist}{O{}O{}}%
{%
\noindent\rule{\textwidth}{\heavyrulewidth}\\*
\noindent\textbf{\textcolor{red}{#1}}\\*
\noindent\rule[2mm]{\textwidth}{\lightrulewidth}\par
}%
{%
% Some glue should probably go here that prevents the bottom of the table from being alone on a page but I am not sure how to do this.
% Do I need \leavevmode\vadjust{\penalty 10000} or something similar?
{\nopagebreak\noindent\rule{\textwidth}{\heavyrulewidth}\\*
\tiny #2\par}
}%
\DeclareDocumentCommand{\jkoitem}{O{}m}{% Optional:comma list of values; mandatory:description
\noindent\dojkoitemargs:n{#1}\vadjust{\nobreak\vskip .1ex}
\null\hspace{2em}\par\parbox{\dimexpr\textwidth-4em\relax}{\footnotesize#2}\vskip 1.7ex plus 2ex % Description Format
}%
\clist_new:N\jkoitemargs
\cs_new_protected:Nn\dojkoitemargs:n{{%
\clist_set:Nn\jkoitemargs{#1}%
\textbf{\clist_item:Nn \jkoitemargs {1}},~\clist_item:Nn \jkoitemargs {2} \hfill
\int_step_inline:nnnn {3}{1}{\clist_count:N \jkoitemargs}
{%
\int_compare:nTF { ##1 = \clist_count:N \jkoitemargs }
{\clist_item:Nn \jkoitemargs {##1}}
{\clist_item:Nn \jkoitemargs {##1},~}
}
}}%
\ExplSyntaxOff
\begin{document}
% Syntax
%\jkoitem[Whatever fields are needed]
%{Description}
\section{Introduction}
\subsection{This Fun Document}
\lipsum[1-2]
Hello\\ % <-- Uncomment this line to push the entire bottom of the table to another page.
\begin{jkodescriptionlist}[HL7-Path, Name \hfill R/O, Rep\#][\textbf{R/0} = Required/Optional, \textbf{R} = Required,
\textbf{O} = Optional, \textbf{RO} = Required if known,
\textbf{REP\#} = Repitition, \textbf{Y} = entry can be
taken from the list, \textbf{N} = Individual Values,
\textbf{Number} = Maximum length of the list]
\jkoitem[MSH-6-2,homeCommunityID,R]
{OID of the affinity domain where the document will be stored.}
\jkoitem[MSH-19,languageCode,R]
{Specifies the human language of the document. Format according RFC-3066.}
\jkoitem[PID-3,patientInfo.socialSecurityNumber,R]
{Social security number of the patient. Important: The social security number will be
identified within the list of patient
identifiers (PID-3) by the OID of the social security association.}
\jkoitem[PID-3,patientInfo.sourcePatientID,R]
{Patient identifier of the local CIS/RIS/PACS…Important: The Patient Identifier
will be identified within the list of patient identifiers (PID-3) by the identifier type code PI.}
\jkoitem[PID-5-1,patientInfo.familyName,R]
{Family name of the patient.}
\jkoitem[PID-5-2,patientInfo.givenName,R]
{Given name of the patient.}
\jkoitem[PID-5-3,patientInfo.secondAndFurtherNames[0],O]
{Second and further names of the patient}
\end{jkodescriptionlist}
\jkotabcap{Fun Mapping Data.}
\label{Table:Fun Mapping Data}
\end{document}
输出
半成功的 TeX 改造 > 来自 wipet 代码的 LaTeX
我并不完全满意,因为它的\jkoitem
参数不像原始版本那样灵活。例如,我的\jkoitem
可以支持任意数量的参数(直到它们变得不美观)。此代码仅支持 3 个美观的参数。另外,我不确定我是否理解发生了什么,但我读到了 TeXBook 的第 112 页,并尝试实现我学到的一些关于断行和段落形状的知识。
代码
\documentclass{article}
\usepackage{lipsum}
\newcount\jcodenum
\def\jcode[#1]#2[#3]#4\endjcode{\goodbreak\hbox{}\par
\hrule\medskip\hbox to\hsize{\bf #1}\medskip\hrule\medskip
{\jcodenum=0
\def\jkoitem[##1]##2{\advance\jcodenum by1 } % Dummy function just tallies up jkoitem's as they are parsed
\typeout{**** Value of Counter (before first call of body 4): \the\jcodenum}% Print count of jcodenum at this point to log
#4 % prints contents of jcode=nothing because jkoitem prints nothing, serves to increment \jcodenum (see log output)
\typeout{**** Value of Counter (after first call of body 4): \the\jcodenum}% Print count of jcodenum at this point to log
\interlinepenalty=10000% value of the penalty (node) added after each line of a paragraph.
\let\jkoitem=\jkoitemx% Reassign jkoitem to jkoitemx, passing all args properly
#4 %This #4 represents the jkoitems with the new definition? Commenting this out erases content
\typeout{**** Value of Counter (after redefinition and new call of body 4): \the\jcodenum}% Print count of jcodenum at this point to log
\medskip\hrule\nobreak\smallskip \tiny \noindent#3\bigskip\par}
}
\def\jkoitemx[#1,#2,#3]#4{\goodbreak\par \noindent{\bf#1}, \ignorespaces#2\hfill #3\par\nobreak
\hangindent=\parindent{\footnotesize #4\par}% This #4 represents the descriptions
\advance\jcodenum by-1% descrease count while it is increased
\typeout{**** Value of Counter (inside of jkoitemx): \the\jcodenum}% Print count of jcodenum at this point to log
\ifnum\jcodenum<2 \nobreak \fi
}
\begin{document}
\lipsum[1-2]
\jcode[HL7-Path, Name \hfill R/O, Rep\#]
[{\bf R/0}~=~Required/Optional, {\bf R}~=~Required,
{\bf O}~=~Optional, {\bf RO}~=~Required if known,
{\bf REP\#}~=~Repitition, {\bf Y}~=~entry can be taken from the list,
{\bf N}~=~Individual Values, {\bf Number}~=~Maximum length of the list]
\jkoitem[MSH-6-2,homeCommunityID,R]
{OID of the affinity domain where the document will be stored.}
\jkoitem[MSH-19,languageCode,R]
{Specifies the human language of the document. Format according RFC-3066.}
\jkoitem[PID-3,patientInfo.socialSecurityNumber,R]
{Social security number of the patient. Important: The social security
number will be
identified within the list of patient
identifiers (PID-3) by the OID of the social security association.}
\jkoitem[PID-3,patientInfo.sourcePatientID,R]
{Patient identifier of the local CIS/RIS/PACS…Important: The Patient
Identifier
will be identified within the list of patient identifiers (PID-3) by the
identifier type code PI.}
\jkoitem[PID-5-1,patientInfo.familyName,R]
{Family name of the patient.}
\jkoitem[PID-5-2,patientInfo.givenName,R]
{Given name of the patient.}
\jkoitem[PID-5-3,patientInfo.secondAndFurtherNames{[0]},O]
{Second and further names of the patient}
\jkoitem[PID-5-3,patientInfo.secondAndFurtherNames{[0]},O]
{Second and further names of the patient}
\jkoitem[PID-5-3,patientInfo.secondAndFurtherNames{[0]},O]
{Second and further names of the patient}
\jkoitem[PID-5-3,patientInfo.secondAndFurtherNames{[0]},O]
{Second and further names of the patient}
\jkoitem[PID-5-3,patientInfo.secondAndFurtherNames{[0]},O]
{Second and further names of the patient}
\endjcode
%\bye
\lipsum[1]
\end{document}
相关日志条目:
**** Value of Counter (before first call of body 4): 0
**** Value of Counter (after first call of body 4): 11
**** Value of Counter (inside of jkoitemx): 10
**** Value of Counter (inside of jkoitemx): 9
**** Value of Counter (inside of jkoitemx): 8
**** Value of Counter (inside of jkoitemx): 7
**** Value of Counter (inside of jkoitemx): 6
**** Value of Counter (inside of jkoitemx): 5
**** Value of Counter (inside of jkoitemx): 4
**** Value of Counter (inside of jkoitemx): 3
**** Value of Counter (inside of jkoitemx): 2
**** Value of Counter (inside of jkoitemx): 1
**** Value of Counter (inside of jkoitemx): 0
**** Value of Counter (after redefinition and new call of body 4): 0
输出
使用 Longtable 的改进方法(对 cfr 评论的回应)
2016 年 1 月 7 日更新
问题:
- col1 不加粗(因为我使用的是
\clist_use:Nn \jkoitemargs { & }
而不是\textbf{\clist_item:Nn \jkoitemargs {1}}
) - col3 未正确右对齐
- 行和其描述之间可能会出现分页符(已修复:
\\
至\\*
) - 表格对齐看起来根本不像原来的那么好。
代码
\documentclass{article}
\usepackage{fontspec}
\usepackage[margin=2cm]{geometry}
\usepackage{longtable}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentEnvironment{mytable}{ O{} }
{
\clist_set:Nn \jkotableargs { #1 }
\begin{longtable}{llrr}
\clist_use:Nn \jkoitemargs { & } \endhead
}
{
\end{longtable}
}
\clist_new:N\jkoitemargs
\clist_new:N\jkotableargs
\NewDocumentCommand{\myitem}{ O{} m }
{
\clist_set:Nn \jkoitemargs { #1 }
\clist_use:Nn \jkoitemargs { & }\\*
\multicolumn{ 4 } {@{}l@{}}
{
\hspace{\parindent}
\parbox{\dim_eval:n { \textwidth-\parindent } } {\footnotesize #2}
}
\\[\normalbaselineskip]
}
\ExplSyntaxOff
\begin{document}
\begin{mytable}[HL7-Path, Name, R/O, Rep\#]
\myitem[col1,col2,col3,col4]{description on new row}
\myitem[MSH-6-2,homeCommunityID,R/O,Rep\#]{OID of the affinity domain where the document will be stored.}
\end{mytable}
\end{document}
答案1
您可以开始尝试以下代码
\newcount\jcodenum
\def\jcode[#1]#2[#3]#4\endjcode{\par
\hrule\medskip\hbox to\hsize{\bf #1}\medskip\hrule\medskip
{\jcodenum=0
\def\jkoitem[##1]##2{\advance\jcodenum by1 }#4
\interlinepenalty=10000 \let\jkoitem=\jkoitemx #4
\medskip\hrule\nobreak\medskip #3\par}
}
\def\jkoitemx[#1,#2,#3]#4{\par \noindent{\bf#1}, \ignorespaces#2\hfill #3\par\nobreak
#4\par
\advance\jcodenum by-1 \ifnum\jcodenum<2 \nobreak \fi
}
\jcode[HL7-Path, Name \hfill R/O, Rep\#]
[{\bf R/0} = Required/Optional, {\bf R} = Required,
{\bf O} = Optional, {\bf RO} = Required if known,
{\bf REP\#} = Repitition, {\bf Y} = entry can be taken from the list,
{\bf N} = Individual Values, {\bf Number} = Maximum length of the list]
\jkoitem[MSH-6-2,homeCommunityID,R]
{OID of the affinity domain where the document will be stored.}
\jkoitem[MSH-19,languageCode,R]
{Specifies the human language of the document. Format according RFC-3066.}
\jkoitem[PID-3,patientInfo.socialSecurityNumber,R]
{Social security number of the patient. Important: The social security
number will be
identified within the list of patient
identifiers (PID-3) by the OID of the social security association.}
\jkoitem[PID-3,patientInfo.sourcePatientID,R]
{Patient identifier of the local CIS/RIS/PACS…Important: The Patient
Identifier
will be identified within the list of patient identifiers (PID-3) by the
identifier type code PI.}
\jkoitem[PID-5-1,patientInfo.familyName,R]
{Family name of the patient.}
\jkoitem[PID-5-2,patientInfo.givenName,R]
{Given name of the patient.}
\jkoitem[PID-5-3,patientInfo.secondAndFurtherNames{[0]},O]
{Second and further names of the patient}
\endjcode
\bye
您可以在纯 TeX 中尝试此操作。用 LaTeX 重写此操作很简单。我不会这样做,因为我不支持 LaTeX。
由于测试,段落在行之间不可断开,并且最后两项加页脚也不可断开\ifnum\jcodenum<2 \nobreak \fi
。
答案2
已解决 wipet 答案的解释
2016 年 1 月 11 日更新:
以下代码演示了计算环境内每个项目的解决方案。它还演示了哪些类型的环境支持这种技术。它不受\newenvironment
宏或xparse \NewDocumentEnvironment
宏的支持,因为内容无法像在 TeX 中那样访问。在 LaTeX 中执行此操作的唯一方法是使用environ
包与宏的\NewEnviron
组合\BODY
来插入环境的内容。
诀窍是先统计\additem
s 的数量,然后重新定义以从存储在计数器中的 s\additem
总数中减去。 if/then 语句检查环境是否到达最后一个( )。因此,当有一个项目时(换句话说,计数器达到 1),将( / ) 添加到组合中,这样可以将倒数第二个项目和最后一个项目放在下一页上。真巧妙!谢谢 wipet!\additem
\additem
\ifnum\counter<2 \nobreak \fi
\nobreak
\penalty \@M
\penalty 10000
\documentclass{article}
\usepackage{environ}
\usepackage{xparse}
\newcount\texcounter
\newcounter{latexNewEnvironcounter}
\newcounter{latexcounter}
\newcounter{latexthreecounter}
% TeX Syntax
\def\texenv#1\endtexenv{
\def\additem[##1]##2{\advance\texcounter by1 }
\typeout{**** Value texcounter (before first call of body): \the\texcounter} % Should be 0
#1 % Typeset body of env
\typeout{**** Value texcounter (after first call of body): \the\texcounter} % Should be 3
\let\additem=\additemtex
#1 % Retypeset body of env with new definition of \additem (\additemx)
}
% environ Package NewEnviron
\NewEnviron{NewEnvironenv}{% environ package is the only way I know how to grab the body like in TeX
\def\additem[##1]##2{\addtocounter{latexNewEnvironcounter}{1}}
\typeout{**** Value latexNewEnvironcounter (before first call of body): \the\value{latexNewEnvironcounter}} % Should be 0
\BODY % Typeset body of env
\typeout{**** Value latexNewEnvironcounter (after first call of body): \the\value{latexNewEnvironcounter}} % Should be 3
\let\additem=\additemlatexNewEnviron
\BODY % Retypeset body of env with new definition of \additem (\additemx)
}{}
% LaTeX2e Syntax
\newenvironment{latexenv}{% Not supported because #1 cannot contain the body of the environment like in TeX
\def\additem[##1]##2{\addtocounter{latexcounter}{1}}
\typeout{**** Value latexcounter (before first call of body): \the\value{latexcounter}} % Should be 0
%#1
\typeout{**** Value latexcounter (after first call of body): \the\value{latexcounter}} % Should be 3
\let\additem=\additemlatex
}{}
% LaTeX3 Syntax
\NewDocumentEnvironment{latexthreeenv}{}{% Not supported because #1 cannot contain the body of the environment like in TeX
\def\additem[##1]##2{\addtocounter{latexthreecounter}{1}}
\typeout{**** Value latexthreecounter (before first call of body): \the\value{latexthreecounter}} % Should be 0
%#1
\typeout{**** Value latexthreecounter (after first call of body): \the\value{latexthreecounter}} % Should be 3
\let\additem=\additemlatexthree
}{}
% additem redefinition for TeX
\def\additemtex[#1,#2,#3]#4{\par \noindent{\bf#1}, \ignorespaces#2\hfill
\def\comma{\def\comma{, }}\listargs #3,,\par\nobreak%Redefine comma to use space, see \listargs
\hangindent=\parindent{\parbox{\dimexpr\textwidth-\parindent-1cm\relax}{\footnotesize #4}\par}% This #4 represents the descriptions
\typeout{**** Value texcounter (inside additemx): \the\texcounter}% Print count
\advance\texcounter by-1 % descrease count while it is increased
\ifnum\texcounter<2 \nobreak \fi
}
\def\listargs#1,{\ifx,#1,\else\comma\ignorespaces#1\expandafter\listargs\fi}% Redefine args to user spaces
% additem redefinition for NewEnviron
\def\additemlatexNewEnviron[#1,#2,#3]#4{\par \noindent{\bf#1}, \ignorespaces#2\hfill
\def\comma{\def\comma{, }}\listargs #3,,\par\nobreak%Redefine comma to use space, see \listargs
\hangindent=\parindent{\parbox{\dimexpr\textwidth-\parindent-1cm\relax}{\footnotesize #4}\par}% This #4 represents the descriptions
\typeout{**** Value latexNewEnvironcounter (inside additemlatexNewEnviron): \the\value{latexNewEnvironcounter}} % Print Count
\addtocounter{latexNewEnvironcounter}{-1} % descrease count while it is increased
\ifnum\latexNewEnvironcounter<2 \nobreak \fi
}
% additem redefinition for LaTeX2e
\def\additemlatex[#1,#2,#3]#4{\par \noindent{\bf#1}, \ignorespaces#2\hfill
\def\comma{\def\comma{, }}\listargs #3,,\par\nobreak%Redefine comma to use space, see \listargs
\hangindent=\parindent{\parbox{\dimexpr\textwidth-\parindent-1cm\relax}{\footnotesize #4}\par}% This #4 represents the descriptions
\typeout{**** Value latexcounter (inside additemlatex): \the\value{latexcounter}} % Print Count
\addtocounter{latexcounter}{-1} % descrease count while it is increased
\ifnum\latexcounter <2 \nobreak \fi
}
% additem redefinition for LaTeX3
\def\additemlatexthree[#1,#2,#3]#4{\par \noindent{\bf#1}, \ignorespaces#2\hfill
\def\comma{\def\comma{, }}\listargs #3,,\par\nobreak%Redefine comma to use space, see \listargs
\hangindent=\parindent{\parbox{\dimexpr\textwidth-\parindent-1cm\relax}{\footnotesize #4}\par}% This #4 represents the descriptions
\typeout{**** Value latexthreecounter (inside additemlatexthree): \the\value{latexthreecounter}} % Print Count
\addtocounter{latexthreecounter}{-1} % descrease count while it is increased
\ifnum\latexthreecounter<2 \nobreak \fi
}
\begin{document}
\section{Supported}
\subsection{\TeX}
\texenv
\additem[arg1,arg2,arg3]{A nice description. Value texcounter: \the\texcounter}
\additem[arg1,arg2,arg3]{A nice description. Value texcounter: \the\texcounter}
\additem[arg1,arg2,arg3]{A nice description. Value texcounter: \the\texcounter}
\endtexenv
\subsection{\texttt{environ} Package}
\begin{NewEnvironenv}
\additem[arg1,arg2,arg3]{A nice description. Value latexNewEnvironmentcounter: \the\value{latexNewEnvironcounter}}
\additem[arg1,arg2,arg3]{A nice description. Value latexNewEnvironmentcounter: \the\value{latexNewEnvironcounter}}
\additem[arg1,arg2,arg3]{A nice description. Value latexNewEnvironmentcounter: \the\value{latexNewEnvironcounter}}
\end{NewEnvironenv}
\section{Unsupported}
\subsection{\LaTeX\ 2e}
\begin{latexenv} % Does not work, see definition comment
\additem[arg1,arg2,arg3]{A nice description. Value latexcounter: \the\value{latexcounter}}
\additem[arg1,arg2,arg3]{A nice description. Value latexcounter: \the\value{latexcounter}}
\additem[arg1,arg2,arg3]{A nice description. Value latexcounter: \the\value{latexcounter}}
\end{latexenv}
\subsection{\LaTeX\ 3}
Workaround maybe possible, see egreg's hack: \\http://tex.stackexchange.com/a/172426/13552
\begin{latexthreeenv} % Does not work, see definition comment
\additem[arg1,arg2,arg3]{A nice description. Value latexthreecounter: \the\value{latexthreecounter}}
\additem[arg1,arg2,arg3]{A nice description. Value latexthreecounter: \the\value{latexthreecounter}}
\additem[arg1,arg2,arg3]{A nice description. Value latexthreecounter: \the\value{latexthreecounter}}
\end{latexthreeenv}
\end{document}
输出
[![在此处输入图片描述][1]][1]
日志
这样做的目的是为了证明哪些计数器按预期工作。我们预期:0,3,3,2,1。
纯 TeX
**** Value texcounter (before first call of body): 0
**** Value texcounter (after first call of body): 3
**** Value texcounter (inside additemx): 3
**** Value texcounter (inside additemx): 2
**** Value texcounter (inside additemx): 1
Environ
**** Value latexNewEnvironcounter (before first call of body): 0
**** Value latexNewEnvironcounter (after first call of body): 3
**** Value latexNewEnvironcounter (inside additemlatexNewEnviron): 3
**** Value latexNewEnvironcounter (inside additemlatexNewEnviron): 2
**** Value latexNewEnvironcounter (inside additemlatexNewEnviron): 1
标准 LaTeX\newenvironment
**** Value latexcounter (before first call of body): 0
**** Value latexcounter (after first call of body): 0
**** Value latexcounter (inside additemlatex): 0
**** Value latexcounter (inside additemlatex): -1
**** Value latexcounter (inside additemlatex): -2
xparse
的 \NewDocumentEnvironment
**** Value latexthreecounter (before first call of body): 0
**** Value latexthreecounter (after first call of body): 0
**** Value latexthreecounter (inside additemlatexthree): 0
**** Value latexthreecounter (inside additemlatexthree): -1
**** Value latexthreecounter (inside additemlatexthree): -2