让 example 环境带上导言区

前言

在《用 minted 宏包制作 example 环境》中,我们知道一种最基本的 example 环境的制作方法,即:

\documentclass{article}
\usepackage{verbatim,xcolor}

\makeatletter
\newwrite\example@out
\newlength\savefboxrule
\newlength\savefboxsep
\edef\example@name{\jobname-example.aux}
\newenvironment{example}%
{\begingroup\@bsphack
\immediate\openout\example@out=\example@name
\let\do\@makeother\dospecials\catcode`\^^M\active
\def\verbatim@processline{\immediate\write\example@out{\the\verbatim@line}}%
\verbatim@start}%
{\immediate\closeout\example@out\@esphack\endgroup%
\trivlist\item\relax
\setlength{\savefboxrule}{\fboxrule}%
\setlength{\savefboxsep}{\fboxsep}%
\setlength{\fboxsep}{0.015\textwidth}%
\setlength{\fboxrule}{0.4pt}%
\fcolorbox[gray]{0}{0.95}{%
\begin{minipage}[c]{0.45\textwidth}%
\setlength{\fboxrule}{\savefboxrule}%
\setlength{\fboxsep}{\savefboxsep}%
\small\verbatiminput{\example@name}%
\end{minipage}%
}%
\hfill%
\fbox{%
\begin{minipage}[c]{0.45\textwidth}%
\setlength{\fboxrule}{\savefboxrule}%
\setlength{\fboxsep}{\savefboxsep}%
\setlength{\parskip}{1ex plus 0.4ex minus 0.2ex}%
\normalsize\input{\example@name}%
\end{minipage}%
}%
\endtrivlist
}
\makeatother
\begin{document}
\begin{example}
\begin{equation}\label{eq:1}
E = mc^2
\end{equation}
The equation~(\ref{eq:1}) is a
famous mass energy equation.
\end{example}
\end{document}

然而这种方法无法添加导言区(可以用 % 去注释文档类及宏包,但这并不开箱即用),因此我给出一种全局设置导言区的方法。

正文

注意:前两种方法不再推荐。

低级方法

基本思路:在输出代码之前输出固定的导言区。

由于在盒子中使用 \verb 会报错,因此使用

\char`\

来输出 \{},用法为

\char`\\ \char`\{ \char`\}

输出 \ { }

定义一个导言区命令

\def\mypreamble{%
\char`\\documentclass\char`\{article\char`\}\\%
\char`\\begin\char`\{document\char`\}%
}

然后输出代码部分为

\texttt{\mypreamble}%
\verbatiminput{\example@name}%
\texttt{\char`\\end\char`\{document\char`\}}%

这时候会注意到,导言区与正文之间存在纵向间距,这是由于 verbatim 环境内部使用了 trivlist 环境,所以在前后有 \topsep + \parskip (+ \partopsep) 的纵向间距(参考《怎么把MATLAB的程序插入latex而不改变段前段后的行距?》)。

因此调整参数即可(参考 texdoc verbatim, sec 2.2 The interfaces

\parskip=0pt\topsep=0pt\partopsep=0pt

完整代码:

\documentclass{article}
\usepackage{verbatim,xcolor}

\makeatletter
\newwrite\example@out
\newlength\savefboxrule
\newlength\savefboxsep
\edef\example@name{\jobname-example.aux}
\newenvironment{example}%
{\begingroup\@bsphack
\immediate\openout\example@out=\example@name
\let\do\@makeother\dospecials\catcode`\^^M\active
\def\verbatim@processline{\immediate\write\example@out{\the\verbatim@line}}%
\verbatim@start}%
{\immediate\closeout\example@out\@esphack\endgroup%
\trivlist\item\relax
\setlength{\savefboxrule}{\fboxrule}%
\setlength{\savefboxsep}{\fboxsep}%
\setlength{\fboxsep}{0.015\textwidth}%
\setlength{\fboxrule}{0.4pt}%
\fcolorbox[gray]{0}{0.95}{%
\begin{minipage}[c]{0.5\textwidth}%
\setlength{\fboxrule}{\savefboxrule}%
\setlength{\fboxsep}{\savefboxsep}%
\parskip=0pt\topsep=0pt\partopsep=0pt%
\small%
\texttt{\mypreamble}%
\verbatiminput{\example@name}%
\texttt{\char`\\end\char`\{document\char`\}}%
\end{minipage}%
}%
\hfill%
\fbox{%
\begin{minipage}[c]{0.4\textwidth}%
\setlength{\fboxrule}{\savefboxrule}%
\setlength{\fboxsep}{\savefboxsep}%
\setlength{\parskip}{1ex plus 0.4ex minus 0.2ex}%
\normalsize\input{\example@name}%
\end{minipage}%
}%
\endtrivlist
}
\makeatother
\def\mypreamble{%
\char`\\documentclass\char`\{article\char`\}\\%
\char`\\begin\char`\{document\char`\}%
}
\begin{document}
\begin{example}
\begin{equation}\label{eq:1}
E = mc^2
\end{equation}
The equation~(\ref{eq:1}) is a
famous mass energy equation.
\end{example}
\end{document}

效果为

稍微高级一点的方法

在初级方法中,我们看到,我们需要手动输入导言区,且非常繁琐。

因此可以仿照输出、导入 \jobname-example.aux 的方法,写一个环境用来导入导言区代码。

我们看到定义 example 环境的代码

\newenvironment{example}%
{\begingroup\@bsphack
\immediate\openout\example@out=\example@name
\let\do\@makeother\dospecials\catcode`\^^M\active
\def\verbatim@processline{\immediate\write\example@out{\the\verbatim@line}}%
\verbatim@start}%
{\immediate\closeout\example@out\@esphack\endgroup%

看得出来,这部分的功能是将环境内的代码写到 \example@name 文件中,因此仿写即可。

仿写部分为

\newwrite\pre@out
\edef\pre@name{\jobname-pre.aux}
\newenvironment{pre}
{\begingroup\@bsphack
\immediate\openout\pre@out=\pre@name
\let\do\@makeother\dospecials\catcode`\^^M\active
\def\verbatim@processline{\immediate\write\pre@out{\the\verbatim@line}}%
\verbatim@start}%
{\immediate\closeout\pre@out\@esphack\endgroup}

这部分的功能为写一个名为 \jobname-pre.aux 的文件,内容为 pre 环境内的代码。
然后我们只需要在适当的位置导入文件即可。

完整代码:

\documentclass{article}
\usepackage{verbatim,xcolor}

\makeatletter
\newwrite\example@out
\newwrite\pre@out
\newlength\savefboxrule
\newlength\savefboxsep
\edef\example@name{\jobname-example.aux}
\edef\pre@name{\jobname-pre.aux}
\newenvironment{pre}
{\begingroup\@bsphack
\immediate\openout\pre@out=\pre@name
\let\do\@makeother\dospecials\catcode`\^^M\active
\def\verbatim@processline{\immediate\write\pre@out{\the\verbatim@line}}%
\verbatim@start}%
{\immediate\closeout\pre@out\@esphack\endgroup}
\newenvironment{example}%
{\begingroup\@bsphack
\immediate\openout\example@out=\example@name
\let\do\@makeother\dospecials\catcode`\^^M\active
\def\verbatim@processline{\immediate\write\example@out{\the\verbatim@line}}%
\verbatim@start}%
{\immediate\closeout\example@out\@esphack\endgroup%
\trivlist\item\relax
\setlength{\savefboxrule}{\fboxrule}%
\setlength{\savefboxsep}{\fboxsep}%
\setlength{\fboxsep}{0.015\textwidth}%
\setlength{\fboxrule}{0.4pt}%
\fcolorbox[gray]{0}{0.95}{%
\begin{minipage}[c]{0.5\textwidth}%
\setlength{\fboxrule}{\savefboxrule}%
\setlength{\fboxsep}{\savefboxsep}%
\parskip=0pt\topsep=0pt\partopsep=0pt%
\small%
\verbatiminput{\pre@name}%
\verbatiminput{\example@name}%
\texttt{\char`\\end\char`\{document\char`\}}%
\end{minipage}%
}%
\hfill%
\fbox{%
\begin{minipage}[c]{0.4\textwidth}%
\setlength{\fboxrule}{\savefboxrule}%
\setlength{\fboxsep}{\savefboxsep}%
\setlength{\parskip}{1ex plus 0.4ex minus 0.2ex}%
\normalsize\input{\example@name}%
\end{minipage}%
}%
\endtrivlist
}
\makeatother
\begin{document}
\begin{pre}
\documentclass{article}
\begin{document}
\end{pre}
\begin{example}
\begin{equation}\label{eq:1}
E = mc^2
\end{equation}
The equation~(\ref{eq:1}) is a
famous mass energy equation.
\end{example}
\end{document}

高级方法

我们知道,不能在 example 环境中写导言区是因为这个环境本质上是在「执行」这一段代码,那么我们转化思路,不在主文件中执行即可,即在「外部」执行。

必备知识

  • \write18{shell_command}\write18 会在工作目录下,向命令行输入一段代码;
  • \immediate:要求现在将代码发送到操作系统中执行。

用这两个命令,就可以在「将代码输出到外部文件」后执行命令行编译。

实现代码如下:

\documentclass{ctexart}
\usepackage{verbatim, graphicx, xcolor}
\newcounter{myexample}
\setcounter{myexample}{1}
\makeatletter
\newwrite\example@out
\newlength\savefboxrule
\newlength\savefboxsep
\edef\example@name{\jobname-example\themyexample.tex}
\newenvironment{hexample}
{\begingroup\@bsphack
\immediate\openout\example@out=\jobname-example\themyexample.tex
\let\do\@makeother\dospecials\catcode`\^^M\active
\def\verbatim@processline{\immediate\write\example@out{\the\verbatim@line}}%
\verbatim@start}%
{\immediate\closeout\example@out\@esphack\endgroup%
\immediate\write18{latexmk -C \jobname-example\themyexample.tex && latexmk -xelatex \jobname-example\themyexample.tex && pdfcrop \jobname-example\themyexample.pdf \jobname-example\themyexample.pdf}%
\trivlist\item\relax
\setlength{\savefboxrule}{\fboxrule}%
\setlength{\savefboxsep}{\fboxsep}%
\setlength{\fboxsep}{0.015\textwidth}%
\setlength{\fboxrule}{0.4pt}%
\fcolorbox[gray]{0}{0.95}{%
\begin{minipage}[c]{0.45\textwidth}%
\setlength{\fboxrule}{\savefboxrule}%
\setlength{\fboxsep}{\savefboxsep}%
\small\verbatiminput{\jobname-example\themyexample.tex}%
\end{minipage}%
}%
\hfill%
\fbox{%
\begin{minipage}[c]{0.45\textwidth}%
\setlength{\fboxrule}{\savefboxrule}%
\setlength{\fboxsep}{\savefboxsep}%
\includegraphics
{\jobname-example\themyexample.pdf}
\end{minipage}%
}%
\endtrivlist\addtocounter{myexample}{1}
}
\newenvironment{vexample}
{\begingroup\@bsphack
\immediate\openout\example@out=\jobname-example\themyexample.tex
\let\do\@makeother\dospecials\catcode`\^^M\active
\def\verbatim@processline{\immediate\write\example@out{\the\verbatim@line}}%
\verbatim@start}%
{\immediate\closeout\example@out\@esphack\endgroup%
\immediate\write18{latexmk -C \jobname-example\themyexample.tex && latexmk -xelatex \jobname-example\themyexample.tex && pdfcrop \jobname-example\themyexample.pdf \jobname-example\themyexample.pdf}%
\trivlist\item\relax
\setlength{\savefboxrule}{\fboxrule}%
\setlength{\savefboxsep}{\fboxsep}%
\setlength{\fboxsep}{0.015\textwidth}%
\setlength{\fboxrule}{0.4pt}%
\fcolorbox[gray]{0}{0.95}{%
\begin{minipage}[c]{\textwidth}%
\setlength{\fboxrule}{\savefboxrule}%
\setlength{\fboxsep}{\savefboxsep}%
\small\verbatiminput{\jobname-example\themyexample.tex}%
\end{minipage}%
}%
\\%
\fbox{%
\begin{minipage}[c]{\textwidth}%
\setlength{\fboxrule}{\savefboxrule}%
\setlength{\fboxsep}{\savefboxsep}%
\centering\includegraphics
{\jobname-example\themyexample.pdf}
\end{minipage}%
}%
\endtrivlist\addtocounter{myexample}{1}
}
\makeatother
\usepackage[a4paper,margin=2cm]{geometry}
\begin{document}
\begin{hexample}
\documentclass{article}
\pagestyle{empty}
\usepackage{amsmath, unicode-math}
\begin{document}
$\alpha, \symbfit{\alpha}$
\end{document}
\end{hexample}
\begin{vexample}
\documentclass{article}
\pagestyle{empty}
\usepackage{graphicx}
\begin{document}
\includegraphics[width=0.5\textwidth]{example-image.pdf}
\end{document}
\end{vexample}
\end{document}

注意:example 环境内的代码需要加上 \pagestyle{empty},否则 pdfcrop 不会裁剪到合适的尺寸。

编译方式:xelatex --shell-escape

文章作者: sikouhjw
文章链接: https://sikouhjw.github.io/2021/03/03/2021-03-03-preamble-verbatim-example/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 sikouhjw.github.io
支付宝捐赠
微信捐赠