- SlideBoom

~ベタなPHP開発手法~
2013/01/22 “よや” <[email protected]>
退職届PDFメーカーの作り方
自己紹介
 昨年まで PHP + C言語、たまに JavaScript
でお仕事してました。
 趣味で公開してるライブラリ
(Flashバイナリ操作)
 http://sourceforge.jp/projects/swfed
 http://openpear.org/package/IO_SWF
 ImageMagick のストーカーやってます。
 http://d.hatena.ne.jp/yoya/searchdiary?word=Ima
geMagick
発表内容
 退職届PDFメーカーについて
 普通のPHPプログラミング
 普通じゃない(?)PHPプログラミング
 PDF 出力について
 iframe を使った PDF 表示
 壁紙機能 (ストレージ、擬似α透過)
 QR コード対応
退職届PDFメーカーとは
 参考) http://gigazine.net/news/20140105resignation-letter-generator/
 http://app.awm.jp/resign/ 退職届PDFメーカー
利用者様の喜びの声!?
普通のPHPプログラミング
 SSI (server side include) 風
 CGI (common gateway interface) 風
 テンプレートエンジン(Smarty)
 フレームワーク?
普通の PHP プログラミング(1/4)
 SSI(server side include) 的な使い方
<html>
<head><title> 1 </title> </head>
<body>
<?php echo date("Y/m/d H:i:s"); ?>
</body> </html>
 http://awm.jp/~yoya/study/php/sagami/1/1.php
普通の PHP プログラミング(2/4)
 CGI (common gateway interface) 的な使い方
<?php
echo "<html>\n";
echo "<head><title> 2 </title> </head>\n";
echo "<body>\n";
echo date("Y/m/d H:i:s");
echo "</body> </html>\n";
?>
 http://awm.jp/~yoya/study/php/sagami/1/2.php
普通の PHP プログラミング(3/4)
 テンプレートエンジン(Smarty) で
http://awm.jp/~yoya/study/php/sagami/1/3.tpl
<html>
<head><title> 3 </title> </head>
<body> {$now} </body>
</html>
<?php
require_once('Smarty.class.php');
$smarty = new Smarty();
$smarty->assign('now', date("Y/m/d
H:i:s"));
$smarty->display('3.tpl');
 http://awm.jp/~yoya/study/php/sagami/1/3.php
普通の PHP プログラミング(4/4)
PHPフレームワーク
CakePHP ? ZendFramework ?
Symphony ? CodeIgniter ?
↑省略!!!
こういったページでも見て下さい >
http://antenasites.com/2013/07/php-framework/
いずれにしても HTML 出力の話ばかり
\今回もはそういう話はしません/
そもそも、僕は普通の発表した事ないです。
普通じゃないPHPプログラミング
\HTML 以外を出力してみよう/
 PHP は HTML 以外にも色々出力出来ます。
PHP で HTML じゃないモノ出力
 JPEG/PNG画像出力
 PDF 出力
 Excel 出力 (要望があれば次回に解説)
 Spreadsheet/Excel/Writer はそのままだと
PHP5.4 で動かないんですよね…
PNG画像出力 (1/2)
<?php
header ('Content-type: image/png');
$im = imagecreate(240, 240);
$red = imagecolorallocate($im, 255, 0, 0);
$green = imagecolorallocate($im, 0, 255, 0);
$blue = imagecolorallocate($im, 0, 0, 255);
imageline($im, 0, 0, 200, 200, $green);
imageline($im, 0, 100, 200, 100, $blue);
imagepng($im);
 http://awm.jp/~yoya/study/php/sagami/1/line.php
PNG画像出力 (2/2)
<?php
header ('Content-type: image/png');
$im = imagecreate(240, 240);
$palette = array();
for ($i = 0 ; $i < 256 ; $i++) {
$palette []= imagecolorallocate(
$im, mt_rand(0, 255), mt_rand(0, 255), mt_rand(0, 255)
);
}
for ($y = 0 ; $y < 240 ; $y++) {
for ($x = 0 ; $x < 240 ; $x++) {
imagesetpixel($im, $x, $y, $palette[mt_rand(0, 255)]);
}
}
imagepng($im);
 http://awm.jp/~yoya/study/php/sagami/1/png.php
JPEG画像出力
<?php
header ('Content-type: image/jpeg');
$im = imagecreate(240, 240);
$palette = array();
for ($i = 0 ; $i < 256 ; $i++) {
$palette []= imagecolorallocate(
$im, mt_rand(0, 255), mt_rand(0, 255), mt_rand(0, 255)
);
}
for ($y = 0 ; $y < 240 ; $y++) {
for ($x = 0 ; $x < 240 ; $x++) {
imagesetpixel($im, $x, $y, $palette[mt_rand(0, 255)]);
}
}
imagejpeg($im);
 http://awm.jp/~yoya/study/php/sagami/1/jpeg.php
PDF 出力
 FPDF と PHPlib が有名
 とりあえず FPDF を試す
 インストール (Debian6)
$ apt-cache search fpdf
php-fpdf - PHP class to generate PDF files
$ sudo apt-get install php-fpdf
 日本語対応
 http://www.fpdf.org/phorum/read.php?f=1&i=7977&t=79
77
 japanese.zip と mbfpdf10b.zip があって後者の方が評
判が良いが、導入が楽なので前者を使ってみた
 (実際に何が困るのか知りたかったのもある)
 でも今の所、困る事がないので困ってる。
PDF 出力 (日本語対応)
 japanese.php は SJIS(cP932) 前提
 今時の PHP は日本語を UTF-8
 変換する
function Text($x,$y,$txt) {
$txt = mb_convert_encoding($txt, "CP932", "UTF-8"); // ☆
parent::Text($x,$y,$txt);
}
require('japanese.php');
header('Content-Type: application/pdf;');
$pdf=new PDF_Japanese();
$pdf->AddSJISFont();
$pdf->AddPage();
$pdf->SetFont('SJIS','',18);
$pdf->Text(100,60,'こんにちわ!');
$pdf->Output();
 http://awm.jp/~yoya/study/php/sagami/1/fpdfj.php
PDF 出力 (縦書き対応)
 rotation.php で回転出来るけど日本語未対応
 継承で合体させてみた
 書き換え
require('japanese.php');
// class PDF_Rotate extends PDF // オリジナル
class PDF_Rotate extends PDF_Japanese // ☆ {
require('japanese.php');
header('Content-Type: application/pdf;');
$pdf=new PDF_Japanese();
$pdf->AddSJISFont();
$pdf->AddPage();
$pdf->SetFont('SJIS','',18);
$pdf->Rotate(45 ,100, 60);
$pdf->Text(100,60,'こんにちわ!');
$pdf->Output();
 http://awm.jp/~yoya/study/php/sagami/1/fpdfr.php
日本語対応 (縦書きレイアウト)
 単純に1文字ずつ縦にしても駄目
 90度倒さないと駄目なもの > ー (長音)
 横がズレルもの > 」 (かぎ括弧)
 縦書き用フォントならカーニングに情報
が入ってそうだけど、悲しいけどデバイ
スフォントなのよね。。
 デバイスフォントでPDF の容量を減らして、
さくさく操作感を重視している。
日本語対応 (縦書きレイアウト)
 失敗例を元に、対処前と対処後
バグってました
後で治します
日本語対応 (縦書き無理やり対
応)
switch ($c) {
case 'ー':
case '-':
case '―':
case '‐':
case '~':
$this->Rotate(90, $x+$fontSize*1.1, $y-$fontSize*1.1);
$this->Text($x, $y, $c);
$this->Rotate(0);
break;
case '、':
case '。':
case ',':
case '.':
$this->Text($x + $fontSize*1.5, $y - $fontSize*2, $c);
break;
case '」':
case '”':
$this->Text($x + $fontSize*1.5, $y, $c);
break;
default:
$this->Text($x, $y, $c);
break;
日本語対応 (漢数字)
 パターン
 1 => 一
 10 => 十
 20 => 二十
 21 => 二十一
日本語対応 (漢数字 \力づく/)
function date2japanize($date) { // range: 0 - 99
$from = array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9');
$to = array('〇', '一', '二', '三', '四', '五', '六', '七', '八', '九');
$dates = split('-', $date);
$dates[0] -= 1988;
foreach (range(0, 2) as $idx) {
$d = $dates[$idx];
if ($d < 10) {
$d = mb_substr($d, 1);
} elseif ($d == 10) {
$d = '十';
} elseif ($d < 20) {
$d = '十'.mb_substr($d, 1);
} elseif (($d % 10) === 0) {
$d = mb_substr($d, 0, 1).'十';
} else {
$d = mb_substr($d, 0, 1).'十'.mb_substr($d, 1);
}
$dates[$idx] = str_replace($from, $to, $d);
}
return '平成'.$dates[0].'年'.$dates[1].'月'.$dates[2].'日';
}
iFrame を使った即時反映
 http://app.awm.jp/resign/resign.php (トップ)
 GET パラメータを読んで入力 フォームを埋め
る
 それと同時に iframe
で pdf.php を開く
resign.php
入力
フォ
ーム
 http://app.awm.jp/resign/pdf.php
 GET パラメータを応じた PDF を出力
\それだけ/
pdf.php
(iframe)
壁紙
 普通のファイル送信フォーム
<form enctype="multipart/form-data" action="?"
method="POST">
<input type="hidden" name="MAX_FILE_SIZE"
value="100000000" />
壁紙用の画像ファイルをアップロード: <input
name="image_file" type="file" class="btn btn-success" />
<input type="submit" value="画像ファイル送信"
class="btn btn-primary" />
 ファイルに一意な ID を付けて保存する
 擬似αブレンディング処理をする
壁紙 (ファイルに一意なIDを)
 sha1 を使ってデータを数値に変換する
 40文字は長いので12文字に減らす (衝突御免)
function image_data2id($data, $ext) { // 16 chars digest
$sha1 = sha1($data, true);
$base64 = base64_encode(substr($sha1, 0, 12));
return strtr($base64, '+/', '-_').'.'.$ext;
}
 フォルダを分割して保存
 1フォルダに大量のファイル置くの怖いので
[email protected]:/<秘密>/app/resign/img$ ls
0H 1j 3g 6u 9L AP Cg Eu JY PB Ur XV ZC cx iI jm nL qP sN ve
0N 2D 3w 6z 9O AW DA F_ Kg S5 VG Xq _A dK iP kL oE rl sO xa
0x 2j 6k 8M A0 CH DN GR MU SE VR YM ao g0 ip lF oQ ry sn xs
17 33 6s 8p AA Ca Ed Hh Ni Un Wb Yj bM i1 ja lX pa sM tz z9
壁紙 (αブレンディング)
 元画像と白っぽく変換した画像を保存
[email protected]:/<秘密>/app/resign/img$ ls 17/
17Pe9qdjx8OtTz4h.jpg _17Pe9qdjx8OtTz4h.jpg
 (PNG は大丈夫だけど) JPEG 画像に対して
PDF に半透明の機能が見つからなかった
ので、擬似アルファブレンディング
擬似αブレンディング
 あらかじめ画像を白くしておけば、その
まま重ねても半透明で張ったように見え
る。
function filterPixel(&$red, &$green, &$blue) {
$red = (255 * 6 + $red) / 7;
$green = (255 * 6 + $green) / 7;
$blue = (255 * 6 + $blue) / 7;
}
QRコード対応
 PHP QR Code encoder を使ってます
 http://phpqrcode.sourceforge.net/
 処理が重たいので大規模サイトではお勧
め出来ませんが、趣味では十分です。
require('phpqrcode.php');
$url = (empty($_SERVER["HTTPS"]) ? "http://" : "https://") .
$_SERVER["HTTP_HOST"] . $_SERVER["REQUEST_URI"];
$url = substr($url, 0, strlen($url) - strlen('&ext=.pdf'));
$tmpfile = tempnam('tmp', 'qr');
QRcode::png($url, $tmpfile, 'L', 4, 0);
$pdf->Image($tmpfile, 25, 220, 40, 40, 'png');
unlink($tmpfile);
Github で公開中
 https://github.com/yoya/resign
 簡単な説明も
 http://pwiki.awm.jp/~yoya/?resign
質問コーナー
 Q) sha1 の第二引数 false にすれば base64_encode 要らない?

気付きませんでした!でも互換性怖いのでこのままで。
 Q) jpeg も png で保存すれば半透明対応できない?


Jpeg と png でリサイズのレンダリングが違う可能性があるので。
あと、容量増えるの嫌だったので。(後付け理由)
 Q) 反響はどうですか


おかげ様で400を超えるブクマを頂いてます。
利用者の方から直接ありがとうメールを貰ったりもします。
 Q) 生成された PDF を手元にこっそり保存してません?
URL で完全に再現できるので access_log を保存すれば出来るけど、
誓ってみてません。
 あと、万が一のサーバクラックが怖いので、日付でにファイルを
切り替えてて古いのは消してます。見てませんよ!

おわり