@massimo-cassandro/minimo 1.32.4 → 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.
- package/README.md +2 -1
- package/dev-tools/starter-kit/source_files/_private/pwd.md +16 -0
- package/dev-tools/starter-kit/source_files/_private/sf.md +12 -0
- package/dev-tools/starter-kit/source_files/{CLAUDE-frontend.md → app/CLAUDE.md} +2 -1
- package/dev-tools/starter-kit/source_files/app/assets/api_htaccess +20 -0
- package/dev-tools/starter-kit/source_files/{layout-critical.css → app/css/layout.critical.css} +1 -1
- package/dev-tools/starter-kit/source_files/app/css/layout.css +0 -0
- package/dev-tools/starter-kit/source_files/app/index.js +1 -0
- package/dev-tools/starter-kit/{package-tpl.json → source_files/root/package-tpl.json} +1 -1
- package/dev-tools/starter-kit/source_files/{stylelint.config.mjs → root/stylelint.config.mjs} +1 -1
- package/dev-tools/starter-kit/source_files/root/webpack-config-modules/inline-critical-css.mjs +41 -0
- package/dev-tools/starter-kit/{webpack-config-modules → source_files/root/webpack-config-modules}/purgecss-setup.mjs +1 -1
- package/dev-tools/starter-kit/{webpack.config.mjs → source_files/root/webpack.config.mjs} +56 -38
- package/dev-tools/starter-kit/starter-install.sh +233 -81
- package/dev-tools/stylelint-config/README.md +45 -0
- package/dev-tools/stylelint-config/index.js +184 -0
- package/dev-tools/stylelint-config/test/test.module.scss +9 -0
- package/package.json +11 -2
- package/snippets/php/iviewer.php +488 -0
- package/snippets/twig-symfony/ViewerController.php +549 -0
- package/src/custom-properties.css +36 -0
- package/src/minimo.css +3 -0
- package/src/utilities/dom-builder/dom-builder.js +9 -5
- package/src/web-components/json-table/json-table-component.js +632 -0
- package/src/web-components/json-table/json-table-component.module.css +247 -0
- package/src/web-components/json-table/json-table.minimo.tokens.mjs +243 -0
- package/src/web-components/json-table/readme.md +764 -0
- package/src/web-components/json-table/src/cell-content.js +136 -0
- package/src/web-components/json-table/src/content-utils.js +103 -0
- package/src/web-components/json-table/src/data-types.js +280 -0
- package/src/web-components/json-table/src/defaults.js +424 -0
- package/src/web-components/json-table/src/get-data.js +152 -0
- package/src/web-components/json-table/src/icons.js +80 -0
- package/src/web-components/json-table/src/info-section.js +107 -0
- package/src/web-components/json-table/src/main-builder.js +218 -0
- package/src/web-components/json-table/src/pagination.js +253 -0
- package/src/web-components/json-table/src/parse-cols.js +113 -0
- package/src/web-components/json-table/src/parse-rows.js +81 -0
- package/src/web-components/json-table/src/resolve-params.js +166 -0
- package/src/web-components/json-table/src/search.js +71 -0
- package/src/web-components/json-table/src/sorting.js +111 -0
- package/src/web-components/json-table/src/table-body.js +109 -0
- package/src/web-components/json-table/src/table-builder.js +152 -0
- package/src/web-components/json-table/src/table-tfoot.js +56 -0
- package/src/web-components/json-table/src/table-thead.js +183 -0
- package/src/web-components/json-table/src/update-info.js +67 -0
- package/src/web-components/s-datatable-component/{docs.md → readme.md} +67 -3
- package/src/web-components/s-datatable-component/s-datatable-component.js +1 -1
- package/types/global.d.ts +17 -3
- package/dev-tools/starter-kit/source_files/app_index.js +0 -1
- package/snippets/config-templates/_htaccess +0 -69
- package/snippets/config-templates/robots.txt +0 -5
- package/snippets/config-templates/rollup.config.mjs +0 -115
- /package/dev-tools/starter-kit/source_files/{_root_robots.txt → app/assets/robots.txt} +0 -0
- /package/dev-tools/starter-kit/source_files/{_root_htaccess → app/assets/root_htaccess} +0 -0
- /package/{src → dev-tools/starter-kit/source_files/app/css}/fonts.css +0 -0
- /package/dev-tools/starter-kit/source_files/{favicons-readme.md → app/favicons/favicons-readme.md} +0 -0
- /package/dev-tools/starter-kit/{webpack-template.ejs → source_files/app/tpl/index.ejs} +0 -0
- /package/dev-tools/starter-kit/source_files/{__project__.code-workspace → root/__project__.code-workspace} +0 -0
- /package/dev-tools/starter-kit/source_files/{_browserslistrc → root/_browserslistrc} +0 -0
- /package/dev-tools/starter-kit/source_files/{_editorconfig → root/_editorconfig} +0 -0
- /package/dev-tools/starter-kit/source_files/{_gitignore → root/_gitignore} +0 -0
- /package/dev-tools/starter-kit/source_files/{_prettierrc → root/_prettierrc} +0 -0
- /package/dev-tools/starter-kit/source_files/{eslint.config.mjs → root/eslint.config.mjs} +0 -0
- /package/dev-tools/starter-kit/source_files/{jsconfig.json → root/jsconfig.json} +0 -0
- /package/dev-tools/starter-kit/{webpack-config-modules → source_files/root/webpack-config-modules}/css-rules.mjs +0 -0
- /package/dev-tools/starter-kit/{webpack-config-modules → source_files/root/webpack-config-modules}/get-jsConfig-aliases.mjs +0 -0
- /package/dev-tools/starter-kit/{webpack-config-modules → source_files/root/webpack-config-modules}/mini-svg-data-uri-loader.cjs +0 -0
- /package/dev-tools/starter-kit/{webpack-config-modules → source_files/root/webpack-config-modules}/postcss.config.mjs +0 -0
- /package/dev-tools/starter-kit/{webpack-config-modules → source_files/root/webpack-config-modules}/purgecss-variables-safelist.mjs +0 -0
- /package/dev-tools/starter-kit/{webpack-config-modules → source_files/root/webpack-config-modules}/svg-rules.mjs +0 -0
- /package/dev-tools/starter-kit/{webpack-config-modules → source_files/root/webpack-config-modules}/svgo.config.mjs +0 -0
- /package/snippets/{config-templates/_github → _github}/dependabot.yml +0 -0
- /package/snippets/{error-pages/error-pages.js → error-pages.js} +0 -0
- /package/snippets/{config-templates/react → react}/offcanvas/offcanvas.jsx +0 -0
- /package/snippets/{config-templates/react → react}/offcanvas/offcanvas.module.scss +0 -0
- /package/snippets/{config-templates → root}/_hintrc.cjs +0 -0
- /package/snippets/{config-templates → root}/_npmignore +0 -0
- /package/snippets/{config-templates → root}/_npmrc +0 -0
- /package/snippets/{config-templates → root}/_stylelintignore +0 -0
- /package/snippets/{config-templates → root}/babel.config--react.json +0 -0
- /package/snippets/{config-templates → root}/babel.config--solidJS.json +0 -0
- /package/snippets/{ImgViewerController.php → twig-symfony/ImgViewerController.php} +0 -0
- /package/snippets/{UnsplashController.php → twig-symfony/UnsplashController.php} +0 -0
- /package/snippets/{config-templates → twig-symfony}/_env.local +0 -0
- /package/snippets/{error-pages → twig-symfony}/bundles/TwigBundle/Exception/error.html.twig +0 -0
- /package/snippets/{flash-messages.html.twig → twig-symfony/flash-messages.html.twig} +0 -0
- /package/snippets/{minimo-form-layout.html.twig → twig-symfony/minimo-form-layout.html.twig} +0 -0
package/package.json
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@massimo-cassandro/minimo",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.33.1",
|
|
4
4
|
"description": "Light css framework & utilities",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./index.js",
|
|
7
7
|
"exports": {
|
|
8
8
|
".": "./index.js",
|
|
9
9
|
"./charts": "./charts/index.js",
|
|
10
|
+
"./stylelint-config": "./dev-tools/stylelint-config/index.js",
|
|
10
11
|
"./css/*": "./src/css/*.css",
|
|
11
12
|
"./tokens/*": "./design-tokens/tokens/*",
|
|
12
13
|
"./*": "./*"
|
|
@@ -94,7 +95,6 @@
|
|
|
94
95
|
},
|
|
95
96
|
"devDependencies": {
|
|
96
97
|
"@massimo-cassandro/eslint-config": "^3.0.16",
|
|
97
|
-
"@massimo-cassandro/stylelint-config": "^2.3.15",
|
|
98
98
|
"@types/node": "^26.2.0",
|
|
99
99
|
"jsdom": "^30.0.1",
|
|
100
100
|
"style-dictionary": "^5.5.1"
|
|
@@ -102,9 +102,18 @@
|
|
|
102
102
|
"dependencies": {
|
|
103
103
|
"@inquirer/input": "^5.1.2",
|
|
104
104
|
"@inquirer/select": "^5.2.1",
|
|
105
|
+
"@stylistic/stylelint-config": "^5.0.0",
|
|
106
|
+
"@stylistic/stylelint-plugin": "^5.3.0",
|
|
105
107
|
"clipboardy": "^5.3.2",
|
|
106
108
|
"sharp": "^0.35.3",
|
|
107
109
|
"sharp-ico": "^0.1.5",
|
|
110
|
+
"stylelint": "^17.15.0",
|
|
111
|
+
"stylelint-config-css-modules": "^4.6.0",
|
|
112
|
+
"stylelint-config-recess-order": "^7.8.0",
|
|
113
|
+
"stylelint-config-standard": "^40.0.0",
|
|
114
|
+
"stylelint-config-standard-scss": "^17.0.0",
|
|
115
|
+
"stylelint-order": "^8.1.1",
|
|
116
|
+
"stylelint-scss": "^7.2.0",
|
|
108
117
|
"svgo": "^4.0.2"
|
|
109
118
|
},
|
|
110
119
|
"directories": {
|
|
@@ -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
|
+
}
|