Skip to content

Commit

Permalink
Dynamic Media Support: Support for PNG with alpha channel (#51)
Browse files Browse the repository at this point in the history
Append &fmt=png-alpha for PNG assets to ensure that the alpha channel is preserved in the Dynamic Media rendition
  • Loading branch information
stefanseifert authored May 22, 2024
1 parent 535c9f9 commit dfd6b5b
Show file tree
Hide file tree
Showing 5 changed files with 50 additions and 4 deletions.
6 changes: 6 additions & 0 deletions changes.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@
xsi:schemaLocation="http://maven.apache.org/changes/1.0.0 http://maven.apache.org/plugins/maven-changes-plugin/xsd/changes-1.0.0.xsd">
<body>

<release version="2.0.8" date="not released">
<action type="update" dev="sseifert" issue="51">
Dynamic Media Support: Append fmt=png-alpha for PNG assets to ensure that the alpha channel is preserved in the Dynamic Media rendition.
</action>
</release>

<release version="2.0.6" date="2024-04-25">
<action type="fix" dev="sseifert" issue="50">
MediaFileServlet: Use Content-Security-Policy instead of Content-Disposition header to prevent XSS attacks in SVG files.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import io.wcm.handler.media.Dimension;
import io.wcm.handler.media.format.Ratio;
import io.wcm.handler.mediasource.dam.impl.DamContext;
import io.wcm.wcm.commons.contenttype.ContentType;

/**
* Build part of dynamic media/scene7 URL to render renditions.
Expand Down Expand Up @@ -130,6 +131,10 @@ private DynamicMediaPath() {
.append("hei=").append(dimension.getHeight()).append("&")
// cropping/width/height is pre-calculated to fit with original ratio, make sure there are no 1px background lines visible
.append("fit=stretch");
if (isPNG(damContext)) {
// if original image is PNG image, make sure alpha channel is preserved
result.append("&fmt=png-alpha");
}
logResult(damContext, result);
return result.toString();
}
Expand All @@ -146,6 +151,10 @@ private DynamicMediaPath() {
.append("hei=").append(dimension.getHeight()).append("&")
// cropping/width/height is pre-calculated to fit with original ratio, make sure there are no 1px background lines visible
.append("fit=stretch");
if (isPNG(damContext)) {
// if original image is PNG image, make sure alpha channel is preserved
result.append("&fmt=png-alpha");
}
logResult(damContext, result);
return result.toString();
}
Expand Down Expand Up @@ -195,4 +204,8 @@ private static String encodeDynamicMediaObject(@NotNull DamContext damContext) {
return StringUtils.join(pathParts, "/");
}

private static boolean isPNG(@NotNull DamContext damContext) {
return StringUtils.equals(damContext.getAsset().getMimeType(), ContentType.PNG);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ class MediaHandlerImplEnd2EndDynamicMediaSmartCropTest {

private final AemContext context = AppAemContext.newAemContext();

private Resource assetFolder;
private Asset asset;
private MediaHandler mediaHandler;

Expand All @@ -74,7 +75,7 @@ void setUp() {
PN_CROP_TYPE, CROP_TYPE_SMART,
PN_BANNER, "16-10,16,10|4-3,40,30");

Resource assetFolder = context.create().resource("/content/dam/folder1");
assetFolder = context.create().resource("/content/dam/folder1");
context.create().resource(assetFolder, JCR_CONTENT, DamConstants.IMAGE_PROFILE, profile1.getPath());

asset = context.create().asset(assetFolder.getPath() + "/test.jpg", 160, 100, ContentType.JPEG,
Expand Down Expand Up @@ -156,6 +157,21 @@ void testValidSmartCroppedRenditionOnlyRatio_MatchingOriginalRatio() {
assertEquals("https://dummy.scene7.com/is/image/DummyFolder/test%3A16-10?wid=120&hei=75&fit=stretch", renditions.get(0).getUrl());
}

@Test
void testValidSmartCroppedRendition_OnlyRatio_PNG() {
// create PNG asset instead of JPEG, skip the extra renditions
asset = context.create().asset(assetFolder.getPath() + "/test.png", 160, 100, ContentType.PNG,
Scene7Constants.PN_S7_FILE, "DummyFolder/test");

Media media = getMediaWithRatio(RATIO_4_3);
assertTrue(media.isValid());

List<Rendition> renditions = List.copyOf(media.getRenditions());
assertEquals(1, renditions.size());
assertEquals("https://dummy.scene7.com/is/image/DummyFolder/test%3A4-3?wid=133&hei=100&fit=stretch&fmt=png-alpha", renditions.get(0).getUrl());
}


private Media getMediaWithWidths(MediaFormat mediaFormat, long... widths) {
return mediaHandler.get(asset.getPath())
.pictureSource(new PictureSource(mediaFormat).widths(widths))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ void testFileUpload_GIF_AutoCrop() {
void testAsset_PNG_Original() {
Asset asset = createSampleAsset("/filetype/sample.png", ContentType.PNG);
buildAssertMedia(asset, 100, 50,
"https://dummy.scene7.com/is/image/DummyFolder/sample.png?wid=100&hei=50&fit=stretch",
"https://dummy.scene7.com/is/image/DummyFolder/sample.png?wid=100&hei=50&fit=stretch&fmt=png-alpha",
ContentType.PNG);
}

Expand All @@ -196,7 +196,7 @@ void testAsset_PNG_Original() {
void testAsset_PNG_Rescale() {
Asset asset = createSampleAsset("/filetype/sample.png", ContentType.PNG);
buildAssertMedia_Rescale(asset, 80, 40,
"https://dummy.scene7.com/is/image/DummyFolder/sample.png?wid=80&hei=40&fit=stretch",
"https://dummy.scene7.com/is/image/DummyFolder/sample.png?wid=80&hei=40&fit=stretch&fmt=png-alpha",
ContentType.PNG);
}

Expand All @@ -205,7 +205,7 @@ void testAsset_PNG_Rescale() {
void testAsset_PNG_AutoCrop() {
Asset asset = createSampleAsset("/filetype/sample.png", ContentType.PNG);
buildAssertMedia_AutoCrop(asset, 50, 50,
"https://dummy.scene7.com/is/image/DummyFolder/sample.png?crop=25,0,50,50&wid=50&hei=50&fit=stretch",
"https://dummy.scene7.com/is/image/DummyFolder/sample.png?crop=25,0,50,50&wid=50&hei=50&fit=stretch&fmt=png-alpha",
ContentType.PNG);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,4 +174,15 @@ void testBuildImage_SpecialChars() {
assertEquals("/is/content/DummyFolder/test%20with%20spaces%20%C3%A4%C3%B6%C3%BC%C3%9F%E2%82%AC", result);
}

@Test
void testCrop_PNG() {
asset = context.create().asset(assetFolder.getPath() + "/test.png", 50, 30, ContentType.PNG,
Scene7Constants.PN_S7_FILE, "DummyFolder/test");
damContext = new DamContext(asset, new MediaArgs(), mediaHandlerConfig,
dynamicMediaSupportService, webOptimizedImageDeliveryService, context.request());

String result = DynamicMediaPath.buildImage(damContext, 30, 20, new CropDimension(5, 2, 10, 8), null);
assertEquals("/is/image/DummyFolder/test?crop=5,2,10,8&wid=30&hei=20&fit=stretch&fmt=png-alpha", result);
}

}

0 comments on commit dfd6b5b

Please sign in to comment.