/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2002-2008, Open Source Geospatial Foundation (OSGeo) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. */ package org.geotools.util; import java.awt.Color; import java.text.DecimalFormat; import java.text.NumberFormat; import java.util.Collections; import java.util.HashMap; import java.util.Locale; import java.util.Map; import java.util.Map.Entry; import org.geotools.factory.Hints; /** * ConverterFactory for handling color conversions. * <p> * Supported conversions: * <ul> * <li>"#FF0000" (String) -> Color.RED</li> * <li>0xCC0000FF (Integer) -> RED with 80% alpha</li> * <li>"gray" (String) --> Color.GRAY</li> * </ul> * </p> * <p> * This code was previously part of the SLD utility class, it is being made * available as part of the Converters framework to allow for broader use. * </p> * @author Jody Garnett (Refractions Research) * @since 2.5 * * @source $URL$ */ public class ColorConverterFactory implements ConverterFactory { /** * Uses {@link Color#decode(String)} to convert String to Color. */ public static Converter CONVERT_STRING = new Converter() { public <T> T convert(Object source, Class<T> target) throws Exception { String rgba = (String) source; try { return target.cast(Color.decode(rgba)); } catch (NumberFormatException badRGB) { // unavailable return null; } } @Override public String toString() { return "CONVERT_STRING"; } }; /** * Converts provided integer to color, taking care to allow rgb and rgba support. */ public static Converter CONVERT_NUMBER_TO_COLOR = new Converter() { public <T> T convert(Object source, Class<T> target) throws Exception { Number number = (Number) source; // is it an integral number, and small enough to be an integer? if (((int) number.doubleValue()) == number.doubleValue() && number.doubleValue() < Integer.MAX_VALUE) { int rgba = number.intValue(); int alpha = 0xff000000 & rgba; return target.cast(new Color(rgba, alpha != 0)); } else { return null; } } @Override public String toString() { return "CONVERT_NUMBER_TO_COLOR"; } }; protected static DecimalFormat FORMAT; static { DecimalFormat decimalFormat = (DecimalFormat) NumberFormat.getNumberInstance(Locale.ENGLISH); decimalFormat.applyPattern("#.###"); FORMAT = decimalFormat; } /** * Converts color to css representation. */ private static Converter CONVERT_COLOR_TO_CSS = new Converter() { public <T> T convert(Object source, Class<T> target) throws Exception { Color color = (Color) source; if( CSS_COLORS.containsValue(color)){ for( Entry<String, Color> entry : CSS_COLORS.entrySet()){ if( entry.getValue().equals(color)){ return target.cast(entry.getKey()); } } return null; // something is inconsistent here } if( color.getAlpha() == 255 ){ StringBuilder rgb = new StringBuilder(16); rgb.append("rgb("); rgb.append( color.getRed() ); rgb.append( ","); rgb.append( color.getGreen() ); rgb.append( ","); rgb.append( color.getBlue() ); rgb.append( ")"); return target.cast( rgb.toString() ); } else { StringBuilder rgba = new StringBuilder(20); rgba.append("rgba("); rgba.append( color.getRed() ); rgba.append( ","); rgba.append( color.getGreen() ); rgba.append( ","); rgba.append( color.getBlue() ); rgba.append( ","); float opacity = ((float) color.getAlpha())/256.0f; rgba.append( FORMAT.format(opacity)); rgba.append( ")"); return target.cast( rgba.toString() ); } // String alphaCode = Integer.toHexString(color.getAlpha()); // String redCode = Integer.toHexString(color.getRed()); // String greenCode = Integer.toHexString(color.getGreen()); // String blueCode = Integer.toHexString(color.getBlue()); // // StringBuilder hex = new StringBuilder(9); // // hex.append("#"); // if (redCode.length() == 1) { // hex.append("0"); // } // hex.append( redCode.toUpperCase() ); // // if (greenCode.length() == 1) { // hex.append("0"); // } // hex.append( greenCode.toUpperCase() ); // // if (blueCode.length() == 1) { // hex.append("0"); // } // hex.append( blueCode.toUpperCase() ); // // if( !"ff".equals(alphaCode)){ // if (alphaCode.length() == 1) { // hex.append("0"); // } // hex.append( alphaCode.toUpperCase() ); // } // String str = hex.toString(); // return target.cast(str); } @Override public String toString() { return "CONVERT_COLOR_TO_CSS"; } }; /** * A mapping from CSS Color Module 4 names to Color objects. * * @see <a href="https://www.w3.org/TR/css-color-4/#named-colors">https://www.w3.org/TR/css-color-4/#named-colors</a> for more information. */ public static final Map<String, Color> CSS_COLORS; static { HashMap<String, Color> colors = new HashMap<>(); colors.put("aliceblue", new Color(240, 248, 255)); colors.put("antiquewhite", new Color(250, 235, 215)); colors.put("aqua", new Color(0, 255, 255)); colors.put("aquamarine", new Color(127, 255, 212)); colors.put("azure", new Color(240, 255, 255)); colors.put("beige", new Color(245, 245, 220)); colors.put("bisque", new Color(255, 228, 196)); colors.put("black", new Color(0, 0, 0)); colors.put("blanchedalmond", new Color(255, 235, 205)); colors.put("blue", new Color(0, 0, 255)); colors.put("blueviolet", new Color(138, 43, 226)); colors.put("brown", new Color(165, 42, 42)); colors.put("burlywood", new Color(222, 184, 135)); colors.put("cadetblue", new Color(95, 158, 160)); colors.put("chartreuse", new Color(127, 255, 0)); colors.put("chocolate", new Color(210, 105, 30)); colors.put("coral", new Color(255, 127, 80)); colors.put("cornflowerblue", new Color(100, 149, 237)); colors.put("cornsilk", new Color(255, 248, 220)); colors.put("crimson", new Color(220, 20, 60)); colors.put("cyan", new Color(0, 255, 255)); colors.put("darkblue", new Color(0, 0, 139)); colors.put("darkcyan", new Color(0, 139, 139)); colors.put("darkgoldenrod", new Color(184, 134, 11)); colors.put("darkgray", new Color(169, 169, 169)); colors.put("darkgreen", new Color(0, 100, 0)); colors.put("darkgrey", new Color(169, 169, 169)); colors.put("darkkhaki", new Color(189, 183, 107)); colors.put("darkmagenta", new Color(139, 0, 139)); colors.put("darkolivegreen", new Color(85, 107, 47)); colors.put("darkorange", new Color(255, 140, 0)); colors.put("darkorchid", new Color(153, 50, 204)); colors.put("darkred", new Color(139, 0, 0)); colors.put("darksalmon", new Color(233, 150, 122)); colors.put("darkseagreen", new Color(143, 188, 143)); colors.put("darkslateblue", new Color(72, 61, 139)); colors.put("darkslategray", new Color(47, 79, 79)); colors.put("darkslategrey", new Color(47, 79, 79)); colors.put("darkturquoise", new Color(0, 206, 209)); colors.put("darkviolet", new Color(148, 0, 211)); colors.put("deeppink", new Color(255, 20, 147)); colors.put("deepskyblue", new Color(0, 191, 255)); colors.put("dimgray", new Color(105, 105, 105)); colors.put("dimgrey", new Color(105, 105, 105)); colors.put("dodgerblue", new Color(30, 144, 255)); colors.put("firebrick", new Color(178, 34, 34)); colors.put("floralwhite", new Color(255, 250, 240)); colors.put("forestgreen", new Color(34, 139, 34)); colors.put("fuchsia", new Color(255, 0, 255)); colors.put("gainsboro", new Color(220, 220, 220)); colors.put("ghostwhite", new Color(248, 248, 255)); colors.put("gold", new Color(255, 215, 0)); colors.put("goldenrod", new Color(218, 165, 32)); colors.put("gray", new Color(128, 128, 128)); colors.put("green", new Color(0, 128, 0)); colors.put("greenyellow", new Color(173, 255, 47)); colors.put("grey", new Color(128, 128, 128)); colors.put("honeydew", new Color(240, 255, 240)); colors.put("hotpink", new Color(255, 105, 180)); colors.put("indianred", new Color(205, 92, 92)); colors.put("indigo", new Color(75, 0, 130)); colors.put("ivory", new Color(255, 255, 240)); colors.put("khaki", new Color(240, 230, 140)); colors.put("lavender", new Color(230, 230, 250)); colors.put("lavenderblush", new Color(255, 240, 245)); colors.put("lawngreen", new Color(124, 252, 0)); colors.put("lemonchiffon", new Color(255, 250, 205)); colors.put("lightblue", new Color(173, 216, 230)); colors.put("lightcoral", new Color(240, 128, 128)); colors.put("lightcyan", new Color(224, 255, 255)); colors.put("lightgoldenrodyellow", new Color(250, 250, 210)); colors.put("lightgray", new Color(211, 211, 211)); colors.put("lightgreen", new Color(144, 238, 144)); colors.put("lightgrey", new Color(211, 211, 211)); colors.put("lightpink", new Color(255, 182, 193)); colors.put("lightsalmon", new Color(255, 160, 122)); colors.put("lightseagreen", new Color(32, 178, 170)); colors.put("lightskyblue", new Color(135, 206, 250)); colors.put("lightslategray", new Color(119, 136, 153)); colors.put("lightslategrey", new Color(119, 136, 153)); colors.put("lightsteelblue", new Color(176, 196, 222)); colors.put("lightyellow", new Color(255, 255, 224)); colors.put("lime", new Color(0, 255, 0)); colors.put("limegreen", new Color(50, 205, 50)); colors.put("linen", new Color(250, 240, 230)); colors.put("magenta", new Color(255, 0, 255)); colors.put("maroon", new Color(128, 0, 0)); colors.put("mediumaquamarine", new Color(102, 205, 170)); colors.put("mediumblue", new Color(0, 0, 205)); colors.put("mediumorchid", new Color(186, 85, 211)); colors.put("mediumpurple", new Color(147, 112, 219)); colors.put("mediumseagreen", new Color(60, 179, 113)); colors.put("mediumslateblue", new Color(123, 104, 238)); colors.put("mediumspringgreen", new Color(0, 250, 154)); colors.put("mediumturquoise", new Color(72, 209, 204)); colors.put("mediumvioletred", new Color(199, 21, 133)); colors.put("midnightblue", new Color(25, 25, 112)); colors.put("mintcream", new Color(245, 255, 250)); colors.put("mistyrose", new Color(255, 228, 225)); colors.put("moccasin", new Color(255, 228, 181)); colors.put("navajowhite", new Color(255, 222, 173)); colors.put("navy", new Color(0, 0, 128)); colors.put("oldlace", new Color(253, 245, 230)); colors.put("olive", new Color(128, 128, 0)); colors.put("olivedrab", new Color(107, 142, 35)); colors.put("orange", new Color(255, 165, 0)); colors.put("orangered", new Color(255, 69, 0)); colors.put("orchid", new Color(218, 112, 214)); colors.put("palegoldenrod", new Color(238, 232, 170)); colors.put("palegreen", new Color(152, 251, 152)); colors.put("paleturquoise", new Color(175, 238, 238)); colors.put("palevioletred", new Color(219, 112, 147)); colors.put("papayawhip", new Color(255, 239, 213)); colors.put("peachpuff", new Color(255, 218, 185)); colors.put("peru", new Color(205, 133, 63)); colors.put("pink", new Color(255, 192, 203)); colors.put("plum", new Color(221, 160, 221)); colors.put("powderblue", new Color(176, 224, 230)); colors.put("purple", new Color(128, 0, 128)); colors.put("rebeccapurple", new Color(102, 51, 153)); colors.put("red", new Color(255, 0, 0)); colors.put("rosybrown", new Color(188, 143, 143)); colors.put("royalblue", new Color(65, 105, 225)); colors.put("saddlebrown", new Color(139, 69, 19)); colors.put("salmon", new Color(250, 128, 114)); colors.put("sandybrown", new Color(244, 164, 96)); colors.put("seagreen", new Color(46, 139, 87)); colors.put("seashell", new Color(255, 245, 238)); colors.put("sienna", new Color(160, 82, 45)); colors.put("silver", new Color(192, 192, 192)); colors.put("skyblue", new Color(135, 206, 235)); colors.put("slateblue", new Color(106, 90, 205)); colors.put("slategray", new Color(112, 128, 144)); colors.put("slategrey", new Color(112, 128, 144)); colors.put("snow", new Color(255, 250, 250)); colors.put("springgreen", new Color(0, 255, 127)); colors.put("steelblue", new Color(70, 130, 180)); colors.put("tan", new Color(210, 180, 140)); colors.put("teal", new Color(0, 128, 128)); colors.put("thistle", new Color(216, 191, 216)); colors.put("tomato", new Color(255, 99, 71)); colors.put("turquoise", new Color(64, 224, 208)); colors.put("violet", new Color(238, 130, 238)); colors.put("wheat", new Color(245, 222, 179)); colors.put("white", new Color(255, 255, 255)); colors.put("whitesmoke", new Color(245, 245, 245)); colors.put("yellow", new Color(255, 255, 0)); colors.put("yellowgreen", new Color(154, 205, 50)); CSS_COLORS = Collections.unmodifiableMap(colors); } /** * Converts CSS Color Module 4 names to colors, with a fallback to the basic {@link #CONVERT_STRING} converter if the provided source String is * not found in the {@link #CSS_COLORS} map. * <p> * This converter is willing to work with: * <ul> * <li>{@link #CSS_COLORS} names such as "aliceblue".</li> * <li>rgb representation of the form <code>rgb(0,0,255)</code></li> * <li>rgba representation of the form <code>rgba(0,0,255,255)</code></li> * <li>Hex representation of the form <code>#RRGGBB</code> and <code>#RRGGBBAA</code></li> * </ul> */ public static Converter CONVERT_CSS_TO_COLOR = new Converter() { @Override public <T> T convert(Object source, Class<T> target) throws Exception { String text = (String) source; String key = text.toLowerCase().trim(); try { if (CSS_COLORS.containsKey(key)) { return target.cast(CSS_COLORS.get(key)); } else if (text.startsWith("rgb(")) { String colorString = text.substring(4, text.length() - 1); String rgb[] = colorString.split("\\s*,\\s*"); Color c = new Color(Integer.parseInt(rgb[0]), Integer.parseInt(rgb[1]), Integer.parseInt(rgb[2])); return target.cast(c); } else if (text.startsWith("rgba(")) { String colorString = text.substring(5, text.length() - 1); String rgba[] = colorString.split("\\s*,\\s*"); float opacity = Float.parseFloat(rgba[3]); Color c = new Color(Integer.parseInt(rgba[0]), Integer.parseInt(rgba[1]), Integer.parseInt(rgba[2]), (int) (Math.floor(opacity == 1.0f ? 255 : opacity* 256f))); return target.cast(c); } else if (text.startsWith("#") || text.startsWith("0x")) { Number number = Long.decode(text); long rgba = number.longValue(); long h = (rgba >> 24) & 0xFF; if (h != 0) { int r = (int) h; int g = (int) ((rgba >> 16) & 0xFF); int b = (int) ((rgba >> 8) & 0xFF); int a = (int) ((rgba >> 0) & 0xFF); Color color = new Color(r, g, b, a); return target.cast(color); } else { int r = (int) ((rgba >> 16) & 0xFF); int g = (int) ((rgba >> 8) & 0xFF); int b = (int) ((rgba >> 0) & 0xFF); Color color = new Color(r, g, b); return target.cast(color); } } return null; // unavailable } catch (NumberFormatException badRGB) { // unavailable return null; } } @Override public String toString() { return "CONVERT_CSS_TO_COLOR"; } }; /** * Converts color to hex representation. */ public static Converter CONVERT_COLOR_TO_STRING = new Converter() { public <T> T convert(Object source, Class<T> target) throws Exception { Color color = (Color) source; String redCode = Integer.toHexString(color.getRed()); String greenCode = Integer.toHexString(color.getGreen()); String blueCode = Integer.toHexString(color.getBlue()); StringBuilder hex = new StringBuilder(9); hex.append("#"); if (redCode.length() == 1) { hex.append("0"); } hex.append( redCode.toUpperCase() ); if (greenCode.length() == 1) { hex.append("0"); } hex.append( greenCode.toUpperCase() ); if (blueCode.length() == 1) { hex.append("0"); } hex.append( blueCode.toUpperCase() ); String str = hex.toString(); return target.cast(str); } @Override public String toString() { return "CONVERT_COLOR_TO_STRING"; } }; public Converter createConverter(Class source, Class target, Hints hints) { if (target.equals(Color.class)) { // string to color if (source.equals(String.class)) { if (hints != null && hints.containsKey(Hints.COLOR_DEFINITION)) { String hint = (String) hints.get(Hints.COLOR_DEFINITION); if ("CSS".equalsIgnoreCase(hint)) { return CONVERT_CSS_TO_COLOR; } } return CONVERT_STRING; } // can we convert the thing to a Integer with a safe conversion? if (Number.class.isAssignableFrom(source)) { return CONVERT_NUMBER_TO_COLOR; } } else if (target.equals(String.class) && Color.class.isAssignableFrom(source)) { if (hints != null && hints.containsKey(Hints.COLOR_DEFINITION)) { String hint = (String) hints.get(Hints.COLOR_DEFINITION); if ("CSS".equalsIgnoreCase(hint)) { return CONVERT_COLOR_TO_CSS; } } return CONVERT_COLOR_TO_STRING; } return null; } }