Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
59.46% |
22 / 37 |
|
25.00% |
1 / 4 |
CRAP | |
0.00% |
0 / 1 |
JpegQualityDetector | |
59.46% |
22 / 37 |
|
25.00% |
1 / 4 |
71.04 | |
0.00% |
0 / 1 |
detectQualityOfJpgUsingImagick | |
20.00% |
2 / 10 |
|
0.00% |
0 / 1 |
32.09 | |||
detectQualityOfJpgUsingImageMagick | |
75.00% |
6 / 8 |
|
0.00% |
0 / 1 |
7.77 | |||
detectQualityOfJpgUsingGraphicsMagick | |
54.55% |
6 / 11 |
|
0.00% |
0 / 1 |
14.01 | |||
detectQualityOfJpg | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
4 |
1 | <?php |
2 | |
3 | namespace WebPConvert\Convert\Helpers; |
4 | |
5 | use ExecWithFallback\ExecWithFallback; |
6 | |
7 | /** |
8 | * Try to detect quality of a jpeg image using various tools. |
9 | * |
10 | * @package WebPConvert |
11 | * @author Bjørn Rosell <it@rosell.dk> |
12 | * @since Class available since Release 2.0.0 |
13 | */ |
14 | class JpegQualityDetector |
15 | { |
16 | |
17 | /** |
18 | * Try to detect quality of jpeg using imagick extension. |
19 | * |
20 | * Note that the detection might fail for two different reasons: |
21 | * 1) Imagick is not installed |
22 | * 2) Imagick for some reason fails to detect quality for some images |
23 | * |
24 | * In both cases, null is returned. |
25 | * |
26 | * @param string $filename A complete file path to file to be examined |
27 | * @return int|null Quality, or null if it was not possible to detect quality |
28 | */ |
29 | private static function detectQualityOfJpgUsingImagick($filename) |
30 | { |
31 | if (extension_loaded('imagick') && class_exists('\\Imagick')) { |
32 | try { |
33 | $img = new \Imagick($filename); |
34 | |
35 | // The required function is available as from PECL imagick v2.2.2 |
36 | if (method_exists($img, 'getImageCompressionQuality')) { |
37 | $quality = $img->getImageCompressionQuality(); |
38 | if ($quality === 0) { |
39 | // We have experienced that this Imagick method returns 0 for some images, |
40 | // (even though the imagemagick binary is able to detect the quality) |
41 | // ie "/test/images/quality-undetectable-with-imagick.jpg". See #208 |
42 | $quality = null; |
43 | } |
44 | return $quality; |
45 | } |
46 | } catch (\Exception $e) { |
47 | // Well well, it just didn't work out. |
48 | // - But perhaps next method will work... |
49 | } catch (\Throwable $e) { |
50 | } |
51 | } |
52 | return null; |
53 | } |
54 | |
55 | |
56 | /** |
57 | * Try to detect quality of jpeg using imagick binary. |
58 | * |
59 | * Note that the detection might fail for three different reasons: |
60 | * 1) exec function is not available |
61 | * 2) the 'identify' command is not available on the system |
62 | * 3) imagemagick for some reason fails to detect quality for some images |
63 | * |
64 | * In the first two cases, null is returned. |
65 | * In the third case, 92 is returned. This is what imagemagick returns when it cannot detect the quality. |
66 | * and unfortunately we cannot distinguish between the situation where the quality is undetectable |
67 | * and the situation where the quality is actually 92 (at least, I have not found a way to do so) |
68 | * |
69 | * @param string $filename A complete file path to file to be examined |
70 | * @return int|null Quality, or null if it was not possible to detect quality |
71 | */ |
72 | private static function detectQualityOfJpgUsingImageMagick($filename) |
73 | { |
74 | if (ExecWithFallback::anyAvailable()) { |
75 | // Try Imagick using exec, and routing stderr to stdout (the "2>$1" magic) |
76 | |
77 | try { |
78 | ExecWithFallback::exec( |
79 | "identify -format '%Q' " . escapeshellarg($filename) . " 2>&1", |
80 | $output, |
81 | $returnCode |
82 | ); |
83 | //echo 'out:' . print_r($output, true); |
84 | if ((intval($returnCode) == 0) && (is_array($output)) && (count($output) == 1)) { |
85 | return intval($output[0]); |
86 | } |
87 | } catch (\Exception $e) { |
88 | // its ok, there are other fish in the sea |
89 | } catch (\Throwable $e) { |
90 | } |
91 | } |
92 | return null; |
93 | } |
94 | |
95 | |
96 | /** |
97 | * Try to detect quality of jpeg using graphicsmagick binary. |
98 | * |
99 | * It seems that graphicsmagick is never able to detect the quality! - and always returns |
100 | * the default quality, which is 75. |
101 | * However, as this might be solved in future versions, the method might be useful one day. |
102 | * But we treat "75" as a failure to detect and shall return null in that case. |
103 | * |
104 | * @param string $filename A complete file path to file to be examined |
105 | * @return int|null Quality, or null if it was not possible to detect quality |
106 | */ |
107 | private static function detectQualityOfJpgUsingGraphicsMagick($filename) |
108 | { |
109 | if (ExecWithFallback::anyAvailable()) { |
110 | // Try GraphicsMagick |
111 | try { |
112 | ExecWithFallback::exec( |
113 | "gm identify -format '%Q' " . escapeshellarg($filename) . " 2>&1", |
114 | $output, |
115 | $returnCode |
116 | ); |
117 | if ((intval($returnCode) == 0) && (is_array($output)) && (count($output) == 1)) { |
118 | $quality = intval($output[0]); |
119 | |
120 | // It seems that graphicsmagick is (currently) never able to detect the quality! |
121 | // - and always returns 75 as a fallback |
122 | // We shall therefore treat 75 as a failure to detect. (#209) |
123 | if ($quality == 75) { |
124 | return null; |
125 | } |
126 | return $quality; |
127 | } |
128 | } catch (\Exception $e) { |
129 | } catch (\Throwable $e) { |
130 | } |
131 | } |
132 | return null; |
133 | } |
134 | |
135 | |
136 | /** |
137 | * Try to detect quality of jpeg. |
138 | * |
139 | * Note: This method does not throw errors, but might dispatch warnings. |
140 | * You can use the WarningsIntoExceptions class if it is critical to you that nothing gets "printed" |
141 | * |
142 | * @param string $filename A complete file path to file to be examined |
143 | * @return int|null Quality, or null if it was not possible to detect quality |
144 | */ |
145 | public static function detectQualityOfJpg($filename) |
146 | { |
147 | |
148 | //trigger_error('warning test', E_USER_WARNING); |
149 | |
150 | // Test that file exists in order not to break things. |
151 | if (!file_exists($filename)) { |
152 | // One could argue that it would be better to throw an Exception...? |
153 | return null; |
154 | } |
155 | |
156 | // Try Imagick extension, if available |
157 | $quality = self::detectQualityOfJpgUsingImagick($filename); |
158 | |
159 | if (is_null($quality)) { |
160 | $quality = self::detectQualityOfJpgUsingImageMagick($filename); |
161 | } |
162 | |
163 | if (is_null($quality)) { |
164 | $quality = self::detectQualityOfJpgUsingGraphicsMagick($filename); |
165 | } |
166 | |
167 | return $quality; |
168 | } |
169 | } |