CommandsHelper::sortWords()   A
last analyzed

Complexity

Conditions 3
Paths 2

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 6
rs 10
c 0
b 0
f 0
cc 3
nc 2
nop 3
1
<?php
2
3
namespace SixtyNine\Cloud\Command;
4
5
use Imagine\Image\ImageInterface;
6
use SixtyNine\Cloud\Builder\CloudBuilder;
7
use SixtyNine\Cloud\Builder\FiltersBuilder;
8
use SixtyNine\Cloud\Builder\PalettesBuilder;
9
use SixtyNine\Cloud\Builder\WordsListBuilder;
10
use SixtyNine\Cloud\Color\ColorGeneratorInterface;
11
use SixtyNine\Cloud\Color\RandomColorGenerator;
12
use SixtyNine\Cloud\Color\RotateColorGenerator;
13
use SixtyNine\Cloud\Factory\FontsFactory;
14
use SixtyNine\Cloud\Factory\PlacerFactory;
15
use SixtyNine\Cloud\FontSize\BoostFontSizeGenerator;
16
use SixtyNine\Cloud\FontSize\DimFontSizeGenerator;
17
use SixtyNine\Cloud\FontSize\FontSizeGeneratorInterface;
18
use SixtyNine\Cloud\FontSize\LinearFontSizeGenerator;
19
use SixtyNine\Cloud\Placer\PlacerInterface;
20
use SixtyNine\Cloud\Renderer\CloudRenderer;
21
use Symfony\Component\Console\Input\InputInterface;
22
use Symfony\Component\Stopwatch\Stopwatch;
23
use Webmozart\Assert\Assert;
24
25
class CommandsHelper
26
{
27
    protected $fontSizeBoosts = array('linear', 'dim', 'boost');
28
    protected $paletteTypes = array('cycle', 'random');
29
    protected $outputFormats = array('gif', 'jpeg', 'png');
30
31
    /**
32
     * @param string $name
33
     * @return PlacerInterface
34
     * @throws \InvalidArgumentException
35
     */
36
    public function getPlacer($name)
37
    {
38
        $availablePlacers = PlacerFactory::getInstance()->getPlacersNames();
39
40
        if ($name) {
41
            Assert::oneOf($name, $availablePlacers, 'Word placer not found: ' . $name);
42
            return $name;
43
        }
44
45
        Assert::notEmpty($availablePlacers, 'No word placers available');
46
        return $availablePlacers[0];
47
    }
48
49
    /**
50
     * @param FontsFactory $factory
51
     * @param string $font
52
     * @return string
53
     * @throws \InvalidArgumentException
54
     */
55
    public function getFont(FontsFactory $factory, $font)
56
    {
57
        if ($font) {
58
            return $font;
59
        }
60
61
        Assert::notEmpty($factory->getFonts(), 'No font file found');
62
        return $factory->getFonts()[0];
63
    }
64
65
    /**
66
     * @param string $type
67
     * @return FontSizeGeneratorInterface
68
     * @throws \InvalidArgumentException
69
     */
70
    public function getFontSizeGenerator($type = 'linear')
71
    {
72
        Assert::oneOf($type, $this->fontSizeBoosts, 'Invalid font size boost: ' . $type);
73
74
        $generatorClass = LinearFontSizeGenerator::class;
75
        if ($type === 'dim') {
76
            $generatorClass = DimFontSizeGenerator::class;
77
        }
78
        if ($type === 'boost') {
79
            $generatorClass = BoostFontSizeGenerator::class;
80
        }
81
82
        return new $generatorClass();
83
    }
84
85
    /**
86
     * @param string $paletteName
87
     * @param string $paletteType
88
     * @param string $palettesFile
89
     * @return bool|ColorGeneratorInterface
90
     * @throws \InvalidArgumentException
91
     */
92
    public function getColorGenerator($paletteName, $paletteType, $palettesFile = null)
93
    {
94
        if ($paletteName && $paletteType) {
95
96
            Assert::oneOf($paletteType, $this->paletteTypes, 'Palette type must be either "cycle" or "random"');
97
98
            $file = $palettesFile
99
                ? $palettesFile
100
                : __DIR__ . '/../Resources/palettes.yml'
101
            ;
102
            $paletteBuilder = PalettesBuilder::create()->importPalettes($file);
103
104
            $palette = $paletteBuilder->getNamedPalette($paletteName);
105
            $generatorClass = ($paletteType === 'cycle')
106
                ? RotateColorGenerator::class
107
                : RandomColorGenerator::class
108
            ;
109
            return new $generatorClass($palette);
110
        }
111
112
        return false;
113
    }
114
115
    /**
116
     * @param ImageInterface $image
117
     * @param string $outputFormat
118
     * @param string $outputFile
119
     * @throws \InvalidArgumentException
120
     */
121
    public function output(ImageInterface $image, $outputFormat, $outputFile = null)
122
    {
123
        Assert::oneOf($outputFormat, $this->outputFormats, 'Invalid output format: ' . $outputFormat);
124
125
        if ($outputFile) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $outputFile of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
126
            $image->save($outputFile, array('format' => $outputFormat));
127
            return;
128
        }
129
130
        echo $image->get($outputFormat);
131
    }
132
133
    /**
134
     * @param CloudBuilder $cloudBuilder
135
     * @param FontsFactory $factory
136
     * @param PlacerInterface $placer
137
     * @param bool $renderBoxes
138
     * @return \Imagine\Gd\Image|ImageInterface
139
     */
140
    protected function render(
141
        CloudBuilder $cloudBuilder,
142
        FontsFactory $factory,
143
        PlacerInterface $placer = null,
144
        $renderBoxes = false,
145
        $renderMask = false
146
    ) {
147
        $renderer = new CloudRenderer($cloudBuilder->build(), $factory);
148
149
        if ($renderBoxes) {
150
            $renderer->renderBoundingBoxes('#888888');
151
        }
152
153
        if ($placer) {
154
            $renderer->renderUsher($placer, '#333333');
155
        }
156
157
        if ($renderMask) {
158
            $renderer->renderMask($cloudBuilder->getMask(), '#ababab');
159
        }
160
161
        $renderer->renderCloud();
162
163
        return $renderer->getImage();
164
    }
165
166
    /**
167
     * @param int $minWordLength
168
     * @param int $maxWordLength
169
     * @param null $changeCase
170
     * @param bool $noRemoveNumbers
171
     * @param bool $noRemoveUnwanted
172
     * @param bool $noRemoveTrailing
173
     * @return FiltersBuilder
174
     */
175
    public function getFilterBuilder(
176
        $minWordLength,
177
        $maxWordLength,
178
        $changeCase = null,
179
        $noRemoveNumbers = false,
180
        $noRemoveUnwanted = false,
181
        $noRemoveTrailing = false
182
    ) {
183
        $filtersBuilder = FiltersBuilder::create()
184
            ->setMinLength($minWordLength)
185
            ->setMaxLength($maxWordLength)
186
            ->setRemoveNumbers(!$noRemoveNumbers)
187
            ->setRemoveUnwanted(!$noRemoveTrailing)
188
            ->setRemoveTrailing(!$noRemoveUnwanted)
189
        ;
190
191
        if ($changeCase && in_array($changeCase, $filtersBuilder->getAllowedCase())) {
192
            $filtersBuilder->setCase($changeCase);
193
        }
194
195
        return $filtersBuilder;
196
    }
197
198
    /**
199
     * @param WordsListBuilder $builder
200
     * @param string $type
201
     * @param string $file
202
     * @param string $url
203
     */
204
    public function insertWords(WordsListBuilder $builder, $type, $file = null, $url = null)
205
    {
206
        Assert::true(null !== $file || null !== $url);
207
        Assert::oneOf($type, array('from-url', 'from-file'), 'Invalid type for createCloud: ' . $type);
208
209
        if ($type === 'from-file') {
210
            Assert::fileExists($file, 'File not found: ' . $file);
211
            $builder->importWords(file_get_contents($file));
212
        } else {
213
            $builder->importUrl($url);
214
        }
215
216
    }
217
218
    /**
219
     * @param WordsListBuilder $builder
220
     * @param string $sortBy
221
     * @param string $sortOrder
222
     */
223
    public function sortWords(WordsListBuilder $builder, $sortBy, $sortOrder)
224
    {
225
        if ($sortBy && $sortOrder) {
226
            $builder->sort($sortBy, $sortOrder);
227
        }
228
    }
229
230
    /**
231
     * @param string $type
232
     * @param InputInterface $input
233
     * @throws \InvalidArgumentException
234
     */
235
    public function createCloud($type, InputInterface $input)
236
    {
237
        Assert::oneOf($type, array('from-url', 'from-file'), 'Invalid type for createCloud: ' . $type);
238
239
        $stopwatch = new Stopwatch();
240
        $stopwatch->start('createCloud');
241
242
        // Build the filters
243
        $filtersBuilder = $this->getFilterBuilder(
244
            $input->getOption('min-word-length'),
245
            $input->getOption('max-word-length'),
246
            $input->getOption('case'),
247
            $input->getOption('no-remove-numbers'),
248
            $input->getOption('no-remove-unwanted'),
249
            $input->getOption('no-remove-trailing')
250
        );
251
252
        // Create a placer
253
        $placerName = $this->getPlacer($input->getOption('placer'));
254
        $placer = PlacerFactory::getInstance()->getPlacer(
255
            $placerName,
256
            $input->getOption('width'),
257
            $input->getOption('height')
258
        );
259
260
        // Get the font file
261
        $fontsPath = $input->getOption('fonts-path')
262
            ? realpath($input->getOption('fonts-path'))
263
            : constant('BASE_PATH') . '/fonts'
264
        ;
265
        $factory = FontsFactory::create($fontsPath);
266
        $font = $this->getFont($factory, $input->getOption('font'));
267
268
        // Create the list builder
269
        $listBuilder = WordsListBuilder::create()
270
            ->setMaxWords($input->getOption('max-word-count'))
271
            ->setFilters($filtersBuilder->build())
272
            ->randomizeOrientation($input->getOption('vertical-probability'))
273
        ;
274
275
        $this->insertWords($listBuilder, $type, $input->getOption('save-to-file'), $input->getArgument('url'));
0 ignored issues
show
Bug introduced by
It seems like $input->getArgument('url') targeting Symfony\Component\Consol...nterface::getArgument() can also be of type array<integer,string>; however, SixtyNine\Cloud\Command\...dsHelper::insertWords() does only seem to accept string|null, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
276
277
        $this->sortWords($listBuilder, $input->getOption('sort-by'), $input->getOption('sort-order'));
278
279
        // Apply a color generator if needed
280
        $colorGenerator = $this->getColorGenerator(
281
            $input->getOption('palette'),
282
            $input->getOption('palette-type'),
283
            $input->getOption('palettes-file')
284
        );
285
286
        if ($colorGenerator) {
287
            $listBuilder->randomizeColors($colorGenerator);
288
        }
289
290
        // Build the list
291
        $list = $listBuilder->build('list');
292
293
        // Create a cloud builder
294
        $cloudBuilder = CloudBuilder::create($factory)
295
            ->setBackgroundColor($input->getOption('background-color'))
296
            ->setDimension($input->getOption('width'), $input->getOption('height'))
297
            ->setFont($font)
298
            ->setFontSizes($input->getOption('min-font-size'), $input->getOption('max-font-size'))
299
            ->setPlacer($placerName)
300
            ->setSizeGenerator($this->getFontSizeGenerator($input->getOption('font-size-boost')))
301
            ->useList($list)
302
        ;
303
304
        if ($input->getOption('precise')) {
305
            $cloudBuilder->setPrecise();
306
        }
307
308
        // Render the cloud and show the bounding boxes and the usher if needed
309
        $image = $this->render(
310
            $cloudBuilder,
311
            $factory,
312
            $input->getOption('render-usher') ? $placer : null,
313
            $input->getOption('render-boxes'),
314
            $input->getOption('render-mask')
315
        );
316
317
        $this->output($image, $input->getOption('format'), $input->getOption('save-to-file'));
318
319
        $event = $stopwatch->stop('createCloud');
320
        return $event;
321
    }
322
}
323