• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

timber / timber / 20695674007

04 Jan 2026 04:14PM UTC coverage: 89.681% (+1.5%) from 88.211%
20695674007

push

travis-ci

nlemoine
test: Fix ancestors post tests

4615 of 5146 relevant lines covered (89.68%)

63.45 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

96.69
/src/Image/Operation/Resize.php
1
<?php
2

3
namespace Timber\Image\Operation;
4

5
use Imagick;
6
use Timber\Helper;
7
use Timber\Image\Operation as ImageOperation;
8
use Timber\ImageHelper;
9
use WP_Image_Editor;
10

11
/**
12
 * Changes image to new size, by shrinking/enlarging
13
 * then cropping to respect new ratio.
14
 *
15
 * Arguments:
16
 * - width of new image
17
 * - height of new image
18
 * - crop method
19
 */
20
class Resize extends ImageOperation
21
{
22
    private $crop;
23

24
    /**
25
     * @param int    $w    width of new image
26
     * @param int    $h    height of new image
27
     * @param string $crop cropping method, one of: 'default', 'center', 'top', 'bottom', 'left', 'right', 'top-center', 'bottom-center'.
28
     */
29
    public function __construct(
54✔
30
        private $w,
31
        private $h,
32
        $crop
33
    ) {
34
        // Sanitize crop position
35
        $allowed_crop_positions = ['default', 'center', 'top', 'bottom', 'left', 'right', 'top-center', 'bottom-center'];
54✔
36
        if ($crop !== false && !\in_array($crop, $allowed_crop_positions)) {
54✔
37
            $crop = $allowed_crop_positions[0];
3✔
38
        }
39
        $this->crop = $crop;
54✔
40
    }
41

42
    /**
43
     * @param   string    $src_filename     the basename of the file (ex: my-awesome-pic)
44
     * @param   string    $src_extension    the extension (ex: .jpg)
45
     * @return  string    the final filename to be used (ex: my-awesome-pic-300x200-c-default.jpg)
46
     */
47
    public function filename($src_filename, $src_extension)
45✔
48
    {
49
        $w = 0;
45✔
50
        $h = 0;
45✔
51
        if ($this->w) {
45✔
52
            $w = $this->w;
44✔
53
        }
54
        if ($this->h) {
45✔
55
            $h = $this->h;
43✔
56
        }
57
        $result = $src_filename . '-' . $w . 'x' . $h . '-c-' . ($this->crop ?: 'f'); // Crop will be either user named or f (false)
45✔
58
        if ($src_extension) {
45✔
59
            $result .= '.' . $src_extension;
45✔
60
        }
61
        return $result;
45✔
62
    }
63

64
    /**
65
     * Run a resize as animated GIF (if the server supports it)
66
     *
67
     * @param string           $load_filename the name of the file to resize.
68
     * @param string           $save_filename the desired name of the file to save.
69
     * @param WP_Image_Editor $editor the image editor we're using.
70
     * @return bool
71
     */
72
    protected function run_animated_gif($load_filename, $save_filename, WP_Image_Editor $editor)
2✔
73
    {
74
        $w = $this->w;
2✔
75
        $h = $this->h;
2✔
76
        if (!\class_exists('Imagick') || (\defined('TEST_NO_IMAGICK') && TEST_NO_IMAGICK)) {
2✔
77
            Helper::warn('Cannot resize GIF, Imagick is not installed');
×
78
            return false;
×
79
        }
80
        $image = new Imagick($load_filename);
2✔
81
        $image = $image->coalesceImages();
2✔
82
        $crop = $this->get_target_sizes($editor);
2✔
83
        foreach ($image as $frame) {
2✔
84
            $frame->cropImage($crop['src_w'], $crop['src_h'], \round($crop['x']), \round($crop['y']));
2✔
85
            $frame->thumbnailImage($w, $h);
2✔
86
            $frame->setImagePage($w, $h, 0, 0);
2✔
87
        }
88
        $image = $image->deconstructImages();
2✔
89
        return $image->writeImages($save_filename, true);
2✔
90
    }
91

92
    protected function get_target_sizes(WP_Image_Editor $image)
40✔
93
    {
94
        $w = $this->w;
40✔
95
        $h = $this->h;
40✔
96
        $crop = $this->crop;
40✔
97

98
        $current_size = $image->get_size();
40✔
99
        $src_w = $current_size['width'];
40✔
100
        $src_h = $current_size['height'];
40✔
101
        $src_ratio = $src_w / $src_h;
40✔
102
        if (!$h) {
40✔
103
            $h = \round($w / $src_ratio);
2✔
104
        }
105
        if (!$w) {
40✔
106
            //the user wants to resize based on constant height
107
            $w = \round($h * $src_ratio);
1✔
108
        }
109

110
        if (!$crop) {
40✔
111
            return [
1✔
112
                'x' => 0,
1✔
113
                'y' => 0,
1✔
114
                'src_w' => $src_w,
1✔
115
                'src_h' => $src_h,
1✔
116
                'target_w' => $w,
1✔
117
                'target_h' => $h,
1✔
118
            ];
1✔
119
        }
120
        // Get ratios
121
        $dest_ratio = $w / $h;
39✔
122
        $src_wt = $src_h * $dest_ratio;
39✔
123
        $src_ht = $src_w / $dest_ratio;
39✔
124
        $src_x = $src_w / 2 - $src_wt / 2;
39✔
125
        $src_y = ($src_h - $src_ht) / 6;
39✔
126
        //now specific overrides based on options:
127
        switch ($crop) {
128
            case 'center':
39✔
129
                // Get source x and y
130
                $src_x = \round(($src_w - $src_wt) / 2);
1✔
131
                $src_y = \round(($src_h - $src_ht) / 2);
1✔
132
                break;
1✔
133

134
            case 'top':
38✔
135
                $src_y = 0;
1✔
136
                break;
1✔
137

138
            case 'bottom':
37✔
139
                $src_y = $src_h - $src_ht;
1✔
140
                break;
1✔
141

142
            case 'top-center':
36✔
143
                $src_y = \round(($src_h - $src_ht) / 4);
1✔
144
                break;
1✔
145

146
            case 'bottom-center':
35✔
147
                $src_y = $src_h - $src_ht - \round(($src_h - $src_ht) / 4);
1✔
148
                break;
1✔
149

150
            case 'left':
34✔
151
                $src_x = 0;
5✔
152
                break;
5✔
153

154
            case 'right':
34✔
155
                $src_x = $src_w - $src_wt;
1✔
156
                break;
1✔
157
        }
158
        // Crop the image
159
        return ($dest_ratio > $src_ratio)
39✔
160
            ? [
16✔
161
                'x' => 0,
16✔
162
                'y' => $src_y,
16✔
163
                'src_w' => $src_w,
16✔
164
                'src_h' => $src_ht,
16✔
165
                'target_w' => $w,
16✔
166
                'target_h' => $h,
16✔
167
            ]
16✔
168
            : [
39✔
169
                'x' => $src_x,
39✔
170
                'y' => 0,
39✔
171
                'src_w' => $src_wt,
39✔
172
                'src_h' => $src_h,
39✔
173
                'target_w' => $w,
39✔
174
                'target_h' => $h,
39✔
175
            ];
39✔
176
    }
177

178
    /**
179
     * Performs the actual image manipulation,
180
     * including saving the target file.
181
     *
182
     * @param  string $load_filename filepath (not URL) to source file
183
     *                               (ex: /src/var/www/wp-content/uploads/my-pic.jpg)
184
     * @param  string $save_filename filepath (not URL) where result file should be saved
185
     *                               (ex: /src/var/www/wp-content/uploads/my-pic-300x200-c-default.jpg)
186
     * @return boolean|null                  true if everything went fine, false otherwise
187
     */
188
    public function run($load_filename, $save_filename)
41✔
189
    {
190
        // Attempt to check if SVG.
191
        if (ImageHelper::is_svg($load_filename)) {
41✔
192
            return false;
1✔
193
        }
194
        $image = \wp_get_image_editor($load_filename);
40✔
195
        if (!\is_wp_error($image)) {
40✔
196
            //should be resized by gif resizer
197
            if (ImageHelper::is_animated_gif($load_filename)) {
40✔
198
                //attempt to resize, return if successful proceed if not
199
                $gif = $this->run_animated_gif($load_filename, $save_filename, $image);
2✔
200
                if ($gif) {
2✔
201
                    return true;
2✔
202
                }
203
            }
204

205
            $crop = $this->get_target_sizes($image);
38✔
206
            $image->crop(
38✔
207
                (int) \round($crop['x']),
38✔
208
                (int) \round($crop['y']),
38✔
209
                (int) \round($crop['src_w']),
38✔
210
                (int) \round($crop['src_h']),
38✔
211
                (int) \round($crop['target_w']),
38✔
212
                (int) \round($crop['target_h'])
38✔
213
            );
38✔
214
            $quality = \apply_filters('wp_editor_set_quality', 82, 'image/jpeg');
38✔
215
            $image->set_quality($quality);
38✔
216
            $result = $image->save($save_filename);
38✔
217
            if (\is_wp_error($result)) {
38✔
218
                // @codeCoverageIgnoreStart
219
                Helper::error_log('Error resizing image');
220
                Helper::error_log($result);
221
                return false;
222
                // @codeCoverageIgnoreEnd
223
            } else {
224
                return true;
38✔
225
            }
226
        } elseif (isset($image->error_data['error_loading_image'])) {
×
227
            // @codeCoverageIgnoreStart
228
            Helper::error_log('Error loading ' . $image->error_data['error_loading_image']);
229
        } else {
230
            if (!\extension_loaded('gd')) {
231
                Helper::error_log('Can not resize image, please install php-gd');
232
            } else {
233
                Helper::error_log($image);
234
            }
235
            // @codeCoverageIgnoreEnd
236
        }
237

238
        return false;
×
239
    }
240
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc