LuaTeXについて4(MPlib)

今回はMPlibについて。 LuaTeXの機能としてあげられている、MetaPostのサポート。 実際にはMPlibというMetaPostを外部ライブラリ化して、 Luaインターフェイスを追加したライブラリを通して行うらしい。 MetaPostとはTeXでよく使われるコマンド型の描画ツールです。 (奥村本、付録Fより) METAFONTでグラフとかを書いてepsにできるようにしたものらしい。 多分有名な物なので、詳細は略。 LaTeXで使える描画ツールとしては TeXWikiで紹介されているようにたくさんあるわけだが、 LuaTeXではMetaPostをそのまま使える様にすることを選んだ。 MPlibの開発に関していくつか資料を見つけた。 MPlib: MetaPost as a reusable component MetaPost developments: MPlib project report ようは現代的に、外部ライブラリ化してLuaからよびたかったらしい。 apiのリファレンスはこちら。 と前置きはこんな感じ。使ってみる。 texliveでluatexが入っている環境ではluamplibは既にインストール済みだったので、 特にインストール作業はなし。
\documentclass{article}
\usepackage{luamplib}
\begin{document}
\begin{mplibcode}
prologues := 1;
beginfig(1);
draw (0,0)--(100,0)--(100,100)--(0,100)--cycle;
label("some text",(50,50));
endfig;
\end{mplibcode}
\end{document}
パッケージluamplibを使う様に指定して、 MetaPostのコードをmplibcode環境で囲めばいい。 この例では四角形のなかに"some text"と表示される。 そのままだと日本語は通らない。 Luaから使う場合。How to use mplib directly from lua?から拝借。
\documentclass{article}
\usepackage{luamplib}
\begin{document}
\directlua{dofile('test.lua')}
\directlua{ StartMP() }
\end{document}
local mpkpse = kpse.new("luatex", "mpost")

local function finder(name, mode, ftype)
    if mode == "w" then
    return name
    else
    return mpkpse:find_file(name,ftype)
    end
end

function StartMP()
  local mplib = require('mplib')
  local mpx=mplib.new({find_file=finder,ini_version=true})
  local result = mpx:execute('input plain;')
  result=mpx:execute('beginfig(1); draw fullcircle scaled 20 withcolor red; endfig;')
  local t,e,l = result.term,result.error,result.log
  if result.status>0 then
    tex.print([[Result of mplib execute is unsuccessfull.]])
  else
    if result.fig then
      tex.sprint('Converted something: \\vrule\\vbox{\\hrule')
      local converted=luamplib.convert(result)
      tex.sprint('\\hrule}\\vrule')
    else
      tex.print([[No figure output.]])
      tex.print([[Log:]])
      tex.print(l)
    end
  end
  mpx:finish()
end
急に長くなったが、基本的には require('mplib")で読み込み、 mpx = mplib.new(...)でMetaPostのインタプリタを用意し、 result = mpx:execute('...')でMetaPostを図に変換している。 あとはエラー処理のコードと、 mplib.newの引数に渡すfind_file関数を用意している。 この関数がないと動かない。 これはapiリファレンスにデフォルトでローカルのファイルしか探さないから と書いてあるが、だからなぜ使えないかわからなかった(苦)。 kpseとはLuaTeXのパッケージの一つで、 公式のリファレンスにのっているのでそちらを参考に。 普通に使う分にはmplibcode環境で大体事足りそうなので、 とりあえず使えるようになったことにして、まとめた事にする。 日本語の対応の関連は日本語対応版のjmpostあたりの話をそのうち探ってみることにする。

LuaTeXについて3(パッケージの作り方)

今回はLuaTeXの機能を使ったパッケージを作成する。 LuaTeX用のパッケージを作るということはLuaが使えるということである。 これはだいぶ喜ばしい事だろう、多分。 というのも、普通のplatex用のパッケージもろくに作った事ないので、 嬉しさがピンとこない。 それはさておき、Luaの機能を使ってパッケージが作れる。 Luaにあって、TeX単体では難しい作業ということで、 今回は.csvファイルから表を作成するパッケージを作る事にする。 \csvToTable{"filename.csv"}と書くと、 filename.csvを読み込んで、tabular環境の表を作ってくれる関数をつくる。 TeX単体でできるとかできないとかは不明。 とりあえず奥村先生の本にはのってない。 Luaには標準のfile ioが備わっているので簡単にできるはずである。 csvをパースするのはLuaの公式HPから拝借する

function fromCSV (s)
  -- 公式のHPより拝借
  s = s .. ','        -- ending comma
  local t = {}        -- table to collect fields
  local fieldstart = 1
  repeat
    -- next field is quoted? (start with `"'?)
    if string.find(s, '^"', fieldstart) then
      local a, c
      local i  = fieldstart
      repeat
        -- find closing quote
        a, i, c = string.find(s, '"("?)', i+1)
      until c ~= '"'    -- quote not followed by quote?
      if not i then error('unmatched "') end
      local f = string.sub(s, fieldstart+1, i-1)
      table.insert(t, (string.gsub(f, '""', '"')))
      fieldstart = string.find(s, ',', i) + 1
    else                -- unquoted; find next comma
      local nexti = string.find(s, ',', fieldstart)
      table.insert(t, string.sub(s, fieldstart, nexti-1))
      fieldstart = nexti + 1
    end
  until fieldstart > string.len(s)
  return t
end
これでコンマで区切られたデータを分割できる。 次にファイルを読み込んで各行を上の関数に渡す関数を作る。 file ioに関してはこのあたりが詳しい。

function readCSV(filename)
    local f = assert(io.open(filename,'r'))
    data = {}
    count = 1
    while true do
        line = f:read("*line")
        if line == nil then break end
        list = fromCSV(line)
        data[count] = list
        count = count + 1
    end
    return data
end
これでファイルを読み込んで、csvをパースする所までできた。 Luaの基本的な機能だけでごくごく簡単な作業だ。 さていよいよパースしたデータから表を作成する

function csvToSimpleTable(filename)
    data = readCSV(filename)
    -- とりあえず先頭のdataの要素にあわせておく
    local len = #data[1]
    local tab = '{'
    for i = 1,len do
        tab = tab .. "r"
    end
    tab = tab .. '}'
    tex.print("\\begin{tabular}" .. tab)
    for i,list in ipairs(data) do
        local line = ""
        for j,val in ipairs(list) do
            if line == "" then 
                line = val
            else
                line = line .. '&' .. val
            end
        end
        tex.print(line .. '\\\\')
    end
    tex.print("\\end{tabular}")
end
LuaからTeXの入力を行うにはtex.print(...)を用いる。 Luaでは文字列の合成は..で行うことと、#dataがデータの個数 (pythonでいえばlen(data))になっていること、 LaTeXの文字列をエスケープする必要がある事に注意すれば、 そう変なことはしていない。 これでLuaの方は完成である。 TeXの方は.dtxと.insファイルを作る。 これについては次回まとめる事にする。 特にLuaTeX用に工夫すべき点は、上記の関数の書いてある.luaをどこで読み込むかで、 .dtxの上の方でのように書くといいと思う。

%\directlua{dofile('csvReader.lua')}
あとは普通に

\newcommand\csvToSimpleTable[1]{\directlua{csvToSimpleTable(#1)}}
のように定義すれば使える。 >>4に続く。

movyの紹介

awesomeは便利だ。 しかしその便利さは、自分でカスタマイズできてこそである。 大方デフォルトの設定で問題はないのだが、所々気になることがある。 例えば、Gunuplotを起動する時。 基本的にfloat以外のlayoutを使っているときは、新しくでたGnuplotの図はlayoutに組み込まれる。 この挙動はちょっと気に入らない。 というのも ・図の大きさがwindowとあっていない ・foucsがGnuplotにとられる 一つ目は再描画すればいいし、後者にいたってはMod4-jを1回おせばいいだけだ。 さらにMod4-Ctrl-Spaceでfloatにするという選択肢もある。 とはいえ、この手の細かい挙動は操作性に大きく影響する。 そこで起動と同時にfloatにしたい。 それにはデフォルトでのruleを使うか、Shiftyを用いてもいい。 Shiftyのときは

shifty.config.apps = {
    {
        match = {"Gnuplot"},
        nofocus = true,
        float = true,
        run = function (c) c.ontop = true end,
    }
}
これで起動すると同時にfloatにしてfocusを移さずに、 さらに常に最前面(ontop)にできる。 これでそこそこ使いよいのだが、たまにwindowが邪魔だ。 Gnuplotならすぐ消す場合が多いのだが、windowを動かす方法が欲しい。 そんなに真面目に探してないのだが、見当たらなかったので、練習に作ってみることにした。 movyに行くとzipが落とせるはず。(github初めてなのでまだよくわからない) 中身はclientkeyに登録する用の関数群となっている。 movy.scaleがclientのサイズを変更する関数を提供し、 movy.moveがclientを移動させる関数を提供する。 rc.lua内でrequire("movy")として、clientkeyに登録して使う。

clientkeys = awful.util.table.join(clientkeys,
    awful.key({modkey ,"Mod1"}, "s", function (c) movy.scale.rescale(c,1.1) end),
    awful.key({modkey ,"Shift", "Mod1"}, "s", function (c) movy.scale.rescale(c,0.9) end),
    awful.key({modkey , "Mod1"}, "c", function (c) movy.move.set_place(c,"center") end),
    awful.key({modkey , "Mod1"}, "j", function (c) movy.move.down(c) end),
    awful.key({modkey , "Mod1"}, "h", movy.move.left),
    awful.key({modkey , "Mod1"}, "k", movy.move.up),
    awful.key({modkey , "Mod1"}, "l", movy.move.right),
    awful.key({modkey ,"Shift", "Mod1"}, "j", function (c) movy.move.move(c,0.0,1.0) end),
    awful.key({modkey ,"Shift", "Mod1"}, "k", function (c) movy.move.move(c,0.0,-1.0) end),
    awful.key({modkey ,"Shift", "Mod1"}, "l", function (c) movy.move.move(c,1.0,0.0) end),
    awful.key({modkey ,"Shift", "Mod1"}, "h", function (c) movy.move.move(c,-1.0,0.0) end)
    )
マニュアルはそのうち作ろう。(ちなみにMod1はAlt) これでfloatになったclientを上下左右、自由に動かせるようになる。 とりあえず動くことは確認したけども、ほとんどテストとかしていないので、使うときは気をつけて。

awesomeを導入。

先月位からWindowManagerを完全にawesomeに乗り換えた。 いろいろ調べて、だいぶ情報がたまってきたので、ここらで少しまとめることにしよう。 まず情報源: awesome wiki 公式のwiki。ほとんどここで得た情報をもとに話す。 awesomeとは? awesomeはKDEのKWinや、GNOMEのcompizに対応するWindow Maneger。 タイル型とよばれるタイプで、ウインドウを画面にモザイク状に敷き詰める用に配置する。 なので、画面を効率的に使用できることが期待される。 デスクトップ環境ではないので、エディタやpdf viewer等のプログラムは別に自分で用意する必要がある。 とまぁ、awesomeの入門や他のタイル型WM(xmonad等)との比較は日本語でもたくさん記事があるので、 そっちを参照してもらうとしよう。 awesomeはCとLuaで書かれていて、カスタマイズもLuaで行う。 Luaは比較的とっつきやすい言語なので、 The Programming Language Lua 当たりをみればすぐにでも書ける。 LuaTeXもLuaな訳で個人的に今あつい言語である。多分。 今回Luaでawesome用のライブラリを作ってみたので次回紹介する。

xpdf日本語表示

xpdfの日本語対応でちょっとはまったので書いておく。 基本的な設定は他に解説しているブログ等いくつかあるのでそちらを見てもらうとして、 今回は.xpdfrcの設定の書きかたが変わった話。 通常、フォントを指定せずにdvipdfmxなりで.pdfを作ると デフォルトで日本語フォントはryuminに設定される。 これはリュウミン | フォント製品 | 株式会社モリサワで売っているフォントで有料である。 ということで、普段はこれを.xpdfrc内でdisplayNamedCIDFontTTを使って displayNamedCIDFontTT Ryumin-Light-Identity-H /usr/share/font/TTF/ipam.ttf の様にIPAフォントに置き換えていた。 しかしながら、どうも最新のxpdfでは
Config Error: Unknown config file command 'displayNamedCIDFontTT'
と怒られる。 エラーの内容からして、どうもdisplayNamedCIDFont*系のコマンドがなくなったらしい。 調べてみると.xpdfrcの書きかたがxpdf v3.02以降で変更されていた。 READMEの該当箇所
Upgrading from Xpdf 3.02 (and earlier)
--------------------------------------

The font configuration system has been changed.  Previous versions
used mostly separate commands to configure fonts for display and for
PostScript output.  As of 3.03, configuration options that make sense
for both display and PS output have been unified.

The following xpdfrc commands have been removed:
* displayFontT1, displayFontTT: replaced with fontFile
* displayNamedCIDFontT1, displayNamedCIDFontTT: replaced with fontFile
* displayCIDFontT1, displayCIDFontTT: replaced with fontFileCC
* psFont: replaced with psResidentFont
* psNamedFont16: replaced with psResidentFont16
* psFont16: replaced with psResidentFontCC

See the xpdfrc(5) man page for more information on the new commands.

Pdftops will now embed external 16-bit fonts (configured with the
fontFileCC command) when the PDF file refers to a non-embedded font.
It does not do any subsetting (yet), so the resulting PS files will be
large.
ということでdisplayNamedCIDFontTT -> fontFileに置き換えたら無事日本語フォントが適用されましたとさ。

.vimの整理

以前よりごちゃごちゃしていた.vim以下を整理するログ。 参考:vimテクニックバイブル しばらくvimを使っていて、それなりにプラグインも探していたとは思うのだが、 だいぶ勉強になる本だと思う。 さっき買った所でまだちゃんとよめていないけれども。 まず.vimはバックアップしたのち、全消去。 新たに一から組み立てる。 いくつかプラグインを導入するけども、まずプラグイン管理プラグインとしてvundleを導入する。 この辺が参考になるだろう。 Hack #215: Vundle で plugin をモダンに管理する vundleを使ってunite.vimとproject.tar.gzを導入。 この2つはまだ使いかたをよくしらないので、次回調べる。 目標はvimgdbのパッチを使わずにvimgdbを連携させること。 とりあえず今回はこんだけ。

makeでfor文

makeは非常に優秀なツールだ。 一度依存関係を書いておきさえすれば、それにしたがって勝手にコンパイルしてくれる。 しかも一つの言語ともいえるような機能を持っている。 例えば変数を持てる。Makefile中では ARG = value ARG := value の2つの代入の形式がある。 ちょっと複雑なことを使用とするとき、両者の違いは重要だ。 違いは右辺に変数が含まれるときに生じる。 前者は右辺に含まれる変数も代入された変数が評価されるときに初めて評価される。 一方後者では、代入時に右辺値が確定される。 C言語でいえば、前者は引数のない関数の定義、あるいは参照変数に近く、後者は通常の手続きにおける代入に対応する。 さらにmakeの機能にinclude文がある。 include sub.mk とすればsub.mkの中身を今読んでいるMakefileに追加したのち、コンパイルを行ってくれる。 もう一つ、makeには文字列操作に関して非常に強力な組み込みの関数がある。 今回使うのはword,words,wordlistの3つである。 これらはlistの管理を簡単にしてくれる関数である。 list = a b c d $(word 2,$(list)) # b $(words $(list)) # 4 $(wordlist 2,3,$(list)) # b c wordは所定の位置の要素を抜きだし、wordsはlistの長さを返し、wordlistは部分listをつくる。 さて、本題のfor文に取り掛かろう。 種を先にあかしておくと、再帰的にsub.mkを読み込むのである。 幸いにもinclude文は自分自身を読み込むことを拒否しないので、自己再帰的にfor文を構成する。 次のMakefileを見て欲しい。 main.mk:
TARGET = test
LIST = a b c d
all : $(TARGET)
$(TARGET) : subd
    touch $(TARGET)
include sub.mk
sub.mk:
VAR := $(word 1,$(LIST))
LIST := $(wordlist 2,$(words $(LIST)),$(LIST))
sub$(VAR):
    touch $@
ifneq ($(VAR),) # VARが空でなければ真
include sub.mk
endif
依存関係を解決するだけの簡単なMakefileだが、 制御が自己再帰的になっているので読みにくいかもしれない。 ポイントはsub.mkの2行目のLISTを一つずらすところと、 最後のsub.mkからsub.mkをincludeするところである。 LISTの先頭の値をVARに受け取り、LISTは一つ前につめる。 この変数は次のincludeのときにも引き継がれるので、 LISTが空になるまでsubdの生成方法が検索され続ける。 これによりsubdが4回目のincludeの後に発見され、めでたくtestはコンパイルされるのであった。 。。。とまぁ、for文が出来上がったのだが。 なんに使おうかな? これを調べている途中で、
LIST = a.c b.c c.c d.c
$(LIST) : %.c : %.o
    icpc $< -o $@
のような条件付きルールを書く方法を知り、当初の目的が果たせたので、 このfor文は今回必要ないのであった。