154 lines
5.4 KiB
PHP
154 lines
5.4 KiB
PHP
<?php
|
||
|
||
declare(strict_types=1);
|
||
|
||
/**
|
||
* Test 19: ImageCropService – automatisches Wegschneiden weißer Ränder.
|
||
*
|
||
* Erstellt synthetische GD-Bilder im Arbeitsspeicher, speichert sie als
|
||
* temporäre Dateien und prüft, ob cropWhitespace() korrekt zuschneidet.
|
||
* Kein Datenbankzugriff nötig.
|
||
*/
|
||
class ImageCropTest extends TestCase
|
||
{
|
||
protected string $testName = 'Test 19: ImageCropService – Weißrand-Zuschnitt';
|
||
|
||
/** @var string[] */
|
||
private array $tmpFiles = [];
|
||
|
||
public function run(): void
|
||
{
|
||
$this->testCropJpeg();
|
||
$this->testCropPng();
|
||
$this->testNoCropWhenBorderTooSmall();
|
||
$this->testAllWhiteReturnsNoChange();
|
||
$this->testUnsupportedMimeReturnsFalse();
|
||
}
|
||
|
||
// -------------------------------------------------------------------------
|
||
|
||
private function testCropJpeg(): void
|
||
{
|
||
// 300×200 weißes Bild, rotes Rechteck 50×50 px bei (100, 75)
|
||
$img = imagecreatetruecolor(300, 200);
|
||
$white = imagecolorallocate($img, 255, 255, 255);
|
||
$red = imagecolorallocate($img, 200, 50, 50);
|
||
imagefill($img, 0, 0, $white);
|
||
imagefilledrectangle($img, 100, 75, 149, 124, $red);
|
||
|
||
$path = $this->saveTmp($img, 'jpeg');
|
||
imagedestroy($img);
|
||
|
||
$service = new \Steuerkram\Services\ImageCropService();
|
||
$result = $service->cropWhitespace($path);
|
||
|
||
$this->assert($result === true, 'JPEG: cropWhitespace() gibt true zurück');
|
||
|
||
[$newW, $newH] = getimagesize($path);
|
||
|
||
// Erwartete Maße: Rechteck (100,75)–(149,124) + PADDING 25 ringsum → 100×100.
|
||
// JPEG-Verlust-Komprimierung kann Farbe ±2px in Nachbar-DCT-Blöcke bluten lassen,
|
||
// daher Toleranz von 2px je Seite.
|
||
$this->assert($newW <= 104 && $newW >= 96, "JPEG: Breite nach Crop im Bereich 96–104 px (erhalten: {$newW})");
|
||
$this->assert($newH <= 104 && $newH >= 96, "JPEG: Höhe nach Crop im Bereich 96–104 px (erhalten: {$newH})");
|
||
}
|
||
|
||
private function testCropPng(): void
|
||
{
|
||
// 400×300 weißes PNG, blaues Rechteck 80×60 px bei (160, 120)
|
||
$img = imagecreatetruecolor(400, 300);
|
||
$white = imagecolorallocate($img, 255, 255, 255);
|
||
$blue = imagecolorallocate($img, 50, 100, 200);
|
||
imagefill($img, 0, 0, $white);
|
||
imagefilledrectangle($img, 160, 120, 239, 179, $blue);
|
||
|
||
$path = $this->saveTmp($img, 'png');
|
||
imagedestroy($img);
|
||
|
||
$service = new \Steuerkram\Services\ImageCropService();
|
||
$result = $service->cropWhitespace($path);
|
||
|
||
$this->assert($result === true, 'PNG: cropWhitespace() gibt true zurück');
|
||
|
||
[$newW, $newH] = getimagesize($path);
|
||
|
||
// cropX=135, cropY=95, cropW=130, cropH=110
|
||
$this->assertEquals(130, $newW, 'PNG: Breite nach Crop korrekt (80px + 2×25 Padding)');
|
||
$this->assertEquals(110, $newH, 'PNG: Höhe nach Crop korrekt (60px + 2×25 Padding)');
|
||
}
|
||
|
||
private function testNoCropWhenBorderTooSmall(): void
|
||
{
|
||
// Inhalt füllt fast das gesamte Bild – weniger als 3 % Rand
|
||
$img = imagecreatetruecolor(200, 200);
|
||
$white = imagecolorallocate($img, 255, 255, 255);
|
||
$red = imagecolorallocate($img, 200, 50, 50);
|
||
imagefill($img, 0, 0, $white);
|
||
// Inhalt lässt nur 1px Rand ringsum → Einsparung < 3 %
|
||
imagefilledrectangle($img, 1, 1, 198, 198, $red);
|
||
|
||
$path = $this->saveTmp($img, 'jpeg');
|
||
imagedestroy($img);
|
||
|
||
$originalSize = filesize($path);
|
||
|
||
$service = new \Steuerkram\Services\ImageCropService();
|
||
$result = $service->cropWhitespace($path);
|
||
|
||
$this->assert($result === false, 'Kein Crop wenn Randersparnis < 3 %');
|
||
[$newW, $newH] = getimagesize($path);
|
||
$this->assertEquals(200, $newW, 'Bildbreite bleibt unverändert bei minimalem Rand');
|
||
}
|
||
|
||
private function testAllWhiteReturnsNoChange(): void
|
||
{
|
||
$img = imagecreatetruecolor(100, 100);
|
||
$white = imagecolorallocate($img, 255, 255, 255);
|
||
imagefill($img, 0, 0, $white);
|
||
|
||
$path = $this->saveTmp($img, 'jpeg');
|
||
imagedestroy($img);
|
||
|
||
$service = new \Steuerkram\Services\ImageCropService();
|
||
$result = $service->cropWhitespace($path);
|
||
|
||
$this->assert($result === false, 'Komplett weißes Bild: kein Crop, gibt false zurück');
|
||
}
|
||
|
||
private function testUnsupportedMimeReturnsFalse(): void
|
||
{
|
||
// Erstellt eine Dummy-Textdatei – MIME-Typ wird nicht als JPEG/PNG erkannt
|
||
$path = sys_get_temp_dir() . '/crop_test_' . uniqid() . '.txt';
|
||
file_put_contents($path, 'kein Bild');
|
||
$this->tmpFiles[] = $path;
|
||
|
||
$service = new \Steuerkram\Services\ImageCropService();
|
||
$result = $service->cropWhitespace($path);
|
||
|
||
$this->assert($result === false, 'Nicht-Bild-Datei: cropWhitespace() gibt false zurück');
|
||
}
|
||
|
||
// -------------------------------------------------------------------------
|
||
|
||
private function saveTmp(\GdImage $img, string $type): string
|
||
{
|
||
$path = sys_get_temp_dir() . '/crop_test_' . uniqid() . '.' . $type;
|
||
$this->tmpFiles[] = $path;
|
||
|
||
if ($type === 'jpeg') {
|
||
imagejpeg($img, $path, 92);
|
||
} else {
|
||
imagepng($img, $path);
|
||
}
|
||
|
||
return $path;
|
||
}
|
||
|
||
public function tearDown(): void
|
||
{
|
||
foreach ($this->tmpFiles as $f) {
|
||
if (file_exists($f)) unlink($f);
|
||
}
|
||
parent::tearDown();
|
||
}
|
||
}
|