Hatena::ブログ(Diary)

hishidaのblog このページをアンテナに追加 RSSフィード

プロフィール

hishida

hishida

EB series support page 管理人 ブログ

2017-02-24

[]読書尚友に底本出版社別リストを追加

青空文庫HPでは、1万冊を超える作品を探すためのインデックスとして、作家別(作家名の五十音順)、作品別(作品名の五十音順)の総合インデックスと、分野別リスト(日本十進分類)が提供されている。

大体の青空文庫ビューアアプリでは、作家別・作品別のインデックスはあるが、分野別のインデックスを実装しているものは少ない。読書尚友を開発するとき、分野別リストを実現することは目標の一つだった。

読書尚友では、これに加えて、公開年別リストも提供している。青空文庫では毎年1月1日に、その年著作権が切れる著者の作品を一斉に公開する慣例があり、毎年の年初の公開作品を見たい場合があるので、年月日別のインデックスがあれば便利ではないかと思ったためだ。また、青空文庫に最初に登録された作品は何だったかなど、青空文庫の歴史を調べたいときにも役立つ。

今回さらに、新たな試みとして、底本出版社別リストを追加した。たとえば、底本出版社別リスト→「作品社」(底本出版社)→「日本の名随筆10 山」(底本)→「槍ヶ岳第三回登山」のような読み方ができる。

f:id:hishida:20170224232741p:image

f:id:hishida:20170224232810p:image

f:id:hishida:20170224232842p:image

f:id:hishida:20170224232837p:image

実は「えあ草紙」というビューアに、特別企画ページとして「日本の名随筆、花の名随筆」や「推理作家一覧」などがあるのがヒントになった。面白いと思ったが、テーマ別だとメンテナンス作業がネックになる。底本出版社別ならメンテナンス不要で、青空文庫の収録冊数が増えるほど有益性が増すと思う。

一つの問題は、作品や作家のようにID化されていないので、同じ底本でも表記が異なるとインデックスが分かれてしまうこと(例:「山と渓谷社」と「山と溪谷社」、「学芸書林」と「學藝書林」)。全角空白を半角化するなど正規化を試みているが、完全にはいかない。

まだ発展途上だが、新しい青空文庫の読み方を一つ提案できたと思う。

2017-02-12

[]EBWin4,EBMacに「全ての項目の表示」を実装

EBWin4、EBMacに、「検索に一致した全ての項目の表示」機能を追加した。(EBPocket for iOS/Androidも順次対応予定)

これまではEBシリーズの本文の表示モードには、連続表示と項目毎表示しかなかった。

連続表示とは、見出し語の本文を表示する場合に、後続の本文のテキストを続けて表示するモードである。

通常の連続表示の例:検索語 detectiveの後続のテキストを続けて表示

f:id:hishida:20170212101225p:image

EPWINGはもともと「電子書籍」を目指しており、一冊の本、または巻物のように、最初から最後まで通読できるようになっている。このため、EPWINGの公式ビューアであるCDView(富士通)、Viewing(イースト)、こととい(岩波書店)では、基本的に連続表示を行うようになっていた。(今ではWindows10インストールできるかどうかも定かでない)

項目毎表示は、検索に一致した見出し語の本文だけを表示するモードである。ただしEPWINGには項目の区切りという概念がないので、ソフトウェアで何の識別子を項目の区切りにするかを決定しないといけない。これはたぶんUnix上のEPWING検索システムや、DDWinのようなサードパーティ製のEPWINGビューアで出てきた概念だと思う。

今回追加した「全ての項目の表示」とは、検索に一致した見出し語の本文だけを、すべて一覧で表示するものであり、項目毎表示の結果を連結したものと考えるといいと思う。

全ての項目の表示:検索に一致した語の本文を連結表示

f:id:hishida:20170212101244p:image

実はDDWinにはこの機能が昔からあり(項目表示→全て表示)、今でもこの機能のためにDDWinを利用しているというユーザもいらっしゃるらしい。

DDWinには一つ制約があり、串刺し検索で「全て表示」を行なった場合に、先頭の辞書しか外字が表示できなかった。EBWin4/EBMacでは全ての辞書の外字を表示するようになっている。これについては、EBWin3.xまではWin32 APIで描画していたため拡張が難しかったが、EBWin4では本文表示をWebブラウザコントロールで行っているため、実現がしやすくなった。

既知の問題点、および制約についても書いておくと、

  1. 「全ての項目の表示」では縦書き設定は解除される
  2. 「全ての項目の表示」を行なった後で、検索一致リストから項目を選択した時の挙動は、通常の連続表示/項目毎表示に戻る
  3. 串刺し検索の場合、リンクが働くのは先頭の辞書のみ

複合検索のモードレス化

もう一つ、EBWin4で改良を行なったのは、複合検索のダイアログがこれまでモーダルポップアップだったのを、モードレスポップアップに変更した。複合検索ダイアログを表示したままで、次々と絞り込み検索をするという使い方が可能になった。

モードレスにしようとすると、ダイアログ側から、メインスレッドの表示処理をコールバックする必要がある。C++だと関数ポインタ引数で渡すような実装方法になるが、C#にはdelegateという機能があり、もう少し美しく実装できる。

モーダルダイアログの場合。OKボタンを押すまで制御が帰ってこない。OKボタンを押すとダイアログが消え、メインスレッドで検索結果を表示する。

	ComplexSearchDialog cplx_dlg = new ComplexSearchDialog();
	cplx_dlg.Owner = this;
	// モーダルダイアログとして表示
	if (dlg.ShowDialog() == DialogResult.OK)
	{
		// 検索結果を表示
	}

モードレスダイアログの場合。ダイアログを表示したまま、検索結果をメインスレッドで表示する。

	private ComplexSearchDialog cplx_dlg = null

	if ( cplx_dlg == null || cplx_dlg.IsDisposed)   // 二重起動を防ぐ
	{
		cplx_dlg = new ComplexSearchDialog();
		cplx_dlg.RefreshEvent += delegate(object sender, EventArgs e)
		{
			// 検索結果を表示
		};
		cplx_dlg.Owner = this;

		// モードレスダイアログを表示する
		cplx_dlg.Show();
	}					

ダイアログ

public partial class ComplexSearchDialog : Form
{

	public delegate void RefreshEventHandler(object sender, EventArgs e);
	public event RefreshEventHandler RefreshEvent;

	// OKボタンが押された場合
        private void okButton_Click(object sender, EventArgs e)
        {
		//検索処理をする
		(略)
		//  検索結果をメインスレッドで表示する
		this.RefreshEvent(this, new EventArgs());
 }

2017-02-04

[][] EBPocket for iOS サスペンドからの復帰で異常終了する件が解決

「EBPocket for iOS が、サスペンドからの復帰時に異常終了する」という報告が以前から上がっており、なかなか原因が分からなくて頭を悩ませていたが、どうやら解決できたと思う。

iOSアプリプロセスのライフサイクルについて

まず前提として、iOSアプリには「終了させる」という概念がない。(実際、アプリに終了ボタンをつけると審査でリジェクトされる)

アプリを切り替えて(1)アクティブから(2)バックグラウンドになると、しばらくして(3)サスペンドに移行し、メモリが少なくなるとiOSから自動的に終了させられ、(4)停止状態になる。

iOSアプリの状態遷移とライフサイクル - Qiita

ホームボタンを押してアプリ選択した場合、(3)サスペンドから(1)アクティブに復帰する場合と、(4)停止状態から起動されて(1)アクティブになる場合がある。このうち、後者の(4)から(1)のときに異常終了していたらしい。

これを再現しようと思うと、他にメモリを占有するアプリを多数立ち上げて、メモリ不足の状況をつくらないといけない。

私は普段はAndroidを使っていてiOSは実機デバッグでしか使用しないので、なかなか気付かなかった。

ところが最近、あるきっかけでiOSを日常的に使うようになった。

キャンペーンでキクタアプリを買う

昨年末頃にAppStoreのセールで、次のキクタアプリが通常480円のところ全品120円になっており、紙の書籍より大幅に安いので、まとめ買いした。

iPhoneアプリ「キクタン」で、効率的に英単語学習!:アルク

それでiPod touchを毎日使うようになり、キクタンを同時に立ち上げてからEBPocketに戻ると、ユーザからの報告通りに異常終了することがわかった。

Xcodeでのクラッシュログの取得方法

再現さえすれば対処が可能になる。Xcodeデバッグモードで実行し、異常終了させてから、次の手順でクラッシュログが取得できる。

Menu→Windows→Devices

左側ペインの[DEVICES]から実機を選択し、[View Device Logs]を押すとクラッシュログが表示される。デバッグモジュールなので、異常終了したソースの箇所がわかる。

f:id:hishida:20170204105835p:image

その結果、NSString::drawAtPoint:forWidth:withFont:lineBreakMode:で落ちていることがわかった。このメソッドはiOS7からdeprecated(非推奨)になっているもので、

これをiOS7以後の推奨メソッドの NSString::drawAtPoint:withAttributes: に変えたら落ちなくなった。

iOSは毎年メジャーバージョンが上がり、その度に使用できていたAPIが使用できなくなったりするので、メンテナンスを続けていかないとiOSバージョンアップで使用できなくなることがある。

もしかすると二年ぐらい前から異常終了するようになっていた可能性がある。これで、失った信頼が回復できるといいのだが。

EBPocket FreeをEBPocket Basicに改称した経緯

今回提出にあたって一つトラブルがあった。Pro版はすぐに審査が通って公開されたが、EBPocet Freeの方が、アプリ名に"free"が入っていることが原因で、metadata rejectを食らった。

2. 3 PERFORMANCE: ACCURATE METADATA

Performance - 2.3.7

Your app's name to be displayed on the App Store includes references to your app’s price, which is not considered part of an app name.

Next Steps

Please remove any references to your app’s price from your app’s name, including any references to your app being free or discounted. If you would like to advertise changes to your app’s price, it would be appropriate to include this information in the app description. Changes to your app’s price can be made in the Pricing and Availability section of iTunes Connect.

アプリ名に価格を含んでいるといけないらしい。だがAppStore には free という名称を含むアプリがごまんとある。理不尽だが、Appleと戦っても勝てないので、あきらめてアプリ名を EBPocket Basic に変えて再提出したら、あっさり審査に通って公開された。

EBPocket FreeをアップデートしたらEBPocket Basicに変わってしまって驚かれるかもしれないが、これはAppleの審査のためで、内容は同じなのでご理解いただきたい。

P.S.

前述のキクタアプリはとてもよく出来ていて、音声も収録されているので、紙の書籍よりアプリの方がいいと思う。残念ながらiOSのみで、Android版は提供されていない。おかげで通勤時にキクタンを聞く習慣ができた。

2016-12-28

[] 読書尚友のSDカード対応他

以前、Nexus5が故障してASUS Zenfone 3 Laser を購入した顛末を書いた。

Nexus 5 恐怖の無限ループからの脱出 - hishidaのblog

性能的には3年前のNexus 5 と際立った違いはない気もするが、Zenfone 3 Laserに変えて良かったことの一つは、外部Micro SDカードが使えるようになったことである。Nexus5 では外部SDカードによる拡張ができなかったので、拙作の読書尚友でも外部SD青空文庫データを置くことができなかった(ということに作者が気づかなかった)。予定外の出費だったが、外部SDカードに対応できるようになったという意味では怪我の功名だったと思う。

Androidでの外部ストレージの扱いはAPIバージョンごとに変遷がある。古き良き時代はWRITE_EXTERNAL_STORAGEのパーミッションがあれば外部ストレージのどこにでも読み書きができた。ところが、kitkat Android4.4以降、外部SDカードへの書き込みが制限されるようになり、Storage Access Frameworkを使わないとSDカードにアクセスできなくなった。これは通常のファイルアクセスのAPIと異なるのが問題で、内部ストレージと同様に透過的にアクセスできないと、アプリが複雑になってしまう。

実は簡単な解決策があった。外部ストレージのパスの取得にgetExternalFilesDirを使うと、<外部ストレージのルート>/Android/data/<パッケージ名>/filesというアプリ専用のフォルダが作成される。(ここで「外部ストレージ」というのは実は外部SDカードのことではなく、内蔵メモリなのでややこしい。)

ここで外部SDカードをマウントしていた場合、外部SDカードのルート下にも同時に、/Android/data/<パッケージ名>/filesが自動的に作られる。この領域は外部SDカードであっても、アプリから読み書きが許されている。

簡単なことだが、Nexus5ではSDカードを拡張できないので、この挙動がわからなかった。

今回、読書尚友のデータ保存先を、外部SDカードAndroid/data/<パッケージ名>/filesに変更できるようにした。これで、メモリの少ない端末の場合、青空文庫の全データをSDカードに逃がせるようになった。

2016-12-23

[] 僕は明日、昨日の君とデートする

青春恋愛映画の名手といわれる、三木孝浩監督の最新作。この監督のすべての作品を見たわけではないが、『陽だまりの彼女』『くちびるに歌を』は特にお気に入りである。今年は『青空エール』とこの2作品が公開され、三木監督ファンとしてはうれしい限りだ。

美大生の主人公は通学電車で出会った初対面の彼女に一目惚れする。とんとん拍子に理想的な恋人同士になるが、彼女には秘密があり、なぜか彼の未来を知っているようだった‥

ファンタジー色の強い作品で、ネタバレすると、彼女は平行世界からきた別の世界の住人であり、5年ごとに40日しか滞在できないというルールがある。二つの世界はそっくりだが、時間の流れが反対であり、彼が5歳の時彼女は35歳、彼が10歳の時彼女は30歳、彼が15歳の時彼女は25歳‥という関係になっている。二人が同じ20歳で会えるのは、現在の40日しかない。物語を成り立たせる舞台装置を理解すると、この40日の物語がとてもせつなく、貴重で愛おしいものに見えてくる。

時間の流れが逆なら会話も全て逆回しになるはずじゃないかとか、SF的考証がどうとかは、ファンタジーだから棚上げしていい。たぶん二つの地球は公転が逆で自転方向が同じということにしておこう。素直に感動できる良作に仕上がっていた。

原作者はいわゆるライトノベル出身だが、作者自身の経験や年輪が加わってくると、純文学や文芸作品との差はなくなってくる。むしろ現代では、漫画原作やライトノベルの作者のなかに、昔であれば文豪になったような才能が集まっているのかもしれない、と感じた。