1507 lines · 50.4 KB · Java
RawDownload
/* * Copyright (c) 2000, 2010, 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.  Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * 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. */ package java.awt.font; import java.io.IOException;import java.io.ObjectOutputStream;import java.util.Arrays;import java.util.Comparator;import java.util.EnumSet;import java.util.Set; /** * The <code>NumericShaper</code> class is used to convert Latin-1 (European) * digits to other Unicode decimal digits.  Users of this class will * primarily be people who wish to present data using * national digit shapes, but find it more convenient to represent the * data internally using Latin-1 (European) digits.  This does not * interpret the deprecated numeric shape selector character (U+206E). * <p> * Instances of <code>NumericShaper</code> are typically applied * as attributes to text with the * {@link TextAttribute#NUMERIC_SHAPING NUMERIC_SHAPING} attribute * of the <code>TextAttribute</code> class. * For example, this code snippet causes a <code>TextLayout</code> to * shape European digits to Arabic in an Arabic context:<br> * <blockquote><pre> * Map map = new HashMap(); * map.put(TextAttribute.NUMERIC_SHAPING, *     NumericShaper.getContextualShaper(NumericShaper.ARABIC)); * FontRenderContext frc = ...; * TextLayout layout = new TextLayout(text, map, frc); * layout.draw(g2d, x, y); * </pre></blockquote> * <br> * It is also possible to perform numeric shaping explicitly using instances * of <code>NumericShaper</code>, as this code snippet demonstrates:<br> * <blockquote><pre> * char[] text = ...; * // shape all EUROPEAN digits (except zero) to ARABIC digits * NumericShaper shaper = NumericShaper.getShaper(NumericShaper.ARABIC); * shaper.shape(text, start, count); * * // shape European digits to ARABIC digits if preceding text is Arabic, or * // shape European digits to TAMIL digits if preceding text is Tamil, or * // leave European digits alone if there is no preceding text, or * // preceding text is neither Arabic nor Tamil * NumericShaper shaper = *     NumericShaper.getContextualShaper(NumericShaper.ARABIC | *                                         NumericShaper.TAMIL, *                                       NumericShaper.EUROPEAN); * shaper.shape(text, start, count); * </pre></blockquote> * * <p><b>Bit mask- and enum-based Unicode ranges</b></p> * * <p>This class supports two different programming interfaces to * represent Unicode ranges for script-specific digits: bit * mask-based ones, such as {@link #ARABIC NumericShaper.ARABIC}, and * enum-based ones, such as {@link NumericShaper.Range#ARABIC}. * Multiple ranges can be specified by ORing bit mask-based constants, * such as: * <blockquote><pre> * NumericShaper.ARABIC | NumericShaper.TAMIL * </pre></blockquote> * or creating a {@code Set} with the {@link NumericShaper.Range} * constants, such as: * <blockquote><pre> * EnumSet.of(NumericShaper.Scirpt.ARABIC, NumericShaper.Range.TAMIL) * </pre></blockquote> * The enum-based ranges are a super set of the bit mask-based ones. * * <p>If the two interfaces are mixed (including serialization), * Unicode range values are mapped to their counterparts where such * mapping is possible, such as {@code NumericShaper.Range.ARABIC} * from/to {@code NumericShaper.ARABIC}.  If any unmappable range * values are specified, such as {@code NumericShaper.Range.BALINESE}, * those ranges are ignored. * * <p><b>Decimal Digits Precedence</b></p> * * <p>A Unicode range may have more than one set of decimal digits. If * multiple decimal digits sets are specified for the same Unicode * range, one of the sets will take precedence as follows. * * <table border=1 cellspacing=3 cellpadding=0 summary="NumericShaper constants precedence."> *    <tr> *       <th class="TableHeadingColor">Unicode Range</th> *       <th class="TableHeadingColor"><code>NumericShaper</code> Constants</th> *       <th class="TableHeadingColor">Precedence</th> *    </tr> *    <tr> *       <td rowspan="2">Arabic</td> *       <td>{@link NumericShaper#ARABIC NumericShaper.ARABIC}<br> *           {@link NumericShaper#EASTERN_ARABIC NumericShaper.EASTERN_ARABIC}</td> *       <td>{@link NumericShaper#EASTERN_ARABIC NumericShaper.EASTERN_ARABIC}</td> *    </tr> *    <tr> *       <td>{@link NumericShaper.Range#ARABIC}<br> *           {@link NumericShaper.Range#EASTERN_ARABIC}</td> *       <td>{@link NumericShaper.Range#EASTERN_ARABIC}</td> *    </tr> *    <tr> *       <td>Tai Tham</td> *       <td>{@link NumericShaper.Range#TAI_THAM_HORA}<br> *           {@link NumericShaper.Range#TAI_THAM_THAM}</td> *       <td>{@link NumericShaper.Range#TAI_THAM_THAM}</td> *    </tr> * </table> * * @since 1.4 */ public final class NumericShaper implements java.io.Serializable {    /**     * A {@code NumericShaper.Range} represents a Unicode range of a     * script having its own decimal digits. For example, the {@link     * NumericShaper.Range#THAI} range has the Thai digits, THAI DIGIT     * ZERO (U+0E50) to THAI DIGIT NINE (U+0E59).     *     * <p>The <code>Range</code> enum replaces the traditional bit     * mask-based values (e.g., {@link NumericShaper#ARABIC}), and     * supports more Unicode ranges than the bit mask-based ones. For     * example, the following code using the bit mask:     * <blockquote><pre>     * NumericShaper.getContextualShaper(NumericShaper.ARABIC |     *                                     NumericShaper.TAMIL,     *                                   NumericShaper.EUROPEAN);     * </pre></blockquote>     * can be written using this enum as:     * <blockquote><pre>     * NumericShaper.getContextualShaper(EnumSet.of(     *                                     NumericShaper.Range.ARABIC,     *                                     NumericShaper.Range.TAMIL),     *                                   NumericShaper.Range.EUROPEAN);     * </pre></blockquote>     *     * @since 1.7     */    public static enum Range {        // The order of EUROPEAN to MOGOLIAN must be consistent        // with the bitmask-based constants.        /**         * The Latin (European) range with the Latin (ASCII) digits.         */        EUROPEAN        ('\u0030', '\u0000', '\u0300'),        /**         * The Arabic range with the Arabic-Indic digits.         */        ARABIC          ('\u0660', '\u0600', '\u0780'),        /**         * The Arabic range with the Eastern Arabic-Indic digits.         */        EASTERN_ARABIC  ('\u06f0', '\u0600', '\u0780'),        /**         * The Devanagari range with the Devanagari digits.         */        DEVANAGARI      ('\u0966', '\u0900', '\u0980'),        /**         * The Bengali range with the Bengali digits.         */        BENGALI         ('\u09e6', '\u0980', '\u0a00'),        /**         * The Gurmukhi range with the Gurmukhi digits.         */        GURMUKHI        ('\u0a66', '\u0a00', '\u0a80'),        /**         * The Gujarati range with the Gujarati digits.         */        GUJARATI        ('\u0ae6', '\u0b00', '\u0b80'),        /**         * The Oriya range with the Oriya digits.         */        ORIYA           ('\u0b66', '\u0b00', '\u0b80'),        /**         * The Tamil range with the Tamil digits.         */        TAMIL           ('\u0be6', '\u0b80', '\u0c00'),        /**         * The Telugu range with the Telugu digits.         */        TELUGU          ('\u0c66', '\u0c00', '\u0c80'),        /**         * The Kannada range with the Kannada digits.         */        KANNADA         ('\u0ce6', '\u0c80', '\u0d00'),        /**         * The Malayalam range with the Malayalam digits.         */        MALAYALAM       ('\u0d66', '\u0d00', '\u0d80'),        /**         * The Thai range with the Thai digits.         */        THAI            ('\u0e50', '\u0e00', '\u0e80'),        /**         * The Lao range with the Lao digits.         */        LAO             ('\u0ed0', '\u0e80', '\u0f00'),        /**         * The Tibetan range with the Tibetan digits.         */        TIBETAN         ('\u0f20', '\u0f00', '\u1000'),        /**         * The Myanmar range with the Myanmar digits.         */        MYANMAR         ('\u1040', '\u1000', '\u1080'),        /**         * The Ethiopic range with the Ethiopic digits. Ethiopic         * does not have a decimal digit 0 so Latin (European) 0 is         * used.         */        ETHIOPIC        ('\u1369', '\u1200', '\u1380') {            @Override            char getNumericBase() { return 1; }        },        /**         * The Khmer range with the Khmer digits.         */        KHMER           ('\u17e0', '\u1780', '\u1800'),        /**         * The Mongolian range with the Mongolian digits.         */        MONGOLIAN       ('\u1810', '\u1800', '\u1900'),        // The order of EUROPEAN to MOGOLIAN must be consistent        // with the bitmask-based constants.         /**         * The N'Ko range with the N'Ko digits.         */        NKO             ('\u07c0', '\u07c0', '\u0800'),        /**         * The Myanmar range with the Myanmar Shan digits.         */        MYANMAR_SHAN    ('\u1090', '\u1000', '\u10a0'),        /**         * The Limbu range with the Limbu digits.         */        LIMBU           ('\u1946', '\u1900', '\u1950'),        /**         * The New Tai Lue range with the New Tai Lue digits.         */        NEW_TAI_LUE     ('\u19d0', '\u1980', '\u19e0'),        /**         * The Balinese range with the Balinese digits.         */        BALINESE        ('\u1b50', '\u1b00', '\u1b80'),        /**         * The Sundanese range with the Sundanese digits.         */        SUNDANESE       ('\u1bb0', '\u1b80', '\u1bc0'),        /**         * The Lepcha range with the Lepcha digits.         */        LEPCHA          ('\u1c40', '\u1c00', '\u1c50'),        /**         * The Ol Chiki range with the Ol Chiki digits.         */        OL_CHIKI        ('\u1c50', '\u1c50', '\u1c80'),        /**         * The Vai range with the Vai digits.         */        VAI             ('\ua620', '\ua500', '\ua640'),        /**         * The Saurashtra range with the Saurashtra digits.         */        SAURASHTRA      ('\ua8d0', '\ua880', '\ua8e0'),        /**         * The Kayah Li range with the Kayah Li digits.         */        KAYAH_LI        ('\ua900', '\ua900', '\ua930'),        /**         * The Cham range with the Cham digits.         */        CHAM            ('\uaa50', '\uaa00', '\uaa60'),        /**         * The Tai Tham Hora range with the Tai Tham Hora digits.         */        TAI_THAM_HORA   ('\u1a80', '\u1a20', '\u1ab0'),        /**         * The Tai Tham Tham range with the Tai Tham Tham digits.         */        TAI_THAM_THAM   ('\u1a90', '\u1a20', '\u1ab0'),        /**         * The Javanese range with the Javanese digits.         */        JAVANESE        ('\ua9d0', '\ua980', '\ua9e0'),        /**         * The Meetei Mayek range with the Meetei Mayek digits.         */        MEETEI_MAYEK    ('\uabf0', '\uabc0', '\uac00');         private static int toRangeIndex(Range script) {            int index = script.ordinal();            return index < NUM_KEYS ? index : -1;        }         private static Range indexToRange(int index) {            return index < NUM_KEYS ? Range.values()[index] : null;        }         private static int toRangeMask(Set<Range> ranges) {            int m = 0;            for (Range range : ranges) {                int index = range.ordinal();                if (index < NUM_KEYS) {                    m |= 1 << index;                }            }            return m;        }         private static Set<Range> maskToRangeSet(int mask) {            Set<Range> set = EnumSet.noneOf(Range.class);            Range[] a = Range.values();            for (int i = 0; i < NUM_KEYS; i++) {                if ((mask & (1 << i)) != 0) {                    set.add(a[i]);                }            }            return set;        }         // base character of range digits        private final int base;        // Unicode range        private final int start, // inclusive                          end;   // exclusive         private Range(int base, int start, int end) {            this.base = base - ('0' + getNumericBase());            this.start = start;            this.end = end;        }         private int getDigitBase() {            return base;        }         char getNumericBase() {            return 0;        }         private boolean inRange(int c) {            return start <= c && c < end;        }    }     /** index of context for contextual shaping - values range from 0 to 18 */    private int key;     /** flag indicating whether to shape contextually (high bit) and which     *  digit ranges to shape (bits 0-18)     */    private int mask;     /**     * The context {@code Range} for contextual shaping or the {@code     * Range} for non-contextual shaping. {@code null} for the bit     * mask-based API.     *     * @since 1.7     */    private Range shapingRange;     /**     * {@code Set<Range>} indicating which Unicode ranges to     * shape. {@code null} for the bit mask-based API.     */    private transient Set<Range> rangeSet;     /**     * rangeSet.toArray() value. Sorted by Range.base when the number     * of elements is greater then BSEARCH_THRESHOLD.     */    private transient Range[] rangeArray;     /**     * If more than BSEARCH_THRESHOLD ranges are specified, binary search is used.     */    private static final int BSEARCH_THRESHOLD = 3;     private static final long serialVersionUID = -8022764705923730308L;     /** Identifies the Latin-1 (European) and extended range, and     *  Latin-1 (European) decimal base.     */    public static final int EUROPEAN = 1<<0;     /** Identifies the ARABIC range and decimal base. */    public static final int ARABIC = 1<<1;     /** Identifies the ARABIC range and ARABIC_EXTENDED decimal base. */    public static final int EASTERN_ARABIC = 1<<2;     /** Identifies the DEVANAGARI range and decimal base. */    public static final int DEVANAGARI = 1<<3;     /** Identifies the BENGALI range and decimal base. */    public static final int BENGALI = 1<<4;     /** Identifies the GURMUKHI range and decimal base. */    public static final int GURMUKHI = 1<<5;     /** Identifies the GUJARATI range and decimal base. */    public static final int GUJARATI = 1<<6;     /** Identifies the ORIYA range and decimal base. */    public static final int ORIYA = 1<<7;     /** Identifies the TAMIL range and decimal base. */    // TAMIL DIGIT ZERO was added in Unicode 4.1    public static final int TAMIL = 1<<8;     /** Identifies the TELUGU range and decimal base. */    public static final int TELUGU = 1<<9;     /** Identifies the KANNADA range and decimal base. */    public static final int KANNADA = 1<<10;     /** Identifies the MALAYALAM range and decimal base. */    public static final int MALAYALAM = 1<<11;     /** Identifies the THAI range and decimal base. */    public static final int THAI = 1<<12;     /** Identifies the LAO range and decimal base. */    public static final int LAO = 1<<13;     /** Identifies the TIBETAN range and decimal base. */    public static final int TIBETAN = 1<<14;     /** Identifies the MYANMAR range and decimal base. */    public static final int MYANMAR = 1<<15;     /** Identifies the ETHIOPIC range and decimal base. */    public static final int ETHIOPIC = 1<<16;     /** Identifies the KHMER range and decimal base. */    public static final int KHMER = 1<<17;     /** Identifies the MONGOLIAN range and decimal base. */    public static final int MONGOLIAN = 1<<18;     /** Identifies all ranges, for full contextual shaping.     *     * <p>This constant specifies all of the bit mask-based     * ranges. Use {@code EmunSet.allOf(NumericShaper.Range.class)} to     * specify all of the enum-based ranges.     */    public static final int ALL_RANGES = 0x0007ffff;     private static final int EUROPEAN_KEY = 0;    private static final int ARABIC_KEY = 1;    private static final int EASTERN_ARABIC_KEY = 2;    private static final int DEVANAGARI_KEY = 3;    private static final int BENGALI_KEY = 4;    private static final int GURMUKHI_KEY = 5;    private static final int GUJARATI_KEY = 6;    private static final int ORIYA_KEY = 7;    private static final int TAMIL_KEY = 8;    private static final int TELUGU_KEY = 9;    private static final int KANNADA_KEY = 10;    private static final int MALAYALAM_KEY = 11;    private static final int THAI_KEY = 12;    private static final int LAO_KEY = 13;    private static final int TIBETAN_KEY = 14;    private static final int MYANMAR_KEY = 15;    private static final int ETHIOPIC_KEY = 16;    private static final int KHMER_KEY = 17;    private static final int MONGOLIAN_KEY = 18;     private static final int NUM_KEYS = MONGOLIAN_KEY + 1; // fixed     private static final int CONTEXTUAL_MASK = 1<<31;     private static final char[] bases = {        '\u0030' - '\u0030', // EUROPEAN        '\u0660' - '\u0030', // ARABIC-INDIC

Lines 1–500 of 1507.