service-captcha/app/Captcha/Images/Body.php

118 lines
3.9 KiB
PHP
Raw Normal View History

<?php declare(strict_types=1);
namespace App\Captcha\Images;
use App\Captcha\Config\ImageBody as ImageBodyConfig;
use App\Captcha\Contracts\ImageLines;
use App\Captcha\Dto\Coordinators;
use App\Captcha\Dto\Image as ImageDto;
use App\Captcha\Dto\ImageBody as ImageBodyDto;
use App\Captcha\Dto\Sector;
use App\Captcha\Dto\Sectors;
use App\Captcha\Dto\Symbols;
use App\Captcha\Contracts\ImageBody;
use App\Captcha\Enums\SymbolType;
use App\Captcha\Contracts\ImageManager;
final class Body implements ImageBody
{
public function __construct(
private readonly ImageManager $imageManager,
private readonly ImageLines $imageLines
) { }
public function processing(Symbols $symbols, ImageBodyConfig $config): ImageBodyDto
{
$image = $this->imageManager->createImage($config->getWidth(), $config->getHeight());
$image->insertBackground($config->randomBackground());
$imageData = match ($symbols->getType()) {
SymbolType::String => $this->processingString($image, $symbols, $config),
};
$image = $imageData['image'];
if ($config->getNumberLines() > 0) {
$this->imageLines->processing(image: $image, colors: $config->getLineColors(), lines: $config->getNumberLines());
}
$image = new ImageDto(
imageBase64: $image->encode(),
width: $image->getWidth(),
height: $image->getHeight()
);
return new ImageBodyDto(
image: $image,
coordinators: $imageData['coordinators']
);
}
private function processingString(Image $image, Symbols $symbols, ImageBodyConfig $config): array
{
$sectors = $this->calculateSectors($symbols, $image->getWidth(), $image->getHeight());
$coordinators = [];
foreach ($symbols->getSuccess() as $number => $symbol) {
$coordinators[] = $this->processingStringSymbol($image, $symbol, $config, $sectors->random());
}
if (!empty($symbols->getFakes())) {
foreach ($symbols->getFakes() as $number => $symbol) {
$this->processingStringSymbol($image, $symbol, $config, $sectors->random());
}
}
return [
'image' => $image,
'coordinators' => $coordinators
];
}
private function processingStringSymbol(Image &$image, string|int $symbol, ImageBodyConfig $config, Sector $sector): Coordinators
{
$fontSize = $config->fontSize();
if ($fontSize > $sector->getHeight()) {
$fontSize = $sector->getHeight();
}
$fontPathFile = $config->randomFont();
$fontColor = $config->randomFontColor();
$angle = $config->randomAngle();
$marginLeft = mt_rand($sector->getX(), $sector->getX() + $sector->getWidth() - $fontSize);
$marginTop = mt_rand($sector->getY(), $sector->getY() + $sector->getHeight() - $fontSize);
return $image->addText(
text: $symbol,
x: $marginLeft,
y: $marginTop,
size: $fontSize,
angle: $angle,
hexColor: $fontColor,
fontName: $fontPathFile
);
}
private function calculateSectors(Symbols $symbols, int $width, int $height): Sectors
{
$points = count($symbols->getSuccess()) + count($symbols->getFakes());
$sumFloors = 3;
$sumRooms = ceil($points / $sumFloors);
$heightFloor = floor($height / $sumFloors);
$widthFloor = floor($width / $sumRooms);
$sectors = new Sectors();
for ($floor = 0; $floor < $sumFloors; $floor++) {
$y = $heightFloor * $floor;
for ($room = 0; $room < $sumRooms; $room++) {
$sectors->add(
x: ($widthFloor * $room),
y: $y,
width: $widthFloor,
height: $heightFloor
);
}
}
return $sectors;
}
}