diff --git a/config/functions_site.php b/config/functions_site.php
index 93eba77..f9d96c9 100644
--- a/config/functions_site.php
+++ b/config/functions_site.php
@@ -104,6 +104,51 @@ function site_img_url($value, $base_dir): string {
return base_url(rtrim((string) $base_dir, '/') . '/' . ltrim($value, '/'));
}
+// Same value/$base_dir resolution as site_img_url(), but to a filesystem path
+// (for getimagesize()) instead of a URL. Empty string for a remote http(s)
+// image or anything that doesn't resolve to a local file.
+function site_img_path($value, $base_dir): string {
+ $value = (string) $value;
+ if ($value === '' || substr($value, 0, 4) === 'http') return '';
+ $root = dirname(__DIR__);
+ $rel = (substr($value, 0, 8) === 'uploads/')
+ ? $value
+ : rtrim((string) $base_dir, '/') . '/' . ltrim($value, '/');
+ return $root . '/' . $rel;
+}
+
+// width="" height="" attribute string for an uploaded image, read from the
+// file itself (uploads aren't resized to one fixed size — see
+// config/functions_images.php — so this can't be hardcoded). Empty string
+// when the file is missing/unreadable, so the just renders without the
+// attributes rather than with bogus 0x0 ones.
+function site_img_dims_attr($value, $base_dir): string {
+ $path = site_img_path($value, $base_dir);
+ if ($path === '') return '';
+ return svg_img_dims_attr($path);
+}
+
+// width="" height="" for any local raster or SVG file. getimagesize() covers
+// raster; for SVG (which it doesn't decode) this reads the width/height
+// attributes and falls back to the viewBox aspect ratio when only that is
+// set (e.g. the site logos). Empty string when the file is missing or has no
+// size info to go on.
+function svg_img_dims_attr(string $path): string {
+ if (!is_file($path)) return '';
+ if (strtolower(pathinfo($path, PATHINFO_EXTENSION)) !== 'svg') {
+ $info = @getimagesize($path);
+ return $info ? 'width="' . (int) $info[0] . '" height="' . (int) $info[1] . '"' : '';
+ }
+ $svg = (string) @file_get_contents($path, false, null, 0, 4096);
+ if (preg_match('/\bwidth="([\d.]+)"/', $svg, $mw) && preg_match('/\bheight="([\d.]+)"/', $svg, $mh)) {
+ return 'width="' . (int) round((float) $mw[1]) . '" height="' . (int) round((float) $mh[1]) . '"';
+ }
+ if (preg_match('/\bviewBox="[\d.\-]+\s+[\d.\-]+\s+([\d.]+)\s+([\d.]+)"/', $svg, $mv)) {
+ return 'width="' . (int) round((float) $mv[1]) . '" height="' . (int) round((float) $mv[2]) . '"';
+ }
+ return '';
+}
+
// Schedule joined to modalidades (name/colour/icon), optionally filtered by
// local (e.g. 'crossfit'). Mirrors Horario_model::getAll().
function site_horario(PDO $pdo, string $local = '', ?int $gym_id = null): array {
@@ -194,5 +239,7 @@ function modalidade_icon_html(?string $icon): string {
return $svg;
}
}
- return '
';
+ $dims = site_img_dims_attr($icon, 'uploads/modalidades/icons');
+ return '
';
}
diff --git a/site/includes/horario.php b/site/includes/horario.php
index 9b9a7ee..f4fcef1 100644
--- a/site/includes/horario.php
+++ b/site/includes/horario.php
@@ -69,7 +69,7 @@ foreach ($rows as $d) { $grid[$d['dia_semana']][$d['hora_inicio']][] = $d; }