Fonts: a new asset type, picked from the server or dropped in, worn by text objects (#74)
game-maker / suite (push) Successful in 9m10s
game-maker / suite (push) Successful in 9m10s
A font asset holds a file and nothing else, the twin of a texture: the server keeps it
content-addressed under data/fonts/{sha256}.{ext}, with the format read from the file's own
four-byte signature (finfo reports fonts inconsistently across libmagic versions). A text
wears one through its ordinary Font property, so overrides, reset, undo and the Properties
node come for free; no font means the editor's own typeface.
The picker lists everything the server holds and renders each row in the face it offers.
The family name is gm-font-<hash16>, never the name inside the file, so two "Regular" cannot
collide. WorldViewport.syncText is the single place that draws it, which covers scene, game
preview and play at once.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
28c0789b19
commit
86b5363a01
35 files changed
+1451
-85
No files matched your search
+44
-2
@@ -17,10 +17,15 @@ declare(strict_types=1);
|
||||
* POST ?r=textures raw image bytes → {file,width,height,bytes}
|
||||
* DELETE ?r=textures/{file} → 204
|
||||
*
|
||||
* Stored images are served straight from `data/textures/` as static files; the API only
|
||||
* writes them.
|
||||
* GET ?r=fonts → [{file,bytes,updatedAt}]
|
||||
* POST ?r=fonts raw font bytes → {file,bytes}
|
||||
* DELETE ?r=fonts/{file} → 204
|
||||
*
|
||||
* Stored images and fonts are served straight from `data/textures/` and `data/fonts/` as
|
||||
* static files; the API only writes them.
|
||||
*/
|
||||
|
||||
require __DIR__ . '/lib/FontStore.php';
|
||||
require __DIR__ . '/lib/Response.php';
|
||||
require __DIR__ . '/lib/Store.php';
|
||||
require __DIR__ . '/lib/TextureStore.php';
|
||||
@@ -30,6 +35,9 @@ const MAX_BODY_BYTES = 8 * 1024 * 1024;
|
||||
/** Upload ceiling for a single image. The editor downscales larger pictures before sending. */
|
||||
const MAX_TEXTURE_BYTES = 4 * 1024 * 1024;
|
||||
|
||||
/** Upload ceiling for a single font. A font cannot be shrunk on the way, so this is a plain limit. */
|
||||
const MAX_FONT_BYTES = 4 * 1024 * 1024;
|
||||
|
||||
set_error_handler(static function (int $severity, string $message, string $file, int $line): bool {
|
||||
throw new ErrorException($message, 0, $severity, $file, $line);
|
||||
});
|
||||
@@ -126,6 +134,39 @@ function routeTextures(array $parts, string $method): never
|
||||
Response::error('Method not allowed', 405);
|
||||
}
|
||||
|
||||
/** @param list<string> $parts */
|
||||
function routeFonts(array $parts, string $method): never
|
||||
{
|
||||
$store = new FontStore(dirname(__DIR__) . '/data/fonts');
|
||||
|
||||
if (count($parts) === 1) {
|
||||
if ($method === 'GET') {
|
||||
Response::json($store->list());
|
||||
}
|
||||
if ($method === 'POST') {
|
||||
$bytes = Response::rawBody();
|
||||
if (strlen($bytes) > MAX_FONT_BYTES) {
|
||||
Response::error('Font must be at most ' . (MAX_FONT_BYTES / 1024 / 1024) . ' MB', 413);
|
||||
}
|
||||
try {
|
||||
Response::json($store->store($bytes), 201);
|
||||
} catch (InvalidFontException $e) {
|
||||
Response::error($e->getMessage(), 422);
|
||||
}
|
||||
}
|
||||
Response::error('Method not allowed', 405);
|
||||
}
|
||||
|
||||
if ($method === 'DELETE') {
|
||||
if (!$store->delete($parts[1])) {
|
||||
Response::error('Font not found', 404);
|
||||
}
|
||||
Response::noContent();
|
||||
}
|
||||
|
||||
Response::error('Method not allowed', 405);
|
||||
}
|
||||
|
||||
try {
|
||||
if ((int) ($_SERVER['CONTENT_LENGTH'] ?? 0) > MAX_BODY_BYTES) {
|
||||
Response::error('Request body too large', 413);
|
||||
@@ -142,6 +183,7 @@ try {
|
||||
match ($parts[0]) {
|
||||
'projects' => routeProjects($parts, $method),
|
||||
'textures' => routeTextures($parts, $method),
|
||||
'fonts' => routeFonts($parts, $method),
|
||||
default => Response::error('Not found', 404),
|
||||
};
|
||||
} catch (Throwable $e) {
|
||||
|
||||
Reference in new issue
Block a user