ICU-6824 Merge in font testing
X-SVN-Rev: 26510
This commit is contained in:
parent
86c81cfaae
commit
4b2f25a6c1
@ -0,0 +1,649 @@
|
||||
/*
|
||||
**********************************************************************
|
||||
* Copyright (c) 2009, Google, International Business Machines
|
||||
* Corporation and others. All Rights Reserved.
|
||||
**********************************************************************
|
||||
*/
|
||||
package com.ibm.icu.dev.tool.cldr;
|
||||
import java.awt.Font;
|
||||
import java.awt.GraphicsEnvironment;
|
||||
import java.awt.Shape;
|
||||
import java.awt.font.FontRenderContext;
|
||||
import java.awt.font.GlyphVector;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.geom.PathIterator;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
import java.util.TreeSet;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import com.ibm.icu.dev.test.util.BagFormatter;
|
||||
import com.ibm.icu.dev.test.util.Row;
|
||||
import com.ibm.icu.dev.test.util.TransliteratorUtilities;
|
||||
import com.ibm.icu.dev.test.util.UnicodeMap;
|
||||
import com.ibm.icu.dev.test.util.UnicodeMapIterator;
|
||||
import com.ibm.icu.dev.test.util.Row.R2;
|
||||
import com.ibm.icu.dev.test.util.Tabber.HTMLTabber;
|
||||
import com.ibm.icu.dev.test.util.UnicodeMap.Composer;
|
||||
import com.ibm.icu.dev.test.util.XEquivalenceClass.SetMaker;
|
||||
import com.ibm.icu.impl.Utility;
|
||||
import com.ibm.icu.lang.UCharacter;
|
||||
import com.ibm.icu.lang.UScript;
|
||||
import com.ibm.icu.text.Collator;
|
||||
import com.ibm.icu.text.Normalizer;
|
||||
import com.ibm.icu.text.UTF16;
|
||||
import com.ibm.icu.text.UnicodeSet;
|
||||
import com.ibm.icu.text.UnicodeSetIterator;
|
||||
|
||||
|
||||
public class CheckSystemFonts {
|
||||
|
||||
static String outputDirectoryName;
|
||||
static Set<String> SKIP_SHAPES = new HashSet<String>();
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
System.out.println("Arguments:\t" + Arrays.asList(args));
|
||||
if (args.length < 2) {
|
||||
throw new IllegalArgumentException("Need command-line args:" +
|
||||
"\n\t\tfont-name-regex" +
|
||||
"\n\t\toutput-directory"
|
||||
);
|
||||
}
|
||||
Matcher nameMatcher = Pattern.compile(args[0], Pattern.CASE_INSENSITIVE).matcher("");
|
||||
outputDirectoryName = args[1].trim();
|
||||
File outputDirectory = new File(outputDirectoryName);
|
||||
if (!outputDirectory.isDirectory()) {
|
||||
throw new IllegalArgumentException("2nd arg must be valid directory");
|
||||
}
|
||||
loadSkipShapes();
|
||||
|
||||
Map<UnicodeSet,Set<String>> data = new TreeMap<UnicodeSet, Set<String>>();
|
||||
Map<String, Font> fontMap = new TreeMap<String, Font>();
|
||||
getFontData(nameMatcher, data, fontMap);
|
||||
|
||||
showInvisibles();
|
||||
showSameGlyphs();
|
||||
|
||||
UnicodeMap<Set<String>> map = showEquivalentCoverage(data);
|
||||
|
||||
showRawCoverage(data);
|
||||
|
||||
Map<Set<String>, String> toShortName = showRawCoverage(map);
|
||||
|
||||
showFullCoverage(map, toShortName);
|
||||
}
|
||||
|
||||
private static void loadSkipShapes() {
|
||||
try {
|
||||
BufferedReader in = BagFormatter.openUTF8Reader(outputDirectoryName, "skip_fonts.txt");
|
||||
while (true) {
|
||||
String line = in.readLine();
|
||||
if (line == null) break;
|
||||
String[] fonts = line.trim().split("\\s+");
|
||||
for (String font : fonts) {
|
||||
SKIP_SHAPES.add(font);
|
||||
}
|
||||
}
|
||||
in.close();
|
||||
} catch (IOException e) {
|
||||
System.err.println("Couldn't open:\t" + outputDirectoryName + "/" + "skip_fonts.txt");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static final Collator English = Collator.getInstance();
|
||||
|
||||
static {
|
||||
English.setStrength(Collator.SECONDARY);
|
||||
}
|
||||
|
||||
public static final UnicodeSet DONT_CARE = new UnicodeSet("[[:cn:][:co:][:cs:]]").freeze();
|
||||
public static final UnicodeSet COVERAGE = new UnicodeSet(DONT_CARE).complement().freeze();
|
||||
|
||||
private static final Comparator<String> SHORTER_FIRST = new Comparator<String>() {
|
||||
public int compare(String n1, String n2) {
|
||||
int result = n1.length() - n2.length();
|
||||
if (result != 0) return result;
|
||||
return n1.compareTo(n2);
|
||||
}
|
||||
};
|
||||
|
||||
private static final Comparator<UnicodeSet> LONGER_SET_FIRST = new Comparator<UnicodeSet>() {
|
||||
public int compare(UnicodeSet n1, UnicodeSet n2) {
|
||||
int result = n1.size() - n2.size();
|
||||
if (result != 0) return -result;
|
||||
return n1.compareTo(n2);
|
||||
}
|
||||
};
|
||||
|
||||
private static final Comparator<Collection> SHORTER_COLLECTION_FIRST = new Comparator<Collection>() {
|
||||
public int compare(Collection n1, Collection n2) {
|
||||
int result = n1.size() - n2.size();
|
||||
if (result != 0) return result;
|
||||
return UnicodeSet.compare(n1, n2);
|
||||
}
|
||||
};
|
||||
|
||||
private static final HashSet SKIP_TERMS = new HashSet(Arrays.asList("black", "blackitalic", "bold", "boldit", "bolditalic", "bolditalicmt", "boldmt",
|
||||
"boldob", "boldoblique", "boldslanted", "book", "bookitalic", "condensed", "condensedblack", "condensedbold", "condensedextrabold",
|
||||
"condensedlight", "condensedmedium", "extracondensed", "extralight", "heavy", "italic", "italicmt", "light", "lightit", "lightitalic", "medium",
|
||||
"mediumitalic", "oblique", "regular", "roman", "semibold", "semibolditalic", "shadow", "slanted", "ultrabold", "ultralight", "ultralightitalic"
|
||||
));
|
||||
|
||||
private static Composer<Set<String>> composer = new Composer<Set<String>>() {
|
||||
Map<R2<Set<String>, Set<String>>,Set<String>> cache = new HashMap<R2<Set<String>, Set<String>>,Set<String>>();
|
||||
public Set<String> compose(int codePoint, String string, Set<String> a, Set<String> b) {
|
||||
return a == null ? b
|
||||
: b == null ? null
|
||||
: intern(a,b);
|
||||
}
|
||||
private Set<String> intern(Set<String> a, Set<String> b) {
|
||||
R2<Set<String>, Set<String>> row = Row.of(a, b);
|
||||
Set<String> result = cache.get(row);
|
||||
if (result == null) {
|
||||
result = new TreeSet<String>(English);
|
||||
result.addAll(a);
|
||||
result.addAll(b);
|
||||
cache.put(row, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
private static void showFullCoverage(UnicodeMap<Set<String>> map, Map<Set<String>, String> toShortName) throws IOException {
|
||||
System.out.println("\n***COVERAGE:\t" + map.keySet().size() + "\n");
|
||||
PrintWriter out = BagFormatter.openUTF8Writer(outputDirectoryName, "coverage.txt");
|
||||
|
||||
for (UnicodeMapIterator<String> it = new UnicodeMapIterator<String>(map); it.nextRange();) {
|
||||
String codes = "U+" + Utility.hex(it.codepoint);
|
||||
String names = UCharacter.getExtendedName(it.codepoint);
|
||||
if (it.codepointEnd != it.codepoint) {
|
||||
codes += "..U+" + Utility.hex(it.codepointEnd);
|
||||
names += ".." + UCharacter.getExtendedName(it.codepointEnd);
|
||||
}
|
||||
out.println(codes + "\t" + toShortName.get(map.get(it.codepoint)) + "\t" + names);
|
||||
}
|
||||
|
||||
UnicodeSet missing = new UnicodeSet(COVERAGE).removeAll(map.keySet());
|
||||
out.println("\nMISSING:\t" + missing.size() + "\n");
|
||||
|
||||
UnicodeMap<String> missingMap = new UnicodeMap<String>();
|
||||
for (UnicodeSetIterator it = new UnicodeSetIterator(missing); it.next();) {
|
||||
missingMap.put(it.codepoint, UScript.getName(UScript.getScript(it.codepoint)) + "-" + getShortAge(it.codepoint));
|
||||
}
|
||||
|
||||
Set<String> sorted = new TreeSet<String>(English);
|
||||
sorted.addAll(missingMap.values());
|
||||
for (String value : sorted) {
|
||||
UnicodeSet items = missingMap.getSet(value);
|
||||
for (UnicodeSetIterator it = new UnicodeSetIterator(items); it.nextRange();) {
|
||||
String codes = "U+" + Utility.hex(it.codepoint);
|
||||
String names = UCharacter.getExtendedName(it.codepoint);
|
||||
if (it.codepointEnd != it.codepoint) {
|
||||
codes += "..U+" + Utility.hex(it.codepointEnd);
|
||||
names += ".." + UCharacter.getExtendedName(it.codepointEnd);
|
||||
}
|
||||
out.println(codes + "\t" + value + "\t" + names);
|
||||
}
|
||||
out.println();
|
||||
}
|
||||
out.close();
|
||||
}
|
||||
|
||||
private static Map<Set<String>, String> showRawCoverage(UnicodeMap<Set<String>> map) throws IOException {
|
||||
System.out.println("\n***COMBO NAMES\n");
|
||||
PrintWriter out = BagFormatter.openUTF8Writer(outputDirectoryName, "combo_names.txt");
|
||||
|
||||
int count = 0;
|
||||
Map<Set<String>, String> toShortName = new HashMap<Set<String>, String>();
|
||||
TreeSet<Set<String>> sortedValues = new TreeSet<Set<String>>(SHORTER_COLLECTION_FIRST);
|
||||
sortedValues.addAll(map.values());
|
||||
for (Set<String> value : sortedValues) {
|
||||
String shortName = "combo" + count++;
|
||||
Set<String> contained = getLargestContained(value, toShortName.keySet());
|
||||
String valueName;
|
||||
if (contained != null) {
|
||||
Set<String> remainder = new TreeSet<String>();
|
||||
remainder.addAll(value);
|
||||
remainder.removeAll(contained);
|
||||
valueName = toShortName.get(contained) + " + " + remainder;
|
||||
} else {
|
||||
valueName = value.toString();
|
||||
}
|
||||
toShortName.put(value, shortName);
|
||||
out.println(shortName + "\t" + valueName);
|
||||
}
|
||||
out.close();
|
||||
return toShortName;
|
||||
}
|
||||
|
||||
private static void showRawCoverage(Map<UnicodeSet, Set<String>> data) throws IOException {
|
||||
System.out.println("\n***RAW COVERAGE (bridging unassigned)\n");
|
||||
PrintWriter out = BagFormatter.openUTF8Writer(outputDirectoryName, "raw_coverage.txt");
|
||||
|
||||
for (UnicodeSet s : data.keySet()) {
|
||||
Set<String> nameSet = data.get(s);
|
||||
String name = nameSet.iterator().next();
|
||||
UnicodeSet bridged = new UnicodeSet(s).addBridges(DONT_CARE);
|
||||
out.println(name + "\t" + s.size() + "\t" + bridged);
|
||||
}
|
||||
out.close();
|
||||
}
|
||||
|
||||
private static UnicodeMap<Set<String>> showEquivalentCoverage(Map<UnicodeSet, Set<String>> data) throws IOException {
|
||||
System.out.println("\n***EQUIVALENT COVERAGE\n");
|
||||
PrintWriter out = BagFormatter.openUTF8Writer(outputDirectoryName, "equiv_coverage.txt");
|
||||
|
||||
UnicodeMap<Set<String>> map = new UnicodeMap<Set<String>>();
|
||||
|
||||
Map<String,Set<String>> nameToSingleton = new HashMap<String,Set<String>>();
|
||||
|
||||
for (UnicodeSet s : data.keySet()) {
|
||||
Set<String> nameSet = data.get(s);
|
||||
String name = nameSet.iterator().next();
|
||||
//System.out.println(s);
|
||||
Set<String> temp2 = nameToSingleton.get(name);
|
||||
if (temp2 == null) {
|
||||
temp2 = new TreeSet<String>(English);
|
||||
temp2.add(name);
|
||||
}
|
||||
map.composeWith(s, temp2, composer);
|
||||
if (nameSet.size() > 1) {
|
||||
TreeSet<String> temp = new TreeSet<String>(English);
|
||||
temp.addAll(nameSet);
|
||||
temp.remove(name);
|
||||
out.println(name + "\t" + temp);
|
||||
}
|
||||
}
|
||||
out.close();
|
||||
return map;
|
||||
}
|
||||
|
||||
private static void showSameGlyphs() throws IOException {
|
||||
System.out.println("\n***Visual Equivalences");
|
||||
PrintWriter out = BagFormatter.openUTF8Writer(outputDirectoryName, "same_glyphs.txt");
|
||||
PrintWriter out2 = BagFormatter.openUTF8Writer(outputDirectoryName, "same_glyphs.html");
|
||||
out2.println("<html><head>");
|
||||
out2.println("<meta content=\"text/html; charset=utf-8\" http-equiv=Content-Type></HEAD>");
|
||||
out2.println("<link rel='stylesheet' href='index.css' type='text/css'>");
|
||||
out2.println("</head><body><table>");
|
||||
HTMLTabber tabber = new HTMLTabber();
|
||||
|
||||
out2.println(tabber.process("Code1\tCode2\tNFC1\tNFC1\tCh1\tCh1\tCh1/F\tCh2/F\tName1\tName2\tFonts"));
|
||||
tabber.setParameters(0, "class='c'");
|
||||
tabber.setParameters(1, "class='c'");
|
||||
tabber.setParameters(2, "class='nf'");
|
||||
tabber.setParameters(3, "class='nf'");
|
||||
tabber.setParameters(4, "class='p'");
|
||||
tabber.setParameters(5, "class='p'");
|
||||
//tabber.setParameters(6, "class='q'");
|
||||
//tabber.setParameters(7, "class='q'");
|
||||
tabber.setParameters(8, "class='n'");
|
||||
tabber.setParameters(9, "class='n'");
|
||||
tabber.setParameters(10, "class='f'");
|
||||
|
||||
for (R2<Integer,Integer> sample : equivalences.keySet()) {
|
||||
final Set<String> reasonSet = equivalences.get(sample);
|
||||
String reasons = reasonSet.toString();
|
||||
if (reasons.length() > 100) reasons = reasons.substring(0,100) + "...";
|
||||
final Integer codepoint1 = sample.get0();
|
||||
final Integer codepoint2 = sample.get1();
|
||||
|
||||
out.println("U+" + Utility.hex(codepoint1) + "\t" + "U+" + Utility.hex(codepoint2)
|
||||
+ "\t" + showNfc(codepoint1) + "\t" + showNfc(codepoint2)
|
||||
+ "\t" + showChar(codepoint1, false) + "\t" + showChar(codepoint2, false)
|
||||
+ "\t" + UCharacter.getExtendedName(codepoint1) + "\t" + UCharacter.getExtendedName(codepoint2)
|
||||
+ "\t" + reasons);
|
||||
String line = "U+" + Utility.hex(codepoint1) + "\t" + "U+" + Utility.hex(codepoint2)
|
||||
+ "\t" + showNfc(codepoint1) + "\t" + showNfc(codepoint2)
|
||||
+ "\t" + showChar(codepoint1, false) + "\t" + showChar(codepoint2, true)
|
||||
+ "\t" + showChar(codepoint1, false) + "\t" + showChar(codepoint2, true)
|
||||
+ "\t" + UCharacter.getExtendedName(codepoint1) + "\t" + UCharacter.getExtendedName(codepoint2)
|
||||
+ "\t" + reasons;
|
||||
|
||||
String fonts = "class='q' style='font-family:";
|
||||
int maxCount = 5;
|
||||
for (String font : reasonSet) {
|
||||
if (maxCount != 5) {
|
||||
fonts += ",";
|
||||
}
|
||||
fonts += font;
|
||||
--maxCount;
|
||||
if (maxCount <= 0) break;
|
||||
}
|
||||
fonts += "'";
|
||||
tabber.setParameters(6, fonts);
|
||||
tabber.setParameters(7, fonts);
|
||||
out2.println(tabber.process(line));
|
||||
}
|
||||
out2.println("</table></body>");
|
||||
out2.close();
|
||||
out.close();
|
||||
}
|
||||
|
||||
private static void showInvisibles() throws IOException {
|
||||
System.out.println("\n***Invisibles Equivalences");
|
||||
PrintWriter out = BagFormatter.openUTF8Writer(outputDirectoryName, "invisibles.txt");
|
||||
for (String sample : invisibles) {
|
||||
String reasons = invisibles.get(sample).toString();
|
||||
if (reasons.length() > 100) reasons = reasons.substring(0,100) + "...";
|
||||
int codepoint = sample.codePointAt(0);
|
||||
out.println("U+" + Utility.hex(sample)
|
||||
+ "\t" + showChar(codepoint, false)
|
||||
+ "\t" + showNfc(codepoint)
|
||||
+ "\t" + UCharacter.getExtendedName(codepoint)
|
||||
+ "\t" + reasons);
|
||||
|
||||
}
|
||||
out.close();
|
||||
}
|
||||
|
||||
private static void getFontData(Matcher nameMatcher, Map<UnicodeSet, Set<String>> data, Map<String, Font> fontMap) {
|
||||
GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment();
|
||||
Font[] fonts = env.getAllFonts();
|
||||
for (Font font : fonts) {
|
||||
if (!font.isPlain()) {
|
||||
continue;
|
||||
}
|
||||
String name = font.getName();
|
||||
int lastDash = name.lastIndexOf('-');
|
||||
String term = lastDash < 0 ? "" : name.substring(lastDash+1).toLowerCase();
|
||||
if (SKIP_TERMS.contains(term)) {
|
||||
continue;
|
||||
}
|
||||
if (nameMatcher != null && !nameMatcher.reset(name).find()) {
|
||||
continue;
|
||||
}
|
||||
fontMap.put(name,font);
|
||||
}
|
||||
for (String name : fontMap.keySet()) {
|
||||
Font font = fontMap.get(name);
|
||||
System.out.println(name);
|
||||
UnicodeSet coverage = getCoverage(font);
|
||||
Set<String> sameFonts = data.get(coverage);
|
||||
if (sameFonts == null) {
|
||||
data.put(coverage, sameFonts = new TreeSet<String>(SHORTER_FIRST));
|
||||
} else {
|
||||
System.out.println("\tNote: same coverage as " + sameFonts.iterator().next());
|
||||
}
|
||||
sameFonts.add(name);
|
||||
}
|
||||
}
|
||||
|
||||
static Comparator<Integer> NFCLower = new Comparator<Integer>() {
|
||||
public int compare(Integer o1, Integer o2) {
|
||||
boolean n1 = Normalizer.isNormalized(o1, Normalizer.NFC, 0);
|
||||
boolean n2 = Normalizer.isNormalized(o2, Normalizer.NFC, 0);
|
||||
if (n1 != n2) return n1 ? -1 : 1;
|
||||
n1 = Normalizer.isNormalized(o1, Normalizer.NFKC, 0);
|
||||
n2 = Normalizer.isNormalized(o2, Normalizer.NFKC, 0);
|
||||
if (n1 != n2) return n1 ? -1 : 1;
|
||||
return o1.compareTo(o2);
|
||||
}
|
||||
};
|
||||
|
||||
static Comparator<R2<Integer,Integer>> NFCLowerR2 = new Comparator<R2<Integer,Integer>>() {
|
||||
public int compare(R2<Integer, Integer> o1, R2<Integer, Integer> o2) {
|
||||
int diff = NFCLower.compare(o1.get0(), o2.get0());
|
||||
if (diff != 0) return diff;
|
||||
return NFCLower.compare(o1.get1(), o2.get1());
|
||||
}
|
||||
};
|
||||
|
||||
private static String showNfc(int codepoint) {
|
||||
return Normalizer.isNormalized(codepoint, Normalizer.NFC, 0) ? ""
|
||||
: Normalizer.isNormalized(codepoint, Normalizer.NFKC, 0) ? "!C" : "!K";
|
||||
}
|
||||
|
||||
private static String showChar(Integer item, boolean html) {
|
||||
return rtlProtect(UTF16.valueOf(item), html);
|
||||
}
|
||||
static UnicodeSet RTL = new UnicodeSet("[[:bc=R:][:bc=AL:][:bc=AN:]]").freeze();
|
||||
static UnicodeSet CONTROLS = new UnicodeSet("[[:cc:][:Zl:][:Zp:]]").freeze();
|
||||
static UnicodeSet INVISIBLES = new UnicodeSet("[:di:]").freeze();
|
||||
static final char LRM = '\u200E';
|
||||
|
||||
private static String rtlProtect(String source, boolean html) {
|
||||
if (CONTROLS.containsSome(source)) {
|
||||
source = "";
|
||||
} else if (INVISIBLES.containsSome(source)) {
|
||||
source = "";
|
||||
} else if (RTL.containsSome(source) || source.startsWith("\"")) {
|
||||
source = LRM + source + LRM;
|
||||
}
|
||||
return html ? TransliteratorUtilities.toHTML.transform(source) : source;
|
||||
}
|
||||
|
||||
|
||||
private static Set<String> getLargestContained(Set<String> value, Collection<Set<String>> collection) {
|
||||
Set<String> best = null;
|
||||
for (Set<String> set : collection) {
|
||||
if (best != null && best.size() > set.size()) {
|
||||
continue;
|
||||
}
|
||||
if (value.containsAll(set)) {
|
||||
best = set;
|
||||
}
|
||||
}
|
||||
return best;
|
||||
}
|
||||
|
||||
private static String getShortAge(int i) {
|
||||
String age = UCharacter.getAge(i).toString();
|
||||
return age.substring(0,age.indexOf('.',age.indexOf('.') + 1));
|
||||
}
|
||||
|
||||
static SetMaker setMaker = new SetMaker() {
|
||||
public Set make() {
|
||||
return new TreeSet();
|
||||
}
|
||||
};
|
||||
|
||||
static UnicodeMap<Set<String>> invisibles = new UnicodeMap();
|
||||
static Map<R2<Integer,Integer>, Set<String>> equivalences = new TreeMap<R2<Integer,Integer>, Set<String>>(NFCLowerR2);
|
||||
// static Set<String> SKIP_SHAPES = new HashSet<String>(Arrays.asList(
|
||||
// "MT-Extra",
|
||||
// "JCsmPC",
|
||||
// "DFKaiShu-SB-Estd-BF",
|
||||
// "LiGothicMed",
|
||||
// "LiHeiPro",
|
||||
// "LiSongPro",
|
||||
// "LiSungLight",
|
||||
// "PMingLiU",
|
||||
// "SIL-Hei-Med-Jian",
|
||||
// "SIL-Kai-Reg-Jian",
|
||||
// "CharcoalCY",
|
||||
// "GenevaCY",
|
||||
// "HelveticaCYBoldOblique",
|
||||
// "HelveticaCYOblique",
|
||||
// "HelveticaCYPlain",
|
||||
// "HoeflerText-Ornaments",
|
||||
// "Apple-Chancery",
|
||||
// "MSReferenceSpecialty",
|
||||
// "Stencil",
|
||||
// "Hooge0555",
|
||||
// "Hooge0556",
|
||||
// "Desdemona",
|
||||
// "EccentricStd",
|
||||
// "EngraversMT",
|
||||
// "MesquiteStd",
|
||||
// "RosewoodStd-Fill",
|
||||
// "Stencil",
|
||||
// "StencilStd",
|
||||
// "Osaka",
|
||||
// "Osaka-Mono",
|
||||
// "Kroeger0455",
|
||||
// "Kroeger0456",
|
||||
// "Uni0563",
|
||||
// "Uni0564",
|
||||
// "Code2001",
|
||||
// "AppleSymbols",
|
||||
// "AppleGothic",
|
||||
// "AppleMyungjo",
|
||||
// "JCkg",
|
||||
// "MalithiWeb",
|
||||
// "JCfg"
|
||||
// ));
|
||||
|
||||
// bug on Mac: http://forums.sun.com/thread.jspa?threadID=5209611
|
||||
private static UnicodeSet getCoverage(Font font) {
|
||||
String name = font.getFontName();
|
||||
boolean skipShapes = SKIP_SHAPES.contains(name);
|
||||
UnicodeSet result = new UnicodeSet();
|
||||
final FontRenderContext fontRenderContext = new FontRenderContext(null, false, false);
|
||||
char[] array = new char[1];
|
||||
char[] array2 = new char[2];
|
||||
Map<Rectangle2D,Map<Shape,UnicodeSet>> boundsToData = new TreeMap<Rectangle2D,Map<Shape,UnicodeSet>>(ShapeComparator);
|
||||
for (UnicodeSetIterator it = new UnicodeSetIterator(COVERAGE); it.next();) {
|
||||
if (font.canDisplay(it.codepoint)) {
|
||||
char[] temp;
|
||||
if (it.codepoint <= 0xFFFF) {
|
||||
array[0] = (char) it.codepoint;
|
||||
temp = array;
|
||||
} else {
|
||||
Character.toChars(it.codepoint, array2, 0);
|
||||
temp = array2;
|
||||
}
|
||||
|
||||
GlyphVector glyphVector = font.createGlyphVector(fontRenderContext, temp);
|
||||
int glyphCode = glyphVector.getGlyphCode(0);
|
||||
boolean validchar = (glyphCode > 0);
|
||||
if (!validchar) continue;
|
||||
|
||||
result.add(it.codepoint);
|
||||
|
||||
if (skipShapes) continue;
|
||||
Shape shape = glyphVector.getOutline();
|
||||
if (isInvisible(shape)) {
|
||||
Set<String> set = invisibles.get(it.codepoint);
|
||||
if (set == null) {
|
||||
invisibles.put(it.codepoint, set = new TreeSet<String>());
|
||||
}
|
||||
set.add(name);
|
||||
} else {
|
||||
Rectangle2D bounds = glyphVector.getVisualBounds();
|
||||
Map<Shape, UnicodeSet> map = boundsToData.get(bounds);
|
||||
if (map == null) {
|
||||
boundsToData.put(bounds, map = new TreeMap<Shape,UnicodeSet>(ShapeComparator));
|
||||
}
|
||||
UnicodeSet set = map.get(shape);
|
||||
if (set == null) {
|
||||
map.put(shape, set = new UnicodeSet());
|
||||
}
|
||||
if (false && set.size() != 0) {
|
||||
System.out.println("Adding " + Utility.hex(it.codepoint) + "\t" + UTF16.valueOf(it.codepoint) + "\tto " + set.toPattern(false));
|
||||
}
|
||||
set.add(it.codepoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
//System.out.println(result.size() + "\t" + result);
|
||||
for (Rectangle2D bounds : boundsToData.keySet()) {
|
||||
Map<Shape, UnicodeSet> map = boundsToData.get(bounds);
|
||||
for (Shape shape : map.keySet()) {
|
||||
UnicodeSet set = map.get(shape);
|
||||
set.removeAll(CONTROLS);
|
||||
if (set.size() != 1) {
|
||||
//System.out.println(set.toPattern(false));
|
||||
for (UnicodeSetIterator it = new UnicodeSetIterator(set); it.next();) {
|
||||
for (UnicodeSetIterator it2 = new UnicodeSetIterator(set); it2.next();) {
|
||||
int cp = it.codepoint;
|
||||
int cp2 = it2.codepoint;
|
||||
if (cp >= cp2) continue;
|
||||
R2<Integer, Integer> r = Row.of(cp, cp2);
|
||||
Set<String> reasons = equivalences.get(r);
|
||||
if (reasons == null) {
|
||||
equivalences.put(r, reasons = new TreeSet());
|
||||
}
|
||||
reasons.add(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result.freeze();
|
||||
}
|
||||
|
||||
static Comparator<Rectangle2D> RectComparator = new Comparator<Rectangle2D>() {
|
||||
|
||||
public int compare(Rectangle2D r1, Rectangle2D r2) {
|
||||
int diff;
|
||||
if (0 != (diff = compareDiff(r1.getX(),r2.getX()))) return diff;
|
||||
if (0 != (diff = compareDiff(r1.getY(),r2.getY()))) return diff;
|
||||
if (0 != (diff = compareDiff(r1.getWidth(),r2.getWidth()))) return diff;
|
||||
if (0 != (diff = compareDiff(r1.getHeight(),r2.getHeight()))) return diff;
|
||||
return 0;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
static final AffineTransform IDENTITY = new AffineTransform();
|
||||
|
||||
static boolean isInvisible(Shape shape) {
|
||||
return shape.getPathIterator(IDENTITY).isDone();
|
||||
}
|
||||
|
||||
static Comparator<Shape> ShapeComparator = new Comparator<Shape>() {
|
||||
float[] coords1 = new float[6];
|
||||
float[] coords2 = new float[6];
|
||||
|
||||
public int compare(Shape s1, Shape s2) {
|
||||
int diff;
|
||||
PathIterator p1 = s1.getPathIterator(IDENTITY);
|
||||
PathIterator p2 = s2.getPathIterator(IDENTITY);
|
||||
while (true) {
|
||||
if (p1.isDone()) {
|
||||
return p2.isDone() ? 0 : -1;
|
||||
} else if (p2.isDone()) {
|
||||
return 1;
|
||||
}
|
||||
int t1 = p1.currentSegment(coords1);
|
||||
int t2 = p2.currentSegment(coords2);
|
||||
diff = t1 - t2;
|
||||
if (diff != 0) return diff;
|
||||
/*
|
||||
* SEG_MOVETO and SEG_LINETO types returns one point,
|
||||
* SEG_QUADTO returns two points,
|
||||
* SEG_CUBICTO returns 3 points
|
||||
* and SEG_CLOSE does not return any points.
|
||||
*/
|
||||
switch (t1) {
|
||||
case PathIterator.SEG_CUBICTO:
|
||||
if (0 != (diff = compareDiff(coords1[5],coords2[5]))) return diff;
|
||||
if (0 != (diff = compareDiff(coords1[4],coords2[4]))) return diff;
|
||||
case PathIterator.SEG_QUADTO:
|
||||
if (0 != (diff = compareDiff(coords1[3],coords2[3]))) return diff;
|
||||
if (0 != (diff = compareDiff(coords1[2],coords2[2]))) return diff;
|
||||
case PathIterator.SEG_MOVETO:
|
||||
case PathIterator.SEG_LINETO:
|
||||
if (0 != (diff = compareDiff(coords1[1],coords2[1]))) return diff;
|
||||
if (0 != (diff = compareDiff(coords1[0],coords2[0]))) return diff;
|
||||
case PathIterator.SEG_CLOSE: break;
|
||||
default: throw new IllegalArgumentException();
|
||||
}
|
||||
p1.next();
|
||||
p2.next();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private static int compareDiff(float f, float g) {
|
||||
return f < g ? -1 : f > g ? 1 : 0;
|
||||
}
|
||||
private static int compareDiff(double f, double g) {
|
||||
return f < g ? -1 : f > g ? 1 : 0;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user