5

In this MWE, combining newtxtext and microtype leads to a compilation error (Extra \else). This particular example is inspired by the svjour3 document class, but I reduced it to the article class and a simple redefinition of \normalsize. You would think that an \iftrue ... \else\fi construction should not change much compared to just ..., yet it does:

\documentclass{article}
\let\oldnormalsize\normalsize

% this works!
%\let\normalsize\oldnormalsize

% this works!
%\renewcommand\normalsize{\oldnormalsize}

% this works!
%\renewcommand\normalsize{\iffalse\else\fi\oldnormalsize}

% inspired by svglov3.clo. Does not work!
%\renewcommand\normalsize{\iffalse\else\oldnormalsize\fi}

% Does not work, either!
\renewcommand\normalsize{\iftrue\oldnormalsize\else\fi}

\usepackage{newtxtext}
\usepackage{microtype}
\begin{document}
    Anything
\end{document}

Is that a bug in newtxtext or microtype, or are the svjour3 developers doing something that should be avoided? I cannot really see what that might be.

Inverting the order of microtype and newtxtext gets rid of that error, by the way. I have not found anything helpful on that topic, but I wonder whether microtype should be loaded only after all font packages have been loaded. A quick test suggests that in my longer document, the order of the two packages does not matter.

The problem can be isolated to this MWE (see my answer for details, I did not want to flood this question). Removing any of the three unneeded iftrue, \iffalse constructions fixes the problem, too, as does reordering the \ifs in \ShowError. (I can only reproduce this with \iffalse surrounded by an \iftrue.)

\documentclass{article}
\makeatletter

\newcommand{\CommandOne}[3]{#1,#2,#3}
\newcommand{\CommandTwo}{\iftrue\CommandOne{1}{2}{3}\else\fi}
% this works:
% \renewcommand{\CommandTwo}{\CommandOne{1}{2}{3}}

\def\ShowError{
    \iftrue
        \iffalse
        \else
            \begingroup
                \def\CommandOne##1##2##3\@nil{\endgroup}
                \CommandTwo\@nil
        \fi
    \else
    \fi
}
\ShowError

\begin{document}
    X
\end{document}
Schweinebacke
  • 26,336
bers
  • 5,404
  • 2
    The error happens when microtype.cfg is doing \DeclareMicrotypeSet{basicmath}{...} and is probably a by-product of how xkeyval manages keys and values. On the other hand, a redefinition of \normalsize (if it's the right thing to do in the first place), should go after packages have been loaded. – egreg Jul 20 '16 at 17:16
  • Possible duplicate of http://tex.stackexchange.com/questions/24853/incompatibility-between-svjour3-and-microtype – Christian Clason Jul 20 '16 at 18:14
  • I feel this question (especially the answer) has become much more general than that other question marked as a duplicate. – bers Jul 20 '16 at 22:43
  • 1
    \renewcommand\normalsize{\iftrue\expandafter\oldnormalsize\else\fi} fixes your first MWE. Likewise, \renewcommand\normalsize{\iffalse\else\expandafter\oldnormalsize\fi} – Steven B. Segletes Jul 21 '17 at 17:46
  • 1
    In your 2nd MWE, this works: \newcommand{\CommandTwo}{\iftrue\def\next{\CommandOne{1}{2}{3}}\else\def\next{}\fi\next} – Steven B. Segletes Jul 21 '17 at 17:52
  • @StevenBSegletes thanks, this might be the answer indeed. It seems this could/should be taken to the svjour3 maintainers. – bers Jul 28 '17 at 05:24

2 Answers2

3

With only the small MWE you already produced this becomes quite explainable:

First lets take a look at the context this results in. The following is just the stuff this results in which I've replaced each macro with its replacement text indented by 6 additional spaces (in case of a macro inside of an \if-block this results in 8, as I indent those by 2 spaces):

\iftrue
        \iffalse
        \else
                \iffalse
                \else
                        \begingroup
                        \def\set@fontsize#1\@nil{\endgroup}%
                              \iftrue
                                \normalsize
                              \else
                              \fi
                        \@nil
                \fi
        \fi
\else
\fi

Now \normalsize expands to some checks and then \set@fontsize, so if we ignore the checks (and the usual arguments to \set@fontsize as they don't matter here) this becomes:

\iftrue
        \iffalse
        \else
                \iffalse
                \else
                        \begingroup
                        \def\set@fontsize#1\@nil{\endgroup}%
                              \iftrue
                                      \set@fontsize
                              \else
                              \fi
                        \@nil
                \fi
        \fi
\else
\fi

If we now just expand our temporary definition of \set@fontsize without evaluating all the \ifs and \elses before, it'll remove everything up to and including \@nil and instead leaves an \endgroup, so we get:

\iftrue
        \iffalse
        \else
                \iffalse
                \else
                        \begingroup
                        \def\set@fontsize#1\@nil{\endgroup}%
                              \iftrue
                                      \endgroup
                \fi
        \fi
\else
\fi

Now lets remove the contents of \begingroup...\endgroup as they aren't interesting anymore except for the contained \iftrue:

\iftrue
        \iffalse
        \else
                \iffalse
                \else
                              \iftrue
                \fi
        \fi
\else
\fi

And now the issue becomes apparent. Let's just re-indent this to be more readable:

\iftrue
  \iffalse
  \else
    \iffalse
    \else
      \iftrue
      \fi
    \fi
  \else
  \fi

Next, we'll remove matching pairs of \if...\else...\fi blocks, first the inner-most \iftrue:

\iftrue
  \iffalse
  \else
    \iffalse
    \else
    \fi
  \else
  \fi

Now the inner-most \iffalse:

\iftrue
  \iffalse
  \else
  \else
  \fi

and we arrive at the Extra \else.

The issue is that your temporary \set@fontsize-definition will have removed important bits of an \if-construct and the result you get after this will be unbalanced \if-\else-\fi instructions.

Skillmon
  • 60,462
2

I'll summarize here what I found starting with @egreg's answer. The MWE below is what I could reduce it to, independent of svjour3, newtxtext and microtype, by tediously following the sequence of executed functions. What I end up not understanding is this \MT@get@size from microtype, which looks like this:

\def\MT@get@size{%
  ...
  \ifx\@tempa\relax \else
    \begingroup
      \def\set@fontsize##1##2##3##4\@nil{\endgroup\def\MT@val{##2}}%
      \@tempa\@nil
  \fi
  ...
}

This looks nasty to me: I have no clue why \begingroup/\endgroup and {,} appear interleaved there. (Turns out it is this: Explanations about \begingroup\edef\x{\endgroup)

So in some sense, keyval does not like the \begingroup trick in combination with the \iftrue ... \else\fi, but I have not been able to isolate this any further.

\documentclass{article}
\usepackage{keyval}

\newcommand{\fancysize}{\iftrue\normalsize\else\fi}

\makeatletter
\define@key{foo}{bar}[]{
    \begingroup
        \def\set@fontsize##1##2##3##4\@nil{\endgroup}
        \fancysize
        \@nil
    }
\setkeys{foo}{bar}

\begin{document}
    X
\end{document}

Interestingly, removing \@nil works:

\define@key{foo}{bar}[]{
    \begingroup
    \def\set@fontsize##1##2##3##4{\endgroup}
    \fancysize
}

What are those \@nil for? (Answer: Macro delimiter)

Also, moving \@nil inside the \iftrue...\else\fi works:

\define@key{foo}{bar}[]{
    \begingroup
    \def\set@fontsize##1##2##3##4\@nil{\endgroup}
    \iftrue\normalsize\@nil\else\fi
}

This suggests that in the non-working case, \normalsize\else\fi\@nil becomes ...\set@fontsize...\else\fi\@nil, so \else\fi become parameters to \set@fontsize, where they don't match anything. It is really the combination of the \begingroup\def trick, macro delimiters, and a conditional expression. And all this is independent of much of keyval:

\documentclass{article}
\makeatletter

% similar to svjour3:
\newcommand{\fancysize}{\iftrue\normalsize\else\fi}

% isolated from microtype and keyval
\def\KV@foo@bar@default{
    \begingroup
        \def\set@fontsize##1##2##3##4\@nil{\endgroup}
        \fancysize
        \@nil
    }

% isolated from keyval
\def\KV@default{
    \iffalse\else
        \KV@foo@bar@default
    \fi
}

\def\KV@split{
    \iftrue
        \KV@default
    \else\fi
}

\KV@foo@bar@default % works!
\KV@default % works!
\KV@split % fails!

\begin{document}
    X
\end{document}

I wonder if that gives anyone a clue how to solve this in the general case...

% \documentclass{svjour3}

% \usepackage{newtxtext}
% \usepackage[config=foo]{microtype}

% \DeclareMicrotypeSet{}{size={normalsize}}

% \MT@DeclareSet{}{size={normalsize}}

% \renewcommand\MT@DeclareSet[3][]{\MT@map@clist@c\MT@features{{\MT@declare@sets{##1}{#2}{#3}}}}
% \MT@DeclareSet{}{size={normalsize}}

% \MT@map@clist@c\MT@features{{\MT@declare@sets{#1}{}{size={normalsize}}}}

% \MT@map@clist@c\MT@features{{\MT@declare@sets{#1}{}{size={normalsize}}}}

% \MT@exp@one@n\MT@map@clist@n\MT@features{{\MT@declare@sets{#1}{}{size={normalsize}}}}

% \MT@exp@one@n\MT@map@clist@n\MT@features{{\MT@declare@sets{#1}{}{size={normalsize}}}}

% \expandafter\MT@map@clist@n\expandafter{pr,ex,sp,kn,tr}{{\MT@declare@sets{#1}{}{size={normalsize}}}}

% \def\MT@map@clist@n#1#2{\def\MT@clist@function##1{#2}\MT@map@clist@#1,\@nil,\@nnil}
% \expandafter\MT@map@clist@n\expandafter{pr,ex,sp,kn,tr}{{\MT@declare@sets{#1}{}{size={normalsize}}}}

% \def\MT@clist@function#1{{\MT@declare@sets{#1}{}{size={normalsize}}}}
% \MT@map@clist@ pr,ex,sp,kn,tr,\@nil,\@nnil

% \def\MT@clist@function#1{{\MT@declare@sets{#1}{}{size={normalsize}}}}
% \MT@map@clist@ pr,\@nil,\@nnil

% \def\MT@clist@function#1{{\MT@declare@sets{#1}{}{size={normalsize}}}}
% \MT@clist@function{pr}

% \MT@declare@sets{pr}{}{size={normalsize}}

% \def\MT@declare@sets#1#2#3{\setkeys{MT@#1@set}{#3}}
% \MT@declare@sets{pr}{}{size={normalsize}}

% \setkeys{MT@pr@set}{size={normalsize}}

% \define@key{foo}{size}[]{\MT@map@clist@n{#1}{\def\MT@val{##1}\expandafter\MT@get@range\MT@val--\@nil}}
% \setkeys{foo}{size={normalsize}}

% \define@key{foo}{size}{\MT@map@clist@n{#1}{\def\MT@val{##1}\expandafter\MT@get@range\MT@val--\@nil}}
% \setkeys{foo}{size={normalsize}}

% \define@key{foo}{size}{\def\MT@val{normalsize}\expandafter\MT@get@range\MT@val--\@nil}
% \setkeys{foo}{size={normalsize}}

% \define@key{foo}{bar}{\def\MT@val{normalsize}\expandafter\MT@get@range\MT@val--\@nil}
% \setkeys{foo}{bar=foobar}

% \define@key{foo}{bar}[]{\MT@get@range normalsize--\@nil}
% \setkeys{foo}{bar}

% \def\MT@get@range#1{\def\MT@val{#1}\MT@get@size}
% \define@key{foo}{bar}[]{\MT@get@range{normalsize}}
% \setkeys{foo}{bar}

% \define@key{foo}{bar}[]{\def\MT@val{normalsize}\MT@get@size}
% \setkeys{foo}{bar}

% \define@key{foo}{bar}[]{\MT@let@cn\@tempa{normalsize}\iffalse\else\begingroup\def\set@fontsize##1##2##3##4\@nil{\endgroup\def\MT@val{##2}}\@tempa\@nil\fi}
% \setkeys{foo}{bar}
bers
  • 5,404
  • Can you edit this so it is clearly answer? The ending sounds as if it is still part of the question, which is really confusing. (If it is part of the question, it should be part of the question. Or another question. Or some question-thing somewhere ....) – cfr Jul 22 '17 at 02:38