Nov 27, 2009
Gtk2-Perl で Pango を使った縦書きビューワーを作ろう(3)
2ch Linux板の「GTK+プログラミング」というスレで、今まで躓いていた部分の解決法を教えて貰えました。事前にテキストを描画したオブジェクトが既にあるのかどうかを確かめて、あるようなら次の描画の為に destroy しちゃうという方法です。
言われてみればなぜ今まで気がつかなかったんだろうという感じのとても真っ当な方法なのですけども、独学の悲しさ、自分ではそこに気がつかないままになってしまっていました。教えてくれた方、本当にありがとうございます。まだまだ細々した部分は後回しにして放置しているのがほとんどの状態ですけども、とりあえずは複数ページに渡るテキストを読み進めたり戻ったりする事が出来るようになりました。
ぽちたて 20091127
#!/usr/bin/perl
use strict;
use warnings;
use utf8;
use Encode;
use Encode::Guess qw/euc-jp shiftjis iso-2022-jp/; # 日本語文字コード判別
use Lingua::JA::Fold qw/fold/; # 日本語禁則折り返し
use open IN => ":bytes"; # 入力される文字コードが混在している可能性があるので
use Gtk2 qw/-init/;
use Glib qw/TRUE FALSE/; # TRUE, FALSE で真偽値を扱う
use Cairo;
use Math::Trig qw/pip2/; # π/2 のラジアン値を使う
my $window_width = 605; # ウィンドウの幅
my $window_height = 693; # ウィンドウの高さ
my $surface_width = 600; # テキスト表示部の幅
my $surface_height = 661; # テキスト表示部の高さ
my $font_and_size = 'IPA明朝 11'; # フォント名とサイズ
my $background_color = '#e0ffff'; # 背景色
my $line_spacing = 19400; # 行間隔(1/1024ポイント単位)
my $fold_length = 42; # 文庫本 1ページの一般的な一行の文字数
my $page_lines = 18; # 文庫本 1ページの一般的な行数
my $shiori_file = 'pochitate_shiori.txt'; # 栞ファイルの場所
my @offsets = (0); # 既読ページのテキストデータ読み出し後のオフセット配列
my $page = 0; # 現在表示しているページ
my $shiori_page; # 栞を挟んだページ
my $filename; # 読み込むテキストファイル名
# メニューバーを作る為の下準備
my $entries = [
[ 'FileMenu', undef, 'ファイル(_F)' ],
[ 'Open', 'gtk-open', '開く(_O)...', '<ctrl>O', 'ファイルを開く',
\&text_file_open],
[ 'Save', 'gtk-save', '栞を挟む(_S)', '<ctrl>S', '栞を挟む', \&shiori ],
[ 'Quit', 'gtk-quit', '終了(_Q)', '<ctrl>Q', '終了する',
sub { Gtk2->main_quit; } ],
];
my $menu_info = <<'EOS';
<ui>
<menubar name='MenuBar'>
<menu action='FileMenu'>
<menuitem action='Open' position='top'/>
<menuitem action='Save'/>
<separator/>
<menuitem action='Quit'/>
</menu>
</menubar>
</ui>
EOS
# GTK+ のバージョンを確認
die "This viewer requires GTK+ 2.4.0, but we're compiled for "
. join '.', Gtk2->GET_VERSION_INFO. "\n"
unless Gtk2->CHECK_VERSION(2, 4, 0);
# 引き数が無くても栞があったら自動復帰
unless ($filename = $ARGV[0]){
if (-e $shiori_file){
open my $fh, '<', $shiori_file
or die "Couldn't open $shiori_file for reading: $!\n";
my $shiori_data = <$fh>;
close $fh;
chomp $shiori_data;
($filename, $offsets[0], $shiori_page) = split ' ', $shiori_data;
($ARGV[0], $page) = ($filename, $shiori_page);
}
}
# テキストファイルからの読み込みと文字コード判定・折り返し処理
my $strs;
foreach (@ARGV){
open my $fh, '<', $_ or die "Couldn't open $filename for reading: $!\n";
my $content .= join '', <$fh>;
close $fh;
my $guess = guess_encoding($content);
ref $guess or die "Couldn't guess: $guess\n";
open $fh, '<', $_ or die "Couldn't open $filename for reading: $!\n";
while (<$fh>){
$_ = $guess->decode($_);
$strs .= fold( 'text' => $_, 'length' => $fold_length,
'mode' => 'traditional' );
}
close $fh;
}
# メインウィンドウの準備
my $window = Gtk2::Window->new('toplevel');
$window->signal_connect('delete_event' => sub { Gtk2->main_quit; });
$window->set_title('ぽちたて 20091127');
$window->set_default_size($window_width, $window_height);
# 垂直ボックスとその第一段の中に水平ボックスを配置
my $vbox = Gtk2::VBox->new(FALSE, 2);
my $hbox = Gtk2::HBox->new(FALSE, 0);
$vbox->pack_start($hbox, FALSE, FALSE, 0);
# メニューバーの作成
my $ui = Gtk2::UIManager->new;
my $accelgroup = $ui->get_accel_group;
$window->add_accel_group($accelgroup);
my $actions = Gtk2::ActionGroup->new('actions');
$actions->add_actions ($entries, undef);
$ui->insert_action_group($actions, 0);
$ui->add_ui_from_string ($menu_info);
my $menubar = $ui->get_widget('/MenuBar');
# ページ移動ボタンの作成
my $button_next = Gtk2::Button->new;
my $icon_next = Gtk2::Image->new_from_stock('gtk-go-back', 'menu');
$button_next->set_image($icon_next);
$button_next->signal_connect('clicked' => \&next_ward);
my $button_prev = Gtk2::Button->new;
my $icon_prev = Gtk2::Image->new_from_stock('gtk-go-forward', 'menu');
$button_prev->set_image($icon_prev);
$button_prev->signal_connect('clicked' => \&prev_ward);
# ページ数表示部の作成
my $page_count = Gtk2::Label->new("- $page -");
# 垂直ボックス第一段の水平ボックスへ各ウィジェットの配置
$hbox->pack_start($menubar, FALSE, FALSE, 0);
$hbox->pack_start($button_next, FALSE, FALSE, 0);
$hbox->pack_start($button_prev, FALSE, FALSE, 0);
$hbox->pack_start($page_count, TRUE, TRUE, 0);
# スクロール付きウィンドウを作成して垂直ボックスの第二段に配置
my $scrolled_window = Gtk2::ScrolledWindow->new(undef, undef);
$scrolled_window->set_policy('automatic', 'automatic');
$vbox->pack_start($scrolled_window, TRUE, TRUE, 0);
open my $fh, '<', \$strs or die "Couldn't open virtual file for reading: $!\n";
seek $fh, $offsets[0], 0;
&next_tategaki(\@offsets, \$page);
$window->add($vbox);
$window->show_all;
Gtk2->main;
sub text_file_open {
my $dialog = Gtk2::FileChooserDialog->new(
'ファイルを開く',
undef,
'open',
'gtk-cancel' => 'cancel',
'gtk-ok' => 'ok',
);
$dialog->show_all;
my $response = $dialog->run;
if ($response eq 'ok') {
my $filename = $dialog->get_filename;
# 得たファイル名のテキストファイルを読み込む。ここに色々書く予定。
}
$dialog->destroy;
undef;
}
# 次ページ表示
sub next_tategaki {
my ($offsets_ref, $page_ref) = @_;
return if eof $fh;
&write_tategaki;
push @$offsets_ref, tell $fh or die "Couldn't tell the file offset: $!\n";
$$page_ref++;
# ページ数の表示
$page_count->set_label("- $page -");
undef;
}
# 前ページ表示
sub prev_tategaki {
my ($offsets_ref, $page_ref) = @_;
return if $#offsets < 0 || defined $shiori_page && $shiori_page > $page - 3;
pop @$offsets_ref if $#offsets > 1;
seek $fh, $offsets[-2], 0;
&write_tategaki;
$$page_ref-- if $$page_ref > 1;
# ページ数の表示
$page_count->set_label("- $page -");
undef;
}
# 縦書き描画
sub write_tategaki {
# 既にテキストを表示していれば一旦クリア
my $current_object = $scrolled_window->child;
$current_object->destroy if $current_object;
# cairo を使った描画
my $surface = Cairo::ImageSurface->create('argb32',
$surface_width, $surface_height);
my $cr = Cairo::Context->create($surface);
# $cr->set_source_rgb(0,0,1); # 青
$cr->translate($surface_width, 0);
$cr->rotate(pip2);
my $layout = Gtk2::Pango::Cairo::create_layout($cr);
$layout->set_spacing($line_spacing);
my $context = $layout->get_context;
$context->set_base_gravity('east');
my $desc = Gtk2::Pango::FontDescription->from_string($font_and_size);
$layout->set_font_description($desc);
my $language = Gtk2::Pango::Language->from_string('ja');
$context->set_language($language);
if ($strs){
my $text;
my $lines;
while(<$fh>){
$text .= $_;
$lines++;
last if ($lines == $page_lines);
}
$text = decode_utf8($text);
$layout->set_text($text);
}
Gtk2::Pango::Cairo::show_layout($cr, $layout);
my $drawable = Gtk2::DrawingArea->new;
$drawable->size($surface_width, $surface_height);
$drawable->signal_connect(
'expose_event' => \&set_surface,
$surface,
);
$scrolled_window->add_with_viewport($drawable);
$drawable->show;
undef;
}
sub set_surface {
my ($widget, $event, $surface) = @_;
my $bg_cr = my $cr = Gtk2::Gdk::Cairo::Context->create($widget->window);
my $color = Gtk2::Gdk::Color->parse($background_color);
$bg_cr->set_source_color($color);
$bg_cr->paint;
$cr->set_source_surface($surface, 0, 0);
$cr->paint;
undef;
}
# 栞を挟む
sub shiori {
open my $fh, '>', $shiori_file
or die "Couldn't open $shiori_file for writing: $!\n";
print $fh $filename, ' ', $offsets[-2], ' ' , $page - 1, "\n";
close $fh or die "Couldn't close $shiori_file: $!\n";
undef;
}
sub next_ward{
&next_tategaki(\@offsets, \$page);
undef;
}
sub prev_ward{
&prev_tategaki(\@offsets, \$page);
undef;
}
# Copyright (c) 2009, 犬山ぽち丸
# このコードは、Perl自体と同じライセンスで配布します。
# Copyright (c) 2009, Pochimaru Inuyama. All rights reserved.
# This code is distributed under the same terms as Perl itself.
Nov 19, 2009
今更タッチタイピング
次期ぽちたてが行き詰まってしまったので、気分転換にタッチタイピングを覚えようなんて思い立ちました。元々ぽちは頭の回転が鈍くてタイピングが早くても思考の方が追いつかないので、チャットをしててもわざわざタッチタイピングを習得する必要に迫られませんでした。そんなぽちが今回こんな事を始めたのは、カタタタタと恰好良くタッチタイピングしてるとなんとなく賢そうに見えるかも?というお気楽な理由からです。
とは言うものの、なんとなく賢そうで恰好良いという印象意外には全くタッチタイピングについての知識が無いぽち。Web で検索してみると、両手の指に決まった担当するキーを割り当てて慣れていくのが基本のようです。

「ファイル:TouchTyping HomePosition QWERTY.png - Wikipedia」より
これを参考にしてテキトーに何かの文章でも打ち込んでみようかな?例えば、どう考えても著作権なんて切れている筈の平家物語の本文とか!なんて思いましたけども、良く考えたら世の中にはタイピング練習ツールというものが存在します。Linux向けで何か良いのはないかしら?と探してみましたが、日本語配列キーボードに対応したものはないようです。そんな中でキーボード配列をカスタマイズ出来るタイピング練習ツール、klavaro が見つかりました。公式サイトのトップを見てみると、お手軽にも debian にパッケージがあります。この klavaro は日本語の文章をタイピング練習する為のツールではありませんが、ぽちはずっとローマ字入力なのでたいして差し支えはありません。これを使ってしばらく練習してみようと思います。
あ、そうそう、この文章も練習がてらタッチタイピングで入力していますけども、まだまだとっても遅かったりします。カタタタタなんて素早く打てるようになるのはいつになることやら…。
Nov 11, 2009
Gtk2-Perl で Pango を使った縦書きビューワーを作ろう(2)
早速行き詰まってしまいました。
問題になってるのは、縦書き表示部分のページ移動による再描画です。ぽちには再描画する前にどうやって以前の表示をクリアしたら良いのかが良くわかりません。これは GTK+2 での Pango や cairo の描画の仕組みを詳しく理解出来てないせいなのですけども、Gtk2-Perl版ぽちたては、Perl/Tk版ぽちたてのように簡単に次ページには行けないようです。とほほ[犬]ω;)
ちゃんと動かないのですが、仕方なく途中経過の断片コードを載せておきます。
ぽちたて 20091110
#!/usr/bin/perl
use strict;
use warnings;
use utf8;
use Encode;
use Encode::Guess qw/euc-jp shiftjis iso-2022-jp/; # 日本語文字コード判別
use Lingua::JA::Fold qw/fold/; # 日本語禁則折り返し
use open IN => ":bytes"; # 入力される文字コードが混在している可能性があるので
use Gtk2 qw/-init/;
use Glib qw/TRUE FALSE/; # TRUE, FALSE で真偽値を扱う
use Cairo;
use Math::Trig qw/pip2/; # π/2 のラジアン値を使う
my $window_width = 605; # ウィンドウの幅
my $window_height = 693; # ウィンドウの高さ
my $surface_width = 600; # テキスト表示部の幅
my $surface_height = 661; # テキスト表示部の高さ
my $font_and_size = 'IPA明朝 11'; # フォント名とサイズ
my $background_color = '#e0ffff'; # 背景色
my $line_spacing = 19400; # 行間隔(1/1024ポイント単位)
my $fold_length = 42; # 文庫本 1ページの一般的な一行の文字数
my $page_lines = 18; # 文庫本 1ページの一般的な行数
my @offsets = (0); # 既読ページのテキストデータ読み出し後のオフセット配列
my $page = 1; # 現在表示しているページ
my $filename; # 読み込むテキストファイル名
# メニューバーを作る為の下準備
my $entries = [
[ 'FileMenu', undef, 'ファイル(_F)' ],
[ 'Open', 'gtk-open', '開く(_O)...', '<ctrl>O', 'ファイルを開く',
\&text_file_open],
[ 'Save', 'gtk-save', '栞を挟む(_S)', '<ctrl>S', '栞を挟む', \&shiori ],
[ 'Quit', 'gtk-quit', '終了(_Q)', '<ctrl>Q', '終了する',
sub { Gtk2->main_quit; } ],
];
my $menu_info = <<'EOS';
<ui>
<menubar name='MenuBar'>
<menu action='FileMenu'>
<menuitem action='Open' position='top'/>
<menuitem action='Save'/>
<separator/>
<menuitem action='Quit'/>
</menu>
</menubar>
</ui>
EOS
# GTK+ のバージョンを確認
die "This viewer requires GTK+ 2.4.0, but we're compiled for "
. join '.', Gtk2->GET_VERSION_INFO. "\n"
unless Gtk2->CHECK_VERSION(2, 4, 0);
# テキストファイルからの読み込みと文字コード判定・折り返し処理
my $strs;
foreach (@ARGV){
open my $fh, '<', $_ or die "Couldn't open $filename for reading: $!\n";
my $content .= join '', <$fh>;
close $fh;
my $guess = guess_encoding($content);
ref $guess or die "Couldn't guess: $guess\n";
open $fh, '<', $_ or die "Couldn't open $filename for reading: $!\n";
while (<$fh>){
$_ = $guess->decode($_);
$strs .= fold( 'text' => $_, 'length' => $fold_length,
'mode' => 'traditional' );
}
close $fh;
}
# メインウィンドウの準備
my $window = Gtk2::Window->new('toplevel');
$window->signal_connect('delete_event' => sub { Gtk2->main_quit; });
$window->set_title('ぽちたて 20091110');
$window->set_default_size($window_width, $window_height);
# 垂直ボックスとその第一段の中に水平ボックスを配置
my $vbox = Gtk2::VBox->new(FALSE, 2);
my $hbox = Gtk2::HBox->new(FALSE, 0);
$vbox->pack_start($hbox, FALSE, FALSE, 0);
# メニューバーの作成
my $ui = Gtk2::UIManager->new;
my $accelgroup = $ui->get_accel_group;
$window->add_accel_group($accelgroup);
my $actions = Gtk2::ActionGroup->new('actions');
$actions->add_actions ($entries, undef);
$ui->insert_action_group($actions, 0);
$ui->add_ui_from_string ($menu_info);
my $menubar = $ui->get_widget('/MenuBar');
# ページ移動ボタンの作成
my $button_next = Gtk2::Button->new;
my $icon_next = Gtk2::Image->new_from_stock('gtk-go-back', 'menu');
$button_next->set_image($icon_next);
$button_next->signal_connect('clicked' => \&next_ward);
my $button_prev = Gtk2::Button->new;
my $icon_prev = Gtk2::Image->new_from_stock('gtk-go-forward', 'menu');
$button_prev->set_image($icon_prev);
$button_prev->signal_connect('clicked' => sub { Gtk2->main_quit; });
# ページ数表示部の作成
my $page_count = Gtk2::Label->new("- $page -");
# 垂直ボックス第一段の水平ボックスへ各ウィジェットの配置
$hbox->pack_start($menubar, FALSE, FALSE, 0);
$hbox->pack_start($button_next, FALSE, FALSE, 0);
$hbox->pack_start($button_prev, FALSE, FALSE, 0);
$hbox->pack_start($page_count, TRUE, TRUE, 0);
# スクロール付きウィンドウを作成して垂直ボックスの第二段に配置
my $scrolled_window = Gtk2::ScrolledWindow->new(undef, undef);
$scrolled_window->set_policy('automatic', 'automatic');
$vbox->pack_start($scrolled_window, TRUE, TRUE, 0);
open my $fh, '<', \$strs or die "Couldn't open virtual file for reading: $!\n";
seek $fh, $offsets[0], 0;
&next_tategaki(\@offsets, \$page);
$window->add($vbox);
$window->show_all;
Gtk2->main;
sub text_file_open {
my $dialog = Gtk2::FileChooserDialog->new(
'ファイルを開く',
undef,
'open',
'gtk-cancel' => 'cancel',
'gtk-ok' => 'ok',
);
$dialog->show_all;
my $response = $dialog->run;
if ($response eq 'ok') {
my $filename = $dialog->get_filename;
# 得たファイル名のテキストファイルを読み込む。ここに色々書く予定。
}
$dialog->destroy;
}
sub next_ward{
&next_tategaki(\@offsets, \$page);
undef;
}
# 次ページ表示
sub next_tategaki {
my ($offsets_ref, $page_ref) = @_;
return if eof $fh;
&write_tategaki;
push @$offsets_ref, tell $fh or die "Couldn't tell the file offset: $!\n";
$$page_ref++;
undef;
}
# 縦書き描画
# 次ページや前ページへ移動する時に、
# 現在表示しているページをクリアする良い方法がないかなあ?
sub write_tategaki {
# cairo を使った描画
my $surface = Cairo::ImageSurface->create('argb32',
$surface_width, $surface_height);
my $cr = Cairo::Context->create($surface);
# フォントカラーをここで無理矢理指定出来るけど、0-1 の範囲のRGB表記という…。
# color name や hex code からの変換用関数が用意されてないか探してみようっと。
# 無いようなら自分で作る事!
# $cr->set_source_rgb(0,0,1); # 青
$cr->translate($surface_width, 0);
$cr->rotate(pip2);
my $layout = Gtk2::Pango::Cairo::create_layout($cr);
$layout->set_spacing($line_spacing);
my $context = $layout->get_context;
$context->set_base_gravity('east');
my $desc = Gtk2::Pango::FontDescription->from_string($font_and_size);
$layout->set_font_description($desc);
my $language = Gtk2::Pango::Language->from_string('ja');
$context->set_language($language);
if ($strs){
my $text;
my $lines;
while(<$fh>){
$text .= $_;
$lines++;
last if ($lines == $page_lines);
}
$text = decode_utf8($text);
$layout->set_text($text);
}
Gtk2::Pango::Cairo::show_layout($cr, $layout);
my $drawable = Gtk2::DrawingArea->new;
$drawable->size($surface_width, $surface_height);
$drawable->signal_connect(
'expose_event' => \&set_surface,
$surface,
);
$scrolled_window->add_with_viewport($drawable);
}
sub set_surface {
my ($widget, $event, $surface) = @_;
my $bg_cr = my $cr = Gtk2::Gdk::Cairo::Context->create($widget->window);
my $color = Gtk2::Gdk::Color->parse($background_color);
$bg_cr->set_source_color($color);
$bg_cr->paint;
$cr->set_source_surface($surface, 0, 0);
$cr->paint;
undef;
}
# Copyright (c) 2009, 犬山ぽち丸
# このコードは、Perl自体と同じライセンスで配布します。
# Copyright (c) 2009, Pochimaru Inuyama. All rights reserved.
# This code is distributed under the same terms as Perl itself.
Nov 09, 2009
Pango 1.26 の縦書きは良いみたい
TrueTypeフォントについて調べてた頃に、2ch の Linux板にある「Linux上でのフォント総合スレ」というスレを覗いてみた事があるのですけども、それ以来定期的にそのスレをチェックするようになりました。さっきかなり久しぶりに眺めてみると、Pango 1.26 では句読点が中心線揃えの変な表示位置から正しい表示位置になっているというお話。しかも、次期ぽちたてに向けたぽちんちの断片コードまで紹介してくれているではありませんか!さらに挙句の果てには、ぽち断片を使って最新リリースの Pango 1.26 での次期ぽちたて表示を試してみてくれた方までいらっしゃいました。

うんうん、debian lenny で Pango 1.20 のぽち環境とは違って、充分実用に耐える品質で縦書きされています。次期ぽちたては、Pango の縦書きを使う為ダケに Gtk2-Perl版にしたので、この表示結果にはぽちもひと安心というかホッとしたというか、なんだか言葉では言い尽くせない思いです。
「Linux上でのフォント総合スレ 4」の皆様、本当にありがとうございます。お蔭様で、がんばって早く縦書きビューワーとしてひと通りの機能を実装しなきゃ!と、急に元気が湧いて来ました。
Nov 07, 2009
Gtk2-Perl で Pango を使った縦書きビューワーを作ろう(1)
前回の日記では次期ぽちたての縦書き表示部分の画像だけしか置いてなかったのですけども、今回からはちゃんと Perl のコードも書いておくことにします。

GTK+2 にならとっても分かり易い入門書まで出てたりしてますが、Gtk2-Perl の日本語文書は少ないので、こんないいかげんな断片でも誰かの助けになればと思って載せています。けっして「ぽちたて作りにちっとも進展がないから、とりあえずこないだのコードを載せちゃえ!」って事じゃないんですからねっ?誰ですか?一番役に立つのは結局本家の Gtk2-Perl - Table of Contents だって言ってるのは!(その通りですけども)
当然ではありますけども、Gtk2-Perl版ぽちたてを動かすには Perl や useされる Perlモジュール群だけでなく、事前に GTK+2 や Pango、cairo のライブラリがインストールされていなければなりません。debian lenny では libgtk2-perlパッケージをインストールすると、依存関係で必要なライブラリが一緒にインストールされるのでらくちんです。
ぽちたて 20091103
#!/usr/bin/perl
use strict;
use warnings;
use utf8;
use Encode;
use Encode::Guess qw/euc-jp shiftjis iso-2022-jp/; # 日本語文字コード判別
use Lingua::JA::Fold qw/fold/; # 日本語禁則折り返し
use open IN => ":bytes"; # 入力される文字コードが混在している可能性があるので
use Gtk2 qw/-init/;
use Glib qw/TRUE FALSE/; # TRUE, FALSE で真偽値を扱う
use Cairo;
use Math::Trig qw/pip2/; # π/2 のラジアン値を使う
my $window_width = 605; # ウィンドウの幅
my $window_height = 693; # ウィンドウの高さ
my $surface_width = 600; # テキスト表示部の幅
my $surface_height = 661; # テキスト表示部の高さ
my $font_and_size = 'IPA明朝 11'; # フォント名とサイズ
my $background_color = '#e0ffff'; # 背景色
my $line_spacing = 19400; # 行間隔(1/1024ポイント単位)
my $fold_length = 42; # 文庫本 1ページの一般的な一行の文字数
my $page_lines = 18; # 文庫本 1ページの一般的な行数
my $shiori_file = 'pochitate_shiori.txt'; # 栞ファイルの場所
my @offsets = (0); # 既読ページのテキストデータ読み出し後のオフセット配列
my $page = 1; # 現在表示しているページ
my $shiori_page; # 栞を挟んだページ
my $filename; # 読み込むテキストファイル名
# メニューバーを作る為の下準備
my $entries = [
[ 'FileMenu', undef, 'ファイル(_F)' ],
[ 'Open', 'gtk-open', '開く(_O)...', '<ctrl>O', 'ファイルを開く',
sub { Gtk2->main_quit; } ],
[ 'Save', 'gtk-save', '栞を挟む(_S)', '<ctrl>S', '栞を挟む',
sub { Gtk2->main_quit; } ],
[ 'Quit', 'gtk-quit', '終了(_Q)', '<ctrl>Q', '終了する',
sub { Gtk2->main_quit; } ],
];
my $menu_info = <<'EOS';
<ui>
<menubar name='MenuBar'>
<menu action='FileMenu'>
<menuitem action='Open' position='top'/>
<menuitem action='Save'/>
<separator/>
<menuitem action='Quit'/>
</menu>
</menubar>
</ui>
EOS
# GTK+ のバージョンを確認
die "This viewer requires GTK+ 2.4.0, but we're compiled for "
. join '.', Gtk2->GET_VERSION_INFO. "\n"
unless Gtk2->CHECK_VERSION(2, 4, 0);
# テキストファイルからの読み込みと文字コード判定・折り返し処理
my $strs;
foreach (@ARGV){
open my $fh, '<', $_ or die "Couldn't open $filename for reading: $!\n";
my $content .= join '', <$fh>;
close $fh;
my $guess = guess_encoding($content);
ref $guess or die "Couldn't guess: $guess\n";
open $fh, '<', $_ or die "Couldn't open $filename for reading: $!\n";
while (<$fh>){
$_ = $guess->decode($_);
$strs .= fold( 'text' => $_, 'length' => $fold_length,
'mode' => 'traditional' );
}
close $fh;
}
# メインウィンドウの準備
my $window = Gtk2::Window->new('toplevel');
$window->signal_connect('delete_event' => sub { Gtk2->main_quit; });
$window->set_title('ぽちたて 20091103');
$window->set_default_size($window_width, $window_height);
# 垂直ボックスとその第一段の中に水平ボックスを配置
my $vbox = Gtk2::VBox->new(FALSE, 2);
my $hbox = Gtk2::HBox->new(FALSE, 0);
$vbox->pack_start($hbox, FALSE, FALSE, 0);
# メニューバーの作成
my $ui = Gtk2::UIManager->new;
my $accelgroup = $ui->get_accel_group;
$window->add_accel_group($accelgroup);
my $actions = Gtk2::ActionGroup->new('actions');
$actions->add_actions ($entries, undef);
$ui->insert_action_group($actions, 0);
$ui->add_ui_from_string ($menu_info);
my $menubar = $ui->get_widget('/MenuBar');
# ページ移動ボタンの作成
my $button_next = Gtk2::Button->new;
my $icon_next = Gtk2::Image->new_from_stock('gtk-go-back', 'menu');
$button_next->set_image($icon_next);
$button_next->signal_connect('clicked' => sub {Gtk2->main_quit;} );
my $button_prev = Gtk2::Button->new;
my $icon_prev = Gtk2::Image->new_from_stock('gtk-go-forward', 'menu');
$button_prev->set_image($icon_prev);
$button_prev->signal_connect('clicked' => sub {Gtk2->main_quit;} );
# ページ数表示部の作成
my $page_count = Gtk2::Label->new("- $page -");
# 垂直ボックス第一段の水平ボックスへ各ウィジェットの配置
$hbox->pack_start($menubar, FALSE, FALSE, 0);
$hbox->pack_start($button_next, FALSE, FALSE, 0);
$hbox->pack_start($button_prev, FALSE, FALSE, 0);
$hbox->pack_start($page_count, TRUE, TRUE, 0);
# スクロール付きウィンドウを作成して垂直ボックスの第二段に配置
my $scrolled_window = Gtk2::ScrolledWindow->new(undef, undef);
$scrolled_window->set_policy('automatic', 'automatic');
$vbox->pack_start($scrolled_window, TRUE, TRUE, 0);
open my $fh, '<', \$strs or die "Couldn't open virtual file for reading: $!\n";
seek $fh, $offsets[0], 0;
&write_tategaki;
$window->add($vbox);
$window->show_all;
Gtk2->main;
sub text_file_open {
my $dialog = Gtk2::FileChooserDialog->new(
'ファイルを開く',
undef,
'open',
'gtk-cancel' => 'cancel',
'gtk-ok' => 'ok',
);
$dialog->show_all;
my $response = $dialog->run;
if ($response eq 'ok') {
my $filename = $dialog->get_filename;
# 得たファイル名を縦書き描画サブルーチンへ渡して実行する予定地
}
$dialog->destroy;
}
# 縦書き描画
sub write_tategaki {
# cairo を使った描画
my $surface = Cairo::ImageSurface->create('argb32',
$surface_width, $surface_height);
my $cr = Cairo::Context->create($surface);
# フォントカラーをここで無理矢理指定出来るけど、0-1 の範囲の RGB表記という…。
# color name や hex からの変換用関数が用意されてないか探してみようっと。
# $cr->set_source_rgb(0,0,1); # 青
$cr->translate($surface_width, 0);
$cr->rotate(pip2);
my $layout = Gtk2::Pango::Cairo::create_layout($cr);
$layout->set_spacing($line_spacing);
my $context = $layout->get_context;
$context->set_base_gravity('east');
my $desc = Gtk2::Pango::FontDescription->from_string($font_and_size);
$layout->set_font_description($desc);
my $language = Gtk2::Pango::Language->from_string('ja');
$context->set_language($language);
if ($strs) {
my $text;
$text .= $_ while(<$fh>);
$text = decode_utf8($text);
$layout->set_text($text);
}
Gtk2::Pango::Cairo::show_layout($cr, $layout);
my $drawable = Gtk2::DrawingArea->new;
$drawable->size($surface_width, $surface_height);
$drawable->signal_connect(
'expose_event' => \&set_surface,
$surface,
);
$scrolled_window->add_with_viewport($drawable);
}
sub set_surface {
my ($widget, $event, $surface) = @_;
my $bg_cr = my $cr = Gtk2::Gdk::Cairo::Context->create($widget->window);
my $color = Gtk2::Gdk::Color->parse($background_color);
$bg_cr->set_source_color($color);
$bg_cr->paint;
$cr->set_source_surface($surface, 0, 0);
$cr->paint;
undef;
}
# Copyright (c) 2009, 犬山ぽち丸
# このコードは、Perl自体と同じライセンスで配布します。
# Copyright (c) 2009, Pochimaru Inuyama. All rights reserved.
# This code is distributed under the same terms as Perl itself.
Nov 02, 2009
ぽちたて次期バージョンへのみちのり

Pango を使った縦書きはこんな残念な感じですけれども、Gtk2-Perl や cairo-perl を扱う勉強も兼ねて最初のページを表示するだけのものを即席で作ってみました。後は以前のバージョンで実装した機能をこっちに持って来ればなんとかなりそうかも?メニューバーにページ移動ボタンだけでなく「ファイル(F)」なんて部分があるのは、ダイアログ画面でテキストファイルを指定して読み込む機能も付ける予定だからだったりします。
早くそれなりに恰好を付けて、ぽちたて 0.1.0 として公開出来るレベルになるといいなあ…。
Oct 29, 2009
Gtk2::Pango で縦書きしてみると
この二つのブログで Pango と cairo を使って C で書かれた縦書き表示のソースコードが紹介されていました。そこでそれを参考にして、ぽちも Gtk2-Perl と Cairo-Perl を通して Perl で書いてみました。
#!/usr/bin/perl
use strict;
use warnings;
use utf8;
use Gtk2;
use Cairo;
use Math::Trig qw/:pi/;
my $width = 80;
my $height = 280;
my $format = 'argb32';
my $file = 'verticaltext.png';
my $markup = <<"EOS";
<span font_family='IPA明朝' size='13000'>――《犬山ぽち丸》と、Pooch 123</span>
<span font_family='IPA明朝' size='13000'>『ぷ〜ち』は今日も元気でぇーす。</span>
<span font_family='IPA明朝' size='13000'>「Pangoの縦書きは、こんな感じ」</span>
<span font_family='IPA明朝' size='13000'>(本当に)――大丈夫かな……?</span>
EOS
my $surface = Cairo::ImageSurface->create($format, $width, $height);
my $cr = Cairo::Context->create($surface);
$cr->translate($width, 0);
$cr->rotate(pip2);
my $layout = Gtk2::Pango::Cairo::create_layout($cr);
my $context = $layout->get_context;
$context->set_base_gravity('east');
$layout->set_markup($markup);
Gtk2::Pango::Cairo::show_layout($cr, $layout);
$surface->write_to_png($file);
そしてその表示結果がこちら。

スカラー変数 $markup
の中にある原文と表示結果を比べていただけると分かるように、句読点の位置が真ん中に来ていたり、同じ記号でも場合によって縦になったり横になったりしていて(最初から横倒しにならない記号もありますけども)、結局、参考にしたブログで、Pango の日本語縦書き実装はそのまま実用にするにはまだまだ問題がある、という主旨の事が書かれていたのを再確認してしまいました。とは言うものの、 Perl での書き方の勉強にはなったかもしれません。Pango に代わる簡単な縦書き表示の手段もぽちには見つからないので、ぽちたて次期バージョンはこれを使うしかないのかなあ? debian lenny の公式パッケージにある少し前のバージョンの Pango でしか試していないので、安定版最新の Pango や 開発版の Pango ではどうなっているのかちょっと興味があります。
Oct 28, 2009
Gtk2-Perl を通して GTK+2 を勉強中
こないだ買って来た入門GTK+ は予想以上に良い本でした。入門書をちょっと齧った程度の C知識しかないぽちにも、分かりやすいソースコードで例示して GTK+ の使い方を教えてくれます。今のところ、この本を読みながら、解説されているいろんな機能を Gtk2-Perl - Table of Contents で首っ引きにして、Gtk2-Perl を通して Perl で書き直して動かしてみたりしていますが、Gtk2-Perl - Table of Contents に書いてある情報だけでは Perl でどんな風に書いて良いのか分からないものもあって、前途多難な状態です。誰かが Gtk2-Perl で書いたコードとかを参考にした方が良いのかなあ?
Oct 25, 2009
入門Git を買いに行ったのに入門GTK+ を買って帰る

五十歩百歩さんのお薦めでバージョン管理システムに Git を使い始めたぽちですけども、今一つ使いこなせていません。最近、Git のメンテナの濱野純さんが書かれた、入門Git という本が出てるのを知り、最寄りの大きめの本屋さんに自転車でキコキコ走ってみました。つらつらとコンピュータ関連書籍のコーナーを眺めても見当たらなかったので、書店員の方に入門Git の事を説明して尋ねてみると、入荷の予定が無いというお話。なるだけ本は地元にある本屋さんの店頭にあるものを買う事を心掛けているぽちも、無いものは仕方ありません。まあ、せっかく本屋さんまで来たんだからと、未練がましくコンピュータ関連書籍コーナーをうろうろしていると、入門GTK+ という本を発見。奥付を見るとこれも最近出たばかりの本のようです。

ぽちたて次期バージョンの為に GUIツールキットの GTK+2 を勉強しようと思っていた矢先の事だったので、入門Git が買えなくてちょっと悔しかった事もあり、うっかり勢いで買ってしまいました。
ぽち、しばらくは入門GTK+ を読んでると思います。読了したら感想をここに書こうかなあ。
Oct 24, 2009
最近読んだ本:乙嫁語り 1巻

ぽちが大好きなエマの森薫さんの新作という事で迷わず読んだ一冊ですが、さすが期待通りの面白さでした。
二十歳の花嫁アミルさんの恰好良さ健気さもさる事ながら、十二歳の花婿カルルク君の存在で前作のエマよりも男の子成分を増量してくれているのも個人的に嬉しいです。後半ではカルルクの一族とアミルの一族との不穏な悶着も起こり次巻がとっても気になります。悶着を持ち込んだアミルの兄、アゼルも無口で一徹な感じの超美形ですけども、なんとなくこういうタイプは色々と厄介事を押し付けられたり貧乏籤を引かされる羽目になって、その性格故に早死にしそうな予感が…。
描き込みもとても美麗なので、いやもう、色んな方に読んで欲しい漫画です。
Oct 23, 2009
ぽちたて右往左往中
色々あって怒涛のように忙しかった十月も終わりを迎えつつある今日この頃、ブログも日記というより月記という感じの頻度に成り果ててしまいました。
ぽちたて 0.0.3 をリリース後、Perl だけを使ってなんとか縦書きグリフを表示しようと色々試行錯誤してみましたが、ぽちの頭が悪いせいでどうにも上手く行きません。半ばヤケになっていっそ縦書き専用の明朝体TrueTypeフォントを作って同梱しようかとまで考えましたけども、さすがに開発としての方向性が間違ってる気がして断念(笑)。なぜ Perl だけで実装する事に拘っているのかというと、Perl環境がある OS の間でクロスプラットフォームな縦書きビューワーにしたかったからなのです。けど、その意図はぽちの技術力の無さのお蔭で完全に暗礁に乗り上げてしまいました。
そこで、とりあえず実用的な縦書きビューワーを作る為に、縦書き表示の実装があるという Pango というライブラリを使う事にしました。このライブラリは GTK+2 で使うものなので、GUIツールキットも Perl のみで動いた Perl/Tk から、GTK+2 に変更し、Gtk2 という Perlモジュールを通してライブラリを操作する予定です。Pango にしろ GTK+2 にしろ C のライブラリで、これに依存してては Windows での導入の敷居が格段に高くなり、事実上、Linux/UNIX専用になってしまいますけれども、この際、Perl から操作可能なら贅沢は言えません。ぽちたては今後この方向で(ほぼ)Linux/UNIX向けツールという事で開発していこうと思います。
だってほら、Windows にはもう沢山素敵な縦書きビューワーがあるじゃないですか…(言い訳)。
Sep 30, 2009
最近読んだ本:1789年-フランス革命序論

この夏から秋にかけては政権交代とかなんとか政治の世界でも大変動が起こりました。そこで、アメリカ独立と並んで議会制民主主義の原点とも言えるフランス革命についての本を読んでみたりして…。フランス革命の登場人物たちについての多少の基礎知識が必要ですけども、これが下手な小説よりも面白い!この本の書かれた時期が 1939年なので、最新の歴史研究からするともしかしたら少し時代遅れなのかもしれませんが、きちんと筋が通っていて納得のいく、そして何よりスリリング(結果は分かっているのにその過程が!)な本でした。ジョルジュ・ルフェーヴルってスゴイ歴史研究者だったんですねえ。
Sep 19, 2009
おやすみしたいけど、そうもいかない…
世の中は連休開始なんですけども、ぽちは風邪を惹いて頭痛が…orz
縁があって、毎週土曜深夜にはソードワールド2.0 という TRPG の IRC を使ったオンラインでのキャンペーンに参加しているのですけども、前々回も事情があっておやすみしてたのでこれ以上参加者の皆様方にご迷惑をお掛けするわけにはいきません。保つかどうかわかんないけど、なんとか頑張って参加しないと…。
Sep 18, 2009
バージョン管理に Git を導入
以前にも書きましたけども、ぽちたてのバージョン管理に Git を導入しました。今までバージョン管理システムとか全然使った事が無かったので、五十歩百歩さんのアドバイスや Git入門を参考に(もちろん man gittutorial も)、おっかなびっくり使い始めています。ちょっと Git について調べてみたら、Linuxカーネルや Perl関連の開発でもバージョン管理システムにはこれが使われているみたいですね。
まだ使ったコマンドと言っても、
$ mkdir pochitate $ cd pochitate
で準備した後で、
$ git init $ git add pochitate $ git commit -m 'はじめてのコミット'
しただけなんですけどね。
どうやら Git の機能はかなり豊富そうなんですけども、それだけにどう使って良いのやら、どう使ったら便利なのやら、まだまだ全然把握出来ていません。今のところは、
$ git log
や、
$ git log -p
で、記録を見たり差分を見たりするのが精一杯です。ぽちに Git をバリバリ使いこなせる日が来るのかなあ…?
Sep 17, 2009
最近読んだ本:神の守り人〈上〉来訪編・神の守り人〈下〉帰還編

相変わらずのおもしろさです。スファルの娘シハナがとにかく薄気味悪い。上橋さんはぽちの嫌悪感のツボをドンピシャリに突いてくる稀有な作家さんです。闇の守り人のユグロとか…。シハナについては、こういう思考や行動をカッコイイとか思ってしまうお年頃もあるとは思うけど、本当ならどんなに有能だろうとこういうタイプの人のもとには人は集まらないんじゃないかと思うぽち。こういう人として何か大事なものがポッカリ欠落したまま成長した人ってたまに居ますよね…。

アスラへのバルサの思いがキツイ二冊でした。