diff --git a/Engine/Modules/Core/Helper/EmbeddedUrlHelper.php b/Engine/Modules/Core/Helper/EmbeddedUrlHelper.php
new file mode 100644
index 000000000..055dcbdd0
--- /dev/null
+++ b/Engine/Modules/Core/Helper/EmbeddedUrlHelper.php
@@ -0,0 +1,39 @@
+ $size If array, img tag will be replaced by picture tag
+ *
+ * @return string
+ */
+ public static function resolveSingle(?string $text, $size = 0) : string
+ {
+ if (empty($text)) {
+ return $text;
+ }
+ // Array
+ // (
+ // [0] =>
+ // [1] =>
var/public/images/path
+ // [4] => " id=asd>
+ // [5] => id=asd
+ // )
+ $newText = preg_replace_callback(
+ '/(
)/m',
+ function (array $matches) use ($size) {
+ $imagePath = trim($matches[3]);
+ $attributes = rtrim(' ' . trim($matches[2]) . ' ' . trim($matches[5]));
+ $attributes = preg_replace('/(.*?class=[\'"])(.*?)([\'"].*?)/', '$1$2 lazy$3', $attributes) ?? $attributes;
+
+ $imagePaths = ImageHelper::compressSingle($imagePath, $size);
+ if ($imagePaths === null) {
+ return $matches[0];
+ }
+ if (is_array($imagePaths)) {
+ $sources = '';
+ $minBreakpoint = 999999999;
+ foreach ($imagePaths as $breakpoint => $url) {
+ if (is_numeric($breakpoint)) {
+ $minBreakpoint = min($minBreakpoint, $breakpoint);
+ }
+ $sources .= '';
+ }
+ $sources .= '
';
+
+ return '' . $sources . '';
+ } else {
+ return '
';
+ }
+ },
+ $text
+ );
+
+ return ($newText ?? $text);
+ }
+
+}
diff --git a/Engine/Modules/Media/Helper/ImageHelper.php b/Engine/Modules/Media/Helper/ImageHelper.php
new file mode 100644
index 000000000..34f94701e
--- /dev/null
+++ b/Engine/Modules/Media/Helper/ImageHelper.php
@@ -0,0 +1,114 @@
+Services()->get('image.compress');
+ } catch (ServiceNotFoundException $exception) {
+ Oforge()->Logger()->logException($exception);
+
+ return null;
+ }
+ }
+ $result = [];
+ if ($addOriginalImageUrl) {
+ try {
+ $relativePath = self::$imageCompressService->getPath($idOrPath, 0);
+ if ($relativePath !== null) {
+ $result['full'] = self::finaliseUrl($relativePath, 0);
+ }
+ } catch (Exception $exception) {
+ Oforge()->Logger()->logException($exception);
+ }
+ }
+ if (is_array($size)) {
+ foreach ($size as $breakpoint => $maxSize) {
+ try {
+ $relativePath = self::$imageCompressService->getPath($idOrPath, $maxSize);
+ if ($relativePath !== null) {
+ $result[$breakpoint] = self::finaliseUrl($relativePath, $maxSize);
+ }
+ } catch (Exception $exception) {
+ Oforge()->Logger()->logException($exception);
+ }
+ }
+ } else {
+ try {
+ $relativePath = self::$imageCompressService->getPath($idOrPath, $size);
+ if ($relativePath === null) {
+ return null;
+ }
+ $fullUrl = self::finaliseUrl($relativePath, $size);
+ if ($addOriginalImageUrl) {
+ $result[$size] = $fullUrl;
+ } else {
+ return $fullUrl;
+ }
+ } catch (Exception $exception) {
+ Oforge()->Logger()->logException($exception);
+
+ return null;
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * @param string $relativePath
+ * @param int $maxSize
+ *
+ * @return string
+ */
+ private static function finaliseUrl(string $relativePath, int $maxSize) : string
+ {
+ if (self::DEBUG_SIZE) {
+ $relativePath .= (strpos($relativePath, '?') === false ? '?' : '&') . 'size=' . $maxSize;
+ }
+
+ return RouteHelper::getFullUrl(str_replace(' ', '%20', $relativePath));
+ }
+
+}