unoh.github.com

PHP標準の機能(だけ)でウェブサイトのサムネイルを作る

Tue Jul 24 07:37:50 -0700 2007

こんばんわ、isogawaです。

PHP 5.2.2以降には、Windows版限定で一部のスキモノには注目の機能が追加されています。GDライブラリの開発者Pierre-A. Joyeによって実装された、imageGrabScreenとimageGrabWindowのふたつの関数がそれで、それぞれ画面全体と個々のウィンドウをキャプチャーするものです。

これを使えば、例えばInternet Explorerの画面をキャプチャーして、ウェブページのスクリーンショットやサムネイルを簡単に作成できたりします。ウェブページのキャプチャーはすでに色々な方法が知られていますが、標準機能として用意されたことで、(既存の手法と比べて性能面での優劣はさておき)実にお手軽に作成できるようになったわけです。

てなことで、これで遊んでみようと思うのですが。まずもちろん、Windows版PHPの5.2.2以降は必須ですが、これを利用するには、Windowsのサービスとして動作しているウェブサーバに、“デスクトップとの対話をサービスに許可”してやる必要があります。これを設定するには、Windowsコントロールパネルの管理ツールから「サービス」を選び、ウェブサーバがApacheなら、サービスの一覧からApacheを選択して表示される画面の「ログオン」タブで、「デスクトップとの対話をサービスに許可」をチェックして、Apacheを再起動してください。

デスクトップとの対話をサービスに許可
デスクトップとの対話をサービスに許可 posted by (C)フォト蔵

ではまず試しにInternet Explorerのウィンドウをキャプチャーして、そのサムネイル(ここでは160×120ピクセル)を表示してみましょう。PHPからIEを操作するにはCOMを使用します。

<?php

$url = 'お好みのURLをどーぞ';

if (!extension_loaded('gd')) {
  exit('GD library is not loaded.');
} elseif (!function_exists('imagegrabwindow')) {
  exit('Function imageGrabWindow is not exist.');
}
try {
  $ie = new COM('InternetExplorer.Application');
  $handle = $ie->HWND;
  $ie->Width = 800;
  $ie->Height = 600;
  $ie->Visible = true;
  $ie->Navigate($url);
  while ($ie->Busy) {
    com_message_pump(4000);
  }
  $image = imagegrabwindow($handle);
  $ie->Quit();
} catch (Exception $e) {
  exit($e->getMessage());
}
if (!$image) {
  exit('Failed to grab image.');
}
$thumb = imagecreatetruecolor(160, 120);
imagecopyresampled(
  $thumb, $image,
  0, 0, 0, 0, 160, 120, 800, 600
);
header('Content-Type: image/jpeg');
imagejpeg($thumb);

ここではIEの起動時のウィンドウサイズを800×600に変更していますが、このようにIE(IWebBrowser2オブジェクト)のプロパティ/メソッドを操作することで、PHPでその動作や見た目を変更できます。IWebBrowser2オブジェクトで利用できるプロパティ/メソッドについてはIWebBrowser2 Interfaceを参照ください。

なお、途中のwhile文では、ページを読み込んでいる最中の画面をキャプチャーしないように、IEがページの読み込みを完了するまで待機しています。

さて、imageGrabScreen/imageGrabWindowが返すのはGDリソースなので、上の例のように、GDを使ってキャプチャー画像をリサイズしたり、あるいは文字や図形を重ねたりと、自由に変形できますが、さてそれではなにをしようかと思ったところで、ここで画像のリサイズコードをだらだらと書いてもしょうがないので、フォト蔵APIを利用して、作成したスクリーンショットをフォト蔵にアップロードしてみることにしました。

以下のスクリプトの実行にはフォト蔵アカウントが必要です。もしまだアカウントをお持ちでなければ是非、登録ページでユーザ登録してください!

<?php

/*
 * Photozou username (your email address), password
 * and the ID number of the album to which you want to post the image.
 */
$username = 'フォト蔵のユーザ名(メールアドレス)';
$password = 'フォト蔵のパスワード';
$album_id = アップロード先のアルバムのID(数字);

/*
 * URL to be displayed in IE.
 */
$url = 'お好みのURLをどーぞ';

/*
 * Check if required libraries are available.
 */
require_once 'HTTP/Request.php';
if (!extension_loaded('gd')) {
  exit('GD library is not loaded.');
} elseif (!function_exists('imagegrabwindow')) {
  exit('Function imageGrabWindow is not exist.');
}

/*
 * Launch Internet Explorer and capture the window image.
 * Exit if COM throws an exception.
 */
try {
  $ie = new COM('InternetExplorer.Application');
  $handle = $ie->HWND;
  $ie->Width = 800;
  $ie->Height = 600;
  $ie->Visible = true;
  $ie->Navigate($url);
  while ($ie->Busy) {
    com_message_pump(4000);
  }
  $image = imagegrabwindow($handle);
  $ie->Quit();
} catch (Exception $e) {
  exit($e->getMessage());
}

/*
 * Save the captured image as a temporary file.
 */
$filename = tempnam(sys_get_temp_dir(), 'tmp');
if (!$image || !$filename || !imagejpeg($image, $filename) || !file_exists($filename)) {
  exit('Failed to save image.');
}
imagedestroy($image);

/*
 * Post the temporary file to Photozou via API.
 */
$request = new HTTP_Request;
$request->setURL('http://api.photozou.jp/rest/photo_add');
$request->addHeader('User-Agent', 'Hogebot/1.0 (Bogus)');
$request->setBasicAuth($username, $password);
$request->setMethod(HTTP_REQUEST_METHOD_POST);
$request->addPostData('album_id', $album_id);
$request->addFile('photo', $filename, 'image/jpeg');
$result = $request->sendRequest();

/*
 * Exit if the API request was failed.
 */

$error_message = '';
$code = $request->getResponseCode();
if (PEAR::isError($result)) {
  $error_message = $result->getMessage();
} elseif (!in_array($code, array(200, 401))) {
  $error_message = "Responded status code was $code.";
} elseif ('' == $body = $request->getResponseBody()) {
  $error_message = 'Responded body was empty.';
} else {
  try {
    $rsp = new SimpleXMLElement($body);
  } catch (Exception $e) {
    $error_message = $e->getMessage();
  }
  if (isset($rsp) and (string) $rsp['stat'] != 'ok') {
    $api_error_messages = array();
    foreach ($rsp->err as $error) {
      $api_error_messages[] = (string) $error['msg'];
    }
    $error_message = implode("\n", $api_error_messages);
  }
}
unlink($filename);
if (!empty($error_message)) {
  header('Content-Type: text/plain;charset=UTF-8');
  exit($error_message);
}

/*
 * Respond HTML if everything went well.
 */
echo "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" lang="ja">
  <head>
    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
    <meta http-equiv="Content-Style-Type" content="text/css" />
    <title>Photozou</title>
  </head>
  <body>
    <h1>Image was uploaded.</h1>
    <div><?php echo $rsp->large_tag ?></div>
  </body>
</html>