問題点
- 1ページを超えるソースコードを code-block や literalinclude ディレクティブで配置.
- さらに caption オプションを指定する.
- これを latexpdfja でビルド
この時
- ソースコードが適切に改行されない.
- caption を指定しなければ一応問題ない
- sphinx 1.3.1 + python 3.4.3 + windows 10
とりあえずの結論-Sphinx拡張, 並びに設定追加
code-block 並びに literalincude に caption がついた場合, 全体をfloatで囲わず, captionof(capt-of パッケージ) でキャプションを挿入する用に変更.
またそのために newfloat, needspaceパッケージを利用する. 最近のTeXLive, MikeTeXではこれらが標準で利用できるはず.
コードは, github (https://github.com/takaakiaoki/sphinx_latexnewfloat) に置きました.
メモ
原因
検証した .rst ファイルは次の通り.
################################
Including long sourcecode
################################
literalinclude without caption
==================================
.. literalinclude:: bodeplot_lpf.m
:language: Octave
literalinclude with caption
===================================
.. literalinclude:: bodeplot_lpf.m
:language: Octave
:caption: Caption
make pseudoxml とすると, このファイルから作られた .doctree を pseudoxml でわかりやすく出力する
<!-- 前略 -->
<section ids="including-long-sourcecode" names="including\ long\ sourcecode">
<title>
Including long sourcecode
<section ids="literalinclude-without-caption" names="literalinclude\ without\ caption">
<title>
literalinclude without caption
<literal_block highlight_args="{'linenostart': 1}" language="Octave" linenos="False" source="bodeplot_lpf.m" xml:space="preserve">
<!-- ここからコード(省略) -->
<section ids="literalinclude-with-caption" names="literalinclude\ with\ caption">
<title>
<container classes="literal-block-wrapper" dupnames="caption" ids="caption" literal_block="True">
<caption>
Caption
<literal_block highlight_args="{'linenostart': 1}" language="Octave" linenos="False" source="bodeplot_lpf.m" xml:space="preserve">
<!-- ここからインクルードされたコード(省略) -->
とこのように, :caption: がつくことで ,
これを受けて, latexのソースファイルは
% (前略)
\chapter{Including long sourcecode}
\label{code:welcome-to-latex-long-literalinclude-s-documentation}\label{code::doc}\label{code:including-long-sourcecode}
\section{literalinclude without caption}
\label{code:literalinclude-without-caption}
\begin{Verbatim}[commandchars=\\\{\}]
%%% ここにインクルードされたコード(省略)
\end{Verbatim}
% 次のセクション
\section{literalinclude with caption}
\label{code:literalinclude-with-caption}
\begin{literal-block}
\caption{Caption}
\begin{Verbatim}[commandchars=\\\{\}]
%%% ここにインクルードされたコード(省略)
\end{Verbatim}
\phantomsection\label{code:caption}
\end{literal-block}
% (後略)
と外部に literal-block 環境(と caption)が追加される. この literal-block 環境は
sphinx.sty において,
% Define literal-block environment
\RequirePackage{float}
\floatstyle{plaintop}
\ifx\thechapter\undefined
\newfloat{literal-block}{htbp}{loc}[section]
\else
\newfloat{literal-block}{htbp}{loc}[chapter]
\fi
\floatname{literal-block}{List}
と float パッケージを用いて定義されている. この float 環境では改頁が行われない.
いい加減な回避方法
-
caption オプションをつけない
-
latexの場合にシンタックスハイライティングを行わない
.. only:: html
.. literalinclude:: bodeplot_lpf.m
:language: Octave
:caption: Caption
.. only:: latex
.. include:: bodeplot_lpf.m
:literal:
回避方法, 改造
newfloat, capt-of の利用
「float環境で改頁ができない」という問題については,
http://tex.stackexchange.com/questions/175650/how-to-allow-page-break-inside-a-float-environment において, capt-of 並びに newfloat パッケージを使うとよい, とのこと.
capt-of, newfloat 共に最近のTeXLiveに含まれているので, LaTeXの環境に手を入れる必要がない. 本来ならば, sphinx.sty で定義されている literal-block 環境をいったんリセットし, newfloat の DeclareFloatingEnvironment で再定義する方がよいが, よい方法が見つけられていない.
% (前略)
%
% configure new literal-block-newfloat
\usepackage{newfloat}
\DeclareFloatingEnvironment[name={Listing}]{literal-block-newfloat}
% copy from sphinx.sty
\ifx\thechapter\undefined
\SetupFloatingEnvironment{literal-block-newfloat}{within=section,placement=h}
\else
\SetupFloatingEnvironment{literal-block-newfloat}{within=chapter,placement=h}
\fi
\usepackage{capt-of}
%
% (中略)
%
% 次のセクション
\section{literalinclude with caption}
\label{code:literalinclude-with-caption}
\captionof{literal-block-newfloat}{Caption}
\begin{Verbatim}[commandchars=\\\{\}]
%%% ここにインクルードされたコード(省略)
\end{Verbatim}
\phantomsection\label{code:caption}
% \end{literal-block}
% (後略)
latex writer の改良
上記追加部分の \usepackage 周辺は, conf.py の latex_elements['preamble'] を設定すれば良い. 一方, コード取り込み部分は,
- \begin{literal-block} ... \end{literal-block} を出力しない
- \caption{ ... } の代わりに \captionof{literal-block-newfloat}{ ... } に置き換える.
この部分の出力は,
- sphinx.writer.latex.LaTeXTranslator.visit_containar
- sphinx.writer.latex.LaTeXTranslator.depart_containar
で実施される.
def visit_container(self, node):
if node.get('literal_block'):
ids = ''
for id in self.next_literal_ids:
ids += self.hypertarget(id, anchor=False)
if node['ids']:
ids += self.hypertarget(node['ids'][0])
self.next_literal_ids.clear()
self.body.append('\n\\begin{literal-block}\n')
self.context.append(ids + '\n\\end{literal-block}\n')
def depart_container(self, node):
if node.get('literal_block'):
self.body.append(self.context.pop())
これを見ると 1. は簡単に出来そう(実際できた). 2. は visit_caption, depart_caption で共通化されているので, "visit_caption ノードが visit_container 経由で呼ばれた場合は処理を変える" ことが必要になる(in_caption を参考に in_container_literal_block を追加).
いろいろ感想
- なんかかんやで sphinx の簡単な拡張が書けた.
- \captionof 直後に改頁されるとダサいので, needspace パッケージにより\captionof を実行するために必要なスペースを確認する様にした. latexnewfloat_needspaceforcaption オプションがつくことに
- \captionof 直前のスペースが狭いので \vskip{0.5\baselineskip} を追加. このあたりはアドホックなのでおいおい調整
- literal-block-newfloat 環境初期化を conf.py のプリアンブルに記載するのはいまいち. writer か translator の中に入れるのが妥当. まだ builder translator writer の関係がよくわからない.