diff --git a/fop-core/src/main/java/org/apache/fop/area/inline/ResolvedPageNumber.java b/fop-core/src/main/java/org/apache/fop/area/inline/ResolvedPageNumber.java index cbef88b7e5b..88fa153cc43 100644 --- a/fop-core/src/main/java/org/apache/fop/area/inline/ResolvedPageNumber.java +++ b/fop-core/src/main/java/org/apache/fop/area/inline/ResolvedPageNumber.java @@ -22,6 +22,8 @@ import java.io.IOException; import java.io.ObjectInputStream; +import org.apache.fop.fonts.Font; + /** * Always (pre-) resolved page number area. Needed by BIDI code to distinguish * from UnresolvedPageNumber. @@ -30,8 +32,27 @@ public class ResolvedPageNumber extends TextArea { private static final long serialVersionUID = -1758369835371647979L; + //Transient fields + transient Font font; + + private boolean isVertical; + + public ResolvedPageNumber(Font f, boolean v) { + font = f; + isVertical = v; + } + private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException { ois.defaultReadObject(); } + @Override + public void addWord(String word, int offset, int level) { + if(isVertical) { + for(int i=0; i process * @param gpa associated glyph position adjustments (also reordered) * @param script a script identifier * @param language a language identifier + * @param isVertical * @return the reordered (output) glyph sequence */ - public GlyphSequence reorderCombiningMarks(GlyphSequence gs, int[] widths, int[][] gpa, String script, String language) { - ScriptProcessor sp = ScriptProcessor.getInstance(script, processors); + public GlyphSequence reorderCombiningMarks(GlyphSequence gs, int[] widths, int[][] gpa, String script, String language, boolean isVertical) { + ScriptProcessor sp = ScriptProcessor.getInstance(script, processors, isVertical); return sp.reorderCombiningMarks(this, gs, widths, gpa, script, language); } diff --git a/fop-core/src/main/java/org/apache/fop/complexscripts/fonts/GlyphPositioningTable.java b/fop-core/src/main/java/org/apache/fop/complexscripts/fonts/GlyphPositioningTable.java index 3657330aa95..7e359553be6 100644 --- a/fop-core/src/main/java/org/apache/fop/complexscripts/fonts/GlyphPositioningTable.java +++ b/fop-core/src/main/java/org/apache/fop/complexscripts/fonts/GlyphPositioningTable.java @@ -230,12 +230,13 @@ public static GlyphSubtable createSubtable(int type, String id, int sequence, in * @param widths array of default advancements for each glyph * @param adjustments accumulated adjustments array (sequence) of 4-tuples of placement [PX,PY] and advance [AX,AY] adjustments, in that order, * with one 4-tuple for each element of glyph sequence + * @param isVertical * @return true if some adjustment is not zero; otherwise, false */ - public boolean position(GlyphSequence gs, String script, String language, int fontSize, int[] widths, int[][] adjustments) { + public boolean position(GlyphSequence gs, String script, String language, int fontSize, int[] widths, int[][] adjustments, boolean isVertical) { Map> lookups = matchLookups(script, language, "*"); if ((lookups != null) && (lookups.size() > 0)) { - ScriptProcessor sp = ScriptProcessor.getInstance(script, processors); + ScriptProcessor sp = ScriptProcessor.getInstance(script, processors, isVertical); return sp.position(this, gs, script, language, fontSize, lookups, widths, adjustments); } else { return false; diff --git a/fop-core/src/main/java/org/apache/fop/complexscripts/fonts/GlyphSubstitutionTable.java b/fop-core/src/main/java/org/apache/fop/complexscripts/fonts/GlyphSubstitutionTable.java index 43a3bcb4ad5..24e3e62bc38 100644 --- a/fop-core/src/main/java/org/apache/fop/complexscripts/fonts/GlyphSubstitutionTable.java +++ b/fop-core/src/main/java/org/apache/fop/complexscripts/fonts/GlyphSubstitutionTable.java @@ -92,13 +92,14 @@ public GlyphSubstitutionTable(GlyphDefinitionTable gdef, Map lookups, List subta * @param gs an input glyph sequence * @param script a script identifier * @param language a language identifier + * @param isVertical * @return the substituted (output) glyph sequence */ - public GlyphSequence substitute(GlyphSequence gs, String script, String language) { + public GlyphSequence substitute(GlyphSequence gs, String script, String language, boolean isVertical) { GlyphSequence ogs; Map> lookups = matchLookups(script, language, "*"); if ((lookups != null) && (lookups.size() > 0)) { - ScriptProcessor sp = ScriptProcessor.getInstance(script, processors); + ScriptProcessor sp = ScriptProcessor.getInstance(script, processors, isVertical); ogs = sp.substitute(this, gs, script, language, lookups); } else { ogs = gs; @@ -106,8 +107,8 @@ public GlyphSequence substitute(GlyphSequence gs, String script, String language return ogs; } - public CharSequence preProcess(CharSequence charSequence, String script, MultiByteFont font, List associations) { - ScriptProcessor scriptProcessor = ScriptProcessor.getInstance(script, processors); + public CharSequence preProcess(CharSequence charSequence, String script, MultiByteFont font, List associations, boolean isVertical) { + ScriptProcessor scriptProcessor = ScriptProcessor.getInstance(script, processors, isVertical); return scriptProcessor.preProcess(charSequence, font, associations); } diff --git a/fop-core/src/main/java/org/apache/fop/complexscripts/fonts/Positionable.java b/fop-core/src/main/java/org/apache/fop/complexscripts/fonts/Positionable.java index 890036650ef..2df40420e3d 100644 --- a/fop-core/src/main/java/org/apache/fop/complexscripts/fonts/Positionable.java +++ b/fop-core/src/main/java/org/apache/fop/complexscripts/fonts/Positionable.java @@ -41,19 +41,21 @@ public interface Positionable { * @param script a script identifier * @param language a language identifier * @param fontSize font size + * @param isVertical * @return array (sequence) of 4-tuples of placement [PX,PY] and advance [AX,AY] adjustments, in that order, * with one 4-tuple for each element of glyph sequence, or null if no non-zero adjustment applies */ - int[][] performPositioning(CharSequence cs, String script, String language, int fontSize); + int[][] performPositioning(CharSequence cs, String script, String language, int fontSize, boolean isVertical); /** * Perform glyph positioning using an implied font size. * @param cs character sequence to map to position offsets (advancement adjustments) * @param script a script identifier * @param language a language identifier + * @param isVertical * @return array (sequence) of 4-tuples of placement [PX,PY] and advance [AX,AY] adjustments, in that order, * with one 4-tuple for each element of glyph sequence, or null if no non-zero adjustment applies */ - int[][] performPositioning(CharSequence cs, String script, String language); + int[][] performPositioning(CharSequence cs, String script, String language, boolean isVertical); } diff --git a/fop-core/src/main/java/org/apache/fop/complexscripts/fonts/Substitutable.java b/fop-core/src/main/java/org/apache/fop/complexscripts/fonts/Substitutable.java index 42899505146..b1e389833e9 100644 --- a/fop-core/src/main/java/org/apache/fop/complexscripts/fonts/Substitutable.java +++ b/fop-core/src/main/java/org/apache/fop/complexscripts/fonts/Substitutable.java @@ -47,10 +47,11 @@ public interface Substitutable { * @param language a language identifier * @param associations optional list to receive list of character associations * @param retainControls if true, then retain control characters and their glyph mappings, otherwise remove + * @param isVertical * @return output sequence (represented as a character sequence, where each character in the returned sequence * denotes "font characters", i.e., character codes that map directly (1-1) to their associated glyphs */ - CharSequence performSubstitution(CharSequence cs, String script, String language, List associations, boolean retainControls); + CharSequence performSubstitution(CharSequence cs, String script, String language, List associations, boolean retainControls, boolean isVertical); /** * Reorder combining marks in character sequence so that they precede (within the sequence) the base @@ -62,8 +63,9 @@ public interface Substitutable { * @param script a script identifier * @param language a language identifier * @param associations optional list of associations to be reordered + * @param isVertical * @return output sequence containing reordered "font characters" */ - CharSequence reorderCombiningMarks(CharSequence cs, int[][] gpa, String script, String language, List associations); + CharSequence reorderCombiningMarks(CharSequence cs, int[][] gpa, String script, String language, List associations, boolean isVertical); } diff --git a/fop-core/src/main/java/org/apache/fop/complexscripts/scripts/ArabicScriptProcessor.java b/fop-core/src/main/java/org/apache/fop/complexscripts/scripts/ArabicScriptProcessor.java index dad7f2367e3..21d632c81eb 100644 --- a/fop-core/src/main/java/org/apache/fop/complexscripts/scripts/ArabicScriptProcessor.java +++ b/fop-core/src/main/java/org/apache/fop/complexscripts/scripts/ArabicScriptProcessor.java @@ -115,7 +115,7 @@ public GlyphContextTester getTester(String feature) { private final ScriptContextTester posContextTester; ArabicScriptProcessor(String script) { - super(script); + super(script, false); this.subContextTester = new SubstitutionScriptContextTester(); this.posContextTester = new PositioningScriptContextTester(); } diff --git a/fop-core/src/main/java/org/apache/fop/complexscripts/scripts/DefaultScriptProcessor.java b/fop-core/src/main/java/org/apache/fop/complexscripts/scripts/DefaultScriptProcessor.java index 0dcde599b2e..b26ac52032c 100644 --- a/fop-core/src/main/java/org/apache/fop/complexscripts/scripts/DefaultScriptProcessor.java +++ b/fop-core/src/main/java/org/apache/fop/complexscripts/scripts/DefaultScriptProcessor.java @@ -42,6 +42,15 @@ public class DefaultScriptProcessor extends ScriptProcessor { "locl" // localized forms }; + /** features to use for substitutions vertical */ + private static final String[] GSUB_FEATURES_V = + { + "ccmp", // glyph composition/decomposition + "liga", // common ligatures + "locl", // localized forms + "vert" // vertical writing + }; + /** features to use for positioning */ private static final String[] GPOS_FEATURES = { @@ -50,14 +59,22 @@ public class DefaultScriptProcessor extends ScriptProcessor { "mkmk" // mark to mark positioning }; - DefaultScriptProcessor(String script) { + /** do vert substitution.*/ + private final boolean vertical; + + DefaultScriptProcessor(String script, boolean vertical) { super(script); + this.vertical = vertical; } @Override /** {@inheritDoc} */ public String[] getSubstitutionFeatures() { - return GSUB_FEATURES; + if (vertical) { + return GSUB_FEATURES_V; + } else { + return GSUB_FEATURES; + } } @Override diff --git a/fop-core/src/main/java/org/apache/fop/complexscripts/scripts/HebrewScriptProcessor.java b/fop-core/src/main/java/org/apache/fop/complexscripts/scripts/HebrewScriptProcessor.java index a2915ca24e3..f244ee361ed 100644 --- a/fop-core/src/main/java/org/apache/fop/complexscripts/scripts/HebrewScriptProcessor.java +++ b/fop-core/src/main/java/org/apache/fop/complexscripts/scripts/HebrewScriptProcessor.java @@ -21,8 +21,8 @@ import org.apache.fop.complexscripts.fonts.GlyphDefinitionTable; public class HebrewScriptProcessor extends DefaultScriptProcessor { - HebrewScriptProcessor(String script) { - super(script); + HebrewScriptProcessor(String script, boolean vertical) { + super(script, vertical); } @Override diff --git a/fop-core/src/main/java/org/apache/fop/complexscripts/scripts/IndicScriptProcessor.java b/fop-core/src/main/java/org/apache/fop/complexscripts/scripts/IndicScriptProcessor.java index 76e1df39786..6ced2f452c7 100644 --- a/fop-core/src/main/java/org/apache/fop/complexscripts/scripts/IndicScriptProcessor.java +++ b/fop-core/src/main/java/org/apache/fop/complexscripts/scripts/IndicScriptProcessor.java @@ -142,7 +142,7 @@ public static ScriptProcessor makeProcessor(String script) { private final ScriptContextTester posContextTester; IndicScriptProcessor(String script) { - super(script); + super(script, false); this.subContextTester = new SubstitutionScriptContextTester(); this.posContextTester = new PositioningScriptContextTester(); } diff --git a/fop-core/src/main/java/org/apache/fop/complexscripts/scripts/ScriptProcessor.java b/fop-core/src/main/java/org/apache/fop/complexscripts/scripts/ScriptProcessor.java index 49732029132..0b0f4c36770 100644 --- a/fop-core/src/main/java/org/apache/fop/complexscripts/scripts/ScriptProcessor.java +++ b/fop-core/src/main/java/org/apache/fop/complexscripts/scripts/ScriptProcessor.java @@ -226,19 +226,21 @@ private GlyphTable.UseSpec[] assembledLookupsPut(AssembledLookupsKey key, Glyph /** * Obtain script processor instance associated with specified script. * @param script a script identifier + * @param isVertical * @return a script processor instance or null if none found */ - public static synchronized ScriptProcessor getInstance(String script, Map processors) { + public static synchronized ScriptProcessor getInstance(String script, Map processors, boolean isVertical) { ScriptProcessor sp = null; assert processors != null; - if ((sp = processors.get(script)) == null) { - processors.put(script, sp = createProcessor(script)); + String key = isVertical ? script + "_vert" : script; + if ((sp = processors.get(key)) == null) { + processors.put(key, sp = createProcessor(script, isVertical)); } return sp; } // [TBD] - rework to provide more configurable binding between script name and script processor constructor - private static ScriptProcessor createProcessor(String script) { + private static ScriptProcessor createProcessor(String script, boolean isVertical) { ScriptProcessor sp = null; int sc = CharScript.scriptCodeFromTag(script); if (sc == CharScript.SCRIPT_ARABIC) { @@ -246,11 +248,11 @@ private static ScriptProcessor createProcessor(String script) { } else if (CharScript.isIndicScript(sc)) { sp = IndicScriptProcessor.makeProcessor(script); } else if (sc == CharScript.SCRIPT_THAI) { - sp = new ThaiScriptProcessor(script); + sp = new ThaiScriptProcessor(script, isVertical); } else if (sc == CharScript.SCRIPT_HEBREW) { - sp = new HebrewScriptProcessor(script); + sp = new HebrewScriptProcessor(script, isVertical); } else { - sp = new DefaultScriptProcessor(script); + sp = new DefaultScriptProcessor(script, isVertical); } return sp; } diff --git a/fop-core/src/main/java/org/apache/fop/complexscripts/scripts/ThaiScriptProcessor.java b/fop-core/src/main/java/org/apache/fop/complexscripts/scripts/ThaiScriptProcessor.java index 4c293d41d74..93195015512 100644 --- a/fop-core/src/main/java/org/apache/fop/complexscripts/scripts/ThaiScriptProcessor.java +++ b/fop-core/src/main/java/org/apache/fop/complexscripts/scripts/ThaiScriptProcessor.java @@ -21,8 +21,8 @@ import org.apache.fop.complexscripts.fonts.GlyphDefinitionTable; public class ThaiScriptProcessor extends DefaultScriptProcessor { - ThaiScriptProcessor(String script) { - super(script); + ThaiScriptProcessor(String script, boolean vertical) { + super(script, vertical); } @Override diff --git a/fop-core/src/main/java/org/apache/fop/complexscripts/util/Characters.java b/fop-core/src/main/java/org/apache/fop/complexscripts/util/Characters.java new file mode 100644 index 00000000000..46141a6af02 --- /dev/null +++ b/fop-core/src/main/java/org/apache/fop/complexscripts/util/Characters.java @@ -0,0 +1,2241 @@ +/* + * Copyright 2014-15 Skynav, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY SKYNAV, INC. AND ITS CONTRIBUTORS “AS IS” AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL SKYNAV, INC. OR ITS CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.apache.fop.complexscripts.util; + +import java.util.Arrays; + +public class Characters { + + private Characters() {} + + public static final int UC_HT = '\u0009'; // horizontal tab + public static final int UC_LF = '\n'; // line feed + public static final int UC_VT = '\u000B'; // vertical tab + public static final int UC_FF = '\u000C'; // form feed + public static final int UC_CR = '\r'; // carriage return + public static final int UC_SPACE = '\u0020'; // space + public static final int UC_HYPHEN_MINUS = '\u002D'; // hyphen-minus + public static final int UC_NBSP = '\u00A0'; // non-breaking space + public static final int UC_SOFT_HYPHEN = '\u00AD'; // soft hyphen + public static final int UC_SPACE_EN_QUAD = '\u2000'; // en quad + public static final int UC_SPACE_EM_QUAD = '\u2001'; // em quad + public static final int UC_SPACE_EN = '\u2002'; // en space + public static final int UC_SPACE_EM = '\u2003'; // em space + public static final int UC_SPACE_3_PER_EM = '\u2004'; // 3-per-em space + public static final int UC_SPACE_4_PER_EM = '\u2005'; // 4-per-em space + public static final int UC_SPACE_6_PER_EM = '\u2006'; // 6-per-em space + public static final int UC_SPACE_FIGURE = '\u2007'; // figure space + public static final int UC_SPACE_PUNCTUATION = '\u2008'; // punctuation space + public static final int UC_SPACE_THIN = '\u2009'; // thin space + public static final int UC_SPACE_HAIR = '\u200A'; // hair space + public static final int UC_SPACE_ZWSP = '\u200B'; // zero-width space + public static final int UC_ZWNJ = '\u200C'; // zero-width non-joiner + public static final int UC_ZWJ = '\u200D'; // zero-width joiner + public static final int UC_LRM = '\u200E'; // left-to-right mark + public static final int UC_RLM = '\u200F'; // right-to-left mark + public static final int UC_LINE_SEPARATOR = '\u2028'; // line separator + public static final int UC_PARA_SEPARATOR = '\u2029'; // paragraph separator + public static final int UC_LRE = '\u202A'; // left-to-right embedding + public static final int UC_RLE = '\u202B'; // right-to-left embedding + public static final int UC_PDF = '\u202C'; // pop directional formatting + public static final int UC_LRO = '\u202D'; // left-to-right override + public static final int UC_RLO = '\u202E'; // right-to-left override + public static final int UC_NNBSP = '\u202F'; // narrow non-breaking space + public static final int UC_MMSP = '\u205F'; // medium mathematical space + public static final int UC_LRI = '\u2066'; // left-to-right isolate + public static final int UC_RLI = '\u2067'; // right-to-left isolate + public static final int UC_PDI = '\u2069'; // pop directional isolate + public static final int UC_IDSP = '\u3000'; // ideographic space + public static final int UC_CJK_SYMBOL_START = '\u3000'; // cjk symbols - start + public static final int UC_CJK_SYMBOL_END = '\u303F'; // cjk symbols - end (inclusive) + public static final int UC_HIRAGANA_START = '\u3040'; // hiragana - start + public static final int UC_HIRAGANA_END = '\u309F'; // hiragana - end (inclusive) + public static final int UC_KATAKANA_START = '\u30A0'; // katakana - start + public static final int UC_KATAKANA_END = '\u30FF'; // katakana - end (inclusive) + public static final int UC_CJK_START = '\u4E00'; // unified cjk ideographs - start + public static final int UC_CJK_END = '\u9FCC'; // unified cjk ideographs - end (inclusive) + public static final int UC_CJK_VERTICAL_START = '\uFE10'; // cjk vertical forms - start + public static final int UC_CJK_VERTICAL_END = '\uFE1F'; // cjk vertical forms - end (inclusive) + public static final int UC_CJK_COMPAT_START = '\uFE30'; // cjk compatibility forms - start + public static final int UC_CJK_COMPAT_END = '\uFE4F'; // cjk compatibility forms - end (inclusive) + public static final int UC_CJK_HALF_FULL_WIDTH_START = '\uFF00'; // cjk half and full width forms - start + public static final int UC_CJK_HALF_FULL_WIDTH_END = '\uFFEF'; // cjk half and full width forms - end (inclusive) + public static final int UC_OBJECT = '\uFFFC'; // object replacement character + public static final int UC_REPLACEMENT = '\uFFFD'; // replacement character + public static final int UC_NOT_A_CHARACTER = '\uFFFF'; // not a character + + public static String formatCharacter(int c) { + if (c < 65536) + return String.format("'%c' (U+%04X)", (char) c, c); + else if (c < 1114112) + return String.format("'%c' (U+%06X)", (char) c, c); + else + return String.format("*illegal character* (%08X)", c); + } + + public static boolean isLineSeparator(int c) { + return c == UC_LINE_SEPARATOR; + } + + public static boolean isBreakingWhitespace(int c) { + if (c == UC_HT) + return true; + else if (c == UC_LF) + return true; + else if (c == UC_VT) + return true; + else if (c == UC_FF) + return true; + else if (c == UC_CR) + return true; + else if (c == UC_SPACE) + return true; + else if ((c >= UC_SPACE_EN_QUAD) && (c <= UC_SPACE_ZWSP)) + return true; + else if (c == UC_LINE_SEPARATOR) + return true; + else if (c == UC_MMSP) + return true; + else + return false; + } + + public static boolean isNonBreakingWhitespace(int c) { + if (c == UC_NBSP) + return true; + else if (c == UC_NNBSP) + return true; + else + return false; + } + + public static boolean isWhitespace(int c) { + return isBreakingWhitespace(c) || isNonBreakingWhitespace(c); + } + + public static boolean isZeroWidthWhitespace(int c) { + if (c == UC_HT) + return true; + else if (c == UC_LF) + return true; + else if (c == UC_VT) + return true; + else if (c == UC_FF) + return true; + else if (c == UC_CR) + return true; + else if (c == UC_SPACE_ZWSP) + return true; + else if (c == UC_LINE_SEPARATOR) + return true; + else if (c == UC_PARA_SEPARATOR) + return true; + else + return false; + } + + public static boolean isBidiControl(int c) { + if (c == UC_LRE) + return true; + else if (c == UC_RLE) + return true; + else if (c == UC_LRO) + return true; + else if (c == UC_RLO) + return true; + else if (c == UC_PDF) + return true; + else if (c == UC_LRI) + return true; + else if (c == UC_RLI) + return true; + else if (c == UC_PDI) + return true; + else + return false; + } + + public static boolean isHyphenationPoint(int c) { + return (c == UC_HYPHEN_MINUS) || (c == UC_SOFT_HYPHEN); + } + + public static boolean isNonSpacing(int c) { + if ((c >= 0x0300) && (c <= 0x036F)) + return true; + else if ((c >= 0x0590) && (c <= 0x05CF)) + return (c != 0x05C0) && (c != 0x05C6); + else if (((c >= 0x0610) && (c <= 0x061A)) || (c == 0x061C)) + return true; + else if ((c >= 0x064B) && (c <= 0x065F)) + return true; + else if ((c >= 0x06D6) && (c <= 0x06ED)) + return (c != 0x06E5) && (c != 0x06E6) && (c != 0x06E9); + else if ((c >= 0x1AB0) && (c <= 0x1AFF)) + return true; + else if ((c >= 0x1DC0) && (c <= 0x1DFF)) + return true; + else if ((c >= 0x2000) && (c <= 0x200F)) + return true; + else if ((c >= 0x2028) && (c <= 0x202F)) + return true; + else if ((c >= 0x205F) && (c <= 0x206F)) + return true; + else + return false; + } + + public static boolean isCJKSymbol(int c) { + return (c >= UC_CJK_SYMBOL_START) && (c <= UC_CJK_SYMBOL_END); + } + + public static boolean isCJKIdeograph(int c) { + return (c >= UC_CJK_START) && (c <= UC_CJK_END); + } + + public static boolean isCJKVertical(int c) { + return (c >= UC_CJK_VERTICAL_START) && (c <= UC_CJK_VERTICAL_END); + } + + public static boolean isCJKCompatibility(int c) { + return (c >= UC_CJK_COMPAT_START) && (c <= UC_CJK_COMPAT_END); + } + + public static boolean isCJKHalfFullWidth(int c) { + return (c >= UC_CJK_HALF_FULL_WIDTH_START) && (c <= UC_CJK_HALF_FULL_WIDTH_END); + } + + public static boolean isHiragana(int c) { + return (c >= UC_HIRAGANA_START) && (c <= UC_HIRAGANA_END); + } + + public static boolean isKatakana(int c) { + return (c >= UC_KATAKANA_START) && (c <= UC_KATAKANA_END); + } + + public static boolean isCJK(int c) { + if (isCJKSymbol(c)) + return true; + else if (isHiragana(c)) + return true; + else if (isKatakana(c)) + return true; + else if (isCJKIdeograph(c)) + return true; + else if (isCJKVertical(c)) + return true; + else if (isCJKCompatibility(c)) + return true; + else if (isCJKHalfFullWidth(c)) + return true; + else + return false; + } + + // UTR #50 - Unicode Vertical Text Layout + + public enum VerticalOrientation { + U, // displayed upright, same orientation as in code charts + R, // displayed sideways, rotated 90 from orientation in code charts + Tu, // mapped to vertical glyph variant, or if unavailable, same orientation as in code charts + Tr; // mapped to vertical glyph variant, or if unavailable, same rotated 90 from orientation in code charts + }; + + private static final VerticalOrientation[] verticalOrientations = new VerticalOrientation[] { + VerticalOrientation.U, + VerticalOrientation.R, + VerticalOrientation.Tu, + VerticalOrientation.Tr + }; + + private static final int[] voRanges1 = new int[] { + 0x0000, 0x00A6, // 0000..00A6 ; R + 0x00AA, 0x00AD, // 00AA..00AD ; R + 0x00AF, 0x00B0, // 00AF..00B0 ; R + 0x00B2, 0x00BB, // 00B2..00BB ; R + 0x00BC, 0x00BE, // 00BC..00BE ; U + 0x00BF, 0x00D6, // 00BF..00D6 ; R + 0x00D8, 0x00F6, // 00D8..00F6 ; R + 0x00F8, 0x02E9, // 00F8..02E9 ; R + 0x02EA, 0x02EB, // 02EA..02EB ; U + 0x02EC, 0x10FF, // 02EC..10FF ; R + 0x1100, 0x11FF, // 1100..11FF ; U + 0x1200, 0x1400, // 1200..1400 ; R + 0x1401, 0x167F, // 1401..167F ; U + 0x1680, 0x18AF, // 1680..18AF ; R + 0x18B0, 0x18FF, // 18B0..18FF ; U + 0x1900, 0x2015, // 1900..2015 ; R + 0x2017, 0x201F, // 2017..201F ; R + 0x2020, 0x2021, // 2020..2021 ; U + 0x2022, 0x2025, // 2022..2025 ; R // temporarily subdivide 2022..202F to change 2026 to Tr + 0x2027, 0x202F, // 2027..202F ; R // temporarily subdivide 2022..202F to change 2026 to Tr + 0x2030, 0x2031, // 2030..2031 ; U + 0x2032, 0x203A, // 2032..203A ; R + 0x203B, 0x203C, // 203B..203C ; U + 0x203D, 0x2041, // 203D..2041 ; R + 0x2043, 0x2046, // 2043..2046 ; R + 0x2047, 0x2049, // 2047..2049 ; U + 0x204A, 0x2050, // 204A..2050 ; R + 0x2052, 0x2064, // 2052..2064 ; R + 0x2066, 0x20DC, // 2066..20DC ; R + 0x20DD, 0x20E0, // 20DD..20E0 ; U + 0x20E2, 0x20E4, // 20E2..20E4 ; U + 0x20E5, 0x20FF, // 20E5..20FF ; R + 0x2100, 0x2101, // 2100..2101 ; U + 0x2103, 0x2109, // 2103..2109 ; U + 0x210A, 0x210E, // 210A..210E ; R + 0x2110, 0x2112, // 2110..2112 ; R + 0x2113, 0x2114, // 2113..2114 ; U + 0x2116, 0x2117, // 2116..2117 ; U + 0x2118, 0x211D, // 2118..211D ; R + 0x211E, 0x2123, // 211E..2123 ; U + 0x212A, 0x212D, // 212A..212D ; R + 0x212F, 0x2134, // 212F..2134 ; R + 0x2135, 0x213F, // 2135..213F ; U + 0x2140, 0x2144, // 2140..2144 ; R + 0x2145, 0x214A, // 2145..214A ; U + 0x214C, 0x214D, // 214C..214D ; U + 0x214F, 0x218F, // 214F..218F ; U + 0x2190, 0x221D, // 2190..221D ; R + 0x221F, 0x2233, // 221F..2233 ; R + 0x2234, 0x2235, // 2234..2235 ; U + 0x2236, 0x22FF, // 2236..22FF ; R + 0x2300, 0x2307, // 2300..2307 ; U + 0x230C, 0x231F, // 230C..231F ; U + 0x2320, 0x2323, // 2320..2323 ; R + 0x2324, 0x2328, // 2324..2328 ; U + 0x2329, 0x232A, // 2329..232A ; Tr + 0x232C, 0x237C, // 232C..237C ; R + 0x237D, 0x239A, // 237D..239A ; U + 0x239B, 0x23BD, // 239B..23BD ; R + 0x23BE, 0x23CD, // 23BE..23CD ; U + 0x23D1, 0x23DB, // 23D1..23DB ; U + 0x23DC, 0x23E1, // 23DC..23E1 ; R + 0x23E2, 0x2422, // 23E2..2422 ; U + 0x2424, 0x24FF, // 2424..24FF ; U + 0x2500, 0x259F, // 2500..259F ; R + 0x25A0, 0x2619, // 25A0..2619 ; U + 0x261A, 0x261F, // 261A..261F ; R + 0x2620, 0x2767, // 2620..2767 ; U + 0x2768, 0x2775, // 2768..2775 ; R + 0x2776, 0x2793, // 2776..2793 ; U + 0x2794, 0x2B11, // 2794..2B11 ; R + 0x2B12, 0x2B2F, // 2B12..2B2F ; U + 0x2B30, 0x2B4F, // 2B30..2B4F ; R + 0x2B50, 0x2B59, // 2B50..2B59 ; U + 0x2B5A, 0x2BB7, // 2B5A..2BB7 ; R + 0x2BB8, 0x2BFF, // 2BB8..2BFF ; U + 0x2C00, 0x2E7F, // 2C00..2E7F ; R + 0x2E80, 0x3000, // 2E80..3000 ; U + 0x3001, 0x3002, // 3001..3002 ; Tu + 0x3003, 0x3007, // 3003..3007 ; U + 0x3008, 0x3011, // 3008..3011 ; Tr + 0x3012, 0x3013, // 3012..3013 ; U + 0x3014, 0x301F, // 3014..301F ; Tr + 0x3020, 0x302F, // 3020..302F ; U + 0x3031, 0x3040, // 3031..3040 ; U + 0x304A, 0x3062, // 304A..3062 ; U + 0x3064, 0x3082, // 3064..3082 ; U + 0x3088, 0x308D, // 3088..308D ; U + 0x308F, 0x3094, // 308F..3094 ; U + 0x3095, 0x3096, // 3095..3096 ; Tu + 0x3097, 0x309A, // 3097..309A ; U + 0x309B, 0x309C, // 309B..309C ; Tu + 0x309D, 0x309F, // 309D..309F ; U + 0x30AA, 0x30C2, // 30AA..30C2 ; U + 0x30C4, 0x30E2, // 30C4..30E2 ; U + 0x30E8, 0x30ED, // 30E8..30ED ; U + 0x30EF, 0x30F4, // 30EF..30F4 ; U + 0x30F5, 0x30F6, // 30F5..30F6 ; Tu + 0x30F7, 0x30FB, // 30F7..30FB ; U + 0x30FD, 0x3126, // 30FD..3126 ; U + 0x3128, 0x31EF, // 3128..31EF ; U + 0x31F0, 0x31FF, // 31F0..31FF ; Tu + 0x3200, 0x32FF, // 3200..32FF ; U + 0x3300, 0x3357, // 3300..3357 ; Tu + 0x3358, 0x337A, // 3358..337A ; U + 0x337B, 0x337F, // 337B..337F ; Tu + 0x3380, 0xA4CF, // 3380..A4CF ; U + 0x3400, 0x4DBF, // 3400..4DBF ; U + 0xA4D0, 0xA95F, // A4D0..A95F ; R + 0xA960, 0xA97F, // A960..A97F ; U + 0xA980, 0xABFF, // A980..ABFF ; R + 0xAC00, 0xD7FF, // AC00..D7FF ; U + 0xD800, 0xDFFF, // D800..DFFF ; R + 0xE000, 0xFAFF, // E000..FAFF ; U + 0xFB00, 0xFE0F, // FB00..FE0F ; R + 0xFE10, 0xFE1F, // FE10..FE1F ; U + 0xFE20, 0xFE2F, // FE20..FE2F ; R + 0xFE30, 0xFE48, // FE30..FE48 ; U + 0xFE49, 0xFE4F, // FE49..FE4F ; R + 0xFE50, 0xFE52, // FE50..FE52 ; Tu + 0xFE53, 0xFE57, // FE53..FE57 ; U + 0xFE59, 0xFE5E, // FE59..FE5E ; Tr + 0xFE5F, 0xFE62, // FE5F..FE62 ; U + 0xFE63, 0xFE66, // FE63..FE66 ; R + 0xFE67, 0xFE6F, // FE67..FE6F ; U + 0xFE70, 0xFF00, // FE70..FF00 ; R + 0xFF02, 0xFF07, // FF02..FF07 ; U + 0xFF08, 0xFF09, // FF08..FF09 ; Tr + 0xFF0A, 0xFF0B, // FF0A..FF0B ; U + 0xFF0F, 0xFF19, // FF0F..FF19 ; U + 0xFF1A, 0xFF1B, // FF1A..FF1B ; Tr + 0xFF1C, 0xFF1E, // FF1C..FF1E ; R + 0xFF20, 0xFF3A, // FF20..FF3A ; U + 0xFF40, 0xFF5A, // FF40..FF5A ; U + 0xFF5B, 0xFF60, // FF5B..FF60 ; Tr + 0xFF61, 0xFFDF, // FF61..FFDF ; R + 0xFFE0, 0xFFE2, // FFE0..FFE2 ; U + 0xFFE4, 0xFFE7, // FFE4..FFE7 ; U + 0xFFE8, 0xFFEF, // FFE8..FFEF ; R + 0xFFF0, 0xFFF8, // FFF0..FFF8 ; U + 0xFFF9, 0xFFFB, // FFF9..FFFB ; R + 0xFFFC, 0xFFFD, // FFFC..FFFD ; U + 0xFFFE, 0xFFFF // FFFE..FFFF ; R + }; + + private static final byte[] voRanges1Values = new byte[] { + 1, // 0000..00A6 ; R + 1, // 00AA..00AD ; R + 1, // 00AF..00B0 ; R + 1, // 00B2..00BB ; R + 0, // 00BC..00BE ; U + 1, // 00BF..00D6 ; R + 1, // 00D8..00F6 ; R + 1, // 00F8..02E9 ; R + 0, // 02EA..02EB ; U + 1, // 02EC..10FF ; R + 0, // 1100..11FF ; U + 1, // 1200..1400 ; R + 0, // 1401..167F ; U + 1, // 1680..18AF ; R + 0, // 18B0..18FF ; U + 1, // 1900..2015 ; R + 1, // 2017..201F ; R + 0, // 2020..2021 ; U + 1, // 2022..2025 ; R + 1, // 2027..202F ; R + 0, // 2030..2031 ; U + 1, // 2032..203A ; R + 0, // 203B..203C ; U + 1, // 203D..2041 ; R + 1, // 2043..2046 ; R + 0, // 2047..2049 ; U + 1, // 204A..2050 ; R + 1, // 2052..2064 ; R + 1, // 2066..20DC ; R + 0, // 20DD..20E0 ; U + 0, // 20E2..20E4 ; U + 1, // 20E5..20FF ; R + 0, // 2100..2101 ; U + 0, // 2103..2109 ; U + 1, // 210A..210E ; R + 1, // 2110..2112 ; R + 0, // 2113..2114 ; U + 0, // 2116..2117 ; U + 1, // 2118..211D ; R + 0, // 211E..2123 ; U + 1, // 212A..212D ; R + 1, // 212F..2134 ; R + 0, // 2135..213F ; U + 1, // 2140..2144 ; R + 0, // 2145..214A ; U + 0, // 214C..214D ; U + 0, // 214F..218F ; U + 1, // 2190..221D ; R + 1, // 221F..2233 ; R + 0, // 2234..2235 ; U + 1, // 2236..22FF ; R + 0, // 2300..2307 ; U + 0, // 230C..231F ; U + 1, // 2320..2323 ; R + 0, // 2324..2328 ; U + 3, // 2329..232A ; Tr + 1, // 232C..237C ; R + 0, // 237D..239A ; U + 1, // 239B..23BD ; R + 0, // 23BE..23CD ; U + 0, // 23D1..23DB ; U + 1, // 23DC..23E1 ; R + 0, // 23E2..2422 ; U + 0, // 2424..24FF ; U + 1, // 2500..259F ; R + 0, // 25A0..2619 ; U + 1, // 261A..261F ; R + 0, // 2620..2767 ; U + 1, // 2768..2775 ; R + 0, // 2776..2793 ; U + 1, // 2794..2B11 ; R + 0, // 2B12..2B2F ; U + 1, // 2B30..2B4F ; R + 0, // 2B50..2B59 ; U + 1, // 2B5A..2BB7 ; R + 0, // 2BB8..2BFF ; U + 1, // 2C00..2E7F ; R + 0, // 2E80..3000 ; U + 2, // 3001..3002 ; Tu + 0, // 3003..3007 ; U + 3, // 3008..3011 ; Tr + 0, // 3012..3013 ; U + 3, // 3014..301F ; Tr + 0, // 3020..302F ; U + 0, // 3031..3040 ; U + 0, // 304A..3062 ; U + 0, // 3064..3082 ; U + 0, // 3088..308D ; U + 0, // 308F..3094 ; U + 2, // 3095..3096 ; Tu + 0, // 3097..309A ; U + 2, // 309B..309C ; Tu + 0, // 309D..309F ; U + 0, // 30AA..30C2 ; U + 0, // 30C4..30E2 ; U + 0, // 30E8..30ED ; U + 0, // 30EF..30F4 ; U + 2, // 30F5..30F6 ; Tu + 0, // 30F7..30FB ; U + 0, // 30FD..3126 ; U + 0, // 3128..31EF ; U + 2, // 31F0..31FF ; Tu + 0, // 3200..32FF ; U + 2, // 3300..3357 ; Tu + 0, // 3358..337A ; U + 2, // 337B..337F ; Tu + 0, // 3380..A4CF ; U + 0, // 3400..4DBF ; U + 1, // A4D0..A95F ; R + 0, // A960..A97F ; U + 1, // A980..ABFF ; R + 0, // AC00..D7FF ; U + 1, // D800..DFFF ; R + 0, // E000..FAFF ; U + 1, // FB00..FE0F ; R + 0, // FE10..FE1F ; U + 1, // FE20..FE2F ; R + 0, // FE30..FE48 ; U + 1, // FE49..FE4F ; R + 2, // FE50..FE52 ; Tu + 0, // FE53..FE57 ; U + 3, // FE59..FE5E ; Tr + 0, // FE5F..FE62 ; U + 1, // FE63..FE66 ; R + 0, // FE67..FE6F ; U + 1, // FE70..FF00 ; R + 0, // FF02..FF07 ; U + 3, // FF08..FF09 ; Tr + 0, // FF0A..FF0B ; U + 0, // FF0F..FF19 ; U + 3, // FF1A..FF1B ; Tr + 1, // FF1C..FF1E ; R + 0, // FF20..FF3A ; U + 0, // FF40..FF5A ; U + 3, // FF5B..FF60 ; Tr + 1, // FF61..FFDF ; R + 0, // FFE0..FFE2 ; U + 0, // FFE4..FFE7 ; U + 1, // FFE8..FFEF ; R + 0, // FFF0..FFF8 ; U + 1, // FFF9..FFFB ; R + 0, // FFFC..FFFD ; U + 1 // FFFE..FFFF ; R + }; + + private static final int[] voSingles1 = new int[] { + 0x2016, // 2016..2016 ; U + 0x2026, // 2016..2016 ; Tr + 0x3030, // 3030..3030 ; Tr + 0x3041, // 3041..3041 ; Tu + 0x3042, // 3042..3042 ; U + 0x3043, // 3043..3043 ; Tu + 0x3044, // 3044..3044 ; U + 0x3045, // 3045..3045 ; Tu + 0x3046, // 3046..3046 ; U + 0x3047, // 3047..3047 ; Tu + 0x3048, // 3048..3048 ; U + 0x3049, // 3049..3049 ; Tu + 0x3063, // 3063..3063 ; Tu + 0x3083, // 3083..3083 ; Tu + 0x3084, // 3084..3084 ; U + 0x3085, // 3085..3085 ; Tu + 0x3086, // 3086..3086 ; U + 0x3087, // 3087..3087 ; Tu + 0x308E, // 308E..308E ; Tu + 0x30A0, // 30A0..30A0 ; Tr + 0x30A1, // 30A1..30A1 ; Tu + 0x30A2, // 30A2..30A2 ; U + 0x30A3, // 30A3..30A3 ; Tu + 0x30A4, // 30A4..30A4 ; U + 0x30A5, // 30A5..30A5 ; Tu + 0x30A6, // 30A6..30A6 ; U + 0x30A7, // 30A7..30A7 ; Tu + 0x30A8, // 30A8..30A8 ; U + 0x30A9, // 30A9..30A9 ; Tu + 0x30C3, // 30C3..30C3 ; Tu + 0x30E3, // 30E3..30E3 ; Tu + 0x30E4, // 30E4..30E4 ; U + 0x30E5, // 30E5..30E5 ; Tu + 0x30E6, // 30E6..30E6 ; U + 0x30E7, // 30E7..30E7 ; Tu + 0x30EE, // 30EE..30EE ; Tu + 0x30FC, // 30FC..30FC ; Tr + 0x3127, // 3127..3127 ; Tu + 0xFE58, // FE58..FE58 ; R + 0xFF01, // FF01..FF01 ; Tu + 0xFF0C, // FF0C..FF0C ; Tu + 0xFF0D, // FF0D..FF0D ; R + 0xFF0E, // FF0E..FF0E ; Tu + 0xFF1F, // FF1F..FF1F ; Tu + 0xFF3B, // FF3B..FF3B ; Tr + 0xFF3C, // FF3C..FF3C ; U + 0xFF3D, // FF3D..FF3D ; Tr + 0xFF3E, // FF3E..FF3E ; U + 0xFF3F, // FF3F..FF3F ; Tr + 0xFFE3 // FFE3..FFE3 ; Tr + }; + + private static final byte[] voSingles1Values = new byte[] { + 3, // 2016..2016 ; Tr + 3, // 2026..2026 ; Tr + 3, // 3030..3030 ; Tr + 2, // 3041..3041 ; Tu + 0, // 3042..3042 ; U + 2, // 3043..3043 ; Tu + 0, // 3044..3044 ; U + 2, // 3045..3045 ; Tu + 0, // 3046..3046 ; U + 2, // 3047..3047 ; Tu + 0, // 3048..3048 ; U + 2, // 3049..3049 ; Tu + 2, // 3063..3063 ; Tu + 2, // 3083..3083 ; Tu + 0, // 3084..3084 ; U + 2, // 3085..3085 ; Tu + 0, // 3086..3086 ; U + 2, // 3087..3087 ; Tu + 2, // 308E..308E ; Tu + 3, // 30A0..30A0 ; Tr + 2, // 30A1..30A1 ; Tu + 0, // 30A2..30A2 ; U + 2, // 30A3..30A3 ; Tu + 0, // 30A4..30A4 ; U + 2, // 30A5..30A5 ; Tu + 0, // 30A6..30A6 ; U + 2, // 30A7..30A7 ; Tu + 0, // 30A8..30A8 ; U + 2, // 30A9..30A9 ; Tu + 2, // 30C3..30C3 ; Tu + 2, // 30E3..30E3 ; Tu + 0, // 30E4..30E4 ; U + 2, // 30E5..30E5 ; Tu + 0, // 30E6..30E6 ; U + 2, // 30E7..30E7 ; Tu + 2, // 30EE..30EE ; Tu + 3, // 30FC..30FC ; Tr + 2, // 3127..3127 ; Tu + 1, // FE58..FE58 ; R + 2, // FF01..FF01 ; Tu + 2, // FF0C..FF0C ; Tu + 1, // FF0D..FF0D ; R + 2, // FF0E..FF0E ; Tu + 2, // FF1F..FF1F ; Tu + 3, // FF3B..FF3B ; Tr + 0, // FF3C..FF3C ; U + 3, // FF3D..FF3D ; Tr + 0, // FF3E..FF3E ; U + 3, // FF3F..FF3F ; Tr + 3 // FFE3..FFE3 ; Tr + }; + + public static VerticalOrientation getVerticalOrientation(int c) { + VerticalOrientation vo; + vo = lookupOrientationInRange0(c); + if (vo != null) + return vo; + vo = lookupOrientationInRanges(c); + if (vo != null) + return vo; + vo = lookupOrientationInSingles(c); + if (vo != null) + return vo; + return VerticalOrientation.U; + } + + private static VerticalOrientation lookupOrientationInRange0(int c) { + assert voRanges1.length >= 2; + assert voRanges1Values.length >= 1; + int c0 = voRanges1[0]; + int c1 = voRanges1[1]; + return ((c >= c0) && (c <= c1)) ? verticalOrientations[voRanges1Values[0]] : null; + } + + private static VerticalOrientation lookupOrientationInRanges(int c) { + int n = voRanges1.length / 2; + if (n > 0) { + for (int f = 0, t = n; f <= t; ) { + int m = (f + t) / 2; + int i = 2 * m; + if (i >= voRanges1.length) { + return null; + } + int c0 = voRanges1[i + 0]; + int c1 = voRanges1[i + 1]; + if (c1 < c) { + if ((f = m + 1) > n) + break; + } else if (c0 > c) { + if ((t = m - 1) < 0) + break; + } else + return verticalOrientations[voRanges1Values[m]]; + } + } + return null; + } + + private static VerticalOrientation lookupOrientationInSingles(int c) { + int n = voSingles1.length; + if (n > 0) { + for (int f = 0, t = n; f <= t; ) { + int m = (f + t) >>> 1; + if (m >= voSingles1.length) + return null; + int c0 = voSingles1[m]; + if (c0 < c) { + if ((f = m + 1) > n) + break; + } else if (c0 > c) { + if ((t = m - 1) < 0) + break; + } else + return verticalOrientations[voSingles1Values[m]]; + } + } + return null; + } + + public static boolean isUprightOrientation(int c) { + VerticalOrientation vo = getVerticalOrientation(c); + if (vo == VerticalOrientation.U) + return true; + else if (vo == VerticalOrientation.Tu) + return true; + else if (vo == VerticalOrientation.Tr) { + // TBD - should return false if font doesn't have a Tr mapping, but we don't have a font binding here; so just assume it does have a mapping; + // if it doesn't have one, then will get an upright glyph when we want a rotated glyph + return true; + } else + return false; + } + + private static final int[] h2fKey = new int[] { + 0x0021, + 0x0022, + 0x0023, + 0x0024, + 0x0025, + 0x0026, + 0x0027, + 0x0028, + 0x0029, + 0x002A, + 0x002B, + 0x002C, + 0x002D, + 0x002E, + 0x002F, + 0x0030, + 0x0031, + 0x0032, + 0x0033, + 0x0034, + 0x0035, + 0x0036, + 0x0037, + 0x0038, + 0x0039, + 0x003A, + 0x003B, + 0x003C, + 0x003D, + 0x003E, + 0x003F, + 0x0040, + 0x0041, + 0x0042, + 0x0043, + 0x0044, + 0x0045, + 0x0046, + 0x0047, + 0x0048, + 0x0049, + 0x004A, + 0x004B, + 0x004C, + 0x004D, + 0x004E, + 0x004F, + 0x0050, + 0x0051, + 0x0052, + 0x0053, + 0x0054, + 0x0055, + 0x0056, + 0x0057, + 0x0058, + 0x0059, + 0x005A, + 0x005B, + 0x005C, + 0x005D, + 0x005E, + 0x005F, + 0x0060, + 0x0061, + 0x0062, + 0x0063, + 0x0064, + 0x0065, + 0x0066, + 0x0067, + 0x0068, + 0x0069, + 0x006A, + 0x006B, + 0x006C, + 0x006D, + 0x006E, + 0x006F, + 0x0070, + 0x0071, + 0x0072, + 0x0073, + 0x0074, + 0x0075, + 0x0076, + 0x0077, + 0x0078, + 0x0079, + 0x007A, + 0x007B, + 0x007C, + 0x007D, + 0x007E, + 0x00A2, + 0x00A3, + 0x00A5, + 0x00A6, + 0x00AC, + 0x00AF, + 0x20A9, + 0x2985, + 0x2986, + 0x3001, + 0x3002, + 0x300C, + 0x300D, + 0xFF65, + 0xFF66, + 0xFF67, + 0xFF68, + 0xFF69, + 0xFF6A, + 0xFF6B, + 0xFF6C, + 0xFF6D, + 0xFF6E, + 0xFF6F, + 0xFF70, + 0xFF71, + 0xFF72, + 0xFF73, + 0xFF74, + 0xFF75, + 0xFF76, + 0xFF77, + 0xFF78, + 0xFF79, + 0xFF7A, + 0xFF7B, + 0xFF7C, + 0xFF7D, + 0xFF7E, + 0xFF7F, + 0xFF80, + 0xFF81, + 0xFF82, + 0xFF83, + 0xFF84, + 0xFF85, + 0xFF86, + 0xFF87, + 0xFF88, + 0xFF89, + 0xFF8A, + 0xFF8B, + 0xFF8C, + 0xFF8D, + 0xFF8E, + 0xFF8F, + 0xFF90, + 0xFF91, + 0xFF92, + 0xFF93, + 0xFF94, + 0xFF95, + 0xFF96, + 0xFF97, + 0xFF98, + 0xFF99, + 0xFF9A, + 0xFF9B, + 0xFF9C, + 0xFF9D, + 0xFF9E, + 0xFF9F, + 0xFFE8, + 0xFFE9, + 0xFFEA, + 0xFFEB, + 0xFFEC, + 0xFFED, + 0xFFEE + }; + + private static final int[] h2fVal = new int[] { + 0xFF01, + 0xFF02, + 0xFF03, + 0xFF04, + 0xFF05, + 0xFF06, + 0xFF07, + 0xFF08, + 0xFF09, + 0xFF0A, + 0xFF0B, + 0xFF0C, + 0xFF0D, + 0xFF0E, + 0xFF0F, + 0xFF10, + 0xFF11, + 0xFF12, + 0xFF13, + 0xFF14, + 0xFF15, + 0xFF16, + 0xFF17, + 0xFF18, + 0xFF19, + 0xFF1A, + 0xFF1B, + 0xFF1C, + 0xFF1D, + 0xFF1E, + 0xFF1F, + 0xFF20, + 0xFF21, + 0xFF22, + 0xFF23, + 0xFF24, + 0xFF25, + 0xFF26, + 0xFF27, + 0xFF28, + 0xFF29, + 0xFF2A, + 0xFF2B, + 0xFF2C, + 0xFF2D, + 0xFF2E, + 0xFF2F, + 0xFF30, + 0xFF31, + 0xFF32, + 0xFF33, + 0xFF34, + 0xFF35, + 0xFF36, + 0xFF37, + 0xFF38, + 0xFF39, + 0xFF3A, + 0xFF3B, + 0xFF3C, + 0xFF3D, + 0xFF3E, + 0xFF3F, + 0xFF40, + 0xFF41, + 0xFF42, + 0xFF43, + 0xFF44, + 0xFF45, + 0xFF46, + 0xFF47, + 0xFF48, + 0xFF49, + 0xFF4A, + 0xFF4B, + 0xFF4C, + 0xFF4D, + 0xFF4E, + 0xFF4F, + 0xFF50, + 0xFF51, + 0xFF52, + 0xFF53, + 0xFF54, + 0xFF55, + 0xFF56, + 0xFF57, + 0xFF58, + 0xFF59, + 0xFF5A, + 0xFF5B, + 0xFF5C, + 0xFF5D, + 0xFF5E, + 0xFFE0, + 0xFFE1, + 0xFFE5, + 0xFFE4, + 0xFFE2, + 0xFFE3, + 0xFFE6, + 0xFF5F, + 0xFF60, + 0xFF64, + 0xFF61, + 0xFF62, + 0xFF63, + 0x30FB, + 0x30F2, + 0x30A1, + 0x30A3, + 0x30A5, + 0x30A7, + 0x30A9, + 0x30E3, + 0x30E5, + 0x30E7, + 0x30C3, + 0x30FC, + 0x30A2, + 0x30A4, + 0x30A6, + 0x30A8, + 0x30AA, + 0x30AB, + 0x30AD, + 0x30AF, + 0x30B1, + 0x30B3, + 0x30B5, + 0x30B7, + 0x30B9, + 0x30BB, + 0x30BD, + 0x30BF, + 0x30C1, + 0x30C4, + 0x30C6, + 0x30C8, + 0x30CA, + 0x30CB, + 0x30CC, + 0x30CD, + 0x30CE, + 0x30CF, + 0x30D2, + 0x30D5, + 0x30D8, + 0x30DB, + 0x30DE, + 0x30DF, + 0x30E0, + 0x30E1, + 0x30E2, + 0x30E4, + 0x30E6, + 0x30E8, + 0x30E9, + 0x30EA, + 0x30EB, + 0x30EC, + 0x30ED, + 0x30EF, + 0x30F3, + 0x3099, + 0x309A, + 0x2502, + 0x2190, + 0x2191, + 0x2192, + 0x2193, + 0X25A0, + 0x25CB + }; + + private static final int[] f2hKey = new int[] { + 0x25A0, + 0x2190, + 0x2191, + 0x2192, + 0x2193, + 0x2502, + 0x25CB, + 0x3099, + 0x309A, + 0x30A1, + 0x30A2, + 0x30A3, + 0x30A4, + 0x30A5, + 0x30A6, + 0x30A7, + 0x30A8, + 0x30A9, + 0x30AA, + 0x30AB, + 0x30AD, + 0x30AF, + 0x30B1, + 0x30B3, + 0x30B5, + 0x30B7, + 0x30B9, + 0x30BB, + 0x30BD, + 0x30BF, + 0x30C1, + 0x30C3, + 0x30C4, + 0x30C6, + 0x30C8, + 0x30CA, + 0x30CB, + 0x30CC, + 0x30CD, + 0x30CE, + 0x30CF, + 0x30D2, + 0x30D5, + 0x30D8, + 0x30DB, + 0x30DE, + 0x30DF, + 0x30E0, + 0x30E1, + 0x30E2, + 0x30E3, + 0x30E4, + 0x30E5, + 0x30E6, + 0x30E7, + 0x30E8, + 0x30E9, + 0x30EA, + 0x30EB, + 0x30EC, + 0x30ED, + 0x30EF, + 0x30F2, + 0x30F3, + 0x30FB, + 0x30FC, + 0xFF01, + 0xFF02, + 0xFF03, + 0xFF04, + 0xFF05, + 0xFF06, + 0xFF07, + 0xFF08, + 0xFF09, + 0xFF0A, + 0xFF0B, + 0xFF0C, + 0xFF0D, + 0xFF0E, + 0xFF0F, + 0xFF10, + 0xFF11, + 0xFF12, + 0xFF13, + 0xFF14, + 0xFF15, + 0xFF16, + 0xFF17, + 0xFF18, + 0xFF19, + 0xFF1A, + 0xFF1B, + 0xFF1C, + 0xFF1D, + 0xFF1E, + 0xFF1F, + 0xFF20, + 0xFF21, + 0xFF22, + 0xFF23, + 0xFF24, + 0xFF25, + 0xFF26, + 0xFF27, + 0xFF28, + 0xFF29, + 0xFF2A, + 0xFF2B, + 0xFF2C, + 0xFF2D, + 0xFF2E, + 0xFF2F, + 0xFF30, + 0xFF31, + 0xFF32, + 0xFF33, + 0xFF34, + 0xFF35, + 0xFF36, + 0xFF37, + 0xFF38, + 0xFF39, + 0xFF3A, + 0xFF3B, + 0xFF3C, + 0xFF3D, + 0xFF3E, + 0xFF3F, + 0xFF40, + 0xFF41, + 0xFF42, + 0xFF43, + 0xFF44, + 0xFF45, + 0xFF46, + 0xFF47, + 0xFF48, + 0xFF49, + 0xFF4A, + 0xFF4B, + 0xFF4C, + 0xFF4D, + 0xFF4E, + 0xFF4F, + 0xFF50, + 0xFF51, + 0xFF52, + 0xFF53, + 0xFF54, + 0xFF55, + 0xFF56, + 0xFF57, + 0xFF58, + 0xFF59, + 0xFF5A, + 0xFF5B, + 0xFF5C, + 0xFF5D, + 0xFF5E, + 0xFF5F, + 0xFF60, + 0xFF61, + 0xFF62, + 0xFF63, + 0xFF64, + 0xFFE0, + 0xFFE1, + 0xFFE2, + 0xFFE3, + 0xFFE4, + 0xFFE5, + 0xFFE6 + }; + + private static final int[] f2hVal = new int[] { + 0xFFED, + 0xFFE9, + 0xFFEA, + 0xFFEB, + 0xFFEC, + 0xFFE8, + 0xFFEE, + 0xFF9E, + 0xFF9F, + 0xFF67, + 0xFF71, + 0xFF68, + 0xFF72, + 0xFF69, + 0xFF73, + 0xFF6A, + 0xFF74, + 0xFF6B, + 0xFF75, + 0xFF76, + 0xFF77, + 0xFF78, + 0xFF79, + 0xFF7A, + 0xFF7B, + 0xFF7C, + 0xFF7D, + 0xFF7E, + 0xFF7F, + 0xFF80, + 0xFF81, + 0xFF6F, + 0xFF82, + 0xFF83, + 0xFF84, + 0xFF85, + 0xFF86, + 0xFF87, + 0xFF88, + 0xFF89, + 0xFF8A, + 0xFF8B, + 0xFF8C, + 0xFF8D, + 0xFF8E, + 0xFF8F, + 0xFF90, + 0xFF91, + 0xFF92, + 0xFF93, + 0xFF6C, + 0xFF94, + 0xFF6D, + 0xFF95, + 0xFF6E, + 0xFF96, + 0xFF97, + 0xFF98, + 0xFF99, + 0xFF9A, + 0xFF9B, + 0xFF9C, + 0xFF66, + 0xFF9D, + 0xFF65, + 0xFF70, + 0x0021, + 0x0022, + 0x0023, + 0x0024, + 0x0025, + 0x0026, + 0x0027, + 0x0028, + 0x0029, + 0x002A, + 0x002B, + 0x002C, + 0x002D, + 0x002E, + 0x002F, + 0x0030, + 0x0031, + 0x0032, + 0x0033, + 0x0034, + 0x0035, + 0x0036, + 0x0037, + 0x0038, + 0x0039, + 0x003A, + 0x003B, + 0x003C, + 0x003D, + 0x003E, + 0x003F, + 0x0040, + 0x0041, + 0x0042, + 0x0043, + 0x0044, + 0x0045, + 0x0046, + 0x0047, + 0x0048, + 0x0049, + 0x004A, + 0x004B, + 0x004C, + 0x004D, + 0x004E, + 0x004F, + 0x0050, + 0x0051, + 0x0052, + 0x0053, + 0x0054, + 0x0055, + 0x0056, + 0x0057, + 0x0058, + 0x0059, + 0x005A, + 0x005B, + 0x005C, + 0x005D, + 0x005E, + 0x005F, + 0x0060, + 0x0061, + 0x0062, + 0x0063, + 0x0064, + 0x0065, + 0x0066, + 0x0067, + 0x0068, + 0x0069, + 0x006A, + 0x006B, + 0x006C, + 0x006D, + 0x006E, + 0x006F, + 0x0070, + 0x0071, + 0x0072, + 0x0073, + 0x0074, + 0x0075, + 0x0076, + 0x0077, + 0x0078, + 0x0079, + 0x007A, + 0x007B, + 0x007C, + 0x007D, + 0x007E, + 0x2985, + 0x2986, + 0x3002, + 0x300C, + 0x300D, + 0x3001, + 0x00A2, + 0x00A3, + 0x00AC, + 0x00AF, + 0x00A6, + 0x00A5, + 0x20A9 + }; + + public static boolean hasHalfWidth(int c) { + return Arrays.binarySearch(f2hKey, c) >= 0; + } + + public static int toHalfWidth(int c) { + int k = Arrays.binarySearch(f2hKey, c); + if (k >= 0) + return f2hVal[k]; + else + return c; + } + + public static boolean hasFullWidth(int c) { + return Arrays.binarySearch(h2fKey, c) >= 0; + } + + public static int toFullWidth(int c) { + int k = Arrays.binarySearch(h2fKey, c); + if (k >= 0) + return h2fVal[k]; + else + return c; + } + + private static final int[] h2vKey = new int[] { + 0x2026 + }; + + private static final int[] h2vVal = new int[] { + 0xFE19 + }; + + public static boolean hasVertical(int c) { + return Arrays.binarySearch(h2vKey, c) >= 0; + } + + public static int toVertical(int c) { + int k = Arrays.binarySearch(h2vKey, c); + if (k >= 0) + return h2vVal[k]; + else + return c; + } + + private static final int[] mirKey = new int[] { + 0x0028, // LEFT PARENTHESIS + 0x0029, // RIGHT PARENTHESIS + 0x003C, // LESS-THAN SIGN + 0x003E, // GREATER-THAN SIGN + 0x005B, // LEFT SQUARE BRACKET + 0x005D, // RIGHT SQUARE BRACKET + 0x007B, // LEFT CURLY BRACKET + 0x007D, // RIGHT CURLY BRACKET + 0x00AB, // LEFT-POINTING DOUBLE ANGLE QUOTATION MARK + 0x00BB, // RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK + 0x0F3A, // TIBETAN MARK GUG RTAGS GYON + 0x0F3B, // TIBETAN MARK GUG RTAGS GYAS + 0x0F3C, // TIBETAN MARK ANG KHANG GYON + 0x0F3D, // TIBETAN MARK ANG KHANG GYAS + 0x169B, // OGHAM FEATHER MARK + 0x169C, // OGHAM REVERSED FEATHER MARK + 0x2039, // SINGLE LEFT-POINTING ANGLE QUOTATION MARK + 0x203A, // SINGLE RIGHT-POINTING ANGLE QUOTATION MARK + 0x2045, // LEFT SQUARE BRACKET WITH QUILL + 0x2046, // RIGHT SQUARE BRACKET WITH QUILL + 0x207D, // SUPERSCRIPT LEFT PARENTHESIS + 0x207E, // SUPERSCRIPT RIGHT PARENTHESIS + 0x208D, // SUBSCRIPT LEFT PARENTHESIS + 0x208E, // SUBSCRIPT RIGHT PARENTHESIS + 0x2208, // ELEMENT OF + 0x2209, // NOT AN ELEMENT OF + 0x220A, // SMALL ELEMENT OF + 0x220B, // CONTAINS AS MEMBER + 0x220C, // DOES NOT CONTAIN AS MEMBER + 0x220D, // SMALL CONTAINS AS MEMBER + 0x2215, // DIVISION SLASH + 0x223C, // TILDE OPERATOR + 0x223D, // REVERSED TILDE + 0x2243, // ASYMPTOTICALLY EQUAL TO + 0x2252, // APPROXIMATELY EQUAL TO OR THE IMAGE OF + 0x2253, // IMAGE OF OR APPROXIMATELY EQUAL TO + 0x2254, // COLON EQUALS + 0x2255, // EQUALS COLON + 0x2264, // LESS-THAN OR EQUAL TO + 0x2265, // GREATER-THAN OR EQUAL TO + 0x2266, // LESS-THAN OVER EQUAL TO + 0x2267, // GREATER-THAN OVER EQUAL TO + 0x2268, // LESS-THAN BUT NOT EQUAL TO + 0x2269, // GREATER-THAN BUT NOT EQUAL TO + 0x226A, // MUCH LESS-THAN + 0x226B, // MUCH GREATER-THAN + 0x226E, // NOT LESS-THAN + 0x226F, // NOT GREATER-THAN + 0x2270, // NEITHER LESS-THAN NOR EQUAL TO + 0x2271, // NEITHER GREATER-THAN NOR EQUAL TO + 0x2272, // LESS-THAN OR EQUIVALENT TO + 0x2273, // GREATER-THAN OR EQUIVALENT TO + 0x2274, // NEITHER LESS-THAN NOR EQUIVALENT TO + 0x2275, // NEITHER GREATER-THAN NOR EQUIVALENT TO + 0x2276, // LESS-THAN OR GREATER-THAN + 0x2277, // GREATER-THAN OR LESS-THAN + 0x2278, // NEITHER LESS-THAN NOR GREATER-THAN + 0x2279, // NEITHER GREATER-THAN NOR LESS-THAN + 0x227A, // PRECEDES + 0x227B, // SUCCEEDS + 0x227C, // PRECEDES OR EQUAL TO + 0x227D, // SUCCEEDS OR EQUAL TO + 0x227E, // PRECEDES OR EQUIVALENT TO + 0x227F, // SUCCEEDS OR EQUIVALENT TO + 0x2280, // DOES NOT PRECEDE + 0x2281, // DOES NOT SUCCEED + 0x2282, // SUBSET OF + 0x2283, // SUPERSET OF + 0x2284, // NOT A SUBSET OF + 0x2285, // NOT A SUPERSET OF + 0x2286, // SUBSET OF OR EQUAL TO + 0x2287, // SUPERSET OF OR EQUAL TO + 0x2288, // NEITHER A SUBSET OF NOR EQUAL TO + 0x2289, // NEITHER A SUPERSET OF NOR EQUAL TO + 0x228A, // SUBSET OF WITH NOT EQUAL TO + 0x228B, // SUPERSET OF WITH NOT EQUAL TO + 0x228F, // SQUARE IMAGE OF + 0x2290, // SQUARE ORIGINAL OF + 0x2291, // SQUARE IMAGE OF OR EQUAL TO + 0x2292, // SQUARE ORIGINAL OF OR EQUAL TO + 0x2298, // CIRCLED DIVISION SLASH + 0x22A2, // RIGHT TACK + 0x22A3, // LEFT TACK + 0x22A6, // ASSERTION + 0x22A8, // TRUE + 0x22A9, // FORCES + 0x22AB, // DOUBLE VERTICAL BAR DOUBLE RIGHT TURNSTILE + 0x22B0, // PRECEDES UNDER RELATION + 0x22B1, // SUCCEEDS UNDER RELATION + 0x22B2, // NORMAL SUBGROUP OF + 0x22B3, // CONTAINS AS NORMAL SUBGROUP + 0x22B4, // NORMAL SUBGROUP OF OR EQUAL TO + 0x22B5, // CONTAINS AS NORMAL SUBGROUP OR EQUAL TO + 0x22B6, // ORIGINAL OF + 0x22B7, // IMAGE OF + 0x22C9, // LEFT NORMAL FACTOR SEMIDIRECT PRODUCT + 0x22CA, // RIGHT NORMAL FACTOR SEMIDIRECT PRODUCT + 0x22CB, // LEFT SEMIDIRECT PRODUCT + 0x22CC, // RIGHT SEMIDIRECT PRODUCT + 0x22CD, // REVERSED TILDE EQUALS + 0x22D0, // DOUBLE SUBSET + 0x22D1, // DOUBLE SUPERSET + 0x22D6, // LESS-THAN WITH DOT + 0x22D7, // GREATER-THAN WITH DOT + 0x22D8, // VERY MUCH LESS-THAN + 0x22D9, // VERY MUCH GREATER-THAN + 0x22DA, // LESS-THAN EQUAL TO OR GREATER-THAN + 0x22DB, // GREATER-THAN EQUAL TO OR LESS-THAN + 0x22DC, // EQUAL TO OR LESS-THAN + 0x22DD, // EQUAL TO OR GREATER-THAN + 0x22DE, // EQUAL TO OR PRECEDES + 0x22DF, // EQUAL TO OR SUCCEEDS + 0x22E0, // DOES NOT PRECEDE OR EQUAL + 0x22E1, // DOES NOT SUCCEED OR EQUAL + 0x22E2, // NOT SQUARE IMAGE OF OR EQUAL TO + 0x22E3, // NOT SQUARE ORIGINAL OF OR EQUAL TO + 0x22E4, // SQUARE IMAGE OF OR NOT EQUAL TO + 0x22E5, // SQUARE ORIGINAL OF OR NOT EQUAL TO + 0x22E6, // LESS-THAN BUT NOT EQUIVALENT TO + 0x22E7, // GREATER-THAN BUT NOT EQUIVALENT TO + 0x22E8, // PRECEDES BUT NOT EQUIVALENT TO + 0x22E9, // SUCCEEDS BUT NOT EQUIVALENT TO + 0x22EA, // NOT NORMAL SUBGROUP OF + 0x22EB, // DOES NOT CONTAIN AS NORMAL SUBGROUP + 0x22EC, // NOT NORMAL SUBGROUP OF OR EQUAL TO + 0x22ED, // DOES NOT CONTAIN AS NORMAL SUBGROUP OR EQUAL + 0x22F0, // UP RIGHT DIAGONAL ELLIPSIS + 0x22F1, // DOWN RIGHT DIAGONAL ELLIPSIS + 0x22F2, // ELEMENT OF WITH LONG HORIZONTAL STROKE + 0x22F3, // ELEMENT OF WITH VERTICAL BAR AT END OF HORIZONTAL STROKE + 0x22F4, // SMALL ELEMENT OF WITH VERTICAL BAR AT END OF HORIZONTAL STROKE + 0x22F6, // ELEMENT OF WITH OVERBAR + 0x22F7, // SMALL ELEMENT OF WITH OVERBAR + 0x22FA, // CONTAINS WITH LONG HORIZONTAL STROKE + 0x22FB, // CONTAINS WITH VERTICAL BAR AT END OF HORIZONTAL STROKE + 0x22FC, // SMALL CONTAINS WITH VERTICAL BAR AT END OF HORIZONTAL STROKE + 0x22FD, // CONTAINS WITH OVERBAR + 0x22FE, // SMALL CONTAINS WITH OVERBAR + 0x2308, // LEFT CEILING + 0x2309, // RIGHT CEILING + 0x230A, // LEFT FLOOR + 0x230B, // RIGHT FLOOR + 0x2329, // LEFT-POINTING ANGLE BRACKET + 0x232A, // RIGHT-POINTING ANGLE BRACKET + 0x2768, // MEDIUM LEFT PARENTHESIS ORNAMENT + 0x2769, // MEDIUM RIGHT PARENTHESIS ORNAMENT + 0x276A, // MEDIUM FLATTENED LEFT PARENTHESIS ORNAMENT + 0x276B, // MEDIUM FLATTENED RIGHT PARENTHESIS ORNAMENT + 0x276C, // MEDIUM LEFT-POINTING ANGLE BRACKET ORNAMENT + 0x276D, // MEDIUM RIGHT-POINTING ANGLE BRACKET ORNAMENT + 0x276E, // HEAVY LEFT-POINTING ANGLE QUOTATION MARK ORNAMENT + 0x276F, // HEAVY RIGHT-POINTING ANGLE QUOTATION MARK ORNAMENT + 0x2770, // HEAVY LEFT-POINTING ANGLE BRACKET ORNAMENT + 0x2771, // HEAVY RIGHT-POINTING ANGLE BRACKET ORNAMENT + 0x2772, // LIGHT LEFT TORTOISE SHELL BRACKET + 0x2773, // LIGHT RIGHT TORTOISE SHELL BRACKET + 0x2774, // MEDIUM LEFT CURLY BRACKET ORNAMENT + 0x2775, // MEDIUM RIGHT CURLY BRACKET ORNAMENT + 0x27C3, // OPEN SUBSET + 0x27C4, // OPEN SUPERSET + 0x27C5, // LEFT S-SHAPED BAG DELIMITER + 0x27C6, // RIGHT S-SHAPED BAG DELIMITER + 0x27C8, // REVERSE SOLIDUS PRECEDING SUBSET + 0x27C9, // SUPERSET PRECEDING SOLIDUS + 0x27D5, // LEFT OUTER JOIN + 0x27D6, // RIGHT OUTER JOIN + 0x27DD, // LONG RIGHT TACK + 0x27DE, // LONG LEFT TACK + 0x27E2, // WHITE CONCAVE-SIDED DIAMOND WITH LEFTWARDS TICK + 0x27E3, // WHITE CONCAVE-SIDED DIAMOND WITH RIGHTWARDS TICK + 0x27E4, // WHITE SQUARE WITH LEFTWARDS TICK + 0x27E5, // WHITE SQUARE WITH RIGHTWARDS TICK + 0x27E6, // MATHEMATICAL LEFT WHITE SQUARE BRACKET + 0x27E7, // MATHEMATICAL RIGHT WHITE SQUARE BRACKET + 0x27E8, // MATHEMATICAL LEFT ANGLE BRACKET + 0x27E9, // MATHEMATICAL RIGHT ANGLE BRACKET + 0x27EA, // MATHEMATICAL LEFT DOUBLE ANGLE BRACKET + 0x27EB, // MATHEMATICAL RIGHT DOUBLE ANGLE BRACKET + 0x27EC, // MATHEMATICAL LEFT WHITE TORTOISE SHELL BRACKET + 0x27ED, // MATHEMATICAL RIGHT WHITE TORTOISE SHELL BRACKET + 0x27EE, // MATHEMATICAL LEFT FLATTENED PARENTHESIS + 0x27EF, // MATHEMATICAL RIGHT FLATTENED PARENTHESIS + 0x2983, // LEFT WHITE CURLY BRACKET + 0x2984, // RIGHT WHITE CURLY BRACKET + 0x2985, // LEFT WHITE PARENTHESIS + 0x2986, // RIGHT WHITE PARENTHESIS + 0x2987, // Z NOTATION LEFT IMAGE BRACKET + 0x2988, // Z NOTATION RIGHT IMAGE BRACKET + 0x2989, // Z NOTATION LEFT BINDING BRACKET + 0x298A, // Z NOTATION RIGHT BINDING BRACKET + 0x298B, // LEFT SQUARE BRACKET WITH UNDERBAR + 0x298C, // RIGHT SQUARE BRACKET WITH UNDERBAR + 0x298D, // LEFT SQUARE BRACKET WITH TICK IN TOP CORNER + 0x298E, // RIGHT SQUARE BRACKET WITH TICK IN BOTTOM CORNER + 0x298F, // LEFT SQUARE BRACKET WITH TICK IN BOTTOM CORNER + 0x2990, // RIGHT SQUARE BRACKET WITH TICK IN TOP CORNER + 0x2991, // LEFT ANGLE BRACKET WITH DOT + 0x2992, // RIGHT ANGLE BRACKET WITH DOT + 0x2993, // LEFT ARC LESS-THAN BRACKET + 0x2994, // RIGHT ARC GREATER-THAN BRACKET + 0x2995, // DOUBLE LEFT ARC GREATER-THAN BRACKET + 0x2996, // DOUBLE RIGHT ARC LESS-THAN BRACKET + 0x2997, // LEFT BLACK TORTOISE SHELL BRACKET + 0x2998, // RIGHT BLACK TORTOISE SHELL BRACKET + 0x29B8, // CIRCLED REVERSE SOLIDUS + 0x29C0, // CIRCLED LESS-THAN + 0x29C1, // CIRCLED GREATER-THAN + 0x29C4, // SQUARED RISING DIAGONAL SLASH + 0x29C5, // SQUARED FALLING DIAGONAL SLASH + 0x29CF, // LEFT TRIANGLE BESIDE VERTICAL BAR + 0x29D0, // VERTICAL BAR BESIDE RIGHT TRIANGLE + 0x29D1, // BOWTIE WITH LEFT HALF BLACK + 0x29D2, // BOWTIE WITH RIGHT HALF BLACK + 0x29D4, // TIMES WITH LEFT HALF BLACK + 0x29D5, // TIMES WITH RIGHT HALF BLACK + 0x29D8, // LEFT WIGGLY FENCE + 0x29D9, // RIGHT WIGGLY FENCE + 0x29DA, // LEFT DOUBLE WIGGLY FENCE + 0x29DB, // RIGHT DOUBLE WIGGLY FENCE + 0x29F5, // REVERSE SOLIDUS OPERATOR + 0x29F8, // BIG SOLIDUS + 0x29F9, // BIG REVERSE SOLIDUS + 0x29FC, // LEFT-POINTING CURVED ANGLE BRACKET + 0x29FD, // RIGHT-POINTING CURVED ANGLE BRACKET + 0x2A2B, // MINUS SIGN WITH FALLING DOTS + 0x2A2C, // MINUS SIGN WITH RISING DOTS + 0x2A2D, // PLUS SIGN IN LEFT HALF CIRCLE + 0x2A2E, // PLUS SIGN IN RIGHT HALF CIRCLE + 0x2A34, // MULTIPLICATION SIGN IN LEFT HALF CIRCLE + 0x2A35, // MULTIPLICATION SIGN IN RIGHT HALF CIRCLE + 0x2A3C, // INTERIOR PRODUCT + 0x2A3D, // RIGHTHAND INTERIOR PRODUCT + 0x2A64, // Z NOTATION DOMAIN ANTIRESTRICTION + 0x2A65, // Z NOTATION RANGE ANTIRESTRICTION + 0x2A79, // LESS-THAN WITH CIRCLE INSIDE + 0x2A7A, // GREATER-THAN WITH CIRCLE INSIDE + 0x2A7D, // LESS-THAN OR SLANTED EQUAL TO + 0x2A7E, // GREATER-THAN OR SLANTED EQUAL TO + 0x2A7F, // LESS-THAN OR SLANTED EQUAL TO WITH DOT INSIDE + 0x2A80, // GREATER-THAN OR SLANTED EQUAL TO WITH DOT INSIDE + 0x2A81, // LESS-THAN OR SLANTED EQUAL TO WITH DOT ABOVE + 0x2A82, // GREATER-THAN OR SLANTED EQUAL TO WITH DOT ABOVE + 0x2A83, // LESS-THAN OR SLANTED EQUAL TO WITH DOT ABOVE RIGHT + 0x2A84, // GREATER-THAN OR SLANTED EQUAL TO WITH DOT ABOVE LEFT + 0x2A8B, // LESS-THAN ABOVE DOUBLE-LINE EQUAL ABOVE GREATER-THAN + 0x2A8C, // GREATER-THAN ABOVE DOUBLE-LINE EQUAL ABOVE LESS-THAN + 0x2A91, // LESS-THAN ABOVE GREATER-THAN ABOVE DOUBLE-LINE EQUAL + 0x2A92, // GREATER-THAN ABOVE LESS-THAN ABOVE DOUBLE-LINE EQUAL + 0x2A93, // LESS-THAN ABOVE SLANTED EQUAL ABOVE GREATER-THAN ABOVE SLANTED EQUAL + 0x2A94, // GREATER-THAN ABOVE SLANTED EQUAL ABOVE LESS-THAN ABOVE SLANTED EQUAL + 0x2A95, // SLANTED EQUAL TO OR LESS-THAN + 0x2A96, // SLANTED EQUAL TO OR GREATER-THAN + 0x2A97, // SLANTED EQUAL TO OR LESS-THAN WITH DOT INSIDE + 0x2A98, // SLANTED EQUAL TO OR GREATER-THAN WITH DOT INSIDE + 0x2A99, // DOUBLE-LINE EQUAL TO OR LESS-THAN + 0x2A9A, // DOUBLE-LINE EQUAL TO OR GREATER-THAN + 0x2A9B, // DOUBLE-LINE SLANTED EQUAL TO OR LESS-THAN + 0x2A9C, // DOUBLE-LINE SLANTED EQUAL TO OR GREATER-THAN + 0x2AA1, // DOUBLE NESTED LESS-THAN + 0x2AA2, // DOUBLE NESTED GREATER-THAN + 0x2AA6, // LESS-THAN CLOSED BY CURVE + 0x2AA7, // GREATER-THAN CLOSED BY CURVE + 0x2AA8, // LESS-THAN CLOSED BY CURVE ABOVE SLANTED EQUAL + 0x2AA9, // GREATER-THAN CLOSED BY CURVE ABOVE SLANTED EQUAL + 0x2AAA, // SMALLER THAN + 0x2AAB, // LARGER THAN + 0x2AAC, // SMALLER THAN OR EQUAL TO + 0x2AAD, // LARGER THAN OR EQUAL TO + 0x2AAF, // PRECEDES ABOVE SINGLE-LINE EQUALS SIGN + 0x2AB0, // SUCCEEDS ABOVE SINGLE-LINE EQUALS SIGN + 0x2AB3, // PRECEDES ABOVE EQUALS SIGN + 0x2AB4, // SUCCEEDS ABOVE EQUALS SIGN + 0x2ABB, // DOUBLE PRECEDES + 0x2ABC, // DOUBLE SUCCEEDS + 0x2ABD, // SUBSET WITH DOT + 0x2ABE, // SUPERSET WITH DOT + 0x2ABF, // SUBSET WITH PLUS SIGN BELOW + 0x2AC0, // SUPERSET WITH PLUS SIGN BELOW + 0x2AC1, // SUBSET WITH MULTIPLICATION SIGN BELOW + 0x2AC2, // SUPERSET WITH MULTIPLICATION SIGN BELOW + 0x2AC3, // SUBSET OF OR EQUAL TO WITH DOT ABOVE + 0x2AC4, // SUPERSET OF OR EQUAL TO WITH DOT ABOVE + 0x2AC5, // SUBSET OF ABOVE EQUALS SIGN + 0x2AC6, // SUPERSET OF ABOVE EQUALS SIGN + 0x2ACD, // SQUARE LEFT OPEN BOX OPERATOR + 0x2ACE, // SQUARE RIGHT OPEN BOX OPERATOR + 0x2ACF, // CLOSED SUBSET + 0x2AD0, // CLOSED SUPERSET + 0x2AD1, // CLOSED SUBSET OR EQUAL TO + 0x2AD2, // CLOSED SUPERSET OR EQUAL TO + 0x2AD3, // SUBSET ABOVE SUPERSET + 0x2AD4, // SUPERSET ABOVE SUBSET + 0x2AD5, // SUBSET ABOVE SUBSET + 0x2AD6, // SUPERSET ABOVE SUPERSET + 0x2ADE, // SHORT LEFT TACK + 0x2AE3, // DOUBLE VERTICAL BAR LEFT TURNSTILE + 0x2AE4, // VERTICAL BAR DOUBLE LEFT TURNSTILE + 0x2AE5, // DOUBLE VERTICAL BAR DOUBLE LEFT TURNSTILE + 0x2AEC, // DOUBLE STROKE NOT SIGN + 0x2AED, // REVERSED DOUBLE STROKE NOT SIGN + 0x2AF7, // TRIPLE NESTED LESS-THAN + 0x2AF8, // TRIPLE NESTED GREATER-THAN + 0x2AF9, // DOUBLE-LINE SLANTED LESS-THAN OR EQUAL TO + 0x2AFA, // DOUBLE-LINE SLANTED GREATER-THAN OR EQUAL TO + 0x2E02, // LEFT SUBSTITUTION BRACKET + 0x2E03, // RIGHT SUBSTITUTION BRACKET + 0x2E04, // LEFT DOTTED SUBSTITUTION BRACKET + 0x2E05, // RIGHT DOTTED SUBSTITUTION BRACKET + 0x2E09, // LEFT TRANSPOSITION BRACKET + 0x2E0A, // RIGHT TRANSPOSITION BRACKET + 0x2E0C, // LEFT RAISED OMISSION BRACKET + 0x2E0D, // RIGHT RAISED OMISSION BRACKET + 0x2E1C, // LEFT LOW PARAPHRASE BRACKET + 0x2E1D, // RIGHT LOW PARAPHRASE BRACKET + 0x2E20, // LEFT VERTICAL BAR WITH QUILL + 0x2E21, // RIGHT VERTICAL BAR WITH QUILL + 0x2E22, // TOP LEFT HALF BRACKET + 0x2E23, // TOP RIGHT HALF BRACKET + 0x2E24, // BOTTOM LEFT HALF BRACKET + 0x2E25, // BOTTOM RIGHT HALF BRACKET + 0x2E26, // LEFT SIDEWAYS U BRACKET + 0x2E27, // RIGHT SIDEWAYS U BRACKET + 0x2E28, // LEFT DOUBLE PARENTHESIS + 0x2E29, // RIGHT DOUBLE PARENTHESIS + 0x3008, // LEFT ANGLE BRACKET + 0x3009, // RIGHT ANGLE BRACKET + 0x300A, // LEFT DOUBLE ANGLE BRACKET + 0x300B, // RIGHT DOUBLE ANGLE BRACKET + 0x300C, // LEFT CORNER BRACKET + 0x300D, // RIGHT CORNER BRACKET + 0x300E, // LEFT WHITE CORNER BRACKET + 0x300F, // RIGHT WHITE CORNER BRACKET + 0x3010, // LEFT BLACK LENTICULAR BRACKET + 0x3011, // RIGHT BLACK LENTICULAR BRACKET + 0x3014, // LEFT TORTOISE SHELL BRACKET + 0x3015, // RIGHT TORTOISE SHELL BRACKET + 0x3016, // LEFT WHITE LENTICULAR BRACKET + 0x3017, // RIGHT WHITE LENTICULAR BRACKET + 0x3018, // LEFT WHITE TORTOISE SHELL BRACKET + 0x3019, // RIGHT WHITE TORTOISE SHELL BRACKET + 0x301A, // LEFT WHITE SQUARE BRACKET + 0x301B, // RIGHT WHITE SQUARE BRACKET + 0xFE59, // SMALL LEFT PARENTHESIS + 0xFE5A, // SMALL RIGHT PARENTHESIS + 0xFE5B, // SMALL LEFT CURLY BRACKET + 0xFE5C, // SMALL RIGHT CURLY BRACKET + 0xFE5D, // SMALL LEFT TORTOISE SHELL BRACKET + 0xFE5E, // SMALL RIGHT TORTOISE SHELL BRACKET + 0xFE64, // SMALL LESS-THAN SIGN + 0xFE65, // SMALL GREATER-THAN SIGN + 0xFF08, // FULLWIDTH LEFT PARENTHESIS + 0xFF09, // FULLWIDTH RIGHT PARENTHESIS + 0xFF1C, // FULLWIDTH LESS-THAN SIGN + 0xFF1E, // FULLWIDTH GREATER-THAN SIGN + 0xFF3B, // FULLWIDTH LEFT SQUARE BRACKET + 0xFF3D, // FULLWIDTH RIGHT SQUARE BRACKET + 0xFF5B, // FULLWIDTH LEFT CURLY BRACKET + 0xFF5D, // FULLWIDTH RIGHT CURLY BRACKET + 0xFF5F, // FULLWIDTH LEFT WHITE PARENTHESIS + 0xFF60, // FULLWIDTH RIGHT WHITE PARENTHESIS + 0xFF62, // HALFWIDTH LEFT CORNER BRACKET + 0xFF63, // HALFWIDTH RIGHT CORNER BRACKET + }; + + private static final int[] mirVal = new int[] { + 0x0029, // MIRROR(LEFT PARENTHESIS) + 0x0028, // MIRROR(RIGHT PARENTHESIS) + 0x003E, // MIRROR(LESS-THAN SIGN) + 0x003C, // MIRROR(GREATER-THAN SIGN) + 0x005D, // MIRROR(LEFT SQUARE BRACKET) + 0x005B, // MIRROR(RIGHT SQUARE BRACKET) + 0x007D, // MIRROR(LEFT CURLY BRACKET) + 0x007B, // MIRROR(RIGHT CURLY BRACKET) + 0x00BB, // MIRROR(LEFT-POINTING DOUBLE ANGLE QUOTATION MARK) + 0x00AB, // MIRROR(RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK) + 0x0F3B, // MIRROR(TIBETAN MARK GUG RTAGS GYON) + 0x0F3A, // MIRROR(TIBETAN MARK GUG RTAGS GYAS) + 0x0F3D, // MIRROR(TIBETAN MARK ANG KHANG GYON) + 0x0F3C, // MIRROR(TIBETAN MARK ANG KHANG GYAS) + 0x169C, // MIRROR(OGHAM FEATHER MARK) + 0x169B, // MIRROR(OGHAM REVERSED FEATHER MARK) + 0x203A, // MIRROR(SINGLE LEFT-POINTING ANGLE QUOTATION MARK) + 0x2039, // MIRROR(SINGLE RIGHT-POINTING ANGLE QUOTATION MARK) + 0x2046, // MIRROR(LEFT SQUARE BRACKET WITH QUILL) + 0x2045, // MIRROR(RIGHT SQUARE BRACKET WITH QUILL) + 0x207E, // MIRROR(SUPERSCRIPT LEFT PARENTHESIS) + 0x207D, // MIRROR(SUPERSCRIPT RIGHT PARENTHESIS) + 0x208E, // MIRROR(SUBSCRIPT LEFT PARENTHESIS) + 0x208D, // MIRROR(SUBSCRIPT RIGHT PARENTHESIS) + 0x220B, // MIRROR(ELEMENT OF) + 0x220C, // MIRROR(NOT AN ELEMENT OF) + 0x220D, // MIRROR(SMALL ELEMENT OF) + 0x2208, // MIRROR(CONTAINS AS MEMBER) + 0x2209, // MIRROR(DOES NOT CONTAIN AS MEMBER) + 0x220A, // MIRROR(SMALL CONTAINS AS MEMBER) + 0x29F5, // MIRROR(DIVISION SLASH) + 0x223D, // MIRROR(TILDE OPERATOR) + 0x223C, // MIRROR(REVERSED TILDE) + 0x22CD, // MIRROR(ASYMPTOTICALLY EQUAL TO) + 0x2253, // MIRROR(APPROXIMATELY EQUAL TO OR THE IMAGE OF) + 0x2252, // MIRROR(IMAGE OF OR APPROXIMATELY EQUAL TO) + 0x2255, // MIRROR(COLON EQUALS) + 0x2254, // MIRROR(EQUALS COLON) + 0x2265, // MIRROR(LESS-THAN OR EQUAL TO) + 0x2264, // MIRROR(GREATER-THAN OR EQUAL TO) + 0x2267, // MIRROR(LESS-THAN OVER EQUAL TO) + 0x2266, // MIRROR(GREATER-THAN OVER EQUAL TO) + 0x2269, // MIRROR(LESS-THAN BUT NOT EQUAL TO) + 0x2268, // MIRROR(GREATER-THAN BUT NOT EQUAL TO) + 0x226B, // MIRROR(MUCH LESS-THAN) + 0x226A, // MIRROR(MUCH GREATER-THAN) + 0x226F, // MIRROR(NOT LESS-THAN) + 0x226E, // MIRROR(NOT GREATER-THAN) + 0x2271, // MIRROR(NEITHER LESS-THAN NOR EQUAL TO) + 0x2270, // MIRROR(NEITHER GREATER-THAN NOR EQUAL TO) + 0x2273, // MIRROR(LESS-THAN OR EQUIVALENT TO) + 0x2272, // MIRROR(GREATER-THAN OR EQUIVALENT TO) + 0x2275, // MIRROR(NEITHER LESS-THAN NOR EQUIVALENT TO) + 0x2274, // MIRROR(NEITHER GREATER-THAN NOR EQUIVALENT TO) + 0x2277, // MIRROR(LESS-THAN OR GREATER-THAN) + 0x2276, // MIRROR(GREATER-THAN OR LESS-THAN) + 0x2279, // MIRROR(NEITHER LESS-THAN NOR GREATER-THAN) + 0x2278, // MIRROR(NEITHER GREATER-THAN NOR LESS-THAN) + 0x227B, // MIRROR(PRECEDES) + 0x227A, // MIRROR(SUCCEEDS) + 0x227D, // MIRROR(PRECEDES OR EQUAL TO) + 0x227C, // MIRROR(SUCCEEDS OR EQUAL TO) + 0x227F, // MIRROR(PRECEDES OR EQUIVALENT TO) + 0x227E, // MIRROR(SUCCEEDS OR EQUIVALENT TO) + 0x2281, // MIRROR(DOES NOT PRECEDE) + 0x2280, // MIRROR(DOES NOT SUCCEED) + 0x2283, // MIRROR(SUBSET OF) + 0x2282, // MIRROR(SUPERSET OF) + 0x2285, // MIRROR(NOT A SUBSET OF) + 0x2284, // MIRROR(NOT A SUPERSET OF) + 0x2287, // MIRROR(SUBSET OF OR EQUAL TO) + 0x2286, // MIRROR(SUPERSET OF OR EQUAL TO) + 0x2289, // MIRROR(NEITHER A SUBSET OF NOR EQUAL TO) + 0x2288, // MIRROR(NEITHER A SUPERSET OF NOR EQUAL TO) + 0x228B, // MIRROR(SUBSET OF WITH NOT EQUAL TO) + 0x228A, // MIRROR(SUPERSET OF WITH NOT EQUAL TO) + 0x2290, // MIRROR(SQUARE IMAGE OF) + 0x228F, // MIRROR(SQUARE ORIGINAL OF) + 0x2292, // MIRROR(SQUARE IMAGE OF OR EQUAL TO) + 0x2291, // MIRROR(SQUARE ORIGINAL OF OR EQUAL TO) + 0x29B8, // MIRROR(CIRCLED DIVISION SLASH) + 0x22A3, // MIRROR(RIGHT TACK) + 0x22A2, // MIRROR(LEFT TACK) + 0x2ADE, // MIRROR(ASSERTION) + 0x2AE4, // MIRROR(TRUE) + 0x2AE3, // MIRROR(FORCES) + 0x2AE5, // MIRROR(DOUBLE VERTICAL BAR DOUBLE RIGHT TURNSTILE) + 0x22B1, // MIRROR(PRECEDES UNDER RELATION) + 0x22B0, // MIRROR(SUCCEEDS UNDER RELATION) + 0x22B3, // MIRROR(NORMAL SUBGROUP OF) + 0x22B2, // MIRROR(CONTAINS AS NORMAL SUBGROUP) + 0x22B5, // MIRROR(NORMAL SUBGROUP OF OR EQUAL TO) + 0x22B4, // MIRROR(CONTAINS AS NORMAL SUBGROUP OR EQUAL TO) + 0x22B7, // MIRROR(ORIGINAL OF) + 0x22B6, // MIRROR(IMAGE OF) + 0x22CA, // MIRROR(LEFT NORMAL FACTOR SEMIDIRECT PRODUCT) + 0x22C9, // MIRROR(RIGHT NORMAL FACTOR SEMIDIRECT PRODUCT) + 0x22CC, // MIRROR(LEFT SEMIDIRECT PRODUCT) + 0x22CB, // MIRROR(RIGHT SEMIDIRECT PRODUCT) + 0x2243, // MIRROR(REVERSED TILDE EQUALS) + 0x22D1, // MIRROR(DOUBLE SUBSET) + 0x22D0, // MIRROR(DOUBLE SUPERSET) + 0x22D7, // MIRROR(LESS-THAN WITH DOT) + 0x22D6, // MIRROR(GREATER-THAN WITH DOT) + 0x22D9, // MIRROR(VERY MUCH LESS-THAN) + 0x22D8, // MIRROR(VERY MUCH GREATER-THAN) + 0x22DB, // MIRROR(LESS-THAN EQUAL TO OR GREATER-THAN) + 0x22DA, // MIRROR(GREATER-THAN EQUAL TO OR LESS-THAN) + 0x22DD, // MIRROR(EQUAL TO OR LESS-THAN) + 0x22DC, // MIRROR(EQUAL TO OR GREATER-THAN) + 0x22DF, // MIRROR(EQUAL TO OR PRECEDES) + 0x22DE, // MIRROR(EQUAL TO OR SUCCEEDS) + 0x22E1, // MIRROR(DOES NOT PRECEDE OR EQUAL) + 0x22E0, // MIRROR(DOES NOT SUCCEED OR EQUAL) + 0x22E3, // MIRROR(NOT SQUARE IMAGE OF OR EQUAL TO) + 0x22E2, // MIRROR(NOT SQUARE ORIGINAL OF OR EQUAL TO) + 0x22E5, // MIRROR(SQUARE IMAGE OF OR NOT EQUAL TO) + 0x22E4, // MIRROR(SQUARE ORIGINAL OF OR NOT EQUAL TO) + 0x22E7, // MIRROR(LESS-THAN BUT NOT EQUIVALENT TO) + 0x22E6, // MIRROR(GREATER-THAN BUT NOT EQUIVALENT TO) + 0x22E9, // MIRROR(PRECEDES BUT NOT EQUIVALENT TO) + 0x22E8, // MIRROR(SUCCEEDS BUT NOT EQUIVALENT TO) + 0x22EB, // MIRROR(NOT NORMAL SUBGROUP OF) + 0x22EA, // MIRROR(DOES NOT CONTAIN AS NORMAL SUBGROUP) + 0x22ED, // MIRROR(NOT NORMAL SUBGROUP OF OR EQUAL TO) + 0x22EC, // MIRROR(DOES NOT CONTAIN AS NORMAL SUBGROUP OR EQUAL) + 0x22F1, // MIRROR(UP RIGHT DIAGONAL ELLIPSIS) + 0x22F0, // MIRROR(DOWN RIGHT DIAGONAL ELLIPSIS) + 0x22FA, // MIRROR(ELEMENT OF WITH LONG HORIZONTAL STROKE) + 0x22FB, // MIRROR(ELEMENT OF WITH VERTICAL BAR AT END OF HORIZONTAL STROKE) + 0x22FC, // MIRROR(SMALL ELEMENT OF WITH VERTICAL BAR AT END OF HORIZONTAL STROKE) + 0x22FD, // MIRROR(ELEMENT OF WITH OVERBAR) + 0x22FE, // MIRROR(SMALL ELEMENT OF WITH OVERBAR) + 0x22F2, // MIRROR(CONTAINS WITH LONG HORIZONTAL STROKE) + 0x22F3, // MIRROR(CONTAINS WITH VERTICAL BAR AT END OF HORIZONTAL STROKE) + 0x22F4, // MIRROR(SMALL CONTAINS WITH VERTICAL BAR AT END OF HORIZONTAL STROKE) + 0x22F6, // MIRROR(CONTAINS WITH OVERBAR) + 0x22F7, // MIRROR(SMALL CONTAINS WITH OVERBAR) + 0x2309, // MIRROR(LEFT CEILING) + 0x2308, // MIRROR(RIGHT CEILING) + 0x230B, // MIRROR(LEFT FLOOR) + 0x230A, // MIRROR(RIGHT FLOOR) + 0x232A, // MIRROR(LEFT-POINTING ANGLE BRACKET) + 0x2329, // MIRROR(RIGHT-POINTING ANGLE BRACKET) + 0x2769, // MIRROR(MEDIUM LEFT PARENTHESIS ORNAMENT) + 0x2768, // MIRROR(MEDIUM RIGHT PARENTHESIS ORNAMENT) + 0x276B, // MIRROR(MEDIUM FLATTENED LEFT PARENTHESIS ORNAMENT) + 0x276A, // MIRROR(MEDIUM FLATTENED RIGHT PARENTHESIS ORNAMENT) + 0x276D, // MIRROR(MEDIUM LEFT-POINTING ANGLE BRACKET ORNAMENT) + 0x276C, // MIRROR(MEDIUM RIGHT-POINTING ANGLE BRACKET ORNAMENT) + 0x276F, // MIRROR(HEAVY LEFT-POINTING ANGLE QUOTATION MARK ORNAMENT) + 0x276E, // MIRROR(HEAVY RIGHT-POINTING ANGLE QUOTATION MARK ORNAMENT) + 0x2771, // MIRROR(HEAVY LEFT-POINTING ANGLE BRACKET ORNAMENT) + 0x2770, // MIRROR(HEAVY RIGHT-POINTING ANGLE BRACKET ORNAMENT) + 0x2773, // MIRROR(LIGHT LEFT TORTOISE SHELL BRACKET) + 0x2772, // MIRROR(LIGHT RIGHT TORTOISE SHELL BRACKET) + 0x2775, // MIRROR(MEDIUM LEFT CURLY BRACKET ORNAMENT) + 0x2774, // MIRROR(MEDIUM RIGHT CURLY BRACKET ORNAMENT) + 0x27C4, // MIRROR(OPEN SUBSET) + 0x27C3, // MIRROR(OPEN SUPERSET) + 0x27C6, // MIRROR(LEFT S-SHAPED BAG DELIMITER) + 0x27C5, // MIRROR(RIGHT S-SHAPED BAG DELIMITER) + 0x27C9, // MIRROR(REVERSE SOLIDUS PRECEDING SUBSET) + 0x27C8, // MIRROR(SUPERSET PRECEDING SOLIDUS) + 0x27D6, // MIRROR(LEFT OUTER JOIN) + 0x27D5, // MIRROR(RIGHT OUTER JOIN) + 0x27DE, // MIRROR(LONG RIGHT TACK) + 0x27DD, // MIRROR(LONG LEFT TACK) + 0x27E3, // MIRROR(WHITE CONCAVE-SIDED DIAMOND WITH LEFTWARDS TICK) + 0x27E2, // MIRROR(WHITE CONCAVE-SIDED DIAMOND WITH RIGHTWARDS TICK) + 0x27E5, // MIRROR(WHITE SQUARE WITH LEFTWARDS TICK) + 0x27E4, // MIRROR(WHITE SQUARE WITH RIGHTWARDS TICK) + 0x27E7, // MIRROR(MATHEMATICAL LEFT WHITE SQUARE BRACKET) + 0x27E6, // MIRROR(MATHEMATICAL RIGHT WHITE SQUARE BRACKET) + 0x27E9, // MIRROR(MATHEMATICAL LEFT ANGLE BRACKET) + 0x27E8, // MIRROR(MATHEMATICAL RIGHT ANGLE BRACKET) + 0x27EB, // MIRROR(MATHEMATICAL LEFT DOUBLE ANGLE BRACKET) + 0x27EA, // MIRROR(MATHEMATICAL RIGHT DOUBLE ANGLE BRACKET) + 0x27ED, // MIRROR(MATHEMATICAL LEFT WHITE TORTOISE SHELL BRACKET) + 0x27EC, // MIRROR(MATHEMATICAL RIGHT WHITE TORTOISE SHELL BRACKET) + 0x27EF, // MIRROR(MATHEMATICAL LEFT FLATTENED PARENTHESIS) + 0x27EE, // MIRROR(MATHEMATICAL RIGHT FLATTENED PARENTHESIS) + 0x2984, // MIRROR(LEFT WHITE CURLY BRACKET) + 0x2983, // MIRROR(RIGHT WHITE CURLY BRACKET) + 0x2986, // MIRROR(LEFT WHITE PARENTHESIS) + 0x2985, // MIRROR(RIGHT WHITE PARENTHESIS) + 0x2988, // MIRROR(Z NOTATION LEFT IMAGE BRACKET) + 0x2987, // MIRROR(Z NOTATION RIGHT IMAGE BRACKET) + 0x298A, // MIRROR(Z NOTATION LEFT BINDING BRACKET) + 0x2989, // MIRROR(Z NOTATION RIGHT BINDING BRACKET) + 0x298C, // MIRROR(LEFT SQUARE BRACKET WITH UNDERBAR) + 0x298B, // MIRROR(RIGHT SQUARE BRACKET WITH UNDERBAR) + 0x2990, // MIRROR(LEFT SQUARE BRACKET WITH TICK IN TOP CORNER) + 0x298F, // MIRROR(RIGHT SQUARE BRACKET WITH TICK IN BOTTOM CORNER) + 0x298E, // MIRROR(LEFT SQUARE BRACKET WITH TICK IN BOTTOM CORNER) + 0x298D, // MIRROR(RIGHT SQUARE BRACKET WITH TICK IN TOP CORNER) + 0x2992, // MIRROR(LEFT ANGLE BRACKET WITH DOT) + 0x2991, // MIRROR(RIGHT ANGLE BRACKET WITH DOT) + 0x2994, // MIRROR(LEFT ARC LESS-THAN BRACKET) + 0x2993, // MIRROR(RIGHT ARC GREATER-THAN BRACKET) + 0x2996, // MIRROR(DOUBLE LEFT ARC GREATER-THAN BRACKET) + 0x2995, // MIRROR(DOUBLE RIGHT ARC LESS-THAN BRACKET) + 0x2998, // MIRROR(LEFT BLACK TORTOISE SHELL BRACKET) + 0x2997, // MIRROR(RIGHT BLACK TORTOISE SHELL BRACKET) + 0x2298, // MIRROR(CIRCLED REVERSE SOLIDUS) + 0x29C1, // MIRROR(CIRCLED LESS-THAN) + 0x29C0, // MIRROR(CIRCLED GREATER-THAN) + 0x29C5, // MIRROR(SQUARED RISING DIAGONAL SLASH) + 0x29C4, // MIRROR(SQUARED FALLING DIAGONAL SLASH) + 0x29D0, // MIRROR(LEFT TRIANGLE BESIDE VERTICAL BAR) + 0x29CF, // MIRROR(VERTICAL BAR BESIDE RIGHT TRIANGLE) + 0x29D2, // MIRROR(BOWTIE WITH LEFT HALF BLACK) + 0x29D1, // MIRROR(BOWTIE WITH RIGHT HALF BLACK) + 0x29D5, // MIRROR(TIMES WITH LEFT HALF BLACK) + 0x29D4, // MIRROR(TIMES WITH RIGHT HALF BLACK) + 0x29D9, // MIRROR(LEFT WIGGLY FENCE) + 0x29D8, // MIRROR(RIGHT WIGGLY FENCE) + 0x29DB, // MIRROR(LEFT DOUBLE WIGGLY FENCE) + 0x29DA, // MIRROR(RIGHT DOUBLE WIGGLY FENCE) + 0x2215, // MIRROR(REVERSE SOLIDUS OPERATOR) + 0x29F9, // MIRROR(BIG SOLIDUS) + 0x29F8, // MIRROR(BIG REVERSE SOLIDUS) + 0x29FD, // MIRROR(LEFT-POINTING CURVED ANGLE BRACKET) + 0x29FC, // MIRROR(RIGHT-POINTING CURVED ANGLE BRACKET) + 0x2A2C, // MIRROR(MINUS SIGN WITH FALLING DOTS) + 0x2A2B, // MIRROR(MINUS SIGN WITH RISING DOTS) + 0x2A2E, // MIRROR(PLUS SIGN IN LEFT HALF CIRCLE) + 0x2A2D, // MIRROR(PLUS SIGN IN RIGHT HALF CIRCLE) + 0x2A35, // MIRROR(MULTIPLICATION SIGN IN LEFT HALF CIRCLE) + 0x2A34, // MIRROR(MULTIPLICATION SIGN IN RIGHT HALF CIRCLE) + 0x2A3D, // MIRROR(INTERIOR PRODUCT) + 0x2A3C, // MIRROR(RIGHTHAND INTERIOR PRODUCT) + 0x2A65, // MIRROR(Z NOTATION DOMAIN ANTIRESTRICTION) + 0x2A64, // MIRROR(Z NOTATION RANGE ANTIRESTRICTION) + 0x2A7A, // MIRROR(LESS-THAN WITH CIRCLE INSIDE) + 0x2A79, // MIRROR(GREATER-THAN WITH CIRCLE INSIDE) + 0x2A7E, // MIRROR(LESS-THAN OR SLANTED EQUAL TO) + 0x2A7D, // MIRROR(GREATER-THAN OR SLANTED EQUAL TO) + 0x2A80, // MIRROR(LESS-THAN OR SLANTED EQUAL TO WITH DOT INSIDE) + 0x2A7F, // MIRROR(GREATER-THAN OR SLANTED EQUAL TO WITH DOT INSIDE) + 0x2A82, // MIRROR(LESS-THAN OR SLANTED EQUAL TO WITH DOT ABOVE) + 0x2A81, // MIRROR(GREATER-THAN OR SLANTED EQUAL TO WITH DOT ABOVE) + 0x2A84, // MIRROR(LESS-THAN OR SLANTED EQUAL TO WITH DOT ABOVE RIGHT) + 0x2A83, // MIRROR(GREATER-THAN OR SLANTED EQUAL TO WITH DOT ABOVE LEFT) + 0x2A8C, // MIRROR(LESS-THAN ABOVE DOUBLE-LINE EQUAL ABOVE GREATER-THAN) + 0x2A8B, // MIRROR(GREATER-THAN ABOVE DOUBLE-LINE EQUAL ABOVE LESS-THAN) + 0x2A92, // MIRROR(LESS-THAN ABOVE GREATER-THAN ABOVE DOUBLE-LINE EQUAL) + 0x2A91, // MIRROR(GREATER-THAN ABOVE LESS-THAN ABOVE DOUBLE-LINE EQUAL) + 0x2A94, // MIRROR(LESS-THAN ABOVE SLANTED EQUAL ABOVE GREATER-THAN ABOVE SLANTED EQUAL) + 0x2A93, // MIRROR(GREATER-THAN ABOVE SLANTED EQUAL ABOVE LESS-THAN ABOVE SLANTED EQUAL) + 0x2A96, // MIRROR(SLANTED EQUAL TO OR LESS-THAN) + 0x2A95, // MIRROR(SLANTED EQUAL TO OR GREATER-THAN) + 0x2A98, // MIRROR(SLANTED EQUAL TO OR LESS-THAN WITH DOT INSIDE) + 0x2A97, // MIRROR(SLANTED EQUAL TO OR GREATER-THAN WITH DOT INSIDE) + 0x2A9A, // MIRROR(DOUBLE-LINE EQUAL TO OR LESS-THAN) + 0x2A99, // MIRROR(DOUBLE-LINE EQUAL TO OR GREATER-THAN) + 0x2A9C, // MIRROR(DOUBLE-LINE SLANTED EQUAL TO OR LESS-THAN) + 0x2A9B, // MIRROR(DOUBLE-LINE SLANTED EQUAL TO OR GREATER-THAN) + 0x2AA2, // MIRROR(DOUBLE NESTED LESS-THAN) + 0x2AA1, // MIRROR(DOUBLE NESTED GREATER-THAN) + 0x2AA7, // MIRROR(LESS-THAN CLOSED BY CURVE) + 0x2AA6, // MIRROR(GREATER-THAN CLOSED BY CURVE) + 0x2AA9, // MIRROR(LESS-THAN CLOSED BY CURVE ABOVE SLANTED EQUAL) + 0x2AA8, // MIRROR(GREATER-THAN CLOSED BY CURVE ABOVE SLANTED EQUAL) + 0x2AAB, // MIRROR(SMALLER THAN) + 0x2AAA, // MIRROR(LARGER THAN) + 0x2AAD, // MIRROR(SMALLER THAN OR EQUAL TO) + 0x2AAC, // MIRROR(LARGER THAN OR EQUAL TO) + 0x2AB0, // MIRROR(PRECEDES ABOVE SINGLE-LINE EQUALS SIGN) + 0x2AAF, // MIRROR(SUCCEEDS ABOVE SINGLE-LINE EQUALS SIGN) + 0x2AB4, // MIRROR(PRECEDES ABOVE EQUALS SIGN) + 0x2AB3, // MIRROR(SUCCEEDS ABOVE EQUALS SIGN) + 0x2ABC, // MIRROR(DOUBLE PRECEDES) + 0x2ABB, // MIRROR(DOUBLE SUCCEEDS) + 0x2ABE, // MIRROR(SUBSET WITH DOT) + 0x2ABD, // MIRROR(SUPERSET WITH DOT) + 0x2AC0, // MIRROR(SUBSET WITH PLUS SIGN BELOW) + 0x2ABF, // MIRROR(SUPERSET WITH PLUS SIGN BELOW) + 0x2AC2, // MIRROR(SUBSET WITH MULTIPLICATION SIGN BELOW) + 0x2AC1, // MIRROR(SUPERSET WITH MULTIPLICATION SIGN BELOW) + 0x2AC4, // MIRROR(SUBSET OF OR EQUAL TO WITH DOT ABOVE) + 0x2AC3, // MIRROR(SUPERSET OF OR EQUAL TO WITH DOT ABOVE) + 0x2AC6, // MIRROR(SUBSET OF ABOVE EQUALS SIGN) + 0x2AC5, // MIRROR(SUPERSET OF ABOVE EQUALS SIGN) + 0x2ACE, // MIRROR(SQUARE LEFT OPEN BOX OPERATOR) + 0x2ACD, // MIRROR(SQUARE RIGHT OPEN BOX OPERATOR) + 0x2AD0, // MIRROR(CLOSED SUBSET) + 0x2ACF, // MIRROR(CLOSED SUPERSET) + 0x2AD2, // MIRROR(CLOSED SUBSET OR EQUAL TO) + 0x2AD1, // MIRROR(CLOSED SUPERSET OR EQUAL TO) + 0x2AD4, // MIRROR(SUBSET ABOVE SUPERSET) + 0x2AD3, // MIRROR(SUPERSET ABOVE SUBSET) + 0x2AD6, // MIRROR(SUBSET ABOVE SUBSET) + 0x2AD5, // MIRROR(SUPERSET ABOVE SUPERSET) + 0x22A6, // MIRROR(SHORT LEFT TACK) + 0x22A9, // MIRROR(DOUBLE VERTICAL BAR LEFT TURNSTILE) + 0x22A8, // MIRROR(VERTICAL BAR DOUBLE LEFT TURNSTILE) + 0x22AB, // MIRROR(DOUBLE VERTICAL BAR DOUBLE LEFT TURNSTILE) + 0x2AED, // MIRROR(DOUBLE STROKE NOT SIGN) + 0x2AEC, // MIRROR(REVERSED DOUBLE STROKE NOT SIGN) + 0x2AF8, // MIRROR(TRIPLE NESTED LESS-THAN) + 0x2AF7, // MIRROR(TRIPLE NESTED GREATER-THAN) + 0x2AFA, // MIRROR(DOUBLE-LINE SLANTED LESS-THAN OR EQUAL TO) + 0x2AF9, // MIRROR(DOUBLE-LINE SLANTED GREATER-THAN OR EQUAL TO) + 0x2E03, // MIRROR(LEFT SUBSTITUTION BRACKET) + 0x2E02, // MIRROR(RIGHT SUBSTITUTION BRACKET) + 0x2E05, // MIRROR(LEFT DOTTED SUBSTITUTION BRACKET) + 0x2E04, // MIRROR(RIGHT DOTTED SUBSTITUTION BRACKET) + 0x2E0A, // MIRROR(LEFT TRANSPOSITION BRACKET) + 0x2E09, // MIRROR(RIGHT TRANSPOSITION BRACKET) + 0x2E0D, // MIRROR(LEFT RAISED OMISSION BRACKET) + 0x2E0C, // MIRROR(RIGHT RAISED OMISSION BRACKET) + 0x2E1D, // MIRROR(LEFT LOW PARAPHRASE BRACKET) + 0x2E1C, // MIRROR(RIGHT LOW PARAPHRASE BRACKET) + 0x2E21, // MIRROR(LEFT VERTICAL BAR WITH QUILL) + 0x2E20, // MIRROR(RIGHT VERTICAL BAR WITH QUILL) + 0x2E23, // MIRROR(TOP LEFT HALF BRACKET) + 0x2E22, // MIRROR(TOP RIGHT HALF BRACKET) + 0x2E25, // MIRROR(BOTTOM LEFT HALF BRACKET) + 0x2E24, // MIRROR(BOTTOM RIGHT HALF BRACKET) + 0x2E27, // MIRROR(LEFT SIDEWAYS U BRACKET) + 0x2E26, // MIRROR(RIGHT SIDEWAYS U BRACKET) + 0x2E29, // MIRROR(LEFT DOUBLE PARENTHESIS) + 0x2E28, // MIRROR(RIGHT DOUBLE PARENTHESIS) + 0x3009, // MIRROR(LEFT ANGLE BRACKET) + 0x3008, // MIRROR(RIGHT ANGLE BRACKET) + 0x300B, // MIRROR(LEFT DOUBLE ANGLE BRACKET) + 0x300A, // MIRROR(RIGHT DOUBLE ANGLE BRACKET) + 0x300D, // MIRROR(LEFT CORNER BRACKET) + 0x300C, // MIRROR(RIGHT CORNER BRACKET) + 0x300F, // MIRROR(LEFT WHITE CORNER BRACKET) + 0x300E, // MIRROR(RIGHT WHITE CORNER BRACKET) + 0x3011, // MIRROR(LEFT BLACK LENTICULAR BRACKET) + 0x3010, // MIRROR(RIGHT BLACK LENTICULAR BRACKET) + 0x3015, // MIRROR(LEFT TORTOISE SHELL BRACKET) + 0x3014, // MIRROR(RIGHT TORTOISE SHELL BRACKET) + 0x3017, // MIRROR(LEFT WHITE LENTICULAR BRACKET) + 0x3016, // MIRROR(RIGHT WHITE LENTICULAR BRACKET) + 0x3019, // MIRROR(LEFT WHITE TORTOISE SHELL BRACKET) + 0x3018, // MIRROR(RIGHT WHITE TORTOISE SHELL BRACKET) + 0x301B, // MIRROR(LEFT WHITE SQUARE BRACKET) + 0x301A, // MIRROR(RIGHT WHITE SQUARE BRACKET) + 0xFE5A, // MIRROR(SMALL LEFT PARENTHESIS) + 0xFE59, // MIRROR(SMALL RIGHT PARENTHESIS) + 0xFE5C, // MIRROR(SMALL LEFT CURLY BRACKET) + 0xFE5B, // MIRROR(SMALL RIGHT CURLY BRACKET) + 0xFE5E, // MIRROR(SMALL LEFT TORTOISE SHELL BRACKET) + 0xFE5D, // MIRROR(SMALL RIGHT TORTOISE SHELL BRACKET) + 0xFE65, // MIRROR(SMALL LESS-THAN SIGN) + 0xFE64, // MIRROR(SMALL GREATER-THAN SIGN) + 0xFF09, // MIRROR(FULLWIDTH LEFT PARENTHESIS) + 0xFF08, // MIRROR(FULLWIDTH RIGHT PARENTHESIS) + 0xFF1E, // MIRROR(FULLWIDTH LESS-THAN SIGN) + 0xFF1C, // MIRROR(FULLWIDTH GREATER-THAN SIGN) + 0xFF3D, // MIRROR(FULLWIDTH LEFT SQUARE BRACKET) + 0xFF3B, // MIRROR(FULLWIDTH RIGHT SQUARE BRACKET) + 0xFF5D, // MIRROR(FULLWIDTH LEFT CURLY BRACKET) + 0xFF5B, // MIRROR(FULLWIDTH RIGHT CURLY BRACKET) + 0xFF60, // MIRROR(FULLWIDTH LEFT WHITE PARENTHESIS) + 0xFF5F, // MIRROR(FULLWIDTH RIGHT WHITE PARENTHESIS) + 0xFF63, // MIRROR(HALFWIDTH LEFT CORNER BRACKET) + 0xFF62, // MIRROR(HALFWIDTH RIGHT CORNER BRACKET) + }; + + public static boolean hasMirror(int c) { + return Arrays.binarySearch(mirKey, c) >= 0; + } + + public static int toMirror(int c) { + int k = Arrays.binarySearch(mirKey, c); + if (k >= 0) + return mirVal[k]; + else + return c; + } + +} diff --git a/fop-core/src/main/java/org/apache/fop/complexscripts/util/JapaneseToNumbers.java b/fop-core/src/main/java/org/apache/fop/complexscripts/util/JapaneseToNumbers.java new file mode 100644 index 00000000000..189b4f04a88 --- /dev/null +++ b/fop-core/src/main/java/org/apache/fop/complexscripts/util/JapaneseToNumbers.java @@ -0,0 +1,156 @@ +package org.apache.fop.complexscripts.util; + +// From https://github.com/joumorisu/SuuKotoba/blob/master/src/SuuKotoba.java + +import java.math.BigInteger; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * Class containing static utility functions that allow for the conversion of (Arabic) numerals to Japanese kanji form + * Note that {@link BigInteger} is used to model the number - as the function handles numbers (0, 9.999 * 10^15]. + * + * @author Joseph Morris + * @version 1.0 + */ + +public class JapaneseToNumbers { + + final private static String[] NUMERALS_KANJI = new String[] {"","一","二","三","四","五","六","七","八","九","十"}; + + private static Map placeValues; + static { + // LinkedHashMap used to preserve order + placeValues = new LinkedHashMap(); + placeValues.put(new BigInteger("1000000000000"), "兆"); + placeValues.put(new BigInteger("100000000"), "億"); + placeValues.put(new BigInteger("10000"), "万"); + placeValues.put(new BigInteger("1000"), "千"); + placeValues.put(new BigInteger("100"), "百"); + placeValues.put(new BigInteger("10"), "十"); + }; + + /** + * Constructor. + * + * @param num String form of the numeral to be converted + */ + public static String numToWord(String num) { + return numToWord(num, false); + } + + /** + * Overloaded Constructor. + * + * @param num Integer form of the numeral to be converted + */ + public static String numToWord(Integer num) { + String numStr = num.toString(); + return numToWord(numStr, false); + } + + /** + * Overloaded Constructor. + * + * @param num BigInteger form of the numeral to be converted + */ + public static String numToWord(BigInteger num) { + String numStr = num.toString(); + return numToWord(numStr, false); + } + + /** + * Performs the conversion of a numeral (from String) to Japanese kanji form + * Keeping track of recursive calls is necessary to allow the function to properly + * determine the unit value (1-9999) of the place-value before moving to the next place-value. + * + * I.E. 486900000000 --> 4869 * 10^8 (oku) --> We can just find the written form of 4869 then add oku after it + * 4869 --> our recursive call --> 四千八百六十九 + * 10^8 --> 億 + * 四千八百六十九 * 億 === 四千八百六十九億 + * + * @param num the BigInteger number to be converted + * @param isRecursive whether or not this call is recursive + * @return numStr the final written representation in Japanese kanji + */ + public static String numToWord(String num, Boolean isRecursive) { + String numStr = ""; + + // Counter will be used to keep track of the remainder as each larger unit is subtracted + // E.G., 2486954371891 --> minus 2 chou (10^12) --> 486954371891 --> . . . + + BigInteger counter = new BigInteger(num); + + // Walk through the place values from largest to smallest + for (Map.Entry entry : placeValues.entrySet()) { + String pvKanji = entry.getValue(); + Object[] results = getUnitStr(entry.getKey(), pvKanji, counter); + + // There are some irregularities with the 10's, 100's, and 1000's place in terms of written representation + // Namely, "一十" and "一百" are invalid. In addition, "sen" is used with numbers 1000-1999, + // and issen with all higher numbers containing 1 in the 1000's place of the unit for that place-value. + // E.G., 10000000 --> 1000 * 10000 (man) --> 一千万 (issenman) + + String strVal = (String) results[0]; + + if (pvKanji.equals("十") || pvKanji.equals("百")) { + if (strVal.startsWith("一")) { + strVal = pvKanji; + } + } + + if (pvKanji.equals("千") && !isRecursive) { + if (strVal.startsWith("一千")) { + + // If numStr is empty at this point, it means that num >= 9999 + // and therefore 一 shouldn't be placed before 千 + if (numStr.isEmpty()) { + strVal = strVal.substring(1); + } + + } + } + + numStr += strVal; + counter = (BigInteger) results[1]; + } + + // Tack on the one's place value + numStr += NUMERALS_KANJI[counter.intValue()]; + + return numStr; + } + + /** + * This recursive function is the core of the library. It determines the number of units within a place-value. + * It uses recursive calls to break numbers down into chunks of 4 digits (the place-value system of Japanese + * changes in groups of 4 (units), not 3 like the US) in order to determine the written form for that place-value. + * The counter must be updated (subtracting the current place-value) to allow for smaller place values + * to be determined. + * + * @param placeValue The place-value that we are determining + * @param pvKanji The kanji representation of this place-value + * @param counter The current number that we are converting + * @return results Array with two elements: [0] = String result of conversion, [1] = updated counter + */ + private static Object[] getUnitStr(BigInteger placeValue, String pvKanji, BigInteger counter) { + String unitStr; + BigInteger numUnit = counter.divide(placeValue); + Object[] results; + + if (numUnit.intValue() > 9) { + // If numUnit > 10 then that means we can use recursion to get the written form for this unit + unitStr = numToWord(numUnit.toString(), true) + pvKanji; + } else { + // Otherwise, we can simply use the 1-9 kanji representations before the place-value + unitStr = NUMERALS_KANJI[numUnit.intValue()] + pvKanji; + } + + counter = counter.subtract(numUnit.multiply(placeValue)); + + // Don't return anything if there are no units of that place-value present + results = new Object[] {(numUnit.intValue() == 0) ? "" : unitStr, counter}; + + return results; + } +} diff --git a/fop-core/src/main/java/org/apache/fop/complexscripts/util/NumberConverter.java b/fop-core/src/main/java/org/apache/fop/complexscripts/util/NumberConverter.java index ec2c7d4ace5..1b3a2318aa5 100644 --- a/fop-core/src/main/java/org/apache/fop/complexscripts/util/NumberConverter.java +++ b/fop-core/src/main/java/org/apache/fop/complexscripts/util/NumberConverter.java @@ -20,6 +20,7 @@ package org.apache.fop.complexscripts.util; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -267,6 +268,7 @@ private Integer[] formatNumber(long number, Integer[] token) { case (int) 'a': // handled as numeric sequence case (int) 'I': // handled as numeric special case (int) 'i': // handled as numeric special + case 19968: // handled as numeric special - Japanese Numerals, first is 一 = 19968 decimal; default: if (isStartOfDecimalSequence(s)) { fn = formatNumberAsDecimal(number, s, 1); @@ -553,6 +555,7 @@ private static int getSequenceBase(int s) { { '\u3044' }, // kana - hiragana (iroha) { '\u30A2' }, // kana - katakana (gojuon) - default alphabetic sequence { '\u30A4' }, // kana - katakana (iroha) + { '\u4E00' }, // Japanese numbers }; private static boolean isStartOfNumericSpecial(int s) { @@ -590,6 +593,8 @@ private SpecialNumberFormatter getSpecialFormatter(int one, int letterValue, Str return new KanaNumeralsFormatter(); } else if (one == (int) '\u30A4') { return new KanaNumeralsFormatter(); + } else if (one == (int) '\u4E00') { + return new JapaneseNumeralsFormatter(); } else { return null; } @@ -1532,6 +1537,24 @@ public Integer[] format(long number, int one, int letterValue, String features, } } + private class JapaneseNumeralsFormatter implements SpecialNumberFormatter { + public Integer[] format(long number, int one, int letterValue, String features, String language, String country) { + if (one == 0x4E00) { + List items = new ArrayList<>(); + + String numStr = JapaneseToNumbers.numToWord((int)number); // add + 10 for two characters testing + for (char ch: numStr.toCharArray()) { + items.add((int)ch); + } + + return items.toArray(new Integer [ items.size() ]); + } else { + return null; + } + } + } + + /** * Thai Numerals */ diff --git a/fop-core/src/main/java/org/apache/fop/fo/flow/InlineContainer.java b/fop-core/src/main/java/org/apache/fop/fo/flow/InlineContainer.java index 5c95fa34e5b..6d87e7e93c8 100644 --- a/fop-core/src/main/java/org/apache/fop/fo/flow/InlineContainer.java +++ b/fop-core/src/main/java/org/apache/fop/fo/flow/InlineContainer.java @@ -19,6 +19,7 @@ package org.apache.fop.fo.flow; +import org.apache.fop.traits.WritingModeTraitsGetter; import org.xml.sax.Locator; import org.apache.fop.apps.FOPException; @@ -37,7 +38,7 @@ import org.apache.fop.traits.WritingMode; import org.apache.fop.traits.WritingModeTraits; -public class InlineContainer extends FObj { +public class InlineContainer extends FObj implements WritingModeTraitsGetter { private LengthRangeProperty inlineProgressionDimension; private LengthRangeProperty blockProgressionDimension; diff --git a/fop-core/src/main/java/org/apache/fop/fonts/Font.java b/fop-core/src/main/java/org/apache/fop/fonts/Font.java index 272019e9681..b9f4b6a843d 100644 --- a/fop-core/src/main/java/org/apache/fop/fonts/Font.java +++ b/fop-core/src/main/java/org/apache/fop/fonts/Font.java @@ -465,10 +465,10 @@ public boolean performsSubstitution() { /** {@inheritDoc} */ public CharSequence performSubstitution(CharSequence cs, - String script, String language, List associations, boolean retainControls) { + String script, String language, List associations, boolean retainControls, boolean isVertical) { if (metric instanceof Substitutable) { Substitutable s = (Substitutable) metric; - return s.performSubstitution(cs, script, language, associations, retainControls); + return s.performSubstitution(cs, script, language, associations, retainControls, isVertical); } else { throw new UnsupportedOperationException(); } @@ -476,10 +476,10 @@ public CharSequence performSubstitution(CharSequence cs, /** {@inheritDoc} */ public CharSequence reorderCombiningMarks(CharSequence cs, int[][] gpa, - String script, String language, List associations) { + String script, String language, List associations, boolean isVertical) { if (metric instanceof Substitutable) { Substitutable s = (Substitutable) metric; - return s.reorderCombiningMarks(cs, gpa, script, language, associations); + return s.reorderCombiningMarks(cs, gpa, script, language, associations, isVertical); } else { throw new UnsupportedOperationException(); } @@ -496,18 +496,18 @@ public boolean performsPositioning() { } /** {@inheritDoc} */ - public int[][] performPositioning(CharSequence cs, String script, String language, int fontSize) { + public int[][] performPositioning(CharSequence cs, String script, String language, int fontSize, boolean isVertical) { if (metric instanceof Positionable) { Positionable p = (Positionable) metric; - return p.performPositioning(cs, script, language, fontSize); + return p.performPositioning(cs, script, language, fontSize, isVertical); } else { throw new UnsupportedOperationException(); } } /** {@inheritDoc} */ - public int[][] performPositioning(CharSequence cs, String script, String language) { - return performPositioning(cs, script, language, fontSize); + public int[][] performPositioning(CharSequence cs, String script, String language, boolean isVertical) { + return performPositioning(cs, script, language, fontSize, isVertical); } } diff --git a/fop-core/src/main/java/org/apache/fop/fonts/GlyphMapping.java b/fop-core/src/main/java/org/apache/fop/fonts/GlyphMapping.java index 8eedafffadb..1d2d3571e59 100644 --- a/fop-core/src/main/java/org/apache/fop/fonts/GlyphMapping.java +++ b/fop-core/src/main/java/org/apache/fop/fonts/GlyphMapping.java @@ -28,6 +28,7 @@ import org.apache.fop.complexscripts.fonts.GlyphPositioningTable; import org.apache.fop.complexscripts.fonts.GlyphTable; import org.apache.fop.complexscripts.util.CharScript; +import org.apache.fop.complexscripts.util.Characters; import org.apache.fop.traits.MinOptMax; import org.apache.fop.util.CharUtilities; @@ -55,17 +56,18 @@ public class GlyphMapping { public final int[][] gposAdjustments; public String mapping; public List associations; + public boolean isUpright; public GlyphMapping(int startIndex, int endIndex, int wordSpaceCount, int letterSpaceCount, MinOptMax areaIPD, boolean isHyphenated, boolean isSpace, boolean breakOppAfter, Font font, int level, int[][] gposAdjustments) { this(startIndex, endIndex, wordSpaceCount, letterSpaceCount, areaIPD, isHyphenated, - isSpace, breakOppAfter, font, level, gposAdjustments, null, null); + isSpace, breakOppAfter, font, level, gposAdjustments, null, null, false); } public GlyphMapping(int startIndex, int endIndex, int wordSpaceCount, int letterSpaceCount, MinOptMax areaIPD, boolean isHyphenated, boolean isSpace, boolean breakOppAfter, - Font font, int level, int[][] gposAdjustments, String mapping, List associations) { + Font font, int level, int[][] gposAdjustments, String mapping, List associations, boolean isUpright) { assert startIndex <= endIndex; this.startIndex = startIndex; this.endIndex = endIndex; @@ -81,17 +83,18 @@ public GlyphMapping(int startIndex, int endIndex, int wordSpaceCount, int letter this.gposAdjustments = gposAdjustments; this.mapping = mapping; this.associations = associations; + this.isUpright = isUpright; } public static GlyphMapping doGlyphMapping(TextFragment text, int startIndex, int endIndex, Font font, MinOptMax letterSpaceIPD, MinOptMax[] letterSpaceAdjustArray, char precedingChar, char breakOpportunityChar, final boolean endsWithHyphen, int level, - boolean dontOptimizeForIdentityMapping, boolean retainAssociations, boolean retainControls) { + boolean dontOptimizeForIdentityMapping, boolean retainAssociations, boolean retainControls, boolean isVertical) { GlyphMapping mapping; if (font.performsSubstitution() || font.performsPositioning()) { mapping = processWordMapping(text, startIndex, endIndex, font, breakOpportunityChar, endsWithHyphen, level, - dontOptimizeForIdentityMapping, retainAssociations, retainControls); + dontOptimizeForIdentityMapping, retainAssociations, retainControls, isVertical); } else { mapping = processWordNoMapping(text, startIndex, endIndex, font, letterSpaceIPD, letterSpaceAdjustArray, precedingChar, breakOpportunityChar, endsWithHyphen, level); @@ -102,7 +105,7 @@ public static GlyphMapping doGlyphMapping(TextFragment text, int startIndex, int private static GlyphMapping processWordMapping(TextFragment text, int startIndex, int endIndex, final Font font, final char breakOpportunityChar, final boolean endsWithHyphen, int level, - boolean dontOptimizeForIdentityMapping, boolean retainAssociations, boolean retainControls) { + boolean dontOptimizeForIdentityMapping, boolean retainAssociations, boolean retainControls, boolean isVertical) { int nLS = 0; // # of letter spaces String script = text.getScript(); String language = text.getLanguage(); @@ -138,13 +141,13 @@ private static GlyphMapping processWordMapping(TextFragment text, int startIndex script = "*"; } - CharSequence mcs = font.performSubstitution(ics, script, language, associations, retainControls); + CharSequence mcs = font.performSubstitution(ics, script, language, associations, retainControls, isVertical); // 4. compute glyph position adjustments on (substituted) characters. int[][] gpa = null; if (font.performsPositioning()) { // handle GPOS adjustments - gpa = font.performPositioning(mcs, script, language); + gpa = font.performPositioning(mcs, script, language, isVertical); } if (useKerningAdjustments(font, script, language)) { // handle standard (non-GPOS) kerning adjustments @@ -153,12 +156,13 @@ private static GlyphMapping processWordMapping(TextFragment text, int startIndex // 5. reorder combining marks so that they precede (within the mapped char sequence) the // base to which they are applied; N.B. position adjustments (gpa) are reordered in place. - mcs = font.reorderCombiningMarks(mcs, gpa, script, language, associations); + mcs = font.reorderCombiningMarks(mcs, gpa, script, language, associations, isVertical); // 6. compute word ipd based on final position adjustments. MinOptMax ipd = MinOptMax.ZERO; // The gpa array is sized by code point count + boolean isUprightChar = false; for (int i = 0, cpi = 0, n = mcs.length(); i < n; i++, cpi++) { int c = mcs.charAt(i); @@ -166,7 +170,10 @@ private static GlyphMapping processWordMapping(TextFragment text, int startIndex c = Character.toCodePoint((char) c, mcs.charAt(++i)); } - int w = font.getCharWidth(c); + if (isVertical) { + isUprightChar = isUprightChar || Characters.isUprightOrientation(c); + } + int w = font.getCharWidth(c); // TODO Vertical text use vertical width if (w < 0) { w = 0; } @@ -181,7 +188,7 @@ private static GlyphMapping processWordMapping(TextFragment text, int startIndex return new GlyphMapping(startIndex, endIndex, 0, nLS, ipd, endsWithHyphen, false, breakOpportunityChar != 0, font, level, gpa, !dontOptimizeForIdentityMapping && CharUtilities.isSameSequence(mcs, ics) ? null : mcs.toString(), - associations); + associations, isUprightChar); } private static boolean useKerningAdjustments(final Font font, String script, String language) { diff --git a/fop-core/src/main/java/org/apache/fop/fonts/LazyFont.java b/fop-core/src/main/java/org/apache/fop/fonts/LazyFont.java index 753cece8c1e..833abb4215e 100644 --- a/fop-core/src/main/java/org/apache/fop/fonts/LazyFont.java +++ b/fop-core/src/main/java/org/apache/fop/fonts/LazyFont.java @@ -420,11 +420,11 @@ public boolean performsSubstitution() { * {@inheritDoc} */ public CharSequence performSubstitution(CharSequence cs, String script, String language, List associations, - boolean retainControls) { + boolean retainControls, boolean isVertical) { load(true); if (realFontDescriptor instanceof Substitutable) { return ((Substitutable)realFontDescriptor).performSubstitution(cs, - script, language, associations, retainControls); + script, language, associations, retainControls, isVertical); } else { return cs; } @@ -434,13 +434,13 @@ public CharSequence performSubstitution(CharSequence cs, String script, String l * {@inheritDoc} */ public CharSequence reorderCombiningMarks( - CharSequence cs, int[][] gpa, String script, String language, List associations) { + CharSequence cs, int[][] gpa, String script, String language, List associations, boolean isVertical) { if (!isMetricsLoaded) { load(true); } if (realFontDescriptor instanceof Substitutable) { return ((Substitutable)realFontDescriptor) - .reorderCombiningMarks(cs, gpa, script, language, associations); + .reorderCombiningMarks(cs, gpa, script, language, associations, isVertical); } else { return cs; } @@ -464,13 +464,13 @@ public boolean performsPositioning() { * {@inheritDoc} */ public int[][] - performPositioning(CharSequence cs, String script, String language, int fontSize) { + performPositioning(CharSequence cs, String script, String language, int fontSize, boolean isVertical) { if (!isMetricsLoaded) { load(true); } if (realFontDescriptor instanceof Positionable) { return ((Positionable)realFontDescriptor) - .performPositioning(cs, script, language, fontSize); + .performPositioning(cs, script, language, fontSize, isVertical); } else { return null; } @@ -480,13 +480,13 @@ public boolean performsPositioning() { * {@inheritDoc} */ public int[][] - performPositioning(CharSequence cs, String script, String language) { + performPositioning(CharSequence cs, String script, String language, boolean isVertical) { if (!isMetricsLoaded) { load(true); } if (realFontDescriptor instanceof Positionable) { return ((Positionable)realFontDescriptor) - .performPositioning(cs, script, language); + .performPositioning(cs, script, language, isVertical); } else { return null; } diff --git a/fop-core/src/main/java/org/apache/fop/fonts/MultiByteFont.java b/fop-core/src/main/java/org/apache/fop/fonts/MultiByteFont.java index cbaace8a2e9..8b2cdfbacde 100644 --- a/fop-core/src/main/java/org/apache/fop/fonts/MultiByteFont.java +++ b/fop-core/src/main/java/org/apache/fop/fonts/MultiByteFont.java @@ -547,11 +547,11 @@ public boolean performsSubstitution() { /** {@inheritDoc} */ public CharSequence performSubstitution(CharSequence charSequence, String script, String language, - List associations, boolean retainControls) { + List associations, boolean retainControls, boolean isVertical) { if (gsub != null) { - charSequence = gsub.preProcess(charSequence, script, this, associations); + charSequence = gsub.preProcess(charSequence, script, this, associations, isVertical); GlyphSequence glyphSequence = charSequenceToGlyphSequence(charSequence, associations); - GlyphSequence glyphSequenceSubstituted = gsub.substitute(glyphSequence, script, language); + GlyphSequence glyphSequenceSubstituted = gsub.substitute(glyphSequence, script, language, isVertical); if (associations != null) { associations.clear(); associations.addAll(glyphSequenceSubstituted.getAssociations()); @@ -574,10 +574,10 @@ public GlyphSequence charSequenceToGlyphSequence(CharSequence charSequence, List /** {@inheritDoc} */ public CharSequence reorderCombiningMarks( - CharSequence cs, int[][] gpa, String script, String language, List associations) { + CharSequence cs, int[][] gpa, String script, String language, List associations, boolean isVertical) { if (gdef != null) { GlyphSequence igs = mapCharsToGlyphs(cs, associations); - GlyphSequence ogs = gdef.reorderCombiningMarks(igs, getUnscaledWidths(igs), gpa, script, language); + GlyphSequence ogs = gdef.reorderCombiningMarks(igs, getUnscaledWidths(igs), gpa, script, language, isVertical); if (associations != null) { associations.clear(); associations.addAll(ogs.getAssociations()); @@ -606,11 +606,11 @@ public boolean performsPositioning() { /** {@inheritDoc} */ public int[][] - performPositioning(CharSequence cs, String script, String language, int fontSize) { + performPositioning(CharSequence cs, String script, String language, int fontSize, boolean isVertical) { if (gpos != null) { GlyphSequence gs = mapCharsToGlyphs(cs, null); int[][] adjustments = new int [ gs.getGlyphCount() ] [ 4 ]; - if (gpos.position(gs, script, language, fontSize, this.width, adjustments)) { + if (gpos.position(gs, script, language, fontSize, this.width, adjustments, isVertical)) { return scaleAdjustments(adjustments, fontSize); } else { return null; @@ -621,7 +621,7 @@ public boolean performsPositioning() { } /** {@inheritDoc} */ - public int[][] performPositioning(CharSequence cs, String script, String language) { + public int[][] performPositioning(CharSequence cs, String script, String language, boolean isVertical) { throw new UnsupportedOperationException(); } diff --git a/fop-core/src/main/java/org/apache/fop/layoutmgr/AbstractBreaker.java b/fop-core/src/main/java/org/apache/fop/layoutmgr/AbstractBreaker.java index e1e6ee5385d..39dcc2308de 100644 --- a/fop-core/src/main/java/org/apache/fop/layoutmgr/AbstractBreaker.java +++ b/fop-core/src/main/java/org/apache/fop/layoutmgr/AbstractBreaker.java @@ -457,7 +457,7 @@ public boolean doLayout(int flowBPD, boolean autoHeight) { + " pageBreaks.size()= " + alg.getPageBreaks().size()); //*** Phase 3: Add areas *** - doPhase3(alg, optimalPageCount, blockList, blockList); + doPhase3(alg, optimalPageCount, blockList, blockList, childLC); } } } catch (PagePositionOnlyException e) { @@ -519,7 +519,7 @@ protected boolean containsNonRestartableLM(Position position) { * @param effectiveList effective Knuth element list (after adjustments) */ protected abstract void doPhase3(PageBreakingAlgorithm alg, int partCount, - BlockSequence originalList, BlockSequence effectiveList); + BlockSequence originalList, BlockSequence effectiveList, LayoutContext context); /** * Phase 3 of Knuth algorithm: Adds the areas diff --git a/fop-core/src/main/java/org/apache/fop/layoutmgr/BlockContainerLayoutManager.java b/fop-core/src/main/java/org/apache/fop/layoutmgr/BlockContainerLayoutManager.java index 7f540337881..d7e66092b90 100644 --- a/fop-core/src/main/java/org/apache/fop/layoutmgr/BlockContainerLayoutManager.java +++ b/fop-core/src/main/java/org/apache/fop/layoutmgr/BlockContainerLayoutManager.java @@ -197,7 +197,7 @@ protected LayoutContext makeChildLayoutContext(LayoutContext context) { childLC.setStackLimitBP( context.getStackLimitBP().minus(MinOptMax.getInstance(relDims.bpd))); childLC.setRefIPD(relDims.ipd); - childLC.setWritingMode(getBlockContainerFO().getWritingMode()); + childLC.setWritingMode(context.getWritingMode()); return childLC; } @@ -692,7 +692,7 @@ protected void addAreas(PositionIterator posIter, LayoutContext context) { } protected void doPhase3(PageBreakingAlgorithm alg, int partCount, - BlockSequence originalList, BlockSequence effectiveList) { + BlockSequence originalList, BlockSequence effectiveList, LayoutContext context) { //Defer adding of areas until addAreas is called by the parent LM this.deferredAlg = alg; this.deferredOriginalList = originalList; @@ -757,6 +757,7 @@ public void addAreas(PositionIterator parentIter, LayoutContext layoutContext) { LayoutManager childLM; LayoutManager lastLM = null; LayoutContext lc = LayoutContext.offspringOf(layoutContext); + lc.setWritingMode(getBlockContainerFO().getWritingMode()); lc.setSpaceAdjust(layoutContext.getSpaceAdjust()); // set space after in the LayoutContext for children if (layoutContext.getSpaceAfter() > 0) { @@ -823,7 +824,7 @@ public void addAreas(PositionIterator parentIter, LayoutContext layoutContext) { } } else { //Add child areas inside the reference area - bcpos.getBreaker().addContainedAreas(layoutContext); + bcpos.getBreaker().addContainedAreas(lc); } registerMarkers(false, isFirst(firstPos), isLast(lastPos)); diff --git a/fop-core/src/main/java/org/apache/fop/layoutmgr/BlockStackingLayoutManager.java b/fop-core/src/main/java/org/apache/fop/layoutmgr/BlockStackingLayoutManager.java index c7ca3a6c4db..d5c53ab1d71 100644 --- a/fop-core/src/main/java/org/apache/fop/layoutmgr/BlockStackingLayoutManager.java +++ b/fop-core/src/main/java/org/apache/fop/layoutmgr/BlockStackingLayoutManager.java @@ -391,6 +391,7 @@ protected LayoutContext makeChildLayoutContext(LayoutContext context) { childLC.copyPendingMarksFrom(context); childLC.setStackLimitBP(context.getStackLimitBP()); childLC.setRefIPD(referenceIPD); + childLC.setWritingMode(context.getWritingMode()); return childLC; } diff --git a/fop-core/src/main/java/org/apache/fop/layoutmgr/FlowLayoutManager.java b/fop-core/src/main/java/org/apache/fop/layoutmgr/FlowLayoutManager.java index 1d5acdee1e3..db3f0a3607d 100644 --- a/fop-core/src/main/java/org/apache/fop/layoutmgr/FlowLayoutManager.java +++ b/fop-core/src/main/java/org/apache/fop/layoutmgr/FlowLayoutManager.java @@ -30,6 +30,7 @@ import org.apache.fop.area.Area; import org.apache.fop.area.BlockParent; import org.apache.fop.fo.pagination.Flow; +import org.apache.fop.fo.pagination.Region; import org.apache.fop.util.ListUtil; /** @@ -205,7 +206,7 @@ protected LayoutContext makeChildLayoutContext(LayoutContext context) { LayoutContext childLC = LayoutContext.newInstance(); childLC.setStackLimitBP(context.getStackLimitBP()); childLC.setRefIPD(context.getRefIPD()); - childLC.setWritingMode(getCurrentPage().getSimplePageMaster().getWritingMode()); + childLC.setWritingMode(getCurrentPage().getSimplePageMaster().getRegion(Region.FO_REGION_BODY).getWritingMode()); return childLC; } diff --git a/fop-core/src/main/java/org/apache/fop/layoutmgr/FootnoteBodyLayoutManager.java b/fop-core/src/main/java/org/apache/fop/layoutmgr/FootnoteBodyLayoutManager.java index ccb31820309..61d5645b0c7 100644 --- a/fop-core/src/main/java/org/apache/fop/layoutmgr/FootnoteBodyLayoutManager.java +++ b/fop-core/src/main/java/org/apache/fop/layoutmgr/FootnoteBodyLayoutManager.java @@ -24,6 +24,8 @@ import org.apache.fop.area.Area; import org.apache.fop.fo.flow.FootnoteBody; +import org.apache.fop.fo.pagination.Region; +import org.apache.fop.traits.WritingMode; /** * Layout manager for footnote bodies. @@ -43,6 +45,8 @@ public FootnoteBodyLayoutManager(FootnoteBody body) { @Override public List getNextKnuthElements(LayoutContext context, int alignment) { if (knuthElements == null) { + // added for fixing https://github.com/metanorma/xmlgraphics-fop/issues/37 + context.setWritingMode(getCurrentPage().getSimplePageMaster().getRegion(Region.FO_REGION_BODY).getWritingMode()); knuthElements = super.getNextKnuthElements(context, alignment); } return knuthElements; diff --git a/fop-core/src/main/java/org/apache/fop/layoutmgr/LayoutContext.java b/fop-core/src/main/java/org/apache/fop/layoutmgr/LayoutContext.java index c2082108dfc..6a9940831f9 100644 --- a/fop-core/src/main/java/org/apache/fop/layoutmgr/LayoutContext.java +++ b/fop-core/src/main/java/org/apache/fop/layoutmgr/LayoutContext.java @@ -142,6 +142,7 @@ public static LayoutContext copyOf(LayoutContext copy) { public static LayoutContext offspringOf(LayoutContext parent) { LayoutContext offspring = new LayoutContext(0); offspring.setTreatAsArtifact(parent.treatAsArtifact()); + offspring.setWritingMode(parent.getWritingMode()); return offspring; } @@ -664,7 +665,8 @@ public String toString() { + "\nKeeps: \t[keep-with-next=" + getKeepWithNextPending() + "][keep-with-previous=" + getKeepWithPreviousPending() + "] pending" + "\nBreaks: \tforced [" + (breakBefore != Constants.EN_AUTO ? "break-before" : "") + "][" - + (breakAfter != Constants.EN_AUTO ? "break-after" : "") + "]"; + + (breakAfter != Constants.EN_AUTO ? "break-after" : "") + "]" + + "\nWriting Mode: \t" + getWritingMode(); } /** diff --git a/fop-core/src/main/java/org/apache/fop/layoutmgr/LocalBreaker.java b/fop-core/src/main/java/org/apache/fop/layoutmgr/LocalBreaker.java index 5d632c658bf..6f1a8eb3877 100644 --- a/fop-core/src/main/java/org/apache/fop/layoutmgr/LocalBreaker.java +++ b/fop-core/src/main/java/org/apache/fop/layoutmgr/LocalBreaker.java @@ -137,7 +137,7 @@ protected void addAreas(PositionIterator posIter, LayoutContext context) { } protected void doPhase3(PageBreakingAlgorithm alg, int partCount, BlockSequence originalList, - BlockSequence effectiveList) { + BlockSequence effectiveList, LayoutContext context) { if (partCount > 1) { PageBreakPosition pos = alg.getPageBreaks().getFirst(); int firstPartLength = ElementListUtils.calcContentLength(effectiveList, @@ -148,7 +148,9 @@ protected void doPhase3(PageBreakingAlgorithm alg, int partCount, BlockSequence // overflow should be visible. alg.removeAllPageBreaks(); // Directly add areas after finding the breaks - this.addAreas(alg, 1, originalList, effectiveList); + LayoutContext childLC = LayoutContext.newInstance(); + childLC.setWritingMode(context.getWritingMode()); + this.addAreas(alg, 0, 1, originalList, effectiveList, childLC); } protected void finishPart(PageBreakingAlgorithm alg, PageBreakPosition pbp) { diff --git a/fop-core/src/main/java/org/apache/fop/layoutmgr/PageBreaker.java b/fop-core/src/main/java/org/apache/fop/layoutmgr/PageBreaker.java index 3f90d033a6b..309afe58eeb 100644 --- a/fop-core/src/main/java/org/apache/fop/layoutmgr/PageBreaker.java +++ b/fop-core/src/main/java/org/apache/fop/layoutmgr/PageBreaker.java @@ -322,7 +322,7 @@ protected void addAreas(PositionIterator posIter, LayoutContext context) { * or whether to take into account a 'last-page' condition. */ protected void doPhase3(PageBreakingAlgorithm alg, int partCount, - BlockSequence originalList, BlockSequence effectiveList) { + BlockSequence originalList, BlockSequence effectiveList, LayoutContext context) { if (needColumnBalancing) { //column balancing for the last part diff --git a/fop-core/src/main/java/org/apache/fop/layoutmgr/StaticContentLayoutManager.java b/fop-core/src/main/java/org/apache/fop/layoutmgr/StaticContentLayoutManager.java index 20b337fe9ed..d14f5922dbd 100644 --- a/fop-core/src/main/java/org/apache/fop/layoutmgr/StaticContentLayoutManager.java +++ b/fop-core/src/main/java/org/apache/fop/layoutmgr/StaticContentLayoutManager.java @@ -169,6 +169,14 @@ public StaticContentBreaker(StaticContentLayoutManager lm, int ipd, int displayA super(lm, ipd, displayAlign); } + @Override + protected void updateLayoutContext(LayoutContext context) { + super.updateLayoutContext(context); + if (StaticContentLayoutManager.this.regionFO != null) { + context.setWritingMode(StaticContentLayoutManager.this.regionFO.getWritingMode()); + } + } + /** {@inheritDoc} */ protected void observeElementList(List elementList) { String elementListID = getStaticContentFO().getFlowName(); diff --git a/fop-core/src/main/java/org/apache/fop/layoutmgr/inline/AbstractPageNumberCitationLayoutManager.java b/fop-core/src/main/java/org/apache/fop/layoutmgr/inline/AbstractPageNumberCitationLayoutManager.java index c06ec522aec..41b6600c30f 100644 --- a/fop-core/src/main/java/org/apache/fop/layoutmgr/inline/AbstractPageNumberCitationLayoutManager.java +++ b/fop-core/src/main/java/org/apache/fop/layoutmgr/inline/AbstractPageNumberCitationLayoutManager.java @@ -19,6 +19,8 @@ package org.apache.fop.layoutmgr.inline; +import java.util.List; + import org.apache.fop.area.PageViewport; import org.apache.fop.area.Trait; import org.apache.fop.area.inline.InlineArea; @@ -28,6 +30,7 @@ import org.apache.fop.fonts.Font; import org.apache.fop.fonts.FontInfo; import org.apache.fop.fonts.FontTriplet; +import org.apache.fop.layoutmgr.KnuthSequence; import org.apache.fop.layoutmgr.LayoutContext; import org.apache.fop.layoutmgr.TraitSetter; import org.apache.fop.traits.MinOptMax; @@ -48,6 +51,9 @@ public abstract class AbstractPageNumberCitationLayoutManager extends LeafNodeLa private String citationString; + /** Indicate whether the writing mode is vertical */ + private boolean isVertical; + /** * Constructor * @@ -120,6 +126,13 @@ protected InlineArea getEffectiveArea(LayoutContext layoutContext) { return area; } + /** {@inheritDoc} */ + @Override + public List getNextKnuthElements(LayoutContext context, int alignment) { + isVertical = context.getWritingMode().isVertical(); + return super.getNextKnuthElements(context, alignment); + } + private InlineArea getPageNumberCitationArea() { TextArea text; if (resolved) { @@ -129,7 +142,7 @@ private InlineArea getPageNumberCitationArea() { text.addWord(citationString, getStringWidth(citationString), null, null, null, 0); } else { UnresolvedPageNumber unresolved = new UnresolvedPageNumber(citation.getRefId(), font, - getReferenceType()); + getReferenceType(), isVertical); getPSLM().addUnresolvedArea(citation.getRefId(), unresolved); text = unresolved; } diff --git a/fop-core/src/main/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java b/fop-core/src/main/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java index 4fb3ba18efd..c66eec722ab 100644 --- a/fop-core/src/main/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java +++ b/fop-core/src/main/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java @@ -118,6 +118,7 @@ private List getChildKnuthElements(LayoutContext layoutContext, int LayoutManager childLM; while ((childLM = getChildLM()) != null) { LayoutContext childLC = LayoutContext.offspringOf(layoutContext); + childLC.setWritingMode(getInlineContainer().getWritingMode()); childLC.setRefIPD(contentAreaIPD); @SuppressWarnings("unchecked") List childElements = childLM.getNextKnuthElements(childLC, alignment); @@ -246,7 +247,9 @@ public void addAreas(PositionIterator posIter, LayoutContext context) { } assert inlineContainerPosition != null; KnuthPossPosIter childPosIter = new KnuthPossPosIter(childElements); - AreaAdditionUtil.addAreas(this, childPosIter, context); + LayoutContext childLC = LayoutContext.offspringOf(context); + childLC.setWritingMode(getInlineContainer().getWritingMode()); + AreaAdditionUtil.addAreas(this, childPosIter, childLC); } @Override diff --git a/fop-core/src/main/java/org/apache/fop/layoutmgr/inline/PageNumberLayoutManager.java b/fop-core/src/main/java/org/apache/fop/layoutmgr/inline/PageNumberLayoutManager.java index 7881283c0e6..96f5d1bc5ef 100644 --- a/fop-core/src/main/java/org/apache/fop/layoutmgr/inline/PageNumberLayoutManager.java +++ b/fop-core/src/main/java/org/apache/fop/layoutmgr/inline/PageNumberLayoutManager.java @@ -19,6 +19,8 @@ package org.apache.fop.layoutmgr.inline; +import java.util.List; + import org.apache.fop.area.Trait; import org.apache.fop.area.inline.InlineArea; import org.apache.fop.area.inline.ResolvedPageNumber; @@ -26,6 +28,7 @@ import org.apache.fop.fonts.Font; import org.apache.fop.fonts.FontInfo; import org.apache.fop.fonts.FontTriplet; +import org.apache.fop.layoutmgr.KnuthSequence; import org.apache.fop.layoutmgr.LayoutContext; import org.apache.fop.layoutmgr.TraitSetter; import org.apache.fop.traits.MinOptMax; @@ -73,7 +76,7 @@ protected AlignmentContext makeAlignmentContext(LayoutContext context) { /** {@inheritDoc} */ public InlineArea get(LayoutContext context) { // get page string from parent, build area - ResolvedPageNumber pn = new ResolvedPageNumber(); + ResolvedPageNumber pn = new ResolvedPageNumber(font, context.getWritingMode().isVertical()); String str = getCurrentPV().getPageNumberString(); int width = getStringWidth(str); int level = getBidiLevel(); @@ -94,7 +97,7 @@ protected InlineArea getEffectiveArea(LayoutContext layoutContext) { //TODO Maybe replace that with a clone() call or better, a copy constructor //TODO or even better: delay area creation until addAreas() stage //ResolvedPageNumber is cloned because the LM is reused in static areas and the area can't be. - ResolvedPageNumber pn = new ResolvedPageNumber(); + ResolvedPageNumber pn = new ResolvedPageNumber(font, layoutContext.getWritingMode().isVertical()); TraitSetter.setProducerID(pn, fobj.getId()); pn.setIPD(baseArea.getIPD()); pn.setBPD(baseArea.getBPD()); diff --git a/fop-core/src/main/java/org/apache/fop/layoutmgr/inline/TextLayoutManager.java b/fop-core/src/main/java/org/apache/fop/layoutmgr/inline/TextLayoutManager.java index a96e5706e83..9c45f34dcb2 100644 --- a/fop-core/src/main/java/org/apache/fop/layoutmgr/inline/TextLayoutManager.java +++ b/fop-core/src/main/java/org/apache/fop/layoutmgr/inline/TextLayoutManager.java @@ -31,6 +31,7 @@ import org.apache.fop.apps.FOUserAgent; import org.apache.fop.area.Trait; import org.apache.fop.area.inline.TextArea; +import org.apache.fop.complexscripts.util.Characters; import org.apache.fop.fo.Constants; import org.apache.fop.fo.FOText; import org.apache.fop.fo.flow.ChangeBar; @@ -475,7 +476,7 @@ private void setText() { } private boolean isWordEnd(int mappingIndex) { - return mappingIndex == lastIndex || getGlyphMapping(mappingIndex + 1).isSpace; + return mappingIndex == lastIndex || mapping.isUpright || getGlyphMapping(mappingIndex + 1).isSpace || getGlyphMapping(mappingIndex + 1).isUpright; //TODO If is vertical mode and GlyphMapping is hani, return true. } /** @@ -499,8 +500,10 @@ private void addWord(int startIndex, int endIndex, int wordLength) { initWord(wordLength); // iterate over word's fragments, adding word chars (with bidi // levels), letter space adjustments, and glyph position adjustments + boolean isUpright = false; for (int i = startIndex; i <= endIndex; i++) { GlyphMapping wordMapping = getGlyphMapping(i); + isUpright = isUpright || wordMapping.isUpright; addWordChars(wordMapping); addLetterAdjust(wordMapping); if (addGlyphPositionAdjustments(wordMapping)) { @@ -515,7 +518,7 @@ private void addWord(int startIndex, int endIndex, int wordLength) { gposAdjustments = null; } textArea.addWord(wordChars.toString(), wordIPD, letterSpaceAdjust, getNonEmptyLevels(), gposAdjustments, - blockProgressionOffset, isWordSpace(endIndex + 1)); + blockProgressionOffset, isWordSpace(endIndex + 1), isUpright); } private boolean isWordSpace(int mappingIndex) { @@ -783,6 +786,9 @@ public List getNextKnuthElements(final LayoutContext context, fin int level = -1; int prevLevel = -1; boolean retainControls = false; + boolean isVertical = context.getWritingMode().isVertical(); + boolean prevCharIsUpright = false; + boolean inUpright = false; Font lastFont = null; int lastFontPos = -1; while (nextStart < foText.length()) { @@ -816,11 +822,16 @@ public List getNextKnuthElements(final LayoutContext context, fin + ", inSpace = " + inWhitespace + "}"); } + if (isVertical) { + inUpright = prevCharIsUpright || (prevCharIsUpright = Characters.isUprightOrientation(ch)); + } + if (inWord) { boolean processWord = breakOpportunity || GlyphMapping.isSpace(ch) || CharUtilities.isExplicitBreak(ch) - || ((prevLevel != -1) && (level != prevLevel)); + || ((prevLevel != -1) && (level != prevLevel)) + || inUpright; if (!processWord && foText.getCommonFont().getFontSelectionStrategy() == EN_CHARACTER_BY_CHARACTER) { if (lastFont == null || lastFontPos != nextStart - 1) { lastFont = FontSelector.selectFontForCharactersInText( @@ -835,7 +846,7 @@ public List getNextKnuthElements(final LayoutContext context, fin if (processWord) { // this.foText.charAt(lastIndex) == CharUtilities.SOFT_HYPHEN prevMapping = processWord(alignment, sequence, prevMapping, ch, - breakOpportunity, true, prevLevel, retainControls); + breakOpportunity, true, prevLevel, retainControls, context.getWritingMode().isVertical()); } } else if (inWhitespace) { if (ch != CharUtilities.SPACE || breakOpportunity) { @@ -893,7 +904,7 @@ public List getNextKnuthElements(final LayoutContext context, fin // Process any last elements if (inWord) { - processWord(alignment, sequence, prevMapping, ch, false, false, prevLevel, retainControls); + processWord(alignment, sequence, prevMapping, ch, false, false, prevLevel, retainControls, context.getWritingMode().isVertical()); } else if (inWhitespace) { processWhitespace(alignment, sequence, !keepTogether, prevLevel); } else if (mapping != null) { @@ -961,7 +972,7 @@ private GlyphMapping processWhitespace(final int alignment, private GlyphMapping processWord(final int alignment, final KnuthSequence sequence, GlyphMapping prevMapping, final char ch, final boolean breakOpportunity, - final boolean checkEndsWithHyphen, int level, boolean retainControls) { + final boolean checkEndsWithHyphen, int level, boolean retainControls, boolean isVertical) { //Word boundary found, process widths and kerning int lastIndex = nextStart; @@ -977,7 +988,7 @@ private GlyphMapping processWord(final int alignment, final KnuthSequence sequen && prevMapping.endIndex > 0 ? foText.charAt(prevMapping.endIndex - 1) : 0; GlyphMapping mapping = GlyphMapping.doGlyphMapping(foText, thisStart, lastIndex, font, letterSpaceIPD, letterSpaceAdjustArray, precedingChar, breakOpportunityChar, - endsWithHyphen, level, false, false, retainControls); + endsWithHyphen, level, false, false, retainControls, isVertical); prevMapping = mapping; addGlyphMapping(mapping); tempStart = nextStart; @@ -1370,7 +1381,7 @@ private void addElementsForAWordFragment(List baseList, int alignment, GlyphMapp alignmentContext, notifyPos(mainPosition), false)); } else { // adjustable letter spacing - int unsuppressibleLetterSpaces = suppressibleLetterSpace + int unsuppressibleLetterSpaces = (suppressibleLetterSpace && mapping.letterSpaceCount > 0) ? mapping.letterSpaceCount - 1 : mapping.letterSpaceCount; baseList.add(new KnuthInlineBox(mapping.areaIPD.getOpt() diff --git a/fop-core/src/main/java/org/apache/fop/layoutmgr/table/RowGroupLayoutManager.java b/fop-core/src/main/java/org/apache/fop/layoutmgr/table/RowGroupLayoutManager.java index 8b74f635df8..337b24bcf60 100644 --- a/fop-core/src/main/java/org/apache/fop/layoutmgr/table/RowGroupLayoutManager.java +++ b/fop-core/src/main/java/org/apache/fop/layoutmgr/table/RowGroupLayoutManager.java @@ -116,6 +116,7 @@ private void createElementsForRowGroup(LayoutContext context, int alignment, LayoutContext childLC = LayoutContext.newInstance(); childLC.setStackLimitBP(context.getStackLimitBP()); //necessary? childLC.setRefIPD(spanWidth); + childLC.setWritingMode(context.getWritingMode()); //Get the element list for the cell contents List elems = primary.getCellLM().getNextKnuthElements( diff --git a/fop-core/src/main/java/org/apache/fop/layoutmgr/table/TableCellLayoutManager.java b/fop-core/src/main/java/org/apache/fop/layoutmgr/table/TableCellLayoutManager.java index 7e369e5261d..fa65a0859c0 100644 --- a/fop-core/src/main/java/org/apache/fop/layoutmgr/table/TableCellLayoutManager.java +++ b/fop-core/src/main/java/org/apache/fop/layoutmgr/table/TableCellLayoutManager.java @@ -184,6 +184,8 @@ public List getNextKnuthElements(LayoutContext context, int alignme LayoutManager prevLM = null; // previously active LM while ((curLM = getChildLM()) != null) { LayoutContext childLC = LayoutContext.newInstance(); + childLC.setWritingMode(context.getWritingMode()); + // curLM is a ? childLC.setStackLimitBP(context.getStackLimitBP().minus(stackLimit)); childLC.setRefIPD(cellIPD); diff --git a/fop-core/src/main/java/org/apache/fop/layoutmgr/table/TableLayoutManager.java b/fop-core/src/main/java/org/apache/fop/layoutmgr/table/TableLayoutManager.java index 284dbdede66..1693333d5d8 100644 --- a/fop-core/src/main/java/org/apache/fop/layoutmgr/table/TableLayoutManager.java +++ b/fop-core/src/main/java/org/apache/fop/layoutmgr/table/TableLayoutManager.java @@ -273,6 +273,7 @@ public List getNextKnuthElements(LayoutContext context, int alignme stackSize));*/ childLC.setRefIPD(context.getRefIPD()); childLC.copyPendingMarksFrom(context); + childLC.setWritingMode(context.getWritingMode()); contentKnuthElements = contentLM.getNextKnuthElements(childLC, alignment); //Set index values on elements coming from the content LM diff --git a/fop-core/src/main/java/org/apache/fop/render/intermediate/IFRenderer.java b/fop-core/src/main/java/org/apache/fop/render/intermediate/IFRenderer.java index 84e4e3a2c49..d1e63f18593 100644 --- a/fop-core/src/main/java/org/apache/fop/render/intermediate/IFRenderer.java +++ b/fop-core/src/main/java/org/apache/fop/render/intermediate/IFRenderer.java @@ -35,6 +35,7 @@ import javax.xml.transform.stream.StreamResult; +import org.apache.fop.util.CharUtilities; import org.w3c.dom.Document; import org.xml.sax.SAXException; @@ -1061,12 +1062,14 @@ protected void renderText(TextArea text) { int rx = currentIPPosition + text.getBorderAndPaddingWidthStart(); int bl = currentBPPosition + text.getBlockProgressionOffset() + text.getBaselineOffset(); textUtil.flush(); + textUtil.resetLetterSpaces(); textUtil.setStartPosition(rx, bl); textUtil.setSpacing(text.getTextLetterSpaceAdjust(), text.getTextWordSpaceAdjust()); documentHandler.getContext().setHyphenated(text.isHyphenated()); super.renderText(text); textUtil.flush(); + textUtil.resetLetterSpaces(); renderTextDecoration(tf, size, text, bl, rx); documentHandler.getContext().setHyphenated(false); resetStructurePointer(); @@ -1077,6 +1080,21 @@ protected void renderWord(WordArea word) { Font font = getFontFromArea(word.getParentArea()); String s = word.getWord(); + if (word.isUpright()) { + textUtil.flush(); + int rx = currentIPPosition + word.getParentArea().getBorderAndPaddingWidthStart() + textUtil.getNextStart(s.charAt(0)); + textUtil.setStartPosition(rx, textUtil.starty); + saveGraphicsState(); + AffineTransform positionTransform = new AffineTransform(); + positionTransform.rotate(Math.toRadians(-90), (textUtil.startx + word.getAllocIPD() / 2.0) / 1000.0, (textUtil.starty - (font.getAscender() + font.getDescender()) / 2.0) / 1000.0); + concatenateTransformationMatrix(positionTransform); + } else if (textUtil.text.length() == 0) { + // if havn't add other thing, add space + int rx = currentIPPosition + word.getParentArea().getBorderAndPaddingWidthStart() + + textUtil.getNextStart(s.isEmpty() ? ' ' : s.charAt(0)); + textUtil.setStartPosition(rx, textUtil.starty); + } + int[][] dp = word.getGlyphPositionAdjustments(); Area parentArea = word.getParentArea(); assert (parentArea instanceof AbstractTextArea); @@ -1092,6 +1110,13 @@ protected void renderWord(WordArea word) { } textUtil.nextIsSpace = word.isNextIsSpace(); + if (word.isUpright()) { + textUtil.flush(); + // don't known if next is a space, set start point. + int rx = currentIPPosition + word.getAllocIPD() + word.getParentArea().getBorderAndPaddingWidthStart() + textUtil.letterSpacesIPD; + textUtil.setStartPosition(rx, textUtil.starty); + restoreGraphicsState(); + } super.renderWord(word); } @@ -1180,8 +1205,30 @@ private class TextUtil { private int tls; private int tws; private boolean nextIsSpace; + private int letterSpacesIPD = 0; + private int lastChar = -1; + + int getNextStart(char ch) { + if (ch != CharUtilities.SPACE && ch != CharUtilities.NBSPACE) { + if (lastChar != -1 && lastChar != CharUtilities.SPACE && lastChar != CharUtilities.NBSPACE) { + return letterSpacesIPD + tls; + } + } + return letterSpacesIPD; + } + + void resetLetterSpaces() { + letterSpacesIPD = 0; + lastChar = -1; + } void addChar(char ch) { + if (ch != CharUtilities.SPACE && ch != CharUtilities.NBSPACE) { + if (lastChar != -1 && lastChar != CharUtilities.SPACE && lastChar != CharUtilities.NBSPACE) { + letterSpacesIPD += tls; + } + } + lastChar = ch; text.append(ch); } diff --git a/fop-core/src/main/java/org/apache/fop/render/java2d/CustomFontMetricsMapper.java b/fop-core/src/main/java/org/apache/fop/render/java2d/CustomFontMetricsMapper.java index 4e656cea8d1..fc6a78e31b7 100644 --- a/fop-core/src/main/java/org/apache/fop/render/java2d/CustomFontMetricsMapper.java +++ b/fop-core/src/main/java/org/apache/fop/render/java2d/CustomFontMetricsMapper.java @@ -240,9 +240,9 @@ public boolean performsPositioning() { /** * {@inheritDoc} */ - public int[][] performPositioning(CharSequence cs, String script, String language, int fontSize) { + public int[][] performPositioning(CharSequence cs, String script, String language, int fontSize, boolean isVertical) { if (typeface instanceof Positionable) { - return ((Positionable) typeface).performPositioning(cs, script, language, fontSize); + return ((Positionable) typeface).performPositioning(cs, script, language, fontSize, isVertical); } else { return null; } @@ -251,9 +251,9 @@ public int[][] performPositioning(CharSequence cs, String script, String languag /** * {@inheritDoc} */ - public int[][] performPositioning(CharSequence cs, String script, String language) { + public int[][] performPositioning(CharSequence cs, String script, String language, boolean isVertical) { if (typeface instanceof Positionable) { - return ((Positionable) typeface).performPositioning(cs, script, language); + return ((Positionable) typeface).performPositioning(cs, script, language, isVertical); } else { return null; } @@ -274,9 +274,9 @@ public boolean performsSubstitution() { * {@inheritDoc} */ public CharSequence performSubstitution(CharSequence cs, String script, String language, List associations, - boolean retainControls) { + boolean retainControls, boolean isVertical) { if (typeface instanceof Substitutable) { - return ((Substitutable) typeface).performSubstitution(cs, script, language, associations, retainControls); + return ((Substitutable) typeface).performSubstitution(cs, script, language, associations, retainControls, isVertical); } else { return cs; } @@ -286,9 +286,9 @@ public CharSequence performSubstitution(CharSequence cs, String script, String l * {@inheritDoc} */ public CharSequence reorderCombiningMarks(CharSequence cs, int[][] gpa, - String script, String language, List associations) { + String script, String language, List associations, boolean isVertical) { if (typeface instanceof Substitutable) { - return ((Substitutable) typeface).reorderCombiningMarks(cs, gpa, script, language, associations); + return ((Substitutable) typeface).reorderCombiningMarks(cs, gpa, script, language, associations, isVertical); } else { return cs; } diff --git a/fop-core/src/main/java/org/apache/fop/svg/font/FOPGVTGlyphVector.java b/fop-core/src/main/java/org/apache/fop/svg/font/FOPGVTGlyphVector.java index 0f92be578da..d96c2b615f0 100644 --- a/fop-core/src/main/java/org/apache/fop/svg/font/FOPGVTGlyphVector.java +++ b/fop-core/src/main/java/org/apache/fop/svg/font/FOPGVTGlyphVector.java @@ -93,7 +93,7 @@ public void performDefaultLayout() { boolean retainControls = false; GlyphMapping mapping = GlyphMapping.doGlyphMapping(text, text.getBeginIndex(), text.getEndIndex(), f, letterSpaceIPD, letterSpaceAdjustments, '\0', '\0', - false, text.getBidiLevel(), true, true, retainControls); + false, text.getBidiLevel(), true, true, retainControls, false); CharacterIterator glyphAsCharIter = mapping.mapping != null ? new StringCharacterIterator(mapping.mapping) : text.getIterator(); this.glyphs = buildGlyphs(f, glyphAsCharIter); diff --git a/fop-core/src/test/java/org/apache/fop/complexscripts/fonts/GSUBTestCase.java b/fop-core/src/test/java/org/apache/fop/complexscripts/fonts/GSUBTestCase.java index ad43b920f74..fabd4299423 100644 --- a/fop-core/src/test/java/org/apache/fop/complexscripts/fonts/GSUBTestCase.java +++ b/fop-core/src/test/java/org/apache/fop/complexscripts/fonts/GSUBTestCase.java @@ -205,7 +205,7 @@ public void testSubRuleSets() throws Exception { MultiByteFont font = (MultiByteFont) fontLoader.getFont(); String in = "\u006A\u0301"; String out = "\u0237\u0301"; - String sub = font.performSubstitution(in, "latn", "dflt", null, false).toString(); + String sub = font.performSubstitution(in, "latn", "dflt", null, false, false).toString(); assertEquals(sub, out); } } diff --git a/fop-core/src/test/java/org/apache/fop/complexscripts/scripts/DefaultScriptTestCase.java b/fop-core/src/test/java/org/apache/fop/complexscripts/scripts/DefaultScriptTestCase.java index a7fec3b4eb0..dcb91647e0d 100644 --- a/fop-core/src/test/java/org/apache/fop/complexscripts/scripts/DefaultScriptTestCase.java +++ b/fop-core/src/test/java/org/apache/fop/complexscripts/scripts/DefaultScriptTestCase.java @@ -40,7 +40,7 @@ public void testProcessorReorder() { int[][] gpa = new int[2][1]; gpa[0][0] = 1; gpa[1][0] = 1; - String actual = getFont().reorderCombiningMarks(in, gpa, OTFScript.DEFAULT, null, null).toString(); + String actual = getFont().reorderCombiningMarks(in, gpa, OTFScript.DEFAULT, null, null, false).toString(); Assert.assertEquals(actual.charAt(0), 803); } @@ -48,7 +48,7 @@ public void testProcessorReorder() { public void testProcessorNoReorder() { String in = "\u00F6\u0323"; int[][] gpa = new int[2][1]; - String actual = getFont().reorderCombiningMarks(in, gpa, OTFScript.DEFAULT, null, null).toString(); + String actual = getFont().reorderCombiningMarks(in, gpa, OTFScript.DEFAULT, null, null, false).toString(); Assert.assertEquals(actual.charAt(0), 57344); } @@ -57,7 +57,7 @@ public void testProcessorReorder2() { String in = "S\u0323\u0323;"; int[][] gpa = new int[4][1]; gpa[2][0] = 1; - String actual = getFont().reorderCombiningMarks(in, gpa, OTFScript.DEFAULT, null, null).toString(); + String actual = getFont().reorderCombiningMarks(in, gpa, OTFScript.DEFAULT, null, null, false).toString(); Assert.assertEquals(actual.charAt(0), 803); Assert.assertEquals(actual.charAt(1), 57344); Assert.assertEquals(actual.charAt(2), 803); diff --git a/fop-core/src/test/java/org/apache/fop/complexscripts/scripts/KhmerTestCase.java b/fop-core/src/test/java/org/apache/fop/complexscripts/scripts/KhmerTestCase.java index 2268fee3050..9261b496e6c 100644 --- a/fop-core/src/test/java/org/apache/fop/complexscripts/scripts/KhmerTestCase.java +++ b/fop-core/src/test/java/org/apache/fop/complexscripts/scripts/KhmerTestCase.java @@ -76,14 +76,14 @@ public void testPositioning() { GlyphPositioningTable gpt = new GlyphPositioningTable(null, lookups, Collections.singletonList(subtable5), processors); - ScriptProcessor scriptProcessor = ScriptProcessor.getInstance(OTFScript.KHMER, processors); + ScriptProcessor scriptProcessor = ScriptProcessor.getInstance(OTFScript.KHMER, processors, false); MultiByteFont multiByteFont = new MultiByteFont(null, null); GlyphSequence glyphSequence = multiByteFont.charSequenceToGlyphSequence("test", null); scriptProcessor.preProcess("test", multiByteFont, null); scriptProcessor.substitute( glyphSequence, OTFScript.KHMER, OTFLanguage.DEFAULT, new GlyphTable.UseSpec[0], null); int[][] adjustments = new int[4][1]; - gpt.position(glyphSequence, OTFScript.KHMER, OTFLanguage.DEFAULT, 0, null, adjustments); + gpt.position(glyphSequence, OTFScript.KHMER, OTFLanguage.DEFAULT, 0, null, adjustments, false); Assert.assertArrayEquals(adjustments[1], new int[]{12}); } diff --git a/fop-core/src/test/java/org/apache/fop/complexscripts/scripts/ThaiTestCase.java b/fop-core/src/test/java/org/apache/fop/complexscripts/scripts/ThaiTestCase.java index 27ac791972b..6549e6403fd 100644 --- a/fop-core/src/test/java/org/apache/fop/complexscripts/scripts/ThaiTestCase.java +++ b/fop-core/src/test/java/org/apache/fop/complexscripts/scripts/ThaiTestCase.java @@ -43,7 +43,7 @@ public void testProcessor() { GlyphClassTable.createClassTable(Arrays.asList(0, GlyphDefinitionTable.GLYPH_CLASS_MARK)), null); font.setGDEF( new GlyphDefinitionTable(Collections.singletonList(table), new HashMap())); - String actual = font.reorderCombiningMarks(in, null, OTFScript.THAI, null, null).toString(); + String actual = font.reorderCombiningMarks(in, null, OTFScript.THAI, null, null, false).toString(); Assert.assertTrue(actual.endsWith("\u0E2A")); } } diff --git a/fop-core/src/test/java/org/apache/fop/complexscripts/scripts/arabic/ArabicJoinersTestCase.java b/fop-core/src/test/java/org/apache/fop/complexscripts/scripts/arabic/ArabicJoinersTestCase.java index 6b34379aa53..4dcc518d7b0 100644 --- a/fop-core/src/test/java/org/apache/fop/complexscripts/scripts/arabic/ArabicJoinersTestCase.java +++ b/fop-core/src/test/java/org/apache/fop/complexscripts/scripts/arabic/ArabicJoinersTestCase.java @@ -75,7 +75,7 @@ public class ArabicJoinersTestCase { @Test public void testArabicJoiners() { String script = CharScript.scriptTagFromCode(CharScript.SCRIPT_ARABIC); - ScriptProcessor sp = ScriptProcessor.getInstance(script, new HashMap()); + ScriptProcessor sp = ScriptProcessor.getInstance(script, new HashMap(), false); assertTrue(sp != null); ScriptContextTester sct = sp.getSubstitutionContextTester(); assertTrue(sct != null); diff --git a/fop-core/src/test/java/org/apache/fop/complexscripts/scripts/arabic/ArabicWordFormsTestCase.java b/fop-core/src/test/java/org/apache/fop/complexscripts/scripts/arabic/ArabicWordFormsTestCase.java index 118961aa47d..343774b73fb 100644 --- a/fop-core/src/test/java/org/apache/fop/complexscripts/scripts/arabic/ArabicWordFormsTestCase.java +++ b/fop-core/src/test/java/org/apache/fop/complexscripts/scripts/arabic/ArabicWordFormsTestCase.java @@ -143,10 +143,10 @@ private void processWordForms(List data) { int[][] paa = (int[][]) d[3]; GlyphSequence tigs = tf.mapCharsToGlyphs(wf); assertSameGlyphs(iga, getGlyphs(tigs), "input glyphs", wf, tfn); - GlyphSequence togs = gsub.substitute(tigs, script, language); + GlyphSequence togs = gsub.substitute(tigs, script, language, false); assertSameGlyphs(oga, getGlyphs(togs), "output glyphs", wf, tfn); int[][] tpaa = new int [ togs.getGlyphCount() ] [ 4 ]; - if (gpos.position(togs, script, language, 1000, widths, tpaa)) { + if (gpos.position(togs, script, language, 1000, widths, tpaa, false)) { assertSameAdjustments(paa, tpaa, wf, tfn); } else if (paa != null) { assertEquals("unequal adjustment count, word form(" + wf + "), font (" + tfn + ")", paa.length, 0); diff --git a/fop-core/src/test/java/org/apache/fop/complexscripts/scripts/arabic/GenerateArabicTestData.java b/fop-core/src/test/java/org/apache/fop/complexscripts/scripts/arabic/GenerateArabicTestData.java index 9c2cd78e06f..6814c547423 100644 --- a/fop-core/src/test/java/org/apache/fop/complexscripts/scripts/arabic/GenerateArabicTestData.java +++ b/fop-core/src/test/java/org/apache/fop/complexscripts/scripts/arabic/GenerateArabicTestData.java @@ -108,9 +108,9 @@ private static List compile(String script, String language, String spn, String t String wf; while ((wf = lr.readLine()) != null) { GlyphSequence igs = tf.mapCharsToGlyphs(wf); - GlyphSequence ogs = gsub.substitute(igs, script, language); + GlyphSequence ogs = gsub.substitute(igs, script, language, false); int[][] paa = new int [ ogs.getGlyphCount() ] [ 4 ]; - if (!gpos.position(ogs, script, language, 1000, widths, paa)) { + if (!gpos.position(ogs, script, language, 1000, widths, paa, false)) { paa = null; } data.add(new Object[] { wf, getGlyphs(igs), getGlyphs(ogs), paa }); diff --git a/fop/examples/fo/build.xml b/fop/examples/fo/build.xml index de83b945c82..cad7bf2b409 100644 --- a/fop/examples/fo/build.xml +++ b/fop/examples/fo/build.xml @@ -73,6 +73,9 @@ + + + diff --git a/fop/examples/fo/japanese/vertical_writing.fo b/fop/examples/fo/japanese/vertical_writing.fo new file mode 100644 index 00000000000..6c14bdff187 --- /dev/null +++ b/fop/examples/fo/japanese/vertical_writing.fo @@ -0,0 +1,234 @@ + + + + + + + + + + + + + + + 縦書きサンプル + + + + + + + + + + + + 目 次 + + + Item 1 + + + + + + + + + Item 2 + + + + + + + + + Item 3 + + + + + + + + fo:block: + + 「天の人を生ずるは億兆皆同一轍にて、之に附与するに動かす可からざるの通義を以てす。即ち其通義とは人の自 + + + 他より之を如何ともす可らざるものなり + + から生命を保し自由を求め幸福を祈るの類にて、他より之を如何ともす可らざるものなり。」 + ――『アメリカ独立宣言』 + + fo:block-container: + + + 「天の人を生ずるは億兆皆同一轍にて、之に附与するに動かす可からざるの通義を以てす。即ち其通義とは人の自から生命を保し自由を求め幸福を祈るの類にて、他より之を如何ともす可らざるものなり。」 + ――『アメリカ独立宣言』 + + + fo:table: + + + + + + 「天の人を生ずるは億兆皆同一轍にて、之に附与するに動かす可からざるの通義を以てす。即ち其通義とは人の自から生命を保し自由を求め幸福を祈るの類にて、他より之を如何ともす可らざるものなり。」 + ――『アメリカ独立宣言』 + + + + + + + “我等之见解为,下述真理不证自明:凡人生而平等,秉造物者之赐,拥诸无可转让之权利,包含生命权、自由权、与追寻幸福之权。” + ——《美国独立宣言》 + + + 平成 + + 29 + + + + 8 + + + + 11 + + 日(縦中横サンプル) + + + + + 縦書きと横書きの使い分け + + 現代日本においては、縦書きも横書きもともに用いられる。 + 縦書き(縦組み)は、日本語本来の記法である伝統と矜持と共に、書道作品のほとんど、国語の教科書、文芸(小説、詩歌、戯曲など)、新聞などで用いられる。漫画もその戦前からの伝統を踏襲しており、コマ運びは右横進行、吹出しの台詞は縦書きが標準であるが、左開きに製本された場合、コマ運びだけは、右横進行のことも左横進行のこともある。自然科学関連の書籍でも、数式などを用いない啓蒙書では、縦書きの例が依然として多い。社会科学系の書物も、数理経済学、会計学の専門書を除くと縦書きが多い。公文書においては法令や法案、官報、あるいは国会での決議と決議案が縦書きにされる。縦書きおよび右横書き基調の綴じ本は、右開きに製本される。 + 横書き(横組み)は、例えば、外国語、数学、科学、音楽になどに関する専門書、つまり、横書きの言語、数式、楽譜を含むような文書のほとんどで使われる。コンピュータの出力もほとんど横書きである。映画・ゲーム情報誌なども、横長の画面写真を扱うレイアウトの性質上、横書きが主流である。 左横書き基調の綴本は、左開きに製本される。 + シナリオも縦書きで出版されるのが普通であるが、英語学習用の洋画の対訳つきシナリオ書などは横書きである。 + 社会科学系の書物では、副島隆彦の『アメリカ政治思想の大研究』は、人名や専門用語などに正式な英語表記が併記されるために横書きで出版された。しかし、文庫化されたときに縦書きになった。 + 数式を多用する経済学の場合、専門書は横書きの場合も多いが、経済評論などの場合は縦が普通である。『資本論』も縦書きで出版されることが多い。小室直樹の経済学の啓蒙書は、数式を使うが縦書きである。トム・ピーターズの経営書の訳書も縦書きで出版されていたが、「マニフェスト・シリーズ」は横書きである。 + 学校教育の教科書では、国語に属する分野以外はほぼ横書きが用いられる。社会科が縦書きだった時期も昭和60年代まであったが、その後は横書きになった。 + 横書き基調の書面の左右端などのスペースに縦書きが用いられたり、逆に縦書き基調の書面の上下端に横書きが用いられることも珍しくなく、こういったことは縦横両用の日本語組版の強みといえる。新聞では、見出しにおいてデザインやレイアウトの都合または強調のために、横書きを使うこともある。またテレビ・ラジオの番組予定欄(ラテ欄)は、原則として横書きである。 + + + 縦中横(たてちゅうよこ)は、縦組みの文書の中で横組みすることである。読みは「たてちゅうよこ」であり、「たてなかよこ」ではない(JIS X 4051で規定)。縦組みの行の中で数文字(通常2、3文字。まれに4文字以上)の欧字や数字を、1文字分の高さで左横書きに配置する。 + 縦中横を用いる場合、前の文字と後ろの文字の間に空白は開けず、行の中心が、縦中横全体の中心にくるように文字を配置する。また、縦中横の中の文字数が多い場合、それぞれの文字の幅を文字数に応じて三分(さんぶ。通常の文字の幅の3分の1)や四分(しぶ。同4分の1)などとすることで、縦中横にした箇所の幅が前後の文字と揃うように配慮する。 + + + + + + + 横書きサンプル + + + + + 「天の人を生ずるは億兆皆同一轍にて、之に附与するに動かす可からざるの通義を以てす。即ち其通義とは人の自から生命を保し自由を求め幸福を祈るの類にて、他より之を如何ともす可らざるものなり。」 + ――『アメリカ独立宣言』 + + + “我等之见解为,下述真理不证自明:凡人生而平等,秉造物者之赐,拥诸无可转让之权利,包含生命权、自由权、与追寻幸福之权。” + ——《美国独立宣言》 + + + 平成 + + 29 + + + + 8 + + + + 11 + + 日(縦中横サンプル) + + + + + 縦書きと横書きの使い分け + + 現代日本においては、縦書きも横書きもともに用いられる。 + 縦書き(縦組み)は、日本語本来の記法である伝統と矜持と共に、書道作品のほとんど、国語の教科書、文芸(小説、詩歌、戯曲など)、新聞などで用いられる。漫画もその戦前からの伝統を踏襲しており、コマ運びは右横進行、吹出しの台詞は縦書きが標準であるが、左開きに製本された場合、コマ運びだけは、右横進行のことも左横進行のこともある。自然科学関連の書籍でも、数式などを用いない啓蒙書では、縦書きの例が依然として多い。社会科学系の書物も、数理経済学、会計学の専門書を除くと縦書きが多い。公文書においては法令や法案、官報、あるいは国会での決議と決議案が縦書きにされる。縦書きおよび右横書き基調の綴じ本は、右開きに製本される。 + 横書き(横組み)は、例えば、外国語、数学、科学、音楽になどに関する専門書、つまり、横書きの言語、数式、楽譜を含むような文書のほとんどで使われる。コンピュータの出力もほとんど横書きである。映画・ゲーム情報誌なども、横長の画面写真を扱うレイアウトの性質上、横書きが主流である。 左横書き基調の綴本は、左開きに製本される。 + シナリオも縦書きで出版されるのが普通であるが、英語学習用の洋画の対訳つきシナリオ書などは横書きである。 + 社会科学系の書物では、副島隆彦の『アメリカ政治思想の大研究』は、人名や専門用語などに正式な英語表記が併記されるために横書きで出版された。しかし、文庫化されたときに縦書きになった。 + 数式を多用する経済学の場合、専門書は横書きの場合も多いが、経済評論などの場合は縦が普通である。『資本論』も縦書きで出版されることが多い。小室直樹の経済学の啓蒙書は、数式を使うが縦書きである。トム・ピーターズの経営書の訳書も縦書きで出版されていたが、「マニフェスト・シリーズ」は横書きである。 + 学校教育の教科書では、国語に属する分野以外はほぼ横書きが用いられる。社会科が縦書きだった時期も昭和60年代まであったが、その後は横書きになった。 + 横書き基調の書面の左右端などのスペースに縦書きが用いられたり、逆に縦書き基調の書面の上下端に横書きが用いられることも珍しくなく、こういったことは縦横両用の日本語組版の強みといえる。新聞では、見出しにおいてデザインやレイアウトの都合または強調のために、横書きを使うこともある。またテレビ・ラジオの番組予定欄(ラテ欄)は、原則として横書きである。 + + + Many East Asian scripts can be written horizontally or vertically. The Chinese, Japanese and Korean scripts can be oriented in either direction, as they consist mainly of disconnected syllabic units and/or ideographic units, each occupying a square block of space, thus allowing for flexibility for which direction texts can be written, be it horizontally from left-to-right, horizontally from right-to-left, vertically from top-to-bottom, and even vertically from bottom-to-top. On the other hand, the traditional Kapampangan script, Mongolian script and its offshoots (like Manchu) are written vertically from top-to-bottom. + Horizontal writing is known in Chinese as hengpai (simplified Chinese: 横排; traditional Chinese: 橫排; pinyin: héngpái; literally: "horizontal alignment"), in Japanese as yokogaki (横書き, "horizontal writing", also yokogumi, 横組み), and in Korean as garosseugi (가로쓰기) or hoengseo (횡서; 橫書). + Vertical writing is known respectively as zongpai (simplified Chinese: 纵排; traditional Chinese: 縱排; pinyin: zōngpái; literally: "vertical alignment"), tategaki (縦書き, "vertical writing", also tategumi, 縦組み), or serosseugi (세로쓰기) or jongseo (종서; 縱書). + Traditionally, Chinese, Japanese, and Korean are written vertically in columns going from top to bottom and ordered from right to left, with each new column starting to the left of the preceding one. The stroke order and stroke direction of Chinese characters (hanzi in Chinese, kanji in Japanese, hanja in Korean), Japanese kana, and Korean Hangul all facilitate writing in this manner. In addition, writing in vertical columns from right to left facilitated writing with a brush in the right hand while continually unrolling the sheet of paper or scroll with the left. Since the nineteenth century, it has become increasingly common for these languages to be written horizontally, from left to right, with successive rows going from top to bottom, under the influence of European languages such as English, although vertical writing is still frequently used in Japan, Taiwan, Hong Kong and Macau. + + + + + 縦中横(たてちゅうよこ)は、縦組みの文書の中で横組みすることである。読みは「たてちゅうよこ」であり、「たてなかよこ」ではない(JIS X 4051で規定)。縦組みの行の中で数文字(通常2、3文字。まれに4文字以上)の欧字や数字を、1文字分の高さで左横書きに配置する。 + 縦中横を用いる場合、前の文字と後ろの文字の間に空白は開けず、行の中心が、縦中横全体の中心にくるように文字を配置する。また、縦中横の中の文字数が多い場合、それぞれの文字の幅を文字数に応じて三分(さんぶ。通常の文字の幅の3分の1)や四分(しぶ。同4分の1)などとすることで、縦中横にした箇所の幅が前後の文字と揃うように配慮する。 + + + + XSL-FOにはルビに関する仕様がありません。しかし、fo:inline-containerを使ってルビを表現することが可能です。 + + XMLとは + じょほう + 情報 + + ひょうげん + 表現するための + あたら + しい + ほうほう + 方法です。 + + + + + + + + + + 15文字まで出力出来る枠により + + + + + + + 15文字まで出力出来る枠により多い文字を入れると自動縮小されます。 + + + + + + + + + \ No newline at end of file