Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 38
0.00% covered (danger)
0.00%
0 / 9
CRAP
0.00% covered (danger)
0.00%
0 / 1
Generator
0.00% covered (danger)
0.00%
0 / 38
0.00% covered (danger)
0.00%
0 / 9
210
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 setOptions
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
6
 initImage
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 destroyImage
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 renderPng
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
2
 generate
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
2
 getBackgroundLayer
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 getTextLayer
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 fontPath
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2
3/**
4 * Dynamic Book Cover Generator
5 *
6 * PHP version 8
7 *
8 * Copyright (C) Villanova University 2014.
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   Chris Hallberg <crhallberg@gmail.com>
26 * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
27 * @link     https://vufind.org/wiki/configuration:external_content Wiki
28 */
29
30namespace VuFind\Cover;
31
32use VuFind\Cover\Layer\LayerInterface;
33use VuFind\Cover\Layer\PluginManager as LayerManager;
34use VuFindTheme\ThemeInfo;
35
36use function count;
37
38/**
39 * Dynamic Book Cover Generator
40 *
41 * @category VuFind
42 * @package  Cover_Generator
43 * @author   Chris Hallberg <crhallberg@gmail.com>
44 * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
45 * @link     https://vufind.org/wiki/configuration:external_content Wiki
46 */
47class Generator
48{
49    /**
50     * Default settings (values used by setOptions() if not overridden).
51     *
52     * @var array
53     */
54    protected $defaultSettings = [
55        'backgroundMode' => 'grid',
56        'textMode' => 'default',
57        'authorFont' => 'DroidSerif-Bold.ttf',
58        'titleFontSize' => 7,
59        'authorFontSize' => 6,
60        'lightness' => 220,
61        'maxTitleLines' => 5,
62        'minAuthorFontSize' => 5,
63        'saturation' => 80,
64        'size' => 84,
65        'textAlign' => 'center',
66        'titleFont' => 'DroidSerif-Bold.ttf',
67        'topPadding' => 19,
68        'bottomPadding' => 3,
69        'wrapWidth' => 80,
70        'titleFillColor' => 'black',
71        'titleBorderColor' => 'none',
72        'authorFillColor' => 'white',
73        'authorBorderColor' => 'black',
74        'baseColor' => 'white',
75        'accentColor' => 'random',
76    ];
77
78    /**
79     * Active style configuration
80     *
81     * @var object
82     */
83    protected $settings;
84
85    /**
86     * Base for image
87     *
88     * @var resource
89     */
90    protected $im;
91
92    /**
93     * ThemeInfo object
94     *
95     * @var ThemeInfo
96     */
97    protected $themeTools;
98
99    /**
100     * Layer manager
101     *
102     * @var LayerManager
103     */
104    protected $layerManager;
105
106    /**
107     * Constructor
108     *
109     * @param ThemeInfo    $themeTools For font loading
110     * @param LayerManager $lm         Layer manager
111     * @param array        $settings   Overwrite styles
112     */
113    public function __construct(
114        ThemeInfo $themeTools,
115        LayerManager $lm,
116        array $settings = []
117    ) {
118        $this->themeTools = $themeTools;
119        $this->layerManager = $lm;
120        $this->setOptions($settings);
121    }
122
123    /**
124     * Set the generator options.
125     *
126     * @param array $rawSettings Overwrite styles
127     *
128     * @return void
129     */
130    public function setOptions($rawSettings)
131    {
132        // Merge incoming settings with defaults:
133        $settings = $rawSettings + $this->defaultSettings;
134
135        // Adjust font paths:
136        $settings['authorFont'] = $this->fontPath($settings['authorFont']);
137        $settings['titleFont']  = $this->fontPath($settings['titleFont']);
138
139        // Determine final dimensions:
140        $parts = explode('x', strtolower($settings['size']));
141        if (count($parts) < 2) {
142            $settings['width'] = $settings['height'] = $parts[0];
143        } else {
144            [$settings['width'], $settings['height']] = $parts;
145        }
146
147        // Store the results as an object:
148        $this->settings = (object)$settings;
149
150        // Reinitialize everything based on settings:
151        $this->initImage();
152    }
153
154    /**
155     * Initialize the image in the object.
156     *
157     * @return void
158     */
159    protected function initImage()
160    {
161        // Create image
162        $this->im = imagecreate($this->settings->width, $this->settings->height);
163        if (!$this->im) {
164            throw new \Exception('Cannot Initialize new GD image stream');
165        }
166    }
167
168    /**
169     * Clear the resources associated with the image in the object.
170     *
171     * @return void
172     */
173    protected function destroyImage()
174    {
175        imagedestroy($this->im);
176    }
177
178    /**
179     * Render the contents of the image in the object to a PNG; return as string.
180     *
181     * @return string
182     */
183    protected function renderPng()
184    {
185        ob_start();
186        imagepng($this->im);
187        $img = ob_get_contents();
188        ob_end_clean();
189        return $img;
190    }
191
192    /**
193     * Generates a dynamic cover image from elements of the item
194     *
195     * @param string $title      Title of the book
196     * @param string $author     Author of the book
197     * @param string $callnumber Callnumber of the book
198     *
199     * @return string contents of image file
200     */
201    public function generate($title, $author, $callnumber = null)
202    {
203        $details = compact('title', 'author', 'callnumber');
204
205        // Build the image
206        $this->getBackgroundLayer()->render($this->im, $details, $this->settings);
207        $this->getTextLayer()->render($this->im, $details, $this->settings);
208
209        // Render the image
210        $png = $this->renderPng();
211        $this->destroyImage();
212        return $png;
213    }
214
215    /**
216     * Get the layer plugin for the background
217     *
218     * @return LayerInterface
219     */
220    protected function getBackgroundLayer()
221    {
222        $service = strtolower($this->settings->backgroundMode) . 'background';
223        return $this->layerManager->get(
224            $this->layerManager->has($service) ? $service : 'gridbackground'
225        );
226    }
227
228    /**
229     * Get the layer plugin for the text
230     *
231     * @return LayerInterface
232     */
233    protected function getTextLayer()
234    {
235        $service = strtolower($this->settings->textMode) . 'text';
236        return $this->layerManager->get(
237            $this->layerManager->has($service) ? $service : 'defaulttext'
238        );
239    }
240
241    /**
242     * Find font in the theme folder
243     *
244     * @param string $font Font_name.ttf
245     *
246     * @return string file path
247     */
248    protected function fontPath($font)
249    {
250        // Check all supported image formats:
251        $filenames = ['css/font/' . $font];
252        $fileMatch = $this->themeTools->findContainingTheme($filenames, true);
253        return empty($fileMatch) ? false : $fileMatch;
254    }
255}