ホーム > com/net

カテゴリー com/net の記事

[底] 小説の縦書き表示

 最近、pixiv のブラウザ版で、小説の縦書き表示が実装されました。元々日本語の小説は本なら縦書きが一般的なのですが、元々英語圏で開発されたパソコン上で縦書きを扱うのはまだまだしちめんどくさいのです。で、pixiv ではどんな風に実装しているのかをスタートに、自分なりに縦書き表示ができないものかと調べてみました。

 結論から書くと、pixiv の縦書きの基本的な部分は CSS 3 で実現しているということがわかりました。CSS というのは Web ページの見た目を変更する仕組みで、CSS 3 ではかなり複雑なことができるようになっています。私が Web サイトを自力で組んでいた頃はまだ CSS 3 なんてなかったので、今回使った要素も初めて使うものが大半でした。

 pixiv で実装されている要素のうち、真似ができたのはこんな感じです。

  • 縦書き表示 …… -webkit-writing-mode:vertical-rl、direction:ltr、unicode-bidi:bidi-override
  • 小説の表示エリアからはみ出さない …… width:100%、overflow:auto
  • フォントをきれいに表示する …… font-family:Meiryo

 ここでキモになるのが -webkit-writing-mode。これを vertical-rl にすることで右から左に読む縦書き表示モードになります。direction と unicode-bidi はどちらも文字の方向を決めているのですが、これはどうやら指定しなくても適切な方向にデフォルトで設定されるみたいです。試しに逆にしてみてもうまくいきませんでした。

 しかし、-webkit-writing-mode を指定しただけでは、縦書きになった日本語が画面右側に思い切り延びていきます。これを防ぐために、widthoverflow を指定します。width は横幅を指定するもので、これを % で指定すると、親要素、ここでは小説の表示エリアを 100% として実際の表示範囲を決めることができます。これで 100% とすると横幅一杯になりますし、例えば 90% くらいにすると、右側に1割ほど余裕ができます。
 しかしこれだけでは収まりきらない部分が見えません。そこで、追加で overflow を指定します。こいつは指定した範囲から中身がはみ出した時にスクロールバーを出すかどうかを指定するもので、auto に指定するとはみ出している方向のスクロールバー(縦書き表示の場合は横スクロールバー)だけが表示されます。
 最後のフォントをきれいに表示するというのは、たまたま私のブラウザの指定では鉤括弧が次の文字にくっついてしまうフォントを使っていたので、pixiv を参考にメイリオを優先的に使うように指定しただけです。これは私の環境が Windows 7 だからで、Mac だったらヒラギノとかを指定するとよいと思います。Web で公開するなら両方指定しておいて、ある方で表示するようにするのが無難ですね。

 さて、ここまでができたこと。次にできなかったことです。

  • スクロールバーをクリックするまで表示しない …… jQuery プラグインの nicescroll を使用しているみたいです
  • マウスカーソルが縦書きの範囲にある時にホイールを回すと左右にスクロールする …… これも nicescroll です
  • 左右の矢印をクリックすると一定行スクロール …… 正体不明

 特に3番目の矢印をクリックすると一定行スクロール。これができると、ページめくりに近いことが実現できます。これは CSS だけで実現するのは無理なので JavaScript を使っていると思いますし、スクロールバーに nicescroll という jQuery プラグインを使っていることを考えれば、同じ jQuery プラグインの何かを使っているか、jQuery をベースに自力開発したかのどちらかだと思いますが。これはちょろっと試してみる程度では無理で、ちゃんとファイルをいじる必要があるので手を出していません。
 しかし、2番目のホイールで左右のスクロールができると大幅に読みやすくなるので、nicescroll を導入するだけでも意外と実用レベルの縦書き表示は難しくないかもしれません。
 そして、縦書きじゃなくて横書きの方がいい、という方のために切り替え機能を作る必要もあるので、JavaScript で CSS の指定を動的に切り替えるというボタンを作ることになりますが、これは難しくなさそう。実際、お気に入りをクリックすると背景色を変える、というスクリプトを書いたことがあるので、同じことを -webkit-writing-mode に対して行うだけです。

 ただし、一番大きな問題は、Firefox が対象外だということ。Chrome 系ブラウザーや Opera、Safari は大丈夫で、Internet Explorer も別の要素を指定する必要はありますが対応は難しくありません。しかし Firefox は writing-mode に対応していないので肝心の縦書きがそもそも実現できないのです。

 ちょっと余裕がある時かちょっと余裕がない時に、やってみますかねぇ。せっかく小説メインのサイトなのですし。

[底]はじめてのおでかけ

 先日アイマスタジオの公開録音の会場に行ってきました。とは言え、抽選制のこのイベント、私は外れてしまったので単に物販目当てです。

 物販スタートが10時だったので、スタートと同時くらいに着けば好きに買えるだろうと思っていたら寝過ごし。1時間遅れて11時に現場に着いたのですが、見事に行列。何故か2列。建物の外に並んでいて、建物に入ってからもすぐにカウンターがあるようには見えないので、そこそこ並ぶなーと覚悟はしていました。
 物販自体は、本編参加できるわけでもないのに建物の中の列も外と同じくらい列が伸びていて、冷房がなかったので外より暑かった上に、一番欲しかったものが自分の目の前で売り切れる(本当にすぐ前に並んでいた人がラス1を買ったのです!)という哀しい事態になったわけですが……それはさておき。本題はこっからです。

 気付いたら2時間くらい並んでいたのですが、その間、とりあえず電波は来てるのでモバマスをちょっとやって、残りの時間は kobo で本を読んでました。まとまった時間使うのは初めてだったので、どんなものか期待半分心配半分だったのですが、1時間半ほど使っていて、片手で保持するにもまったく重いと感じることはなく、両手でなくてもページめくり操作(ページの左右をタップ)がどちらもできました。iPad だと重くて途中でダウンしてましたし、iPhone では画面が小さくてきついのと電池の減りが早いので、リーダーとしての使い勝手は十分でした。値段や入手のしやすさを考えれば、ハード自体は悪くないと思います。

 初期設定にバグがあったこと(既に直ってるかもしれませんが)と、オンラインで買ったファイル以外を快適に読めるようにするのが難しい(USB で繋いでも転送が不安定な上、PDF は重かったり画面の再描画に不備があるため快適には読めず、快適に読める ePub や CBZ は作成/変換にツールが必要)のとで、正直オンラインでばんばん買う気がある人か PC 操作に慣れている人でなければ十全に使うのは無理だと思います。
 おそらく楽天(ないし kobo)としては既存のファイルを転送して読むのはおまけで、基本はオンライン書店から買って読め、という意識なのでしょうし、それであれば逆に前述の「リーダーとしての使い勝手」を享受できると思います。オンラインではまだ1冊も買ってないのであくまで「たぶん」ですが。

 ラインナップも含めて、普及させる気あるのか?と思ったりもするのですが、ガラパゴスのこと考えたらずいぶん進歩したかな、と。これから Kindle が日本市場に本格的に入ってくると、一気に競争が激しくなるでしょう。どうなっていくのか、楽しみです。

 そういえば iPhone も初期設定に若干手間取ったような記憶がうっすらと。あれはネットワーク設定だったかな……。Kobo みたいに2晩かけて4回も初期化かけて、なんてもちろんしませんでしたが。

[底]Perl で UTF-16 のファイルを書く

 仕事で使う UTF-16 のファイルを Perl で処理するスクリプトの改修がようやく一段落しそうです。今年の2月に書いた記事「Perl で UTF-16 のファイルを読む」の続きです。

 前回は BOM 付き UTF-16 リトルエンディアン(長くて面倒なので以下 Encode の記述方式に倣って UTF16LE とします)の SGML ファイルを読み込んで UTF-8 のテキストを書き出すところまで実現できました。今回はその続きで、入力ファイルと同じ UTF16LE の SGML を書き出す部分です。前回の記事のスクリプトに、書き出し先の文を追加するだけです。

 まずは改修前のスクリプト。

use utf8;
use strict;
use Encode;
use Encode::Guess qw/ shiftjis /;
$in = <>; #入力ファイルはスクリプトと別に手入力
open TEST, '<:bytes', "$in" or die; #入力値をバイト列としてファイルを開く
my $le = 0;
my $testline = <TEST>;
$testline = unpack "H4", $testline; #先頭の4バイトを16進数の文字列として取得
$le = 1 if ($testline =~ m!fffe!); #先頭の4バイトが FFFE だったらフラグを1にする
close TEST;
if ($le) {
open IN, '<:encoding(utf16le)', "$in" or die; #フラグが1なら UTF-16 で開く
} else {
open IN, '<:encoding(shiftjis)', "$in" or die; #フラグが0なら Shift_JIS で開く
}
while ($line = <EXP>) {
$line =~ s!(¥x0d¥x0a|¥x¥d|¥x¥a)!¥n!g; #改行コードを全部 ¥n に変換
#行ごとの処理
}
close IN;

 これを改修した結果は以下のとおり。入力が UTF16LE と Shift_JIS の2とおりあるため、出力は入力と同じ文字コードで出力するようにしています。

use utf8;
use strict;
use Encode;
use Encode::Guess qw/ shiftjis /;
my $in = <>; #入力ファイルはスクリプトと別に手入力
my $execMode = <>; #出力モードを手入力
open TEST, '<:bytes', "$in" or die; #入力値をバイト列としてファイルを開く
my $le = 0;
my $testline = <TEST>;
$testline = unpack "H4", $testline; #先頭の4バイトを16進数の文字列として取得
$le = 1 if ($testline =~ m!fffe!); #先頭の4バイトが FFFE だったらフラグを1にする
close TEST;
if ($le) {
open IN, '<:encoding(utf16le)', "$in" or die; #フラグが1なら入力ファイルを UTF16LE で開く
if ($execMode =~ m!x$!i) { #出力を UTF16LE の SGML にする場合は……
open OUT, ">raw:encoding(utf16le):crlf", "$out" or die; #(1) 改行コードを指定できる形で開く
print OUT "¥x{FEFF}"; #(2) BOM を 16 進数で直接指定
print OUT "<root>¥n"; #ルートのタグを追加

} else { #出力を UTF-8 のテキストにする場合は……
open OUT, '>encoding(utf8)', "$out" or die;
}
} else {
open IN, '<:encoding(shiftjis)', "$in" or die; #フラグが0なら入力ファイルを Shift_JIS で開く
if ($execMode =~ m!x$!i) { #出力を Shift_JIS の SGML にする場合は
open OUT, ">encoding(shiftjis)", "$out" or die; #Shift_JIS で開く
print OUT "<root>¥n"; #ルートのタグを追加

} else { #出力を UTF-8 のテキストにする場合は……
open OUT, '>encoding(utf8)', "$out" or die;
}
}
while ($line = <EXP>) {
$line =~ s!(¥x0d¥x0a|¥x¥d|¥x¥a)!¥n!g; #改行コードを全部 ¥n に変換
#行ごとの処理
}
close IN;
#出力処理
print OUT "</root>" if ($execMode =~ m!x$!i);
close OUT;

 前回挫折したポイントが (1) の改行コード部分。これを単純に “>:encoding(utf16le)” とするだけでは出力がすべて文字化けしてしまうのです。そして今回地味に時間がかかったのが、(2) の BOM。

 上記の “>:raw:encoding(utf16le):crlf” の部分をクリアしたとしても、適切な BOM をファイルの先頭におかなければ、UTF16LE であるという判定ができないため、結果的に文字化けしてしまいます。ただし、単純に print “FFFE” (「FFFE」と出力) と書いてももちろんダメで、16進数としてファイルの先頭に書く必要があります。
 16進数をそのまま表すために \{hh} という表記方法があるのですが、私はそれを知らなかったために pack/unpack をひたすら読み込んで、文字コード判定時のコードを逆転させてなんとかならないかとトライアンドエラーを繰り返しました。完全に無駄な時間でしたが……。ともあれ、途中で pack/unpack ではどうにもならんと検索し直したら \x{hh} を発見して、一件落着。何故か FEFF と逆に記載していますが、タイプミスではなくこうしないとダメみたいです。

 しかし、ここまで来てしまうと全体のブラッシュアップをしたくなってきました。出力の処理を記述する部分がダラダラと長くて改修もしづらいし、作成当初は使っていたのですが今はもういらないモードもありますし……。しかしそれをするにはさすがに時間が足りないですね。とりあえず現状でも問題なく使えているので、そのうち、そのうち……。

トップページ > com/net

サイト内検索
紹介

管理人:春屋アロヅ(alohz)
連絡先:alohzhgmail.com
詳細、サイトについては諸々の紹介から。

最近の記事
カテゴリー
昔の記事
外部コンテンツ

ページの先頭に戻る