-
Notifications
You must be signed in to change notification settings - Fork 766
Issue Fix for #2958 Transparent backgrounds on PNG images lost in dynamic media Urls #2978
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 1 commit
362a5a5
701df18
653d1b1
e467bc3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -99,6 +99,10 @@ public class ImageImpl extends com.adobe.cq.wcm.core.components.internal.models. | |
| * The path of the delegated content policy. | ||
| */ | ||
| private static final String CONTENT_POLICY_DELEGATE_PATH = "contentPolicyDelegatePath"; | ||
| /** | ||
| * Auto-preserve PNG transparency configuration property. | ||
| */ | ||
| private static final String PN_DESIGN_AUTO_PRESERVE_PNG_TRANSPARENCY = "autoPreservePngTransparency"; | ||
|
|
||
| /** | ||
| * Server path for dynamic media | ||
|
|
@@ -159,11 +163,12 @@ protected void initModel() { | |
| // if content policy delegate path is provided pass it to the image Uri | ||
| String policyDelegatePath = request.getParameter(CONTENT_POLICY_DELEGATE_PATH); | ||
| String dmImageUrl = null; | ||
| Asset asset = null; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Any reason for moving the variable definition here? Looks like it's not really used outside of the
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @vladbailescu Good catch! The variable scope was actually appropriate in this case since the
The current scope provides the best balance between readability and proper variable lifecycle management. |
||
| if (StringUtils.isNotEmpty(fileReference)) { | ||
| // the image is coming from DAM | ||
| final Resource assetResource = request.getResourceResolver().getResource(fileReference); | ||
| if (assetResource != null) { | ||
| Asset asset = assetResource.adaptTo(Asset.class); | ||
| asset = assetResource.adaptTo(Asset.class); | ||
| if (asset != null) { | ||
| if (!uuidDisabled) { | ||
| uuid = asset.getID(); | ||
|
|
@@ -289,6 +294,20 @@ protected void initModel() { | |
| srcUriTemplate += imagePresetCommand; | ||
| src += imagePresetCommand; | ||
| } | ||
|
|
||
| // Auto-preserve PNG transparency if enabled and asset has transparency | ||
| boolean autoPreservePngTransparency = currentStyle.get(PN_DESIGN_AUTO_PRESERVE_PNG_TRANSPARENCY, false); | ||
|
||
| if (autoPreservePngTransparency && hasPngTransparency(asset)) { | ||
| String pngAlphaModifier = "fmt=png-alpha"; | ||
|
||
| if (StringUtils.isNotBlank(imageModifiers)) { | ||
| // Append to existing modifiers | ||
| imageModifiers += "&" + pngAlphaModifier; | ||
| } else { | ||
| // Set as the only modifier | ||
| imageModifiers = pngAlphaModifier; | ||
| } | ||
| } | ||
|
|
||
| if (StringUtils.isNotBlank(imageModifiers)){ | ||
| String imageModifiersCommand = (srcUriTemplate.contains("?") ? '&':'?') + imageModifiers; | ||
| srcUriTemplate += imageModifiersCommand; | ||
|
|
@@ -402,5 +421,33 @@ protected ImageArea newImageArea(String shape, String coordinates, String relati | |
| return new ImageAreaImpl(shape, coordinates, relativeCoordinates, link, alt); | ||
| } | ||
|
|
||
| /** | ||
| * Checks if a PNG asset has transparency based on its bits per pixel metadata. | ||
| * @param asset The DAM asset to check | ||
| * @return true if the PNG has transparency (32 bits), false otherwise | ||
| */ | ||
| private boolean hasPngTransparency(Asset asset) { | ||
| if (asset == null) { | ||
| return false; | ||
| } | ||
|
|
||
| String mimeType = asset.getMimeType(); | ||
| if (!"image/png".equals(mimeType)) { | ||
|
||
| return false; | ||
| } | ||
|
|
||
| String bitsPerPixel = asset.getMetadataValue("dam:Bitsperpixel"); | ||
|
||
| if (StringUtils.isEmpty(bitsPerPixel)) { | ||
| return false; | ||
| } | ||
|
|
||
| try { | ||
| int bits = Integer.parseInt(bitsPerPixel); | ||
| return bits == 32; // 32 bits indicates PNG with alpha channel | ||
| } catch (NumberFormatException e) { | ||
| LOGGER.debug("Could not parse bits per pixel value: {}", bitsPerPixel); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Any reason for not logging this as an error?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @vladbailescu Improved! I've enhanced the logging approach:
This provides visibility for troubleshooting while avoiding unnecessary error-level entries for expected conditions. |
||
| return false; | ||
| } | ||
| } | ||
|
|
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -77,6 +77,11 @@ public class ImageImpl extends com.adobe.cq.wcm.core.components.internal.models. | |
| private static final String EMPTY_PIXEL = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="; | ||
| static final int DEFAULT_NGDM_ASSET_WIDTH = 640; | ||
|
|
||
| /** | ||
| * Auto-preserve PNG transparency configuration property. | ||
| */ | ||
| private static final String PN_DESIGN_AUTO_PRESERVE_PNG_TRANSPARENCY = "autoPreservePngTransparency"; | ||
|
|
||
| @OSGiService | ||
| @Optional | ||
| private NextGenDynamicMediaConfig nextGenDynamicMediaConfig; | ||
|
|
@@ -344,6 +349,25 @@ private void initNextGenerationDynamicMedia() { | |
| if(StringUtils.isNotEmpty(smartCrop) && !StringUtils.equals(smartCrop, SMART_CROP_AUTO)) { | ||
| builder.withSmartCrop(smartCrop); | ||
| } | ||
|
|
||
| // Auto-preserve PNG transparency if enabled | ||
| boolean autoPreservePngTransparency = currentStyle.get(PN_DESIGN_AUTO_PRESERVE_PNG_TRANSPARENCY, false); | ||
| if (autoPreservePngTransparency) { | ||
| // Get the asset to check for PNG transparency | ||
| Resource assetResource = resource.getResourceResolver().getResource(fileReference); | ||
| if (assetResource != null) { | ||
| Asset asset = assetResource.adaptTo(Asset.class); | ||
| if (hasPngTransparency(asset)) { | ||
| String pngAlphaModifier = "fmt=png-alpha"; | ||
| if (StringUtils.isNotEmpty(modifiers)) { | ||
| modifiers += "&" + pngAlphaModifier; | ||
| } else { | ||
| modifiers = pngAlphaModifier; | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| if (StringUtils.isNotEmpty(modifiers)) { | ||
| builder.withImageModifiers(modifiers); | ||
| } | ||
|
|
@@ -377,4 +401,33 @@ private String prepareNgdmSrcUriTemplate() { | |
| public static boolean isNgdmImageReference(String fileReference) { | ||
| return StringUtils.isNotBlank(fileReference) && fileReference.startsWith("/urn:"); | ||
| } | ||
|
|
||
| /** | ||
| * Checks if a PNG asset has transparency based on its bits per pixel metadata. | ||
| * @param asset The DAM asset to check | ||
| * @return true if the PNG has transparency (32 bits), false otherwise | ||
| */ | ||
| private boolean hasPngTransparency(Asset asset) { | ||
|
||
| if (asset == null) { | ||
| return false; | ||
| } | ||
|
|
||
| String mimeType = asset.getMimeType(); | ||
| if (!"image/png".equals(mimeType)) { | ||
| return false; | ||
| } | ||
|
|
||
| String bitsPerPixel = asset.getMetadataValue("dam:Bitsperpixel"); | ||
| if (StringUtils.isEmpty(bitsPerPixel)) { | ||
| return false; | ||
| } | ||
|
|
||
| try { | ||
| int bits = Integer.parseInt(bitsPerPixel); | ||
| return bits == 32; // 32 bits indicates PNG with alpha channel | ||
| } catch (NumberFormatException e) { | ||
| LOGGER.debug("Could not parse bits per pixel value: {}", bitsPerPixel); | ||
| return false; | ||
| } | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would move this constant to the interface, along with the rest of the properties, since they become part of the API.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@vladbailescu Moved! I've relocated all PNG transparency-related constants to the
Imageinterface:PN_DESIGN_AUTO_PRESERVE_PNG_TRANSPARENCY = "autoPreservePngTransparency"PNG_ALPHA_FORMAT_MODIFIER = "fmt=png-alpha"DAM_BITS_PER_PIXEL = "dam:Bitsperpixel"This makes them part of the public API that other implementations can rely on and follows the existing pattern of other image-related constants in the interface.