ImageConverter   A
last analyzed

Complexity

Total Complexity 23

Size/Duplication

Total Lines 224
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 7

Test Coverage

Coverage 13.73%

Importance

Changes 3
Bugs 3 Features 0
Metric Value
wmc 23
c 3
b 3
f 0
lcom 1
cbo 7
dl 0
loc 224
ccs 14
cts 102
cp 0.1373
rs 10

7 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 8 2
A setBackend() 0 13 3
C getThumbnail() 0 51 8
B generateThumbnail() 0 59 4
A getImagine() 0 18 4
A setCache() 0 6 1
A unsetCache() 0 6 1
1
<?php
2
3
namespace Soluble\Media\Converter;
4
5
use Soluble\Media\BoxDimension;
6
use Imagine\Imagick\Imagine as ImagickImagine;
7
use Imagine\Gd\Imagine as GdImagine;
8
use Imagine\Image\ImageInterface;
9
use Imagine\Image\Box;
10
use Zend\Cache\Storage\StorageInterface;
11
12
class ImageConverter implements ConverterInterface
13
{
14
    protected $default_backend = 'gd';
15
    protected $supported_backends = ['gd', 'imagick'];
16
    protected $backend;
17
18
    protected $default_quality = 90;
19
20
    /**
21
     *
22
     * @var boolean
23
     */
24
    protected $cacheEnabled = false;
25
26
    /**
27
     *
28
     * @var StorageInterface
29
     */
30
    protected $cacheStorage;
31
32
    /**
33
     *
34
     * @param array $params
35
     */
36 5
    public function __construct(array $params = [])
37
    {
38 5
        if (array_key_exists('backend', $params)) {
39 2
            $this->setBackend($params['backend']);
40 1
        } else {
41 5
            $this->setBackend($this->default_backend);
42
        }
43 5
    }
44
45
    /**
46
     * Set the backend gd of imagick to use
47
     *
48
     * @param string $backend (gd/imagick)
49
     *
50
     * @throws Exception\UnsupportedBackendException
51
     * @return \Soluble\Media\Converter\ImageConverter
52
     */
53 5
    public function setBackend($backend)
54
    {
55 5
        if (!is_string($backend)) {
56 1
            throw new Exception\InvalidArgumentException(__METHOD__ . " backend parameter must be a valid string.");
57
        }
58
59 5
        if (!in_array($backend, $this->supported_backends)) {
60 2
            $valid_backends = implode(',', $this->supported_backends);
61 2
            throw new Exception\UnsupportedBackendException(__METHOD__ . " Backend '$backend' is not supported, supported backends are '$valid_backends'''");
62
        }
63 5
        $this->backend = $backend;
64 5
        return $this;
65
    }
66
67
    /**
68
     *
69
     * @param string $filename
70
     * @param \Soluble\Media\BoxDimension $box
71
     * @param string $format
72
     * @param int $quality
73
     * @throws \Soluble\Media\Converter\Exception
74
     * @throws \Exception
75
     */
76
    public function getThumbnail($filename, BoxDimension $box, $format = null, $quality = null)
77
    {
78
        $width    = $box->getWidth();
79
        $height   = $box->getHeight();
80
81
        if ($quality === null) {
82
            $quality = $this->default_quality;
83
        }
84
85
        $cache_key = md5("$filename/$width/$height/$quality/$format");
86
87
        if ($this->cacheEnabled && $this->cacheStorage->hasItem($cache_key)) {
88
            $cacheMd = $this->cacheStorage->getMetadata($cache_key);
89
            if ($cacheMd['mtime'] < filemtime($filename)) {
90
                // invalid cache
91
92
                $binaryContent = $this->generateThumbnail($filename, $box, $format, $quality);
93
                $this->cacheStorage->setItem($cache_key, $binaryContent);
94
            } else {
95
                $binaryContent = $this->cacheStorage->getItem($cache_key);
96
            }
97
        } else {
98
            $binaryContent = $this->generateThumbnail($filename, $box, $format, $quality);
99
            $this->cacheStorage->setItem($cache_key, $binaryContent);
100
        }
101
102
        switch ($format) {
103
            case 'jpg':
104
                $content_type = 'image/jpeg';
105
                break;
106
            case 'png':
107
                $content_type = 'image/png';
108
                break;
109
            case 'gif':
110
                $content_type = 'image/gif';
111
                break;
112
            default:
113
                throw new \Exception("Unsupported format '$format'");
114
        }
115
116
        header("Content-type: $content_type", true);
117
        header("Accept-Ranges: bytes", true);
118
        header("Cache-control: max-age=2592000, public", true);
119
        header("Content-Disposition: inline; filename=\"$filename\";", true);
120
        header('Last-Modified: '. gmdate('D, d M Y H:i:s', filemtime($filename)).' GMT', true);
121
        header('Expires: ' . gmdate('D, d M Y H:i:s', strtotime('+1 years')) . ' GMT', true);
122
        //header('Content-Disposition: attachment; filename="downloaded.pdf"');
123
        header('Pragma: cache', true);
124
        echo $binaryContent;
125
        die();
126
    }
127
128
    protected function generateThumbnail($filename, BoxDimension $box, $format = null, $quality = null, $strip = true)
129
    {
130
        $width    = $box->getWidth();
131
        $height   = $box->getHeight();
132
133
        try {
134
            $imagine = $this->getImagine();
135
136
            if ($imagine instanceof ImagickImagine) {
137
                $filter = ImageInterface::FILTER_LANCZOS;
138
                /**
139
                 * BESSEL : 53k
140
                 * LANCZOS: 54.5k
141
                 * GAUSSIAN: 52k
142
                 * MITCHELL: 53k
143
                 jpegtran -optimize 14610.jpg > 14610_test.jpg
144
                 */
145
            } else {
146
                $filter = ImageInterface::FILTER_UNDEFINED;
147
            }
148
149
            $image = $imagine->open($filename);
150
151
152
                // Get dimension by keeping proportions
153
            $size = $image->getSize();
154
            $ratio_x = $size->getWidth() / $width;
155
            $ratio_y = $size->getHeight() / $height;
156
            $max_ratio = max($ratio_x, $ratio_y);
157
            $new_width  = (int) ($size->getWidth() / $max_ratio);
158
            $new_height = (int) ($size->getHeight() / $max_ratio);
159
160
            $newSize = new Box($new_width, $new_height);
161
162
            if ($strip) {
163
                $image->strip();
164
            }
165
166
            //$image->interlace(ImageInterface::INTERLACE_LINE);
167
168
            $image->resize($newSize, $filter);
169
            $options = [
170
                'quality' => $quality,
171
                'flatten' => true,
172
                //'resolution-units' => ImageInterface::RESOLUTION_PIXELSPERINCH,
173
                //'resolution-y' => 72,
174
                //'resolution-x' => 72,
175
            ];
176
177
            //var_dump(get_class($image));die();
178
            $content = $image->get($format, $options);
179
        } catch (\Exception $e) {
180
            // ERROR 403 ?
181
            //var_dump($e);
182
            //die();
183
            throw $e;
184
        }
185
        return $content;
186
    }
187
188
189
    /**
190
     *
191
     * @param string $backend
192
     * @return
193
     */
194
    protected function getImagine($backend = null)
195
    {
196
        if ($backend === null) {
197
            $backend = $this->backend;
198
        }
199
        switch (strtolower($backend)) {
200
            case 'imagick':
201
                $imagine = new ImagickImagine();
202
                break;
203
            case 'gd':
204
                $imagine = new GdImagine();
205
                break;
206
            default:
207
                throw new \Exception("Library '$backend' not supported");
208
209
        }
210
        return $imagine;
211
    }
212
213
    /**
214
     *
215
     * @param StorageInterface $storage
216
     * @return \Soluble\Media\Converter\ImageConverter
217
     */
218
    public function setCache(StorageInterface $storage)
219
    {
220
        $this->cacheStorage = $storage;
221
        $this->cacheEnabled = true;
222
        return $this;
223
    }
224
225
    /**
226
     * Unset cache (primarly for unit testing)
227
     * @return \Soluble\Media\Converter\ImageConverter
228
     */
229
    public function unsetCache()
230
    {
231
        $this->cacheEnabled = false;
232
        $this->cacheStorage = null;
233
        return $this;
234
    }
235
}
236