19declare(strict_types=1);
25use ILIAS\Filesystem\Util\MemoryStreamToTempFileStream;
33 use MemoryStreamToTempFileStream;
53 $this->image = new \Imagick();
65 }
catch (\Throwable $t) {
67 $this->throwable = $t;
68 if ($this->conversion_options->throwOnError()) {
77 $requested_width = $this->conversion_options->getWidth();
78 $requested_height = $this->conversion_options->getHeight();
79 $original_image_width = $this->image->getImageWidth();
80 $original_image_height = $this->image->getImageHeight();
82 switch ($this->conversion_options->getDimensionMode()) {
88 if ($this->conversion_options->hasCrop()) {
89 $final_height = $requested_height;
90 $final_width = $requested_width;
103 if ($requested_width > 0 && $requested_height > 0) {
104 $final_width = $requested_width;
105 $final_height = $requested_height;
107 throw new \InvalidArgumentException(
'Dimension Mode does not match the given width/height');
112 if ($requested_width ===
null && $requested_height > 0) {
113 $ratio = $original_image_height / $requested_height;
114 $final_width = intval($original_image_width / $ratio);
115 $final_height = $requested_height;
118 throw new \InvalidArgumentException(
'Dimension Mode does not match the given width/height');
123 if ($requested_width > 0 && $requested_height ===
null) {
124 $ratio = $original_image_width / $requested_width;
125 $final_width = $requested_width;
126 $final_height = intval($original_image_height / $ratio);
128 throw new \InvalidArgumentException(
'Dimension Mode does not match the given width/height');
133 if ($requested_width ===
null && $requested_height ===
null) {
134 $final_width = $original_image_width;
135 $final_height = $original_image_height;
137 throw new \InvalidArgumentException(
'Dimension Mode does not match the given width/height');
142 if ($this->conversion_options->hasCrop()) {
158 if ($this->conversion_options->getOutputPath() ===
null) {
161 $this->image->writeImage($this->conversion_options->getOutputPath());
162 $this->output_stream =
Streams::ofResource(fopen($this->conversion_options->getOutputPath(),
'rb'));
165 $this->output_stream->rewind();
172 $this->image->setImageResolution(
183 $quality = $this->output_options->getQuality();
188 $this->output_options = $this->output_options->withFormat(strtolower($this->image->getImageFormat()));
193 switch ($this->output_options->getFormat()) {
195 $this->image->setImageFormat(
'webp');
196 $this->image->setImageAlphaChannel(\Imagick::ALPHACHANNEL_REMOVE);
197 $this->image = $this->image->mergeImageLayers(\Imagick::LAYERMETHOD_FLATTEN);
198 if ($quality === 0) {
199 $this->image->setOption(
'webp:lossless',
'false');
201 if ($quality === 100) {
202 $this->image->setOption(
'webp:lossless',
'true');
206 $this->image->setImageFormat(
'jpeg');
207 $this->image->setImageAlphaChannel(\Imagick::ALPHACHANNEL_REMOVE);
208 $this->image = $this->image->mergeImageLayers(\Imagick::LAYERMETHOD_FLATTEN);
209 $this->image->setImageCompression(\Imagick::COMPRESSION_JPEG);
210 $this->image->setImageCompressionQuality($quality);
213 $png_compression_level = round($quality / 100 * 9, 0);
214 if ($this->requested_background !==
null && $this->requested_background !==
'transparent') {
215 $this->image->setImageAlphaChannel(\Imagick::ALPHACHANNEL_REMOVE);
217 $this->image->setImageAlphaChannel(\Imagick::ALPHACHANNEL_ACTIVATE);
219 $this->image->setImageFormat(
'png');
220 $this->image->setOption(
'png:compression-level', (
string) $png_compression_level);
223 $this->image->stripImage();
228 $this->requested_background = $this->conversion_options->getBackgroundColor();
229 if ($this->output_options->getFormat(
231 $this->requested_background =
'#FFFFFF';
233 if ($this->output_options->getFormat(
235 $this->requested_background =
'transparent';
237 if ($this->requested_background !==
null) {
238 $this->image->setBackgroundColor(
new \ImagickPixel($this->requested_background));
244 if ($this->conversion_options->makeTemporaryFiles()) {
245 $this->stream = $this->maybeSafeToTempStream($this->stream);
247 $this->stream->rewind();
248 $this->image->readImageFile($this->stream->detach());
253 if ($this->image->getNumberImages() > 1) {
254 $this->image->setFirstIterator();
255 $first_frame = $this->image->getImage();
257 $first_frame->setImageAlphaChannel(\Imagick::ALPHACHANNEL_SET);
258 $this->image = $first_frame->coalesceImages();
262 if ($this->requested_background !==
null) {
263 $this->image->setImageBackgroundColor(
new \ImagickPixel($this->requested_background));
286 return intval(round($initial * self::RESOLUTION_FACTOR, 0));
289 protected function doCrop(
int $width,
int $height): void
291 $this->image->setGravity(\Imagick::GRAVITY_CENTER);
292 $this->image->cropThumbnailImage(
298 protected function doResize(
int $width,
int $height): void
300 $this->image->resizeImage(
303 \Imagick::FILTER_LANCZOS,
310 $this->image->thumbnailImage(
314 !$this->conversion_options->keepAspectRatio(),
Class Exception Thrown if some procedure did not get the arguments it expected.
Stream factory which enables the user to create streams without the knowledge of the concrete class.
static ofString(string $string)
Creates a new stream with an initial value.
static ofResource($resource)
Wraps an already created resource with the stream abstraction.
const DIMENSTION_MODE_RESIZE_TO_FIXED
const DIMENSTION_MODE_RESIZE_BY_WIDTH
const DIMENSION_MODE_NONE
const DIMENSTION_MODE_RESIZE_BY_HEIGHT
const DIMENSION_MODE_KEEP
FileStream $output_stream
factoredResolution(int $initial)
__construct(protected ImageConversionOptions $conversion_options, protected ImageOutputOptions $output_options, protected FileStream $stream)
doResize(int $width, int $height)
doThumbnail(int $width, int $height)
string $requested_background
doCrop(int $width, int $height)
The base interface for all filesystem streams.