depage-graphics
Loading...
Searching...
No Matches
Imgurl.php
Go to the documentation of this file.
1<?php
2
3namespace Depage\Graphics;
4
5/*
6 * @todo test to stay inside 3MP as maximum image size for safety reasons and
7 * to be able to support all iOS devices:
8 * http://www.williammalone.com/articles/html5-javascript-ios-maximum-image-size/
9 */
10
11class Imgurl
12{
13 protected $options = [];
14 protected $actions = [];
15 protected $srcImg = '';
16 protected $outImg = '';
17 protected $notFound = false;
18 public $id = "";
19 public $rendered = false;
20 protected $invalidAction = false;
21 protected $cachePath = '';
22
23 /*
24 * action aliases
25 *
26 * Note that the order is important so shoter action names should
27 * come after larger ones
28 */
29 protected $aliases = [
30 'quality' => "setQuality",
31 'q' => "setQuality",
32 'crop' => "addCrop",
33 'c' => "addCrop",
34 'resize' => "addResize",
35 'r' => "addResize",
36 'thumbfill' => "addThumbfill",
37 'tf' => "addThumbfill",
38 'thumb' => "addThumb",
39 't' => "addThumb",
40 'background' => "addBackground",
41 'bg' => "addBackground",
42 ];
43
44 /*
45 * @param $options hold the same options as the graphics class
46 */
47 public function __construct($options = [])
48 {
49 $this->options = $options;
50 }
51
52 /*
53 * Analyzes the image url and set the path for srcImg and outImg
54 *
55 * @param string $url the url to analyze
56 */
57 protected function analyze($url): void
58 {
59 $this->invalidAction = false;
60 $this->notFound = false;
61 $this->rendered = false;
62 $this->id = "";
63 $this->actions = [];
64
65 if (isset($this->options['baseUrl']) && isset($this->options['cachePath'])) {
66 $baseUrl = rtrim($this->options['baseUrl'], '/');
67 $this->cachePath = $this->options['cachePath'];
68 $relativePath = $this->options['relPath'] ?? '';
69 } elseif (defined('DEPAGE_PATH') && defined('DEPAGE_CACHE_PATH')) {
70 // we are using depage-framework so use constants for paths
71 $info = parse_url(DEPAGE_BASE);
72 $baseUrl = rtrim($info['path'], '/');
73 $relativePath = "";
74 $this->cachePath = \DEPAGE_CACHE_PATH . "graphics/";
75 } else {
76 // we using the library plainly -> get path through url
77 $scriptParts = explode("/", $_SERVER["SCRIPT_NAME"]);
78 $uriParts = explode("/", $_SERVER["REQUEST_URI"]);
79
80 if (strpos($_SERVER["SCRIPT_NAME"], "/lib/") !== false) {
81 for ($i = 0; $i < count($uriParts); $i++) {
82 // find common parts of url up to lib parameter
83 if ($uriParts[$i] == "lib") {
84 break;
85 }
86 }
87 } else {
88 for ($i = 0; $i < count($uriParts); $i++) {
89 // find common parts of url up to lib parameter
90 if ($scriptParts[$i] != $uriParts[$i]) {
91 break;
92 }
93 }
94 }
95 $baseUrl = implode("/", array_slice($uriParts, 0, $i));
96 if (isset($this->options['relPath'])) {
97 $relativePath = $this->options['relPath'];
98 } else {
99 $relativePath = str_repeat("../", count($scriptParts) - $i - 1);
100 }
101 $this->cachePath = $relativePath . "lib/cache/graphics/";
102 }
103 $baseUrlStatic = $baseUrl;
104 if (isset($this->options['baseUrlStatic'])) {
105 $baseUrlStatic = rtrim($this->options['baseUrlStatic'], '/');
106 }
107
108 // get image name
109 $imgUrl = $url;
110 if ($baseUrl == "" && $baseUrlStatic == "") {
111 $imgUrl = substr($url, 1);
112 } elseif (strpos($url, $baseUrlStatic) === 0) {
113 $imgUrl = substr($url, strlen($baseUrlStatic) + 1);
114 } elseif (strpos($url, $baseUrl) === 0) {
115 $imgUrl = substr($url, strlen($baseUrl) + 1);
116 }
117
118 // get action parameters
119 $matches = [];
120 $success = preg_match("/(.*\.(jpg|jpeg|gif|png|webp|eps|tif|tiff|pdf|svg))\.([^\\\]*)\.(jpg|jpeg|gif|png|webp)/i", $imgUrl, $matches);
121
122 if (!$success) {
123 $this->invalidAction = true;
124
125 return;
126 }
127
128 $this->id = rawurldecode($matches[0]);
129 $this->srcImg = $relativePath . rawurldecode($matches[1]);
130 $this->outImg = $this->cachePath . rawurldecode($matches[0]);
131 $this->actions = $this->analyzeActions($matches[3]);
132 }
133 /*
134 * Analyzes actions and replaces shortcuts with real actions
135 *
136 * @param string $actionString the string with actions to analyze
137 */
138 protected function analyzeActions($actionString): array
139 {
140 $actions = explode(".", $actionString);
141
142 foreach ($actions as &$action) {
143 $regex = implode("|", array_keys($this->aliases));
144 preg_match("/^($regex)/i", $action, $matches);
145
146 if (isset($matches[1]) && isset($this->aliases[$matches[1]])) {
147 $func = $this->aliases[$matches[1]];
148 $params = substr($action, strlen($matches[1]));
149 } else {
150 $func = "";
151 $params = "";
152 }
153
154 if (!empty($func)) {
155 $params = preg_split("/[-x,]+/", $params, -1, PREG_SPLIT_NO_EMPTY);
156
157 if ($func == "addBackground") {
158 if (!in_array($params[0], ["transparent", "checkerboard"])) {
159 $params[0] = "#{$params[0]}";
160 }
161 } else {
162 foreach ($params as $i => &$p) {
163 $p = intval($p);
164 if ($p == 0 && $i < 2) {
165 $p = null;
166 }
167 }
168 }
169
170 $this->actions[] = [$func, $params];
171 } else {
172 $this->invalidAction = true;
173 }
174 }
175
176 return $this->actions;
177 }
178 /*
179 * Renders the image with the given actions
180 *
181 * @param string $url the url to analyze and render
182 */
183 public function render($url = null): self
184 {
185 if (is_null($url)) {
186 $url = $_SERVER["REQUEST_URI"];
187 }
188 $this->analyze($url);
189
190 if ($this->invalidAction) {
191 return $this;
192 }
193 // make cache diretories
194 $outDir = dirname($this->outImg);
195 if (!is_dir($outDir)) {
196 mkdir($outDir, 0755, true);
197 }
198 if (file_exists($this->outImg) && filemtime($this->outImg) >= filemtime($this->srcImg)) {
199 // rendered image does exist already
200 return $this;
201 }
202
203 try {
204 $graphics = Graphics::factory($this->options);
205
206 // add actions to graphics class
207 foreach ($this->actions as $action) {
208 list($func, $params) = $action;
209 if (is_callable([$graphics, $func])) {
210 call_user_func_array([$graphics, $func], $params);
211 }
212 }
213
214 // render image out
215 $graphics->render($this->srcImg, $this->outImg);
216
217 $this->rendered = true;
218 } catch (Exceptions\FileNotFound $e) {
219 $this->notFound = true;
220 return $this;
221 } catch (Exceptions\Exception $e) {
222 $this->invalidAction = true;
223 }
224
225 return $this;
226 }
227
228 /*
229 * Displays the rendered image with correct header
230 */
231 public function display(): self
232 {
233 $info = pathinfo($this->outImg);
234 $ext = strtolower($info['extension'] ?? '');
235
236 if ($this->invalidAction) {
237 header("HTTP/1.1 500 Internal Server Error");
238 echo("invalid image action");
239 die();
240 }
241 if ($this->notFound) {
242 header("HTTP/1.1 404 Not Found");
243 echo("file not found");
244 die();
245 }
246
247 if ($ext == "jpg" || $ext == "jpeg") {
248 header("Content-type: image/jpeg");
249 } elseif ($ext == "png") {
250 header("Content-type: image/png");
251 } elseif ($ext == "gif") {
252 header("Content-type: image/gif");
253 } elseif ($ext == "webp") {
254 header("Content-type: image/webp");
255 }
256 readfile($this->outImg);
257
258 return $this;
259 }
260
261 /*
262 * Returns the url for the given image with the actions added
263 *
264 * @param string $img the image to get the url for
265 */
266 public function getUrl($img): string
267 {
268 $info = pathinfo($img);
269 $ext = $info['extension'];
270
271 if (count($this->actions) > 0) {
272 return $img . "." . implode(".", $this->actions) . "." . $ext;
273 } else {
274 return $img;
275 }
276 }
277
278 /*
279 * Adds a background action to the image
280 *
281 * @param string $background the background to add (color in hex or "transparent" or "checkerboard")
282 */
283 public function addBackground($background): self
284 {
285 $this->actions[] = "bg{$background}";
286
287 return $this;
288 }
289 /*
290 * Adds a crop action to the image
291 *
292 * @param int $width the width to crop to
293 * @param int $height the height to crop to
294 * @param int $x the x position to start cropping from (default: 0)
295 * @param int $y the y position to start cropping from (default: 0)
296 */
297 public function addCrop($width, $height, $x = 0, $y = 0): self
298 {
299 $this->actions[] = "crop{$width}x{$height}-{$x}x{$y}";
300
301 return $this;
302 }
303 /*
304 * Adds a resize action to the image
305 *
306 * @param int $width the width to resize to
307 * @param int $height the height to resize to
308 */
309 public function addResize($width, $height): self
310 {
311 $this->actions[] = "r{$width}x{$height}";
312
313 return $this;
314 }
315 /*
316 * Adds a thumb action to the image
317 *
318 * @param int $width the width to thumb to
319 * @param int $height the height to thumb to
320 */
321 public function addThumb($width, $height): self
322 {
323 $this->actions[] = "t{$width}x{$height}";
324
325 return $this;
326 }
327 /*
328 * Adds a thumbfill action to the image
329 *
330 * @param int $width the width to thumbfill to
331 * @param int $height the height to thumbfill to
332 * @param int $centerX the x position of the center of the thumbfill (default: 50)
333 * @param int $centerY the y position of the center of the thumbfill (default: 50)
334 */
335 public function addThumbfill($width, $height, $centerX = 50, $centerY = 50): self
336 {
337 $action = "tf{$width}x{$height}";
338 if ($centerX != 50 || $centerY != 50) {
339 $action .= "-{$centerX}x{$centerY}";
340 }
341
342 $this->actions[] = $action;
343
344 return $this;
345 }
346 /*
347 * Adds a quality action to the image
348 *
349 * @param int $quality the quality to set (0-100)
350 */
351 public function setQuality($quality): self
352 {
353 $this->actions[] = "q{$quality}";
354
355 return $this;
356 }
357}
358/* vim:set ft=php sw=4 sts=4 fdm=marker et : */
static factory($options=[])
graphics object factory
Definition Graphics.php:116
addThumbfill($width, $height, $centerX=50, $centerY=50)
Definition Imgurl.php:335
addThumb($width, $height)
Definition Imgurl.php:321
addCrop($width, $height, $x=0, $y=0)
Definition Imgurl.php:297
analyzeActions($actionString)
Definition Imgurl.php:138
__construct($options=[])
Definition Imgurl.php:47
addBackground($background)
Definition Imgurl.php:283
addResize($width, $height)
Definition Imgurl.php:309