From 775d611cfa8528a2cf250b389bd031d3eb182939 Mon Sep 17 00:00:00 2001 From: Jan-Niclas Loosen Date: Mon, 19 Oct 2026 18:02:29 +0200 Subject: [PATCH] feat: FontSignature (Magic Bytes) und FontNameRules (Namensregeln eigener Schriften) Co-Authored-By: Claude Sonnet 5.5 --- .../Divi/Design/FontNameRules.php | 74 +++++++++++++++++++ .../Divi/Design/FontSignature.php | 32 ++++++++ .../Divi/Design/FontNameRulesTest.php | 64 ++++++++++++++++ .../Divi/Design/FontSignatureTest.php | 31 ++++++++ 4 files changed, 201 insertions(+) create mode 100644 src/Integrations/Divi/Design/FontNameRules.php create mode 100644 src/Integrations/Divi/Design/FontSignature.php create mode 100644 tests/Unit/Integrations/Divi/Design/FontNameRulesTest.php create mode 100644 tests/Unit/Integrations/Divi/Design/FontSignatureTest.php diff --git a/src/Integrations/Divi/Design/FontNameRules.php b/src/Integrations/Divi/Design/FontNameRules.php new file mode 100644 index 0000000..869fb4b --- /dev/null +++ b/src/Integrations/Divi/Design/FontNameRules.php @@ -0,0 +1,74 @@ + $reserved Names of Google, web-safe and uploaded fonts (FontCatalog::reserved()). + * @return string|null null = valid, else the problem (German). + */ + public static function check(string $name, array $reserved, int $maxLength = self::MAX_LENGTH): ?string + { + if (trim($name) === '') { + return sprintf('Name fehlt (1–%d Zeichen)', $maxLength); + } + + if (preg_match(self::ALLOWED, $name) !== 1) { + $stored = trim((string) preg_replace(self::DIVI_STRIPS, '', $name)); + + return 'nur Buchstaben A–Z/a–z, Ziffern, Leerzeichen, _ und - erlaubt (' + . ($stored === '' ? 'Divi ließe nichts übrig)' : sprintf('Divi würde „%s“ speichern)', $stored)); + } + + if ($name !== trim($name)) { + return 'ohne Leerzeichen am Anfang oder Ende'; + } + + if (strlen($name) > $maxLength) { + return sprintf('höchstens %d Zeichen (%d angegeben)', $maxLength, strlen($name)); + } + + $taken = self::taken($name, $reserved); + + return $taken === null ? null : sprintf( + '„%s“ gibt es schon als Google-, Web-Safe- oder hochgeladene Schrift (ohne Beachtung der ' + . 'Groß-/Kleinschreibung)', + $taken + ); + } + + /** + * @param list $reserved + * @return string|null The existing name $name collides with (case-insensitive), null when it is free. + */ + public static function taken(string $name, array $reserved): ?string + { + $lower = strtolower($name); + foreach ($reserved as $existing) { + if (strtolower($existing) === $lower) { + return $existing; + } + } + + return null; + } +} diff --git a/src/Integrations/Divi/Design/FontSignature.php b/src/Integrations/Divi/Design/FontSignature.php new file mode 100644 index 0000000..f67e3a8 --- /dev/null +++ b/src/Integrations/Divi/Design/FontSignature.php @@ -0,0 +1,32 @@ + 'woff2', + 'wOFF' => 'woff', + "\x00\x01\x00\x00" => 'ttf', + 'true' => 'ttf', + 'OTTO' => 'otf', + ]; + + /** @return 'woff2'|'woff'|'ttf'|'otf'|null */ + public static function detect(string $header): ?string + { + return self::MAGIC[substr($header, 0, self::HEADER_BYTES)] ?? null; + } +} diff --git a/tests/Unit/Integrations/Divi/Design/FontNameRulesTest.php b/tests/Unit/Integrations/Divi/Design/FontNameRulesTest.php new file mode 100644 index 0000000..dda2197 --- /dev/null +++ b/tests/Unit/Integrations/Divi/Design/FontNameRulesTest.php @@ -0,0 +1,64 @@ +