images: Datenmodell, Textarchiv, Herkunftsprüfung und gemeinsamer Uploader für Formular v2 (Bildkennzeichnung Block A)
- press_release_images: Kundenangabe/Portalkennzeichnung getrennt, Herkunftsbefund, Beweisoriginal, Snapshot-Pfade, published_ever_at, rules_version, Fokus/Letterbox; Bestand unangetastet - legal_texts (insert-only) mit img-confirm-v1 (alter Wortlaut), v2 (AGB 0.6), v3 (AGB 0.7, noch ohne Datum); image-rights:backfill-confirmations - image_rights_confirmations polymorph, insert-only, IP als versionierter HMAC; Löschung nur für nie veröffentlichte Uploads - ImageProvenanceInspector (Imagick/exif): IPTC, XMP, EXIF; algorithmicallyEnhanced nur Vermerk; unlesbare Datei = error - PressReleaseImageUploader: Original vor Derivaten, Hash-Bindung, Titelbild-Regel unter Sperre, Dateien bei Fehlern zurückgerollt; Admin-Upload ohne Bestätigung - ImageService: kein Rückfall auf die Quelldatei, og-Variante, Fokuspunkt und no_crop Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
b624f80d7f
commit
e9bdff3ad3
36 changed files with 2631 additions and 30 deletions
4
tests/Fixtures/images/broken.jpg
Normal file
4
tests/Fixtures/images/broken.jpg
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
ЪьЪЮПiц│?°·Р┌╪~
|
||||
й█ФЪвфB÷Н╡┘T┐шЮ╞ф²В╖│r┤@╥╕Е)Jb2В≥╦9щ\╔X∙│╤4Ч░фX█нАAр╠Г╬QЩ#ВS⌠>9Ъб█bАся3<LвВ·лэOл╟о0╬ДП}eqхсЛрgцц}┤
|
||||
ё{У╣Jьyф°уM╥*а≈{Г┌>мй▓║ЦЖБm@р╝'T@2Е!IBцb%┤ы]т@┼Щ]┴CE^r ├;
|
||||
ж÷°█ФЩп
|
||||
|
After Width: | Height: | Size: 204 B |
BIN
tests/Fixtures/images/camera-iptc.jpg
Normal file
BIN
tests/Fixtures/images/camera-iptc.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 437 B |
85
tests/Fixtures/images/generate-fixtures.php
Normal file
85
tests/Fixtures/images/generate-fixtures.php
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Erzeugt die Fixture-Dateien für die Herkunftsprüfung (Bildkennzeichnung
|
||||
* A.5) einmalig mit Imagick. Aufruf: php tests/Fixtures/images/generate-fixtures.php
|
||||
*
|
||||
* Die Dateien werden eingecheckt; das Skript dient nur der Nachvollziehbarkeit.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
$dir = __DIR__;
|
||||
|
||||
function iptcBlock(array $datasets): string
|
||||
{
|
||||
// IPTC-IIM: 0x1C, Record 2, Dataset, Länge (16 Bit), Daten.
|
||||
$block = '';
|
||||
foreach ($datasets as $dataset => $values) {
|
||||
foreach ((array) $values as $value) {
|
||||
$block .= "\x1C\x02".chr($dataset).pack('n', strlen($value)).$value;
|
||||
}
|
||||
}
|
||||
|
||||
return $block;
|
||||
}
|
||||
|
||||
function xmp(array $description): string
|
||||
{
|
||||
return '<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?>'
|
||||
.'<x:xmpmeta xmlns:x="adobe:ns:meta/"><rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">'
|
||||
.'<rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmlns:Iptc4xmpExt="http://iptc.org/std/Iptc4xmpExt/2008-02-29/">'
|
||||
.implode('', $description)
|
||||
.'</rdf:Description></rdf:RDF></x:xmpmeta><?xpacket end="w"?>';
|
||||
}
|
||||
|
||||
function make(string $file, string $format, ?string $iptc = null, ?string $xmp = null): void
|
||||
{
|
||||
$image = new Imagick;
|
||||
$image->newImage(64, 48, new ImagickPixel('#4a6fa5'), $format);
|
||||
$image->setImageFormat($format);
|
||||
|
||||
if ($iptc !== null) {
|
||||
$image->setImageProfile('iptc', $iptc);
|
||||
}
|
||||
|
||||
if ($xmp !== null) {
|
||||
$image->setImageProfile('xmp', $xmp);
|
||||
}
|
||||
|
||||
$image->writeImage($file);
|
||||
$image->clear();
|
||||
echo "written: {$file}\n";
|
||||
}
|
||||
|
||||
// 1. Kamera-JPEG mit klassischem IPTC (Credit, By-line, Copyright).
|
||||
make("{$dir}/camera-iptc.jpg", 'jpeg', iptcBlock([
|
||||
110 => 'Musterfirma GmbH / Anna Beispiel',
|
||||
80 => 'Anna Beispiel',
|
||||
116 => '© 2026 Musterfirma GmbH',
|
||||
]));
|
||||
|
||||
// 2. JPEG mit XMP: KI-generiert (volle IPTC-URI) und Credit als Element.
|
||||
make("{$dir}/xmp-ai-generated.jpg", 'jpeg', null, xmp([
|
||||
'<Iptc4xmpExt:DigitalSourceType>http://cv.iptc.org/newscodes/digitalsourcetype/trainedAlgorithmicMedia</Iptc4xmpExt:DigitalSourceType>',
|
||||
'<photoshop:Credit>KI-Bild / Musterfirma</photoshop:Credit>',
|
||||
]));
|
||||
|
||||
// 3. JPEG mit XMP algorithmicallyEnhanced (Kurzform) und dc:creator-Liste.
|
||||
make("{$dir}/xmp-enhanced.jpg", 'jpeg', null, xmp([
|
||||
'<Iptc4xmpExt:DigitalSourceType>algorithmicallyEnhanced</Iptc4xmpExt:DigitalSourceType>',
|
||||
'<dc:creator><rdf:Seq><rdf:li>Max Mustermann</rdf:li></rdf:Seq></dc:creator>',
|
||||
]));
|
||||
|
||||
// 4. PNG ohne Metadaten.
|
||||
make("{$dir}/plain.png", 'png');
|
||||
|
||||
// 5. WebP mit XMP als Attribute (digitalCapture, dc:rights).
|
||||
make("{$dir}/webp-capture.webp", 'webp', null, xmp([
|
||||
'<Iptc4xmpExt:DigitalSourceType>digitalCapture</Iptc4xmpExt:DigitalSourceType>',
|
||||
'<dc:rights><rdf:Alt><rdf:li xml:lang="x-default">© Beispiel AG</rdf:li></rdf:Alt></dc:rights>',
|
||||
]));
|
||||
|
||||
// 6. Beschädigte Datei: JPEG-Header, dann Müll.
|
||||
file_put_contents("{$dir}/broken.jpg", "\xFF\xD8\xFF\xE0".random_bytes(200));
|
||||
echo "written: {$dir}/broken.jpg\n";
|
||||
BIN
tests/Fixtures/images/plain.png
Normal file
BIN
tests/Fixtures/images/plain.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 146 B |
BIN
tests/Fixtures/images/webp-capture.webp
Normal file
BIN
tests/Fixtures/images/webp-capture.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 678 B |
BIN
tests/Fixtures/images/xmp-ai-generated.jpg
Normal file
BIN
tests/Fixtures/images/xmp-ai-generated.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 948 B |
BIN
tests/Fixtures/images/xmp-enhanced.jpg
Normal file
BIN
tests/Fixtures/images/xmp-enhanced.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 918 B |
Loading…
Add table
Add a link
Reference in a new issue