雑記帳

このページは下の方が新しい項目です。

メモ

この記事へのリンク

このサイトでは、CSSはブラウザ別に何種類か用意してあって、ブラウザの判定をCGIでしています。今まではCGIから直接CSSを出力していたのですが、CSSファイルにリダイレクトしてやった方がいいのではないだろうかと思ったのでスクリプトを書きかえました。

また、トップページもCGIで出力してるのですが、プロキシを使った場合、Firefoxでアクセスした直後、同じプロキシ経由でIEでアクセスしようとするとダウンロードになってしまう(IEはapplication/xhtml+xmlなファイルを扱えないので)ことに気がつきました。どうやら強制リロードができないのでお手上げですね、これは。ということで応答ヘッダに "Cache-Control:private" のおまじないを入れときました。

両方とも結局キャッシュ対策で、ある環境用のファイルが別の環境に行かないようにってことなんですが、もっと早いうちにやっとくべきだった気がします。

スクリプト書き換え

この記事へのリンク

動作しなくなったperlスクリプトをPHPで書き換えようプロジェクト、(だいぶ前に)完了。perlで動かなくなったところっていうのは、よそのサーバからHTML文書を取得する部分です。

PHPはローカルファイルもリモートファイル(よそのサーバにあるファイル)も同じ手順で読めるのが便利ですね。ただ、ちょっとつっこんだことをしようとすると、やっぱり面倒なことになるわけで。たとえば、リダイレクトされたかどうか確認したり、リクエストの際Acceptヘッダをつけるとか。最初は Acceptヘッダをつける方法がわからなくて、できないもんだと思ってました。

できないと思ってたので、自前で処理してるサンプルを見つけて利用してました。リダイレクトに対応してなかったので、その処理を追加しました。こんな感じ。

/* ###################################
    $url     : http://から始まるURL( http://user:pass@host:port/path?query )
    $method  : GET, POST, HEADのいずれか(デフォルトはGET)
    $headers : 任意の追加ヘッダ
    $post    : POSTの時に送信するデータを格納した配列("変数名"=>"値")
*/
function http($url, $method="GET", $headers="", $post=array(""))
{
	global $ua;	/* ユーザーエジェント名を設定しておく */
	global $DATA;	/* 得られるデータが入る */
	global $redirURL;	/* リダイレクト先URLが入る */
	global $redirCount;	/* リダイレクト回数カウント */
	global $redirLimit;	/* リダイレクト回数の上限を設定しておく */
	global $ResCode;	/* http応答コードが入る */
	global $ResHeaders;	/* http応答ヘッダが入る */

    /* URLを分解 */
    $URL = parse_url($url);

    /* クエリー */
    if (isset($URL['query'])) {
        $URL['query'] = "?".$URL['query'];
    } else {
        $URL['query'] = "";
    }

    /* デフォルトのポートは80 */
    if (!isset($URL['port'])) $URL['port'] = 80;

    /* リクエストライン */
    $request  = $method." ".$URL['path'].$URL['query']." HTTP/1.0¥r¥n";

    /* リクエストヘッダ */
    $request .= "Host: ".$URL['host']."¥r¥n";
    $request .= "User-Agent: $ua PHP/".phpversion()."¥r¥n";

    /* Basic認証用のヘッダ */
    if (isset($URL['user']) && isset($URL['pass'])) {
        $request .= "Authorization: Basic ".base64_encode($URL['user'].":".$URL['pass'])."¥r¥n";
    }

    /* 追加ヘッダ */
    $request .= $headers;

    /* POSTの時はヘッダを追加して末尾にURLエンコードしたデータを添付 */
    if (strtoupper($method) == "POST") {
        while (list($name, $value) = each($post)) {
            $POST[] = $name."=".urlencode($value);
        }
        $postdata = implode("&", $POST);
        $request .= "Content-Type: application/x-www-form-urlencoded¥r¥n";
        $request .= "Content-Length: ".strlen($postdata)."¥r¥n";
        $request .= "¥r¥n";
        $request .= $postdata;
    } else {
        $request .= "¥r¥n";
    }

    /* WEBサーバへ接続 */
    $fp = fsockopen($URL['host'], $URL['port']);

    /* 接続に失敗した時の処理 */
    if (!$fp) {
        error_mess("$url を取得できませんでした。");
            /* error_mess() : 別途定義したエラーメッセージ表示用関数。(表示してそこで終了) */
    }

    /* 要求データ送信 */
    fputs($fp, $request);

    /* 応答データ受信 */
    $response = "";
    while (!feof($fp)) {
        $response .= fgets($fp, 4096);
    }

    /* 接続を終了 */
    fclose($fp);

    /* ヘッダ部分とボディ部分を分離 */
    $DATA = split("¥r¥n¥r¥n", $response, 2);

    $ResHeaders = split("¥r¥n", $DATA[0]);
    foreach( $ResHeaders as $res ) {
        if ( preg_match('/HTTP¥/1¥.¥d¥s* (.*)/',$res,$matches) ) {
            $ResCode = $matches[1]; break;
        }
    }
        /* リダイレクト処理 */
    if ( ( (int)$ResCode == 301 )||( (int)$ResCode == 302 )||( (int)$ResCode == 307 ) ) {
        foreach( $ResHeaders as $res ) {
            if ( preg_match('/(Location:¥s*)(.*)/',$res,$matches) ) {
                $redirURL=$matches[2];
                $redirCount++;
                if( $redirCount > $redirLimit ){
                    error_mess("リダイレクト回数が $redirLimit 回に達しました。中断します。" );
                } else { http( $redirURL,$method,$headers,$post ); return;}
            }
        }
    }

    /* リクエストヘッダをコメントアウトして出力 */
    /* echo "<!--¥n".$request."¥n-->¥n"; */

    /* レスポンスヘッダをコメントアウトして出力 */
    /* echo "<!--¥n".$DATA[0]."¥n-->¥n"; */

    /* メッセージボディを出力 */
    /* echo $DATA[1]; */
}

強調部分(たぶん赤字で表示されてるはず)が書き変えた部分です。結果をグローバル変数で返す仕様にしました。

今は Acceptヘッダを指定する方法とかわかったので、普通に fopen()を使ってます。

引っ越し4

この記事へのリンク

ktplanでのアカウント、というかサーバに置いていたファイル、4月2日の16時前くらいに削除された模様。

3ヵ月程度では移転したことがなかなか行き渡らないんだけど、まあしかたないかな。

明瞭

この記事へのリンク

各地でなかなか評判のよいフォント「メイリオ」、配布しているサイトがあったので、もらってきて使ってみました。拡張子がttcでXPではOpenType Fontとして認識されるので、TrueTypeアウトライン データを持っているOpenTypeフォントらしいTrueTypeフォントです。 XP上ではサイコーです。クリアタイプの使えないMEや98では「なんじゃこりゃあ」でしたが(ビットマップは含んでないらしい)。

デザインは、直線的で線のカーブが大きい、「懐が広い」(内側の白い部分が大きい)フォントです。 さらに全体的に大きめで(特にかなが)、同じような特徴を持つSH G30の上を行ってる感じです。また、少々平体(横長)にみえます(実際同じ大きさのほかのフォントより横幅があります)。そしてヒラギノと同じように適度に行間が空くようです (てなことはよそで述べられ済みだと思いますが)。

それに加えて大きな特徴が、太字(bold体)がペアになってることだと思います。Windowsの標準日本語フォントでは初めてですね。Boldを含んでいないフォントでの太字表示は、横や縦にずらして重ねることで字を太らせてるだけなので(おそらく)、専用にデザインされたBold体の方がいいのは当然でしょう。

さて、話がちょっとそれますが、「Safariでボールド指定が効かない」という話をときどき目にします。これは「Safariではフォントによってはボールド指定が効かない」が正解です。では、どのフォントでボールド指定が効くのかというと、「Bold体が含まれているフォント」です。 標準の日本語フォントでは「Hiragino Kaku Gothic Pro(ヒラギノ角ゴ Pro)」と「Hiragino Mincho Pro(ヒラギノ明朝 Pro)」だけとなります。斜体でも同様に「Safariでは斜体(ItalicあるいはOblique)が含まれているフォントであれば斜体指定が効く」のです。ただ、日本語フォントで斜体を持つものはないので(標準日本語フォントや出回ってる日本語フォントでは)、Safariでは日本語の斜体は不可能と言えます。

さて、またメイリオの話に戻ります。メイリオ、メイリオボールドともにItalicを含んでいますが、英数字部分のみで、日本語部分は残念ながら斜体はありません。RegularとItalicではデザインが異なる欧文と違い、日本語では単純に傾けるだけなのでitalic体が含まれてないのでしょう。

ついでにCrearTypeなどのサブピクセルレンダリングについても考察してみます。斜めの線の拡大図 このように斜めに線を引く場合(水色で区切られているのが画面上の1ピクセル)を考えます。

斜めの線の拡大図・液晶の場合 液晶画面上で3つRGBと並んだ組が消灯していれば黒になります。

斜めの線の拡大図・サブピクセル利用 とにかく3つ並んでいれば順番は構わないはずなので、少しずつずらしてやります。これできれいな斜線になるはず、という期待を胸に抱いて、これがどんな色のピクセルになるか見てみましょう。

サブピクセル利用の結果? なんかかなり不安な結果となりました。実際の画面を拡大したものとはかなり違います。 実際のフォントの表示はこのような手法ではないようです。

Wikipediaによる手順 WikipediaにClearTypeの項目があり、その中に画像生成の手順が記されていました。その手順で作成すると確かにそれらしい画像ができます。しかし、いまいち納得できない所もあります。本来効果の必要ない縦の直線にまで処理が及ぶこととか。ただ、実際のフォント表示でも、縦線にも処理がかかっているのでこのような処理方法が使われているのでしょう。

処理比較(RGB配列液晶用) せっかくなので上で作った斜線の1/1画像を置いておきます(RGB配列液晶用)。左から①処理なし ②普通のアンチエリアシング処理 ③原色バリバリのサブピクセルレンダリング ④Wikipediaに記載の手法。③はとりあえず黒の線に見えるものの、ギザギザ感がありますね。

処理比較(BGR液晶用) BGR配列液晶用のも置いときます。

おまけ。Unicode 2603 SNOWMAN Unicode 2603 SNOWMAN (☃←これ) のいろいろ。ヒラギノがちょっとさびしいな。

この記事へのリンク

ということで夏の絵。 トップレスな夏の人

雨はもう飽きましたよ……

立秋

この記事へのリンク

夏らしくなったと思ったらもう立秋ですと。

ひも水着

慣れない水着でちょっと不安になってるねこみみさんの図。

なんかいろいろ歪んでますが、ごめんなさい。

前の夏の絵の空は、GIMPのフィルターで作りましたが、今回は写真です(淀川で撮影)。

答えてみる

この記事へのリンク

前回につづいて答えてみる。 今回のターゲットは「海外掲示板日本語サイトにMacから投稿の文字化けを回避するには?

なんか無茶な話だなー。「日本語サイト」といいつつ「lang="en"   charset=ISO-8859-1」なんてめちゃくちゃもいいとこ。これでは文字化けして当たり前、の話ではないか。

とりあえずの答え。

1. 「Macからの投稿文字化けを回避する方法を教えてください。」

Mac、Winという問題ではなく、使っているソフトウェア(この場合はWebブラウザ)の問題。Safari の代わりに Firefox を使えば大丈夫なはず。

「Winからのmessageは "&#20803;・・・"」というのは、フォーム(ページ)の文字コードと投稿文章の文字コードが違うので、ブラウザがsubmitの時に気を利かせて文字参照に変換してくれただけ。(Safariはこの「気遣い」をしてくれないようだ)

2. 「コードISO-8859-1なのに、WinからのPOSTは問題なく日本語表示されるのは何故ですか?」

上で書いたように、文字参照の形での投稿になっているため。文字参照はユニコードのコード番号で文字を指定するものだ。ページの文字コードでは表記できない文字を書き表したいときに重宝する。たとえば、日本語のページに少しハングルを書くとか。

だから ISO-8859-1 のページに日本語も表示できる。

3. 「Mac(safari)より数ヶ月前までは日本語POSTできたのに、今は文字化けする」

わからない。Mac OS X のアップデートと同時にSafariのバージョンも変わることが多いので、そのあたりで挙動が変わった可能性もある。

とにかく、英語しか想定してない掲示板に日本語を突っ込むのが根本的に間違っている、と思う。

Safariからの投稿

この記事へのリンク

答えてみるのつづき。

さて、Safariはこの「気遣い」をしてくれない、と書いたが、ちょっと違うようだ。 Safariの場合は、フォーム(ページ)の文字コードと投稿文章の文字コードが違う時(フォームの文字コードでは投稿文章の文字が表示できない時)、"?"(クエッションマーク)に変換してくれている。 てっきり、SafariはそのままShift_JISなどで送り出し、掲示板側でうまく処理できなかった結果、文字化けが起こるのかと思っていた。そうではなくて、掲示板スクリプトが受け取った時点ですでに"????"となっているわけだ。

ということでいくつかのブラウザをざくっと調べてみた。lang="en"、encoding="ISO-8859-1"のページのフォームに日本語を記入して送信したらどんなデータが送られたかの表である。

ブラウザ送信データの形式
Firefox(Minefield3.0a1) / Mac OS X 10.4.7数値参照
Safari 2.0.4(419.3) / Mac OS X 10.4.7"?"に変換
Konqueror 3.5.3 / Mac OS X 10.4.7 + X11数値参照
iCab3.0.3/ Mac OS X 10.4.7"?"に変換
IE5.2.3 / Mac OS X 10.4.7入力時に化けたとおり
Netscape4.7 / Mac OS 9.2入力時に化けたとおり
IE6 / Windows MEShift_JIS
Opera 8.01 / Windows ME数値参照

送信データは実際はさらにURLエンコードされて送られている。

直接Shift_JISを送りつけるのは調べた中ではWin IEだけか。まあ、Shift_JISにしろ数値参照にしろ、掲示板スクリプトの方で適切に処理できなければちゃんとした表示は期待できないわけで。Shift_JISの場合は文字コード変換はほぼ必須だろうし、数値参照の場合でも、&を&amp;と変換されてしまったら(HTMLでは & は特別な意味があるので & そのものを書くときは&amp;と書かなければならないため)アウトだし。

作成中

この記事へのリンク

残暑見舞い絵作成中。 残暑見舞い絵途中 思ったとおりの背景ができるかどうか……。

リダイレクト

この記事へのリンク

前の「スクリプト書き換え」で、なぜリダイレクトされたかどうか知りたいのか、書き忘れてました。

単にHTML文書が得られればいいのだったら関係ないのですが、そこからほかのファイルをたどる場合はリダイレクトされたかどうかしっかり確認する必要があります。

例えば http://www.example.jp/abc というURLにアクセスしてHTML文書が得られたとしましょう。その HTML に <img src="xyz.jpg" alt="hoge" /> というように画像が貼ってあった場合、この画像はどこにあるのかわかりますか。

HTML文書と同じ階層にあるのはわかりますね。ではそれはどこなのか。http://www.example.jp/ なのか http://www.example.jp/abc/ なのか、という話になります。要するに http://www.example.jp/abc が「ファイル」なのか「ディレクトリ」なのか、です。

ポイントは、どちらになるのかはURLからは判断できず、アクセスしない限りわからないことです。(もしディレクトリなら、http://www.example.jp/abc にアクセスすると http://www.example.jp/abc/ にリダイレクトされます。)

ということで、やりたいことによってはきちんと確認しないと痛い目を見るわけですが、世の中には確かめずに決め打ち(URL末尾に拡張子がなければディレクトリだと思い込んで勝手に / を補完とか)するサービスがちらほら……。

てなことを書いてますが、私も以前はそんなことをしてました。

ところで IE の履歴なんですが、末尾に"/"が明示されてるURLでもその末尾の"/"が削除されてしまうのはどうしてでしょうね。

http応答ヘッダを表示する

この記事へのリンク

どんな風にリダイレクトされるのかとかの観察に便利かも、ということでhttp応答ヘッダを表示するスクリプトです。

ユーザーエージェント

この記事へのリンク

うちのサイトにアクセスしてきたUAの集計をまた久々にしてみました。ちなみに前回は2005年10月です。期間は2006年12月1日から29日まで、対象はトップページへのアクセスで、下位ページからの移動を省いています。

No ユーザーエージェント 種別 割合
1MSIE 6 (WIN)B39.701%
2Firefox (Win)B16.080%
3MSIE 7 (Win)B8.185%
4WWWCA6.446%
5Yahoo! SlurpC3.287%
6livedoorCheckersA3.051%
7Hatena AntennaA2.988%
8PSP (PlayStation Portable)B2.408%
9(MSIE 6 互換)?1.521%
10SafariB1.521%
11ChieriA1.150%

種別のAはアンテナ、Bはブラウザ、Cはクローラー。

うちではMSIEの比率が低いですね。6.0はついに4割を切りました。代わって7.0が急上昇中。表には入ってませんが、OperaはWin用ver.9が一番多くて0.788%でした。

9位は"Mozilla/4.0 (compatible; MSIE 6.0;"を名乗るもので、MSIEではないものです。検索エンジンのクローラーであることが多いようです。

SafariよりPSPの方が多いってのがちょっとびっくり。(誤差の範囲内程度の差しかないですが)

ファイルの更新日を知る

この記事へのリンク

ブラウザに表示されているWEBページの更新日を知るにはどうしたらいいか。その話題が出ていた某所では Javascript を使う方法が示されていました。

javascript:alert(document.lastModified)

これをアドレスバーに打ち込めば更新日時が表示されます。ブックマークレットにもできる(新規ブックマークのURL欄に上記のJavascriptをそのまま入れて登録するだけ)ので便利かも。 ただし、当然Javascriptを有効にしておく必要があるし、ページによっては更新日時が現在時刻(ページを読み込んだ時刻)になることがあります。

さて、このJavascriptが何を見て更新日時を表示しているかと言えば、HTTP応答ヘッダの情報(Last-Modified フィールド)です(→http応答ヘッダを表示する)。 通常の場合、普通のHTML文書であれば、WEBサーバがファイルの更新日時を通知してくれます。 逆にWEBサーバがファイルの更新日時を返さない場合というのは、CGIで出力されたファイルとかSSIで処理された文書のときです。無料スペースで自動的に広告が入る場合もそうです。 サーバがファイルの更新日時を教えてくれなかった場合(HTTPヘッダにLast-Modified フィールドがない場合)、ブラウザはファイルを受け取った時刻(おそらくHTTPヘッダのdateフィールドの時刻)を更新日時と見なすようです。

そこでこのファイルの更新日時、信用できるものでしょうか。例えば、AというHTML文書とBというHTML文書を比べてBの方が新しい更新日時だったら、Aの方が先に公開された文書だと言えるでしょうか。—もちろんそんなことはありません。加筆修正などすれば最初に公開したときよりも新しいタイムスタンプになりますし、perlやPHPなどのスクリプトでタイムスタンプをいじることもできます。

タイムスタンプいじり見本 ということでタイムスタンプをいじってかなり昔の日付にしてみました。確認してみてください。

以下余談。スクリプトでタイムスタンプを変更したわけですが、実際には少々面倒です。

perlやPHPにはタイムスタンプを変更する関数が用意されていますが、これを使っても変更できない場合があります(参考:perl で utime できない問題について)。これは、ファイルの所有者(オーナー)とは違う権限でスクリプトが実行されるせい(ただし suEXEC を導入しているサーバではオーナー権限でスクリプトが走るので問題ないはず)です。

そこで私が取った手法はこんな感じ。

  1. タイムスタンプを変えたいファイルAを開き、中身を読み出す。
  2. スクリプトで新しいファイルBを作り、さっき読んだ中身を書き出す。(ディレクトリを書き込み可にしておく必要あり)
  3. 新しく作ったファイルのタイムスタンプを変更する。スクリプトが自分で作ったファイルなので問題なく変更できる。
  4. ファイル名を変更する。元のAを別の名にし、Bのファイル名をAにする。
  5. このままだとFTPツールなどで削除できないのでAのパーミッションを 666 あたりに変更する。

…まあ、わざわざ手間をかけるだけのメリットはない、というのが正直なところ。