From 9c64bcceaebf596fb0a49efd0f11579f30164542 Mon Sep 17 00:00:00 2001 From: Pavel Zotikov Date: Fri, 10 Jan 2025 16:03:58 +0300 Subject: [PATCH] Add resource limits for Imagick to manage memory and disk usage --- src/SocialMediaImageGenerator/Generator.php | 8 ++++++ .../ImagickResourceLimiter.php | 18 +++++++++++++ src/SocialMediaImageGenerator/Magnetic.php | 27 +++++++++++++++++++ .../Types/Background.php | 7 +++++ .../Types/BackgroundImage.php | 5 ++++ src/SocialMediaImageGenerator/Types/Image.php | 11 ++++++++ .../Types/ImageGravityCenter.php | 4 +++ src/SocialMediaImageGenerator/Types/Text.php | 9 +++++++ 8 files changed, 89 insertions(+) create mode 100644 src/SocialMediaImageGenerator/ImagickResourceLimiter.php diff --git a/src/SocialMediaImageGenerator/Generator.php b/src/SocialMediaImageGenerator/Generator.php index 3dd52dd..45bc507 100644 --- a/src/SocialMediaImageGenerator/Generator.php +++ b/src/SocialMediaImageGenerator/Generator.php @@ -16,9 +16,14 @@ class Generator private $layers_counter = 0; + /** + * @throws \ImagickException + */ public function __construct(int $width, int $height, string $color) { $this->image = new \Imagick(); + ImagickResourceLimiter::applyLimits($this->image); + $this->image->setResolution(300, 300); $this->image->newImage($width, $height, $color); @@ -59,6 +64,9 @@ public function loadLayers(array $array): self return $this; } + /** + * @throws \ImagickException + */ public function addLayer(AbstractType $layer): self { if ($layer->getName()) { diff --git a/src/SocialMediaImageGenerator/ImagickResourceLimiter.php b/src/SocialMediaImageGenerator/ImagickResourceLimiter.php new file mode 100644 index 0000000..dd0238e --- /dev/null +++ b/src/SocialMediaImageGenerator/ImagickResourceLimiter.php @@ -0,0 +1,18 @@ +setResourceLimit(\Imagick::RESOURCETYPE_MEMORY, 256); + $im->setResourceLimit(\Imagick::RESOURCETYPE_MAP, 256); + $im->setResourceLimit(\Imagick::RESOURCETYPE_AREA, 1512); + $im->setResourceLimit(\Imagick::RESOURCETYPE_FILE, 768); + $im->setResourceLimit(\Imagick::RESOURCETYPE_DISK, -1); + } +} diff --git a/src/SocialMediaImageGenerator/Magnetic.php b/src/SocialMediaImageGenerator/Magnetic.php index 86f4abe..3601070 100644 --- a/src/SocialMediaImageGenerator/Magnetic.php +++ b/src/SocialMediaImageGenerator/Magnetic.php @@ -15,9 +15,13 @@ class Magnetic /** @var AbstractType */ private $to_layer; + /** + * @throws \ImagickException + */ public function __construct(array &$layers, array &$layers_with_names) { $this->im = new \Imagick(); + ImagickResourceLimiter::applyLimits($this->im); $this->layers = &$layers; $this->layers_with_names = &$layers_with_names; @@ -73,6 +77,7 @@ private function horizontalCenter(AbstractType &$layer) } else { /*$im = new \Imagick(); + ImagickResourceLimiter::applyLimits($im); $layer_info = $im->queryFontMetrics($layer->$this->getImage(), $layer->getText());*/ $layer->setX( @@ -89,6 +94,7 @@ private function horizontalCenter(AbstractType &$layer) if ($layer->getImage() instanceof \Imagick) { /*$im = new \Imagick(); + ImagickResourceLimiter::applyLimits($im); $to_layer_info = $im->queryFontMetrics($this->to_layer->getImage(), $this->to_layer->getText());*/ $layer->setX( @@ -122,6 +128,7 @@ private function left(AbstractType &$layer) /*if ($layer_image instanceof \ImagickDraw) { $im = new \Imagick(); + ImagickResourceLimiter::applyLimits($im); $layer_info = $im->queryFontMetrics($layer_image, $layer->getText()); if ($layer->getAlignment() === \Imagick::ALIGN_CENTER) { @@ -145,6 +152,7 @@ private function right(AbstractType &$layer) /*if ($layer_image instanceof \ImagickDraw) { $im = new \Imagick(); + ImagickResourceLimiter::applyLimits($im); $layer_info = $im->queryFontMetrics($layer_image, $layer->getText()); if ($layer->getAlignment() === \Imagick::ALIGN_CENTER) { @@ -183,6 +191,9 @@ private function right(AbstractType &$layer) unset($layer); } + /** + * @throws \ImagickException + */ private function verticalCenter(AbstractType &$layer) { if ($this->to_layer->getImage()instanceof \Imagick) { @@ -193,6 +204,8 @@ private function verticalCenter(AbstractType &$layer) )); } else { $im = new \Imagick(); + ImagickResourceLimiter::applyLimits($im); + $layer_info = $im->queryFontMetrics($layer->getImage(), $layer->getText()); $layer->setY((int) ( @@ -205,6 +218,8 @@ private function verticalCenter(AbstractType &$layer) } elseif ($this->to_layer->getImage() instanceof \ImagickDraw) { if ($layer->getImage() instanceof \Imagick) { $im = new \Imagick(); + ImagickResourceLimiter::applyLimits($im); + $to_layer_info = $im->queryFontMetrics($this->to_layer->getImage(), $this->to_layer->getText()); $layer->setY((int) ( @@ -215,6 +230,8 @@ private function verticalCenter(AbstractType &$layer) )); } else { $im = new \Imagick(); + ImagickResourceLimiter::applyLimits($im); + $layer_info = $im->queryFontMetrics($layer->getImage(), $layer->getText()); $to_layer_info = $im->queryFontMetrics($this->to_layer->getImage(), $this->to_layer->getText()); @@ -240,6 +257,8 @@ private function top(AbstractType &$layer) )); } elseif ($layer->getImage() instanceof \ImagickDraw) { $im = new \Imagick(); + ImagickResourceLimiter::applyLimits($im); + $layer_info = $im->queryFontMetrics($layer->getImage(), $layer->getText()); $layer->setY((int) ( @@ -249,6 +268,8 @@ private function top(AbstractType &$layer) } elseif ($this->to_layer->getImage() instanceof \ImagickDraw) { if ($layer->getImage() instanceof \Imagick) { $im = new \Imagick(); + ImagickResourceLimiter::applyLimits($im); + $to_layer_info = $im->queryFontMetrics($this->to_layer->getImage(), $this->to_layer->getText()); $layer->setY((int) ( @@ -275,6 +296,8 @@ private function bottom(AbstractType &$layer) )); } elseif ($layer->getImage() instanceof \ImagickDraw) { $im = new \Imagick(); + ImagickResourceLimiter::applyLimits($im); + $layer_info = $im->queryFontMetrics($layer->getImage(), $layer->getText()); $layer->setY((int) ( @@ -287,6 +310,8 @@ private function bottom(AbstractType &$layer) } elseif ($this->to_layer->getImage() instanceof \ImagickDraw) { if ($layer->getImage() instanceof \Imagick) { $im = new \Imagick(); + ImagickResourceLimiter::applyLimits($im); + $to_layer_info = $im->queryFontMetrics($this->to_layer->getImage(), $this->to_layer->getText()); $layer->setY((int) ( @@ -297,6 +322,8 @@ private function bottom(AbstractType &$layer) )); } elseif ($layer->getImage() instanceof \ImagickDraw) { $im = new \Imagick(); + ImagickResourceLimiter::applyLimits($im); + $to_layer_info = $im->queryFontMetrics($this->to_layer->getImage(), $this->to_layer->getText()); $layer->setY((int) ( diff --git a/src/SocialMediaImageGenerator/Types/Background.php b/src/SocialMediaImageGenerator/Types/Background.php index d5af905..ddac540 100644 --- a/src/SocialMediaImageGenerator/Types/Background.php +++ b/src/SocialMediaImageGenerator/Types/Background.php @@ -2,11 +2,16 @@ declare(strict_types=1); namespace SocialMediaImageGenerator\Types; +use SocialMediaImageGenerator\ImagickResourceLimiter; + class Background extends AbstractType { protected $color = '#FFFFFF'; protected $layer; + /** + * @throws \ImagickException + */ public function getImage(): \Imagick { if ($this->layer) { @@ -14,6 +19,8 @@ public function getImage(): \Imagick } $layer = new \Imagick(); + ImagickResourceLimiter::applyLimits($layer); + $layer->newImage($this->getWidth(), $this->getHeight(), $this->getColor()); $this->layer = $layer; diff --git a/src/SocialMediaImageGenerator/Types/BackgroundImage.php b/src/SocialMediaImageGenerator/Types/BackgroundImage.php index 518a6ab..98366c7 100644 --- a/src/SocialMediaImageGenerator/Types/BackgroundImage.php +++ b/src/SocialMediaImageGenerator/Types/BackgroundImage.php @@ -2,6 +2,7 @@ declare(strict_types=1); namespace SocialMediaImageGenerator\Types; +use SocialMediaImageGenerator\ImagickResourceLimiter; use SocialMediaImageGenerator\Properties\Blackout; class BackgroundImage extends Background @@ -16,6 +17,8 @@ public function getImage(): \Imagick } $layer = new \Imagick(); + ImagickResourceLimiter::applyLimits($layer); + $layer->readImage($this->getPath()); $layer->setImageInterpolateMethod(\Imagick::INTERPOLATE_BICUBIC); @@ -40,6 +43,8 @@ public function getImage(): \Imagick if ($this->getBlackout()) { $blackout_draw = new \Imagick(); + ImagickResourceLimiter::applyLimits($blackout_draw); + $blackout_draw->newImage($width, $height, new \ImagickPixel($this->getBlackout()->getColor())); if (method_exists($blackout_draw, 'setImageAlpha')) { diff --git a/src/SocialMediaImageGenerator/Types/Image.php b/src/SocialMediaImageGenerator/Types/Image.php index 45de58c..b138160 100644 --- a/src/SocialMediaImageGenerator/Types/Image.php +++ b/src/SocialMediaImageGenerator/Types/Image.php @@ -2,6 +2,7 @@ declare(strict_types=1); namespace SocialMediaImageGenerator\Types; +use SocialMediaImageGenerator\ImagickResourceLimiter; use SocialMediaImageGenerator\Properties\Transform; class Image extends AbstractType @@ -14,6 +15,10 @@ class Image extends AbstractType protected $round_corners = 0; + /** + * @throws \ImagickDrawException + * @throws \ImagickException + */ public function getImage(bool $no_resize = false): \Imagick { if ($this->layer) { @@ -21,6 +26,8 @@ public function getImage(bool $no_resize = false): \Imagick } $layer = new \Imagick(); + ImagickResourceLimiter::applyLimits($layer); + $layer->readImage($this->getPath()); $layer->setImageResolution(300, 300); $layer->resampleImage(300, 300, \Imagick::FILTER_LANCZOS, 0); @@ -31,6 +38,8 @@ public function getImage(bool $no_resize = false): \Imagick if ($this->getRoundCorners()) { $mask = new \Imagick(); + ImagickResourceLimiter::applyLimits($mask); + $mask->newImage($this->getWidth(), $this->getHeight(), new \ImagickPixel('transparent')); $shape = new \ImagickDraw(); @@ -47,6 +56,8 @@ public function getImage(bool $no_resize = false): \Imagick if ($this->getFill()) { $layer_colorize = new \Imagick(); + ImagickResourceLimiter::applyLimits($layer_colorize); + $layer_colorize->newImage($layer->getImageWidth(), $layer->getImageHeight(), $this->getFill()); $layer_colorize->compositeImage($layer, \Imagick::COMPOSITE_COPYOPACITY, 0, 0); diff --git a/src/SocialMediaImageGenerator/Types/ImageGravityCenter.php b/src/SocialMediaImageGenerator/Types/ImageGravityCenter.php index a3f65f1..febfbca 100644 --- a/src/SocialMediaImageGenerator/Types/ImageGravityCenter.php +++ b/src/SocialMediaImageGenerator/Types/ImageGravityCenter.php @@ -2,6 +2,8 @@ declare(strict_types=1); namespace SocialMediaImageGenerator\Types; +use SocialMediaImageGenerator\ImagickResourceLimiter; + class ImageGravityCenter extends Image { public function getImage(bool $no_resize = true): \Imagick @@ -17,6 +19,8 @@ public function getImage(bool $no_resize = true): \Imagick if ($this->fill) { $layer_colorize = new \Imagick(); + ImagickResourceLimiter::applyLimits($layer_colorize); + $layer_colorize->newImage($layer->getImageWidth(), $layer->getImageHeight(), $this->fill); $layer_colorize->compositeImage($layer, \Imagick::COMPOSITE_COPYOPACITY, 0, 0); $layer = $layer_colorize; diff --git a/src/SocialMediaImageGenerator/Types/Text.php b/src/SocialMediaImageGenerator/Types/Text.php index bb1447b..ac3df72 100644 --- a/src/SocialMediaImageGenerator/Types/Text.php +++ b/src/SocialMediaImageGenerator/Types/Text.php @@ -2,6 +2,7 @@ declare(strict_types=1); namespace SocialMediaImageGenerator\Types; +use SocialMediaImageGenerator\ImagickResourceLimiter; use SocialMediaImageGenerator\Properties\Font as FontProperties; use SocialMediaImageGenerator\Properties\Underline as UnderlineProperties; @@ -78,9 +79,13 @@ private function sliceTextByLines(string $text, int $lines, string $symbol = '.. return implode("\n", $slice_text_arr) . $symbol; } + /** + * @throws \ImagickException + */ private function getMetricsForEachOfLine(string $text, \ImagickDraw $draw): array { $im = new \Imagick(); + ImagickResourceLimiter::applyLimits($im); $info = []; foreach (explode("\n", $text) as $string) { @@ -91,9 +96,13 @@ private function getMetricsForEachOfLine(string $text, \ImagickDraw $draw): arra return $info; } + /** + * @throws \ImagickException + */ private function wordwrap(string $text, int $width, \ImagickDraw $draw, int &$number_of_lines = 1): string { $im = new \Imagick(); + ImagickResourceLimiter::applyLimits($im); $final_text = ""; $words = explode(' ', $text);