Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
32.82% |
64 / 195 |
|
14.81% |
4 / 27 |
CRAP | |
0.00% |
0 / 1 |
Graphics | |
32.82% |
64 / 195 |
|
14.81% |
4 / 27 |
1512.47 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
2 | |||
disposeImpl | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
create | |
75.00% |
3 / 4 |
|
0.00% |
0 / 1 |
2.06 | |||
load | |
92.31% |
12 / 13 |
|
0.00% |
0 / 1 |
7.02 | |||
open | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
getInformation | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getDpi | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
6 | |||
setDpi | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 | |||
getSize | |
71.43% |
5 / 7 |
|
0.00% |
0 / 1 |
3.21 | |||
scale | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
12 | |||
rotate | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
6 | |||
getPixel | |
88.89% |
8 / 9 |
|
0.00% |
0 / 1 |
2.01 | |||
setPixel | |
90.91% |
10 / 11 |
|
0.00% |
0 / 1 |
2.00 | |||
setThickness | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
6 | |||
applyThickness | |
0.00% |
0 / 12 |
|
0.00% |
0 / 1 |
12 | |||
attachColor | |
83.33% |
5 / 6 |
|
0.00% |
0 / 1 |
3.04 | |||
detachColor | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
doColorCore | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
doColor | |
80.00% |
4 / 5 |
|
0.00% |
0 / 1 |
2.03 | |||
fillRectangle | |
0.00% |
0 / 13 |
|
0.00% |
0 / 1 |
6 | |||
drawRectangle | |
0.00% |
0 / 17 |
|
0.00% |
0 / 1 |
12 | |||
calculateTextArea | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
6 | |||
drawString | |
0.00% |
0 / 16 |
|
0.00% |
0 / 1 |
6 | |||
drawText | |
0.00% |
0 / 18 |
|
0.00% |
0 / 1 |
56 | |||
saveCore | |
57.14% |
4 / 7 |
|
0.00% |
0 / 1 |
8.83 | |||
save | |
66.67% |
2 / 3 |
|
0.00% |
0 / 1 |
2.15 | |||
saveHtmlSource | |
0.00% |
0 / 10 |
|
0.00% |
0 / 1 |
12 |
1 | <?php |
2 | |
3 | declare(strict_types=1); |
4 | |
5 | namespace PeServer\Core\Image; |
6 | |
7 | use GdImage; |
8 | use PeServer\Core\Binary; |
9 | use PeServer\Core\DisposerBase; |
10 | use PeServer\Core\Errors\ErrorHandler; |
11 | use PeServer\Core\IDisposable; |
12 | use PeServer\Core\Image\Alignment; |
13 | use PeServer\Core\Image\Area; |
14 | use PeServer\Core\Image\Color\ColorResource; |
15 | use PeServer\Core\Image\Color\IColor; |
16 | use PeServer\Core\Image\Color\RgbColor; |
17 | use PeServer\Core\Image\ImageSetting; |
18 | use PeServer\Core\Image\ImageType; |
19 | use PeServer\Core\Image\Point; |
20 | use PeServer\Core\Image\Rectangle; |
21 | use PeServer\Core\Image\ScaleMode; |
22 | use PeServer\Core\Image\Size; |
23 | use PeServer\Core\Image\TextSetting; |
24 | use PeServer\Core\IO\File; |
25 | use PeServer\Core\IO\IOUtility; |
26 | use PeServer\Core\OutputBuffer; |
27 | use PeServer\Core\Throws\ArgumentException; |
28 | use PeServer\Core\Throws\GraphicsException; |
29 | use PeServer\Core\Throws\NotImplementedException; |
30 | use PeServer\Core\TypeUtility; |
31 | use PeServer\Core\Image\HorizontalAlignment; |
32 | use PeServer\Core\Image\VerticalAlignment; |
33 | |
34 | /** |
35 | * GD関数ラッパー。 |
36 | * |
37 | * @see https://www.php.net/manual/book.image.php |
38 | */ |
39 | class Graphics extends DisposerBase |
40 | { |
41 | public const CURRENT_THICKNESS = -1; |
42 | public const DEFAULT_THICKNESS = 1; |
43 | |
44 | public GdImage $image; |
45 | /** @phpstan-var positive-int */ |
46 | private int $thickness; |
47 | /** @phpstan-ignore-next-line */ |
48 | private bool $antiAlias; |
49 | |
50 | private function __construct(GdImage $image, bool $isEnabledAlpha) |
51 | { |
52 | $this->image = $image; |
53 | |
54 | $this->thickness = self::DEFAULT_THICKNESS; |
55 | imagesetthickness($this->image, $this->thickness); |
56 | |
57 | if ($isEnabledAlpha) { |
58 | imagealphablending($this->image, false); |
59 | imagesavealpha($this->image, true); |
60 | } |
61 | |
62 | $this->antiAlias = imageantialias($this->image, true); |
63 | } |
64 | |
65 | /** |
66 | * @see https://www.php.net/manual/function.imagedestroy.php |
67 | */ |
68 | protected function disposeImpl(): void |
69 | { |
70 | imagedestroy($this->image); |
71 | |
72 | parent::disposeImpl(); |
73 | } |
74 | |
75 | /** |
76 | * 画像の大きさを指定して生成。 |
77 | * |
78 | * @param Size $size |
79 | * @return Graphics |
80 | * @throws GraphicsException |
81 | */ |
82 | public static function create(Size $size): self |
83 | { |
84 | $image = imagecreatetruecolor($size->width, $size->height); |
85 | if ($image === false) { |
86 | throw new GraphicsException(); |
87 | } |
88 | |
89 | return new self($image, true); |
90 | } |
91 | |
92 | /** |
93 | * バイナリデータから生成。 |
94 | * |
95 | * @param Binary $binary |
96 | * @param ImageType $imageType |
97 | * @return Graphics |
98 | */ |
99 | public static function load(Binary $binary, ImageType $imageType = ImageType::Auto): self |
100 | { |
101 | $funcName = match ($imageType) { |
102 | ImageType::Png => 'imagecreatefrompng', |
103 | ImageType::Jpeg => 'imagecreatefromjpeg', |
104 | ImageType::Webp => 'imagecreatefromwebp', |
105 | ImageType::Bmp => 'imagecreatefrombmp', //cspell:disable-line |
106 | default => 'imagecreatefromstring' |
107 | }; |
108 | |
109 | $result = ErrorHandler::trap( |
110 | fn () => call_user_func($funcName, $binary->raw) |
111 | ); |
112 | |
113 | if (!$result->success) { |
114 | throw new GraphicsException(); |
115 | } |
116 | |
117 | return new self($result->value, true); |
118 | } |
119 | |
120 | /** |
121 | * ファイルから生成。 |
122 | * |
123 | * @param non-empty-string $path |
124 | * @return Graphics |
125 | */ |
126 | public static function open(string $path): self |
127 | { |
128 | $binary = File::readContent($path); |
129 | return self::load($binary); |
130 | } |
131 | |
132 | /** |
133 | * `gd_info` ラッパー。 |
134 | * |
135 | * @return array<string,string|bool> |
136 | * @see https://www.php.net/manual/function.gd-info.php |
137 | */ |
138 | public static function getInformation(): array |
139 | { |
140 | return gd_info(); |
141 | } |
142 | |
143 | /** |
144 | * DPI取得。 |
145 | * |
146 | * @return Size |
147 | * @throws GraphicsException |
148 | * @see https://www.php.net/manual/function.imageresolution.php |
149 | */ |
150 | public function getDpi(): Size |
151 | { |
152 | $result = imageresolution($this->image); //cspell:disable-line |
153 | if ($result === false) { |
154 | throw new GraphicsException('imageresolution'); //cspell:disable-line |
155 | } |
156 | assert(is_array($result)); |
157 | |
158 | return new Size($result[0], $result[1]); |
159 | } |
160 | /** |
161 | * DPI設定。 |
162 | * |
163 | * @param Size $size |
164 | * @throws GraphicsException |
165 | * @see https://www.php.net/manual/function.imageresolution.php |
166 | */ |
167 | public function setDpi(Size $size): void |
168 | { |
169 | $result = imageresolution($this->image, $size->width, $size->height); //cspell:disable-line |
170 | if ($result === false) { |
171 | throw new GraphicsException('imageresolution: ' . $size); //cspell:disable-line |
172 | } |
173 | } |
174 | |
175 | /** |
176 | * 画像サイズを取得 |
177 | * |
178 | * @return Size |
179 | * @throws GraphicsException |
180 | * @see https://www.php.net/manual/function.imagesx.php |
181 | * @see https://www.php.net/manual/function.imagesy.php |
182 | */ |
183 | public function getSize(): Size |
184 | { |
185 | $width = imagesx($this->image); |
186 | if ($width === false) { //@phpstan-ignore-line [PHP_VERSION] |
187 | throw new GraphicsException(); |
188 | } |
189 | |
190 | $height = imagesy($this->image); |
191 | if ($height === false) { //@phpstan-ignore-line [PHP_VERSION] |
192 | throw new GraphicsException(); |
193 | } |
194 | |
195 | return new Size($width, $height); |
196 | } |
197 | |
198 | /** |
199 | * 縮尺を変更。 |
200 | * |
201 | * @param int|Size $size int: 横幅のみ、高さは自動設定される。 Size:幅・高さ |
202 | * @phpstan positive-int|Size $size |
203 | * @param ScaleMode $scaleMode 変換フラグ。 |
204 | * @return Graphics |
205 | * @throws GraphicsException |
206 | * @see https://www.php.net/manual/function.imagescale.php |
207 | */ |
208 | public function scale(int|Size $size, ScaleMode $scaleMode): self |
209 | { |
210 | $result = false; |
211 | if (is_int($size)) { |
212 | $result = imagescale($this->image, $size, -1, $scaleMode->value); |
213 | } else { |
214 | $result = imagescale($this->image, $size->width, $size->height, $scaleMode->value); |
215 | } |
216 | if ($result === false) { |
217 | throw new GraphicsException(); |
218 | } |
219 | |
220 | return new self($result, true); |
221 | } |
222 | |
223 | /** |
224 | * `imagerotate` ラッパー。 |
225 | * |
226 | * @param float $angle |
227 | * @param IColor $backgroundColor |
228 | * @return Graphics |
229 | * @throws GraphicsException |
230 | * @see https://www.php.net/manual/function.imagerotate.php |
231 | */ |
232 | public function rotate(float $angle, IColor $backgroundColor): self |
233 | { |
234 | $result = $this->doColor( |
235 | $backgroundColor, |
236 | fn ($attachedColor) => imagerotate($this->image, $angle, $attachedColor) |
237 | ); |
238 | if ($result === false) { |
239 | throw new GraphicsException(); |
240 | } |
241 | |
242 | return new self($result, true); |
243 | } |
244 | |
245 | /** |
246 | * 指定したピクセルの色を取得。 |
247 | * |
248 | * `imagecolorat` ラッパー。 |
249 | * |
250 | * @param Point $point |
251 | * @return RgbColor |
252 | * @throws GraphicsException |
253 | * @see https://www.php.net/manual/function.imagecolorat.php |
254 | */ |
255 | public function getPixel(Point $point): RgbColor |
256 | { |
257 | $rgb = imagecolorat($this->image, $point->x, $point->y); |
258 | if ($rgb === false) { |
259 | throw new GraphicsException(); |
260 | } |
261 | |
262 | return new RgbColor( |
263 | ($rgb >> 16) & 0xff, |
264 | ($rgb >> 8) & 0xff, |
265 | $rgb & 0xff, |
266 | ($rgb >> 24) & 0x7f |
267 | ); |
268 | } |
269 | |
270 | /** |
271 | * 指定したピクセルの色を設定。 |
272 | * |
273 | * `imagesetpixel` ラッパー。 |
274 | * |
275 | * @param Point $point |
276 | * @param IColor $color |
277 | * @throws GraphicsException |
278 | * @see https://www.php.net/manual/function.imagesetpixel.php |
279 | */ |
280 | public function setPixel(Point $point, IColor $color): void |
281 | { |
282 | $result = $this->doColor( |
283 | $color, |
284 | fn ($attachedColor) => imagesetpixel( |
285 | $this->image, |
286 | $point->x, |
287 | $point->y, |
288 | $attachedColor |
289 | ) |
290 | ); |
291 | |
292 | if (!$result) { |
293 | throw new GraphicsException(); |
294 | } |
295 | } |
296 | |
297 | /** |
298 | * 線幅設定。 |
299 | * |
300 | * @param int $thickness |
301 | * @phpstan-param positive-int $thickness |
302 | */ |
303 | public function setThickness(int $thickness): void |
304 | { |
305 | $result = imagesetthickness($this->image, $thickness); |
306 | if ($result === false) { |
307 | throw new GraphicsException(); |
308 | } |
309 | |
310 | $this->thickness = $thickness; |
311 | } |
312 | |
313 | /** |
314 | * 線幅適用。 |
315 | * |
316 | * @param int $thickness |
317 | * @phpstan-param positive-int $thickness |
318 | * @return IDisposable 戻し。 |
319 | */ |
320 | private function applyThickness(int $thickness): IDisposable |
321 | { |
322 | if ($thickness === $this->thickness) { |
323 | return DisposerBase::empty(); |
324 | } |
325 | if ($thickness < 1) { //@phpstan-ignore-line [DOCTYPE] |
326 | throw new ArgumentException('$thickness'); |
327 | } |
328 | |
329 | $restoreThickness = $this->thickness; |
330 | $this->setThickness($thickness); |
331 | |
332 | //phpcs:ignore PSR12.Classes.AnonClassDeclaration.SpaceAfterKeyword |
333 | return new class($this, $restoreThickness) extends DisposerBase |
334 | { |
335 | /** |
336 | * 生成。 |
337 | * |
338 | * @param Graphics $graphics |
339 | * @param int $restoreThickness |
340 | * @phpstan-param positive-int $restoreThickness |
341 | */ |
342 | public function __construct( |
343 | private Graphics $graphics, |
344 | private int $restoreThickness |
345 | ) { |
346 | } |
347 | |
348 | protected function disposeImpl(): void |
349 | { |
350 | $this->graphics->setThickness($this->restoreThickness); |
351 | |
352 | parent::disposeImpl(); |
353 | } |
354 | }; |
355 | } |
356 | |
357 | /** |
358 | * `imagecolorallocate` ラッパー。 |
359 | * |
360 | * @param RgbColor $color |
361 | * @return ColorResource |
362 | * @throws GraphicsException |
363 | * @see https://www.php.net/manual/function.imagecolorallocatealpha.php |
364 | * @see https://www.php.net/manual/function.imagecolorallocate.php |
365 | */ |
366 | public function attachColor(RgbColor $color): ColorResource |
367 | { |
368 | $result = $color->alpha !== RgbColor::ALPHA_NONE |
369 | ? imagecolorallocatealpha($this->image, $color->red, $color->green, $color->blue, $color->alpha) |
370 | : imagecolorallocate($this->image, $color->red, $color->green, $color->blue); |
371 | if ($result === false) { |
372 | throw new GraphicsException(TypeUtility::toString($color)); |
373 | } |
374 | return new ColorResource($this, $result); |
375 | } |
376 | /** |
377 | * `imagecolordeallocate` ラッパー。 |
378 | * |
379 | * @param ColorResource $colorResource |
380 | * @return bool |
381 | */ |
382 | public function detachColor(ColorResource $colorResource): bool |
383 | { |
384 | return imagecolordeallocate($this->image, $colorResource->value); |
385 | } |
386 | |
387 | /** |
388 | * 色処理の実行部分。 |
389 | * |
390 | * @template TResult |
391 | * @param ColorResource $color |
392 | * @param callable $callback |
393 | * @phpstan-param callable(mixed): TResult $callback |
394 | * @return mixed |
395 | * @phpstan-return TResult |
396 | */ |
397 | private function doColorCore(ColorResource $color, callable $callback): mixed |
398 | { |
399 | return $callback($color->value); |
400 | } |
401 | |
402 | /** |
403 | * 色の処理。 |
404 | * |
405 | * @template TResult |
406 | * @param IColor $color |
407 | * @param callable $callback |
408 | * @phpstan-param callable(mixed): TResult $callback |
409 | * @return mixed |
410 | * @phpstan-return TResult |
411 | */ |
412 | private function doColor(IColor $color, callable $callback): mixed |
413 | { |
414 | if ($color instanceof RgbColor) { |
415 | $colorResource = $this->attachColor($color); |
416 | try { |
417 | return $this->doColorCore($colorResource, $callback); |
418 | } finally { |
419 | $this->detachColor($colorResource); |
420 | } |
421 | } else { |
422 | assert($color instanceof ColorResource); |
423 | return $this->doColorCore($color, $callback); |
424 | } |
425 | } |
426 | |
427 | /** |
428 | * 矩形塗り潰し。 |
429 | * |
430 | * @param IColor $color 色。 |
431 | * @param Rectangle $rectangle 矩形領域。 |
432 | */ |
433 | public function fillRectangle(IColor $color, Rectangle $rectangle): void |
434 | { |
435 | $result = $this->doColor( |
436 | $color, |
437 | fn ($attachedColor) => imagefilledrectangle( |
438 | $this->image, |
439 | $rectangle->left(), |
440 | $rectangle->top(), |
441 | $rectangle->right(), |
442 | $rectangle->bottom(), |
443 | $attachedColor |
444 | ) |
445 | ); |
446 | if ($result === false) { |
447 | throw new GraphicsException(); |
448 | } |
449 | } |
450 | |
451 | /** |
452 | * 矩形描画。 |
453 | * |
454 | * @param IColor $color |
455 | * @param Rectangle $rectangle |
456 | * @param int $thickness |
457 | * @phpstan-param positive-int|self::CURRENT_THICKNESS $thickness |
458 | */ |
459 | public function drawRectangle(IColor $color, Rectangle $rectangle, int $thickness = self::CURRENT_THICKNESS): void |
460 | { |
461 | $restore = $thickness === self::CURRENT_THICKNESS |
462 | ? DisposerBase::empty() |
463 | : $this->applyThickness($thickness); |
464 | |
465 | try { |
466 | $result = $this->doColor( |
467 | $color, |
468 | fn ($attachedColor) => imagerectangle( |
469 | $this->image, |
470 | $rectangle->left(), |
471 | $rectangle->top(), |
472 | $rectangle->right(), |
473 | $rectangle->bottom(), |
474 | $attachedColor |
475 | ) |
476 | ); |
477 | |
478 | if ($result === false) { |
479 | throw new GraphicsException(); |
480 | } |
481 | } finally { |
482 | $restore->dispose(); |
483 | } |
484 | } |
485 | |
486 | /** |
487 | * テキスト描画領域取得。 |
488 | * |
489 | * @param string $text |
490 | * @param float $fontSize |
491 | * @param string $fontNameOrPath |
492 | * @param float $angle |
493 | * @return Area |
494 | * @throws GraphicsException |
495 | * @see https://www.php.net/manual/function.imageftbbox.php |
496 | */ |
497 | public static function calculateTextArea(string $text, float $fontSize, string $fontNameOrPath, float $angle): Area |
498 | { |
499 | $options = []; |
500 | /** @phpstan-var non-empty-array<non-negative-int>|false */ |
501 | $result = imageftbbox($fontSize, $angle, $fontNameOrPath, $text, $options); |
502 | if ($result === false) { |
503 | throw new GraphicsException(); |
504 | } |
505 | |
506 | return Area::create($result); |
507 | } |
508 | |
509 | /** |
510 | * テキスト描画。 |
511 | * |
512 | * `imagettftext` ラッパー。 |
513 | * |
514 | * @param string $text 描画テキスト。 |
515 | * @param float $fontSize フォントサイズ。 |
516 | * @param Point $location 描画開始座標。 |
517 | * @param IColor $color 描画色。 |
518 | * @param TextSetting $setting 描画するテキスト設定。 |
519 | * @return Area 描画領域。 |
520 | * @throws GraphicsException |
521 | * @see https://www.php.net/manual/ja/function.imagettftext.php |
522 | */ |
523 | public function drawString(string $text, float $fontSize, Point $location, IColor $color, TextSetting $setting): Area |
524 | { |
525 | $result = $this->doColor( |
526 | $color, |
527 | fn ($attachedColor) => imagettftext( |
528 | $this->image, |
529 | $fontSize, |
530 | $setting->angle, |
531 | $location->x, |
532 | $location->y, |
533 | $attachedColor, |
534 | $setting->fontNameOrPath, |
535 | $text |
536 | ) |
537 | ); |
538 | |
539 | if ($result === false) { |
540 | throw new GraphicsException(); |
541 | } |
542 | |
543 | //@phpstan-ignore-next-line ↑が false だけのはずなんだけど true を捕まえてる感じ |
544 | return Area::create($result); |
545 | } |
546 | |
547 | /** |
548 | * いい感じにテキスト描画。 |
549 | * |
550 | * 内部的に `self::calculateTextArea`, `self::drawString` を使用。 |
551 | * |
552 | * @param string $text 描画テキスト。 |
553 | * @param float $fontSize フォントサイズ。 |
554 | * @param Rectangle $rectangle 描画する矩形。 |
555 | * @param IColor $color 描画色。 |
556 | * @param TextSetting $setting 描画するテキスト設定。 |
557 | * @return Area 描画領域。 |
558 | * @throws GraphicsException |
559 | * @see https://www.php.net/manual/function.imagettftext.php |
560 | */ |
561 | public function drawText(string $text, float $fontSize, Rectangle $rectangle, IColor $color, TextSetting $setting): Area |
562 | { |
563 | $fontArea = self::calculateTextArea($text, $fontSize, $setting->fontNameOrPath, $setting->angle); |
564 | |
565 | $x = match ($setting->horizontal) { |
566 | HorizontalAlignment::Left => $rectangle->left() - min($fontArea->left(), $fontArea->right()), |
567 | HorizontalAlignment::Center => $rectangle->left() + ($rectangle->size->width / 2) - ($fontArea->width() / 2), |
568 | HorizontalAlignment::Right => $rectangle->right() - max($fontArea->left(), $fontArea->right()), |
569 | }; |
570 | $y = match ($setting->vertical) { |
571 | VerticalAlignment::Top => $rectangle->top() - min($fontArea->top(), $fontArea->bottom()), |
572 | VerticalAlignment::Center => $rectangle->top() + ($rectangle->size->height / 2) + ($fontArea->height() / 2), |
573 | VerticalAlignment::Bottom => $rectangle->bottom() - max($fontArea->top(), $fontArea->bottom()), |
574 | }; |
575 | |
576 | return $this->drawString( |
577 | $text, |
578 | $fontSize, |
579 | new Point((int)$x, (int)$y), |
580 | $color, |
581 | $setting |
582 | ); |
583 | } |
584 | |
585 | private function saveCore(ImageSetting $setting): Binary |
586 | { |
587 | return OutputBuffer::get(fn () => match ($setting->imageType) { |
588 | ImageType::Png => imagepng($this->image, null, ...$setting->options()), |
589 | ImageType::Jpeg => imagejpeg($this->image, null, ...$setting->options()), |
590 | ImageType::Webp => imagewebp($this->image, null, ...$setting->options()), |
591 | ImageType::Bmp => imagebmp($this->image, null, ...$setting->options()), //cspell:disable-line |
592 | default => throw new NotImplementedException(), |
593 | }); |
594 | } |
595 | |
596 | /** |
597 | * 画像データ出力。 |
598 | * |
599 | * @param ImageSetting $setting |
600 | * @return Binary |
601 | */ |
602 | public function save(ImageSetting $setting): Binary |
603 | { |
604 | if ($setting->imageType == ImageType::Auto) { |
605 | throw new ArgumentException('ImageType::AUTO'); |
606 | } |
607 | |
608 | return $this->saveCore($setting); |
609 | } |
610 | |
611 | /** |
612 | * 画像データをHTMLのソースとして出力。 |
613 | * |
614 | * @param ImageSetting $setting |
615 | * @return string "data" URL scheme。 |
616 | */ |
617 | public function saveHtmlSource(ImageSetting $setting): string |
618 | { |
619 | if ($setting->imageType == ImageType::Auto) { |
620 | throw new ArgumentException('ImageType::AUTO'); |
621 | } |
622 | |
623 | $image = $this->saveCore($setting); |
624 | |
625 | $mime = match ($setting->imageType) { |
626 | default => $setting->imageType->toMime(), |
627 | }; |
628 | |
629 | $data = 'data:' . $mime . ';base64,'; |
630 | $body = $image->toBase64(); |
631 | |
632 | $result = $data . $body; |
633 | |
634 | return $result; |
635 | } |
636 | } |