118 lines
3.9 KiB
PHP
118 lines
3.9 KiB
PHP
|
<?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;
|
||
|
}
|
||
|
}
|