2

Minimum Working Example

\documentclass{article}
\usepackage{kpfonts}
\usepackage{environ}

\makeatletter
\AtBeginDocument{%
    \begingroup
        \newwrite\@bibmeta
        \immediate\openout\@bibmeta=\jobname.meta-bib\relax
    \endgroup
}

\NewEnviron{wwwabstract}{%
    \begin{abstract}
        \BODY
    \end{abstract}
    \immediate\write\@bibmeta{abstract = {\BODY},}
}
\makeatother

\begin{document}
    \begin{wwwabstract}
        Test $R_1^2$
    \end{wwwabstract}

\end{document}

Compiles fine.

Minimum Non-Working Example

\documentclass{article}
\usepackage{kpfonts}
\usepackage{environ}

\makeatletter
\AtBeginDocument{%
    \begingroup
        \newwrite\@bibmeta
        \immediate\openout\@bibmeta=\jobname.meta-bib\relax
    \endgroup
}

\NewEnviron{wwwabstract}{%
    \begin{abstract}
        \BODY
    \end{abstract}
    \immediate\write\@bibmeta{abstract = {\BODY},}
}
\makeatother

\begin{document}
    \begin{wwwabstract}
        Test $\mathbf{R}_1^2$
    \end{wwwabstract}

\end{document}

Gives the following error in the log file:

LaTeX Font Info:    Try loading font information for U+jkpsyb on input line 24.


(/usr/share/texmf-dist/tex/latex/kpfonts/ujkpsyb.fd
File: ujkpsyb.fd 2006/08/15 v1.0
)
LaTeX Font Info:    Try loading font information for U+jkpsyc on input line 24.


(/usr/share/texmf-dist/tex/latex/kpfonts/ujkpsyc.fd
File: ujkpsyc.fd 2006/08/15 v1.0
)
! Undefined control sequence.
\GenericError  ...                                
                                                    #4  \errhelp \@err@     ...
l.24    \end{wwwabstract}

? q
OK, entering \batchmode...
! Undefined control sequence.
\GenericError  ...                                
                                                  \let \@err@               ...
l.24    \end{wwwabstract}

The control sequence at the end of the top line
of your error message was never \def'ed. If you have
misspelled it (e.g., `\hobx'), type `I' and the correct
spelling (e.g., `I\hbox'). Otherwise just continue,
and I'll forget about whatever was undefined.

! Undefined control sequence.
\GenericError  ...                                
                                                  \@empty \def \MessageBreak...
l.24    \end{wwwabstract}

The control sequence at the end of the top line
of your error message was never \def'ed. If you have
misspelled it (e.g., `\hobx'), type `I' and the correct
spelling (e.g., `I\hbox'). Otherwise just continue,
and I'll forget about whatever was undefined.

! Undefined control sequence.
\GenericError  ...                                
                                                     \endgroup 
l.24    \end{wwwabstract}

The control sequence at the end of the top line
of your error message was never \def'ed. If you have
misspelled it (e.g., `\hobx'), type `I' and the correct
spelling (e.g., `I\hbox'). Otherwise just continue,
and I'll forget about whatever was undefined.

 [1

{/var/lib/texmf/fonts/map/pdftex/updmap/pdftex.map}] (./test.aux) ) 

The same code worked about a month ago. I think I did update some of the TeXLive packages, but I am not sure what broke. Any thoughts?

  • kpfonts has nothing to do with the issue. If I comment it out, the error is exactly the same. – egreg Aug 28 '18 at 21:36
3

kpfonts has nothing to do with the issue.

The problem is having commands such as \mathbf in \write (they don't survive it), here solved by making the first level expansion of \BODY unexpandable.

\documentclass{article}
%\usepackage{kpfonts}
\usepackage{environ}

\makeatletter
\newwrite\@bibmeta
\immediate\openout\@bibmeta=\jobname.meta-bib

\NewEnviron{wwwabstract}{%
    \begin{abstract}
        \BODY
    \end{abstract}
    \immediate\write\@bibmeta{abstract = {\unexpanded\expandafter{\BODY}},}
}
\makeatother

\begin{document}
    \begin{wwwabstract}
        Test $\mathbf{R}_1^2$
    \end{wwwabstract}

\end{document}

The group around \newwrite and \openout is useless. There's no need for \AtBeginDocument.

A simpler solution is to use kernel functions, in this case \protected@write. No \immediate is necessary, provided we expand \BODY one level.

\documentclass{article}
%\usepackage{kpfonts}
\usepackage{environ}


\makeatletter
\newwrite\@bibmeta
\openout\@bibmeta=\jobname.meta-bib
\newcommand\abstract@write[1]{\protected@write\@bibmeta{}{abstract = {#1}}}

\NewEnviron{wwwabstract}{%
    \begin{abstract}
        \BODY
    \end{abstract}
    \expandafter\abstract@write\expandafter{\BODY}%
}
\makeatother

\begin{document}
    \begin{wwwabstract}
        Test $\mathbf{R}_1^2$
    \end{wwwabstract}

    \begin{wwwabstract}
        Test $\mathbf{R}_1^3$
    \end{wwwabstract}

\end{document}

An expl3 version that exploits the fact that \iow_now:Nn performs no expansion at all.

\documentclass{article}
%\usepackage{kpfonts}
\usepackage{xparse,environ}

\ExplSyntaxOn
\NewEnviron{wwwabstract}
 {
  \begin{abstract}
  \BODY
  \end{abstract}
  \wong_abstract_write:V \BODY
 }

\iow_new:N \g_wong_abstract_stream
\iow_open:Nn \g_wong_abstract_stream { \c_sys_jobname_str.meta-bib }

\cs_new_protected:Nn \wong_abstract_write:n
 {
  \iow_now:Nn \g_wong_abstract_stream { #1 }
 }
\cs_generate_variant:Nn \wong_abstract_write:n { V }
\ExplSyntaxOff

\begin{document}
    \begin{wwwabstract}
        Test $\mathbf{R}_1^2$
    \end{wwwabstract}

    \begin{wwwabstract}
        Test $\mathbf{R}_1^3$
    \end{wwwabstract}

\end{document}
  • Concerning the second solution: (1) Why to “pre-expand” \BODY? Isn’t it going to be expanded within \protected@write itself? (2) I think that, in the definition of \abstract@write, saying \protected@write\@bibmeta{\let\par\relax}{abstract = {#1}} could be advisable in this case. – GuM Aug 30 '18 at 22:08
  • @GuM You're probably right about preexpanding \BODY, but I think this is conceptually better. I don't think that \let\par\relax is needed. Anyway, I think that the \unexpanded solution is better. – egreg Aug 31 '18 at 8:27
1

Of course, @egreg’s answer is fine, but you would have avoided the problem if you had been well-behaved (:-) and had followed the recommended practices about how to write \protected commands to a file: that is, you have to set \protect equal either to \noexpand or to \@unexpandable@protect. In this case, I think the former is more adequate. I also prefer to open custom files at “\begin{document}-time” and to neatly close them at the end of the document:

\documentclass{article}
% \usepackage{kpfonts}
\usepackage{environ}

\makeatletter

\AtBeginDocument{%
    \newwrite\@bibmeta
    \immediate\openout\@bibmeta=\jobname.meta-bib\relax
}

\AtEndDocument{%
    \immediate\closeout\@bibmeta
}

\NewEnviron{wwwabstract}{%
    \begin{abstract}
        \BODY
    \end{abstract}
    \let\protect=\noexpand
    \immediate\write\@bibmeta{abstract = {\BODY},}
}

\makeatother

\newcommand*{\this}{this command}



\begin{document}
    \begin{wwwabstract}
        Test $\mathbf{R}_1^2$;
        but let's say that you want to expand \this.
    \end{wwwabstract}

\end{document}

This solution expands the \BODY macro, except that robust commands are written as their own name, followed by spaces. It’s up to you to decide whether you need to prevent expansion of robust commands only (expanding all the rest, as this solution does) or of the \BODY macro as a whole (as @egreg’s solution does).

(The main reason why I wrote this answer is that, reading @egreg’s, one might think “Hey, how comes \mathbf doesn’t survive a \write operation? Isn’t it robust?”.)

  • I wouldn't advertise \let\protect\noexpand; the correct method (used by \protected@write) is \let\protect\@unexpandable@protect. – egreg Aug 29 '18 at 8:19
  • /Slaps forehead/ Ah! Now I remember. I actually knew/fixed this before by robustifying a bunch of my macros. (I had a git commit from Oct of 2017 doing just this. It took me a few days to figure it out last time.) I was fixing a different bug and carelessly copied over an old version of my macro file without the robustified commands. Thanks! Now I know it is entirely my own stupidity that suddenly broke the code, not something that changed in TeX. – Willie Wong Aug 29 '18 at 12:56
  • I ended up accepting egreg's answer: what you did here is much more akin to what I did in October last year. Thinking about my use case, however, it is probably much more useful to me personally to have all commands not expand in the write; hence egreg's first solution is the one I will adopt going forward. Thanks again! – Willie Wong Aug 29 '18 at 13:11
  • @egreg: For once, I beg to disagree with you, at least in part: \protected@write uses a deferred (non-\immediate) \write, but “pre-expands” its third argument completely, except for the c.s. \thepage; other tokens can be made to behave exceptionally via the second argument—as you know very well. So, \let\protect\@unexpandable@protect is needed to make \protect survive this preliminary expansion, but when the \write actually takes place, \protect is \let equal to \noexpand (see definition of \@outputpage). On the other hand… – GuM Aug 30 '18 at 21:58
  • …when one uses an \immediate \write, it perfectly makes sense to \let \protect = \noexpand, exactly as \@outputpage does, to avoid that \protect survives in the file too, which can be advisable or not, depending on the circumstances. Pretty subtle considerations may be involved, and I think that the choice of one solution over the other ultimately depends on what the written-on file will be used for. – GuM Aug 30 '18 at 21:58

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.