How to build two different .tex files from same .tex file
Update.
Added support to leave the empty environment in the copy, removing only its contents, or to remove also the \begin...\end
pair (by default).
I programmed a LuaLaTeX solution and tried to make it flexible enough. These are the files which compose the solution:
remove-env.lua
-- remove-env.lua
omittedEnvironments = {}
omitFileSuffix = "-without"
leaveEmptyEnvs = false
function shouldOmit(line)
for i,v in ipairs(omittedEnvironments) do
if (string.find(line, "\\begin{"..v.."}")~=nil) then
return true
end
end
return false
end
function shouldResume(line)
for i,v in ipairs(omittedEnvironments) do
if (string.find(line, "\\end{"..v.."}")~=nil) then
return true
end
end
return false
end
function dumpfile()
myout = io.open(tex.jobname..omitFileSuffix..".tex", "w")
myin = io.open(tex.jobname..".tex", "r")
omitting = false
for line in myin:lines() do
if (not omitting and shouldOmit(line)) then
if (leaveEmptyEnvs) then myout:write(line.."\n") end
omitting = true
end
if (not omitting) then
myout:write(line.."\n")
end
if (omitting and shouldResume(line)) then
if (leaveEmptyEnvs) then myout:write(line.."\n") end
omitting = false
end
end
myout:close()
myin:close()
end
remove-env.tex
\directlua{dofile("remove-env.lua")}
\def\omitEnvironment#1{\directlua{table.insert(omittedEnvironments, "#1")}}
\def\omitFileSuffix#1{\directlua{omitFileSuffix="#1"}}
\def\leaveEmptyEnvs{\directlua{leaveEmptyEnvs=true}}
\def\removeEmptyEnvs{\directlua{leaveEmptyEnvs=false}}
\AtEndDocument{\directlua{dumpfile()}}
MWE.tex
\input remove-env
\documentclass{article}
\usepackage{fontspec}
\usepackage{lipsum}
\newenvironment{solution}{}{}
\omitEnvironment{solution}
\omitFileSuffix{-sans-sol}
\begin{document}\parindent0pt\parskip1em
1. \lipsum[1]\hrulefill\par
\begin{solution}
2. \lipsum[2]\hrulefill\par
\end{solution}
3. \lipsum[3]\hrulefill\par
\end{document}
This MWE defines a no-op solution
environment which acts simply as markup, but of course you can define it in a way that produces some effect in the pdf. Macro \omitEnvironment
specifies the environment you want to omit. You can use this macro several times to specify several environments, and all of them will be omitted. Macro \omitFileSuffix
specifies the suffix that will be appended to the output filename.
Run:
$ lualatex MWE.tex
And you will get two files (and all the usual auxiliar files, of course):
MWE.pdf
will be generated as usually, and all the contents (including omitted environments) will be present.MWE-sans-sol.tex
is a copy ofMWE.tex
in which allsolution
environments are removed.
$ diff MWE.tex MWE-sans-sol.tex
11,13d10
< \begin{solution}
< 2. \lipsum[2]\hrulefill\par
< \end{solution}
If you want to remove only the contents of the solution but leave the empty environment, you only have to specify \leaveEmptyEnvs
at some point of MWE.tex
. In this case the diff will show:
$ diff MWE.tex MWE-sans-sol.tex
12d11
< 2. \lipsum[2]\hrulefill\par
PS: Thanks to Scott H. who suggested me not to use luatex callbacks, which was my first (and too convoluted) approach
A TeX only solution; it assumes that you don't have any other environment whose name starts with the string soluti
other than solution
.
Prepare the following extract.tex
file:
\newread\texfileread
\newwrite\texfilewrite
\openin\texfileread=ignasimain.tex % put here the main file name
\immediate\openout\texfilewrite=ignasistudents.tex % put here the secondary file name
\edef\BEGINSOLUTI{\string\begin\string{soluti}
\edef\ENDSOLUTION{\string\end\string{solution}
\newif\ifwritesolution
\writesolutiontrue
\long\def\ignasidecide#1#2#3#4#5#6#7#8#9\relax{%
\def\temp{#1#2#3#4#5#6#7#8}%
\ignasidecideaux#9}
\long\def\ignasidecideaux#1#2#3#4#5#6\relax{%
\ifnum\pdfstrcmp{\temp#1#2#3#4#5}{\BEGINSOLUTI}=0
\immediate\write\texfilewrite{\ignasiline^^J}
\writesolutionfalse
\else
\ifnum\pdfstrcmp{\temp#1#2#3#4#5}{\ENDSOLUTION}=0
\writesolutiontrue
\fi
\fi
\ifwritesolution
\immediate\write\texfilewrite{\ignasiline}
\fi
}
\endlinechar=-1 \newlinechar=`\^^J
\loop\unless\ifeof\texfileread%
\readline\texfileread to \ignasiline%
\expandafter\ignasidecide\ignasiline%
\relax\relax\relax\relax%
\relax\relax\relax\relax%
\relax\relax\relax\relax%
\relax\relax%
\repeat%
\csname bye\endcsname%
\csname @@end\endcsname
Change the file names as desired. Put this file along with the main file and compile with pdftex
or pdflatex
(it's the same).
With your example file, the resulting file will be
% This is main.tex file
\documentclass{article}
\begin{document}
\begin{exercise}
This is the first exercise
\begin{solution}
\end{solution}
\end{exercise}
\end{document}
Basically we read the main file line by line (ignoring category codes) with \readline
; if the line starts with
\begin{soluti
then we write out the line with a blank line following it and set a conditional to false; if the line starts with
\end{solution
then the conditional is set again to true. The current line is written out if the conditional is true.
I had the same problem. I ended up using docstrip
.
The system I set up produces:
(i) separate tex and pdf files for me (with solutions) and the students (without solutions:),
(ii) several versions of the exercise, for grouping students, and
(iii) typesets the only selected topic(s), allowing me to keep all the exercises in the same file.
Here's the outline of the main file containing exercises and solutions:
% This is exercises.tex
\documentclass{article}
\newenvironment{exercise}{}{}
\newenvironment{solution}{}{}
\title{Subject\\\normalsize homework topic:
%<topic1>topic 1
%<topic2>topic 2
}
\author{}
\begin{document}
\maketitle
%<*topic1>
\begin{exercise}
Exercise text (topic 1)
%<A> Version for group A,
%<B> Version for group B,
%<C> Version for group C,
%<D> Version for group D.
More exercise text.
\end{exercise}
%<*!student>
\begin{solution}
Solution, visible only in my copy:
%<A> for group A,
%<B> for group B,
%<C> for group C,
%<D> for group D.
\end{solution}
%</!student>
%<*student>
% Template for student's answer.
\begin{solution}
\end{solution}
%</student>
%</topic1>
%<*topic2>
\begin{exercise}
Exercise text (topic 1)
%<A> Version for group A,
%<B> Version for group B,
%<C> Version for group C,
%<D> Version for group D.
More exercise text.
\end{exercise}
%<*!student>
\begin{solution}
Solution, visible only in my copy:
%<A> for group A,
%<B> for group B,
%<C> for group C,
%<D> for group D.
\end{solution}
%</!student>
%<*student>
% Template for student's answer.
\begin{solution}
\end{solution}
%</student>
%</topic2>
\end{document}
And this is the .ins
file for topic1
:
% This is exercises.ins
\input docstrip
\nopreamble\nopostamble
\askforoverwritefalse
\generate{%
\file{exercises-topic1-A.tex}{\from{exercises.tex}{student,topic1,A}}%
\file{exercises-topic1-B.tex}{\from{exercises.tex}{student,topic1,B}}%
\file{exercises-topic1-C.tex}{\from{exercises.tex}{student,topic1,C}}%
\file{exercises-topic1-D.tex}{\from{exercises.tex}{student,topic1,D}}%
\file{exercises-topic1-ME.tex}{\from{exercises.tex}{topic1,A,B,C,D}}%
%\file{exercises-topic2-A.tex}{\from{exercises.tex}{student,topic2,A}}%
%\file{exercises-topic2-B.tex}{\from{exercises.tex}{student,topic2,B}}%
%\file{exercises-topic2-C.tex}{\from{exercises.tex}{student,topic2,C}}%
%\file{exercises-topic2-D.tex}{\from{exercises.tex}{student,topic2,D}}%
%\file{exercises-topic2-ME.tex}{\from{exercises.tex}{topic2,A,B,C,D}}%
}
\endbatchfile
Finally, a very badly written makefile --- nothing more than a camouflaged bash script, really. (I guess that ideally, running make
should produce the .ins
file automatically ... ahh, some day...)
all:
pdftex exercises.ins
bash -c 'for I in {A,B,C,D,ME}; do pdflatex exercises-topic1-$$I ; done'
#bash -c 'for I in {A,B,C,D,ME}; do pdflatex exercises-topic2-$$I ; done'