@massimo-cassandro/minimo 1.33.0 → 1.33.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/README.md +2 -1
  2. package/dev-tools/starter-kit/source_files/{CLAUDE-frontend.md → app/CLAUDE.md} +1 -1
  3. package/dev-tools/starter-kit/source_files/app/assets/api_htaccess +20 -0
  4. package/dev-tools/starter-kit/source_files/{layout-critical.css → app/css/layout.critical.css} +1 -1
  5. package/dev-tools/starter-kit/source_files/app/css/layout.css +0 -0
  6. package/dev-tools/starter-kit/source_files/app/index.js +1 -0
  7. package/dev-tools/starter-kit/source_files/root/webpack-config-modules/inline-critical-css.mjs +41 -0
  8. package/dev-tools/starter-kit/{webpack-config-modules → source_files/root/webpack-config-modules}/purgecss-setup.mjs +1 -1
  9. package/dev-tools/starter-kit/{webpack.config.mjs → source_files/root/webpack.config.mjs} +50 -24
  10. package/dev-tools/starter-kit/starter-install.sh +141 -83
  11. package/dev-tools/stylelint-config/test/test.module.scss +9 -0
  12. package/package.json +1 -1
  13. package/snippets/php/iviewer.php +488 -0
  14. package/snippets/twig-symfony/ViewerController.php +549 -0
  15. package/src/custom-properties.css +29 -0
  16. package/src/minimo.css +3 -0
  17. package/src/utilities/dom-builder/dom-builder.js +9 -5
  18. package/src/web-components/json-table/json-table-component.js +387 -38
  19. package/src/web-components/json-table/json-table-component.module.css +190 -0
  20. package/src/web-components/json-table/json-table.minimo.tokens.mjs +195 -0
  21. package/src/web-components/json-table/readme.md +603 -82
  22. package/src/web-components/json-table/src/cell-content.js +136 -0
  23. package/src/web-components/json-table/src/content-utils.js +103 -0
  24. package/src/web-components/json-table/src/data-types.js +280 -0
  25. package/src/web-components/json-table/src/defaults.js +377 -58
  26. package/src/web-components/json-table/src/get-data.js +105 -10
  27. package/src/web-components/json-table/src/icons.js +80 -0
  28. package/src/web-components/json-table/src/info-section.js +85 -50
  29. package/src/web-components/json-table/src/main-builder.js +175 -52
  30. package/src/web-components/json-table/src/pagination.js +253 -0
  31. package/src/web-components/json-table/src/parse-cols.js +113 -0
  32. package/src/web-components/json-table/src/parse-rows.js +81 -0
  33. package/src/web-components/json-table/src/resolve-params.js +102 -32
  34. package/src/web-components/json-table/src/search.js +71 -0
  35. package/src/web-components/json-table/src/sorting.js +111 -0
  36. package/src/web-components/json-table/src/table-body.js +109 -0
  37. package/src/web-components/json-table/src/table-builder.js +152 -0
  38. package/src/web-components/json-table/src/table-tfoot.js +56 -0
  39. package/src/web-components/json-table/src/table-thead.js +183 -0
  40. package/src/web-components/json-table/src/update-info.js +43 -24
  41. package/src/web-components/s-datatable-component/{docs.md → readme.md} +5 -3
  42. package/src/web-components/s-datatable-component/s-datatable-component.js +1 -1
  43. package/types/global.d.ts +17 -3
  44. package/dev-tools/starter-kit/source_files/app_index.js +0 -1
  45. package/snippets/config-templates/_htaccess +0 -69
  46. package/snippets/config-templates/robots.txt +0 -5
  47. package/snippets/config-templates/rollup.config.mjs +0 -115
  48. /package/dev-tools/starter-kit/source_files/{_root_robots.txt → app/assets/robots.txt} +0 -0
  49. /package/dev-tools/starter-kit/source_files/{_root_htaccess → app/assets/root_htaccess} +0 -0
  50. /package/{src → dev-tools/starter-kit/source_files/app/css}/fonts.css +0 -0
  51. /package/dev-tools/starter-kit/source_files/{favicons-readme.md → app/favicons/favicons-readme.md} +0 -0
  52. /package/dev-tools/starter-kit/{webpack-template.ejs → source_files/app/tpl/index.ejs} +0 -0
  53. /package/dev-tools/starter-kit/source_files/{__project__.code-workspace → root/__project__.code-workspace} +0 -0
  54. /package/dev-tools/starter-kit/source_files/{_browserslistrc → root/_browserslistrc} +0 -0
  55. /package/dev-tools/starter-kit/source_files/{_editorconfig → root/_editorconfig} +0 -0
  56. /package/dev-tools/starter-kit/source_files/{_gitignore → root/_gitignore} +0 -0
  57. /package/dev-tools/starter-kit/source_files/{_prettierrc → root/_prettierrc} +0 -0
  58. /package/dev-tools/starter-kit/source_files/{eslint.config.mjs → root/eslint.config.mjs} +0 -0
  59. /package/dev-tools/starter-kit/source_files/{jsconfig.json → root/jsconfig.json} +0 -0
  60. /package/dev-tools/starter-kit/{package-tpl.json → source_files/root/package-tpl.json} +0 -0
  61. /package/dev-tools/starter-kit/source_files/{stylelint.config.mjs → root/stylelint.config.mjs} +0 -0
  62. /package/dev-tools/starter-kit/{webpack-config-modules → source_files/root/webpack-config-modules}/css-rules.mjs +0 -0
  63. /package/dev-tools/starter-kit/{webpack-config-modules → source_files/root/webpack-config-modules}/get-jsConfig-aliases.mjs +0 -0
  64. /package/dev-tools/starter-kit/{webpack-config-modules → source_files/root/webpack-config-modules}/mini-svg-data-uri-loader.cjs +0 -0
  65. /package/dev-tools/starter-kit/{webpack-config-modules → source_files/root/webpack-config-modules}/postcss.config.mjs +0 -0
  66. /package/dev-tools/starter-kit/{webpack-config-modules → source_files/root/webpack-config-modules}/purgecss-variables-safelist.mjs +0 -0
  67. /package/dev-tools/starter-kit/{webpack-config-modules → source_files/root/webpack-config-modules}/svg-rules.mjs +0 -0
  68. /package/dev-tools/starter-kit/{webpack-config-modules → source_files/root/webpack-config-modules}/svgo.config.mjs +0 -0
  69. /package/snippets/{config-templates/_github → _github}/dependabot.yml +0 -0
  70. /package/snippets/{error-pages/error-pages.js → error-pages.js} +0 -0
  71. /package/snippets/{config-templates/react → react}/offcanvas/offcanvas.jsx +0 -0
  72. /package/snippets/{config-templates/react → react}/offcanvas/offcanvas.module.scss +0 -0
  73. /package/snippets/{config-templates → root}/_hintrc.cjs +0 -0
  74. /package/snippets/{config-templates → root}/_npmignore +0 -0
  75. /package/snippets/{config-templates → root}/_npmrc +0 -0
  76. /package/snippets/{config-templates → root}/_stylelintignore +0 -0
  77. /package/snippets/{config-templates → root}/babel.config--react.json +0 -0
  78. /package/snippets/{config-templates → root}/babel.config--solidJS.json +0 -0
  79. /package/snippets/{ImgViewerController.php → twig-symfony/ImgViewerController.php} +0 -0
  80. /package/snippets/{UnsplashController.php → twig-symfony/UnsplashController.php} +0 -0
  81. /package/snippets/{config-templates → twig-symfony}/_env.local +0 -0
  82. /package/snippets/{error-pages → twig-symfony}/bundles/TwigBundle/Exception/error.html.twig +0 -0
  83. /package/snippets/{flash-messages.html.twig → twig-symfony/flash-messages.html.twig} +0 -0
  84. /package/snippets/{minimo-form-layout.html.twig → twig-symfony/minimo-form-layout.html.twig} +0 -0
@@ -0,0 +1,488 @@
1
+ <?php
2
+
3
+ /*
4
+ * iviewer.php
5
+ * ------------------------------------------------------------------
6
+ * Versione "plain PHP" (nessun framework) del visualizzatore immagini,
7
+ * riscritta usando l'estensione GD al posto di Imagick.
8
+ *
9
+ * Uso: puntare la rotta /iviewer a questo file (con un rewrite del
10
+ * webserver, oppure semplicemente chiamandolo come iviewer.php?src=...).
11
+ *
12
+ * Parametri GET accettati:
13
+ * src (string) percorso dell'immagine, relativo a PUBLIC_DIR
14
+ * crop (string) "x,y,w,h" - ritaglia l'immagine originale prima
15
+ * dell'eventuale ridimensionamento con bb
16
+ * bb (string) bounding box "WxH" che deve contenere l'immagine
17
+ * ridimensionata
18
+ * fd (bool) forza le dimensioni esatte (in coppia con bb,
19
+ * ritagliando l'eccedenza)
20
+ * q (int) qualità di compressione
21
+ * refresh (bool) forza la rigenerazione della cache
22
+ * f (string) formato di output, tra le chiavi di ALLOWED_FORMATS;
23
+ * "auto" (default) sceglie il formato migliore in
24
+ * base al valore di PREFERRED_AUTO_FORMATS
25
+ * ext (bool) mantiene inalterato il formato originale
26
+ * d (bool) forza il download dell'immagine
27
+ *
28
+ * Gestione della trasparenza:
29
+ * il canale alpha eventualmente presente nell'immagine di origine
30
+ * viene mantenuto se il formato di output lo supporta (png, webp,
31
+ * avif); negli altri casi (jpeg, gif) viene riempito con sfondo
32
+ * bianco (#fff).
33
+ *
34
+ * Limiti rispetto alla versione Imagick:
35
+ * - niente supporto TIFF in ingresso (GD non lo gestisce);
36
+ * - "pjpeg" viene scritto come jpeg con interlacciamento attivo,
37
+ * dato che GD non distingue i due formati come mimetype separati.
38
+ * ------------------------------------------------------------------
39
+ */
40
+
41
+ /* ==================================================================
42
+ * CONFIGURAZIONE - da adattare all'ambiente in cui gira lo script
43
+ * ================================================================== */
44
+
45
+ /* cartella "pubblica" a cui è relativo il parametro src */
46
+ define('PUBLIC_DIR', $_SERVER['DOCUMENT_ROOT']);
47
+
48
+ /* cartella dove vengono salvati i file di cache (deve esistere ed
49
+ essere scrivibile dal webserver) */
50
+ define('CACHE_DIR', __DIR__ . '/cache/');
51
+
52
+ /* durata della cache dei file, in secondi (qui: 7 giorni) */
53
+ define('CACHE_LIFETIME', 604800);
54
+
55
+ /* qualità di compressione predefinita */
56
+ define('DEFAULT_COMPRESSION_QUALITY', 60);
57
+
58
+
59
+ /* ==================================================================
60
+ * COSTANTI DI ELABORAZIONE
61
+ * ================================================================== */
62
+
63
+ /* parametri GET accettati dal viewer */
64
+ const ALLOWED_PARAMS = ['bb', 'fd', 'q', 'refresh', 'f', 'ext', 'd', 'crop'];
65
+
66
+ /* formati di output ammessi -> relativo mime type */
67
+ const ALLOWED_FORMATS = [
68
+ 'avif' => 'image/avif',
69
+ 'pjpeg' => 'image/jpeg',
70
+ 'jpeg' => 'image/jpeg',
71
+ 'jpg' => 'image/jpeg',
72
+ 'png' => 'image/png',
73
+ 'gif' => 'image/gif',
74
+ 'webp' => 'image/webp',
75
+ ];
76
+
77
+ /* formati che supportano il canale alpha in output */
78
+ const ALPHA_COMPATIBLE_FORMATS = ['png', 'webp', 'avif'];
79
+
80
+ /* formati preferiti per la scelta automatica, in ordine di preferenza
81
+ (le chiavi devono coincidere con quelle di ALLOWED_FORMATS);
82
+ in loro assenza il formato predefinito è jpeg */
83
+ const PREFERRED_AUTO_FORMATS = ['avif', 'webp'];
84
+
85
+ /* formati di input accettati in aggiunta a quelli di ALLOWED_FORMATS
86
+ (NB: GD non supporta il TIFF, quindi qui resta solo il bmp) */
87
+ const ALLOWED_INPUT = ['bmp'];
88
+
89
+
90
+ /* ==================================================================
91
+ * FUNZIONI DI SUPPORTO
92
+ * ================================================================== */
93
+
94
+ /**
95
+ * Restituisce l'elenco dei formati di OUTPUT effettivamente
96
+ * disponibili con l'installazione di GD corrente.
97
+ */
98
+ function getSupportedOutputFormats(): array
99
+ {
100
+ $supported = ['jpeg', 'jpg', 'pjpeg', 'png', 'gif'];
101
+
102
+ if (function_exists('imagewebp')) {
103
+ $supported[] = 'webp';
104
+ }
105
+ if (function_exists('imageavif')) {
106
+ $supported[] = 'avif';
107
+ }
108
+
109
+ return $supported;
110
+ }
111
+
112
+ /**
113
+ * Crea una risorsa immagine GD leggendo il file indicato, in base
114
+ * alla sua estensione.
115
+ */
116
+ function createImageFromFile(string $filePath, string $ext): \GdImage
117
+ {
118
+ $ext = strtolower($ext);
119
+
120
+ $img = match ($ext) {
121
+ 'jpeg', 'jpg', 'pjpeg' => imagecreatefromjpeg($filePath),
122
+ 'png' => imagecreatefrompng($filePath),
123
+ 'gif' => imagecreatefromgif($filePath),
124
+ 'webp' => function_exists('imagecreatefromwebp') ? imagecreatefromwebp($filePath) : false,
125
+ 'avif' => function_exists('imagecreatefromavif') ? imagecreatefromavif($filePath) : false,
126
+ 'bmp' => function_exists('imagecreatefrombmp') ? imagecreatefrombmp($filePath) : false,
127
+ default => false,
128
+ };
129
+
130
+ if ($img === false) {
131
+ throw new \RuntimeException("Impossibile leggere l'immagine ($ext)");
132
+ }
133
+
134
+ /* mantiene canale alpha e "blend" corretti per i formati che lo supportano */
135
+ imagealphablending($img, false);
136
+ imagesavealpha($img, true);
137
+
138
+ return $img;
139
+ }
140
+
141
+ /**
142
+ * Ritaglia un'immagine secondo x, y, larghezza e altezza indicati.
143
+ */
144
+ function cropImage(\GdImage $img, int $x, int $y, int $w, int $h): \GdImage
145
+ {
146
+ $cropped = imagecrop($img, ['x' => $x, 'y' => $y, 'width' => $w, 'height' => $h]);
147
+
148
+ if ($cropped === false) {
149
+ throw new \RuntimeException('Errore durante il ritaglio dell\'immagine');
150
+ }
151
+
152
+ imagealphablending($cropped, false);
153
+ imagesavealpha($cropped, true);
154
+
155
+ return $cropped;
156
+ }
157
+
158
+ /**
159
+ * Ridimensiona l'immagine esattamente alle dimensioni indicate
160
+ * (equivalente a Imagick::thumbnailImage quando $w e $h sono già
161
+ * calcolati nel rispetto delle proporzioni originali).
162
+ */
163
+ function resizeImage(\GdImage $img, int $w, int $h): \GdImage
164
+ {
165
+ $resized = imagecreatetruecolor($w, $h);
166
+ imagealphablending($resized, false);
167
+ imagesavealpha($resized, true);
168
+
169
+ imagecopyresampled($resized, $img, 0, 0, 0, 0, $w, $h, imagesx($img), imagesy($img));
170
+
171
+ return $resized;
172
+ }
173
+
174
+ /**
175
+ * Ridimensiona l'immagine "a copertura" del bounding box indicato,
176
+ * ritagliando l'eccedenza centrata (equivalente a
177
+ * Imagick::cropThumbnailImage - stile CSS object-fit: cover).
178
+ */
179
+ function coverResizeImage(\GdImage $img, int $w, int $h): \GdImage
180
+ {
181
+ $ow = imagesx($img);
182
+ $oh = imagesy($img);
183
+
184
+ $scale = max($w / $ow, $h / $oh);
185
+ $rw = (int) round($ow * $scale);
186
+ $rh = (int) round($oh * $scale);
187
+
188
+ $resized = resizeImage($img, $rw, $rh);
189
+
190
+ $offsetX = (int) round(($rw - $w) / 2);
191
+ $offsetY = (int) round(($rh - $h) / 2);
192
+
193
+ return cropImage($resized, $offsetX, $offsetY, $w, $h);
194
+ }
195
+
196
+ /**
197
+ * "Appiattisce" la trasparenza di un'immagine su sfondo bianco.
198
+ * Usata per i formati di output che non supportano il canale alpha.
199
+ */
200
+ function flattenImageOnWhite(\GdImage $img): \GdImage
201
+ {
202
+ $w = imagesx($img);
203
+ $h = imagesy($img);
204
+
205
+ $flattened = imagecreatetruecolor($w, $h);
206
+ $white = imagecolorallocate($flattened, 255, 255, 255);
207
+ imagefill($flattened, 0, 0, $white);
208
+
209
+ imagealphablending($flattened, true);
210
+ imagecopy($flattened, $img, 0, 0, 0, 0, $w, $h);
211
+
212
+ return $flattened;
213
+ }
214
+
215
+ /**
216
+ * Scrive un'immagine GD su file, nel formato e con la qualità
217
+ * indicati. Ritorna il mime type effettivamente utilizzato.
218
+ */
219
+ function writeImage(\GdImage $img, string $format, string $destPath, int $quality): string
220
+ {
221
+ $mimeType = ALLOWED_FORMATS[$format];
222
+
223
+ switch ($format) {
224
+ case 'jpeg':
225
+ case 'jpg':
226
+ imagejpeg($img, $destPath, $quality);
227
+ break;
228
+
229
+ case 'pjpeg':
230
+ /* progressive jpeg: interlacciamento attivo prima del salvataggio */
231
+ imageinterlace($img, true);
232
+ imagejpeg($img, $destPath, $quality);
233
+ break;
234
+
235
+ case 'png':
236
+ /* la qualità di Imagick (0-100) va convertita nel livello di
237
+ compressione di GD (0-9, dove 9 = massima compressione) */
238
+ $level = (int) round((100 - $quality) / 100 * 9);
239
+ imagepng($img, $destPath, max(0, min(9, $level)));
240
+ break;
241
+
242
+ case 'gif':
243
+ imagegif($img, $destPath);
244
+ break;
245
+
246
+ case 'webp':
247
+ imagewebp($img, $destPath, $quality);
248
+ break;
249
+
250
+ case 'avif':
251
+ /* imageavif(immagine, percorso, qualità, velocità) */
252
+ imageavif($img, $destPath, $quality);
253
+ break;
254
+
255
+ default:
256
+ throw new \RuntimeException("Formato di output non gestito: $format");
257
+ }
258
+
259
+ return $mimeType;
260
+ }
261
+
262
+
263
+ /* ==================================================================
264
+ * ESECUZIONE
265
+ * ================================================================== */
266
+
267
+ $get = $_GET;
268
+ $src = $get['src'] ?? null;
269
+
270
+ if (!$src) {
271
+ http_response_code(404);
272
+ exit('Parametro mancante! (src)');
273
+ }
274
+
275
+ /* formati di output disponibili con l'installazione GD corrente */
276
+ $supportedFormats = getSupportedOutputFormats();
277
+
278
+ /* formati accettati dal browser */
279
+ $accept = $_SERVER['HTTP_ACCEPT'] ?? '';
280
+
281
+ /* formato "auto" predefinito */
282
+ if (!isset($get['f'])) {
283
+ $get['f'] = 'auto';
284
+ }
285
+
286
+ /* formato "auto" -> sceglie il formato migliore per il browser, se disponibile */
287
+ if (strtolower($get['f']) === 'auto') {
288
+ foreach (PREFERRED_AUTO_FORMATS as $f) {
289
+ if (
290
+ strpos($accept, ALLOWED_FORMATS[$f]) !== false &&
291
+ array_key_exists($f, ALLOWED_FORMATS) &&
292
+ in_array($f, $supportedFormats, true)
293
+ ) {
294
+ $get['f'] = $f;
295
+ break;
296
+ }
297
+ }
298
+ }
299
+
300
+ $params = cleanParams($get);
301
+
302
+ /* parte del nome file di cache formata da tutti i parametri concatenati */
303
+ $cacheParamsStr = '';
304
+ foreach ($params as $kg => $g) {
305
+ if ($kg !== 'refresh') {
306
+ $cacheParamsStr .= '_' . $kg . $g;
307
+ }
308
+ }
309
+ $cacheParamsStr = str_replace(',', '_', $cacheParamsStr);
310
+
311
+ $filePath = PUBLIC_DIR . $src;
312
+
313
+ if (!is_file($filePath)) {
314
+ http_response_code(404);
315
+ exit('Immagine non trovata!');
316
+ }
317
+
318
+ $fileExt = strtolower(pathinfo($filePath, PATHINFO_EXTENSION));
319
+ $fileCacheName = str_replace('/', '_', $src);
320
+
321
+ $disposition = isset($params['d']) ? 'attachment' : 'inline';
322
+
323
+ /* elaborazione immagine: solo se sono presenti parametri ed è un
324
+ formato trattabile da GD (altrimenti viene restituito il file
325
+ originale, ad es. per gli svg) */
326
+ if (count($params) && in_array($fileExt, array_merge(ALLOWED_INPUT, array_keys(ALLOWED_FORMATS)), true)) {
327
+
328
+ /* scelta del formato di output */
329
+ if (isset($params['f']) && in_array($params['f'], array_keys(ALLOWED_FORMATS), true) && in_array($params['f'], $supportedFormats, true)) {
330
+ $format = $params['f'];
331
+ } elseif (isset($params['ext']) && $params['ext'] && in_array($fileExt, array_keys(ALLOWED_FORMATS), true)) {
332
+ $format = $fileExt;
333
+ } else {
334
+ $format = 'jpeg';
335
+ }
336
+
337
+ $fileCacheName .= $cacheParamsStr . '.' . ($format === 'pjpeg' ? 'jpeg' : $format);
338
+ $cacheFile = CACHE_DIR . $fileCacheName;
339
+
340
+ $compressionQuality = !empty($params['q']) ? (int) $params['q'] : DEFAULT_COMPRESSION_QUALITY;
341
+
342
+ /* rigenera l'immagine se: manca la cache, è scaduta, o è richiesto il refresh */
343
+ if (!file_exists($cacheFile) || (filemtime($cacheFile) + CACHE_LIFETIME) < time() || isset($params['refresh'])) {
344
+
345
+ $img = createImageFromFile($filePath, $fileExt);
346
+
347
+ if (isset($params['crop']) || isset($params['bb'])) {
348
+ $ow = imagesx($img);
349
+ $oh = imagesy($img);
350
+ }
351
+
352
+ if (isset($params['crop'])) {
353
+ $cropParams = array_map(fn($val) => (int) trim($val), explode(',', $params['crop']));
354
+
355
+ if (count($cropParams) !== 4 || min($cropParams) < 0) {
356
+ http_response_code(400);
357
+ exit('Errore di configurazione! (crop 1)');
358
+ }
359
+
360
+ [$x, $y, $cropW, $cropH] = $cropParams;
361
+
362
+ if ($x + $cropW > $ow || $y + $cropH > $oh) {
363
+ http_response_code(400);
364
+ exit('Errore di configurazione! (crop 2)');
365
+ }
366
+
367
+ $img = cropImage($img, $x, $y, $cropW, $cropH);
368
+
369
+ /* nuove dimensioni dell'immagine (corrispondono a $cropW e $cropH) */
370
+ $ow = imagesx($img);
371
+ $oh = imagesy($img);
372
+ }
373
+
374
+ $w = 0;
375
+ $h = 0;
376
+ $bbHasX = false;
377
+
378
+ if (isset($params['bb'])) {
379
+ $bbParam = strtolower($params['bb']);
380
+ $bbHasX = strpos($bbParam, 'x') !== false;
381
+
382
+ if ($bbHasX) {
383
+ /* dimensioni richieste; se un parametro è mancante viene
384
+ impostato a 0 e non ha influenza nelle elaborazioni successive */
385
+ [$w, $h] = array_map(fn($val) => $val ? (int) $val : 0, explode('x', $bbParam));
386
+
387
+ /* le dimensioni richieste non possono superare quelle originali */
388
+ if ($w > $ow) $w = $ow;
389
+ if ($h > $oh) $h = $oh;
390
+ }
391
+ }
392
+
393
+ /* fd => l'immagine deve rispettare con precisione le dimensioni
394
+ richieste (necessari entrambi i parametri di bb) */
395
+ if (isset($params['bb']) && isset($params['fd']) && $bbHasX && $w > 0 && $h > 0) {
396
+
397
+ if ($ow < $w && $oh < $h) {
398
+ $nb = $ow;
399
+ $nh = $oh;
400
+
401
+ $r = $w > $h ? $h / $w : $w / $h;
402
+
403
+ if ($nb > $nh) {
404
+ $nh = $r * $nb;
405
+ } else {
406
+ $nb = $r * $nh;
407
+ }
408
+
409
+ $w = (int) round($nb);
410
+ $h = (int) round($nh);
411
+ }
412
+
413
+ $img = coverResizeImage($img, $w, $h);
414
+
415
+ } elseif (isset($params['bb']) && $bbHasX) {
416
+
417
+ if ($w !== 0 || $h !== 0) {
418
+ $nb = $ow;
419
+ $nh = $oh;
420
+
421
+ if ($w !== 0 && $w < $nb) {
422
+ $nh = ($w / $nb) * $nh;
423
+ $nb = $w;
424
+ }
425
+
426
+ if ($h !== 0 && $h < $nh) {
427
+ $nb = ($h / $nh) * $nb;
428
+ $nh = $h;
429
+ }
430
+
431
+ $w = (int) round($nb);
432
+ $h = (int) round($nh);
433
+ }
434
+
435
+ $img = resizeImage($img, $w, $h);
436
+ }
437
+
438
+ /* trasparenza: mantenuta se il formato di output la supporta,
439
+ altrimenti riempita con sfondo bianco */
440
+ if (!in_array($format, ALPHA_COMPATIBLE_FORMATS, true)) {
441
+ $img = flattenImageOnWhite($img);
442
+ }
443
+
444
+ $mimeType = writeImage($img, $format, $cacheFile, $compressionQuality);
445
+
446
+ imagedestroy($img);
447
+
448
+ } else {
449
+ $mimeType = ALLOWED_FORMATS[$format];
450
+ }
451
+
452
+ } else {
453
+
454
+ /* nessuna elaborazione richiesta: restituisce il file originale */
455
+ $cacheFile = $filePath;
456
+ $fileCacheName = str_replace('/', '_', $src);
457
+
458
+ $mimeType = $fileExt === 'svg' ? 'image/svg+xml' : (mime_content_type($filePath) ?: 'application/octet-stream');
459
+ }
460
+
461
+ /* invio del file al browser */
462
+ header('Content-Type: ' . $mimeType);
463
+ header('Content-Disposition: ' . $disposition . '; filename="' . $fileCacheName . '"');
464
+ header('Content-Length: ' . filesize($cacheFile));
465
+
466
+ readfile($cacheFile);
467
+ exit;
468
+
469
+
470
+ /* ==================================================================
471
+ * cleanParams - filtra e normalizza i parametri GET
472
+ * ================================================================== */
473
+ function cleanParams(array $params): array
474
+ {
475
+ foreach ($params as $key => $value) {
476
+ if (!in_array($key, ALLOWED_PARAMS, true)) {
477
+ unset($params[$key]);
478
+ } elseif ($value) {
479
+ $params[$key] = trim($value);
480
+ }
481
+ }
482
+
483
+ if (isset($params['f'])) {
484
+ $params['f'] = strtolower($params['f']);
485
+ }
486
+
487
+ return $params;
488
+ }