「ソフトウェア」の最近のエントリー

LaTeXに図を入れる方法~OOoを活用編

| | コメント(0) | トラックバック(0) LaTeXに図を入れる方法~OOoを活用編

猫も杓子もTeX,TeX,TeX.論文を書くといったら,TeXですよね~.Wordで論文を書くなんて,無駄な労力が発生しまくりで,生産性ががた落ちです.どの辺がダメかって?まず,論理と視覚が分離できない点.章の題字はゴシックのボールドなのに,本文が明朝だったりすると,フォントを変更しなくちゃいけないですよね?ちゃんと設定すればスタイルで変更できるかもしれませんが,うっかりBSしすぎたり,Delしすぎたりすると,書式が移っていやーんな状態になります.イライラします.それから,図表の位置とか,全体のページ数とかが見えちゃって,余計なことに気を取られて,本文の執筆が一向に進みません!イライラします.もちろん,Wordで論文を書くこともできるでしょうが,その苦労ったらありゃしない.それ,TeXなら一発なのに!ってのがいっぱいです.投稿先が変わって書式を変更しなくちゃいけなくなったときの悲劇とか考えてご覧よ.

閑話休題.というわけで,論文を書くためのツールと言えば,TeXですよね.しかしながら,TeXで論文を書く場合に直面するいくつかの問題があります.まずは表の挿入でしょうか?\hlineしまくると何が何だか分からなくなります.ある意味ではHTMLのTableよりも難しいです.しかし,そんなときはExcel2LaTeXを使えばばっちりだ!そして,もう1つの問題が図の挿入です.

実のところ,TeXで1番面倒なのが図の挿入かもしれません.図としては,BMPでもJPEGでもPNGでも挿入することができますが,ラスタ画像なので,拡大縮小に弱いです.となると,ベクタ画像を挿入したくなりますが,その場合にはEPSやらPDFやらに変更する必要があります.しかし,これがなかなかの曲者なのです!

おそらく,学生に何の指示もせずに「論文を書け」というと図はパワーポイントで描くはずです.これが早くも困難です.パワーポイントはEPSを出力する能力がありません.Office 2007以降であれば,PDFでの出力が可能です.しかし,この出力されたPDFがなんだかおかしくて,少なくともIllustrator10(こんな使い方しかしていないので,買い換えるモチベーションが沸きません.最新はCS5です.)では開けません.Acrobat9では開けます.

ここでいくつかの実例をご覧に入れます.例えば,パワポでこんな図を描いたとしましょう.

100831_01.jpg

これを選択してコピペでイラレに持って行くとこうなります.

100831_02.jpg

あははは!笑うしかない.本当にどうしようもありません.むぎゃ!続いて,パワポの図をDistillerでPDFにしてAcrobatからEPSに出力してイラレで開くと,ほら,ドン!

100831_03.jpg

こりゃダメだー.というわけで,試行錯誤すると,まともなEPSをはき出すためには,パワポの機能でPDFにしてAcrobatでEPS出力します.

でも,よく思い出してみると,奥村先生の美文書第7章にも書いてあるように,PDFをそのまま読み込んでも良いように思います.そこで,色々な失敗例をご覧に入れたいと思います.文章は著作権フリーな自由文庫から,ラッセル・アインシュタイン宣言の一部を利用させていただきました.なお,PDFの挿入にはBBファイルが必要になるので,mediabb.styを使って楽しています.当然のことながら,文章と図には一切の関係がありません.あくまで出力サンプルとして見て下さい.

まずは理想型をご覧に入れましょう.パワポからPDFとして保存してAcrobatからEPS出力してイラレで微調整したEPSを図として挿入したパターンです.

100831_05.jpg

こうなって欲しいです.こうなることを目指して,色々と試してきましょう!次はパワポをPDFとして保存したやつを図として挿入してみます.

100831_04.jpg

うーん.何故か図がいない・・・.どうしてこうなった!これじゃダメだ!続いて,パワポからDistillerしてAcrobatからEPS出力したものを図として挿入してみました.

100831_06.jpg

図はいるけど余白がおかしいし,BBもおかしいっぽい.うーんうーん.こうなってしまいます.

というわけで,困難がいっぱいあるパワポからTeXへの図の挿入ですが,私は今まで以下の手順でやっていました.パワポの図をパワポの機能(Distillerを使うと上手くいかない)でPDFに出力,AcrobatからEPS出力,イラレで開いて微調整してアウトライン作成してEPSで保存,TeXでincludegraphicsしてclipする.色々と試行錯誤して,イラレで図を描いたり,TeXのpicture環境を駆使したこともありました.しかし,結局は発表に使い回せる的な意味で,パワポに戻ってきました.パワポかわいいよパワポ.

しかし,よく考えてみて下さい.研究室であれば,Microsoft Officeは入っていて,パワポは当然のように使えるでしょう.しかし,イラレやアクロバットまで入っていることは稀でしょう.さらに言えば,自宅PCにパワポはまだしも,イラレやアクロバットを導入している人はそうそう多くないと思います.なんといっても,オレが持ってないくらいだからね!そう考えると,パワポの図をイラレやアクロバット経由でTeXに挿入するという技は使いにくいです.代替手法はないのか!と思うわけです.

さぁ,前置きが長くなりましたが,いよいよ核心です.オープンソースソフトウェアであるOpenOffice.org(以下OOo)を使えばいいわけです.OOoはMicrosoft Officeとほぼ同等のことができるオープンソースソフトウェアです.オープンソースなので,無償で使うことができます.そのくせに,PDFやEPSを始め,FlashやSVGなどの多彩なフォーマットへの出力が可能であり,実にアメージングなソフトウェアです.もちろん,無償です.大事なことなので2回言いました.

100831_07.jpg

このような感じでパワポのファイルを開いて,そのまま他形式に出力できます.そんなOOoを使った場合の例を見てみましょう.まずは,パワポのファイルをOOoで開いて,PDFで出力したものを図として挿入してみました.ポイントとしては,エクスポート時のオプションとして選択範囲を選んでおくことです.さぁ,PDFの取り込みは上手くいくか!?

100831_08.jpg

大変にガッカリです.ガッカリしすぎて涙も涸れる.仕方がないので,EPSを試しましょう.

100831_09.jpg

キタコレ!これで安心だね!

まとめ:
パワポで描いた図をTeXに挿入するためには,以下の方法が確実と思われる.

  • パワポの出力機能でPDFにしてAcrobatからEPSに出力しイラレで微調整してEPSにする
  • OOoでパワポのファイルを開いて,選択範囲を選んでEPSにする

特に,OOoはオープンソースソフトウェアで無償なので,オススメです.というか,パワポを使わずに,最初からOOoで図を描けばいいのではないかと思います. 

関連:
LaTeXにPDFを図として挿入するのは簡単 - 4403 is written
Ubuntuで実現する無料の研究勉強環境 - 4403 is written
Excel2LaTeXにありがちな事 - 4403 is written
ヘタレ系DのBlog(終了しました): Wordで論文を書くなんて異常

今年の夏の自由研究を何やるか決められないまま,既に夏休みなんですが,どうしましょう?と思っていたところ,タイミング良くGoogle Developer Day 2010 DevQuizが始まったので,参加中です.脳味噌コネコネにはちょうど良いです.昨晩から参加して,今のところ,こんな感じです.

gdd2010devquiz01.jpg

とりま,共通問題はサクッとやって,HTML5間違い探しもサクッと終わらせました.認証屋としてOAuthができないなんてのは問題外(というかそういうデモを作ってるくらいだし)なので,サクッとPHPで挑戦して,ムリってなりましたw.Pecl::OAuthはダメだ.realm指定できないw.仕方ないから,PerlのOAuth::Lite::Consumerを使いました.パッケージのスペルを間違えてて,小一時間悩んだのは良い思い出.

んで.Top Favoritesは一切何も関与していないことが判明したので,Super HackersかNext Generationに応募するしかないので,とりま,Super Hackersに挑戦中です.Shiritoriはレベル2までは適当にブルートフォース的なやり方で解けました.レベル3は単語数が増えるので,プログラムを作って挑みました.実装はC言語で.なんとなくPerlの方が簡単に作れた気がする・・・.久しぶりの再帰関数にちょっと四苦八苦しながら,見事に撃退しました.ちなみに,相手の手のことを考えていなくて,解答が合わなくてパニクったのは良い思い出.

残るはPAC-MANとエッセイです.エッセイはまぁいいとして,PAC-MANをどうするかなぁ・・・.これは結構マジで作らないとダメだよなぁ・・・.そもそもパックマンは苦手だから,戦略アルゴリズムを立てられないorz.

というわけで,PAC-MANは置いておいて,解けそうなGoogle Maps APIをやろうかなと思っている.中身的にはMaps APIを叩いて最短経路問題を解けばいいので,高々10点らしいので,マップを作ってダイクストラ法の総当たりでフィニッシュです.でも,手元にダイクストラのプログラムがないので,自宅に戻ってから.

まとめ:
なかなか楽しいです!全く解法が解らないでもなく,めちゃめちゃ簡単でもない.挑戦しがいのある問題って素敵.ところで,GDD2010の開催日が9月28日で,大腸内視鏡検査とバッティングしそうなんだよね・・・.無駄に終わるかもしれんorz.

CSS2010公式サイトを見ていたところ,会場アクセスのページに「晴れの国 岡山の天気」と書いてあって,「何故ここだけ・・・」と思ってしまったので,うっかりな勢いでブックマークレットを書いた.なお,CSS2010公式サイトはフレームを使っているので,普通にブックマークレットを書いたら動かなくて,比較的本気で調べてしまったことを申し添えておきます.

javascript:(function(){var w=frames.length?frames:[window],i,j,d,r;for(j=0;j<w.length;j++){d=w[j].document,r=d.evaluate('//text() [contains(.,"岡山")]',d,null,7,null),i=0;while(d=r.snapshotItem(i++))d.data=d.data.replace(/岡山/g,'晴れの国 岡山');}})();

主にはCSS2010公式サイトでお楽しみ下さい.

うまく表示できない・・・.

javascript:(function() {
var w=frames.length?frames:[window],i,j,d,r;
for(j=0;j<w.length;j++){
d=w[j].document,i=0,
r=d.evaluate('//text() [contains(.,"岡山")]',d,null,7,null);
while(d=r.snapshotItem(i++))
d.data=d.data.replace(/岡山/g,'晴れの国 岡山');
}
})();

201007151531追記:

@k4403 「岡山」を「大都会岡山」に置き換えるブックマークレットもお願いします><

Twitter / tohki: @k4403 「岡山」を「大都会岡山」に置き換えるブ ...

要望を頂いたので.

javascript:(function() {
var w=frames.length?frames:[window],i,j,d,r;
for(j=0;j<w.length;j++){
d=w[j].document,i=0,
r=d.evaluate('//text() [contains(.,"岡山")]',d,null,7,null);
while(d=r.snapshotItem(i++))
d.data=d.data.replace(/岡山/g,'大都会岡山');
}
})();

参考:
「ツイート」を「ツイート(笑)」に置換するブックマークレット - IT戦記
Bookmarkletサンプル集 / ナビゲーション補助

もうすぐiOS4が使えるようになるわけですが,JBerとしてはすぐに更新することができません.どうもiOS4でもボクが欲する機能はまだ実装されないようです.実装予定があるのかどうかも知らないけど.

実のところ,JBをしている理由は実に些細なのです.JBしている理由は以下の通り.

  • ロック画面にカレンダーを出したい(Lock Calendar)
  • 1画面にアイコンをいっぱい置きたい(Five Icon Dock, Five-Column SpringBoard)
  • SBSettingで設定をサクサク変更(SBSetting)
  • 正確な時間をボクに!(gNTPdate)
  • 着信拒否したいです(iBlacklist)
  • 3GでもSkypeしたいよ(VoIPover3G)
  • テザリング(PdaNet)

Skypeが3G対応になったり,フォルダ機能が搭載されたりで,実のところ,上に挙げた中で本当に欲しいのは,ロック画面にカレンダーとSBSettingと時間と着拒くらいです.これらを公式iOSに積んでくれれば,JBerをやめるのはやぶさかではありません.

などと寝言を言っており,事実とは限らない.

ちょっと所用があるので,OpenID AXを喋るOPとRPを作ってみた.厳密に言えば,作ってみたではなく動かしてみたなのだが,ネット上に情報が少なすぎる,特にAXを喋るOPの情報が皆無に等しいので,とりあえず書き残しておく.実装はOpenID EnaledのPHP Library 2.1.3を使いました.PHPは5.3系で上手く動かなかったので,5.2.10でやってます.apacheは2.2.12で,OSはUbuntu9.10x64です.

AXを喋るRPの実装
まずはRP側を実装します.examples/consumerを用いれば,RPは簡単に作れます.しかも,サンプルにはSregとPAPEを喋るように作られています.まぁ,普通に動かせば動きます.んで,AXを喋るようにいくつか改変します.基本的にAXはSregやPAPEと同じExtensionなので,参考にしながら実装します.実装に当たっては,当然ながらドキュメントが役に立ちます.読みにくいですけどね.

まずはcommon.phpをいじります.AX関連のクラスを使いますので,doIncludes()の中で,require_once "Auth/OpenID/AX.php"しておきます.papeを参考にすると,ここらで定数などを宣言しているようなので,真似して宣言してみます.

global $ax_attributes;
$ax_attributes = array(
'http://axschema.org/contact/email' => 'email',
'http://axschema.org/namePerson/first' => 'firstname',
'http://axschema.org/namePerson/friendly' => 'nickname');

属性型はaxschema.orgにしました.別に他のでもいいです.次に,index.phpを変更しておきましょう.PAPEの部分を参考にします.というか,バッサリ頂きます.61-67行目を以下のように置き換えちゃいます.

<p>Optionally, request these attributes:</p>
<p>
<?php
global $ax_attributes;
while (list($key, $val) = each($ax_attributes)) {
print "<input type=\"checkbox\" name=\"ax_requests[]\" value=\"$key\" checked />";
print "$key ($val)<br/>";
}
?>
</p>

恐ろしく汚いコードですが,気にしたら負けです.これでIdentifierの入力を尋ねるときに,要求するAXを3種類の中から選べるようにしています.デフォでは全部ONですが. 続いて,try_auth.phpをいじります.まず,SregとPAPEは使わないので,該当部分をバッサリ捨てます.30-38行目がSregで,40-45行目がPAPEなので,コメントアウトするなり,消しちゃうなりして,機能しないようにしておきます.で,この辺りにAXのコードを書きます.

global $ax_attributes;
$ax = new Auth_OpenID_AX_FetchRequest;
$ax_uris = $_GET['ax_requests'];
while (list($key, $val) = each($ax_attributes)) {
if(in_array($key, $ax_uris)) {
$attribute[] = Auth_OpenID_AX_AttrInfo::make($key, 1, false, $val);
}
}
foreach($attribute as $attr){
$ax->add($attr);
}
$auth_request->addExtension($ax);

3行目の$_GET['ax_requests']はindex.phpのチェックボックスからどの属性がリクエストされたかを受け取っています.そんで,その要求されたものを$ax_attributesと比較して,Auth_OpenID_AX_AttrInfoでAXのリクエストにあうように準備します.ちなみに,第3パラメータをfalseにしているので,要求する属性はrequiredではなく,if_availableになります.そんで,$ax->add($attr)で属性セットして,最後にextensionに追加する感じです.このコードはExample usage of AX in PHP OpenIDを参考にしました. 最後に,finish_auth.phpを書き換えます.SregとPAPEを無効にしたいので,41-90行目をバッサリと消しちゃいます.かわりにAX用のコードを書き加えます.

$ax_resp = Auth_OpenID_AX_FetchResponse::fromSuccessResponse($response);
$success .= '<dl>';
global $ax_attributes;
while (list($key, $val) = each($ax_attributes)) {
$ax_data = $ax_resp->get($key);
if(!is_object($ax_data)) {
$success .= "<dt>$val</dt><dd>$ax_data[0]</dd>";
}
}
$success .= '</dl>';

特に解説することもないですけど,AXの返事は$ax_resp->get($key)で取り出しています.Type URIをキーにして,取り出しています.コードを見ればわかると思いますが,何を要求したとか,何が返ってきているかは全く考えず,総当たりにチェックしてます.大した量じゃないし.そのため,!is_object($ax_data)でエラートラップしてます.getメソッドで取り出せなかった場合,Auth_OpenID_AX_Errorが返りますので,objectかどうかのチェックでさばいてます.複数の属性値が返ってきた場合でも最初の1つしか表示しない辺りも詰めが甘いですが,気にしないで下さい.気になるなら,自分で工夫して下さい.僕には必要がないコードです.

AXを喋るOPの実装
続いて,OP側を作ります.examples/serverを使います.こっちが結構に四苦八苦でした.勝手にはまっただけですけど.こっちはいじくる部分はlib/common.phpだけです.まずはSregとPAPE関連を消し去ります.56-76行目がSregで,PAPEは・・・ないですねw.なんだ,OP側のPAPEは実装されていないのか.それはそれとして,Sregが書いてあった辺りに,AX用のコードを書き加えます.

$attributes = array(
'http://axschema.org/contact/email' => 'email@example.com',
'http://axschema.org/namePerson/first' => 'first',
'http://axschema.org/namePerson/friendly' => 'nickname');

$ax_requested = Auth_OpenID_AX_FetchRequest::fromOpenIDRequest($info)->getExtensionArgs();
$ax_response = new Auth_OpenID_AX_FetchResponse();
while (list($key, $val) = each($attributes)) {
if(in_array($key, $ax_requested)) {
$ax_response->addValue($key, $val);
}
}
$ax_response->toMessage($response->fields);

実に,このコードにたどり着くまでに苦労しました.何にはまっていたかは後で説明します.コードは解説する必要がないくらいに簡単です.$attributes[]の中は連想配列でType URIをキーとして,属性値を格納します.んで,$ax_requestedと比較して,リクエストされている属性を$ax_response->addValue($key, $val)でセットします.以上で完成です.

動作例

100608_ax01.jpg

これはRP側です.http://localhost/rp/です.色々突っ込みどころ満載ですが,デモですからスルーして下さい.Identity URLのところに,自前OPのidentifierを入れます.AXで取得したい属性をチェックボックスから選びます.とりま,全選択で.

100608_ax02.jpg

こっちがOPからの返事を受け取ったRPです.要求した属性が全て取得できています.下の黄枠の中にはAXレスポンスの内容を表示してみました.aliasがext0とかになっています.ここで勝手にはまってました.結果的には,これで良かったんです.問題なく動いています.これで,AXを喋るOPとRPはできあがるので,後は煮るなり焼くなり・・・.

はまったところ
OPのレスポンスを作るのに苦労しました.というか,勝手な思い込みです.上記で示したOPのコードでAXを喋らせると,違和感があったんです.例えばこのエントリで例示されているように,ax.type.nicknameでリクエストしたら,レスポンスもax.type.nicknameとax.value.nicknameで返るのが自然のように思います.僕もそうだと思っていましたが,これは全然どうでもいい話でした.ここで例示したnicknameというのはaliasなのですが,aliasについては以下のように書かれています.

The <alias> will further be used to identify the attribute being exchanged.

Final: OpenID Attribute Exchange 1.0 - Final

とのことですので,identify出来ればよろしく,何だっていいようです.なので,ここではnicknameでリクエストしても,レスポンスはfriendlyでもいいわけです.問題なのはレスポンスにおいて,friendly.typeとfriendly.valueが紐付きますので,その関係だけが保たれていれば,aliasは何でもokです.

実際のところ,上記に示したOPが喋るAXはレスポンスとしてext0とかext1というaliasを張ります.最初はこれをみて正規の手続きでやっていないからまずいんだと思い込み,AX.phpやMessage.phpの中にまでダイブして,色々と調査しました.で,追っていけば行くほど,コードは正しそうに見えてくるんです.おかしいと思って確認してみたところ,さっきの例示したエントリでも,profile_imgでリクエストして,imageでレスポンスされている.つまりまとめると,重要なのはaliasではなく,Type URIである.aliasは一意ではない(そんな定めがない)が,Type URIは一意なので,そっちを見ろということである.なるほど当然の道理である.

まとめ:
PHPでAXを喋るOPとRPを実装した.簡易版なので,色々と不十分な実装(特にOP側)となっているが,実証コードには十分である.

201006081344追記:
Apache License 2.0らしいので,10はてブ超えたらソースコード公開する.

あわせて読みたい