Rework ImageBanner Support
Refactor several aspects of the ImageBanner: - Extract a few new classes and methods from the previous code - Directly encode ANSI rather than using `${}` properties - Rework the scaling algorithm to prefer a fixed width - Allow ImageBanner and TextBanner to be used together - Rename several of the `banner.image` properties - Add support for a left hand margin - Add property meta-data See gh-4647
|
@ -1 +1 @@
|
|||
name: Phil
|
||||
name: Phil
|
||||
|
|
After Width: | Height: | Size: 62 KiB |
|
@ -0,0 +1 @@
|
|||
${Ansi.GREEN} :: Sample application build with Spring Boot${spring-boot.formatted-version} ::${Ansi.DEFAULT}
|
|
@ -18,26 +18,24 @@ package org.springframework.boot;
|
|||
|
||||
import java.awt.Color;
|
||||
import java.awt.Image;
|
||||
import java.awt.color.ColorSpace;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.PrintStream;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.boot.ansi.AnsiPropertySource;
|
||||
import org.springframework.boot.ansi.AnsiBackground;
|
||||
import org.springframework.boot.ansi.AnsiColor;
|
||||
import org.springframework.boot.ansi.AnsiColors;
|
||||
import org.springframework.boot.ansi.AnsiElement;
|
||||
import org.springframework.boot.ansi.AnsiOutput;
|
||||
import org.springframework.boot.bind.RelaxedPropertyResolver;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.env.MutablePropertySources;
|
||||
import org.springframework.core.env.PropertyResolver;
|
||||
import org.springframework.core.env.PropertySourcesPropertyResolver;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
|
@ -46,271 +44,137 @@ import org.springframework.util.Assert;
|
|||
* {@link Resource}.
|
||||
*
|
||||
* @author Craig Burke
|
||||
* @author Phillip Webb
|
||||
* @since 1.4.0
|
||||
*/
|
||||
public class ImageBanner implements Banner {
|
||||
|
||||
private static final Log log = LogFactory.getLog(ImageBanner.class);
|
||||
|
||||
private static final double RED_WEIGHT = 0.2126d;
|
||||
private static final double GREEN_WEIGHT = 0.7152d;
|
||||
private static final double BLUE_WEIGHT = 0.0722d;
|
||||
private static final double[] RGB_WEIGHT = { 0.2126d, 0.7152d, 0.0722d };
|
||||
|
||||
private static final int DEFAULT_MAX_WIDTH = 72;
|
||||
private static final double DEFAULT_ASPECT_RATIO = 0.5d;
|
||||
private static final boolean DEFAULT_DARK = false;
|
||||
private static final char[] PIXEL = { ' ', '.', '*', ':', 'o', '&', '8', '#', '@' };
|
||||
|
||||
private Resource image;
|
||||
private Map<String, Color> colors = new HashMap<String, Color>();
|
||||
private static final int LUMINANCE_INCREMENT = 10;
|
||||
|
||||
private static final int LUMINANCE_START = LUMINANCE_INCREMENT * PIXEL.length;
|
||||
|
||||
private final Resource image;
|
||||
|
||||
public ImageBanner(Resource image) {
|
||||
Assert.notNull(image, "Image must not be null");
|
||||
Assert.isTrue(image.exists(), "Image must exist");
|
||||
this.image = image;
|
||||
colorsInit();
|
||||
}
|
||||
|
||||
private void colorsInit() {
|
||||
this.colors.put("BLACK", new Color(0, 0, 0));
|
||||
this.colors.put("RED", new Color(170, 0, 0));
|
||||
this.colors.put("GREEN", new Color(0, 170, 0));
|
||||
this.colors.put("YELLOW", new Color(170, 85, 0));
|
||||
this.colors.put("BLUE", new Color(0, 0, 170));
|
||||
this.colors.put("MAGENTA", new Color(170, 0, 170));
|
||||
this.colors.put("CYAN", new Color(0, 170, 170));
|
||||
this.colors.put("WHITE", new Color(170, 170, 170));
|
||||
|
||||
this.colors.put("BRIGHT_BLACK", new Color(85, 85, 85));
|
||||
this.colors.put("BRIGHT_RED", new Color(255, 85, 85));
|
||||
this.colors.put("BRIGHT_GREEN", new Color(85, 255, 85));
|
||||
this.colors.put("BRIGHT_YELLOW", new Color(255, 255, 85));
|
||||
this.colors.put("BRIGHT_BLUE", new Color(85, 85, 255));
|
||||
this.colors.put("BRIGHT_MAGENTA", new Color(255, 85, 255));
|
||||
this.colors.put("BRIGHT_CYAN", new Color(85, 255, 255));
|
||||
this.colors.put("BRIGHT_WHITE", new Color(255, 255, 255));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void printBanner(Environment environment, Class<?> sourceClass, PrintStream out) {
|
||||
String headlessProperty = System.getProperty("java.awt.headless");
|
||||
public void printBanner(Environment environment, Class<?> sourceClass,
|
||||
PrintStream out) {
|
||||
String headless = System.getProperty("java.awt.headless");
|
||||
try {
|
||||
System.setProperty("java.awt.headless", "true");
|
||||
BufferedImage sourceImage = ImageIO.read(this.image.getInputStream());
|
||||
|
||||
int maxWidth = environment.getProperty("banner.image.max-width",
|
||||
Integer.class, DEFAULT_MAX_WIDTH);
|
||||
Double aspectRatio = environment.getProperty("banner.image.aspect-ratio",
|
||||
Double.class, DEFAULT_ASPECT_RATIO);
|
||||
boolean invert = environment.getProperty("banner.image.dark", Boolean.class,
|
||||
DEFAULT_DARK);
|
||||
|
||||
BufferedImage resizedImage = resizeImage(sourceImage, maxWidth, aspectRatio);
|
||||
String banner = imageToBanner(resizedImage, invert);
|
||||
|
||||
PropertyResolver ansiResolver = getAnsiResolver();
|
||||
banner = ansiResolver.resolvePlaceholders(banner);
|
||||
out.println(banner);
|
||||
printBanner(environment, out);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
log.warn("Image banner not printable: " + this.image + " (" + ex.getClass()
|
||||
+ ": '" + ex.getMessage() + "')", ex);
|
||||
}
|
||||
finally {
|
||||
System.setProperty("java.awt.headless", headlessProperty);
|
||||
if (headless == null) {
|
||||
System.clearProperty("java.awt.headless");
|
||||
}
|
||||
else {
|
||||
System.setProperty("java.awt.headless", headless);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private PropertyResolver getAnsiResolver() {
|
||||
MutablePropertySources sources = new MutablePropertySources();
|
||||
sources.addFirst(new AnsiPropertySource("ansi", true));
|
||||
return new PropertySourcesPropertyResolver(sources);
|
||||
private void printBanner(Environment environment, PrintStream out)
|
||||
throws IOException {
|
||||
PropertyResolver properties = new RelaxedPropertyResolver(environment,
|
||||
"banner.image.");
|
||||
int width = properties.getProperty("width", Integer.class, 76);
|
||||
int heigth = properties.getProperty("height", Integer.class, 0);
|
||||
int margin = properties.getProperty("margin", Integer.class, 2);
|
||||
boolean invert = properties.getProperty("invert", Boolean.class, false);
|
||||
BufferedImage image = readImage(width, heigth);
|
||||
printBanner(image, margin, invert, out);
|
||||
}
|
||||
|
||||
private String imageToBanner(BufferedImage image, boolean dark) {
|
||||
StringBuilder banner = new StringBuilder();
|
||||
private BufferedImage readImage(int width, int heigth) throws IOException {
|
||||
InputStream inputStream = this.image.getInputStream();
|
||||
try {
|
||||
BufferedImage image = ImageIO.read(inputStream);
|
||||
return resizeImage(image, width, heigth);
|
||||
}
|
||||
finally {
|
||||
inputStream.close();
|
||||
}
|
||||
}
|
||||
|
||||
private BufferedImage resizeImage(BufferedImage image, int width, int height) {
|
||||
if (width < 1) {
|
||||
width = 1;
|
||||
}
|
||||
if (height <= 0) {
|
||||
double aspectRatio = (double) width / image.getWidth() * 0.5;
|
||||
height = (int) Math.ceil(image.getHeight() * aspectRatio);
|
||||
}
|
||||
BufferedImage resized = new BufferedImage(width, height,
|
||||
BufferedImage.TYPE_INT_RGB);
|
||||
Image scaled = image.getScaledInstance(width, height, Image.SCALE_DEFAULT);
|
||||
resized.getGraphics().drawImage(scaled, 0, 0, null);
|
||||
return resized;
|
||||
}
|
||||
|
||||
private void printBanner(BufferedImage image, int margin, boolean invert,
|
||||
PrintStream out) {
|
||||
AnsiElement background = (invert ? AnsiBackground.BLACK : AnsiBackground.DEFAULT);
|
||||
out.print(AnsiOutput.encode(AnsiColor.DEFAULT));
|
||||
out.print(AnsiOutput.encode(background));
|
||||
out.println();
|
||||
out.println();
|
||||
AnsiColor lastColor = AnsiColor.DEFAULT;
|
||||
for (int y = 0; y < image.getHeight(); y++) {
|
||||
if (dark) {
|
||||
banner.append("${AnsiBackground.BLACK}");
|
||||
}
|
||||
else {
|
||||
banner.append("${AnsiBackground.DEFAULT}");
|
||||
for (int i = 0; i < margin; i++) {
|
||||
out.print(" ");
|
||||
}
|
||||
for (int x = 0; x < image.getWidth(); x++) {
|
||||
Color color = new Color(image.getRGB(x, y), false);
|
||||
banner.append(getFormatString(color, dark));
|
||||
AnsiColor ansiColor = AnsiColors.getClosest(color);
|
||||
if (ansiColor != lastColor) {
|
||||
out.print(AnsiOutput.encode(ansiColor));
|
||||
lastColor = ansiColor;
|
||||
}
|
||||
out.print(getAsciiPixel(color, invert));
|
||||
}
|
||||
if (dark) {
|
||||
banner.append("${AnsiBackground.DEFAULT}");
|
||||
}
|
||||
banner.append("${AnsiColor.DEFAULT}\n");
|
||||
out.println();
|
||||
}
|
||||
|
||||
return banner.toString();
|
||||
out.print(AnsiOutput.encode(AnsiColor.DEFAULT));
|
||||
out.print(AnsiOutput.encode(AnsiBackground.DEFAULT));
|
||||
out.println();
|
||||
}
|
||||
|
||||
protected String getFormatString(Color color, boolean dark) {
|
||||
String matchedColorName = null;
|
||||
Double minColorDistance = null;
|
||||
|
||||
for (Entry<String, Color> colorOption : this.colors.entrySet()) {
|
||||
double distance = getColorDistance(color, colorOption.getValue());
|
||||
|
||||
if (minColorDistance == null || distance < minColorDistance) {
|
||||
minColorDistance = distance;
|
||||
matchedColorName = colorOption.getKey();
|
||||
}
|
||||
}
|
||||
|
||||
return "${AnsiColor." + matchedColorName + "}" + getAsciiCharacter(color, dark);
|
||||
}
|
||||
|
||||
private static int getLuminance(Color color, boolean inverse) {
|
||||
double red = color.getRed();
|
||||
double green = color.getGreen();
|
||||
double blue = color.getBlue();
|
||||
|
||||
double luminance;
|
||||
|
||||
if (inverse) {
|
||||
luminance = (RED_WEIGHT * (255.0d - red)) + (GREEN_WEIGHT * (255.0d - green))
|
||||
+ (BLUE_WEIGHT * (255.0d - blue));
|
||||
}
|
||||
else {
|
||||
luminance = (RED_WEIGHT * red) + (GREEN_WEIGHT * green)
|
||||
+ (BLUE_WEIGHT * blue);
|
||||
}
|
||||
|
||||
return (int) Math.ceil((luminance / 255.0d) * 100);
|
||||
}
|
||||
|
||||
private static char getAsciiCharacter(Color color, boolean dark) {
|
||||
private char getAsciiPixel(Color color, boolean dark) {
|
||||
double luminance = getLuminance(color, dark);
|
||||
|
||||
if (luminance >= 90) {
|
||||
return ' ';
|
||||
}
|
||||
else if (luminance >= 80) {
|
||||
return '.';
|
||||
}
|
||||
else if (luminance >= 70) {
|
||||
return '*';
|
||||
}
|
||||
else if (luminance >= 60) {
|
||||
return ':';
|
||||
}
|
||||
else if (luminance >= 50) {
|
||||
return 'o';
|
||||
}
|
||||
else if (luminance >= 40) {
|
||||
return '&';
|
||||
}
|
||||
else if (luminance >= 30) {
|
||||
return '8';
|
||||
}
|
||||
else if (luminance >= 20) {
|
||||
return '#';
|
||||
}
|
||||
else {
|
||||
return '@';
|
||||
for (int i = 0; i < PIXEL.length; i++) {
|
||||
if (luminance >= (LUMINANCE_START - (i * LUMINANCE_INCREMENT))) {
|
||||
return PIXEL[i];
|
||||
}
|
||||
}
|
||||
return PIXEL[PIXEL.length - 1];
|
||||
}
|
||||
|
||||
private static BufferedImage resizeImage(BufferedImage sourceImage, int maxWidth,
|
||||
double aspectRatio) {
|
||||
int width;
|
||||
double resizeRatio;
|
||||
if (sourceImage.getWidth() > maxWidth) {
|
||||
resizeRatio = (double) maxWidth / (double) sourceImage.getWidth();
|
||||
width = maxWidth;
|
||||
}
|
||||
else {
|
||||
resizeRatio = 1.0d;
|
||||
width = sourceImage.getWidth();
|
||||
}
|
||||
|
||||
int height = (int) (Math.ceil(resizeRatio * aspectRatio
|
||||
* (double) sourceImage.getHeight()));
|
||||
Image image = sourceImage.getScaledInstance(width, height, Image.SCALE_DEFAULT);
|
||||
|
||||
BufferedImage resizedImage = new BufferedImage(image.getWidth(null),
|
||||
image.getHeight(null), BufferedImage.TYPE_INT_RGB);
|
||||
|
||||
resizedImage.getGraphics().drawImage(image, 0, 0, null);
|
||||
return resizedImage;
|
||||
private int getLuminance(Color color, boolean inverse) {
|
||||
double luminance = 0.0;
|
||||
luminance += getLuminance(color.getRed(), inverse, RGB_WEIGHT[0]);
|
||||
luminance += getLuminance(color.getGreen(), inverse, RGB_WEIGHT[1]);
|
||||
luminance += getLuminance(color.getBlue(), inverse, RGB_WEIGHT[2]);
|
||||
return (int) Math.ceil((luminance / 0xFF) * 100);
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the CIE94 distance between two colors.
|
||||
*
|
||||
* Contributed by michael-simons
|
||||
* (original implementation https://github.com/michael-simons/dfx-mosaic/blob/public/src/main/java/de/dailyfratze/mosaic/images/CIE94ColorDistance.java)
|
||||
*
|
||||
* @param color1 the first color
|
||||
* @param color2 the second color
|
||||
* @return the distance between the colors
|
||||
*/
|
||||
private static double getColorDistance(final Color color1, final Color color2) {
|
||||
// Convert to L*a*b* color space
|
||||
float[] lab1 = toLab(color1);
|
||||
float[] lab2 = toLab(color2);
|
||||
|
||||
// Make it more readable
|
||||
double L1 = lab1[0];
|
||||
double a1 = lab1[1];
|
||||
double b1 = lab1[2];
|
||||
double L2 = lab2[0];
|
||||
double a2 = lab2[1];
|
||||
double b2 = lab2[2];
|
||||
|
||||
// CIE94 coefficients for graphic arts
|
||||
double kL = 1;
|
||||
double K1 = 0.045;
|
||||
double K2 = 0.015;
|
||||
// Weighting factors
|
||||
double sl = 1.0;
|
||||
double kc = 1.0;
|
||||
double kh = 1.0;
|
||||
|
||||
// See http://en.wikipedia.org/wiki/Color_difference#CIE94
|
||||
double c1 = Math.sqrt(a1 * a1 + b1 * b1);
|
||||
double deltaC = c1 - Math.sqrt(a2 * a2 + b2 * b2);
|
||||
double deltaA = a1 - a2;
|
||||
double deltaB = b1 - b2;
|
||||
double deltaH = Math.sqrt(Math.max(0.0, deltaA * deltaA + deltaB * deltaB - deltaC * deltaC));
|
||||
|
||||
return Math.sqrt(Math.max(0.0, Math.pow((L1 - L2) / (kL * sl), 2) + Math.pow(deltaC / (kc * (1 + K1 * c1)), 2) + Math.pow(deltaH / (kh * (1 + K2 * c1)), 2.0)));
|
||||
private double getLuminance(int component, boolean inverse, double weight) {
|
||||
return (inverse ? 0xFF - component : component) * weight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the CIE L*a*b* values of this color.
|
||||
*
|
||||
* Implements the forward transformation described in
|
||||
* https://en.wikipedia.org/wiki/Lab_color_space
|
||||
*
|
||||
* @param color the color to convert
|
||||
* @return the xyz color components
|
||||
*/
|
||||
static float[] toLab(Color color) {
|
||||
float[] xyz = color.getColorComponents(
|
||||
ColorSpace.getInstance(ColorSpace.CS_CIEXYZ), null);
|
||||
|
||||
return xyzToLab(xyz);
|
||||
}
|
||||
|
||||
static float[] xyzToLab(float[] colorvalue) {
|
||||
double l = f(colorvalue[1]);
|
||||
double L = 116.0 * l - 16.0;
|
||||
double a = 500.0 * (f(colorvalue[0]) - l);
|
||||
double b = 200.0 * (l - f(colorvalue[2]));
|
||||
return new float[]{(float) L, (float) a, (float) b};
|
||||
}
|
||||
|
||||
private static double f(double t) {
|
||||
if (t > 216.0 / 24389.0) {
|
||||
return Math.cbrt(t);
|
||||
}
|
||||
else {
|
||||
return (1.0 / 3.0) * Math.pow(29.0 / 6.0, 2) * t + (4.0 / 29.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ import org.springframework.util.Assert;
|
|||
import org.springframework.util.StreamUtils;
|
||||
|
||||
/**
|
||||
* Banner implementation that prints from a source {@link Resource}.
|
||||
* Banner implementation that prints from a source text {@link Resource}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Vedran Pavic
|
||||
|
|
|
@ -16,9 +16,6 @@
|
|||
|
||||
package org.springframework.boot;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.security.AccessControlException;
|
||||
import java.util.ArrayList;
|
||||
|
@ -41,6 +38,7 @@ import org.springframework.beans.factory.groovy.GroovyBeanDefinitionReader;
|
|||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.BeanNameGenerator;
|
||||
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
|
||||
import org.springframework.boot.Banner.Mode;
|
||||
import org.springframework.boot.diagnostics.FailureAnalyzers;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextInitializer;
|
||||
|
@ -167,19 +165,17 @@ public class SpringApplication {
|
|||
/**
|
||||
* Default banner location.
|
||||
*/
|
||||
public static final String BANNER_LOCATION_PROPERTY_VALUE = "banner.txt";
|
||||
public static final String BANNER_LOCATION_PROPERTY_VALUE = SpringApplicationBannerPrinter.DEFAULT_BANNER_LOCATION;
|
||||
|
||||
/**
|
||||
* Banner location property key.
|
||||
*/
|
||||
public static final String BANNER_LOCATION_PROPERTY = "banner.location";
|
||||
public static final String BANNER_LOCATION_PROPERTY = SpringApplicationBannerPrinter.BANNER_LOCATION_PROPERTY;
|
||||
|
||||
private static final String CONFIGURABLE_WEB_ENVIRONMENT_CLASS = "org.springframework.web.context.ConfigurableWebEnvironment";
|
||||
|
||||
private static final String SYSTEM_PROPERTY_JAVA_AWT_HEADLESS = "java.awt.headless";
|
||||
|
||||
private static final Banner DEFAULT_BANNER = new SpringBootBanner();
|
||||
|
||||
private static final Set<String> SERVLET_ENVIRONMENT_SOURCE_NAMES;
|
||||
|
||||
static {
|
||||
|
@ -543,60 +539,16 @@ public class SpringApplication {
|
|||
* @see #setBannerMode
|
||||
*/
|
||||
protected void printBanner(Environment environment) {
|
||||
Banner selectedBanner = selectBanner(environment);
|
||||
if (this.bannerMode == Banner.Mode.LOG) {
|
||||
try {
|
||||
logger.info(createStringFromBanner(selectedBanner, environment));
|
||||
}
|
||||
catch (UnsupportedEncodingException ex) {
|
||||
logger.warn("Failed to create String for banner", ex);
|
||||
}
|
||||
}
|
||||
else {
|
||||
selectedBanner.printBanner(environment, this.mainApplicationClass,
|
||||
System.out);
|
||||
}
|
||||
}
|
||||
|
||||
private Banner selectBanner(Environment environment) {
|
||||
String location = environment.getProperty(BANNER_LOCATION_PROPERTY,
|
||||
BANNER_LOCATION_PROPERTY_VALUE);
|
||||
ResourceLoader resourceLoader = this.resourceLoader != null ? this.resourceLoader
|
||||
: new DefaultResourceLoader(getClassLoader());
|
||||
Resource resource = resourceLoader.getResource(location);
|
||||
if (resource.exists()) {
|
||||
return new ResourceBanner(resource);
|
||||
SpringApplicationBannerPrinter banner = new SpringApplicationBannerPrinter(resourceLoader,
|
||||
this.banner);
|
||||
if (this.bannerMode == Mode.LOG) {
|
||||
banner.print(environment, this.mainApplicationClass, logger);
|
||||
}
|
||||
if (this.banner != null) {
|
||||
return this.banner;
|
||||
else {
|
||||
banner.print(environment, this.mainApplicationClass, System.out);
|
||||
}
|
||||
|
||||
Resource image = getBannerImage(environment, resourceLoader);
|
||||
if (image.exists()) {
|
||||
return new ImageBanner(image);
|
||||
}
|
||||
|
||||
return DEFAULT_BANNER;
|
||||
}
|
||||
|
||||
private Resource getBannerImage(Environment environment, ResourceLoader resourceLoader) {
|
||||
String imageLocation = environment.getProperty("banner.image", "banner.gif");
|
||||
Resource image = resourceLoader.getResource(imageLocation);
|
||||
if (!image.exists()) {
|
||||
image = resourceLoader.getResource("banner.jpg");
|
||||
}
|
||||
if (!image.exists()) {
|
||||
image = resourceLoader.getResource("banner.png");
|
||||
}
|
||||
return image;
|
||||
}
|
||||
|
||||
private String createStringFromBanner(Banner banner, Environment environment)
|
||||
throws UnsupportedEncodingException {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
banner.printBanner(environment, this.mainApplicationClass, new PrintStream(baos));
|
||||
String charset = environment.getProperty("banner.charset", "UTF-8");
|
||||
return baos.toString(charset);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,146 @@
|
|||
/*
|
||||
* Copyright 2012-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Class used by {@link SpringApplication} to print the application banner.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class SpringApplicationBannerPrinter {
|
||||
|
||||
static final String BANNER_LOCATION_PROPERTY = "banner.location";
|
||||
|
||||
static final String BANNER_IMAGE_LOCATION_PROPERTY = "banner.image.location";
|
||||
|
||||
static final String DEFAULT_BANNER_LOCATION = "banner.txt";
|
||||
|
||||
static final String[] IMAGE_EXTENSION = { "gif", "jpg", "png" };
|
||||
|
||||
private static final Banner DEFAULT_BANNER = new SpringBootBanner();
|
||||
|
||||
private final ResourceLoader resourceLoader;
|
||||
|
||||
private final Banner fallbackBanner;
|
||||
|
||||
SpringApplicationBannerPrinter(ResourceLoader resourceLoader, Banner fallbackBanner) {
|
||||
this.resourceLoader = resourceLoader;
|
||||
this.fallbackBanner = fallbackBanner;
|
||||
}
|
||||
|
||||
public void print(Environment environment, Class<?> sourceClass, Log logger) {
|
||||
Banner banner = getBanner(environment, this.fallbackBanner);
|
||||
try {
|
||||
logger.info(createStringFromBanner(banner, environment, sourceClass));
|
||||
}
|
||||
catch (UnsupportedEncodingException ex) {
|
||||
logger.warn("Failed to create String for banner", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void print(Environment environment, Class<?> sourceClass, PrintStream out) {
|
||||
Banner banner = getBanner(environment, this.fallbackBanner);
|
||||
banner.printBanner(environment, sourceClass, out);
|
||||
}
|
||||
|
||||
private Banner getBanner(Environment environment, Banner definedBanner) {
|
||||
Banners banners = new Banners();
|
||||
banners.addIfNotNull(getImageBanner(environment));
|
||||
banners.addIfNotNull(getTextBanner(environment));
|
||||
if (banners.hasAtLeastOneBanner()) {
|
||||
return banners;
|
||||
}
|
||||
if (this.fallbackBanner != null) {
|
||||
return this.fallbackBanner;
|
||||
}
|
||||
return DEFAULT_BANNER;
|
||||
}
|
||||
|
||||
private Banner getTextBanner(Environment environment) {
|
||||
String location = environment.getProperty(BANNER_LOCATION_PROPERTY,
|
||||
DEFAULT_BANNER_LOCATION);
|
||||
Resource resource = this.resourceLoader.getResource(location);
|
||||
if (resource.exists()) {
|
||||
return new ResourceBanner(resource);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Banner getImageBanner(Environment environment) {
|
||||
String location = environment.getProperty(BANNER_IMAGE_LOCATION_PROPERTY);
|
||||
if (StringUtils.hasLength(location)) {
|
||||
Resource resource = this.resourceLoader.getResource(location);
|
||||
return (resource.exists() ? new ImageBanner(resource) : null);
|
||||
}
|
||||
for (String ext : IMAGE_EXTENSION) {
|
||||
Resource resource = this.resourceLoader.getResource("banner." + ext);
|
||||
if (resource.exists()) {
|
||||
return new ImageBanner(resource);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private String createStringFromBanner(Banner banner, Environment environment,
|
||||
Class<?> mainApplicationClass) throws UnsupportedEncodingException {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
banner.printBanner(environment, mainApplicationClass, new PrintStream(baos));
|
||||
String charset = environment.getProperty("banner.charset", "UTF-8");
|
||||
return baos.toString(charset);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Banner} comprised of other {@link Banner Banners}.
|
||||
*/
|
||||
private static class Banners implements Banner {
|
||||
|
||||
private final List<Banner> banners = new ArrayList<Banner>();
|
||||
|
||||
public void addIfNotNull(Banner banner) {
|
||||
if (banner != null) {
|
||||
this.banners.add(banner);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasAtLeastOneBanner() {
|
||||
return !this.banners.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void printBanner(Environment environment, Class<?> sourceClass,
|
||||
PrintStream out) {
|
||||
for (Banner banner : this.banners) {
|
||||
banner.printBanner(environment, sourceClass, out);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,140 @@
|
|||
/*
|
||||
* Copyright 2012-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.ansi;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.color.ColorSpace;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Utility for working with {@link AnsiColor} in the context of {@link Color AWT Colors}.
|
||||
*
|
||||
* @author Craig Burke
|
||||
* @author Ruben Dijkstra
|
||||
* @author Phillip Webb
|
||||
* @author Michael Simons
|
||||
* @since 1.4.0
|
||||
*/
|
||||
public final class AnsiColors {
|
||||
|
||||
private static final Map<AnsiColor, LabColor> ANSI_COLOR_MAP;
|
||||
|
||||
static {
|
||||
Map<AnsiColor, LabColor> colorMap = new LinkedHashMap<AnsiColor, LabColor>();
|
||||
colorMap.put(AnsiColor.BLACK, new LabColor(0x000000));
|
||||
colorMap.put(AnsiColor.RED, new LabColor(0xAA0000));
|
||||
colorMap.put(AnsiColor.GREEN, new LabColor(0x00AA00));
|
||||
colorMap.put(AnsiColor.YELLOW, new LabColor(0xAA5500));
|
||||
colorMap.put(AnsiColor.BLUE, new LabColor(0x0000AA));
|
||||
colorMap.put(AnsiColor.MAGENTA, new LabColor(0xAA00AA));
|
||||
colorMap.put(AnsiColor.CYAN, new LabColor(0x00AAAA));
|
||||
colorMap.put(AnsiColor.WHITE, new LabColor(0xAAAAAA));
|
||||
colorMap.put(AnsiColor.BRIGHT_BLACK, new LabColor(0x555555));
|
||||
colorMap.put(AnsiColor.BRIGHT_RED, new LabColor(0xFF5555));
|
||||
colorMap.put(AnsiColor.BRIGHT_GREEN, new LabColor(0x55FF00));
|
||||
colorMap.put(AnsiColor.BRIGHT_YELLOW, new LabColor(0xFFFF55));
|
||||
colorMap.put(AnsiColor.BRIGHT_BLUE, new LabColor(0x5555FF));
|
||||
colorMap.put(AnsiColor.BRIGHT_MAGENTA, new LabColor(0xFF55FF));
|
||||
colorMap.put(AnsiColor.BRIGHT_CYAN, new LabColor(0x55FFFF));
|
||||
colorMap.put(AnsiColor.BRIGHT_WHITE, new LabColor(0xFFFFFF));
|
||||
ANSI_COLOR_MAP = Collections.unmodifiableMap(colorMap);
|
||||
}
|
||||
|
||||
private AnsiColors() {
|
||||
}
|
||||
|
||||
public static AnsiColor getClosest(Color color) {
|
||||
return getClosest(new LabColor(color));
|
||||
}
|
||||
|
||||
private static AnsiColor getClosest(LabColor color) {
|
||||
AnsiColor result = null;
|
||||
double resultDistance = Float.MAX_VALUE;
|
||||
for (Entry<AnsiColor, LabColor> entry : ANSI_COLOR_MAP.entrySet()) {
|
||||
double distance = color.getDistance(entry.getValue());
|
||||
if (result == null || distance < resultDistance) {
|
||||
resultDistance = distance;
|
||||
result = entry.getKey();
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a color stored in LAB form.
|
||||
*/
|
||||
private static final class LabColor {
|
||||
|
||||
private static final ColorSpace XYZ_COLOR_SPACE = ColorSpace
|
||||
.getInstance(ColorSpace.CS_CIEXYZ);
|
||||
|
||||
private final double l;
|
||||
|
||||
private final double a;
|
||||
|
||||
private final double b;
|
||||
|
||||
LabColor(Integer rgb) {
|
||||
this(rgb == null ? (Color) null : new Color(rgb));
|
||||
}
|
||||
|
||||
LabColor(Color color) {
|
||||
Assert.notNull(color, "Color must not be null");
|
||||
float[] lab = fromXyz(color.getColorComponents(XYZ_COLOR_SPACE, null));
|
||||
this.l = lab[0];
|
||||
this.a = lab[1];
|
||||
this.b = lab[2];
|
||||
}
|
||||
|
||||
private float[] fromXyz(float[] xyz) {
|
||||
return fromXyz(xyz[0], xyz[1], xyz[2]);
|
||||
}
|
||||
|
||||
private float[] fromXyz(float x, float y, float z) {
|
||||
double l = (f(y) - 16.0) * 116.0;
|
||||
double a = (f(x) - f(y)) * 500.0;
|
||||
double b = (f(y) - f(z)) * 200.0;
|
||||
return new float[] { (float) l, (float) a, (float) b };
|
||||
}
|
||||
|
||||
private double f(double t) {
|
||||
return (t > (216.0 / 24389.0) ? Math.cbrt(t)
|
||||
: (1.0 / 3.0) * Math.pow(29.0 / 6.0, 2) * t + (4.0 / 29.0));
|
||||
}
|
||||
|
||||
// See http://en.wikipedia.org/wiki/Color_difference#CIE94
|
||||
public double getDistance(LabColor other) {
|
||||
double c1 = Math.sqrt(this.a * this.a + this.b * this.b);
|
||||
double deltaC = c1 - Math.sqrt(other.a * other.a + other.b * other.b);
|
||||
double deltaA = this.a - other.a;
|
||||
double deltaB = this.b - other.b;
|
||||
double deltaH = Math.sqrt(
|
||||
Math.max(0.0, deltaA * deltaA + deltaB * deltaB - deltaC * deltaC));
|
||||
return Math.sqrt(Math.max(0.0,
|
||||
Math.pow((this.l - other.l) / (1.0), 2)
|
||||
+ Math.pow(deltaC / (1 + 0.045 * c1), 2)
|
||||
+ Math.pow(deltaH / (1 + 0.015 * c1), 2.0)));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* Copyright 2012-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.ansi;
|
||||
|
||||
import java.awt.Color;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link AnsiColors}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class AnsiColorsTests {
|
||||
|
||||
@Test
|
||||
public void getClosestWhenExactMatchShouldReturnAnsiColor() throws Exception {
|
||||
assertThat(getClosest(0x000000)).isEqualTo(AnsiColor.BLACK);
|
||||
assertThat(getClosest(0xAA0000)).isEqualTo(AnsiColor.RED);
|
||||
assertThat(getClosest(0x00AA00)).isEqualTo(AnsiColor.GREEN);
|
||||
assertThat(getClosest(0xAA5500)).isEqualTo(AnsiColor.YELLOW);
|
||||
assertThat(getClosest(0x0000AA)).isEqualTo(AnsiColor.BLUE);
|
||||
assertThat(getClosest(0xAA00AA)).isEqualTo(AnsiColor.MAGENTA);
|
||||
assertThat(getClosest(0x00AAAA)).isEqualTo(AnsiColor.CYAN);
|
||||
assertThat(getClosest(0xAAAAAA)).isEqualTo(AnsiColor.WHITE);
|
||||
assertThat(getClosest(0x555555)).isEqualTo(AnsiColor.BRIGHT_BLACK);
|
||||
assertThat(getClosest(0xFF5555)).isEqualTo(AnsiColor.BRIGHT_RED);
|
||||
assertThat(getClosest(0x55FF00)).isEqualTo(AnsiColor.BRIGHT_GREEN);
|
||||
assertThat(getClosest(0xFFFF55)).isEqualTo(AnsiColor.BRIGHT_YELLOW);
|
||||
assertThat(getClosest(0x5555FF)).isEqualTo(AnsiColor.BRIGHT_BLUE);
|
||||
assertThat(getClosest(0xFF55FF)).isEqualTo(AnsiColor.BRIGHT_MAGENTA);
|
||||
assertThat(getClosest(0x55FFFF)).isEqualTo(AnsiColor.BRIGHT_CYAN);
|
||||
assertThat(getClosest(0xFFFFFF)).isEqualTo(AnsiColor.BRIGHT_WHITE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getClosestWhenCloseShouldReturnAnsiColor() throws Exception {
|
||||
assertThat(getClosest(0x292424)).isEqualTo(AnsiColor.BLACK);
|
||||
assertThat(getClosest(0x8C1919)).isEqualTo(AnsiColor.RED);
|
||||
assertThat(getClosest(0x0BA10B)).isEqualTo(AnsiColor.GREEN);
|
||||
assertThat(getClosest(0xB55F09)).isEqualTo(AnsiColor.YELLOW);
|
||||
assertThat(getClosest(0x0B0BA1)).isEqualTo(AnsiColor.BLUE);
|
||||
assertThat(getClosest(0xA312A3)).isEqualTo(AnsiColor.MAGENTA);
|
||||
assertThat(getClosest(0x0BB5B5)).isEqualTo(AnsiColor.CYAN);
|
||||
assertThat(getClosest(0xBAB6B6)).isEqualTo(AnsiColor.WHITE);
|
||||
assertThat(getClosest(0x615A5A)).isEqualTo(AnsiColor.BRIGHT_BLACK);
|
||||
assertThat(getClosest(0xF23333)).isEqualTo(AnsiColor.BRIGHT_RED);
|
||||
assertThat(getClosest(0x55E80C)).isEqualTo(AnsiColor.BRIGHT_GREEN);
|
||||
assertThat(getClosest(0xF5F54C)).isEqualTo(AnsiColor.BRIGHT_YELLOW);
|
||||
assertThat(getClosest(0x5656F0)).isEqualTo(AnsiColor.BRIGHT_BLUE);
|
||||
assertThat(getClosest(0xFA50FA)).isEqualTo(AnsiColor.BRIGHT_MAGENTA);
|
||||
assertThat(getClosest(0x56F5F5)).isEqualTo(AnsiColor.BRIGHT_CYAN);
|
||||
assertThat(getClosest(0xEDF5F5)).isEqualTo(AnsiColor.BRIGHT_WHITE);
|
||||
}
|
||||
|
||||
private AnsiColor getClosest(int rgb) {
|
||||
return AnsiColors.getClosest(new Color(rgb));
|
||||
}
|
||||
|
||||
}
|
|
@ -13,9 +13,36 @@
|
|||
{
|
||||
"name": "banner.location",
|
||||
"type": "org.springframework.core.io.Resource",
|
||||
"description": "Banner file location.",
|
||||
"description": "Banner text resource location.",
|
||||
"defaultValue": "classpath:banner.txt"
|
||||
},
|
||||
{
|
||||
"name": "banner.image.location",
|
||||
"type": "org.springframework.core.io.Resource",
|
||||
"description": "Banner image file location.",
|
||||
"defaultValue": "banner.gif"
|
||||
},
|
||||
{
|
||||
"name": "banner.image.width",
|
||||
"type": "java.lang.Integer",
|
||||
"description": "Banner image width (in chars)."
|
||||
},
|
||||
{
|
||||
"name": "banner.image.height",
|
||||
"type": "java.lang.Integer",
|
||||
"description": "Banner image height (in chars)."
|
||||
},
|
||||
{
|
||||
"name": "banner.image.margin",
|
||||
"type": "java.lang.Integer",
|
||||
"description": "Left hand image height (in chars)."
|
||||
},
|
||||
{
|
||||
"name": "banner.image.invert",
|
||||
"type": "java.lang.Boolean",
|
||||
"description": "Invert images for dark console themes.",
|
||||
"defaultValue": false
|
||||
},
|
||||
{
|
||||
"name": "debug",
|
||||
"type": "java.lang.Boolean",
|
||||
|
|
|
@ -18,219 +18,171 @@ package org.springframework.boot;
|
|||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.boot.ansi.AnsiBackground;
|
||||
import org.springframework.boot.ansi.AnsiColor;
|
||||
import org.springframework.boot.ansi.AnsiElement;
|
||||
import org.springframework.boot.ansi.AnsiOutput;
|
||||
import org.springframework.boot.ansi.AnsiOutput.Enabled;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.env.MapPropertySource;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.mock.env.MockEnvironment;
|
||||
import org.springframework.test.context.support.TestPropertySourceUtils;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.startsWith;
|
||||
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link ImageBanner}.
|
||||
*
|
||||
* @author Craig Burke
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class ImageBannerTests {
|
||||
|
||||
private static final String IMAGE_BANNER_BLACK_AND_WHITE = "banners/black-and-white.gif";
|
||||
private static final String IMAGE_BANNER_LARGE = "banners/large.gif";
|
||||
private static final String IMAGE_BANNER_ALL_COLORS = "banners/colors.gif";
|
||||
private static final String IMAGE_BANNER_GRADIENT = "banners/gradient.gif";
|
||||
|
||||
private static final String BACKGROUND_DEFAULT_ANSI = getAnsiOutput(AnsiBackground.DEFAULT);
|
||||
private static final String BACKGROUND_DARK_ANSI = getAnsiOutput(AnsiBackground.BLACK);
|
||||
private static final String NEW_LINE = System.getProperty("line.separator");
|
||||
|
||||
private static final char HIGH_LUMINANCE_CHARACTER = ' ';
|
||||
|
||||
private static final char LOW_LUMINANCE_CHARACTER = '@';
|
||||
|
||||
private static Map<String, Object> properties;
|
||||
private static final String INVERT_TRUE = "banner.image.invert=true";
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
AnsiOutput.setEnabled(AnsiOutput.Enabled.ALWAYS);
|
||||
properties = new HashMap<String, Object>();
|
||||
}
|
||||
|
||||
@After
|
||||
public void cleanup() {
|
||||
AnsiOutput.setEnabled(Enabled.DETECT);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void renderDefaultBackground() {
|
||||
String banner = printBanner(IMAGE_BANNER_BLACK_AND_WHITE);
|
||||
|
||||
assertThat(banner, startsWith(BACKGROUND_DEFAULT_ANSI));
|
||||
public void printBannerShouldResetForegroundAndBackground() {
|
||||
String banner = printBanner("black-and-white.gif");
|
||||
String expected = AnsiOutput.encode(AnsiColor.DEFAULT)
|
||||
+ AnsiOutput.encode(AnsiBackground.DEFAULT);
|
||||
assertThat(banner).startsWith(expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void renderDarkBackground() {
|
||||
setDark(true);
|
||||
String banner = printBanner(IMAGE_BANNER_BLACK_AND_WHITE);
|
||||
|
||||
assertThat(banner, startsWith(BACKGROUND_DARK_ANSI));
|
||||
public void printBannerWhenInvertedShouldResetForegroundAndBackground() {
|
||||
String banner = printBanner("black-and-white.gif", INVERT_TRUE);
|
||||
String expected = AnsiOutput.encode(AnsiColor.DEFAULT)
|
||||
+ AnsiOutput.encode(AnsiBackground.BLACK);
|
||||
assertThat(banner).startsWith(expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void renderWhiteCharactersWithColors() {
|
||||
String banner = printBanner(IMAGE_BANNER_BLACK_AND_WHITE);
|
||||
String expectedFirstLine = getAnsiOutput(AnsiColor.BRIGHT_WHITE)
|
||||
public void printBannerShouldPrintWhiteAsBrightWhiteHighLuminance() {
|
||||
String banner = printBanner("black-and-white.gif");
|
||||
String expected = AnsiOutput.encode(AnsiColor.BRIGHT_WHITE)
|
||||
+ HIGH_LUMINANCE_CHARACTER;
|
||||
|
||||
assertThat(banner, containsString(expectedFirstLine));
|
||||
assertThat(banner).contains(expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void renderWhiteCharactersOnDarkBackground() {
|
||||
setDark(true);
|
||||
String banner = printBanner(IMAGE_BANNER_BLACK_AND_WHITE);
|
||||
String expectedFirstLine = getAnsiOutput(AnsiColor.BRIGHT_WHITE)
|
||||
public void printBannerWhenInvertedShouldPrintWhiteAsBrightWhiteLowLuminance() {
|
||||
String banner = printBanner("black-and-white.gif", INVERT_TRUE);
|
||||
String expected = AnsiOutput.encode(AnsiColor.BRIGHT_WHITE)
|
||||
+ LOW_LUMINANCE_CHARACTER;
|
||||
|
||||
assertThat(banner, containsString(expectedFirstLine));
|
||||
assertThat(banner).contains(expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void renderBlackCharactersOnDefaultBackground() {
|
||||
String banner = printBanner(IMAGE_BANNER_BLACK_AND_WHITE);
|
||||
String blackCharacter = getAnsiOutput(AnsiColor.BLACK) + LOW_LUMINANCE_CHARACTER;
|
||||
|
||||
assertThat(banner, containsString(blackCharacter));
|
||||
public void printBannerShouldPrintBlackAsBlackLowLuminance() {
|
||||
String banner = printBanner("black-and-white.gif");
|
||||
String expected = AnsiOutput.encode(AnsiColor.BLACK) + LOW_LUMINANCE_CHARACTER;
|
||||
assertThat(banner).contains(expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void renderBlackCharactersOnDarkBackground() {
|
||||
setDark(true);
|
||||
String banner = printBanner(IMAGE_BANNER_BLACK_AND_WHITE);
|
||||
String blackCharacter = getAnsiOutput(AnsiColor.BLACK) + HIGH_LUMINANCE_CHARACTER;
|
||||
|
||||
assertThat(banner, containsString(blackCharacter));
|
||||
public void printBannerWhenInvertedShouldPrintBlackAsBlackHighLuminance() {
|
||||
String banner = printBanner("black-and-white.gif", INVERT_TRUE);
|
||||
String expected = AnsiOutput.encode(AnsiColor.BLACK) + HIGH_LUMINANCE_CHARACTER;
|
||||
assertThat(banner).contains(expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void renderBannerWithAllColors() {
|
||||
String banner = printBanner(IMAGE_BANNER_ALL_COLORS);
|
||||
|
||||
assertThat("Banner contains BLACK", banner,
|
||||
containsString(getAnsiOutput(AnsiColor.BLACK)));
|
||||
assertThat("Banner contains RED", banner,
|
||||
containsString(getAnsiOutput(AnsiColor.RED)));
|
||||
assertThat("Banner contains GREEN", banner,
|
||||
containsString(getAnsiOutput(AnsiColor.GREEN)));
|
||||
assertThat("Banner contains YELLOW", banner,
|
||||
containsString(getAnsiOutput(AnsiColor.YELLOW)));
|
||||
assertThat("Banner contains BLUE", banner,
|
||||
containsString(getAnsiOutput(AnsiColor.BLUE)));
|
||||
assertThat("Banner contains MAGENTA", banner,
|
||||
containsString(getAnsiOutput(AnsiColor.MAGENTA)));
|
||||
assertThat("Banner contains CYAN", banner,
|
||||
containsString(getAnsiOutput(AnsiColor.CYAN)));
|
||||
assertThat("Banner contains WHITE", banner,
|
||||
containsString(getAnsiOutput(AnsiColor.WHITE)));
|
||||
|
||||
assertThat("Banner contains BRIGHT_BLACK", banner,
|
||||
containsString(getAnsiOutput(AnsiColor.BRIGHT_BLACK)));
|
||||
assertThat("Banner contains BRIGHT_RED", banner,
|
||||
containsString(getAnsiOutput(AnsiColor.BRIGHT_RED)));
|
||||
assertThat("Banner contains BRIGHT_GREEN", banner,
|
||||
containsString(getAnsiOutput(AnsiColor.BRIGHT_GREEN)));
|
||||
assertThat("Banner contains BRIGHT_YELLOW", banner,
|
||||
containsString(getAnsiOutput(AnsiColor.BRIGHT_YELLOW)));
|
||||
assertThat("Banner contains BRIGHT_BLUE", banner,
|
||||
containsString(getAnsiOutput(AnsiColor.BRIGHT_BLUE)));
|
||||
assertThat("Banner contains BRIGHT_MAGENTA", banner,
|
||||
containsString(getAnsiOutput(AnsiColor.BRIGHT_MAGENTA)));
|
||||
assertThat("Banner contains BRIGHT_CYAN", banner,
|
||||
containsString(getAnsiOutput(AnsiColor.BRIGHT_CYAN)));
|
||||
assertThat("Banner contains BRIGHT_WHITE", banner,
|
||||
containsString(getAnsiOutput(AnsiColor.BRIGHT_WHITE)));
|
||||
public void printBannerWhenShouldPrintAllColors() {
|
||||
String banner = printBanner("colors.gif");
|
||||
for (AnsiColor color : AnsiColor.values()) {
|
||||
if (color != AnsiColor.DEFAULT) {
|
||||
assertThat(banner).contains(AnsiOutput.encode(color));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void renderSimpleGradient() {
|
||||
public void printBannerShouldRenderGradient() throws Exception {
|
||||
AnsiOutput.setEnabled(AnsiOutput.Enabled.NEVER);
|
||||
String banner = printBanner(IMAGE_BANNER_GRADIENT);
|
||||
String expectedResult = "@#8&o:*. ";
|
||||
|
||||
assertThat(banner, startsWith(expectedResult));
|
||||
String banner = printBanner("gradient.gif", "banner.image.width=10",
|
||||
"banner.image.margin=0");
|
||||
System.out.println(banner);
|
||||
assertThat(banner).contains("@#8&o:*. ");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void renderBannerWithDefaultAspectRatio() {
|
||||
String banner = printBanner(IMAGE_BANNER_BLACK_AND_WHITE);
|
||||
int bannerHeight = getBannerHeight(banner);
|
||||
|
||||
assertThat(bannerHeight, equalTo(2));
|
||||
public void printBannerShouldCalculateHeight() throws Exception {
|
||||
String banner = printBanner("large.gif", "banner.image.width=20");
|
||||
assertThat(getBannerHeight(banner)).isEqualTo(10);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void renderBannerWithCustomAspectRatio() {
|
||||
setAspectRatio(1.0d);
|
||||
String banner = printBanner(IMAGE_BANNER_BLACK_AND_WHITE);
|
||||
int bannerHeight = getBannerHeight(banner);
|
||||
|
||||
assertThat(bannerHeight, equalTo(4));
|
||||
public void printBannerWhenHasHeightPropertyShouldSetHeight() throws Exception {
|
||||
String banner = printBanner("large.gif", "banner.image.width=20",
|
||||
"banner.image.height=30");
|
||||
assertThat(getBannerHeight(banner)).isEqualTo(30);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void renderLargeBanner() {
|
||||
String banner = printBanner(IMAGE_BANNER_LARGE);
|
||||
int bannerWidth = getBannerWidth(banner);
|
||||
|
||||
assertThat(bannerWidth, equalTo(72));
|
||||
public void printBannerShouldCapWidthAndCalculateHeight() throws Exception {
|
||||
AnsiOutput.setEnabled(AnsiOutput.Enabled.NEVER);
|
||||
String banner = printBanner("large.gif", "banner.image.margin=0");
|
||||
assertThat(getBannerWidth(banner)).isEqualTo(76);
|
||||
assertThat(getBannerHeight(banner)).isEqualTo(37);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void renderLargeBannerWithACustomWidth() {
|
||||
setMaxWidth(60);
|
||||
String banner = printBanner(IMAGE_BANNER_LARGE);
|
||||
int bannerWidth = getBannerWidth(banner);
|
||||
public void printBannerShouldPrintMargin() throws Exception {
|
||||
AnsiOutput.setEnabled(AnsiOutput.Enabled.NEVER);
|
||||
String banner = printBanner("large.gif");
|
||||
String[] lines = banner.split(NEW_LINE);
|
||||
for (int i = 2; i < lines.length - 1; i++) {
|
||||
assertThat(lines[i]).startsWith(" @");
|
||||
}
|
||||
}
|
||||
|
||||
assertThat(bannerWidth, equalTo(60));
|
||||
@Test
|
||||
public void printBannerWhenHasMarginPropertShouldPrintSizedMargin() throws Exception {
|
||||
AnsiOutput.setEnabled(AnsiOutput.Enabled.NEVER);
|
||||
String banner = printBanner("large.gif", "banner.image.margin=4");
|
||||
String[] lines = banner.split(NEW_LINE);
|
||||
for (int i = 2; i < lines.length - 1; i++) {
|
||||
assertThat(lines[i]).startsWith(" @");
|
||||
}
|
||||
}
|
||||
|
||||
private int getBannerHeight(String banner) {
|
||||
return banner.split("\n").length;
|
||||
return banner.split(NEW_LINE).length - 3;
|
||||
}
|
||||
|
||||
private int getBannerWidth(String banner) {
|
||||
String strippedBanner = banner.replaceAll("\u001B\\[.*?m", "");
|
||||
String firstLine = strippedBanner.split("\n")[0];
|
||||
return firstLine.length();
|
||||
int width = 0;
|
||||
for (String line : banner.split(NEW_LINE)) {
|
||||
width = Math.max(width, line.length());
|
||||
}
|
||||
return width;
|
||||
}
|
||||
|
||||
private static String getAnsiOutput(AnsiElement ansi) {
|
||||
return "\u001B[" + ansi.toString() + "m";
|
||||
}
|
||||
|
||||
private void setDark(boolean dark) {
|
||||
properties.put("banner.image.dark", dark);
|
||||
}
|
||||
|
||||
private void setMaxWidth(int maxWidth) {
|
||||
properties.put("banner.image.max-width", maxWidth);
|
||||
}
|
||||
|
||||
private void setAspectRatio(double aspectRatio) {
|
||||
properties.put("banner.image.aspect-ratio", aspectRatio);
|
||||
}
|
||||
|
||||
private String printBanner(String imagePath) {
|
||||
Resource image = new ClassPathResource(imagePath);
|
||||
ImageBanner banner = new ImageBanner(image);
|
||||
private String printBanner(String path, String... properties) {
|
||||
ImageBanner banner = new ImageBanner(new ClassPathResource(path, getClass()));
|
||||
ConfigurableEnvironment environment = new MockEnvironment();
|
||||
environment.getPropertySources().addLast(
|
||||
new MapPropertySource("testConfig", properties));
|
||||
TestPropertySourceUtils.addInlinedPropertiesToEnvironment(environment,
|
||||
properties);
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
banner.printBanner(environment, getClass(), new PrintStream(out));
|
||||
return out.toString();
|
||||
|
|
|
@ -67,6 +67,7 @@ import org.springframework.core.env.Environment;
|
|||
import org.springframework.core.env.MapPropertySource;
|
||||
import org.springframework.core.env.PropertySource;
|
||||
import org.springframework.core.env.StandardEnvironment;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.DefaultResourceLoader;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
|
@ -196,29 +197,26 @@ public class SpringApplicationTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void textBannerTakesPrecedence() throws Exception {
|
||||
public void imageBannerAndTextBanner() throws Exception {
|
||||
SpringApplication application = new SpringApplication(ExampleConfig.class);
|
||||
BannerResourceLoaderStub resourceLoader = new BannerResourceLoaderStub();
|
||||
resourceLoader.addResource("banner.gif", "banners/black-and-white.gif");
|
||||
resourceLoader.addResource("banner.txt", "banners/foobar.txt");
|
||||
|
||||
MockResourceLoader resourceLoader = new MockResourceLoader();
|
||||
resourceLoader.addResource("banner.gif", "black-and-white.gif");
|
||||
resourceLoader.addResource("banner.txt", "foobar.txt");
|
||||
application.setWebEnvironment(false);
|
||||
application.setResourceLoader(resourceLoader);
|
||||
application.run();
|
||||
|
||||
assertThat(this.output.toString()).startsWith("Foo Bar");
|
||||
assertThat(this.output.toString()).contains("@@@@").contains("Foo Bar");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void imageBannerLoads() throws Exception {
|
||||
SpringApplication application = new SpringApplication(ExampleConfig.class);
|
||||
BannerResourceLoaderStub resourceLoader = new BannerResourceLoaderStub();
|
||||
resourceLoader.addResource("banner.gif", "banners/black-and-white.gif");
|
||||
MockResourceLoader resourceLoader = new MockResourceLoader();
|
||||
resourceLoader.addResource("banner.gif", "black-and-white.gif");
|
||||
application.setWebEnvironment(false);
|
||||
application.setResourceLoader(resourceLoader);
|
||||
application.run();
|
||||
|
||||
assertThat(this.output.toString()).startsWith("@");
|
||||
assertThat(this.output.toString()).contains("@@@@@@");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -1120,28 +1118,23 @@ public class SpringApplicationTests {
|
|||
|
||||
}
|
||||
|
||||
private static class BannerResourceLoaderStub extends DefaultResourceLoader {
|
||||
private static class MockResourceLoader implements ResourceLoader {
|
||||
|
||||
private Map<String, String> resources = new HashMap<String, String>();
|
||||
Resource notFoundResource;
|
||||
private final Map<String, Resource> resources = new HashMap<String, Resource>();
|
||||
|
||||
BannerResourceLoaderStub() {
|
||||
this.notFoundResource = super.getResource("classpath:foo/bar/foobar");
|
||||
assert !this.notFoundResource.exists();
|
||||
}
|
||||
|
||||
public void addResource(String file, String realPath) {
|
||||
this.resources.put(file, realPath);
|
||||
public void addResource(String source, String path) {
|
||||
this.resources.put(source, new ClassPathResource(path, getClass()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Resource getResource(String s) {
|
||||
if (this.resources.containsKey(s)) {
|
||||
return super.getResource("classpath:" + this.resources.get(s));
|
||||
}
|
||||
else {
|
||||
return this.notFoundResource;
|
||||
}
|
||||
public Resource getResource(String path) {
|
||||
Resource resource = this.resources.get(path);
|
||||
return (resource == null ? new ClassPathResource("doesnotexit") : resource);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClassLoader getClassLoader() {
|
||||
return getClass().getClassLoader();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Before Width: | Height: | Size: 44 B After Width: | Height: | Size: 44 B |
Before Width: | Height: | Size: 95 B After Width: | Height: | Size: 95 B |
Before Width: | Height: | Size: 817 B After Width: | Height: | Size: 817 B |
Before Width: | Height: | Size: 142 B After Width: | Height: | Size: 142 B |