{"id":73909,"date":"2025-08-12T15:16:54","date_gmt":"2025-08-12T09:46:54","guid":{"rendered":"https:\/\/www.tothenew.com\/blog\/?p=73909"},"modified":"2025-08-27T13:41:03","modified_gmt":"2025-08-27T08:11:03","slug":"webp-support-for-aem-on-prem","status":"publish","type":"post","link":"https:\/\/www.tothenew.com\/blog\/webp-support-for-aem-on-prem\/","title":{"rendered":"WebP Support for AEM On Prem"},"content":{"rendered":"<p style=\"text-align: left;\">Working with enterprise CMS like AEM often means balancing between business requirements and platform limitations. Recently , I encountered a scenario where the client needed support for WebP images in the authoring interface.<\/p>\n<p style=\"text-align: left;\">WebP is a modern image format developed by Google that provides superior lossy and lossless compression for images on the web. The goal of WebP is to create smaller, faster-loading images without sacrificing quality, making it ideal for websites and mobile applications.<\/p>\n<p style=\"text-align: left;\">When you drag and drop a DAM asset (like a JPEG or PNG) into the OOTB Image component:<\/p>\n<ul style=\"text-align: left;\">\n<li>AEM references the original DAM asset, and does not create a new image.<\/li>\n<li>Renditions (e.g., cq5dam.thumbnail.319.319.png) are automatically created if they don\u2019t already exist.<\/li>\n<\/ul>\n<p style=\"text-align: left;\">When you drag and drop a WebP image into the OOTB Image component in AEM, the behavior differs because AEM 6.5 (and earlier) does not support WebP out of the box.<\/p>\n<ul style=\"text-align: left;\">\n<li>No renditions are generated automatically for Webp.<\/li>\n<li>Thumbnail \/review in DAM UI is not shown.<\/li>\n<\/ul>\n<p style=\"text-align: left;\">However ,To bridge this gap, I developed a custom utility that enables WebP image handling within AEM 6.5, making it seamless for content authors to work with this format.To refer the list of supported images format , please refer the link below<\/p>\n<p style=\"text-align: left;\">https:\/\/experienceleague.adobe.com\/en\/docs\/experience-manager-65\/content\/assets\/administer\/assets-formats.<\/p>\n<p style=\"text-align: left;\">Lets dive into the steps involved to write this custom utility .<\/p>\n<p style=\"text-align: left;\">Steps &#8211;<\/p>\n<ol>\n<li style=\"text-align: left;\"><strong>Adding 3rd Party Plugins<\/strong><br \/>\nThere are many third party plugins available which can be added to support webp images , we will be using Twelve Monkeys ImageIO plugin in the implementation , which provides extended support for image file formats in Java Platform.<\/li>\n<\/ol>\n<p style=\"text-align: left;\">\u00a0 \u00a0 \u00a0 \u00a0..\/pom.xml<\/p>\n<pre style=\"text-align: left;\">&lt;dependency&gt;\r\n&lt;groupId&gt;com.twelvemonkeys.imageio&lt;\/groupId&gt;\r\n \u00a0\u00a0&lt;artifactId&gt;imageio-core&lt;\/artifactId&gt;\r\n \u00a0\u00a0&lt;version&gt;3.10.0&lt;\/version&gt;\r\n \u00a0\u00a0&lt;scope&gt;provided&lt;\/scope&gt;\r\n&lt;\/dependency&gt;\r\n&lt;dependency&gt;\r\n \u00a0\u00a0&lt;groupId&gt;com.twelvemonkeys.imageio&lt;\/groupId&gt;\r\n \u00a0\u00a0&lt;artifactId&gt;imageio-webp&lt;\/artifactId&gt;\r\n \u00a0\u00a0&lt;version&gt;3.10.0&lt;\/version&gt;\r\n \u00a0\u00a0&lt;scope&gt;provided&lt;\/scope&gt;\r\n&lt;\/dependency&gt;\r\n&lt;dependency&gt;\r\n \u00a0\u00a0&lt;groupId&gt;com.twelvemonkeys.imageio&lt;\/groupId&gt;\r\n \u00a0\u00a0&lt;artifactId&gt;imageio-psd&lt;\/artifactId&gt;\r\n \u00a0\u00a0&lt;version&gt;3.10.0&lt;\/version&gt;\r\n \u00a0\u00a0&lt;scope&gt;provided&lt;\/scope&gt;\r\n&lt;\/dependency&gt;\r\n&lt;dependency&gt;\r\n \u00a0\u00a0&lt;groupId&gt;jmagick&lt;\/groupId&gt;\r\n \u00a0\u00a0&lt;artifactId&gt;jmagick&lt;\/artifactId&gt;\r\n \u00a0\u00a0&lt;version&gt;6.6.9&lt;\/version&gt;\r\n \u00a0\u00a0&lt;scope&gt;provided&lt;\/scope&gt;\r\n&lt;\/dependency&gt;<\/pre>\n<div id=\"attachment_73914\" style=\"width: 635px\" class=\"wp-caption alignnone\"><img aria-describedby=\"caption-attachment-73914\" decoding=\"async\" loading=\"lazy\" class=\"wp-image-73914 size-large\" src=\"https:\/\/www.tothenew.com\/blog\/wp-ttn-blog\/uploads\/2025\/08\/bundle-ss-1-1-1024x631.png\" alt=\"bundle\" width=\"625\" height=\"385\" srcset=\"\/blog\/wp-ttn-blog\/uploads\/2025\/08\/bundle-ss-1-1-1024x631.png 1024w, \/blog\/wp-ttn-blog\/uploads\/2025\/08\/bundle-ss-1-1-300x185.png 300w, \/blog\/wp-ttn-blog\/uploads\/2025\/08\/bundle-ss-1-1-768x474.png 768w, \/blog\/wp-ttn-blog\/uploads\/2025\/08\/bundle-ss-1-1-624x385.png 624w, \/blog\/wp-ttn-blog\/uploads\/2025\/08\/bundle-ss-1-1.png 1038w\" sizes=\"(max-width: 625px) 100vw, 625px\" \/><p id=\"caption-attachment-73914\" class=\"wp-caption-text\">bundle with listed dependencies<\/p><\/div>\n<p><strong>2.\u00a0 Adding the logic to read webp images and generate renditions of different sizes.<\/strong><br \/>\nThe program can be created in form of :<\/p>\n<p>1. Custom Process Step &#8211; Workflow<br \/>\n2. OSGI Service\/Servlet<br \/>\n3. or, incorporated with Event Listener<\/p>\n<p>In this blog ,AEM listeners listens\/works for any dam asset upload of webp format image and generates the renditions of sizes as per requirement. Content Fragment is used to take inputs for rendition sizes.<\/p>\n<p>Created below is the Event Listener to listen to the event of assets uploaded in the dam.<\/p>\n<pre> @Activate\r\n @Override\r\n \u00a0\u00a0public void onEvent(EventIterator events) {\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0try {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0Map&lt;String, Object&gt; authInfo = Collections.singletonMap(ResourceResolverFactory.SUBSERVICE, \"getResourceResolver\");<\/pre>\n<pre>try (ResourceResolver resourceResolver = resolverFactory.getServiceResourceResolver(authInfo)) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0if (!isListenerEnabled(resourceResolver)) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0log.info(\"WebPAssetListener is disabled via configuration.\");\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0return;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0}\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0List&lt;String&gt; renditionSizes = getRenditionSizes(resourceResolver);\u00a0\r\n\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0while (events.hasNext()) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0Event event = events.nextEvent();\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0String path = event.getPath();\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0try {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0webPAssetsRenditionService.generateRenditions(asset, renditionSizes,resourceResolver);\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0log.info(\"Successfully generated renditions for WebP asset: {}\", path);\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0} catch (Exception e) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0log.error(\"Error generating renditions for WebP asset: {}\", path, e);\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0}\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0}\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0}\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0} catch (Exception e) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0log.error(\"Error in WebPAssetListener\", e);\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0}\r\n\u00a0\u00a0\u00a0}\r\n\r\n\r\n<\/pre>\n<p>You can generate assets with WebP Image format of different sizes which can be added from any configurable space like a content fragment .<\/p>\n<pre>private List&lt;String&gt; getRenditionSizes(ResourceResolver resourceResolver) {\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0Resource configResource = resourceResolver.getResource(ASSET_LISTENER_CONFIGURATION);\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0if (configResource != null) {\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0String[] sizes = configResource.getValueMap().get(RENDITION_SIZES, String[].class);\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0return sizes != null ? Arrays.asList(sizes) : Collections.emptyList();\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0}\r\nreturn Collections.emptyList();\r\n  }\r\n}\r\n\r\n\r\n\r\n<\/pre>\n<div id=\"attachment_73913\" style=\"width: 967px\" class=\"wp-caption alignnone\"><img aria-describedby=\"caption-attachment-73913\" decoding=\"async\" loading=\"lazy\" class=\"wp-image-73913 \" src=\"https:\/\/www.tothenew.com\/blog\/wp-ttn-blog\/uploads\/2025\/08\/cf-rend-2-1.png\" alt=\"content fragment\" width=\"957\" height=\"587\" srcset=\"\/blog\/wp-ttn-blog\/uploads\/2025\/08\/cf-rend-2-1.png 1003w, \/blog\/wp-ttn-blog\/uploads\/2025\/08\/cf-rend-2-1-300x184.png 300w, \/blog\/wp-ttn-blog\/uploads\/2025\/08\/cf-rend-2-1-768x471.png 768w, \/blog\/wp-ttn-blog\/uploads\/2025\/08\/cf-rend-2-1-624x383.png 624w\" sizes=\"(max-width: 957px) 100vw, 957px\" \/><p id=\"caption-attachment-73913\" class=\"wp-caption-text\">content fragment to input rendition values<\/p><\/div>\n<p>Below is the WebPAssetsRenditionService Implementation where you can add below functionality to generate WebP format Images of the asset uploaded in DAM.<\/p>\n<pre> @Activate\r\n \u00a0\u00a0protected void activate() {\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0IIORegistry registry = IIORegistry.getDefaultInstance();\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0registry.registerServiceProvider(new WebPImageReaderSpi());\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0LOGGER.info(\"WebP Image Reader registered successfully\");\r\n \u00a0\u00a0}\r\n\r\n<\/pre>\n<ol>\n<li><strong>IIORegistry registry = IIORegistry.getDefaultInstance();<\/strong><br \/>\nThis gets the default instance of the IIORegistry, which is essentially a registry\/database of available image readers, writers, and other services in Java&#8217;s Image I\/O system.<\/li>\n<li><strong>registry.registerServiceProvider(new WebPImageReaderSpi());<\/strong><br \/>\nThis gets the default instance of the IIORegistry, which is essentially a registry\/database of available image readers, writers, and other services in Java&#8217;s Image I\/O system.<\/li>\n<\/ol>\n<pre>@Override\r\n \u00a0\u00a0public void generateRenditions(Asset asset, List&lt;String&gt; renditionSizes, ResourceResolver resourceResolver) throws Exception {\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0BufferedImage originalImage;\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0try (InputStream is = asset.getOriginal().getStream()) {\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0originalImage = ImageIO.read(is);\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0}\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0if (originalImage == null) {\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0throw new IOException(\"Failed to read WebP image\");\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0}\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0for (String size : renditionSizes) {\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0String[] dimensions = size.split(\":\");\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0if (dimensions.length != 2) {\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0LOGGER.warn(\"Invalid size format: {}. Skipping this rendition.\", size);\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0continue;\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0}\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0int width = Integer.parseInt(dimensions[0]);\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0int height = Integer.parseInt(dimensions[1]);\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0generateRendition(asset, originalImage, width, height);\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0resourceResolver.commit();\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0}<\/pre>\n<p>Add rendition to the asset by following Logic-<\/p>\n<pre> private void generateRendition(Asset asset, BufferedImage originalImage, int width, int height) throws Exception {\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0String renditionName = String.format(\"cq5dam.thumbnail.%d.%d.png\", width, height);\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0BufferedImage resizedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0resizedImage.createGraphics().drawImage(originalImage.getScaledInstance(width, height, java.awt.Image.SCALE_SMOOTH), 0, 0, null);\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0ByteArrayOutputStream baos = new ByteArrayOutputStream();\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0ImageIO.write(resizedImage, \"PNG\", baos);\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0try (InputStream pngInputStream = new ByteArrayInputStream(baos.toByteArray())) {\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0asset.addRendition(renditionName, pngInputStream, PNG_MIME_TYPE);\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0}\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0LOGGER.info(\"Generated rendition: {} for asset: {}\", renditionName, asset.getPath());\r\n \u00a0\u00a0}\r\n}\r\n\r\n\r\n<\/pre>\n<p>Conclusion-<\/p>\n<p>Basis the above implementation, the renditions are available for webp images,\u00a0 Please find below the screenshots &#8211;<\/p>\n<div id=\"attachment_73912\" style=\"width: 793px\" class=\"wp-caption aligncenter\"><img aria-describedby=\"caption-attachment-73912\" decoding=\"async\" loading=\"lazy\" class=\"wp-image-73912 size-full\" src=\"https:\/\/www.tothenew.com\/blog\/wp-ttn-blog\/uploads\/2025\/08\/3-1.png\" alt=\"image ootb\" width=\"783\" height=\"528\" srcset=\"\/blog\/wp-ttn-blog\/uploads\/2025\/08\/3-1.png 783w, \/blog\/wp-ttn-blog\/uploads\/2025\/08\/3-1-300x202.png 300w, \/blog\/wp-ttn-blog\/uploads\/2025\/08\/3-1-768x518.png 768w, \/blog\/wp-ttn-blog\/uploads\/2025\/08\/3-1-624x421.png 624w\" sizes=\"(max-width: 783px) 100vw, 783px\" \/><p id=\"caption-attachment-73912\" class=\"wp-caption-text\">result<\/p><\/div>\n<div id=\"attachment_73911\" style=\"width: 635px\" class=\"wp-caption aligncenter\"><img aria-describedby=\"caption-attachment-73911\" decoding=\"async\" loading=\"lazy\" class=\"wp-image-73911 size-large\" src=\"https:\/\/www.tothenew.com\/blog\/wp-ttn-blog\/uploads\/2025\/08\/4-1-1024x715.png\" alt=\"ss\" width=\"625\" height=\"436\" srcset=\"\/blog\/wp-ttn-blog\/uploads\/2025\/08\/4-1-1024x715.png 1024w, \/blog\/wp-ttn-blog\/uploads\/2025\/08\/4-1-300x209.png 300w, \/blog\/wp-ttn-blog\/uploads\/2025\/08\/4-1-768x536.png 768w, \/blog\/wp-ttn-blog\/uploads\/2025\/08\/4-1-624x436.png 624w, \/blog\/wp-ttn-blog\/uploads\/2025\/08\/4-1.png 1123w\" sizes=\"(max-width: 625px) 100vw, 625px\" \/><p id=\"caption-attachment-73911\" class=\"wp-caption-text\">result<\/p><\/div>\n","protected":false},"excerpt":{"rendered":"<p>Working with enterprise CMS like AEM often means balancing between business requirements and platform limitations. Recently , I encountered a scenario where the client needed support for WebP images in the authoring interface. WebP is a modern image format developed by Google that provides superior lossy and lossless compression for images on the web. The [&hellip;]<\/p>\n","protected":false},"author":2122,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"iawp_total_views":223},"categories":[5868],"tags":[4847,7748,7749,7747],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/posts\/73909"}],"collection":[{"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/users\/2122"}],"replies":[{"embeddable":true,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/comments?post=73909"}],"version-history":[{"count":11,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/posts\/73909\/revisions"}],"predecessor-version":[{"id":74325,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/posts\/73909\/revisions\/74325"}],"wp:attachment":[{"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/media?parent=73909"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/categories?post=73909"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/tags?post=73909"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}