我希望在我的报告中定义几种结构损坏,并希望在文档开头自动生成汇总表。下面是一个例子,展示了我目前取得的进展以及我想要实现的目标。
\documentclass{article}
\usepackage{etoolbox}
\usepackage{pgffor}
\parindent=0pt
\newcounter{cntClassA}\newcounter{cntClassB}\newcounter{cntClassC}\newcounter{cntAddTmp}
\makeatletter
\newcommand\defineDamageKey[4] { \expandafter\edef\csname damage.#1.#2.#3\endcsname{#4} }
\newcommand{\addDamage}[3]{%
%
\ifstrequal{#1}{A}{\stepcounter{cntClassA} \setcounter{cntAddTmp}{\thecntClassA} }{}%
\ifstrequal{#1}{B}{\stepcounter{cntClassB} \setcounter{cntAddTmp}{\thecntClassB} }{}%
\ifstrequal{#1}{C}{\stepcounter{cntClassC} \setcounter{cntAddTmp}{\thecntClassC} }{}%
%
\defineDamageKey{#1}{\thecntAddTmp}{description}{#2}%
\defineDamageKey{#1}{\thecntAddTmp}{solution}{#3}%
\defineDamageKey{#1}{\thecntAddTmp}{ID}{#1-\thecntAddTmp}%
%
\printDamage{#1}{\thecntAddTmp}%
}
\newcommand{\printDamage}[2]{%
\textbf{ID:} \@nameuse{damage.#1.#2.ID}\\%
\textbf{Damage class:} #1\\%
\textbf{Description:} \@nameuse{damage.#1.#2.description}\\%
\textbf{Solution:} \@nameuse{damage.#1.#2.solution}\\%
}
\newcommand{\damageSummary}[1]{%
\newcount\damageCountForClass
%
\ifstrequal{#1}{A}{ \damageCountForClass=\thecntClassA }{}%
\ifstrequal{#1}{B}{ \damageCountForClass=\thecntClassB }{}%
\ifstrequal{#1}{C}{ \damageCountForClass=\thecntClassC }{}%
%
\foreach \n in {1,...,\damageCountForClass}%
{%
\textbf{\@nameuse{damage.#1.\n.ID}:} \@nameuse{damage.#1.\n.description}\\
}%
}
\makeatother
\begin{document}
\section{Building 1}
\addDamage{A}{Broken window}{Fix window}
\addDamage{B}{Wrong wall paint}{Repaint}
\section{Building 2}
\addDamage{C}{Leaky windows}{Seal windows}
\addDamage{A}{Defective electrics}{Fix electrics}
\section{Summary}
\damageSummary{A}
\damageSummary{B}
\damageSummary{C}
\end{document}
我的基本想法是将我的数据定义为具有结构化命名宏的键/值对:
\@namedef{damage.A.1.description}{description damage A-1}
\@namedef{damage.A.1.solution}{solution damage A-1}
\@namedef{damage.A.2.description}{description damage A-2}
但现在我想在文档开头显示摘要表!我说的对吗?这只适用于 AUX 文件?
我刚刚开始学习低级 TeX / 宏编写,并试图通过检查 todonotes 包(因为可能存在类似的机制)来弄清楚如何做到这一点\todo
。\listoftodos
但我不明白。
那么我当前的解决方案是否真的是一个很好的起点,还是我需要不同的方法?有人能给我提示如何实现这一点吗?
答案1
编辑:如果您希望从摘要中建立超链接到描述损坏的文档位置,则可以添加 hyperref-support。
如果您不介意有一个辅助文件,其名称是每个损坏类别的模式,那么您可能可以使用 LaTeX 2ε 的- -meachanism。我使用-environments 来列出项目。如何调整-environment 取决于正在使用的文档类,并且可能会变得棘手。⟨expansion of jobname⟩.Damageclass⟨name of damage-class⟩
\addtocontents
\@starttoc
description
description
这个例子需要编译三次,每次运行之间不能删除辅助文件,直到一切都正确匹配。(第一次运行创建辅助文件。第二次运行创建摘要的描述项标签并将测量值存储在辅助文件中。第三次运行使用辅助文件中的测量值。)
\documentclass{article}
\usepackage{hyperref}
\makeatletter
\newcommand\maxIDwidth{0pt}%
\AtBeginDocument{\providecommand\r@maxIDwidthpreviousrun{0pt}}%
\AtEndDocument{%
\protected@write\@auxout{}{\string\newlabel{maxIDwidthpreviousrun}{\maxIDwidth}}%
}%
\DeclareRobustCommand\damageSummaryItem[3]{%
\item[%
\setbox\scratchbox\hbox{#1}%
\ifdim\maxIDwidth<\wd\scratchbox\xdef\maxIDwidth{\the\wd\scratchbox}\fi
\rlap{\hbox{\@ifundefined{hyperlink}{\@secondoftwo}{\hyperlink}{#3}{#1}}}%
\hbox to\r@maxIDwidthpreviousrun{\hfill}%
]%
#2%
}%
\newbox\scratchbox
\newcommand\addDamage[3]{%
%#1 - Damage class
%#2 - Damage description
%#3 - Fix/Solution description
\@ifundefined{c@Damageclass@#1}{%
\newcounter{Damageclass@#1}%
\expandafter\gdef\csname theDamageclass@#1\endcsname{#1\mbox{-}\arabic{Damageclass@#1}}%
}{}%
\begingroup
\begin{description}%
\itemsep=0pt
\parskip=0pt
\item[{\refstepcounter{Damageclass@#1}ID:%
\addtocontents{Damageclass#1}{%
\damageSummaryItem{\csname theDamageclass@#1\endcsname:}{#2}{\@ifundefined{@currentHref}{}{\@currentHref}}\protected@file@percent
}%
}]\csname theDamageclass@#1\endcsname
\item[{Damage class:}]#1%
\item[{Description:}]#2%
\item[{Solution:}]#3%
\end{description}%
\endgroup
}%
\newcommand\damageSummary[1]{%
\begingroup
\IfFileExists{\jobname.Damageclass#1}{%
\@ifundefined{hyperlink}{\@firstoftwo}{%
{\expandafter}\expandafter\ifx\csname hyper@last\endcsname\relax
\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
}%
}{\@secondoftwo}%
{%
\begin{description}%
\itemsep=0pt
\parskip=0pt
\@starttoc{Damageclass#1}%
\par\vskip-\@topsepadd\vskip-\parskip
\end{description}%
}{\@starttoc{Damageclass#1}}%
\endgroup
}%
\makeatother
\begin{document}
\section{Summary}
\damageSummary{A}
\damageSummary{B}
\damageSummary{C}
\section{Building 1}
\addDamage{A}{Broken window}{Fix window}
\addDamage{B}{Wrong wall paint}{Repaint}
\section{Building 2}
\addDamage{C}{Leaky windows}{Seal windows}
\addDamage{A}{Defective electrics}{Fix electrics}
\end{document}
问题“使用 LaTeX 制作零件清单吗?”你可能会对这个问题的答案感兴趣——那里也提到了类似的问题。在答案中,你会发现一种不同的方法,它基于包数据工具和一个.csv 文件。
答案2
你是对的:你需要一个辅助文件。
.dam
如果文件与之前的运行不同,以下代码将发出警告并告诉您重新运行 LaTeX。
\documentclass{article}
\ExplSyntaxOn
% the user interface
\NewDocumentCommand{\addDamage}{mmm}
{% #1 = class, #2 = type, #3 = action
\cryptkeeper_damage_add:nnn { #1 } { #2 } { #3 }
}
\NewDocumentCommand{\printDamageSummary}{}
{
\section*{Summary}
\cryptkeeper_damage_print:
}
% the auxiliary command
\NewDocumentCommand{\damageSummary}{mmm}
{
\cryptkeeper_damage_add_summary:nnn { #1 } { #2 } { #3 }
}
% the lower level code
% variables
\iow_new:N \g_cryptkeeper_damage_iow
\prop_new:N \g_cryptkeeper_damage_prop
\str_new:N \g_cryptkeeper_damage_hash_str
\int_new:N \g_cryptkeeper_damage_class_A_int
\int_new:N \g_cryptkeeper_damage_class_B_int
\int_new:N \g_cryptkeeper_damage_class_C_int
\seq_new:N \g_cryptkeeper_damage_id_seq
% actions for setup
\AtBeginDocument
{
\file_if_exist:nT { \c_sys_jobname_str.dam }
{% the file exists, save its hash and input it
\str_gset:Nx \g_cryptkeeper_damage_hash_str
{
\file_mdfive_hash:n { \c_sys_jobname_str.dam }
}
\file_input:n { \c_sys_jobname_str.dam }
}
\iow_open:Nn \g_cryptkeeper_damage_iow { \c_sys_jobname_str.dam }
}
\AtEndDocument
{
\iow_close:N \g_cryptkeeper_damage_iow
\str_if_eq:eeF
{ \g_cryptkeeper_damage_hash_str } % saved hash
{ \file_mdfive_hash:n { \c_sys_jobname_str.dam } } % new hash
{% if differ, issue a message
\msg_warning:nn { cryptkeeper/damage } { file differ }
}
}
% messages
\msg_new:nnn { cryptkeeper/damage } { file differ }
{
.dam~file~modified.~Rerun~LaTeX.
}
\cs_new_protected:Nn \cryptkeeper_damage_add:nnn
{
% increment the counter
\int_gincr:c { g_cryptkeeper_damage_class_#1_int }
% write a the .dam file
\iow_now:Nx \g_cryptkeeper_damage_iow
{
\exp_not:N \damageSummary % the command
{#1} % the class
{ \int_use:c { g_cryptkeeper_damage_class_#1_int } } % the number
{ \exp_not:n { #2 } } % the damage
}
% print the data
\par\addvspace{\topsep}
\noindent
\textbf{ID:}~#1-\int_use:c { g_cryptkeeper_damage_class_#1_int } \\*
\textbf{Damage~class:}~#1 \\*
\textbf{Description:}~#2 \\*
\textbf{Solution:}~#3
\par\addvspace{\topsep}
}
\cs_new_protected:Nn \cryptkeeper_damage_add_summary:nnn
{
% save the ID in the form {1|2|3}000+damage number
% class A is 1, and so on
% the sequence will be sorted by \printSummary
\seq_gput_right:Nx \g_cryptkeeper_damage_id_seq
{ \int_eval:n { \int_from_alph:n { #1 } * 1000 + #2 } }
% save the data in a property list
\prop_gput:Nen \g_cryptkeeper_damage_prop
{ \int_eval:n { \int_from_alph:n { #1 } * 1000 + #2 } }
{
\noindent\textbf{#1-#2:}~#3\par
}
}
\cs_generate_variant:Nn \prop_gput:Nnn { Ne }
\cs_new_protected:Nn \cryptkeeper_damage_print:
{
% sort the sequence
\seq_sort:Nn \g_cryptkeeper_damage_id_seq
{
\int_compare:nTF { ##1 > ##2 } { \sort_return_swapped: } { \sort_return_same: }
}
% map the sequence and print the data stored in the property list
\seq_map_inline:Nn \g_cryptkeeper_damage_id_seq
{
\prop_item:Nn \g_cryptkeeper_damage_prop { ##1 }
}
}
\ExplSyntaxOff
\begin{document}
\printDamageSummary
\section{Building 1}
\addDamage{A}{Broken window}{Fix window}
\addDamage{B}{Wrong wall paint}{Repaint}
\section{Building 2}
\addDamage{C}{Leaky windows}{Seal windows}
\addDamage{A}{Defective electrics}{Fix electrics}
\end{document}
注意\printSummary
可以去任何地方。
答案3
使用memory
包存储任意自由格式n维数据,使用tocloft
包通过命令收集磁盘上的摘要信息\addtocontents
(虽然\addcontentsline
也有效并提供 TOC 页码),并使用非常简单的 expl3 语法对类、类计数器和待办事项进行嵌套循环来创建摘要(用于排序):
目前在某些地方是硬编码的,但可以动态扩展(例如,创建新的类计数器或计算循环端点)。
并且使用memory
包,添加额外的维度(例如建筑领域)变得微不足道。
平均能量损失
\documentclass{article}
\usepackage[table]{xcolor}
\usepackage{tocloft}
\newlistof{todos}{ltd}{Summary}
\usepackage{memory}
\usepackage{etoolbox}
\usepackage{xparse}
\newcounter{todoitem}
\newcounter{classcounterA}
\newcounter{classcounterB}
\newcounter{classcounterC}
\newcounter{classcounterZ}
\newcounter{classcountertemp}
\ExplSyntaxOn
\int_new:N \l_myclassfirst_int
\int_new:N \l_myclasslast_int
\int_new:N \l_myclasscountmax_int
\int_new:N \l_mytempc_int
\int_new:N \l_mytempd_int
\int_new:N \l_myloopcount_int
\int_new:N \l_myloopcountcc_int
\int_new:N \l_mytodoloopcount_int
\int_new:N \l_mytodocountmax_int
\str_new:N \l_mytemp_str
\str_new:N \l_mytempb_str
\str_new:N \l_mytempc_str
\str_new:N \l_mytempd_str
\str_new:N \l_mytodototal_str
\NewDocumentCommand { \summarytodos } { } {
%
\int_set:Nn \l_myclassfirst_int {
\int_from_alph:n { A }
}
\int_set:Nn \l_myclasslast_int {
\int_from_alph:n { Z }
}
%
\int_set:Nn \l_myloopcount_int { \c_zero_int }
% loop through the classes (A-Z)
\int_do_while:nNnn
{ \l_myloopcount_int } < { \l_myclasslast_int }
{
\int_set:Nn
\l_myloopcount_int
{ \l_myloopcount_int + 1 }
{
% loop through the class counters
\int_set:Nn \l_myloopcountcc_int { \c_one_int }
\int_set:Nn \l_myclasscountmax_int { 15 } %
\int_do_while:nNnn
{ \l_myloopcountcc_int } < { \l_myclasscountmax_int }
{
{
%--------------- loop through all the todo items
%--------------- if there is a match on class + classcounter
%--------------- then print
% \alltodos[total]
\str_set:Nx \l_mytofototal_str {
\cs:w @alltodos@total@memory\cs_end:
}
\int_set:Nn \l_mytodoloopcount_int { \c_one_int }
\int_set:Nn \l_mytodocountmax_int { \l_mytofototal_str + 1}
\int_do_while:nNnn
{ \l_mytodoloopcount_int }
<
{ \l_mytodocountmax_int }
{ %true
%match on class:
\str_set:Nx \l_mytemp_str {
\cs:w @blist@\int_use:N\l_mytodoloopcount_int,\lbdimb @memory\cs_end:
}
\str_set:Nx \l_mytempb_str {
\int_to_Alph:n { \l_myloopcount_int }
}
%
\str_if_eq:NNTF
\l_mytempb_str
\l_mytemp_str
{
%match on class counter:
\str_set:Nx \l_mytempc_str {
{\cs:w @blist@\int_use:N\l_mytodoloopcount_int,\lbdimc @memory\cs_end:}
}
\str_set:Nx \l_mytempd_str {
{ \int_eval:n { \l_myloopcountcc_int } }
}
%
\str_if_eq:NNTF
\l_mytempc_str
\l_mytempd_str
{
% add to summary
\addtocontents{ltd}{%
% List:
{\bfseries
%% :\cs:w
%% @blist@\int_use:N\l_mytodoloopcount_int,\lbdima @memory
%% \cs_end:
\cs:w
@blist@\int_use:N\l_mytodoloopcount_int,\lbdimb @memory
\cs_end:
-
\cs:w
@blist@\int_use:N\l_mytodoloopcount_int,\lbdimc @memory
\cs_end:
}%end bfseries
:~~ % space
\cs:w
@blist@\int_use:N\l_mytodoloopcount_int,\lbdimd @memory
\cs_end:
%%%% :\cs:w
%%% @blist@\int_use:N\l_mytodoloopcount_int,\lbdime @memory
%%% \cs_end:
%%% :\cs:w
%%% @blist@\int_use:N\l_mytodoloopcount_int,\lbdimf @memory
%%% \cs_end:
\par
}% end addtocontens
}{}% end match on class counter
}{}% end match on class
\int_set:Nn
\l_mytodoloopcount_int
{ \l_mytodoloopcount_int + \c_one_int }
}{}% todo loop
}{}% class counter loop
\int_set:Nn
\l_myloopcountcc_int
{ \l_myloopcountcc_int + \c_one_int }
}% class counter loop
}
{ }
} % end class loop
\addtocontents{ltd}{\protect\mbox{}\protect\hrulefill\par}
}
\ExplSyntaxOff
\newcommand\lbdimbuilding{building}
\newcommand\lbdimdclass{dclass}
\newcommand\lbdimdclasscounter{dclasscounter}
\newcommand\lbdimdescription{description}
\newcommand\lbdimsolution{solution}
\newcommand\lbdimstatus{status}
\newdata*{blist}
\newdata{alltodos}
\newdata{tempblist}
%-------------------------------
\newcommand\lbdima{\lbdimbuilding}
\newcommand\lbdimb{\lbdimdclass}
\newcommand\lbdimc{\lbdimdclasscounter}
\newcommand\lbdimd{\lbdimdescription}
\newcommand\lbdime{\lbdimsolution}
\newcommand\lbdimf{\lbdimstatus}
\newcommand\lbclasscountername{}
%-------------------------------
\newcommand\addtodo[4]{%
\stepcounter{todoitem}
\blist[\thetodoitem,\lbdima]={#1}
\blist[\thetodoitem,\lbdimb]={#2}
\expandafter\renewcommand\expandafter\lbclasscountername{classcounter#2}
%\lbclasscountername
\stepcounter{\lbclasscountername}
\setcounter{classcountertemp}{\value{\lbclasscountername}}
\blist[\thetodoitem,\lbdimc] := {\theclasscountertemp}%{\lbclasscountername}
\blist[\thetodoitem,\lbdimd]={#3}
\blist[\thetodoitem,\lbdime]={#4}
\blist[\thetodoitem,\lbdimf]={To do}
\alltodos[total]=\thetodoitem
\printtodoitem{\thetodoitem}
}
%-------------------------------
\newcommand\printtodoitem[1]{%
\begin{tabular}{ll}
\rowcolor{blue!20}
{\Large {\phantom{L}}} Item & \\
\hline
%\blist[\thetodoitem,\lbdima]
\bfseries ID & \blist[\thetodoitem,\lbdimb]-\blist[\thetodoitem,\lbdimc] \\
\bfseries Class & \blist[\thetodoitem,\lbdimb]\\
\bfseries Description & \blist[\thetodoitem,\lbdimd]\\
\bfseries Solution & \blist[\thetodoitem,\lbdime]
%\blist[\thetodoitem,\lbdimf]
\end{tabular}
\hspace{1in}
%\par
%\bigskip
}
%-------------------------------
\newcommand\showalltodostotal{\alltodos[total]\ items in total.}
\begin{document}
\listoftodos
\section{b1}
\addtodo{(b1)}{A}{Broken window}{Fix window}
\addtodo{(b1)}{B}{Wrong wall paint}{Repaint}
\section{b2}
\addtodo{(b2)}{C}{Leaky windows}{Seal windows}
\addtodo{(b2)}{A}{Defective electrics}{Fix electrics}
\section{b3}
\addtodo{(b3)}{Z}{Too sunny}{Put sunshade up}
\addtodo{(b3)}{Z}{No solar panels}{Install solar panels}
\section{b4}
\showalltodostotal
\summarytodos
\end{document}