Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
80.52% covered (warning)
80.52%
62 / 77
14.29% covered (danger)
14.29%
1 / 7
CRAP
0.00% covered (danger)
0.00%
0 / 1
Support
80.52% covered (warning)
80.52%
62 / 77
14.29% covered (danger)
14.29%
1 / 7
63.33
0.00% covered (danger)
0.00%
0 / 1
 getListBinary
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 resolveListBinary
27.27% covered (danger)
27.27%
3 / 11
0.00% covered (danger)
0.00%
0 / 1
14.62
 gravity
80.00% covered (warning)
80.00%
12 / 15
0.00% covered (danger)
0.00%
0 / 1
10.80
 imageTypes
91.67% covered (success)
91.67%
11 / 12
0.00% covered (danger)
0.00%
0 / 1
7.03
 colorSpace
91.67% covered (success)
91.67%
11 / 12
0.00% covered (danger)
0.00%
0 / 1
7.03
 layerMethod
90.91% covered (success)
90.91%
10 / 11
0.00% covered (danger)
0.00%
0 / 1
7.04
 supportedFormat
92.86% covered (success)
92.86%
13 / 14
0.00% covered (danger)
0.00%
0 / 1
9.03
1<?php
2
3/**
4 * Karla ImageMagick wrapper library
5 *
6 * PHP Version 8.0<
7 *
8 * @category Utility
9 * @author   Johannes Skov Frandsen <jsf@greenoak.dk>
10 * @license  http://www.opensource.org/licenses/mit-license.php MIT
11 * @link     https://github.com/localgod/karla Karla
12 * @since    2013-05-26
13 */
14
15declare(strict_types=1);
16
17namespace Karla;
18
19use Karla\Platform;
20use Karla\Program;
21use Karla\Program\Identify;
22use Karla\Program\Convert;
23use Karla\Program\ImageMagick;
24use Karla\Program\Composite;
25
26/**
27 * Class for querying for supported features
28 *
29 * @category Utility
30 * @author   Johannes Skov Frandsen <jsf@greenoak.dk>
31 * @license  http://www.opensource.org/licenses/mit-license.php MIT
32 * @link     https://github.com/localgod/karla Karla
33 */
34class Support
35{
36    /**
37     * Get the appropriate binary for -list commands.
38     * For ImageMagick 7, use 'magick'.
39     * For ImageMagick 6, use 'convert' or 'identify' (Composite doesn't support -list).
40     *
41     * @param Program $program Program instance to extract binary path from
42     */
43    private static function getListBinary(Program $program): string
44    {
45        $binary = self::resolveListBinary($program);
46
47        // Quote the full path on Windows so install locations with spaces work
48        return Platform::isWindows() ? escapeshellarg($binary) : $binary;
49    }
50
51    /**
52     * Resolve the binary path to use for -list commands.
53     *
54     * @param Program $program Program instance
55     */
56    private static function resolveListBinary(Program $program): string
57    {
58        $binary = $program->getBinary();
59
60        // For Composite programs in IM6, we need to use 'convert' instead
61        // because 'composite' doesn't support -list commands
62        if ($program instanceof Composite) {
63            // If binary contains 'magick', it's IM7 - use as-is
64            if (strpos($binary, 'magick') !== false) {
65                return $binary;
66            }
67
68            // IM6 - replace composite with convert
69            // Get the directory and construct convert path
70            $dirPath = dirname($binary);
71            $convertBin = Platform::getBinary('convert');
72
73            $result = $dirPath . DIRECTORY_SEPARATOR . $convertBin;
74
75            // If convert doesn't exist, try using the original binary anyway
76            // (it might work in some configurations)
77            if (!file_exists($result) && !is_executable($result)) {
78                return $binary;
79            }
80
81            return $result;
82        }
83
84        // For Convert and Identify, use their binary as-is (they support -list)
85        return $binary;
86    }
87
88    /**
89     * Check if a gravity is supported by ImageMagick.
90     *
91     * @param Program $program Program to check
92     * @param string $gravity Gravity to check
93     *
94     * @throws \BadMethodCallException if called in a wrong context
95     */
96    public static function gravity(Program $program, string $gravity): bool
97    {
98        if (! ($program instanceof Convert) && ! ($program instanceof Composite)) {
99            $message = 'This method can not be used in this context. (' . get_class($program) . ')';
100            throw new \BadMethodCallException($message);
101        }
102
103        // Use the appropriate binary for -list commands
104        // Redirect stderr to stdout to capture all output
105        $command = self::getListBinary($program) . ' -list gravity 2>&1';
106
107        $gravities = shell_exec($command);
108
109        // If command fails, try without stderr redirect as fallback
110        if ($gravities === null || $gravities === false || trim($gravities) === '') {
111            $command = self::getListBinary($program) . ' -list gravity';
112            $gravities = shell_exec($command);
113        }
114
115        if ($gravities === null || $gravities === false || trim($gravities) === '') {
116            return false;
117        }
118
119        $gravities = explode("\n", $gravities);
120        $count = count($gravities);
121        for ($i = 0; $i < $count; $i++) {
122            $gravities[$i] = trim(strtolower($gravities[$i]));
123        }
124
125        return in_array(strtolower(trim($gravity)), $gravities);
126    }
127
128    /**
129     * Check if an image type is supported by the ImageMagick program.
130     *
131     * @param Program $program Program to check
132     * @param string $type Type to check
133     */
134    public static function imageTypes(Program $program, string $type): bool
135    {
136        if (! ($program instanceof Convert) && ! ($program instanceof Identify)) {
137            $message = 'This method can not be used in this context. (' . get_class($program) . ')';
138            throw new \BadMethodCallException($message);
139        }
140
141        // Use the appropriate binary for -list commands
142        // Redirect stderr to stdout to capture all output
143        $command = self::getListBinary($program) . ' -list type 2>&1';
144
145        $types = shell_exec($command);
146        if ($types === null || $types === false || trim($types) === '') {
147            return false;
148        }
149
150        $types = explode("\n", $types);
151        $count = count($types);
152        for ($i = 0; $i < $count; $i++) {
153            $types[$i] = trim(strtolower($types[$i]));
154        }
155
156        return in_array(strtolower(trim($type)), $types);
157    }
158
159    /**
160     * Check if a colorspace is supported by the ImageMagick program.
161     *
162     * @param Program $program Program to check
163     * @param string $colorSpace Colorspace to check
164     */
165    public static function colorSpace(Program $program, string $colorSpace): bool
166    {
167        if (! ($program instanceof Convert) && ! ($program instanceof Identify)) {
168            $message = 'This method can not be used in this context. (' . get_class($program) . ')';
169            throw new \BadMethodCallException($message);
170        }
171
172        // Use the appropriate binary for -list commands
173        // Redirect stderr to stdout to capture all output
174        $command = self::getListBinary($program) . ' -list colorspace 2>&1';
175
176        $colorspaces = shell_exec($command);
177        if ($colorspaces === null || $colorspaces === false || trim($colorspaces) === '') {
178            // Command failed, return false
179            return false;
180        }
181
182        $colorspaces = explode("\n", $colorspaces);
183        $count = count($colorspaces);
184        for ($i = 0; $i < $count; $i++) {
185            $colorspaces[$i] = trim(strtolower($colorspaces[$i]));
186        }
187
188        return in_array(strtolower(trim($colorSpace)), $colorspaces);
189    }
190
191    /**
192     * Check if a method is supported by ImageMagick.
193     *
194     * @param Program $program Program to check
195     * @param string $method Method to check
196     *
197     * @throws \BadMethodCallException if called in a wrong context
198     */
199    public static function layerMethod(Program $program, string $method): bool
200    {
201        if (! ($program instanceof Convert) && ! ($program instanceof Identify)) {
202            throw new \BadMethodCallException('This method can not be used in this context');
203        }
204
205        // Use the appropriate binary for -list commands
206        // Redirect stderr to stdout to capture all output
207        $command = self::getListBinary($program) . ' -list layers 2>&1';
208
209        $methods = shell_exec($command);
210        if ($methods === null || $methods === false || trim($methods) === '') {
211            return false;
212        }
213
214        $methods = explode("\n", $methods);
215        $count = count($methods);
216        for ($i = 0; $i < $count; $i++) {
217            $methods[$i] = trim(strtolower($methods[$i]));
218        }
219
220        return in_array(strtolower(trim($method)), $methods);
221    }
222
223    /**
224     * Check if a format is supported by ImageMagick.
225     *
226     * @param Program $program Program to check
227     * @param string $format Format to check
228     *
229     * @throws \BadMethodCallException if called in a wrong context
230     */
231    public static function supportedFormat(Program $program, string $format): bool
232    {
233        if (! ($program instanceof Convert) && ! ($program instanceof Identify)) {
234            throw new \BadMethodCallException('This method can not be used in this context');
235        }
236
237        // Use the appropriate binary for -list commands
238        // Redirect stderr to stdout to capture all output
239        $command = self::getListBinary($program) . ' -list format 2>&1';
240
241        $formats = shell_exec($command);
242        if ($formats === null || $formats === false || trim($formats) === '') {
243            return false;
244        }
245
246        $formats = explode("\n", $formats);
247        $count = count($formats);
248        for ($i = 0; $i < $count; $i++) {
249            $matches = [];
250            preg_match("/^[\s]*[A-Z0-9]+/", $formats[$i], $matches);
251            if (isset($matches[0]) && ! strpos($matches[0], 'Format')) {
252                $formats[$i] = strtolower(trim($matches[0]));
253            }
254        }
255
256        return in_array(strtolower(trim($format)), $formats);
257    }
258}