はじめに
業務で画像処理機能を作った。
その際に使ったIntervention Imageというライブラリが非常に優秀だったので、
今回実装した内容をもとに、使い方について説明していく。
この記事でやること
###大前提
・ベース画像(縦42px 横123px 白)に、テキストとアップロードした画像を合成する
・アップロードされる画像は、ベース画像に合わせて適宜リサイズされる
###3パターン用意
・画像テキストを縦に合成
・画像テキストを横に合成
・テキストのみの合成
準備
まず最初に、今回の主役Intervention Imageをインストールする。
実行するコマンドはこれだけ
composer require intervention/image
※ Laravel 5.4 以下の場合はPackage Auto-Discoveryがないので手動でconfig/app.phpにパッケージを登録する必要がある。
app.phpに設定を追記する場合↓
'providers' => [
// 省略
Intervention\Image\ImageServiceProvider::class
]
'aliases' => [
// 省略
'Image' => Intervention\Image\Facades\Image::class
]
※シンボリックリンクは事前に貼ってある前提なので割愛。
実装
早速Intervention Imageを使って実装を進める。
今回必要な処理は、
1.ロゴ・テキストの座標値とサイズの定義
2.ベースとなる画像を読み込んでおく
3.画像とテキストをリサイズする関数を作成
4.テキストのみ合成する
5.画像テキストを縦横列で合成する
6.クライアントサイドからのリクエストを元に処理を出しわけ
項目ごとに処理を説明していく。
コントローラの肥大化を防ぐため、画像処理はImageDomainにまとめた。
1、ロゴ・テキストの座標値とサイズの定義
ベース画像(縦42px 横123px)に合うサイズ感になるようにconstで初めに定義しておく。
//ロゴ・テキストを配置する縦横の座標値
const COORDINATES = [
'logoCenterX' => 0,
'logoCenterY' => 5,
'logoLeftX' => 5,
'logoLeftY' => 0,
'textCenterX' => 61,
'textCenterY' => 37,
'textLeftX' => 77,
'textLeftY' => 21,
'textOnlyX' => 61,
'textOnlyY' => 21,
];
//リサイズするロゴ画像サイズを定義
const RESIZE_LOGO_SIZE = [
'widthLeft' => 27,
'heightLeft' => 27,
'widthCenter' => 37,
'heightCenter' => 15,
];
2、ベースの画像を読み込む
ベースの画像を所定の箇所にあらかじめ保存しておき、呼び出す。
Intervention Imageなら、Image::make(***);で画像を読み込むことができる。
makeは様々な形式に対応していて、この点においても非常に優秀。
public static function common(): array
{
//デフォルト画像(白地)の呼び出し
$whiteBackground = storage_path('app/public/sample/default.png');
$default = Image::make($whiteBackground);
//保存先のパスを定義
$save_path = storage_path('app/public/sample/image.png');
return [$default, $save_path];
}
3、画像とテキストをリサイズする関数を作成
あとで複数回使うので、ここでリサイズする関数を作っておく
public static function resizedLogoText($request): array
{
//クライアントが送信したロゴ画像をリサイズ
$upload_image = $request->file('sample_image');
$logoImage = Image::make($upload_image);
//ベース画像にリサイズしたロゴ・社名を入れる
$uploadClientName = $request->input('sample_text');
return [$logoImage, $uploadClientName];
}
4、テキストのみ合成する
ここから合成処理を行なっていく。
テキストは文字数によって表示するフォントサイズを変えて、いい感じの大きさになるようにした。
使用しているIntervention Image独自の関数は以下。
file( )・・・テキストに使用するフォントを決める。今回はGoogleのNotoSansJP
を使用
size( )・・・テキストの大きさを指定
align( )・・・横の揃え方を指定。left, center, right
の3つを引数に取れる
valign( )・・・縦の揃え方を指定。top, middle, bottom
の3つを引数に取れる
save( )・・・保存
public static function onlyText($request)
{
[$default, $save_path] = self::common($request);
$uploadClientName = $request->input('only_text');
$lengthText = mb_strlen($uploadClientName, "UTF-8");
$size = floor(100 / $lengthText);
if ($size < 8){
$size = 8;
}elseif ($size > 25){
$size = 25;
}
$onlyText = $default->text($uploadClientName, self::COORDINATES['textOnlyX'], self::COORDINATES['textOnlyY'], function($font) use($size){
$font->file(base_path('public/font/NotoSansJP-Black.otf'));
$font->size($size);
$font->color('#333333');
$font->align('center');
$font->valign('middle');
});
$imageData = $onlyText->save($save_path);
return $imageData;
}
5、画像テキストを縦横列で合成する(ほぼ一緒なので横列の処理は割愛)
やっていることは4とほとんど変わらない。
用意していた関数を使って画像サイズをリサイズする処理と合成の処理が加わった。
新たに使用したIntervention Imageの関数は以下。
resize( )・・・画像のリサイズ。resize(横の長さ, 縦の長さ)でサイズを決められる
insert( )・・・画像を挿入(合成)。insert(挿入したい画像, 位置, 横軸, 縦軸)
public static function imageVertical($request)
{
list($default, $save_path) = self::common();
list($logoImage, $uploadClientName) = self::resizedLogoText($request);
//セットした画像をリサイズする
$resizedCenter = $logoImage->resize(self::RESIZE_LOGO_SIZE['widthCenter'], self::RESIZE_LOGO_SIZE['heightCenter']);
//リサイズした画像を白地に合成する
$vertical = $default->insert($resizedCenter, 'top', self::COORDINATES['logoCenterX'], self::COORDINATES['logoCenterY']);
$vertical->text($uploadClientName, self::COORDINATES['textCenterX'], self::COORDINATES['textCenterY'], function($font) {
$font->file(storage_path('app/public/font/NotoSansJP-Black.otf'));
$font->size(8);
$font->color('#333333');
$font->align('center');
$font->valign('bottom');
});
$imageData = $vertical->save($save_path);
return $imageData;
}
6、処理の出しわけ
今回、表側の処理は書いていないが、クライアントサイドから渡ってきたリクエストで判断し出し分ける
public function update(Request $request)
{
if ($request->filled('only_text') === true) {
$imageData = ImageDomain::onlyText($request);
} else {
if ($request->boolean('image') === true) {
$imageData = ImageDomain::imageVertical($request);
} else {
$imageData = ImageDomain::imageSide($request);
}
}
できた
まとめ
今回は使わなかったが、モザイク処理や、画像の調光処理など他にも色々な機能が充実している。しかも簡単。
Laravelで画像処理するならIntervention Imageを使わない手はない!!
参考にしたサイト