Skip to content

Commit 54e67bd

Browse files
committed
Add CropResizer
1 parent 54de03d commit 54e67bd

File tree

4 files changed

+414
-0
lines changed

4 files changed

+414
-0
lines changed

src/DependencyInjection/SonataMediaExtension.php

+10
Original file line numberDiff line numberDiff line change
@@ -548,6 +548,16 @@ private function configureAdapters(ContainerBuilder $container, array $config):
548548

549549
private function configureResizers(ContainerBuilder $container, array $config): void
550550
{
551+
if ($container->hasParameter('sonata.media.resizer.crop.class')) {
552+
$class = $container->getParameter('sonata.media.resizer.crop.class');
553+
$definition = new Definition($class, [
554+
new Reference('sonata.media.adapter.image.default'),
555+
new Reference('sonata.media.metadata.proxy'),
556+
]);
557+
$definition->addTag('sonata.media.resizer');
558+
$container->setDefinition('sonata.media.resizer.crop', $definition);
559+
}
560+
551561
if ($container->hasParameter('sonata.media.resizer.simple.class')) {
552562
$class = $container->getParameter('sonata.media.resizer.simple.class');
553563
$definition = new Definition($class, [

src/Resizer/CropResizer.php

+166
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/*
6+
* This file is part of the Sonata Project package.
7+
*
8+
* (c) Thomas Rabaix <[email protected]>
9+
*
10+
* For the full copyright and license information, please view the LICENSE
11+
* file that was distributed with this source code.
12+
*/
13+
14+
namespace Sonata\MediaBundle\Resizer;
15+
16+
use Gaufrette\File;
17+
use Imagine\Image\Box;
18+
use Imagine\Image\ImageInterface;
19+
use Imagine\Image\ImagineInterface;
20+
use Imagine\Image\Point;
21+
use Sonata\MediaBundle\Metadata\MetadataBuilderInterface;
22+
use Sonata\MediaBundle\Model\MediaInterface;
23+
24+
/**
25+
* @author Christian Gripp <[email protected]>
26+
*/
27+
final class CropResizer implements ResizerInterface
28+
{
29+
/**
30+
* @var ImagineInterface
31+
*/
32+
private $adapter;
33+
34+
/**
35+
* @var MetadataBuilderInterface
36+
*/
37+
private $metadata;
38+
39+
public function __construct(ImagineInterface $adapter, MetadataBuilderInterface $metadata)
40+
{
41+
$this->adapter = $adapter;
42+
$this->metadata = $metadata;
43+
}
44+
45+
public function resize(MediaInterface $media, File $in, File $out, $format, array $settings): void
46+
{
47+
if (!isset($settings['width'])) {
48+
throw new \RuntimeException(sprintf(
49+
'Width parameter is missing in context "%s" for provider "%s"',
50+
$media->getContext(),
51+
$media->getProviderName()
52+
));
53+
}
54+
55+
if (!isset($settings['height'])) {
56+
throw new \RuntimeException(sprintf(
57+
'Height parameter is missing in context "%s" for provider "%s"',
58+
$media->getContext(),
59+
$media->getProviderName()
60+
));
61+
}
62+
63+
$image = $this->adapter->load($in->getContent());
64+
65+
$sourceSize = $media->getBox();
66+
$targetSize = $this->createTargetBox($settings);
67+
68+
if ($this->shouldModify($sourceSize, $targetSize)) {
69+
$image = $this->cropImage($image, $sourceSize, $targetSize);
70+
}
71+
72+
// Always change format and quality
73+
$content = $image->get($format, [
74+
'quality' => $settings['quality'],
75+
]);
76+
77+
$out->setContent($content, $this->metadata->get($media, $out->getName()));
78+
}
79+
80+
public function getBox(MediaInterface $media, array $settings)
81+
{
82+
$sourceSize = $media->getBox();
83+
$targetSize = $this->createTargetBox($settings);
84+
85+
return new Box(
86+
min($sourceSize->getWidth(), $targetSize->getWidth()),
87+
min($sourceSize->getHeight(), $targetSize->getHeight())
88+
);
89+
}
90+
91+
/**
92+
* @param array<string, mixed> $settings
93+
*/
94+
private function createTargetBox(array $settings): Box
95+
{
96+
return new Box($settings['width'], $settings['height']);
97+
}
98+
99+
private function shouldModify(Box $sourceSize, Box $targetSize): bool
100+
{
101+
return !($sourceSize->getWidth() <= $targetSize->getWidth() && $sourceSize->getHeight() <= $targetSize->getHeight());
102+
}
103+
104+
private function shouldResize(Box $sourceSize, Box $targetSize): bool
105+
{
106+
if ($sourceSize->getWidth() <= $targetSize->getWidth()) {
107+
return false;
108+
}
109+
110+
return $sourceSize->getHeight() > $targetSize->getHeight();
111+
}
112+
113+
private function shouldCrop(Box $sourceSize, Box $targetSize): bool
114+
{
115+
return $sourceSize->getWidth() > $targetSize->getWidth() || $sourceSize->getHeight() > $targetSize->getHeight();
116+
}
117+
118+
private function cropImage(ImageInterface $image, Box $sourceSize, Box $targetSize): ImageInterface
119+
{
120+
if ($this->shouldResize($sourceSize, $targetSize)) {
121+
$scaleSize = $this->createBox($sourceSize, $targetSize, false);
122+
123+
$image = $image->thumbnail($scaleSize, 'outbound');
124+
125+
$sourceSize = $scaleSize;
126+
}
127+
128+
if ($this->shouldCrop($sourceSize, $targetSize)) {
129+
$cropSize = new Box(
130+
min($sourceSize->getWidth(), $targetSize->getWidth()),
131+
min($sourceSize->getHeight(), $targetSize->getHeight())
132+
);
133+
134+
$point = new Point(
135+
(int) (($sourceSize->getWidth() - $cropSize->getWidth()) / 2),
136+
(int) (($sourceSize->getHeight() - $cropSize->getHeight()) / 2)
137+
);
138+
139+
$image = $image->crop($point, $cropSize);
140+
}
141+
142+
return $image;
143+
}
144+
145+
private function createBox(Box $sourceSize, Box $targetSize, bool $smallest = true): Box
146+
{
147+
$widthRatio = (float) ($targetSize->getWidth() / $sourceSize->getWidth());
148+
$heightRatio = (float) ($targetSize->getHeight() / $sourceSize->getHeight());
149+
150+
if (0.0 !== $widthRatio - $heightRatio) {
151+
return $sourceSize->scale(
152+
$smallest ? min($widthRatio, $heightRatio) : max($widthRatio, $heightRatio)
153+
);
154+
}
155+
156+
if ($targetSize->getHeight() >= $sourceSize->getHeight()) {
157+
return $sourceSize;
158+
}
159+
160+
if ($targetSize->getWidth() >= $sourceSize->getWidth()) {
161+
return $sourceSize;
162+
}
163+
164+
return $targetSize;
165+
}
166+
}

src/Resources/config/media.xml

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
<?xml version="1.0" encoding="UTF-8"?>
22
<container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
33
<parameters>
4+
<parameter key="sonata.media.resizer.crop.class">Sonata\MediaBundle\Resizer\CropResizer</parameter>
45
<parameter key="sonata.media.resizer.simple.class">Sonata\MediaBundle\Resizer\SimpleResizer</parameter>
56
<parameter key="sonata.media.resizer.square.class">Sonata\MediaBundle\Resizer\SquareResizer</parameter>
67
<parameter key="sonata.media.adapter.image.gd.class">Imagine\Gd\Imagine</parameter>

0 commit comments

Comments
 (0)