DBIx::Handler と mysql_enable_utf8 と utf8mb42013年06月10日 21時52分

Perl で MySQL サーバーに接続するとき、DBIx::Handler を使っています。

my $handler = DBIx::Handler->new(
    $dsn, $username, $password,
    {
        RaiseError        => 1,
        RootClass         => 'My::DBI',
        mysql_enable_utf8 => 1,
    },
);

しかし、これだと "𠮷" (U+20BB7、いわゆる「つちよし」) など BMP 外の文字を保存しようとしたとき、テーブルの文字コードが utf8mb4 であっても "????" (疑問符 4 文字) に化けてしまいます。

ググったところ、mysql_enable_utf8 => 1 を指定した時点で DBD::mysql が接続時の文字コードを utf8 にしてしまうのが問題だそうです。(cf. おそらくはそれさえも平凡な日々: DBD::mysqlでmysql_enable_utf8しつつutf8mb4使いたいとき)

接続時に文字コードを utf8mb4 に指定しなおせばいいとのことですが、DBIx::Handler ではどうするのだろうとソースコードを眺めていたところ、new メソッドの第 5 引数の存在を知りました。

my $handler = DBIx::Handler->new(
    $dsn, $username, $password,
    {
        RaiseError        => 1,
        RootClass         => 'My::DBI',
        mysql_enable_utf8 => 1,
    },
    {
        on_connect_do     => ['SET NAMES utf8mb4'],
    },
);

これで BMP 外の文字も文字化けせず保存・取得できるようになったのですが、この第 5 引数は (DBIx::Handler 0.07 時点では) 文書化されていないので、使っていいものか心配です。

Callbacks を使っても期待通り動作するようなので、そちらのほうがよかったりするのでしょうか。

my $handler = DBIx::Handler->new(
    $dsn, $username, $password,
    {
        RaiseError        => 1,
        RootClass         => 'My::DBI',
        mysql_enable_utf8 => 1,
        Callbacks         => {
            connected => sub {
                $_[0]->do('SET NAMES utf8mb4');
                return;
            },
        },
    },
);

(SET NAMES をつかってはいけないという話もありますが、追いきれていません。)

長い英単語を途中で折り返したいときの CSS の指定方法2013年06月18日 09時32分

ヨーロッパ系の言語では基本的に単語の途中に折り返しを入れません。しかし、幅の狭い領域に長い英単語を記述するときや、笑いを表す「www…www」のように欧文文字を表現に組み込むときなど、欧文文字同士の合間で折り返したいと思うかもしれません。そのような折り返しの制御を CSS で行うにはどうすればよいのでしょうか。

  1. 2013 年 6 月時点の結論
  2. word-wrap: break-word を使うとどうなるのか
  3. word-break: break-all を使うとどうなるのか
  4. word-wrap: break-wordword-break: break-all を両方使うとどうなるのか
  5. なぜ word-break: break-all ではなく word-wrap: break-word を勧めるのか
  6. どこに word-wrap: break-word を指定するのか
  7. position: aboslutedisplay: inline-block との組み合わせ
  8. display: table-cell との組み合わせ
  9. display: flex との組み合わせ
  10. white-space: prewhite-space: nowrap との組み合わせ
  11. word-wrap というプロパティ名
  12. 参考文献

2013 年 6 月時点の結論

  • word-wrap: break-word を使ってください。
  • word-break: break-all使わないでください
  • display: table-celldisplay: inline-block と組み合わせるときは、明示的な最大幅の指定が必要かもしれません。

word-wrap: break-word を使うとどうなるのか

単語 (欧文文字の連続) の途中で折り返さないと行ボックスの幅からあふれてしまうときのみ、その単語の途中で折り返します。単語の途中で折り返しが発生するのは、その単語の幅が行ボックスの幅より大きいときのみです。単語の幅が行ボックスの幅より小さければ、その単語全体が次の行に送られることはあっても、その単語の途中で折り返されることはありません。

[word-wrap: break-word を指定した図] word-wrap: break-word を指定した例。単語 supercalifragilisticexpialidocious の途中で折り返される。

word-break: break-all を使うとどうなるのか

任意の文字間での折り返しが許容され、言語に関わらず一切の禁則処理が無効になります。英単語の途中で行ボックスの幅からあふれそうになれば、あふれる直前で折り返されます。開き括弧の直後や閉じ括弧の直前、句読点の直前でも折り返されます。

[word-break: break-all を指定した図] word-break: break-all を指定した例。単語 such 及び supercalifragilisticexpialidocious の途中で折り返される。

word-wrap: break-wordword-break: break-all を両方使うとどうなるのか

word-break: break-all を使った時点でどの文字の間でも折り返しが起こりうるので、word-wrap: break-word による折り返し発生条件に到達しません。つまり、両方指定するのは word-break: break-all だけの指定と実質変わりません。

なぜ word-break: break-all ではなく word-wrap: break-word を勧めるのか

禁則処理をできるだけ活用し、また単語途中での折り返しを可能な限り避けるためです。ヨーロッパ系の言語で単語途中での折り返しがあると、ひとつの単語がふたつの単語のように見えてしまいます。日本語でも句読点が行頭に来ていれば違和感を持つ人が多いでしょう。

どこに word-wrap: break-word を指定するのか

word-wrap プロパティも word-break プロパティもその値は継承します。言い換えれば、その効果は子孫要素にも及びます。ですから、これらのプロパティは (HTML 文書であれば) body 要素か html 要素に指定しておけば十分でしょう。

position: aboslutedisplay: inline-block との組み合わせ

ときとして長い単語が折り返されず、word-wrap: break-word が効いていないように思えるかもしれません。その単語の祖先要素に position: absolutefloat: leftfloat: rightdisplay: inline-block が指定されていると、このような状況が起こりえます。

これらのプロパティ・値を指定された要素 (で width プロパティの計算値が auto のとき) は、その内容の幅によって自身の幅を決めるのであり、自身の幅を先に決めて内容の幅をそれに合わせるのではありません。このとき、その要素の幅は shrink-to-fit width (内容に合うように縮んだ幅) であるといいます。shrink-to-fit な幅を算出するときに word-wrap プロパティの影響は考慮されないので、単語途中での折り返しは発生しません。

(「考慮されない」といいましたが、内容の幅を決めようというときには行ボックスの幅も定まっていないので、「折り返さないと行ボックスの幅からあふれてしまう」という判断ができません。「word-wrap: break-word の影響を考慮しようにも考慮できない」というのが実際のところでしょう。)

こうした要素に対しては、width プロパティに auto 以外の値を指定することで shrink-to-fit な幅ではなくなります。あるいは、max-width プロパティを指定するのもひとつの手です。いずれにしても行ボックスの最大の幅が定まり、単語の長さがその幅からあふれるときは、その単語の途中で折り返されるようになります。

display: table-cell との組み合わせ

display: table-cell を指定した要素 (セル) も shrink-to-fit な幅の要素と同様、内容の幅によって自身の幅が決まります。しかし、こちらは width プロパティを指定しても単語の途中での折り返されるようになるとは限りません。CSS 2.1 の自動テーブルレイアウトアルゴリズムにおいて、セルの width プロパティの値が最小セル幅として扱われるからです。

(ここで「CSS 2.1 の自動テーブルレイアウトアルゴリズム」と呼んでいるのは CSS 2.1 仕様で定義されているものですが、ブラウザにそのアルゴリズムの利用が強制されているわけではありません。実際に用いられるテーブルレイアウトアルゴリズムはブラウザごとに異なりえます。)

セル内でも word-wrap: break-word による単語途中での折り返しを実現させるためには、セルに max-width プロパティを指定するか (ただし、この方法は IE 8 で機能しないことがあるようです)、セルの子孫要素 (であり、かつ単語の祖先要素でもあるもの) に width プロパティを指定して幅を明示する必要があります。

display: flex との組み合わせ

flexbox レイアウトとの組み合わせでも折り返しが効かないように思えることがあるそうです。

white-space: prewhite-space: nowrap との組み合わせ

IE 8 以降では、word-wrap: break-wordwhite-space: pre の効果が重なると、なぜか (まるで word-break: break-all を指定したかのように) 任意の文字間で折り返しが発生しうるようです。どこでも折り返されるようになるのは white-space: nowrap と組み合わせたときも同じで、こちらは nowrap といっているのに折り返しが発生することになります。(white-space: pre-wrapwhite-space: pre-line のときは問題ありません。)

white-space: nowrap の使い方として、text-overflow: ellipsis を組み合わせ、行ボックスからはみ出した文字列を省略表示にすることがあります。

h1 {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

しかし、IE 8 以降 (少なくとも IE 10 まで) では、祖先要素に word-wrap: break-word が指定されているとこの省略表示が効かず、内容がひとつの行ボックスに収まりきらなければ複数行で表示されます。white-space プロパティに値 pre または nowrap を指定するときは、word-wrap プロパティの値を初期値に戻しておいたほうがいいでしょう。

h1 {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  word-wrap: normal;
}

word-wrap というプロパティ名

この記事の執筆時点の CSS3 Text 草案では、word-wrap プロパティは overflow-wrap プロパティに改称しています。とはいうものの、互換性のため word-wrap というプロパティ名でも受け付けることになっています。現状のブラウザ実装状況を鑑みるに、word-wrap という名前を使うか、word-wrapoverflow-wrap を併記するかがよいと思います。

参考文献