Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
95.12% covered (success)
95.12%
39 / 41
71.43% covered (warning)
71.43%
5 / 7
CRAP
0.00% covered (danger)
0.00%
0 / 1
ImageLoader
95.12% covered (success)
95.12%
39 / 41
71.43% covered (warning)
71.43%
5 / 7
19
0.00% covered (danger)
0.00%
0 / 1
 setThemeInfo
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getImage
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 getContentType
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 searchTheme
85.71% covered (warning)
85.71%
6 / 7
0.00% covered (danger)
0.00%
0 / 1
4.05
 loadUnavailable
93.33% covered (success)
93.33%
14 / 15
0.00% covered (danger)
0.00%
0 / 1
6.01
 loadDefaultFailImage
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
2
 getContentTypeFromExtension
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2
3/**
4 * Base class for loading images (shared by Cover\Loader and QRCode\Loader)
5 *
6 * PHP version 8
7 *
8 * Copyright (C) Villanova University 2007.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2,
12 * as published by the Free Software Foundation.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
22 *
23 * @category VuFind
24 * @package  Cover_Generator
25 * @author   Andrew S. Nagy <vufind-tech@lists.sourceforge.net>
26 * @author   Demian Katz <demian.katz@villanova.edu>
27 * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
28 * @link     https://vufind.org/wiki/configuration:external_content Wiki
29 */
30
31namespace VuFind;
32
33use function array_key_exists;
34
35/**
36 * Base class for loading images (shared by Cover\Loader and QRCode\Loader)
37 *
38 * @category VuFind
39 * @package  Cover_Generator
40 * @author   Andrew S. Nagy <vufind-tech@lists.sourceforge.net>
41 * @author   Demian Katz <demian.katz@villanova.edu>
42 * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
43 * @link     https://vufind.org/wiki/configuration:external_content Wiki
44 */
45class ImageLoader implements \Laminas\Log\LoggerAwareInterface
46{
47    use \VuFind\Log\LoggerAwareTrait;
48
49    /**
50     * Property for storing raw image data; may be null if image is unavailable
51     *
52     * @var string
53     */
54    protected $image = null;
55
56    /**
57     * Content type of data in $image property
58     *
59     * @var string
60     */
61    protected $contentType = null;
62
63    /**
64     * Theme tools
65     *
66     * @var \VuFindTheme\ThemeInfo
67     */
68    protected $themeTools = null;
69
70    /**
71     * User-configured image to load from theme on error.
72     *
73     * @var string
74     */
75    protected $configuredFailImage = null;
76
77    /**
78     * Default image to load from theme if user-configured option fails.
79     *
80     * @var string
81     */
82    protected $defaultFailImage = 'images/noCover2.gif';
83
84    /**
85     * Array containing map of allowed file extensions to mimetypes
86     * (to be extended)
87     *
88     * @var array
89     */
90    protected $allowedFileExtensions = [
91        'gif' => 'image/gif',
92        'jpeg' => 'image/jpeg', 'jpg' => 'image/jpeg',
93        'png' => 'image/png',
94        'tiff' => 'image/tiff', 'tif' => 'image/tiff',
95    ];
96
97    /**
98     * Setter for dependency
99     *
100     * @param \VuFindTheme\ThemeInfo $theme VuFind theme tools
101     *
102     * @return void
103     */
104    public function setThemeInfo(\VuFindTheme\ThemeInfo $theme)
105    {
106        $this->themeTools = $theme;
107    }
108
109    /**
110     * Get the image data (not meant to be called until after image is populated)
111     *
112     * @return string
113     */
114    public function getImage()
115    {
116        // No image loaded?  Use "unavailable" as default:
117        if (null === $this->image) {
118            $this->loadUnavailable();
119        }
120        return $this->image;
121    }
122
123    /**
124     * Get the content type of the current image (not meant to be called until after
125     * contentType is populated)
126     *
127     * @return string
128     */
129    public function getContentType()
130    {
131        // No content type loaded?  Use "unavailable" as default:
132        if (null === $this->contentType) {
133            $this->loadUnavailable();
134        }
135        return $this->contentType;
136    }
137
138    /**
139     * Find a file in the themes (return false if no file exists).
140     *
141     * @param string $path    Relative path of file to find.
142     * @param array  $formats Optional array of suffixes to add to $path while
143     * searching theme (used to check multiple extensions in each theme).
144     *
145     * @return string|bool
146     */
147    protected function searchTheme($path, $formats = [''])
148    {
149        // Check all supported image formats:
150        $filenames = [];
151        foreach ($formats as $format) {
152            $filenames[] = $path . $format;
153        }
154        if (null === $this->themeTools) {
155            throw new \Exception('\VuFindTheme\ThemeInfo object missing');
156        }
157        $fileMatch = $this->themeTools->findContainingTheme($filenames, true);
158        return empty($fileMatch) ? false : $fileMatch;
159    }
160
161    /**
162     * Load the user-specified "cover unavailable" graphic (or default if none
163     * specified).
164     *
165     * @return void
166     * @author Thomas Schwaerzler <vufind-tech@lists.sourceforge.net>
167     */
168    public function loadUnavailable()
169    {
170        // No setting -- use default, and don't log anything:
171        if (empty($this->configuredFailImage)) {
172            $this->loadDefaultFailImage();
173            return;
174        }
175
176        // Setting found -- get "no cover" image from config.ini:
177        $noCoverImage = $this->searchTheme($this->configuredFailImage);
178
179        // If file is blank/inaccessible, log error and display default:
180        if (
181            empty($noCoverImage) || !file_exists($noCoverImage)
182            || !is_readable($noCoverImage)
183        ) {
184            $this->debug("Cannot access '{$this->configuredFailImage}'");
185            $this->loadDefaultFailImage();
186            return;
187        }
188
189        try {
190            // Get mime type from file extension:
191            $this->contentType = $this->getContentTypeFromExtension($noCoverImage);
192        } catch (\Exception $e) {
193            // Log error and bail out if file lacks a known image extension:
194            $this->debug($e->getMessage());
195            $this->loadDefaultFailImage();
196            return;
197        }
198
199        // Load the image data:
200        $this->image = file_get_contents($noCoverImage);
201    }
202
203    /**
204     * Display the default "cover unavailable" graphic.
205     *
206     * @return void
207     */
208    protected function loadDefaultFailImage()
209    {
210        $file = $this->searchTheme($this->defaultFailImage);
211        if (!file_exists($file)) {
212            throw new \Exception('Could not load default fail image.');
213        }
214        $this->contentType = $this->getContentTypeFromExtension($file);
215        $this->image = file_get_contents($file);
216    }
217
218    /**
219     * Get the content-type for a file based on extension. Throw an exception if
220     * an illegal extension is provided.
221     *
222     * @param string $filename Filename to analyze.
223     *
224     * @return string
225     * @throws \Exception
226     */
227    protected function getContentTypeFromExtension($filename)
228    {
229        $parts = explode('.', $filename);
230        $fileExtension = strtolower(end($parts));
231        if (!array_key_exists($fileExtension, $this->allowedFileExtensions)) {
232            throw new \Exception(
233                "Illegal file-extension '$fileExtension' for image '$filename'"
234            );
235        }
236
237        // Get mime type from file extension:
238        return $this->allowedFileExtensions[$fileExtension];
239    }
240}