diff --git a/src/java.desktop/share/classes/javax/swing/text/html/CSS.java b/src/java.desktop/share/classes/javax/swing/text/html/CSS.java index bb60b38265465..107dc8827add4 100644 --- a/src/java.desktop/share/classes/javax/swing/text/html/CSS.java +++ b/src/java.desktop/share/classes/javax/swing/text/html/CSS.java @@ -38,6 +38,7 @@ import java.util.ArrayList; import java.util.Enumeration; import java.util.Hashtable; +import java.util.Map; import java.util.Objects; import javax.swing.ImageIcon; @@ -1354,41 +1355,52 @@ else if (str.length() < 2) return colorstr; } - /** - * Convert a "#FFFFFF" hex string to a Color. - * If the color specification is bad, an attempt - * will be made to fix it up. - */ - static final Color hexToColor(String value) { - String digits; - int n = value.length(); - if (value.startsWith("#")) { - digits = value.substring(1, Math.min(value.length(), 7)); - } else { - digits = value; - } - // Some webpage passes 3 digit color code as in #fff which is - // decoded as #000FFF resulting in blue background. - // As per https://www.w3.org/TR/CSS1/#color-units, - // The three-digit RGB notation (#rgb) is converted into six-digit form - // (#rrggbb) by replicating digits, not by adding zeros. - // This makes sure that white (#ffffff) can be specified with the short notation - // (#fff) and removes any dependencies on the color depth of the display. - if (digits.length() == 3) { - final String r = digits.substring(0, 1); - final String g = digits.substring(1, 2); - final String b = digits.substring(2, 3); - digits = String.format("%s%s%s%s%s%s", r, r, g, g, b, b); - } - String hstr = "0x" + digits; - Color c; - try { - c = Color.decode(hstr); - } catch (NumberFormatException nfe) { - c = null; + /** + * Convert a "#FFF", "#FFFF", "#FFFFFF" or "#FFFFFFFF" hex string to a Color. + * If the color specification is bad, an attempt will be made to fix it up. + */ + static final Color hexToColor(String digits) { + // CSS Color level 4 allows webpage passes 3, 4, 6 or 8 digit color codes. + // - 3 digits #[R][G][B] ........ represents #[RR][GG][BB]FF + // - 4 digits #[R][G][B][A] ..... represents #[RR][GG][BB][AA] + // - 6 digits #[RR][GG][BB] ..... represents #[RR][GG][BB]FF + // - 8 digits #[RR][GG][BB][AA] . represents #[RR][GG][BB][AA] + final byte[] iseq = digits.startsWith("#") ? + iseqmap.get(Integer.valueOf(digits.length())): + iseqmap.get(Integer.valueOf(-digits.length())); + if (iseq == null) { + // Rejects string argument with a wrong number length. + return null; } - return c; - } + // Only 3, 4, 6 and 8 digits notations. + // Parses the string argument and build color value. + int dv; + int value = 0; + for (byte i : iseq) { + dv = i == -15 ? 15 : Character.digit(digits.charAt(i), 16); + if (dv < 0) { + // Rejects string argument with at least a non digit Character. + return null; + } + value = dv | value << 4; + } + return new Color(value, true); + } + + // Map of Index Sequences. Index -15 means, use the default value 15. + private static final Map iseqmap = + Map.ofEntries( + // Positive key, for # prefixed string, is associated with index from 1 to 8. + // Negative key, for not # prefixed string, is associated with index from 0 to 7. + Map.entry(Integer.valueOf(4), new byte[]{-15, -15, 1, 1, 2, 2, 3, 3}), + Map.entry(Integer.valueOf(5), new byte[]{4, 4, 1, 1, 2, 2, 3, 3}), + Map.entry(Integer.valueOf(7), new byte[]{-15, -15, 1, 2, 3, 4, 5, 6}), + Map.entry(Integer.valueOf(9), new byte[]{7, 8, 1, 2, 3, 4, 6, 6}), + Map.entry(Integer.valueOf(-3), new byte[]{-15, -15, 0, 0, 1, 1, 2, 2}), + Map.entry(Integer.valueOf(-4), new byte[]{3, 3, 0, 0, 1, 1, 2, 2}), + Map.entry(Integer.valueOf(-6), new byte[]{-15, -15, 0, 1, 2, 3, 4, 5}), + Map.entry(Integer.valueOf(-8), new byte[]{6, 7, 0, 1, 2, 3, 4, 5}) + ); /** * Convert a color string such as "RED" or "#NNNNNN" or "rgb(r, g, b)" @@ -3770,4 +3782,4 @@ private StyleSheet getStyleSheet(StyleSheet ss) { private transient StyleSheet styleSheet = null; static int baseFontSizeIndex = 3; -} +} \ No newline at end of file diff --git a/test/jdk/javax/swing/text/html/CSS/Hex3468DigitsColor.java b/test/jdk/javax/swing/text/html/CSS/Hex3468DigitsColor.java new file mode 100644 index 0000000000000..bc7ab16d84cc6 --- /dev/null +++ b/test/jdk/javax/swing/text/html/CSS/Hex3468DigitsColor.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8293776 + * @summary Adds CSS 4 and 8 digits hex coded Color + * @run main Hex3468DigitsColor + */ + +import java.awt.Color; +import javax.swing.text.html.StyleSheet; + +public class Hex3468DigitsColor { + + public static void main(String[] args) { + StringBuilder result = new StringBuilder(); + boolean passed = true; + StyleSheet styleSheet = new StyleSheet(); + + // #rgba should be interpreted as #rrggbbaa according CSS Color Level 4. + // Then expecting 0xaaff1122 from Color. + Color color = styleSheet.stringToColor("#f12a"); + + if (0xaaff1122 != color.getRGB()) { + result.append(" Test for #f12a"); + passed = false; + } + + // In #rrggbbaa, last two digits should be interpreted as Alpha value according CSS Color Level 4. + // Then expecting 0xaaff1122 from Color. + color = styleSheet.stringToColor("#ff1122aa"); + + if (0xaaff1122 != color.getRGB()) { + result.append(" Test for #ff1122aa"); + passed = false; + } + + if (!passed) { + result.insert(0, " Failed :"); + throw new RuntimeException(result.toString()); + } + } +} \ No newline at end of file