読者です 読者をやめる 読者になる 読者になる

LuaJIT Libraries on LuaJITTeX de 画像処理したりする話

この記事はTeX & LaTeX Advent Calendar 2016の10日目の記事です。
昨日はyuracoさん、明日はkaizen_nagoyaさんです。

発端

まずはこちらのつぶやきをご覧ください。

以前、このツイートを拝見した際に思っていました。
画像処理で似たことが出来ないかなと。

TeX 出来ないかなと。

そこで、「TeX で画像処理するなら今こそ LuaTeX の出番!」と思いたち*1、LuaTeX でトランプ風アートを出力する私の旅は始まったのでした……

コンテンツ

この記事で掲載する内容は以下になります。

  • Torch7: Lua ライブラリの話
  • texluajit で Lua ライブラリを使う話
  • luajittex から Lua ライブラリを使う話(ホンキのLuaTeX)
    • require の動作を修正する話
  • 画像処理を行う話

Torch7: Lua ライブラリの話

とりあえず画像処理ができないことには目標が達成できません。
処理自体は、単純に範囲ごとに平均を取る方法を利用しようと思っていたので、画像から各ピクセルのRGBが取得できれば問題ありませんでした。

探してみて思ったのですが、あまりLuaの画像処理できるライブラリがないのですね。
最初に見つけたのがTorch7という機械学習などができる*2ライブラリでした。

torchの中にimageというそのままの名前のライブラリがあり、こちらを使うこととしました。

本題とは関係がないですが、このライブラリがあれば更に面白いことができそうですね。

texluajit で Lua ライブラリを使う話

Torch7 が luajit のライブラリ*3なので、まずはluajittexエンジンでluaプログラムを動かす、texluajitコマンドで動作確認です。

require('image')
image.test()

テスト出力がされれば成功です。

luajittex から Lua ライブラリを使う話

texluajitからは動作したので、今度はlatex文章に埋め込んで試しますが動きませんでした。

\documentclass{ltjsarticle}

\directlua{
	require('image')

	image.test()
}

\begin{document}
imageモジュールのテストをパス%しません
\end{document}

これの原因は次のリンク先を参照してください。要約すると、

  • requireはpackage.loaders( or serchers)を利用して探索する
  • lua(jit)texではパスの探索はkpseの探索範囲で行う
  • ただし、texlua(jit)コマンドで起動した場合は、普通のluaと同じ探索を行う

という感じです。

require の動作を修正する話

前述の通りなので、requireで読み込むときの動作を修正するモジュールを作成します。

次のページの回答で出ているコードを利用しますが、LuaTeXのLuaがこれとは少し違うようで、修正を加えています。*4

tex.stackexchange.com

以下のコードを、luaモジュールを使いたいtexファイルと同じディレクトリにというlualoader.lua名前で配置しました。

local make_loader = function(path, pos, loadfunc)
  local target = package.searchers or package.loaders
	local default_loader = target[pos]
	local loader = function(name)
		local file, _ = package.searchpath(name,path)
		if not file then
			local msg = "\n\t[lualoader] Search failed"
			local ret = default_loader(name)
			if type(ret) == "string" then
				return msg ..ret
			elseif type(ret) == "nil" then
				return msg
			else
			  return ret
			end
		end
		print("[lualoader][info]found file: " .. file .. "; (with name: " .. name ..")")
		local loader,err = loadfunc(file, name)
		if not loader then
			return "\n\t[lualoader] Loading error:\n\t"..err
		end
		return loader
	end
	target[pos] = loader
end

local binary_loader = function(file, name)
	local symbol = name:gsub("%.","_")
	return package.loadlib(file, "luaopen_"..symbol)
end

make_loader(package.path,2, function(file, name)
	return loadfile(file)
end)
make_loader(package.cpath,3, binary_loader)

これが動くことを確認するため、以下のtexファイルを実行します。

\documentclass{ltjsarticle}

\directlua{
	require('lualoader')
	require('image')

	image.test()
}

\begin{document}
imageモジュールのテストをパス
\end{document}

pdfが出力すれば成功です。これでようやく画像処理ができますね。

画像処理を行う話

画像処理内容は単純で、100x100マスごとにRGBの値を平均したものを計算します。
その色をtikzで塗っていきます。tikzのfillオプションにRGB指定するのがよくわからなかったので、xcolorで毎回色を作っています。

\documentclass{ltjsarticle}

\usepackage{xcolor,tikz,pgf}

\begin{document}
\begin{tikzpicture}[x=5mm,y=5mm]

	\directlua{
		print()
		print(package)
		for k,v in pairs(package) do
			print(k,v)
		end
		print()
		for k,v in pairs(package.loaders) do
			print(k,v)
		end
		print()

		print(package.path)
		print(package.cpath)
		require "lualoader"
		require('image')

		img = image.load("./lenna.png", 3, 'byte')

		size = 100
		rgb = torch.Tensor(
				4, %-- r,g,b, count
				math.ceil(img:size(2) / size), %-- y
				math.ceil(img:size(3) / size)  %-- x
			):fill(0)

		%-- sum color value
		for y = 1,img:size(2) do
			for x = 1,img:size(3) do
				local vy = math.ceil(y / size)
				local vx = math.ceil(x / size)
				for n = 1, 3 do
					rgb[n][vy][vx] = rgb[n][vy][vx] + img[n][y][x]
				end
				rgb[4][vy][vx] = rgb[4][vy][vx] + 1
			end
			print(y)
		end

		%-- avl color value
		for y = 1, rgb:size(2) do
			for x = 1, rgb:size(3) do
				local px = torch.Tensor(3)
				for n = 1, 3 do
					%-- rgb[n][y][x] = math.floor(rgb[n][y][x] / rgb[4][y][x])
					px[n] = math.floor(rgb[n][y][x] / rgb[4][y][x])
				end
				tex.print("\string\\definecolor{mycolor}{RGB}{" .. px[1] .. "," .. px[2] .. "," .. px[3] .. "}")
				tex.print("\string\\draw[draw=mycolor,fill=mycolor] (" .. (x - 1) .. ",-" .. (y - 1) .. ") rectangle (" .. (x-0.2) .. ",-" .. (y-0.2) .. ");")
			end
		end
	}

\end{tikzpicture}
\end{document}

これを、Lennaの画像*5で利用すると、こんな感じのが出力されます。

f:id:vraisamis:20161210194007p:plain


この画像だと512幅しかないので、このサイズで平均化すると中身がわかりませんね。
サイズを変更するといい感じになるかもしれません。

*1:PyTeX などがあるというツッコミはご容赦ください

*2:らしい

*3:JITではないインストールもできるようですが今回はしていないです

*4:Lua5.2でloadersがserchersに変更されたのが原因のようです。Lua 5.2 リファレンスマニュアル: 8.2節を参照。LuaTeXで5.2が使われた場面ってありましたかね?

*5:File:Lenna.png - Wikipedia

続きを読む

LuaLaTeXでフォントサンプル集を作りたい!

経緯

最近スライドを作成する時にフォントを探して回ったりした。自分がフォントに詳しくないのもあり、デフォルトで入っているフォントに関しては、全く知識がなかった。 フォントビューアーを利用すればいいという話もあるが、現環境がUbuntuで、使い勝手が悪かった。

ついでに、好みの問題で大文字の「W」の中央が交差しているフォントがほしかった。

それなので、とりあえずいろいろなフォントで出力するLuaLaTeXスクリプトを書けないかと試行錯誤した。

インストールされているフォント探し: luaotfload-tool

とりあえずどうやらLuaTeX系で使う、フォントDBツールが存在することがわかった。それがluaotfload-toolである。

こんな感じで使う。

# DBの更新
$ luaotfload-tool --update
# インストールされているフォントの一覧を出す
$ luaotfload-tool --list=*
# インストールされているフォントの一覧を、表示項目を指定して出す
$ luaotfload-tool --list=* --fields=plainname

このコマンド、ドキュメントが不完全に感じられる部分があって、例えば--fieldsオプションに指定できる項目がリストアップされていない。

DBを作ると、kpsewhich --var-value=TEXMFVARで出るディレクトリの、luatex/generic/names/luaotfload-names.lua.gzが作成されると思う。 巨大なファイルなので、見ることはあまりおすすめしないが、途中のmappingsの項目に羅列されているものがそれだと思う。コロン以降は簡単な解説。*1

  • basename : パスなしのファイル名。
  • familyname : フォントファミリー名
  • fontname : フォント名
  • format : ファイルのフォーマット(拡張子)
  • fullname : たぶんフォントファミリー名+サブファミリー名
  • fullpath : 絶対パス付きのフォントファイル名
  • index : 内部のインデックス
  • italicangle
  • location
  • pfmweight
  • plainname : フォントの識別名?
  • psname
  • size
  • subfamily : レギュラーとかボールドとかイタリックとか
  • subfont
  • version : フォントバージョン
  • weight
  • width

後にfontspecを利用してフォント変更を行うけど、この時におそらく使えるのはbasenameとplainnameあたり(結論から言って成功はしていない)。その他は未検証。

さて前述のコマンドでフォント一覧が取得できることがわかったので、これを\directluaブロックで取得して適当にループ回せばできるのではって感じで見通しを立てた。

# インストールされているフォントのplainname一覧を出す
$ luaotfload-tool --list=* --fields=plainname

\directluaブロックからの外部コマンド実行

参考に、luaのコードから外部コマンド(例ではls -al)を実行し、出力を得る場合は、次のようにする。

local handle = io.popen("ls -al")
for line in handle.lines() do
    -- ここでなにか色々する。lineには1行分が入っている。
    print(line)
end
handle::close()

上記を参考に\directluaブロックにluaotfload-tool呼び出しを記述しコンパイルすると、エラーが出る。具体的にはpopen()できない。なので、lualatex実行時に、オプション--shell-escapeを付加する必要がある*2

コンパイルの全容を以下に示す。

# main.texをコンパイルする。
$ lualatex --shell-escape main

これで、全ての利用可能(?)なフォントが画面に出力されるはずである。 このときのtexファイルの全容は以下の通り。

% filename: main.tex
\documentclass{ltjsarticle}

\usepackage{luatexja-fontspec}

\begin{document}
test

\directlua{
    local h = io.popen("luaotfload-tool --list=* --fields=plainname")
    print("###################")
    for line in h:lines() do
        print(line)
    end
    print("###################")
    h:close()
}

\end{document}

ここまでは上々。うまく動く。

このままとりあえずフォント名をPDF出力……できない!

なにかよくない文字が含まれているらしい? 本文中で以下のようにするとエラーが出る(再チェック前)。

\directlua{
    fontlist = {}
    local h = io.popen("luaotfload-tool --list=* --fields=plainname | grep -e Han")
    for line in h:lines() do
        tex.print(line)
        tex.print("")
    end
    h:close()
}

\verb||で囲もう。

     %tex.print(line) %%下のように変更
        tex.print("font: \string\\verb|" .. line .. "|")

できた。フォント名がすごいいっぱい並ぶ。 Ubuntuだと1500ぐらい並ぶんじゃないかな?

目的であるフォントを変更して出力

というわけで、このフォント名で\fontspec{}していけばOKなはず!

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
%% !!!!!!! 注意: これを動かすべきではない
%% !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\documentclass{ltjsarticle}

\usepackage{luatexja-fontspec}

\newcommand{\sampletext}{ABCDEFGHIJKLMNOPQRSTUVWXYZ

abcdefghijklmnopqrstuvwxyz
}

\begin{document}

\directlua{
    fontlist = {}
    local h = io.popen("luaotfload-tool --list=* --fields=plainname")
    print("###################")
    for line in h:lines() do
        table.insert(fontlist, line)
        tex.print("font: \string\\verb|" .. line .. "|")
        tex.print("")
        tex.print("{\string\\fontspec{" .. line .. "}\string\\jfontspec{" .. line .. "} \string\\sampletext }")
        tex.print("")
    end
    print("###################")
    h:close()
}
\end{document}

これが、うまくいかなかった。利用できないフォントのエラーを無視していったが、その他にもエラーが出て止まった。

エラーの解析

コンパイル時にシステムモニタ(タスクマネージャ)等で確認した限り、メモリをかなり消費している。 具体的には、4GBのメインメモリと4GBのスワップがあったが、(コンパイル以外で1GB前後使っているとはいえ)これらを全て食いつぶしていた。まずい。

というわけで、手元にSource Han Sans一式(7つ)があったので、これらだけでどの程度メモリを食うのか検証した。 そのために、以下のような変更を施した。 厳密さは必要ないので、システムモニタを目視することによる検証をした。

 %local h = io.popen("luaotfload-tool --list=* --fields=plainname")
    local h = io.popen("luaotfload-tool --list=* --fields=plainname | grep Han")

結果、2GB程度のメモリを消費していた。このフォントのサイズが大きいとはいえ、さすがに食い過ぎであるように思うが現実は受け止めなければならない。 この結果を見る限り、1ファイルで全てを出力することは現実的ではないと判断した。

どうするか?→フォント毎にファイルを作ってとり込もう!

とりあえず、メインのファイルから各フォントサンプルのPDFを作成して、それを取り込めば表示できるよね!っていう考えに至った。

そこでこちらがmain.texとなる。

\documentclass{ltjsarticle}

\usepackage{graphicx}

\begin{document}
\directlua{
    fontlist = {}
    local h = io.popen("luaotfload-tool --list=* --fields=plainname")
    print("###################")
    for line in h:lines() do
        if line \string~= "" then
            table.insert(fontlist, line)
        end
    end
    h:close()
}
\begin{itemize}
\directlua{
    for k,v in ipairs(fontlist) do
        local fontname = v
        local filename = v:gsub(" ", "_"):gsub("+", "PLUS")
        local ret = os.execute("lualatex --interaction=nonstopmode --output-directory=outd --jobname=\string\"" .. filename .. "\string\" sample fontname=\string\"" .. fontname .. "\string\"")
        print("##########################", fontname, "::::", filename, "==>>", ret, "#########################")
        tex.print("\string\\item \string\\verb|fontspec{" .. fontname .. "}**|", "\string\\\string\\")
        if ret == 0 then
            tex.print("\string\\includegraphics{outd/" .. filename .. ".pdf}")
        else
            tex.print("!! Error: This compile returns", ret, "!!")
            %tex.print("")
        end
    end
}
\end{itemize}
\end{document}

とりあえず出力が出るか出ないかは気にせず、コンパイルエラーならそれを報告するようにした(エラーコードだけ)が、あまり役に立たなかった。 ファイル名にするための無害化とかTeX出力する上での無害化とかをgsub()で行ったけど、不十分だと思う。+以外の記号が含まれる不フォントもあるんじゃないかな?

また、事前に格納先のフォルダoutdが必要になる。同じフォルダに展開すると視覚的に死ぬので注意。

そして、sample.tex。数式関連は表示していない。

\RequirePackage{luatex85}
\documentclass{standalone}
\usepackage{luatexja}
\usepackage{luatexja-fontspec}
\pagestyle{empty}
\directlua{
    fname = "IPAexMincho"
    for i,v in ipairs(arg) do
        a,b,c = string.find(v, "fontname=(.+)")
        print("###########", a, ":::::", b, ":::::", c, "########")
        if c then
            fname = c
        end
    end
    tex.print("\string\\setmainfont{" .. fname .. "}")
    tex.print("\string\\setmainjfont{" .. fname .. "}")
}
\begin{document}
\begin{tabular}{l}
    abcdefghijklmnopqrstuvwxyz
    ABCDEFGHIJKLMNOPQRSTUVWXYZ
    \\
    これは日本語の文書です。
\end{tabular}
\end{document}

standaloneを使うのでluatex85を利用している。フォント指定をコマンドライン引数のインデックスではなく、マッチで行っている。 あまり考えていないので、もしフォント名が渡されなかったらIPAex明朝で表示することにした。

これで動く。ただしコンパイルにすごく時間がかかる。

それと、コンパイルに成功したフォントの数によってはファイルディスクリプタが不足してエラーになることがある。 適当に変更しておこう:うちの環境ではulimit -n 2048でふやせた。

おわりに:今後の更新

気になる部分は指摘していただければと思っています。

  • コメントをつけないとならない。
  • 組み立てを精査しないと少し読みにくい。
  • 環境等を記していない。

以上です、お疲れ様でした。

*1:間違いや追加などのコメントをいただければ幸いです

*2:おそらく、以前pngなどの画像をTeXに取り込む際にはxbbの自動生成の設定が必要だった時のshell escape commandsあたりを弄ればオプションは要らなくなると思うが、私自身はこれ以外の利用ケースを見いだせていないので、コマンドラインオプションとした

NetbeansでJava Servlet環境を整え、index.htmlを表示するまで

環境

Oracle Java8を入れ、デフォルトに設定

$ sudo add-apt-repository ppa:webupd8team/java
$ sudo apt-get update
$ sudo apt-get install oracle-java8-installer
$ sudo apt-get install oracle-java8-set-default

NetBeansをダウンロード

公式サイトからダウンロード。「Java EE」か「すべて」をえらぶ。以降は「すべて」を選んだ場合で説明する。
f:id:vraisamis:20160428142532p:plain

ダウンロードされたファイルの確認とインストーラの起動

ダウンロードが完了すると、シェルスクリプトファイルが得られる。今回はこのディレクトリ内に
何か展開されるわけではないが、説明のためnetbeansというディレクトリを作っている。
f:id:vraisamis:20160428142542p:plain


端末から同ディレクトリに移動し、シェルを起動する。
もしシェルに実行権限がなければ、次のコマンドで実行権限を付加する。

$ chmod u+x netbeans-8.1-linux.sh 

シェルを起動した様子。
f:id:vraisamis:20160428142556p:plain

NetBeansおよびTomcatのインストール設定

きちんと起動すれば次のような画面になる。この画面で、「カスタマイズ」を押す。
f:id:vraisamis:20160428142602p:plain


カスタマイズ画面では、Tomcatが選択されていないので、選択する。以上で「OK」を押す。更に「次へ」。
f:id:vraisamis:20160428142651p:plain


ライセンスの確認。同意すると次へ進むことができる。
f:id:vraisamis:20160428142702p:plain


注意する場所その1JDKのパスはこちらを選択する。デフォルトのものでは動かない。
f:id:vraisamis:20160428142711p:plain


GlassFishJDKも同様(既になっているはず)。
f:id:vraisamis:20160428142727p:plain


Tomcatの設定項目に変更はない。
f:id:vraisamis:20160428142822p:plain


設定の最終確認。チェックボックスは必要に応じて入れる。問題なければ「インストール」を押す。
f:id:vraisamis:20160428142831p:plain


ちなみに、root権限のあるユーザで実行すると、デフォルトのインストールパスなどがこのようになる。
f:id:vraisamis:20160428142841p:plain


インストールの完了までしばらく待機。ダウンロードするものもあるようなので、非課金接続でやるとよい。
f:id:vraisamis:20160428142852p:plain


インストールが完了。チェックボックスはどちらでも良い。
f:id:vraisamis:20160428142902p:plain


デスクトップアイコンで起動。アイコンはこれ。
f:id:vraisamis:20160428142911p:plain


スプラッシュ。
f:id:vraisamis:20160428142922p:plain


起動後のスタートページ。ここまででインストールは完了。
f:id:vraisamis:20160428142935p:plain

プロジェクトの作成と組み込みブラウザでの確認

実際にプロジェクトを作成する。メニューから、「ファイル」→「新規プロジェクト」を選ぶ。
f:id:vraisamis:20160428142950p:plain


プロジェクトタイプの選択。「Java Web」から「Webアプリケーション」を選択。
f:id:vraisamis:20160428143002p:plain


初回のみ機能のアクティベートが行われる。プラグインのインストール等だと思われる。
f:id:vraisamis:20160428143019p:plain


プロジェクトの設定。名前を決め、プロジェクトディレクトリを決める。

下のチェックは、おそらく複数のマシンでの開発が想定される場合には入れたほうが良い。
f:id:vraisamis:20160428143034p:plain


サーバの設定。Tomcatを選択する。「次へ」を押すとフレームワークを選択する画面が出るが、今回は選択しない(説明しない)。
f:id:vraisamis:20160428143042p:plain


以上でプロジェクトの作成は完了。左ペインの「プロジェクト」から、いま作成したアプリケーションのツリーを開いてみると、いくつかファイルがあることが確認できる。

組み込みブラウザで実行を確認する

プロジェクトを選択している状態だと、上部のツールバーのブラウザアイコンが押せるようになっているので、押して実行するブラウザを決定する。

Android云々が表示されているのは、私がAndroidStudioをインストールし、いろいろ設定してあるためだと思われる。好きなブラウザを選べば良い。
f:id:vraisamis:20160428143050p:plain


組み込みブラウザを選んで実行すると、画面中央あたりにindex.htmlの内容が表示される。右ペインにCSS等が表示されるが、中央のブラウザ表示を消すと消える。
f:id:vraisamis:20160428143101p:plain

予想外にもUbuntu14.04 Install Battleを繰り広げた on ASUS UX305FA-WHITE

UbuntuはWeb上でも情報が豊富で、インストールも楽々。そんな風に今まで思っていました。
実際、自分の仮想やらPCやらに入れる際にも、Liveイメージを焼いて入れてあげればきちんと起動、問題なくすべての機能が使えていました。


ですが、今回は一味違ったようです。以下に要約しておきます。

  • UX305にUbuntu 14.04のLiveイメージを入れて起動したところ、無線LANデバイスが使えない状態だった。
  • 問題点はドライバ不足であると推測。Live起動のまま、使えることを確認してからインストールしたいので、いろいろ調べた。
  • 結果、Liveイメージをカスタムし、ファイルを足して焼くことで解決した。

こういった具合でした。疲れました。以下、流れをメモしておきます。時間のある時に加筆します。公開から1月以内にはなんとかします。

インターネットでUbuntuの動作確認をしている動画を発見

それがこちら。


Ubuntu on the Asus Zenbook UX305

ほう。よく動くようですね。そう思いました。

Liveイメージで同様に試す→無線LANが使えない!

しかし、私が自分でいざLiveイメージを起動してみると、無線LANが使えませんでした。

  • OSに存在は認識されている。lspciでは表示される。
  • rfkill listで、ハードおよびソフトで止めていないか確認。問題なし。無論、機内モードでもない。
  • dmesgを確認したところ、ドライバが見つからないということらしい。一番怪しいのでこのドライバを探した。
  • どうやらこのところにある必要なファイルをコピーして起動すればいいらしい:OpenELEC/iwlwifi-firmware · GitHub
  • コピー先は/lib/firmware。しかし、Liveイメージでは起動後にファイルを追加しても、再起動で消滅してしまう。はて、どうしたものか?

Liveイメージのカスタマイズへ

前述のように、ドライバを読ませてやる手段に詰まっていました(実際はサービス再起動などいろいろ試しましたが駄目だったので割愛)。
ならば、いっそのこと、Liveイメージをカスタムして、それを起動すればいいのではないか?そう考えました。


調べてみれば、UbuntuにはどうやらUbuntu Custumization Kitというアプリケーションがあり、ベースになるイメージを指定してカスタマイズできるようです。
そのソフトを使い、コンソール画面による詳細なカスタムのタイミングで別の端末を起動、~/tmp/new-master-root(tmpまでは覚えているけどその下が自信ない)が
Liveイメージのルートとなるディレクトリですので、それ以下を編集しました(あとで加筆します)。


このISOイメージをUSBに焼き、Bootすることで無線LANが利用できるようになりました。


実際はインストール後にドライバを追加して再起動すれば済む話なんですけど、Ubuntuはインストール時にネットワーク接続があったほうが良いので、このようになりましたとさ。
おしまい。

自分がやりたいことをきちんとやるために

最近とてもいろいろ捗らない。確実に散らかってるせいなので片づけることにします。

ついでなんだけれども、どうして散らかっていると捗らないかをざっくりまとめて書いておく。 これは自分への咎め。やりたいことが多いなら、確実に一個ずつこなしていけ。

自分に注意:現状は机上の空論でしかないから自分で実践して裏付けしろ。

まとめから。

読まずにわかる3行まとめ。

  1. 余計なものが目につくと、興味を奪われてロスになるので片づけよう。
  2. 勉強机の漫画は隠せ。
  3. 本当にやりたいものなら、隠しても絶対見つけられるから隠せ。

本文は続きで記す。

続きを読む