diff --git a/Dockerfile b/Dockerfile index 26debdb..3540f0f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -22,12 +22,6 @@ RUN install-php-extensions \ apcu \ sodium -RUN apt-get update && apt-get install -y --no-install-recommends \ - fonts-dejavu-core \ - fontconfig \ - && fc-cache -f -v \ - && rm -rf /var/lib/apt/lists/* - RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini" RUN printf '[opcache]\nopcache.enable=1\nopcache.memory_consumption=256\nopcache.max_accelerated_files=20000\nopcache.validate_timestamps=0\n' \ > "$PHP_INI_DIR/conf.d/opcache.ini" diff --git a/README.md b/README.md index d9ece09..9feef5b 100644 --- a/README.md +++ b/README.md @@ -287,11 +287,12 @@ git push origin v2026.01 --- -## Game Documentation +## Documentation -For detailed information about game mechanics, bonus systems, and scoring rules, see the [docs](./docs/) directory: +For detailed information about game mechanics, bonus systems, fonts, and other technical details, see the [docs](./docs/) directory: - **[Bonus Points System](./docs/game-mechanics/BONUS_POINTS_SYSTEM.md)** — Complete reference for all bonus point types, calculation rules, and implementation details +- **[Fonts](./docs/FONTS.md)** — TrueType fonts used for server-side image generation --- diff --git a/assets/fonts/Carlito-Bold.ttf b/assets/fonts/Carlito-Bold.ttf new file mode 100644 index 0000000..67543b5 Binary files /dev/null and b/assets/fonts/Carlito-Bold.ttf differ diff --git a/assets/js/components/BattleDialog.jsx b/assets/js/components/BattleDialog.jsx index 05405ca..15164e1 100644 --- a/assets/js/components/BattleDialog.jsx +++ b/assets/js/components/BattleDialog.jsx @@ -12,7 +12,9 @@ import { array } from 'prop-types'; import { formatDuration } from '@global-utils/format'; import Dialog from '@mui/material/Dialog'; import { createTheme, styled, ThemeProvider } from '@mui/material/styles'; -import { Avatar, BonusPoints, StatRow } from '@global-components'; +import { Avatar } from './battle-dialog/Avatar'; +import { BonusPoints } from './battle-dialog/BonusPoints'; +import { StatRow } from './battle-dialog/StatRow'; const darkTheme = createTheme({ palette: { mode: 'dark' } }); @@ -69,11 +71,11 @@ export const BattleDialog = ({ games }) => { const endReason = resign ? `${resign.charAt(0).toUpperCase() + resign.slice(1)} resigned` : 26 <= maxPoints ? 'Points' : 'Abandoned'; - const canShare = !canContinue; const bothRegistered = game.bothRegistered; - const canContinue = bothRegistered && !resign && 26 > maxPoints; - const playUrl = `${window.location.origin}/play/${game.uuid}`; const shareUrl = `${window.location.origin}/battle/${game.uuid}`; + const canContinue = bothRegistered && !resign && 26 > maxPoints; + const canShare = !canContinue; + const playUrl = `${window.location.origin}/play/${game.uuid}`; const duration = formatDuration(game.created, game.date); const pointDiff = Math.abs((game.redPoints ?? 0) - (game.bluePoints ?? 0)); diff --git a/config/services.yaml b/config/services.yaml index 68ddf46..d42d84a 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -28,6 +28,7 @@ services: App\Service\BattleCardGenerator: arguments: $cacheDir: '%kernel.project_dir%/var/og-cache' + $fontPath: '%kernel.project_dir%/assets/fonts/Carlito-Bold.ttf' $minioMediaStorage: '@mineseeker.media.storage' Aws\S3\S3Client: diff --git a/docs/FONTS.md b/docs/FONTS.md new file mode 100644 index 0000000..47c6ec6 --- /dev/null +++ b/docs/FONTS.md @@ -0,0 +1,47 @@ +# Font Files + +This directory contains TrueType Font (TTF) files used for server-side image generation with PHP GD. + +## Carlito-Bold.ttf + +- **Font:** Carlito Bold +- **Source:** Google Fonts (Carlito Project) +- **License:** SIL Open Font License 1.1 +- **URL:** https://github.com/googlefonts/carlito +- **Usage:** Used by `BattleCardGenerator` service for generating battle card OG images +- **Note:** Carlito is a metric-compatible font family to Calibri + +## Why TTF instead of @fontsource? + +The `@fontsource` npm packages provide WOFF/WOFF2 files for web usage, but PHP's GD library (`imagettftext()`) requires TrueType Font (TTF) files for server-side text rendering. + +## Alternatives + +If you want to use a different font: + +1. **Install system fonts:** + ```bash + # Find available TTF fonts + find /usr/share/fonts -name "*.ttf" -type f + + # Copy desired font + cp /usr/share/fonts/path/to/Font-Bold.ttf assets/fonts/ + ``` + +2. **Download from Google Fonts:** + ```bash + # Visit https://fonts.google.com + # Download the font family + # Extract the TTF file from the zip + ``` + +3. **Update service configuration:** + Edit `config/services.yaml` and update the `$fontPath` parameter. + +## Cache Clearing + +After changing fonts, clear the OG image cache: + +```bash +rm -rf var/og-cache/* +``` diff --git a/src/Service/BattleCardGenerator.php b/src/Service/BattleCardGenerator.php index 7f3b1a6..abb2f3b 100644 --- a/src/Service/BattleCardGenerator.php +++ b/src/Service/BattleCardGenerator.php @@ -34,11 +34,11 @@ final class BattleCardGenerator { private const int WIDTH = 1200; private const int HEIGHT = 630; - private const string FONT = '/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf'; private const int AVATAR_SIZE = 120; public function __construct( private readonly string $cacheDir, + private readonly string $fontPath, private readonly FilesystemOperator $minioMediaStorage, private readonly LoggerInterface $logger, ) { @@ -159,7 +159,7 @@ final class BattleCardGenerator $redBonusPoints = $game->redBonusPoints ?? 0; $blueBonusPoints = $game->blueBonusPoints ?? 0; $bonusText = number_format((float)$redBonusPoints, 1, '.', '') . ' * : * ' . number_format((float)$blueBonusPoints, 1, '.', ''); - $this->centeredText($im, $bonusText, 24, self::WIDTH / 2, 425, $gold); + $this->centeredText($im, $bonusText, 24, self::WIDTH / 2, 445, $gold); if ($winner === 'red') { $resultText = $redName . ' wins'; @@ -176,11 +176,11 @@ final class BattleCardGenerator } if ($resultText !== '') { - $this->centeredText($im, $resultText, 30, self::WIDTH / 2, 475, $resultColor); + $this->centeredText($im, $resultText, 30, self::WIDTH / 2, 495, $resultColor); } if ($resign) { - $this->centeredText($im, ucfirst($resign) . ' resigned', 18, self::WIDTH / 2, 508, $muted); + $this->centeredText($im, ucfirst($resign) . ' resigned', 18, self::WIDTH / 2, 528, $muted); } $this->centeredText($im, 'mineseeker.hu', 16, self::WIDTH / 2, self::HEIGHT - 20, $muted); @@ -273,23 +273,23 @@ final class BattleCardGenerator /** Draw initials */ $initials = mb_strtoupper(mb_substr($name, 0, 2)); $fontSize = 48; - $bbox = imagettfbbox($fontSize, 0, self::FONT, $initials); + $bbox = imagettfbbox($fontSize, 0, $this->fontPath, $initials); $textW = $bbox[2] - $bbox[0]; $textH = $bbox[1] - $bbox[7]; $textX = $cx - $textW / 2; $textY = $cy + $textH / 2; $white = imagecolorallocate($im, 255, 255, 255); - imagettftext($im, $fontSize, 0, (int)$textX, (int)$textY, $white, self::FONT, $initials); + imagettftext($im, $fontSize, 0, (int)$textX, (int)$textY, $white, $this->fontPath, $initials); } } /** Render text centered on $cx. */ private function centeredText(GdImage $im, string $text, int $size, int $cx, int $y, int $color): void { - $bbox = imagettfbbox($size, 0, self::FONT, $text); + $bbox = imagettfbbox($size, 0, $this->fontPath, $text); $w = $bbox[2] - $bbox[0]; - imagettftext($im, $size, 0, (int)($cx - $w / 2), $y, $color, self::FONT, $text); + imagettftext($im, $size, 0, (int)($cx - $w / 2), $y, $color, $this->fontPath, $text); } /** Render text centered on $cx, shrinking font size to fit $maxWidth. */ @@ -302,7 +302,7 @@ final class BattleCardGenerator int $color, int $maxWidth ): void { - $bbox = imagettfbbox($size, 0, self::FONT, $text); + $bbox = imagettfbbox($size, 0, $this->fontPath, $text); $w = $bbox[2] - $bbox[0]; if ($w > $maxWidth) { $size = (int)($size * $maxWidth / $w);