Acetaminophen’s diary

化学に関すること,TeXに関すること,ゆきだるまに関すること。

LaTeX でパッケージが衝突したときは(2)

前回の続きです。今回は、改善版パッケージを用いるのではなく自力で対処せざるをえない場合について考えてみます。サンプルソースはやはり GitHub に置いておきます。

 

3. クラスファイルの定義とパッケージが衝突する例

次の2つの例は、以前このブログで取り上げた「マルチメディア埋め込み PDF 作成」(1), (2) で出くわした問題です。サンプル画像や動画はここには提示しませんので、タイプセットを試す場合は適宜準備してください*1

animate パッケージの例:

\documentclass{jsarticle}
\usepackage[dvipdfmx]{graphicx}
\usepackage[dvipdfmx]{animate}
\begin{document}
\animategraphics[height=2.8in,autoplay,controls,loop]{12}{wave_}{000}{015}
\end{document}
! LaTeX Error: Command \ifdraft already defined.
               Or name \end... illegal, see p.192 of the manual.

See the LaTeX manual or LaTeX Companion for explanation.
Type  H   for immediate help.
 ...                                              
                                                  
l.56 }
      
? 

movie15 パッケージの例(media9 なら問題なし):

\documentclass[12pt]{jsarticle}
\usepackage[dvipdfmx]{hyperref} % \movieref を使う場合に必要
\usepackage[dvipdfmx]{movie15_dvipdfmx}
\begin{document}
\includemovie{80mm}{60mm}{movie1501.mp4}
\end{document}
! LaTeX Error: Command \ifdraft already defined.
               Or name \end... illegal, see p.192 of the manual.

See the LaTeX manual or LaTeX Companion for explanation.
Type  H   for immediate help.
 ...                                              
                                                  
l.56 }
      
? 

これらのエラーは、jsarticle クラスで既に \ifdraft が定義されているにもかかわらず、animate パッケージや movie15 パッケージが内部で読み込む ifdraft.sty にて \ifdraft が定義されようとすることが原因です。

対処法:重複定義を事前に無効化

この問題に対する対処法として、例えばこちらなどで「ifdraft.sty の定義を書き換える」という方法が紹介されています*2。しかし、これはあまり好ましくありません。というのも、ソースを別の TeX 環境でタイプセットする場合に再びパッケージを書き換える必要がありますし、一般論として配布中のパッケージを勝手に書き換えることはライセンス上も問題になる場合があります*3

より適切な対処法は、ソース中で ifdraft.sty が読み込まれる時点で \ifdraft の定義を無効化しておくことでしょう。

\documentclass{jsarticle}
\let\ifdraft\relax
\usepackage[dvipdfmx]{graphicx}
\usepackage[dvipdfmx]{animate}
\begin{document}
\animategraphics[height=2.8in,autoplay,controls,loop]{12}{wave_}{000}{015}
\end{document}

\let は TeX のプリミティブで、ここでは \relax すなわち「何もしない」という動作を \ifdraft にコピーしています。こうすれば、ifdraft.sty を書き換えることなく処理できます。同様に

\documentclass[12pt]{jsarticle}
\let\ifdraft\relax
\usepackage[dvipdfmx]{hyperref} % \movieref を使う場合に必要
\usepackage[dvipdfmx]{movie15_dvipdfmx}
\begin{document}
\includemovie{80mm}{60mm}{movie1501.mp4}
\end{document}

のようにします(なお、movie15 と hyperref の順序を逆にした場合に発生するエラーについては本記事の対象外です)。

備考:ちなみに、前回の最初の例としてとりあげた txfonts, pxfonts のエラーも

\documentclass{jsarticle}
\usepackage{txfonts}
\let\iint\relax
\let\iiint\relax
\let\iiiint\relax
\let\idotsint\relax
\usepackage{amsmath}
\begin{document}
数式の例:$\iint dx dy, \iiint dx dy dz$
\end{document}

のようにすれば発生しませんが、これではせっかくの積分記号のフォント再定義がキャンセルされてしまい(デフォルトのフォントが使われるだけ)、統一感が失われます。最初に説明したとおり、パッケージの順番で対処しましょう。

 

4. クラスファイルで読まれたパッケージが原因で衝突する例

学会クラスファイルなどの改変不能なクラスファイルの中であるパッケージを読み込んでいて、それが原因でユーザがパッケージを読み込もうとしたときにエラーが出る例です。例えば、こちらに置いた jserror.cls が改変不能であるとしましょう*4。これを使うと、次のソースがエラーになります。

\documentclass{jserror}
\usepackage{amsmath}
\begin{document}
数式の例:$\iint dx dy, \iiint dx dy dz$
\end{document}
! LaTeX Error: Command \iint already defined.
               Or name \end... illegal, see p.192 of the manual.

エラー内容は最初の例と同じですが、今度はクラスファイル内で必須として読まれてしまっているため、このままでは amsmath パッケージを読み込むことができません。

対処法:クラスファイルで特定パッケージだけ読ませない

こちらのブログ記事で提案された方法です。この例ではクラスファイル内で txfonts パッケージを読もうとした時だけ、パッケージを読む \RequirePackage という命令を無効化します。

\RequirePackage{letltxmacro}
\RequirePackage{xstring}
\LetLtxMacro\latexRequirePackage\RequirePackage
\renewcommand\RequirePackage[2][]{\IfStrEq{#2}{txfonts}{}{\latexRequirePackage[#1]{#2}}}
\documentclass{jserror}
\usepackage{amsmath}
\latexRequirePackage{txfonts}
\begin{document}
数式の例:$\iint dx dy, \iiint dx dy dz$
\end{document}

この方法では、いま読もうとしているパッケージが txfonts であるかどうか文字列判定するために xstring パッケージを使い、さらに元の \RequirePackage コマンドを保持するために letltxmacro パッケージの \LetLtxMacro を利用しています。

ここで注意する点があります(これは元記事には書かれていませんでした)。例えば \RequirePackage を txfonts に対して無効化した場合、その後同じ txfonts を \usepackage しようとしても無視されます*5。このため、わざわざ \RequirePackage で例外処理したパッケージを本当に使いたい場合(今回のようなパッケージ読み込み順だけが問題となるときなど)は、先ほど元の \RequirePackage コマンドを保持したときに定義した \latexRequirePackage を使う必要があります。

 

5. 複数パッケージの重複コマンドを両方使いたい

パッケージ A とパッケージ B で「同名だが定義内容の全く異なるマクロ」が定義されている場合です。こうした場合、どちらも便利なので使いたいとすると、上で紹介した \relax による無効化が使えません。

こうした場合の対処法は場合によりけりですが、困ったときはまず noconflict パッケージを試してみましょう。たいていの場合、noconflict パッケージで片方のマクロの名称を変更すれば対処できるはずです。使い方は既にこちらの記事に書いてあります*6。場合によってはうまくいかないこともあるとは思いますが、置き換えマクロの簡単なものには対処できるはずです。

 

以上、5つのケースについてまとめてみました。今後も、以上の方法では対処不能なパッケージ衝突例やより解りやすい実例を募集中です。

*1:とはいえ、今回の目的であるエラー発生と対処法を学ぶだけであれば、本文のソースは「あいうえお」でも何でもかまいませんが…

*2:私の環境では、kpsewhich ifdraft.sty を実行すると
c:/w32tex/share/texmf-dist/tex/latex/oberdiek/ifdraft.sty
にあることがわかります。この中の \newcommand*{\ifdraft}{% という行を \def\ifdraft{% に書き換えることで、jsarticle による定義を無理やり ifdraft.sty によって上書きしようということです。

*3:今回の ifdraft.sty の場合は LaTeX Project Public Li­cense 1.3 に従っており、個人利用の範囲内での書き変えは制限されていません(改変版の再配布は禁止)。しかし、ライセンスにもある通り「書き変えたことを忘れて再配布してしまう」という可能性がある以上、基本的に書き変えないのが好ましいでしょう。書き変えや再配布の可否については個別のパッケージのライセンスを確認してからにしましょう。

*4:問題をどこでもすぐに簡単に再現できる例を思いつかなかったので、即席で「jsarticle クラスを読み込み、その後で txfonts パッケージを読ませる」というだけのクラスファイルを作ってみました。

*5:これは \usepackage が \documentclass によって以降は \RequirePackage のコピーとなるからです。…といっても少々分かりにくいので、TeX/LaTeX StackExchange の回答を参照してください。

*6:ちなみに、この記事ではわざわざ同名マクロを定義したパッケージを無理やり作って実例を示したのですが、何か簡単な実例を随時募集中です。