【2025/09/17 追記】 そもそも Jupyter Notebook → PDF 変換方法の比較表は以下を参照ください (このページの TeX 経由方式はノートブック上で HTML をレンダリングしてる場合には不向きです)。
Jupyter Notebook → PDF 変換方法の比較表 + Playwright 経由変換例
Jupyter Notebook を PDF にするには、参考文献 [1] でお勧めされている「File → Print → PDF に保存」が便利で高速です。しいてこの方法で手の届かない点を挙げると、
- ブラウザから他のページを PDF に保存するときに余白や倍率をカスタマイズした場合、再度 Jupyter Notebook 向けに直さなければならないが、いつも固定したい。
- PDF のヘッダ位置やフッタ位置に好きな文字列を入れるのは難しい。
- 例. 各ページのヘッダに自分の名前を表示したい。
- 例. 先頭ページのヘッダに「関係者外秘」と表示したい。
があると思います。これらは、「File → Save and Export Notebook As → PDF」での変換なら、テンプレートによって解決できます。ただし、こちらの方法での PDF 変換は TeX を経由するため xelatex が必要で、しかも TeX 並みに時間がかかるため、元々 TeX を入れていないような人に「特にお勧めするものではない [1]」とは思います。
「それを踏まえても TeX 経由で上記の手の届かない点を解消したい」場合向けに、この記事は PDF 変換時のテンプレートの修正例を示します。以下の修正をします。
- デフォルトで
\maketitleでタイトル (ファイル名) と日付が出力されるのを抑制する。 - デフォルトで見出しに番号が振られるのを抑制する。
- 余白を調整する。
- フォントを変更する。
- さらにヘッダを指定する (1ページ目のみ or 毎ページ)。
この記事のテンプレート修正は投稿時点の nbconvert 7.16.6 に準拠しています。
【2025/09/16 追記】 この記事の方法で出力した PDF からコードをコピペするとインデントの空白が消えて不便な場合があります。一つの対処法は以下を参照ください。
PDF 変換した Jupyter Notebook からコードをコピーするとインデントの空白がコピーできない問題への対処法 - Qiita
参考文献
- Jupyter Notebook を pdf として保存する - HEROIC 2021: 日本語を含む Notebook の PDF 変換方法が載っていますが、そもそも印刷プレビューが速いことも載っています。
- nbconvert/share/templates/latex/base.tex.j2 at main · jupyter/nbconvert: この記事で主に書き換えるテンプレートです。
【STEP0】前提
手元のマシンに xelatex がインストールされていて、パスが通っていて、以下のコマンドでノートブックを PDF に変換できるものとします (Jupyter Notebook を開いている場合はそちらから File → Save and Export Notebook As... → PDF としても同じ)。
jupyter nbconvert --to pdf Untitled.ipynb
【STEP1】テンプレートの場所確認
PDF 変換の時につかわれるテンプレートがあるディレクトリ nbconvert/templates/latex/ がどこか不明な場合、--debug を付けて適当なノートブックを PDF に変換します。
jupyter nbconvert --to pdf Untitled.ipynb --debug
私の手元では以下のように出力され、テンプレートがC:\Users\user_name\AppData\Local\Programs\Python\Python311\share\jupyter\nbconvert\templates\latex\base.tex.j2 にありました ( Template paths を順に探してください)。
[NbConvertApp] Converting notebook Untitled.ipynb to pdf
[NbConvertApp] Notebook name is 'Untitled'
[NbConvertApp] Template paths:
C:\Users\user_name\AppData\Local\Programs\Python\Python311\share\jupyter\nbconvert\templates\latex
C:\Users\user_name\AppData\Local\Programs\Python\Python311\share\jupyter\nbconvert\templates\base
【STEP2】テンプレートの書き換え
index.tex.j2 の編集
以下の 3 行を削除します。
- 既に日本語対応している場合はここを
documentclass[xelatex,ja=standard]{bxjsarticle}に書き換えているかもしれませんが、今回は大元のbase.tex.j2にdocumentclass[xelatex,ja=standard]{bxjsarticle}を前提とした変更を入れてしまうので、base.tex.j2を直接編集します。
((*- block docclass -*))
\documentclass[11pt]{article}
((*- endblock docclass -*))
base.tex.j2 の header ブロック先頭の編集
以下で documentclass[xelatex,ja=standard]{bxjsarticle} を指定します。必要に応じて、デフォルトのフォントサイズも指定します。
%===============================================================================
% Abstract overrides
%===============================================================================
((*- block header -*))
((* block docclass *))\documentclass[xelatex,ja=standard,10pt]{bxjsarticle}((* endblock docclass *))
base.tex.j2 の header ブロック末尾 & body ブロック先頭の編集
以下のようにページ余白、行間、フォントを指定します。また、見出しに番号を振らせないための設定や、タイトルを出力させないための設定もします。
- .ttf ファイルでフォントを指定するときは、お手元のマシンにその .ttf がある必要があります。システムにインストール済みのフォントはフォント名で指定できます。
- 以下ではタイトルを出力させないため
\maketitleを\thispagestyle{plain}にしています。単に\maketitleを除去してもよいですが、後々に「先頭ページのみヘッダ (フッタ) を指定」としたいときに先頭ページであることを伝えるため\thispagestyle{plain}のみ残しています。
((* block margins *))
\geometry{ % 余白の調整
verbose,
tmargin=0.4in,
bmargin=0.5in,
lmargin=0.8in,
rmargin=0.5in,
}
((* endblock margins *))
((* endblock commands *))
\setcounter{secnumdepth}{0} % 見出しに番号を振らせない
\renewcommand{\baselinestretch}{0.75} % 行間を詰める
\setmainfont[]{MPLUSRounded1c-Regular.ttf} % 英数字本文フォント
\setsansfont[]{MPLUSRounded1c-Medium.ttf} % 英数字見出しフォント
\setmonofont[]{RictyDiminished-Regular.ttf} % 等幅フォント
\setmathfont[]{Latin Modern Math} % 数式フォント
\setCJKmainfont[Scale=0.96]{MPLUSRounded1c-Regular.ttf} % 日本語本文フォント
\setCJKsansfont[Scale=0.96]{MPLUSRounded1c-Medium.ttf} % 日本語見出しフォント
\setCJKmonofont[Scale=0.96]{MPLUSRounded1c-Regular.ttf} % 日本語等幅フォント
((* endblock header *))
((* block body *))
\begin{document}
((* block predoc *))
((* block maketitle *))\thispagestyle{plain}((* endblock maketitle *)) % タイトルを出力しない
((* block abstract *))((* endblock abstract *))
【STEP3】さらにヘッダを指定する場合
以下では base.tex.j2 にはヘッダ用のプレースホルダを用意して、index.tex.j2 にその中身を記述することにします。
base.tex.j2 の header ブロック末尾の編集
名前は何でもよいですがここでは pageheaderfooter という空のプレースホルダ (ブロック) を用意します。
((* block pageheaderfooter *))((* endblock pageheaderfooter *))
((* endblock header *))
index.tex.j2 の編集
全てのページの右上に「関係者外秘」と表示したい場合は以下のように記述します。先頭ページのみでよい場合は「先頭ページ以外のヘッダ」の行を削ってください。
%===============================================================================
% Latex Article
%===============================================================================
((* block pageheaderfooter *))
\usepackage{fancyhdr}
\pagestyle{fancy}
\fancyhf{}
\fancyfoot[C]{\thepage} % ページ番号の表示を保つ
\renewcommand{\headrulewidth}{0pt} % ヘッダー下の水平線を消す
\fancypagestyle{plain}{\fancyhead[R]{関係者外秘}} % 先頭ページのヘッダ
\fancyhead[R]{関係者外秘} % 先頭ページ以外のヘッダ
((* endblock pageheaderfooter *))
なお、先頭と偶数ページにヘッダを表示したい場合は以下のようになります。その他の用法は、fancyhdr のマニュアルを参照してください。
%===============================================================================
% Latex Article
%===============================================================================
((* block docclass *))
\documentclass[xelatex,ja=standard,10pt,twoside]{bxjsarticle}
((* endblock docclass *))
((* block pageheaderfooter *))
\usepackage{fancyhdr}
\pagestyle{fancy}
\fancyhf{}
\fancyfoot[C]{\thepage} % ページ番号の表示を保つ
\renewcommand{\headrulewidth}{0pt} % ヘッダー下の水平線を消す
\fancypagestyle{plain}{\fancyhead[R]{関係者外秘}} % 先頭ページのヘッダ
\fancyhead[RE]{関係者外秘} % 偶数ページのヘッダ
((* endblock pageheaderfooter *))
【おまけ】テンプレートを使い分けたい場合
普段は「関係者外秘」というヘッダは不要だが、時々必要といったこともあるかもしれません。その場合は、テンプレートディレクトリ latex を丸ごとコピーして latex_himitsu などとし、latex_himitsu/index.tex.j2 では「関係者外秘」というヘッダを指定します。
nbconvert/templates/
├ latex/
│ ├ index.tex.j2 # ヘッダ用プレースホルダを上書きしない
│ └ ...
└ latex_himitsu/
├ index.tex.j2 # ヘッダ用プレースホルダを上書きする
└ ...
そして、ヘッダが必要な場合だけ --template=latex_himitsu というオプションを付けて PDF 変換を実行すると必要なときだけヘッダを付与できます。
jupyter nbconvert --to pdf Untitled.ipynb --debug
jupyter nbconvert --to pdf Untitled.ipynb --template=latex_himitsu --debug
