مشکل به استفاده از محیط داخل دستور \caption
بر میگردد. اگر فایل تک زیر را اجرا کنید:
\documentclass{article}
\newenvironment{dumbenv}{}{}
\begin{document}
This is a test
\begin{figure}
\caption{%
This is a test.
\begin{dumbenv}
This is a test
\end{dumbenv}
}
\end{figure}
\end{document}
دقیقاً همان خطا را میگیرید:
./test.tex:11: Argument of \@caption has an extra }.
<inserted text>
\par
l.11 }
?
برای اینکه متوجه بشویم چه چیزی باعث خطا میشود، زمانی که خطا را در ترمینال دید یک r
وارد میکنیم و سپس return
را میزنیم. حالا محتویات فایل .aux
را باز میکنیم. در این فایل باید همچین خطی وجود داشته باشد:
\@writefile{lof}{\contentsline {figure}{\numberline {1}{\ignorespaces This is a test. \def \par }}{1}}
همانطور که میبینید دستور \par
به شکل نامتعارفی تعریف شده است. همچنین هنگامی که r
و return
را وارد کردیم، در ترمینال خطوط زیر ظاهر میشود:
Runaway argument?
\@captype {\def \@currenvir {dumbenv}\edef \@currenvline {\on@line }\csname \ET
C.
./test.tex:11: Paragraph ended before \@caption was complete.
<to be read again>
\par
l.11 }
برای روشن شدن موضوع، بهتر است تعریف دستور \caption
را در فایل latex.ltx
ببینیم:
\def\caption{%
\ifx\@captype\@undefined
\@latex@error{\noexpand\caption outside float}\@ehd
\expandafter\@gobble
\else
\refstepcounter\@captype
\expandafter\@firstofone
\fi
{\@dblarg{\@caption\@captype}}%
}
پس در واقع ارگومانهای دستور \caption
در دستور \@caption
تعریف شده است که آرگومان اولش در خط آخر تعریف دستور \caption
داده شده است و این آرگومان \@captype
است. \@captype
در واقع نوع caption
است. به عنوان مثال، زمانی که در محیط figure
هستیم، \@captype
در واقع figure
است و زمانی که در محیط table
هستیم \@captype
در واقع table
است. تعریف دستور \@caption
بصورت زیر است:
\long\def\@caption#1[#2]#3{%
\par
\addcontentsline{\csname ext@#1\endcsname}{#1}%
{\protect\numberline{\csname the#1\endcsname}{\ignorespaces #2}}%
\begingroup
\@parboxrestore
\if@minipage
\@setminipage
\fi
\normalsize
\@makecaption{\csname fnum@#1\endcsname}{\ignorespaces #3}\par
\endgroup}
قسمت اصلی تعریف که ما علاقهمند به آن هستیم این بخش است:
\addcontentsline{\csname ext@#1\endcsname}{#1}%
{\protect\numberline{\csname the#1\endcsname}{\ignorespaces #2}}%
دستور \addcontentsline
باید برای شما آشنا باشد. پس در واقع چه شما از دستور \listoftables
یا \listoffigures
استفاده کرده باشید یا نبایشد فهرست اشکال و جدواول شما در فایل .aux
نوشته میشود. برای اینکه مشکل را به مشکل کوچکتری بشکافیم و علت خطا را پیدا کنیم، فایل تک زیر را درست میکنیم:
\documentclass{article}
\newenvironment{dumbenv}{}{}
\begin{document}
This is a test
\addcontentsline{lof}{figure}%
{\protect\numberline{\thefigure}{\ignorespaces This is a test
\begin{dumbenv}
This is a test
\end{dumbenv}
}}
\end{document}
که خطای زیر را میگیریم:
! Incomplete \iffalse; all text was ignored after line 10.
<inserted text>
\fi
<*> test.tex
?
با فایل زیر چه اتفاقی میافتد؟
\documentclass{article}
\newenvironment{dumbenv}{}{}
\begin{document}
This is a test
\numberline{\thefigure}{\ignorespaces This is a test
\begin{dumbenv}
This is a test
\end{dumbenv}
}
% \addcontentsline{lof}{figure}%
% {\protect\numberline{\thefigure}{\ignorespaces This is a test
% \begin{dumbenv}
% This is a test
% \end{dumbenv}
% }}
\end{document}
هیچ مشکلی وجود ندارد. پس مشکل باید در دستور \addcontentsline
باشد. این دستور با استفاده از دستور بدوی \write
نوشته شده است و محتویاتش را در فایل .aux
مینویسند. در واقع چیزی مانند:
\documentclass{article}
\newenvironment{dumbenv}{}{}
\begin{document}
This is a test
\makeatletter
\write\@auxout{This is a test \begin{dumbenv}This is a test\end{dumbenv}}
\makeatother
\end{document}
در واقع هر کدام از این لایهها به دلایل مختلف خطاهای متفاوتی را تولید میکنند. خطایی که در اینجا میگیریم با خطای قبلی یکی است.
اگر دوباره r
بزنیم و سپس return
را و به فایل .aux
نگاه کنیم، خط زیر را میبینیم:
This is a test \def document{\def document{dumbenv}\edef { on input line 9}}\global \let
میتوانید این خط را با
\@writefile{lof}{\contentsline {figure}{\numberline {1}{\ignorespaces This is a test. \def \par }}{1}}
مقایسه کنید و شباهت آن را ببینید؟ اگر فایل قبلی را به این شکل مینوشتیم مشکلی نداشتیم:
\documentclass{article}
\newenvironment{dumbenv}{}{}
\begin{document}
This is a test
\makeatletter
\write\@auxout{This is a test \string\begin{dumbenv}This is a test \string\end{dumbenv}}
\makeatother
\end{document}
ولی نکته اصلی این است که لاتک آرگومان دستور \caption
را چک نمیکند که ببیند چه چیزی در دستور بدوی \write
مجاز است و چه چیزی نیست.
بنابراین اگر فایل اولمان را به این شکل مینوشتیم مشکلی نداشتیم:
\documentclass{article}
\newenvironment{dumbenv}{}{}
\begin{document}
This is a test
\begin{figure}
\caption{%
This is a test.
\string\begin{dumbenv}
This is a test
\string\end{dumbenv}
}
\end{figure}
\end{document}