Mimir Yokohamaで続く改修、力を注ぐ

2018年8月5日、Mimir Yokohamaのソースリポジトリにはじめてのタグ、5.0が打たれた。

なぜはじめてのタグなのに5.0だったのか。

これは、Aki SI&Eのプロトタイプを1.0, Aki SIEのウェブサイトを2.0, Mimri YokohamaのWordPressを3.0, 現在のウェブサイトになったときを4.0とカウントしたものだ。

つまり、はじまって以来の、同一システムのままのバージョンアップとなった。

しかもこのタイミングである。 先日、大幅な編成変更と、いいね機能、コメント機能の追加を行った。 これで新しい船出だというタグではない。かといって同バージョン最終仕様としてのタグでもない。

これから次々とアップデートが予定されている。 その中での一区切りだった。

13年積み上げてきたものとは

PureBuiler Simplyの原型となっているのは2005年のACCS1である。

もうブログが流行り始めていた頃だったが、既に「事前生成戦略」に関するイメージはあった。 ブログを避けた理由は、一連の流れを持つ記事群を拾いにくくなること、そして時間とともに消えてしまうことだった。

「普遍的な内容を、きちんと分類して読みやすいように提供したい」と考えたわけだ。

様々な機能、様々なアイディアがあった。 実装されたものもあるし、実装されなかったものもある。 言語もPerl, PHP, Zsh, Rubyと変わってきた。 このような変遷をたどっているのはEQAIとこれだけで、まさに私のライフワークであり、また成長の軌跡でもあった。

この中でこだわってきたものもある。

例えば、デザイン性を保ったまま軽量・高速なウェブサイトを構築すること、可能な限り高いアクセシビリティを提供すること(環境や回線の違い、身体的ハンディキャップなどで差別しないこと)などもそうだ。 意味あるコンテンツを、読みやすい形で提供する、というのもある。

ACCSが追い求めてきた機能は次のようなものだった。

  • カテゴリで分類されて探しやすいインデックス
  • 検索機能
  • 意思表示機能 (コメント、と考えていることが多かった)
  • 連続した記事のページめくり
  • 要約の先読み
  • インライン用語集
  • prev, next, glossary, index, description情報を持たせる

PureBuilder Simplyは当初、Mimir YokohamaのWordPressページで提供されている全機能を提供する、ということを目標としていた。 これについては既に達成されている。そのために、従来のPureBuilderにはなかったタグ機能なども追加された。

「いいね機能」「コメント機能」はWordPressに完全な意味で標準であるものではないのだが、事実上付属するようなもの(特にコメント機能は)なので、これを追加したことで、事実上「WordPressを置き換える」というミッションは完遂した。

だが、同時に既にWordPressにはない機能の搭載もはじまっていた。 用語集機能はWordPressではなく、PureBuilderの伝統に由来する。そう、ACCSが目指していたものを達成する、という次のミッションに向かいはじめたのだ。

そこでつけられたのが5.0タグだった。

用語集機能

用語集機能という発想のスタート地点は、私が使っていたWindows 98SEマシンに搭載されていたインライン翻訳ソフトだった。

カーソルを載せるだけでその単語を翻訳してくれる、というソフトウェアは今持ってそれ以上のユーザービリティを提供するものはない。

「カーソルを載せたらわからない言葉を教えてくれる」というのは最高に便利だと思ったのだ。 現在はニコニコやはてなが似たような機能を提供しているが、あれはページが変遷してしまうため私からすれば理想的ではない。 どらちかといえばWikiに搭載されているインライン展開のほうがずっと理想的だ。

この機能は

  • 表示後にJavaScriptでtreatする
  • 生成時にHTMLを置き換える形で組み込む
  • ソースドキュメントを改変してから生成する

という3つのパターンを行ったり来たりしている。

PureBuilder SimplyではPost Plugins/Pre Pluginsの構造からこの3つとも選択肢として取ることができる。 Mimir YokohamaではPost PluginによるHTML置換え方式を取っている。

このあたりは試行錯誤の成果といえるだろう。 用語集ページも含めてYAMLの辞書ファイルから生成しており、文書に対して特別な処理は必要ない。

次の記事、前の記事

ACCSで最も苦戦したのがこの機能だ。

この解決については何度か言及しているが、今は前後関係をメタ情報として書く、という仕様になっている。

解決方法としては後退しているように見えるが、前後関係の自動解決はファイル名なり、もしくはなんらかのヒントなりを厳密に管理する必要があり、結構バグりやすいということを経験したのだ。

複雑な置換えやユーザーの管理によってバグを発生しうるようなものであるならばメタ情報をユーザー自身が書くべきだ、という割り切りは、これまでの歴史の中で手にしたバランス感覚である。

実のところ今の構成では前後を自動化することは難しくない。 だが、ユーザーがそれを守ってくれることを期待すべきではないだろう。

Post pluginsはページ生成が終わってから実行されるが、これは「他の記事の情報を取得できるようにする必要がある」からであり、 環境変数$pbsimply_indexesという形でデータベースのパスが渡っていることから

のようにして取得できるし、 連番に限るのであればもっと簡単に

とできる。 ちなみに、ディレクトリを認識させる方法は最新のコミットで環境変数$pbsimply_subdirに含まれるようになった。

このように「できるけれど、あえて手書き」だ。 これは、問題を簡単にするためと、この処理をPandocテンプレートを通じて行いたいためだ。 Pre Pluginsを使えばできるが、かなり複雑なことをすることになるため避けている。

意思表示機能

過去には「コメントを直接にHTMLファイル化し、objectで読ませる」ということをしていたこともある。

表示するかどうかを別として、コメント機能はそのときとあまり変わっていない。 表示させるために必要な部分を削ってシンプルになったくらいだ。

このような機能は本質的な部分は極めて簡単に書けることはこれまでの経験によって証明されている。 どの程度正当性を検証する必要があるかという点がwebアプリケーションの分量になる。

要約の先読み

まずは要約を入れる

いよいよ今回のハイライトだ。

descriptionへの対応自体は最初のリリース時点で

という記述があり、対応はちゃんとしていた。 だが、「書くのが面倒」「書いてもあまり意味がない」ということで放置していた。

「descriptionの先読み」は今まで実装計画には入っていたが、実装されたことはなかった。 そもそもdescriptionってSEOのために入れられているくらいで、「descriptionを読ませる」という発想はあまりない。

Firefoxだとこんなふうにブックマークのプロパティを表示するか、ブラウジングライブラリー上で詳細表示にするとdescriptionが表示されたりするのだが、これを見たことがある人という人は地球上に5桁いないのではないだろうか。

Firefoxのブックマークの詳細

だがdescriptionは入れたいと思っていたし、それを活用したいと思っていた。

そもそもの発端はトップページのレイアウト更新で、最新の更新記事と要約を(ニュースとは別に)表示したい、ということだった。 「どうせ記事の要約書くんだったらdescriptionに入れようよ」ということだ。

実はAtomフィードもスタンバイしている。

要約を見える形に

だが、これだけではおもしろくない。どうせ要約を表示するのならばぜひともユーザーに見える形にしたい。

私の文章は基本的に長いので(体系的でない短い文章に価値を感じていない)、読むのがしんどい人もいるだろう。 読むかどうか決めるために要約は重要だ。

要約を見たいタイミングとはいつだろう? やはり記事を読み始める前だろう。ならば本文前に

とかやってやればいいし、そのほうが効果的なのかもしれないが、既に「文書情報」という項目があることを考えるとちょっといただけない。

そこで文書情報に追加した上で「記事タイトルをクリックすると文書情報にジャンプする」という仕様にした。

これはヘルプページにも書いてあるけれども、誰も気づかなそうだ…

もうひとつ、利用者は多くなさそうだが、カテゴリインデックスがある。 ACCSとしてはこれが中心であり、ぜひとも使って欲しい機能だ。 世の中、情報を整頓するということに怠けすぎて、検索が全てになってしまっているので、使われていないような気もするけれど…

しかし私の意図としてはこのようなインデックスを活用してほしいというのがあるし、やはりタイトルだけではわかりにくい。 かといって変遷するとだるいので、変遷せずにインデックス上で要約を確認できると便利だ。

これはタイトルで関心をそそられた後の二次的な情報であり、通常は一覧性が高いほうがいい。 というわけで、ツールチップにしてみた。

PureBuilder Simply ACCS上でDescriptionを扱う

単純には記事タイトルにtitleで入れてあるため、ロールオーバーツールチップとして表示される。 だが、スマホだとこれが効かないので、補助的に“📖?”と表示して、これをタップすればツールチップが表示されるようにしてみた。 全く標準的でないインターフェイスなので、あまり気づいてもらえないような気もするけれど…

PBS ACCS用ツールチップ実装

何度見てもJavaScriptの複数代入が慣れない。 慣れればみやすそうだけども。

世の中的には割と珍しいDOM操作をしているが、これはelementに対してイベントリスナを設定するためで、innerHTMLだと二度手間になる。 基本的にやっていることは「記事部分 > リスト全体 > リンク」と絞り込んでいって要素を作成して追加する、という手順だ。 末っ子要素を追加するとき(今の要素の親要素の最後の子要素として追加する)はelement.parentNode.appendChildという手順は覚えておいてもいいかもしれない。

glosarryと共通のコードがライブラリとして読まれるようになっている。ライブラリはdeferだがasyncではない。

900pxを堺にしているのは、「サイドカラムがあるのであれば表示領域は少なくとも右側にサイドカラム幅はあるが、シングルカラムになるとそうではない」からだ。(このページのシングルカラム境界は800pxである)

機能チェックしているが、document.addEventListenerできないブラウザでJavaScriptに対応しているものはあまり残っていないだろうし、あっとしても単純にイベントリスナー設定時にエラーになるので放置してもいいかもしれない。 ただし、間違って複数回ライブラリが読まれたときのためにArt.tooltipはしておかないとイベントリスナが複数設定されてしまう。

PureBuilder Simplyはうまくいっている

PureBuilder Simplyがここまでうまくいっている理由としては、やはりPandocの強力さがなによりだろう。

PureBuilder SimplyはPandocが持っている機能をちょっと拡張する…という考え方をしている。 今までドキュメントジェネレーター自体を制作していた(PureDoc)ことと比べると問題はかなり簡単になっている。

Pandocの動作は必ずしも簡潔ではなく、自分で実装するのであればドキュメントジェネレーターにここまでの機能をもたせることはないだろう。 だが、Pandocがある以上はPandocを使いたい。

もしPandocがなければdocutilsを拡張することを考えただろうが、その場合はPureBuilder Simplyは今のように良いツールにはなっていなかっただろう。

PureBuilder Simplyが今ほど素晴らしいツールになったのは、Pandocがあったからこそだ。

Mimir Yokohamaに対して行われている様々な拡張は今の所PureBuilder Simplyに対して適用されていない。 これは、PureBuilder Simplyが生成するものに対する機能ではなく、Mimir Yokohama固有の、そしてテンプレートとCSSによるものだからだ。

だが、いくらかでも一般化してPureBuilder Simplyに還元していければと思っている。 PureBuilder Simplyのエコシステムの充実は普及には不可欠だろうから。

Mimir Yokohamaに「いいね機能」「コメント機能」を追加

概要

Weekly 10000PVを達成して機能強化に力の入っているMimir Yokohamaのウェブサイト。

連続の機能強化でついに「いいね機能」と「コメント機能」が追加された。

実は先日の「お問い合わせフォームの実装」は単にその機能を実装する最小限ではなく、簡単なアプリケーションを実装できるプラットフォームになっており、 アプリケーションを追加する条件が整っていたのだ。 また、そのためのテストもしてあった。

そのため、実は今回コード追加はわずかで、両方合わせても23行ほどにとどまる。 ごく簡単だが、確証が持てないためにテストと本番環境のための修正を行ったりして結構な時間がかかった。

これに関してはみるべきところはあまりない。 受け取ったパラメータをファイルに書き込めば良いだけだからだ。

ちなみに、連打しやすいアプリケーションを入れるために連打の対策もサーバーにしてあった。 実は先のパフォーマンスチューニングはこの対策によってパフォーマンスが低下してしまったため、これをカバーするついでに行ったものだった。

いいね機能の設計

いいね機能はごくシンプルだ。

Pandocテンプレートを使ってページタイトルを埋め込むことができるので、これだけ使うのであれば単にテンプレートの中にフォームを書けばいいだけ、ということになる。

実際はリファラ(Rack::Request#referer)及びユーザーエージェント(Rack::Request#user_agent), IPアドレス(Rack::Request#ip)も特定に使用している。

ポイントは「一度送信したら表示を変更、送信を無効化する」ということだ。

inputタグのdisabled属性を使うことで送信ボタンを無効化している。

そう、この機能はHTML上でインラインで書かれているのだ。 このような書き方はW3C的には推奨されないのだが、Googleは推奨している。 実際、これだけのためにJavaScriptファイルをロードさせることをしたくなかったので、インラインにした。

ポイントは

  • Legacy DOMにおいてフォーム部品は連想配列のようにアクセスできるようになっている
  • submitボタンのラベルはvalueである
  • disabledによってフォーム部品を無効化できる

である。

これらの処理は送信の「前に」行われる。 これは正しいことではないが、問題はない。 なぜならば

  • 送信できなかったからといってユーザーが修正するなどの手を入れる余地はない
  • 特に返信を必要とするものでもないので、送信失敗はクリティカルな問題でもない
  • サーバーエラーなどは表示されるようになっている

からだ。

また、画面変遷せずに送るだけ…というと、Ajaxで非同期に送るしかないように考えるかもしれないが、 実際は現代のブラウザは基本的に2XXステータスで空コンテンツを返すとページ変遷しないようになっている。

このような用途のために204(No Content)が用意されているため、204を返す仕様だ。 できるだけブラウザの標準機能に頼るようにしている。

このボタン、めっちゃgooglebotが押してくる…

コメント機能の設計

公開されるものではなく、sanitizeしなくても問題が発生しない設計になっている(単に文字列として扱う以上の取り扱いがなされる条件で使用しない)ため、非常に楽だ。

難しく考えるよりも、シンプルで挙動をちゃんと把握できていて、余計なことをしない方法をとるのが最も楽に、確実に、バグなく設計できる。

ただ、この機能に関しては「コメントフォームの表示」などが必要になり、JavaScriptが必要になった。 また、コメントするというあまりとらない行為のための部品であり、常にロードすることは非常に好ましくない。

そこでとった方法は「JavaScriptの遅延ロード」であり、「クリック時にelementを追加してJavaScriptを読み込む」という方法だ。

これでscript要素を追加している。

では呼び出されたあとどうしているのか…というと、こんな感じだ。

そう、コメントフォームは標準でHTMLに含まれていない。

パフォーマンス的にみても親切機能のためにもう重くなりつつあるドキュメントをこれ以上重くしたくないので、 「フォーム部分はJavaScriptがロードされたときにinnerHTMLで書く、という方法をとっている。

「クリックされたときにはじめて必要とされるのでドキュメントノードを追加してロードする」という手法は稀に使われるが、さすがにそれでHTMLドキュメントそのものを生成するというのはまず見ない手法になっている。

連打されたときのことを考えて、clickイベントを発生する値に特殊なプロパティを埋め込み、何度も実行されないようにしている。 なお、これは本当に連打したときだけ機能するもので、JavaScriptシングルスレッドなので一回このスクリプトに入っていまえばこのスクリプト中に割り込まれることはない。 だが、連打されるとイベントがキューに入ってしまうので何度も実行されてしまうことから、そうしたことがないようにしている。 シングルスレッドなので、このスクリプトが実行されているのにプロパティが設定されないうちにまた実行されるということはない。

元になるフォーム自体は存在していて、submitボタンを配置すればフォームの送信は可能だ。 そのため、submitイベントに対するイベントリスナーを設定するものはHTML上に静的に存在している。

その上で「フォームはやっぱり閉じられたほうがいいな」ということでボタンクリックに対するイベントの変更、及びボタンラベルの変更を行っている。

送信を行った場合はもうコメントフォームは使わないので、コメントフォームは非表示にして(削除はしていない)ラベルを変更している。

ラベル変更だが、input部品とは違ってbutton要素は子要素テキストノードがラベルになっているので、dataプロパティの書き換えによって変更している。

もともとHTML上でLevel0 DOMイベントを使っているので、スクリプト上でもLevel0 DOMによってイベントを変更している。 いつもaddEventListenerを使っていたので、珍しい。 イベントの削除はイベントにnullを代入するだけだ。

なお、HTMLで全て含めてしまえばJavaScriptは排除できるのだが、これ以上余計な要素を組み込むことはできれば避けたいためこのような仕様になっている。既にDOMコンパイルは割と重い。

また、デザインポリシーからいって、多くの場合余計なコメントフォームを常に表示させておくというのは美しくないとも思っている。 JavaScriptを使わずCSSでオンオフするようにもできるが、そうすると今度はボタンのテキストを変更するのが難しいし、連打や再送信を防ぐのも難しい。

このことから、なにがなんでもJavaScriptを使わないのが正義、ということでなければ、 このような付加コンテンツはユーザービリティの点からも素直に使うべきだと思う。

こちらもいいね機能同様、送信する前に状態を変更してしまい、成功すれば204を返す仕様。

Legacy DOM と DOM Level0 Eventに関して

Legacy DOMやDOM Level0 Eventについて意見をもとめられることがたまにあるのだが、私としてはあまり勧められないと考えている。

非常に簡単なので学習にはいいが、Legacy DOMはHTMLの構造に依存する。例えば

として、

とかやってしまうと、ドキュメントの構造が変わるたびに修正だ。

だが、「フォームにのみnameで使い」「フォームはW3C DOMで特定する」のであれば悪くない。

DOM Level0 Eventはイベントリスナーが1つしか登録できないためモジュール設計になっている場合や、なんらかのライブラリを使っている場合は使ってはいけない。 イベントを「追加」する場合はLevel2で、イベントを「設定」する場合はLevel0という使い分けも考えられる。 HTMLに直接書く場合はLevel0しか書けないので、その部品の基本的な動作と定義されているならLevel0でもいい。

ただし、その場合でもできればLevel2 Eventを「後から追加する」のが適切なので、DOM Level0 Eventの使いどころは今回のようなものが唯一だと思う。

Chromiumのインスペクタにいつの間にかモバイルデバイスインスペクションが入っていた

web分野の人にとっては当たり前の可能性もある知識だけれども、Chromiumのインスペクタにモバイルデバイス向けの検証機能がついていた。

Chromiumのモバイルインスペクタ

これをオンにすると、モバイル的な画面サイズとなり、同時にタッチエミュレーションが行われるようになる。 レンダリングが調整されるということはなく、あくまでこの2点が変更される。

モバイルインスペクタのガイド画面

Responsiveモードではサイズを柔軟に変更することができる。 実際に小さな画面ではどのように表示されるのかということをエミュレートできる。

また、デバイスごとの画面設定も可能。 候補は少ないがiPhoneは入っている。

機種や回線をエミュレートできる

ここで言う解像度は論理ピクセル数である。 例えばiPhone8の画面の実ピクセル数は1334×750だが、668×375と認識されている。

これはスケーリングによって1pxを実際の1ピクセルと対応させない、というもので、先日書いたので参考にしてほしい

拡大率は画面表示のサイズを変更するもので、リアルサイズに近づけることで物理的な大きさもチェックできる。

上部にあるガイドは各種デバイスに合わせた表示状態をエミュレートできるものになっている。 クリックするとResponsiveの状態で各プリセットにあったものになる。 Laptopが1024px, Laptop Lが1440pxとなっており、この上に4k2560pxもテストできるようになっている。

また、ネットワーク状態もエミュレートできる。 低速な回線ではどれくらいロードにかかるのかということもテスト可能だ。 offlineはページをロードしたあとにofflineにし、Ajaxが使えない状態をテストすることができる。

Low-endでMimir Yokohamaが4秒程度なのに対し、Chienomiは15秒ほどかかったので、その高速さにだいぶ満足している。

デバイスの論理ピクセル数なんて調べるのはとても大変なのでプリセットが入っているのはとても嬉しい。 サイトの設計にもよるが、私の設計だとアップロード前に十分なテストが行える。

Arial/Helveticaを置換え 読みやすい欧文をウェブに

本当にArialが見やすいと思うのかい?

さて、突然だがあなたに質問だ。

次の画像を見てあなたが読みやすいと思うのはどれだろう。 「見やすい」ではない。ちゃんと英文を読んで、読むのが楽だ、このあと延々百何十ページとこの英文を読むのも苦にならないと思うのはどれか、ということである。

比較1

比較2

比較3

比較4

あなたの心は決まっただろうか?

webでなぜArialを指定したがるのか

Arialが指定されている現実の確認

まずはTwitterのフォント指定を見てみよう。

次にGoogle

Yahoo! Japan。Arialは指定するがHelveticaは指定しない。 MS PGothicが先に指定されているので、かなりArialを指定している意味は恐らく全くない。ヒラギノより先にしている理由もわからず、 これはさすがに技術力が疑わしい。

さらにいえば、フォントファミリーにクォートなしでスペースが入るのも無効なはずなので、技術力完全に駄目なやつでは…?

Mozilla。Zilla Slabはウェブフォントになっている。

Facebook。Helvetica優先でヒラギノ優先なあたり、Facebookはりんご党らしい。

Microsoftコミュニティは日本版でも欧文フォントのみの指定。 それもWindowsデフォルトフォント優先だが、MacデフォルトのHelvetica Neueを優先的に指定する謎のこだわり。

震源地となったMSNは現在は指定なし。私が指摘してから変えた…?

理由と経緯

ArialというフォントそのものはHelveticaの代替フォントである。

Helveticaは1950年代からあるフォントで、すごく使われてきた。PanasonicのコーポレートロゴもHelveticaだ。

Helveticaは現在はライノタイプのフォントで、Macに入っている。 MacはHelveticaを改変したGenevaもあって、割と重用しているようだ。しかもArialもある。

Arialのほうは1982年にRobin Nicholas, Particia Saundersがモノタイプに依頼して制作したもので、Hervetica互換フォントで形は少し異なるものの文字幅は同じ。

ArialはAlternativeを含めればWindowsに古くから収録されていて、Helvetiva互換フォントとして使われてきた。 WindowsのArialはフォント名にHelveticaを含んでいるので、Helveticaと指定してもArialが使われる。

そして代替フォントであったはずが、Arialのほうが美しいとか、WinとMac両方にインストールされているとか1いう理由でArialが主流になっていく。 Arialもポストスクリプトフォントとしてはかなり初期に誕生しているし、当時はあまりポストスクリプトフォントはなかった(し、高額だった)。 指定しなければビットマップフォントが使用される可能性が高く、すごく雑なビットマップフォントが多かったから、Arialの指定は高品位フォントの指定だったといっていい。今の日本の感覚で言うと、モリサワのフォントを指定するような感じだ。特にヒラギノ。

Arialが使える前提に立っていると、UIに使いやすかった。 ボタンはピクセル数を指定しなければならないけれど、その上に載せるフォントに合わせていい感じに調整する機能はなかったから、 「ボタンの上にこの文字を載せるか指定するためにはこの文字がどれだけの幅を取るか特定できなければならない」だったのだ。 当時はDPIも固定だったから、これは表示してしまえば確実に制御できたし、そこでArialが使える前提に立てば簡単に作ることができた。

あと、ワープロ文書でも文字数と改ページの関係でボックスサイズが変わると困ることがある。 ここでもArial前提にしとけばワープロ文書のままで受け渡しができる、というわけだ。 これは今MS Pゴシックの状態でワード文書を送りつける愚か者2と同じような心理である。

ところがだ。HelveticaにせよArialにせよフリーなフォントではない。 そこでサイズが同じ「メトリック互換フォント」がArial以外にも作られることとなる。 これはもう、Helvetica互換というよりはArial互換である。

  • Nimbus Sans (URW)
  • TeX Gyre Heros (Ghostscript)
  • FreeSans (GNU)
  • MS Sans Serif (Windows)
  • Arial (Microsoft)
  • Liberation Sans (Liberation Font Project)
  • Arimo (Chrome OS)
  • Albany (StarOffice)

これでArialに対する互換性をクリアしたわけだ。

和文フォントでも若干そういうものはある。 AAフォントと呼ばれるもの(IPAモナーフォントやMonapoなど)は5ちゃんねる(当時の2ちゃんねる)上でアスキーアートを表示するためのものだが、 梅フォントファミリーがMSフォントメトリック互換であるのは恐らくワープロ文書やメールにおけるレイアウトの都合だろう。

webでの指定の話

CSS登場以前はUI部品のサイズを柔軟かつ明瞭に指定する方法がそもそもなかった。

そのためUI部品にテキストを載せる場合(画像にすると当時は重かった)、レイアウト上「横にいくつボタンを並べる」という都合があり、 これをぴったりに載せる方法がWindowsでもMacでも使えるArialを使う、という方法だった。

これが「WindowsとMacにインストールされているフォントを考慮する」という誤った理解となり、思考停止したままWindowsとMacのフォントをそのまま指定するようになったのが「システムデフォルトの日本語フォントを指定する」問題だ。

挙げ句、MS Pゴシックはまだしもヒラギノとは全くデザインが合わない、単に美しくない状態となるだけの「欧文フォント指定があるから、それを消さずにその後ろに日本語フォントをWindowsとMacで並べればいいよね」という最高に頭の悪いアクションが生まれる、というわけだ。

だが、和文フォントをシステムデフォルトのものを指定するのが 全く意味がない のに対して、Arialを指定することは前述のように多少意味がある。 ただし、現在はCSSを使ってUIサイズを文字サイズ基準で指定できるため、まともなデザインセンスがあるのであれば問題にはならない。 そう、デザインについて数字でしか理解できないエンジニアが解決方法としてフォント指定に走るのはまだわからなくはないが、デザイナーがそれをやったら完全にデザインを投げ捨てているということになる。

基本的に「なり」のデザインになっていれば特に指定する必要はないのだが、UI上(好ましいことではないが)ボックスの中に入っていないテキストなどを書く必要がある場合はArialを指定するのは意味のあることである。 もちろん、その場合でもGoogleがしているように

とするのが適切。

ただし、google検索においてArialの指定が必要かどうかは疑わしい。 ただし、英語で検索するとGoogle検索の結果はArialの場合は2行であるため、2行に内容を詰め込むためには詰め詰めのフォントを使わせたい、というのがあるのかもしれない。そうでないフォントを使うと3行になったりするからだ。 その意味では「ゆるやかに意図を使える指定」ということで、ある意味妥当である。

Google検索の英語での結果 (Raleway)

このArialと汎用のサンセリフを指定する方法は日本語であっても有効である。 日本語はユーザーが設定したサンセリフ体が使われるだろうし、設定していなくても適切な日本語フォントが選択されるため、全く問題ない。

だが、そんなことを理解せず、思考停止してArial > Helvetica > MSPゴシ > ヒラギノにしているサイトが あんまりにもあんまりにもあんまりにも多い ので、3現実的に美しく読みやすく表示させようと思ったら、 「ArialとHelveticaとMS Pゴシックを置き換える」必要がある。

FontConfigに関してはウェブブラウザは当該フォントがFontConfigのエントリ上存在する(物理的にはなくてもいい)場合にその第一候補だけを取得するので、Arialに複数のフォントを設定しても駄目。 そのためArial+MS Pゴシックで合成されることを前提とした設定を行う、ということになりそうだ。

せめて日本語サイトで欧文フォントを指定していなければArialを英語専用で考えることができるのに、langも使わずまぜおって…

UI部品がArialでないと困るという場合でも、それはUI部品をクラス指定して、あるいはUI部品のタグで指定してフォントを指定すべきで、bodyに指定すべきではない。

デザイン的に指定したい (今の時代にデザイン的にデフォルトフォント、かつArialを指定したいとかセンスが死んでるけど4) 場合も「共通で入っている」とかじゃなくウェブフォントにすべき話だ。 和文フォントは大きすぎるため非常に迷惑になるが、欧文フォントは小さいので結構平気なもの。 Roboto, Open Sans, PT Sansあたりはキャッシュされている可能性が高いので、自前ではなくGoogle Fontsあたりを指定するのが良い。

なおWindows

Windowsでは標準でSerifやSans-serifを置換えることができないけれど、 Google ChromeもFirefoxもフォントの設定ができるし、している人は多い、少なくともユーザーにはその自由があるのでやはり強制はすべきでない。

Arialは本当に見やすいか?

冒頭の画像は

  1. Oxygen-Sans
  2. Arial
  3. Sen
  4. Input Sans Narrow

である。 私はInputが圧倒的に読みやすい。

見て気づくかと思うが、Arialはボックスに対して黒い「太めフォント」であり、極めて文字間を詰めた「詰め詰めフォント」である。 ある意味ではMS Pゴシックと類似の特性と言っていい。

Arialは「詰め詰めのフォント」であるため、代替は意外と難しい。 実際にArialは日本語フォントと比べpx数の決まったUI上に載せることが多く、サイズが違うフォントはUIからはみ出すケースが少なくないのだ。

そこでSans-Serifフォントから適当に見繕って表示させてみた。 Arialはとってもプロポーショナルなので文によって長さは大きく変わるが、おおよそこんな感じになった。 なお、試したフォントは200弱ほどあった。

サンセリフフォントの比較 (1)

サンセリフフォントの比較 (2)

サンセリフフォントの比較 (3)
サンセリフフォントの比較 (4)
サンセリフフォントの比較 (5)
サンセリフフォントの比較 (6)
サンセリフフォントの比較 (7)
サンセリフフォントの比較 (8)
サンセリフフォントの比較 (9)
サンセリフフォントの比較 (10)
サンセリフフォントの比較 (11)
サンセリフフォントの比較 (12)
サンセリフフォントの比較 (13)

この中からArialの代替になりそうなフォントを比較してみる。

Arialと代替フォントの比較

割と小さなところにも使われたりする(例えばTwitterのスクリーンネームとか)ので、グリフが小さいフォントは割とツライ。 UIフォントとして人気の高いUbuntuとOxygen-Sansは幅も似たようなもので優秀に見える。

私はlやtの字形がカーブしてくれているほうが見やすいように感じる。ここにはabilityみたいな縦棒いっぱいの単語がないが、 ぱっと見に字形を区別しやすいフォントが日本人には望ましいのではないか。日本語にはあまり「ぱっと見に字形を区別できない字が並ぶ」ということがないし、単語を字形で判断しようとするので、スペースで区切られた単語単位で識別して字は見ないという習慣があまりないためだ。

そう、日本人は日本語に慣れているが、日本語と英語では性質が違う。 日本語では一文字あたりの意味価値が高く、文字の識別が重視される。対して英語は単語感覚なので、どちらかといえば「単語の形」で見ていて一文字ずつはあまり見ていない。

Arialは詰め詰め黒々であり、単語の形で見るのであればなんとなく見やすいかもしれないが、ちゃんと文字がどう並んでいるかを識別しようとするとコストが高い。もうちょっと離すか、細くするかしてほしいし、字形も区別しやすいようにしてほしい、と感じるわけだ。 しかし字を離すと幅が増えてしまう。 TwitterのスクリーンネームなどでもArialが使われてしまうため、グリフ自体を小さくするとそれはそれで見づらい状況になる。

普通のCondenbcedでも厳しいくらい幅が狭く、代替を考えるのも難しい。

なお、字形の差については実はあまり問題がない。読みやすいフォントに代替するのであれば問題になるのは幅であり、例えばSans-serifではなくSerifを選択しても構わない。 ただし、それはデザイン的にArialが選択されている場合には違和感があるかもしれない。

Arialの代わりを探せ

もう少し長文で比較する。Arialがちょうど3行になっている。

Arialと代替フォントの比較 詳細(1)

Arialと代替フォントの比較 詳細(2)

デザイン的にも幅的にも差を小さくしたいのであればUbuntu、またはOxygen-Sansが有力候補だ。 Ubuntuは若干幅広、Oxygen-Sansは細字で詰めているため幅は逆に詰まる傾向がある。

また、リストに入れ忘れてしまったがSource Sans Proはかなり優秀な候補だ。 Source Han Sans Proの日本語部分でもあるため、かなり使いやすい。合成フォントとして人気の高いOpen Sansはやや幅が増えるが、こちらも優秀。

字形が違うものはどうだろうか。 Overlockは英文なら読みやすいのだが、日本語の中に混じっていると結構読みづらい。 Ralewayあたりだと変化が激しく若干の違和感があるが、不自由はない感じになる。

個人的に良いと思えたのはFira Sansだった。SenやAmikoもなかなか魅力的だが、幅の差がちょっと大きい。 今のところFira SansとRalewayとSenで迷っている感じだが、どうも私にはRalewayが良さそうである。

あとはもう好みの問題だ。だが少なくとも、Arialが他のなにより素晴らしいとは私には思えない。

設定例

メトリック互換

メトリック互換フォントに何を使うかはお好みではあるけども。

いい感じに表示する

和文はDPI100程度ならばさわらびゴシック、DPI150程度ならばMMCederがいい感じだった。


  1. もちろん、Helveticaで指定しても両方で使えるのだから、これを言う人は知恵が足りない。

  2. 相手がWindowsでない可能性、相手がMS Pゴシックを持っていない可能性についてわずかでも考えたのだろうか? 相手のことについて思索を巡らせず、自分に問題がないから構わないと考えるのはこの上なく愚かである。

  3. そんなこと言ってこのサイトも…と思っているみなさん。 あんまりCSSいじりたくなかったけど、直しました。アップデートでまた直すことになるのかなぁ。一応追加CSS側で上書きするようにしてはいる。

  4. 「英語のオーソドックスでキレイなフォントを指定したい」場合、RobotoあるいはOpen Sansが現代の主流。 現代は選択肢も豊富で、改良も続けられてきたので、Arialはさすがに古い。

Webページを単一のHTML (data, BASE64形式) に保存する

webページを保存するのにwgetは便利なのだけど、最近のウェブページは非常に複雑なので大量のファイルが生成されることがある。

もちろん、単純には

$ wget -p -k -E URL

でいいのだけど、ちょっと使いづらい。 単純にページを保存したい場合には、「そうじゃないんだよなぁ」と思うことがある。

あるいはテキストページなら

$ w3m -dump URL > file

という手もあるけど、これも画像が入っていたりレイアウトされていたりすると「そうじゃない」となる。

mhtmlは扱いづらいし、せっかくdata形式で埋め込めるのだから、画像やCSSを埋め込んだHTMLファイルを作ってほしい。

なんかないものかと探したところ、zTrix氏によるwebpage2htmlというプログラムが見つかった。

これがなかなか秀逸。

python webpage2html URL > file

でいいので話が早い。依存関係もpipで解消できる。 (requirements.txtも用意されている)

自動化が困難な場合にはwgetと組み合わせて二手間ほどかければ大丈夫だろう。

完成度はそこそこ。 今わかっている問題としては、サーバー側でcharsetを返さない場合、HTML内に書かれていたとしても文字エンコーディングを識別できず文字化けする。 指定もできない。

Pythonは内部文字エンコーディングに変換するため、ここでバグってしまうのだろう。 やっぱりwgetで落としてきて自前ウェブサーバーで指定するような手間が必要になる。

すごくいいツールなので、ぜひ育てていってほしいところ

メッセージフォームのサポート (Nginx + FastCGI + spawn-fcgi + Rack + Ruby)

あらまし

Mimir Yokohamaでついにお問い合わせ方法として「メッセージフォーム」が追加された。

なにがついになのか、なにをドヤっているのかと思うかもしれない。 まぁ、ドヤってはいないのだが。

実は私はかなり長い間ウェブアプリケーションをほとんど作っていない。 そして、今まで私が作ったウェブアプリケーションは、専用サーバーを持つサーブレットタイプか、もしくはCGIだった。

馬鹿にされがちなCGIだが、利便性は高く、頻繁にアクセスする性質を持たないアプリケーションには適している。

そして、そもそもウェブアプリケーションを作っていなかったのは、私が「事前生成戦略」の研究と実験に注力していたからで、 どちらかといえばウェブアプリケーションからは離れる方向にあった。 そして、ウェブアプリケーションを必要とするとしても大部分は静的ページとして提供できる方式を目指していたため、CGIで十分事足りたのである。

ちなみに、これまでウェブサーバーは

  • Apache
  • lighttpd
  • delegate
  • Nginx

という経過をたどっている。 Apacheは言うに及ばずlighttpdとdelegateはApacheよりもCGIが簡単だったので、「ほぼCGI」だった。

だが、時代は変わった。NginxはCGIをそもそもサポートしない。 私も新しい時代に対応する必要がある。

ちなみに、この作業は次の仕事のための実戦テストという意味合いもあった。

方針を考える

最も話が速いのはFastCGI Wrapである。

NginxはFastCGIをサポートしている。 FastCGIはプログラムをデーモンのように起動しっぱなしにする。

だが、通しで実行するプログラムとデーモンではそもそもの前提が違う。 そのためCGIプログラムをFastCGIとして動かすのはそれなりにハードルが高い。

そこでFastCGI Wrapの登場である。 FastCGIとして利用されるプログラムをFastCGI Wrapにする方式だ。 このラッパープログラムは要求に合わせて都度CGIプログラムをCGIインターフェイス経由で起動する。 結果的にFastCGIの意図は無視して従来型CGIを動作させるようにするというものだ。

この方法は結構出てくるのだが、基本的には既存のCGIプログラムを動作させる話である。

個人的な感覚としては、無駄なプロキシを噛ませるような方法を使ってまでCGIに固執したくない…というか、実はfcgi-wrapってそれなりにめんどくさい。

だったらFastCGI直というのもありかなぁ、と考えるわけだ。

ところが、やっぱりFastCGIはデーモン状のプログラムを想定しているわけで、やはり前提が違う。 要求として割と複雑なのか、デーモン化に関してはspawn-fcgiに担ってもらって、さらにRackを使う、というのがどうやら主流らしい。

だいぶ話が複雑になってきた。

サーバーはNginxである。NginxはFastCGIインターフェイスを経由してFastCGIプログラムにパラメータを渡し、応答を受け取る。

FastCGIプログラムはデーモンである。 Rubyでは次のようにしてFastCGIプログラムを書くことができる。

あるいは、CGIライブラリ互換インターフェイスを使うことで、#each_cgiの中身はまるっきりCGIと同じにすることもできる。

spawn-fcgiはこのデーモン部分を担う。 つまりeachしてる部分を担ってくれるわけだ。

プロセスとしてCGIインターフェイスで起動するわけではないので、fcgiwrapほどの互換性はない。 感覚はCGIに近いが、インターフェイスは意識する必要がある。

Rackはミドルウェアと呼ばれている。これはまずFastCGI抜きで話そう。

Rackはインターフェイスを担っている。 今までプログラムはCGIなり、あるいはFCGIなり、さらには各種フレームワークやサーブレットの様式(例えばSinatraとか)で書いていた。

Rackはこれらの違いを吸収するモジュール設計のものだ。 Rackに準拠したプログラムを書いておけば、たとえ愛用のフレームワークがディスコンになっても、サーバーが変わっても安心、というわけだ。

だが、Rack自身はサーバーではないからサーバーがいるのだが、Rack組み込みのサーバーというのはもう完全にRuby世界の住人だ。 だってRackはRubyのWebアプリケーションインターフェイスだから。

Passengerというソフトウェアがあって、これはwebサーバーのモジュールとしてRackに対応する。 Apacheでは比較的簡単だけれど、Nginxだと結構きつい。

そこでRackに対応したサーバーを立ててサーバーとサーバーでやりとりさせる、という方式がすごく現代的。 直接にRack経由でプログラムとやりとりするのはRackに対応したサーバーだけれど、Rackに対応したサーバーにwebサーバーとしての機能を持たせると大変なので、「本物のwebサーバーに矢面に立ってもらって、RackサーバーはあくまでRack対応に特化」というわけである。

Rackに特化したサーバーとしては(別にRackだけではないんだけど)、Webrick, Mongrel, Puma, Thin, Unicornあたりがある。

しかしRackでやりとりする方法があればいいので、FastCGI + Rackという方法もある。 それはRack側でFastCGI経由で受け取って、応答するためのハンドラが用意されている。

つまり、Unicornのようなサーバーを立てる代わりの手段としてFastCGIが使える。 FastCGIもデーモンを必要とするので別にFastCGIにすることで間に挟まってるものを減らす効果はない。 ただ話が楽になるだけである。

Unicornはむちゃくちゃ速いので、UnicornでUnixドメインソケットを使えば形式とししてはspawn-fcgiでUnixドメインソケットを使っているのと一緒だし、やっていることははるかに高度になる。 これが超モダンなやり方である。

が、あえてのFastCGI。 理由は管理する要素数を減らすためである。必要がないのにいかついものを使うことはしない。 これはサーバー運用のコツでもある。

なお、Rackに関してはかなり情報が少ない。 なんらかのフレームワーク…というか、ほぼRailsのバックエンドとしてのRackの話だけで、Rack単独の話ってない。 そして、FastCGIを使う話もない。これもだいたいなんらかのアプリケーションが「使ってる」あるいは「使わせる」話になる。

なんというか、みんなそんなに自分でプログラム作るってことをしてないのか… 世の中エンジニアたくさんいるのに、WordPressとRailsだけで満足なのか…

そんなわけで情報が猛烈に足りていない中、FastCGIとRackについて勉強することになったわけだ。

なお、Nginxでアプリケーションとやりとりする方法に関してはDiscourceで散々やったので経験済みだ。

なぜRackなのか

もちろんこのことからもわかるようにRackはなくても構わない。 spawn-cgiも使用せず単独のFastCGIアプリケーションを開発するのは容易である。

私が気にしたのはRubyのfcgiライブラリは2013年から更新が止まっているとい点だ。 また、Arch LinuxではfcgiライブラリはAURにもなく

# gem install --no-user-install fcgi

とするよりない。

ベーシックな機構であるFastCGIそのものが廃止になるようなことは考えにくいが、NginxのCGIの扱いのように消極的なサポートへと変遷する可能性はある。 その場合にアプリケーションの書き直しが発生してしまう。

Rackは現在主流であり、新規採用例も多い。 Rackが廃止になると影響を受ける範囲も非常に広いので今後10年は安泰だと思われる。

そこでFastCGI+Rackという構成にしたわけだ。 この場合でもRackはFastCGIをネイティブサポートしているわけではく、fcgiライブラリを使ったハンドラを同梱しているだけなのでfcgiライブラリは必要となる。実はこれを回避したかったのだが、結局はできなかった形だ。

とはいえ、この状態であればFastCGIを捨ててUnicornに移行するのも難しくはない。

とりあえずやってみる

Nginx

location / {
    root /var/www/testapp;
    fastcgi_pass /var/run/fcgi-testapp.sock
    fastcgi_index testapp.rb;
    include fastcgi_params;
}

Rack Application

Requestのほうはインターフェイスに絡むけれど、 Responseは単純に#finishでRackに沿った配列を返すための便利クラス。なくてもいい。

spawn-fcgi

# spawn-fcgi -U http -s /var/run/fcgi-testapp.sock /var/www/testapp/testapp.rb -n

試してるうちは-nつきにしてフォアグラウンドで実行するのが楽

実用的にする

起動スクリプト

forkingなので停止・再起動の制御のためPIDファイルを作る。

Systemd Unit

[Unit]
Description = FastCGI Rack Test Application
After = nginx.service

[Service]
Type = forking
PIDFile = /var/run/fcgi-testapp.pid
ExecStart = /usr/local/sbin/fcgi-testapp.bash
ExecStop = kill $MAINPID

[Install]
WantedBy = multi-user.target

forkingなので$MAINPIDがそのままでは使えないため、PIDFileで指定しておく。 Nginxのあとに起動しておいたほうがいいような気がしたけど、なくても構わない。 アクセスが激しい場合は逆にNginxの前に起動したほうがいいだろう

spawn-fcgi自体にはアプリをリロード、再起動するような機能はない。

おまけ

S-NailがSubjectも本文も、UTF-8をちゃんとエンコードしてくれるのですごくびっくりした。

「mailxとは違うのだよ!!!」ってことか。 さすがSMTPやPOPやIMAPにも対応しているだけのことはある。

ここの部分(MIMEエンコーディング)も自分でやるつもりだったので、かなり省力化された形。

今回の構築は他にも色々やったのだけれど、共有して意味のある部分はこれくらいのものだろう。

静的ウェブページでタグ機能を提供する

Mimir Yokohamaのページでタグ機能がバージョンアップし、完全に動作するようになった。

もともとWordPressで提供していたMimir Yokohamaのウェブページだが、独自システムに移行する際には「WordPressで利用していた機能はすべて提供する」という方針のもと、新しいサイト構築システムPureBuilder Simplyを開発して構築した。

PureBuilder Simplyは静的ページを生成するプログラムであり、webサーバーには静的ファイルを配置する。 これはパフォーマンス、セキュリティ、管理、リソースいずれにおいてもメリットが大きい。

基本的にこの考え方は「異なる内容を生成するタイミングより、同一の内容を返すタイミングのほうがずっと多い」ということに基づいており、キャッシュよりも合理的である。 一方、どうしても難しい要素もある。ひとつはページ生成パターンが無限である検索機能、そしてもうひとつはヒントを日本語のみにした場合のタグ機能だ。

検索機能はGoogleに頼っているが、タグ機能は難関だった。

タグ機能を作る

方針

  • 要求タイミングでの動的生成は行わない
  • PureBuilder Simplyの枠内で解決する。 もし解決不能な場合はPureBuilder Simplyを拡張する
  • (PureBuilder Simplyでサポートされている) eRubyテンプレートは使わない。あくまでPandocテンプレートで生成する
  • ファイル名が日本語になることはやむを得ないものとする (Nginxは日本語ファイル名に対してURIエンコーディングされたパスでアクセスできる)
  • リンクを日本語で書く(エンコードをブラウザに委ねる)ことは許容しない
  • ユーザー (この場合自分だけど) にタグに関して文書にタグ付けする以上の手間をかけさせない

タグをつける

既にPureBuilder Simplyでは 「Frontmatterに文書に関する追加的情報を書く」 という仕様となっている。1

単純にこれを反映したタグを書けば良い。

ページにタグ情報をつける

Pandocテンプレートで簡単につけることができる。

英語なら割とこれで済む話なのだけど2、日本語だと当然

みたいなHTMLが生成されてしまう。

そこで、post generate機能を利用する。 post generate機能はページを生成したあと生成ページを加工できるものだ。 基本的に第一引数として生成されたファイルパスが渡される。 このほか環境変数を通じて他の情報にもアクセスできたりするのだが、これはあまり利用を想定していない。

post generateは.post_generateディレクトリ内のファイルを順次Perlに渡す形で実行される。 Perlはshebang行を解釈するので、Perlで書かなければならないわけではない。 そして、スクリプトの出力にファイルは置き換えられる。

これは例えば

のような非常に簡単なフィルタが書けるということだ。

これを使って

のように変換してあげればタグのタイトルは日本語だがURIはエンコード済み、という形ができあがる。 もちろんスラッグへのマップを書いてもいいのだが、タグを管理するのは手間なので避けた。

タグページを作る

生成されたページの情報はindexes.rbmというファイルにRuby Mershal形式で保存される。 ここには本文は含まれないが、大概メタ情報にアクセスしたい場合と本文にアクセスしたい場合は別なので、分けている。

PureBuilder Simplyにおいて、「メタ情報を文書に書き、処理した情報はデータベースに書いておく」というのは設定上の核であると行って過言ではない。 これにより、文書のメタ情報を扱うことはPureBuilder Simplyの外で行うことができるのだ。

タグページは.tagcloud.rbというスクリプトによって生成しているが、 これはMarkdownページを生成する。つまり、「タグページ自体をPureBuilder Simplyによって生成すべきページとして生成する」のである。 タグを含むページを生成・更新した場合は再度タグページを生成し直すことになり、そのためにrefreshというスクリプトもある。この場合、文書ページではないタグのindexを処理されてしまうと困るので次のような処理になっている。

では.tagcloud.rbは、というとこちらも結構単純。

見ての通り著しい力技でデータベースを集計し、最終的にはMarkdownを出力している。

従来もほとんどこうだったのだが、ページ側でエスケープしていないという理由でエスケープしていなかったので追加した。

おわりに

もう少し難しいかと思っていたのだが、どうやらPureBuilder Simplyの設計が思っていた以上に優れていたようで、タグ機能もスムーズに実装することができた。

PureBuilder Simplyは傑作といって差し支えない出来になっている。 もともと思っていたよりもずっと優れたツールになっているのだ。

PureBuilder SimplyはPureBuilderとしては実に3作目である。 Zshの機能をフル活用したPureBuilder, Windowsでも動作可能なようRubyで書かれたPureBuilder2。ページの生成にはいずれも活用できたが、サイト構築労力が高く、安価な案件で利用するにはしんどいものがあった。 また、構築できる内容も割と画一的だったため、様々な要求に応えるのは難しかった。 Zshで書かれたPureBuilderは構築時に任意のZshスクリプトを実行できる方式だったため、なんでもできるといえばできるのだが、サイト構築がプログラミング色の強いものになっていた。これはちょっとユーザーフレンドリーではない。

PureBuilder Simplyは名前の通りずっとシンプルだが、いままでよりずっと強力になった。

小さなスクリプトを書くことは、多くのプログラマにとってはあまり馴染みのないことかもしれないが3、やろうと思えば発想さえ知れば決して難しいことではないはずだ。


  1. これはReSTでもdocutilが許容しないような規格化されていないメタ情報を書くということだ。

  2. ちなみに、Chienomiではタグはすべて英語になっている

  3. Unixに浸っている人はむしろ息をするようにのように行動するだろう。PureBuilder Simplyの考え方はこれに基づいている。

Google Chromeが7月のversion 69でSSLを要求

どうなるのか

Google Chromeが7月にSSL(HTTPS)でないサイトに対して警告するようになる、という話を聞いたので、現在Developer Buildのversion 69をインストールして試してみた。

内容はIT Mediaの記事が正しかった。 つまり、

  • 従来は「HTTPSサイトは表示」だったものを「HTTPサイトは表示」に変更
  • version 70からはHTTPサイトでのフォーム入力時は警告表示

日経BPの記事をみると、まるでSSL署名が正しくないときのような警告がなされるかのように読み取れるがそんなことはない。

話はこういうことだ。

もともとはHTTPが標準だった。HTTPSであることは特別であり、故に「保護されています」と表示していたわけだ。 しかし、今後はHTTPSが標準であるため、いちいちHTTPSであることは強調せず、HTTPであることは逆にリスクがあることを示す、というわけだ。

この変更は正しいと思う。 対応した環境でブラウジングしているのに、HTTPではなくHTTPSであることで困る、ということは考えにくく、HTTPSであることに対してわざわざ注意を引く必要はない。 むしろHTTPであることはリスキーな状態であり、注意を示しておくべき状況だ。 特にフォーム入力を伴うときは警告すべき状況だといっていい。

状況 現在 version 69 version 70
HTTP 表示なし 「保護されていません」(グレー) 「保護されていません」(グレー)
HTTP + フォーム入力 表示なし 「保護されていません」(グレー) 「保護されていません」(赤)
HTTPS 「保護されています」(緑) 鍵マーク(グレー) 鍵マーク(グレー)

極めて正しいように見える。

GoogleがLet’s Encryptを推進したこととも整合性がとれている。

ただ、それなら「スキームなしでアドレスを入力したときにHTTPでアクセスする」ということはやめるべきだし、 むしろ「http://と指定した場合でもHTTPSを試行する」べきではないのか。

どうすべきなのか

これからは明確にHTTPSが標準となっていき、現在HTTPSに対応していないウェブサイトは「リスキーである」という認知が進むだろう。 これは、事情がわからないユーザーがHTTPの「安全でないサイト」を避ける可能性があるということを含んでいる。

  • HTTPSに非対応はもはや論外。早急に対応すること
  • HTTPSトライはしてくれないので、レガシーデバイスを切り捨てるならHTTPアクセスは301を返すべき
  • HTTPS強制にするにしても、HTTPアクセスを許容するにしても、ヴィジターへの説明はしたほうがよさそう

一応、Mimir YokohamaではHTTPS強制で、HTTPはサブドメインで許容する予定。

昨今のブラウザ事情

ウェブブラウザとウェブテクノロジー

今やウェブブラウザは中核的ソフトウェアといって過言ではないだろう。 世界はウェブを中心に回っているのが現実だ。

だが、それは問題をふたつもたらしている。

ひとつは、過当競争だ。

あまりにもウェブに依存しすぎているがために、ウェブが高度技術化し、開発コストも増大しとてもついていくのが困難になっている。 現状ウェブ開発をリードしているのはGoogleであり、ChromiumによるBlink/V8エンジンが最も進んでいる状態だ。 これに対抗するのはMozillaのGecko/SpiderMonkeyで、Chromiumよりも進んだ実装を行うこともないではないものの、現実にはFirefoxに実装されChromiumに実装されない機能は大概標準化されないのが現実だ。 (ただし、標準化においてはある程度Mozillaが主導権を握る展開が続いている)

MicrosoftによるEdgeHTMLはこれと比べて明確に遅れをとっている。 これはちょっとした理由がある。 そもそもEdgeHTMLはWebKitをターゲットとしているのだが、WebKitは現代ではとてもではないが使い物にならない。

WebKitを開発しているのはAppleだし、そもそもWebKitはSafariのものなのだが、WebKit自体はオープンソースだし、他のソフトウェアも利用することができる。 とはいえqtwebkitに関しては既に廃止となっており、WebKitのバージョンも古い。

WebKitとBlinkを比べる良い手段はMidoriとFalkonを使用することだろう。 MidoriはAppleがcommitするWebKitGtk+を使用しており、一方Falkon(かつてのQupzilla)はBlinkを使用するQtWebEngineを使用している。 そして、Falkonで表示に困ることはないのだが、Midoriでは普通に「表示できないサイト」や「利用できないサイト」が存在するのだ。

根本的にWebKitはカジュアルでライトなウェブブラウジングに的を絞っていると考えて良い。 つまり、ちょっとした調べ物などであり、完全に表示できることよりも軽快であることを望むためのものであり、実際Safariは機能的にかなり限定的である。 最小限の機能を搭載することからも「ちょっとした行為」であり、目を尖らせてに向き合うものではないと考えているのがわかる。

実際、Microsoft Edgeもそのような考え方であり、機能的に貧弱だったInternet Explorerと比べても機能はさらに削られており、メニューは実に寂しい。

生殺与奪を握られたウェブエンジン

だが、この問題はなかなか複雑だ。

AppleとGoogleはこれらウェブエンジンを「自由に使わせなければならない」呪縛にとらわれている。

これはもともと、Appleが既にある程度成熟していたウェブエンジンに参入するにあたり、スクラッチから開発することをよしとせず、KHTML/KJS Softwareをベースにすることを選択したことある。 当時のKHTMLはLGPLだったので、ここから派生するためにはLGPLを継承せざるをえなかった。 もちろん、そのWebKitから派生したBlinkにしても同様にLGPLを継承するしかない。これにより望む望まざるに関わらず「開発したものは配布するためには公開し自由を保証しなければならない」ということになったのだ。

そのため均衡が保たれているのだが、実際技術標準というか、「ウェブの世界」そのものは主にはGoogle、そしてAppleの意向は完全に反映されるのであり、 世界をウェブが支配している以上世界の支配者として君臨する状況を許してしまっている。

これは、GoogleやAppleが「採用する」と決めた方針に反する方法がないという問題だ。 LGPLである以上、ウェブブラウザを「秘密」にしてしまったり、伏せられた邪悪な機能を搭載するのは難しい。 だが、正式にプライバシーを侵害するような機能、仕様を採用すると決定したとして(既にそのようなものがなくもないが)、それを覆すことができない。 もちろん、フォークするという選択肢はあるが、ウェブブラウザの規模と進歩を考えるとGoogle並の開発力で追従するということは現実的ではない。 結局のところ誰もがGoogle, Mozilla, Apple, Microsoftの少なくともいずれかには従わなければならないという状況にあるのだ。

実際、Chromeユーザーでも「Google及びChromeに対する信頼度」はかなり低いというアンケート結果がある。

唯一の良心といえるのがMozillaだが、Mozillaの体制や行動がプライバシー上の理由で批判されたことは一度や二度ではない。

結果的に我々の生殺与奪は彼らに握られているというのが現実であり、 そして現状においてはそれを覆すほどのリソースはどこにもないのだ。 これはOperaのような老舗ブラウザ屋がBlinkを採用し、WebKitの祖先であるKDE ProjectがQtWebEngineを採用していることからも明らかだろう。

セキュリティとプライバシー

もうひとつの問題はウェブを侵食するプライバシーの問題だ。

セキュリティについて昔と比べることは意味がない。

昔はセキュリティを脅かすものは見るからに怪しげな「悪人」であり、「怪しげなものを遠ざければ良い」というものだった。 また、そのようなセキュリティ上の問題は「プライバシーの問題」ではなかったのだ。

ところが現代においてはサービスを利用する上でプライバシーを侵害されることは前提であり、 それどころかウェブブラウザを利用することによってもプライバシーが侵害されるという状況にある。

なぜこのような状況になるのか。

まず、Facebookを筆頭にして、現在はプライバシーが主要な商品となりすぎていて、 どこもかしこもプライバシーを欲しがるという現状がある。 これはウェブの技術如何に依らず根本的な問題である。 「プライバシーを侵害したいと思う人がいなければプライバシーを侵害するものが作られることはない」のだから。

だが、彼らウェブブラウザデベロッパー達はこれらの「プライバシーを侵害したい」という要求に応えているし、 また彼ら自身がプライバシーの侵害を望んでもいる。GoogleやAppleが過剰かつ積極的に個人情報を収集している状況を忘れてはいけない。

我々は日々普通に使っているだけでも著しくプライバシーを侵害され続けている状況にあるのだ。

プライバシーを取り返せ

中には完全にプライバシーを保護するブラウザもなくはないが、代償は大きい。 そうではなく、普通は求めているのは「与えて良いと判断して選択し許可した以外のプライバシー侵害は認めない」というものだろう。

だが、これはなかなか難しい。 ウェブブラウザの根本的な機能として既にプライバシー侵害を可能にする機能が組み込まれているからだ。 例えばComodo Dragonはプライバシーを重視しているということだが(PrivDog事件もあったが)、トラッキングなどは普通に行えてしまう。

また、Vivaldiもプライバシーを重視しているが、それでも問題になっているプライバシーはあまり解決しない。 裏側で悪意ある行動をとっていないというだけで、知られないうちに何かが行われることを防いでくれるわけではないからだ。

ある程度、そして確実に効果がある方法は「セッションを残さない」ことだ。 トラッキングが最も驚異なのは、「アクティビティが同定でき」「蓄積されるから」だ。 蓄積されなければ同一人物なのか否かを特定することは困難になるし、取得可能な状況はある程度限定的になる。

Chromiumならば次のようにすれば最初からプライベートモードになるし、使うたびに情報はクリアされ、ある程度安心だ。

chromium --incognito

もちろん、これでTwitterとGoogleとFacebookに同時にログインしたら台無しである。 そんなことはせず、他のサービスを利用するときは必ずブラウザを閉じて行うべきだ。 これは、ログインしなくても、サイトごとにセッションを切るべきである

Vivaldiも--incognitoオプションを受け付けるため、Vivaldiを使えばさらに(いくらか)安全になる。 Linuxにおいてはデフォルトブラウザとして--incognitoつきでVivaldiを起動するようにしてしまうといい。 以下は私が使用している$HOME/.local/share/applications/incogvivaldi.desktopファイルだ。

[Desktop Entry]
Version=1.0
Name=Vivaldi Incognito
# Only KDE 4 seems to use GenericName, so we reuse the KDE strings.
GenericName=Web Browser (with Incongnito)
GenericName[ja]=ウェブブラウザ (with Incongnito)
Comment=Access the Internet (with safety)
Comment[ja]=インターネットに安全にアクセス
Exec=/usr/bin/vivaldi-stable --incognito %U
Terminal=false
Icon=vivaldi
Type=Application
Categories=Network;WebBrowser;
MimeType=text/html;text/xml;application/xhtml_xml;image/webp;x-scheme-handler/http;x-scheme-handler/https;x-scheme-handler/ftp;
Actions=new-private-window;

[Desktop Action new-private-window]
Name=New Incognito Window
Name[ja]=新しいシークレット ウィンドウ
Exec=/usr/bin/vivaldi-stable --incognito

デフォルトの.desktopファイルとの違いは以下の通り

  • 起動時に--incognitoオプションを付加
  • 日本語と英語のみにし、翻訳を排除(どうせ読めないし、ローカル設定だし)
  • new-windowアクションを除去し、デスクトップの右クリックメニューもIncognito限定に

これで開くアプリケーションの選択肢としてVivaldi Incongnitoが追加される。

プライバシーを強化する設定やプラグインを追加すればより万全だろう。

いちいちセッションを切るのは面倒だ、というまっとうな意見のために、私はMy Browser Chooserというソフトウェアを開発している。 サービスごとにプロファイルをわけてしまえば、サービス側がプロファイルを横断しての情報収集はできない。 運用ポリシーさえ守ればだいぶ楽になる。Zshスクリプトなので、Unix的な環境においてのみ機能する。

危ういスマートフォンブラウザのプライバシー

パソコンなら(特にLinuxならば)このようにいくらかの解決策を提示できる。 だが、本当に厄介なのはスマートフォンだ。

スマートフォンの場合ブラウザが握ることができる情報はパソコンの比ではない。 だが、にもかかわらずユーザーに与えられるコントロールは極めて少ない。

スマートフォン的な設計思想として、「可能な限りコントロール部品を減らす」というものがある。 例えばMicrosoft Edgeはメニューボタンがハンバーガーメニューになっておりドロワー式だ。これはタッチデバイス向けの設計であり、GNOME3もまた同様の設計になっている。 ドロワーに含まれるメニューも少なく、ここまでメニューを減らしているのは当然に相応に機能自体を削っているということである。 機能が多ければそれだけなんらかの煩雑さを持たざるをえないからだ。

このような設計にしている理由はふたつある。

ひとつはスマートフォンの狭い画面での煩雑性が操作性の低下に直結するためだ。 そして、もうひとつはユーザーが馬鹿でいられるようにである。

私はこのような考え方は好きではない。 なぜパソコン用のソフトウェアが低機能なスマートフォン設計を採用せねばならないのか?

このコントロールの少なさがプライバシーの問題にも直結している。 現在では必須ともいえるプライベートモードがひどく軽視されているし、ページを開く時点でプライベートモードで開く選択肢を与えられない。 スマートフォンはパソコンよりもアプリからリンクを開く機会が多いにもかかわらずだ。

もちろん、プロファイルの切り替えもサポートされていない。 結果的にスマートフォンに蓄積された情報が簡単に抜き取られてしまう状況である。 なお、ブラウザを使い分けたところでAndroidならば多くのブラウザはAndroidのWebEngineを使用しているため、中身はひとつ、結局は使い分けに意味などない。 iOSについてはあまり知らないが、おそらくは似たようなものだろう。自前でのウェブエンジン実装はあまりに困難だからだ。

ひどい話だと思う。 だがAndroid WebEngineとはつまるところBlinkであり、なかなか太刀打ちできるものではない。

使い分けるのであれば、Mozilla FirefoxとMicrosoft Edge、そしてBlink系ブラウザ(Chrome, Operaなどだ)を使い分けるという方法だ。 数が少ないのであまりリスク軽減はできないが、いくらかマシな方法だろう。

元は表現力も機能も安定性もひどいものだったAndroid Firefoxだが、現在はかなり改善されており、十分実用になる。 Quantam(Firefox 57以降)をもてはやす記事ほど素晴らしいものだとは思わないが、「Firefoxが実用になる」しいうのは極めて喜ばしいことだ。

それよりも推奨できるのはFirefox Focusを使用することだ。 Firefox Focusは常時プライベートモードのFirefoxである。firefox --private-window相当ということではなく、プライバシー保護のためにかなりenhanceされている。 ほとんどのブラウジングにおいてはFirefoxで困ることはなく、またアクティビティを保存する必要もない。 そして、Mozillaによって提供されるFirefox FocusはCM Security Browseなどを使うよりもよっぽど信用できると言える。

そう、ブラウザは重大なプライバシー資源となっているために「信用できるか」が極めて重要なのだ。 ましてスマートフォンブラウザならなおさらで、プライバシーの塊であるスマートフォンを狙ったアプリなど数知れずある。 言ってしまえば大部分は怪しいし、よほど信用できない限りは使えないのが現実である。 ブラウザはその基本的な動作においてほぼ全権を要求するという点も大きい。正常に動作させるために権限を与える必要があるためにリスクが高いソフトウェアなのだ。

言い換えれば、悪意ある人からすれば「これはブラウザです」というふうに言っておけば

スマートフォンブラウザの機能にも不満

典型的なのはOperaで、Operaは以前はかなり高機能なブラウザであった。 PCブラウジングやページ保存、印刷機能も備えていたのだ。

ところがそれらの機能は現在はもうない。スマートフォンには必要ないと判断されたのだろう。 だが、実際はOperaはもはや必要な機能が削除されてしまって使い物にならない。 このような「スマートフォンだから」という言い訳は、一体スマートフォンだからなんだというのかと思うほどに理由になっていない。

表示の難しいinspectionなどはまだしも、印刷、ページの保存、文字エンコーディングの選択、プラグイン、ページ検索、 ローカルファイルの取り扱いなどがスマートフォンにおいて欠けている必要があるのだろうか? それ以外にもウェブブラウザが様々に豊富な機能を備えているにもかかわらず、スマートフォン版ではない、あるいはコントロールする手段が与えられていない。 なぜスマートフォンではユーザーからコントロールを剥奪するのか。理解し難い。

Vivaldiのようなブラウザをスマートフォンに最適化させたようなものは出ないのだろうか。 一応、Vivaldiのスマートフォンバージョンの開発は行われていはいるようだが。

ハイアクセシブルウェブサイトとウェブデザイン (フォント関連)

font-familyの指定

font-familyにシステムデフォルトのフォントを並べているのを非常によく見る。 例えばYahoo! Japan

MSN Japan

Google (google.co.jp)

まず先に言うならば、Arialは欧文書体であり、フォント解釈を厳密に行う環境の場合、Arialが指定されることで文字化けが発生する。 ここではArialがないというケースを除外して次のように処理される。

  • Arialにグリフがないフォントを置き換える
  • Arialにグリフがないためフォント自体を置き換える
  • font-family自体を解釈せず既定のフォントで表示する
  • Arialのみで表示しようとする (グリフのない文字は文字化ける)

このうち最も困るのは最後のケースだが(Arialを指定したがために文字化ける)、実際は欧文をArialで表示し、和文を他のフォントで表示して「美しい」と感じるのは難しい1。それよりは全部和文書体の方がバランス的にもマシである可能性のほうが高いのだ。そのため、最初のケースも明らかに意図的でない限りはまずい動作をする。2

昔、ごく限られた条件下では、フォントが指定されないことに起因して文字化けるということがあった。 これは、ブラウザがデフォルトフォントを欧文フォントに決め打ちしており、フォントレンダラーによる文字選択は可能なので、指定しなければフォントレンダラーに選択を任せないために文字化けるという状況であった。

しかし、現状で考えると

  • 日本語版のWindows/Macであれば和文フォントがあり、メジャーなブラウザは和文フォントを選択する
  • 非日本語版でデフォルトの和文フォントがないのであれば、指定したところでそのフォントはないので使えない
  • 日本語環境を無視しているようなブラウザをわざわざ使用するユーザーは文字化けすれば設定すると思われる

ということから、システムデフォルトのフォントを指定することは 現実的に意味がない

強いて言えば、ヒラギノを優先して指定することで、Windowsユーザーでもモリサワ書体を購入しているユーザーにはヒラギノを優先して使わせる、ということも可能だが、それは「ヒラギノが他のどんな書体よりも美しい」というMaccerの思い込みに過ぎない。3

Mimir Yokohamaでは次のように指定している。4

通常フォントをサンセリフ体、本文フォントはセリフ体を指定している。 セリフ体のSource Han Serif JPを除くと全て商用フォントである。 これは、もしユーザーが商用フォントを持っているのであればそれを使ってもらおうということである。 本来のfont-familyの使い方であると言える。

適当に選択しているわけではなく、本文フォントとなるセリフ体には流れるようなグリフで、十分に収録グリフがあり、長文を読むのに疲れないフォントであることを選択基準にしている。 サンセリフ体についてはいわゆる「丸ゴシック」である。「丸ゴシック」という抽象フォントがないため、イメージとしてより好ましいフォントを並べる、という方式をとっている。もしあるのであればsans-serifではなくrounded-sanscjk-rounded-gothicのような指定がしたいところである。

font-family指定がシステムデフォルトフォントを指定するというのは、「システムデフォルトフォントが嫌いで、わざわざ商用フォントを入れて設定しているユーザーに対してシステムデフォルトフォントを強制する」ということでもある。 これはユーザービリティを高めているのではなく損ねている。 そんなことに貴重な数十バイトを使用するべきではないのではないか。

webfontについて

webfontはCSS 3.0で追加された、ウェブリソース上に配置されたフォントをダウンロードして使用するものである。 font-familyがシステムフォントしか指定できないために、結局システムデフォルトフォントばかり指定するという無意味に近い状況がこれによって改善された。

なんでこんなものが追加されたかというと、従来デザイン上特定のフォントを使用したい場合にどうしても「画像を使う」という状況であったが、W3Cとしては常に文字情報であればテキストとCSSを使うべしという思想である。 これを実現するためにCSSにテキストを重ねたり傾けたりして配置する機能と共に、特定のフォントを使わせる方法を提供したものである。

「デザイナーの意図するフォントを指定できるようにした」と解釈される場合も多いが、実のところそれは副次的なものであり、W3Cがこの仕様を制定した意図としては「テキスト画像を撲滅したい」ということであり、既にテキストとして表示されている情報がどのように表示されようが(W3Cにとっては)どうでも良いのだ。

だが、実際のところW3Cの意図に反して画像撲滅よりは従来からテキストで表示されていた部分のデザイナーの意図反映に使用されている5

このために「確実に好きなフォントが使える」程度の理由でwebfontが指定される傾向があるのだが、それはデザイナーの完全なエゴである。

欧文フォントであれば(W3Cが考えたように)ロゴ画像をダウンロードするのとさして差のないデータ量であり、それほど躊躇われるものではないのだが、和文フォントであると常用漢字のみ収録のものですら2MB程度ある。画像などよりもはるかに大きく、通信料金で考えると2円ほどかかる。 これを表示ごとに要求することになるのだ。6

しかも、そもそもフォントレンダラーは一種のバイトコードインタープリタであり、潜在的にはフォントファイルはウィルスとして機能しうる7

また、そうでなくてもテキストと異なるグリフを持つフォントをダウンロードさせることにより、コンピュータ(例えばGoogleやSymantecのチェック)を欺いてユーザーに嘘の情報を見せるサイトを作ることもできる。 これは詐欺などにおいて有効な手法である。

にも関わらず、現状主だったモバイルウェブブラウザにおいてはwebfontを無効化する機能というのはない。 私も仕事にしていたこともあるくらいだからフォントは大好きなのだが、それでも自分の望むデザインのためならユーザーに負担を強いたり、不快な思いをさせても構わないというデザイナーのエゴには賛同できない。

長文のための工夫

私が書くコンテンツは(この文章を含めて)往々にして長文である。

これは、意味ある情報を提供してこそ意味があるという思想に基づくもので、内容空疎なイメージ戦略や思考停止で選ばれるものを提供しようとは思っていないから…なのだが、長文を含めて読むのが得意な人は比較的少数だし、やはり長文を読むのはどうしても負担になりやすい。

そこで、可能な限り長文を読みやすいように心がけている。

本文に対する設定としては

  • フォントは細めで、流れるような(連続した文字を少ないコストで読める)明朝体
  • 自動カーニングで行間を詰める(長文の流し読みが楽になる)
  • これだけでは「文字量が多く黒い」と感じてしまうため、少しだけ文字間を空ける
  • 黒さを軽減するため、行間を少し空ける
  • 結果として段落間の行間が詰まってみえるため、段落間に空白をもたせる

このほか、リズム感のある句読点や、かぎかっこの使い方、言い回しの選択などの技法と組み合わせてできる限り「楽しく読める長文」を心がけている。

ウェブサイトは内容であって見てくれではない、ということを忘れてはいけない。


  1. この指定は「欧文のみの要素に対して欧文フォントを適用する」指定ではない。和文フォントが適用される条件でもArialのグリフがあるものはArialが使われるのだ。

  2. もちろん、欧文書体を和文書体に含まれていない特定のフォントを選択してほしいという理由で意図的にこのようなことをすることもできるが、その場合は和文書体よりも先に欧文書体を書くべきで、MS PGothicよりもArialが後、というのはArialの欧文よりMS PGothicの欧文のほうが好ましく、けれど和文書体にある欧文ではなくArialを使ってほしい、というよくわからない美的センスである。

  3. モリサワ書体を含め、商用フォントを選択肢とするのであれば、無条件に「ヒラギノが最高」ではなく、数多のフォントからそのサイト、その文章に適切なフォントを選択できるはずだ。

  4. Chienomiでは既存のWordPressテーマを使用しているため、私は関知していない。

  5. これは当然の帰結と言える。デザインされた画像を作成するのと比べて、CSSでそれを実現するのは難しいし、同じフォントであってもレンダリングで見た目に差が出ることから望むほどデザインを再現してもらえない。記述量も多く、しんどい。

  6. キャッシュがあるから、というのは傲慢である。特にモバイル環境ではキャッシュを取り直す状況は非常に多く、セッションごとに取得すると考えて間違いない。

  7. このために以前のWindowsではフォントインストールにセキュリティリスクがあるという警告が出されていた。