listings パッケージ等でソースコードを作成する際に行番号を付記することがあります。
しかし、行番号を付してしまうと PDF 上からソースコードを複数行選択する際に、一緒に行番号も選択してしまいます。
これは非常に厄介で、可能であれば選択したくないものです。
そこで本記事では、絶対に行番号を選択させない TikZ-numeral パッケージを作って対応してみようという試みです。
絶対に行番号を選択させたくない先行研究
“行番号を選択させたくない” と言う希望は多くの人が持っているようで、次のような方法を確認することが出来ました。しかしながら、これらには問題点があります。
-
accsupp パッケージを使って、
ActualText
を空文字にする(参考)ActualText
の扱いはビューワに依存するため、行番号が選択できてしまう場合がある。 -
TikZ を使って行番号をソースコードの後に別個で番号付けする(参考)
ページ毎にソースコードの後で行番号を追加しているため、ページをまたぐ場合、ページ毎にソースコードを選択する必要がある。
-
行番号の背景に白色を追加する(参考)
macOS の Preview や Safari では有効な手段のようですが、(少なくとも筆者の)Windows 環境では行番号を選択できる
と言うことで、現状、完璧に行番号を選択させない方法は無いと言っても過言ではありません。
そこで今回、絶対に行番号を選択させない簡単な方法を提供するパッケージを作成してみました。
TikZ-numeral パッケージを作ってみた
TikZ-numeral パッケージは数字そのものを TikZ で描画します。(以降、TikZ 数字と呼称)
これによって、数字は文字としてではなく図として埋め込まれるため、絶対に選択できません。(OCR すればさすがに読まれるとは思いますが)
使い方
使い方は簡単です。TikZ-numeral パッケージを読み込むだけです。数字を TikZ 数字に変えるには、\tikznumeral
を使います。
\documentclass{article}
\usepackage{tikz-numeral}
\begin{document}
\tikznumeral{0123}
\end{document}
listings パッケージと併用する場合は、次のようにします。
\usepackage{tikz-numeral}
\usepackage{listings}
%% \tikznumeral を使って \thelstnumber を再定義 (cf. p.35, 4.3.7 Line numbers, The Listings Package, Version 1.10c)
\renewcommand*\thelstnumber{\tikznumeral{\the\value{lstnumber}}}
あるいは、TikZ-numeral パッケージに listings
オプションを与えます。これによって、\thelstnumber
の再定義を自動的に行います。
\usepackage[listings]{tikz-numeral}
\usepackage{listings}
具体例は次のようになります。
\documentclass{article}
\usepackage{listings}
\usepackage[listings]{tikz-numeral}
\lstset{
language = Python, backgroundcolor={\color[gray]{.90}}, breaklines = true, breakindent = 10pt, basicstyle = \ttfamily\scriptsize,
frame = tb, framesep = 5pt, stepnumber = 1, tabsize = 4,
%%
%% 行番号を表示するために必須
numbers = left,
}
\begin{document}
\begin{lstlisting}[gobble=2]
import random
inside = 0
for _ in range(100000):
x, y = random.random(), random.random()
if x**2 + y**2 <= 1: inside += 1
print("Approximation of Pi: ", 4 * inside / 100000)
\end{lstlisting}
\end{document}
![]() |
---|
ソースコードを選択している様子 |
ソースコードを選択していますが、行番号は選択していません。絶対に行番号は選択できません。
組み込みフォント
パッケージでは組み込みで 3 つのフォントが font
パッケージオプションから利用できます。
オプション | フォント |
---|---|
newcm-rm (デフォルト) |
New Computer Modern |
newcm-tt |
New Computer Modern Mono |
newcm-sf |
New Computer Modern Sans |
もしも追加で別のフォントが使いたい場合は、外部ファイルからフォントを追加する方法があります。詳細は README を参照してください。
実装
実装としては単純で、数字を左から 1 つずつ TikZ による図に置き替えているだけです。
本当は \arabic
のようにカウンタを直で受け取って変換するように作成するべきだったと思いますが [参考]、よく分からなかったので expl3 で 1 つずつ置き替える方式を採っています。
その代わり、\the\value
を使って \tikznumeral{\the\value{counter}}
とすればカウンタも受け付けます。
TikZ 数字は 1 つずつが tikzpicture 環境内にあります。桁が増えると冗長的な気がしましたが、1 つの tikzpicture 環境に 2 桁以上の数字になったとき数字どうしの間隔を上手く保てなかったため、このようなことになっています。
本実装では \useasboundingbox
を使っていずれの数字も 0~9 の最大幅が同じになるようにしています。
また、tikzpicture 環境のオプションを 0~9 のすべての数字で共通しています。もしも、個別にオプションを変更したい場合、厄介なことになる可能性があります。
TikZ 数字の取得
TikZ で描かれた数字はフォントから取得しています。Python を使って SVG 経由で TikZ になっています。(これを実行する Python コードは ここ にあります)
そのため、手動で TikZ 数字を描く必要はなく、任意のフォントを TikZ 化して TikZ-numeral パッケージで利用できます。(ただし、作成した Python コード内で利用するフォント情報が無いフォントには使えません)
余談
絶対に行番号を選択させない方法を提供する TikZ-numeral パッケージを作ってみました。
この問題の解決案として、初めは PDF 上の文字を部分的にアウトライン化することを考えていましたが、LaTeX でアウトライン化はそもそも出来ないようでした。であれば、すでにアウトライン化された TikZ 数字を使おうと言うのがモチベーションでした。
TikZ-numeral パッケージは TikZ を使って数字を描画しているので、普通の数字よりもタイプセットに時間がかかるようになります。時間短縮のために PGF で直書きした方が良かったかもしれませんが、単純に PGF へ変換する外部手段がなさそうだったので、TikZ を採用しました。
「動作をもっと早くできる」と思った方は、是非リポジトリをフォークして高速化を目指してください。(単純に数字の変換部分が遅い可能性もある)
実は、パッケージ開発よりフォントから TikZ に変換する Python コードを書く方が時間がかかりました。ChatGPT にも頼りつつ書いています。Python コードは優しい目で見てください。
expl3 メモ
外部ファイルを読み込む際は、\file_get:nnNTF
を使います。set_up
はカテゴリコードを調整するための部分です。
\file_get:nnNTF { file_name } { set_up } \l_tmpa_tl
{
%% ファイルが存在したとき
}
{
%% ファイルが存在しなかったとき
}
上の例では、ファイルの内容はすべて \l_tmpa_tl
に格納されます。
TikZ-numeral パッケージでは、外部ファイルの内容を \keys_set:
に渡したいため、次のようにしました。
\keys_set:nV { module } \l_tmpa_tl
これで、ファイルの内容が \keys_set:
に受け渡りました。