Repository: wicket Updated Branches: refs/heads/master e5f6ea091 -> aa859dee6
WICKET-5827 - CssUrlReplacer supports base64 encoded images Project: http://git-wip-us.apache.org/repos/asf/wicket/repo Commit: http://git-wip-us.apache.org/repos/asf/wicket/commit/aa859dee Tree: http://git-wip-us.apache.org/repos/asf/wicket/tree/aa859dee Diff: http://git-wip-us.apache.org/repos/asf/wicket/diff/aa859dee Branch: refs/heads/master Commit: aa859dee621959de98e39ecafda28d9ab7ccc7fa Parents: e5f6ea0 Author: klopfdreh <[email protected]> Authored: Wed Feb 18 16:25:56 2015 +0100 Committer: klopfdreh <[email protected]> Committed: Thu Mar 12 11:35:50 2015 +0100 ---------------------------------------------------------------------- .../apache/wicket/resource/CssUrlReplacer.java | 80 ++++++++++++++++++-- .../wicket/resource/CssUrlReplacerTest.java | 65 +++++++++++----- 2 files changed, 120 insertions(+), 25 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/wicket/blob/aa859dee/wicket-core/src/main/java/org/apache/wicket/resource/CssUrlReplacer.java ---------------------------------------------------------------------- diff --git a/wicket-core/src/main/java/org/apache/wicket/resource/CssUrlReplacer.java b/wicket-core/src/main/java/org/apache/wicket/resource/CssUrlReplacer.java index b7e55f0..8071d9a 100644 --- a/wicket-core/src/main/java/org/apache/wicket/resource/CssUrlReplacer.java +++ b/wicket-core/src/main/java/org/apache/wicket/resource/CssUrlReplacer.java @@ -16,18 +16,25 @@ */ package org.apache.wicket.resource; +import java.io.IOException; +import java.io.InputStream; import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.apache.wicket.WicketRuntimeException; import org.apache.wicket.css.ICssCompressor; import org.apache.wicket.request.Url; import org.apache.wicket.request.cycle.RequestCycle; import org.apache.wicket.request.resource.PackageResourceReference; +import org.apache.wicket.util.crypt.Base64; +import org.apache.wicket.util.io.IOUtils; +import org.apache.wicket.util.resource.IResourceStream; +import org.apache.wicket.util.resource.ResourceStreamNotFoundException; /** * This compressor is used to replace url within css files with resources that belongs to their - * corresponding component classes. The compress method is not compressing any content, but replacing the - * URLs with Wicket representatives.<br> + * corresponding component classes. The compress method is not compressing any content, but + * replacing the URLs with Wicket representatives.<br> * <br> * Usage: * @@ -44,6 +51,12 @@ public class CssUrlReplacer implements IScopeAwareTextResourceProcessor, ICssCom private static final Pattern URL_PATTERN = Pattern.compile("url\\(['|\"]*(.*?)['|\"]*\\)"); /** + * Used to be append to CSS URLs (background-image: url('Beer.gif?embedBase64');). The + * CssUrlReplacer embeds the base64 content instead of using an URL. + */ + public static final String EMBED_BASE64 = "embedBase64"; + + /** * Replaces the URLs of CSS resources with Wicket representatives. */ @Override @@ -58,6 +71,8 @@ public class CssUrlReplacer implements IScopeAwareTextResourceProcessor, ICssCom { Url imageCandidateUrl = Url.parse(matcher.group(1)); CharSequence processedUrl; + boolean embedded = false; + if (imageCandidateUrl.isFull()) { processedUrl = imageCandidateUrl.toString(Url.StringMode.FULL); @@ -71,19 +86,72 @@ public class CssUrlReplacer implements IScopeAwareTextResourceProcessor, ICssCom // relativize against the url for the containing CSS file Url cssUrlCopy = new Url(cssUrl); cssUrlCopy.resolveRelative(imageCandidateUrl); - PackageResourceReference imageReference = new PackageResourceReference(scope, cssUrlCopy.toString()); - processedUrl = cycle.urlFor(imageReference, null); + + // if the image should be processed as URL or base64 embedded + if (cssUrlCopy.getQueryString() != null && + cssUrlCopy.getQueryString().contains(EMBED_BASE64)) + { + embedded = true; + PackageResourceReference imageReference = new PackageResourceReference(scope, + cssUrlCopy.toString().replace("?" + EMBED_BASE64, "")); + try + { + processedUrl = createBase64EncodedImage(imageReference); + } + catch (Exception e) + { + throw new WicketRuntimeException( + "Error while embedding an image into the css: " + imageReference, e); + } + } + else + { + PackageResourceReference imageReference = new PackageResourceReference(scope, + cssUrlCopy.toString()); + processedUrl = cycle.urlFor(imageReference, null); + } } - matcher.appendReplacement(output, "url('" + processedUrl + "')"); + matcher.appendReplacement(output, embedded ? "url(" + processedUrl + ")" : "url('" + + processedUrl + "')"); } matcher.appendTail(output); return output.toString(); } + /** + * Creates a base64 encoded image string based on the given image reference + * + * @param imageReference + * the image reference to create the base64 encoded image string of + * @return the base64 encoded image string + * @throws ResourceStreamNotFoundException + * if the resource couldn't be found + * @throws IOException + * if the stream couldn't be read + */ + private CharSequence createBase64EncodedImage(PackageResourceReference imageReference) + throws ResourceStreamNotFoundException, IOException + { + IResourceStream resourceStream = imageReference.getResource().getResourceStream(); + InputStream inputStream = resourceStream.getInputStream(); + try + { + byte[] bytes = IOUtils.toByteArray(inputStream); + String base64EncodedImage = Base64.encodeBase64String(bytes); + return "data:" + resourceStream.getContentType() + ";base64," + + base64EncodedImage.replaceAll("\\s", ""); + } + finally + { + IOUtils.closeQuietly(inputStream); + } + } + @Override public String compress(String original) { - throw new UnsupportedOperationException(CssUrlReplacer.class.getSimpleName() + ".process() should be used instead!"); + throw new UnsupportedOperationException(CssUrlReplacer.class.getSimpleName() + + ".process() should be used instead!"); } } http://git-wip-us.apache.org/repos/asf/wicket/blob/aa859dee/wicket-core/src/test/java/org/apache/wicket/resource/CssUrlReplacerTest.java ---------------------------------------------------------------------- diff --git a/wicket-core/src/test/java/org/apache/wicket/resource/CssUrlReplacerTest.java b/wicket-core/src/test/java/org/apache/wicket/resource/CssUrlReplacerTest.java index df25e63..741b839 100644 --- a/wicket-core/src/test/java/org/apache/wicket/resource/CssUrlReplacerTest.java +++ b/wicket-core/src/test/java/org/apache/wicket/resource/CssUrlReplacerTest.java @@ -20,6 +20,7 @@ import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.is; import org.apache.wicket.WicketTestCase; +import org.apache.wicket.markup.html.image.ImageTest; import org.apache.wicket.mock.MockApplication; import org.apache.wicket.protocol.http.WebApplication; import org.apache.wicket.request.resource.caching.FilenameWithVersionResourceCachingStrategy; @@ -36,20 +37,23 @@ public class CssUrlReplacerTest extends WicketTestCase @Override protected WebApplication newApplication() { - return new MockApplication() { + return new MockApplication() + { @Override protected void init() { super.init(); - getResourceSettings().setCachingStrategy(new FilenameWithVersionResourceCachingStrategy("=VER=", new MessageDigestResourceVersion()) - { - @Override - public void decorateUrl(ResourceUrl url, IStaticCacheableResource resource) + getResourceSettings().setCachingStrategy( + new FilenameWithVersionResourceCachingStrategy("=VER=", + new MessageDigestResourceVersion()) { - url.setFileName(url.getFileName() + DECORATION_SUFFIX); - } - }); + @Override + public void decorateUrl(ResourceUrl url, IStaticCacheableResource resource) + { + url.setFileName(url.getFileName() + DECORATION_SUFFIX); + } + }); } }; } @@ -87,7 +91,10 @@ public class CssUrlReplacerTest extends WicketTestCase CssUrlReplacer replacer = new CssUrlReplacer(); String processed = replacer.process(input, scope, cssRelativePath); - assertThat(processed, is(".class {background-image: url('./wicket/resource/org.apache.wicket.resource.CssUrlReplacerTest/res/css/some.img"+DECORATION_SUFFIX+"');}")); + assertThat( + processed, + is(".class {background-image: url('./wicket/resource/org.apache.wicket.resource.CssUrlReplacerTest/res/css/some.img" + + DECORATION_SUFFIX + "');}")); } @Test @@ -99,7 +106,10 @@ public class CssUrlReplacerTest extends WicketTestCase CssUrlReplacer replacer = new CssUrlReplacer(); String processed = replacer.process(input, scope, cssRelativePath); - assertThat(processed, is(".class {background-image: url('./wicket/resource/org.apache.wicket.resource.CssUrlReplacerTest/res/css/some.img"+DECORATION_SUFFIX+"');}")); + assertThat( + processed, + is(".class {background-image: url('./wicket/resource/org.apache.wicket.resource.CssUrlReplacerTest/res/css/some.img" + + DECORATION_SUFFIX + "');}")); } @Test @@ -111,7 +121,10 @@ public class CssUrlReplacerTest extends WicketTestCase CssUrlReplacer replacer = new CssUrlReplacer(); String processed = replacer.process(input, scope, cssRelativePath); - assertThat(processed, is(".class {background-image: url('./wicket/resource/org.apache.wicket.resource.CssUrlReplacerTest/res/images/some.img"+DECORATION_SUFFIX+"');}")); + assertThat( + processed, + is(".class {background-image: url('./wicket/resource/org.apache.wicket.resource.CssUrlReplacerTest/res/images/some.img" + + DECORATION_SUFFIX + "');}")); } @Test @@ -123,23 +136,37 @@ public class CssUrlReplacerTest extends WicketTestCase CssUrlReplacer replacer = new CssUrlReplacer(); String processed = replacer.process(input, scope, cssRelativePath); - assertThat(processed, is(".class {background-image: url('./wicket/resource/org.apache.wicket.resource.CssUrlReplacerTest/res/css/images/some.img"+DECORATION_SUFFIX+"');}")); + assertThat( + processed, + is(".class {background-image: url('./wicket/resource/org.apache.wicket.resource.CssUrlReplacerTest/res/css/images/some.img" + + DECORATION_SUFFIX + "');}")); + } + + @Test + public void base64EncodedImage() + { + String input = ".class {background-image: url('Beer.gif?embedBase64');}"; + Class<?> scope = ImageTest.class; + String cssRelativePath = "some.css"; + CssUrlReplacer replacer = new CssUrlReplacer(); + String processed = replacer.process(input, scope, cssRelativePath); + assertThat( + processed, + containsString(".class {background-image: url(data:image/gif;base64,R0lGODlh1wATAXAAACH5BAEAAP8ALAAAAADXA")); } @Test public void severalUrls() { - String input = - ".class {\n" + - "a: url('../images/a.img');\n" + - "b: url('./b.img');\n" + - "}"; + String input = ".class {\n" + "a: url('../images/a.img');\n" + "b: url('./b.img');\n" + "}"; Class<?> scope = CssUrlReplacerTest.class; String cssRelativePath = "res/css/some.css"; CssUrlReplacer replacer = new CssUrlReplacer(); String processed = replacer.process(input, scope, cssRelativePath); - assertThat(processed, containsString("CssUrlReplacerTest/res/images/a.img"+DECORATION_SUFFIX+"');")); - assertThat(processed, containsString("CssUrlReplacerTest/res/css/b.img"+DECORATION_SUFFIX+"');")); + assertThat(processed, containsString("CssUrlReplacerTest/res/images/a.img" + + DECORATION_SUFFIX + "');")); + assertThat(processed, containsString("CssUrlReplacerTest/res/css/b.img" + + DECORATION_SUFFIX + "');")); } }
