Created a new library directory called "FreeLib". All OpenSource RFMKII components will reside there, fontTools being the flagship.
git-svn-id: svn://svn.code.sf.net/p/fonttools/code/trunk@2 4cde692c-a291-49d1-8350-778aa11640f8
diff --git a/Lib/fontTools/__init__.py b/Lib/fontTools/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Lib/fontTools/__init__.py
diff --git a/Lib/fontTools/afmLib.py b/Lib/fontTools/afmLib.py
new file mode 100644
index 0000000..a725e83
--- /dev/null
+++ b/Lib/fontTools/afmLib.py
@@ -0,0 +1,265 @@
+"""Module for reading and writing AFM files."""
+
+# XXX reads AFM's generated by Fog, not tested with much else.
+# It does not implement the full spec (Adobe Technote 5004, Adobe Font Metrics
+# File Format Specification). Still, it should read most "common" AFM files.
+
+import re
+import string
+import types
+
+__version__ = "$Id: afmLib.py,v 1.1 1999-12-16 21:34:51 Just Exp $"
+
+
+# every single line starts with a "word"
+identifierRE = re.compile("^([A-Za-z]+).*")
+
+# regular expression to parse char lines
+charRE = re.compile(
+ "(-?\d+)" # charnum
+ "\s*;\s*WX\s+" # ; WX
+ "(\d+)" # width
+ "\s*;\s*N\s+" # ; N
+ "(\.?[A-Za-z0-9_]+)" # charname
+ "\s*;\s*B\s+" # ; B
+ "(-?\d+)" # left
+ "\s+" #
+ "(-?\d+)" # bottom
+ "\s+" #
+ "(-?\d+)" # right
+ "\s+" #
+ "(-?\d+)" # top
+ "\s*;\s*" # ;
+ )
+
+# regular expression to parse kerning lines
+kernRE = re.compile(
+ "([.A-Za-z0-9_]+)" # leftchar
+ "\s+" #
+ "([.A-Za-z0-9_]+)" # rightchar
+ "\s+" #
+ "(-?\d+)" # value
+ "\s*" #
+ )
+
+error = "AFM.error"
+
+class AFM:
+
+ _keywords = ['StartFontMetrics',
+ 'EndFontMetrics',
+ 'StartCharMetrics',
+ 'EndCharMetrics',
+ 'StartKernData',
+ 'StartKernPairs',
+ 'EndKernPairs',
+ 'EndKernData', ]
+
+ def __init__(self, path = None):
+ self._attrs = {}
+ self._chars = {}
+ self._kerning = {}
+ self._index = {}
+ self._comments = []
+ if path is not None:
+ self.read(path)
+
+ def read(self, path):
+ lines = readlines(path)
+ for line in lines:
+ if not string.strip(line):
+ continue
+ m = identifierRE.match(line)
+ if m is None:
+ raise error, "syntax error in AFM file: " + `line`
+
+ pos = m.regs[1][1]
+ word = line[:pos]
+ rest = string.strip(line[pos:])
+ if word in self._keywords:
+ continue
+ if word == 'C':
+ self.parsechar(rest)
+ elif word == "KPX":
+ self.parsekernpair(rest)
+ else:
+ self.parseattr(word, rest)
+
+ def parsechar(self, rest):
+ m = charRE.match(rest)
+ if m is None:
+ raise error, "syntax error in AFM file: " + `rest`
+ things = []
+ for fr, to in m.regs[1:]:
+ things.append(rest[fr:to])
+ charname = things[2]
+ del things[2]
+ charnum, width, l, b, r, t = map(string.atoi, things)
+ self._chars[charname] = charnum, width, (l, b, r, t)
+
+ def parsekernpair(self, rest):
+ m = kernRE.match(rest)
+ if m is None:
+ raise error, "syntax error in AFM file: " + `rest`
+ things = []
+ for fr, to in m.regs[1:]:
+ things.append(rest[fr:to])
+ leftchar, rightchar, value = things
+ value = string.atoi(value)
+ self._kerning[(leftchar, rightchar)] = value
+
+ def parseattr(self, word, rest):
+ if word == "FontBBox":
+ l, b, r, t = map(string.atoi, string.split(rest))
+ self._attrs[word] = l, b, r, t
+ elif word == "Comment":
+ self._comments.append(rest)
+ else:
+ try:
+ value = string.atoi(rest)
+ except (ValueError, OverflowError):
+ self._attrs[word] = rest
+ else:
+ self._attrs[word] = value
+
+ def write(self, path, sep = '\r'):
+ import time
+ lines = [ "StartFontMetrics 2.0",
+ "Comment Generated by afmLib, version %s; at %s" %
+ (string.split(__version__)[2],
+ time.strftime("%m/%d/%Y %H:%M:%S",
+ time.localtime(time.time())))]
+
+ # write attributes
+ items = self._attrs.items()
+ items.sort() # XXX proper ordering???
+ for attr, value in items:
+ if attr == "FontBBox":
+ value = string.join(map(str, value), " ")
+ lines.append(attr + " " + str(value))
+
+ # write char metrics
+ lines.append("StartCharMetrics " + `len(self._chars)`)
+ items = map(lambda (charname, (charnum, width, box)):
+ (charnum, (charname, width, box)),
+ self._chars.items())
+
+ def myCmp(a, b):
+ """Custom compare function to make sure unencoded chars (-1)
+ end up at the end of the list after sorting."""
+ if a[0] == -1:
+ a = (0xffff,) + a[1:] # 0xffff is an arbitrary large number
+ if b[0] == -1:
+ b = (0xffff,) + b[1:]
+ return cmp(a, b)
+ items.sort(myCmp)
+
+ for charnum, (charname, width, (l, b, r, t)) in items:
+ lines.append("C %d ; WX %d ; N %s ; B %d %d %d %d ;" %
+ (charnum, width, charname, l, b, r, t))
+ lines.append("EndCharMetrics")
+
+ # write kerning info
+ lines.append("StartKernData")
+ lines.append("StartKernPairs " + `len(self._kerning)`)
+ items = self._kerning.items()
+ items.sort() # XXX is order important?
+ for (leftchar, rightchar), value in items:
+ lines.append("KPX %s %s %d" % (leftchar, rightchar, value))
+
+ lines.append("EndKernPairs")
+ lines.append("EndKernData")
+ lines.append("EndFontMetrics")
+
+ writelines(path, lines, sep)
+
+ def has_kernpair(self, pair):
+ return self._kerning.has_key(pair)
+
+ def kernpairs(self):
+ return self._kerning.keys()
+
+ def has_char(self, char):
+ return self._chars.has_key(char)
+
+ def chars(self):
+ return self._chars.keys()
+
+ def comments(self):
+ return self._comments
+
+ def __getattr__(self, attr):
+ if self._attrs.has_key(attr):
+ return self._attrs[attr]
+ else:
+ raise AttributeError, attr
+
+ def __setattr__(self, attr, value):
+ # all attrs *not* starting with "_" are consider to be AFM keywords
+ if attr[:1] == "_":
+ self.__dict__[attr] = value
+ else:
+ self._attrs[attr] = value
+
+ def __getitem__(self, key):
+ if type(key) == types.TupleType:
+ # key is a tuple, return the kernpair
+ if self._kerning.has_key(key):
+ return self._kerning[key]
+ else:
+ raise KeyError, "no kerning pair: " + str(key)
+ else:
+ # return the metrics instead
+ if self._chars.has_key(key):
+ return self._chars[key]
+ else:
+ raise KeyError, "metrics index " + str(key) + " out of range"
+
+ def __repr__(self):
+ if hasattr(self, "FullName"):
+ return '<AFM object for %s>' % self.FullName
+ else:
+ return '<AFM object at %x>' % id(self)
+
+
+def readlines(path):
+ f = open(path, 'rb')
+ data = f.read()
+ f.close()
+ # read any text file, regardless whether it's formatted for Mac, Unix or Dos
+ sep = ""
+ if '\r' in data:
+ sep = sep + '\r' # mac or dos
+ if '\n' in data:
+ sep = sep + '\n' # unix or dos
+ return string.split(data, sep)
+
+def writelines(path, lines, sep = '\r'):
+ f = open(path, 'wb')
+ for line in lines:
+ f.write(line + sep)
+ f.close()
+
+
+
+if __name__ == "__main__":
+ import macfs
+ fss, ok = macfs.StandardGetFile('TEXT')
+ if ok:
+ path = fss.as_pathname()
+ afm = AFM(path)
+ char = 'A'
+ if afm.has_char(char):
+ print afm[char] # print charnum, width and boundingbox
+ pair = ('A', 'V')
+ if afm.has_kernpair(pair):
+ print afm[pair] # print kerning value for pair
+ print afm.Version # various other afm entries have become attributes
+ print afm.Weight
+ # afm.comments() returns a list of all Comment lines found in the AFM
+ print afm.comments()
+ #print afm.chars()
+ #print afm.kernpairs()
+ print afm
+ afm.write(path + ".xxx")
+
diff --git a/Lib/fontTools/agl.py b/Lib/fontTools/agl.py
new file mode 100644
index 0000000..6ddc1e7
--- /dev/null
+++ b/Lib/fontTools/agl.py
@@ -0,0 +1,1189 @@
+_aglText = """\
+#
+# Name: Adobe Glyph List
+# Table version: 1.2
+# Date: 22 Oct 1998
+#
+# Description:
+#
+# The Adobe Glyph List (AGL) list relates Unicode values (UVs) to glyph
+# names, and should be used only as described in the document "Unicode and
+# Glyph Names," at
+# http://www.adobe.com/supportservice/devrelations/typeforum/unicodegn.html .
+#
+# The glyph name to UV relation is one to many. 12 glyph names are mapped to
+# two UVs each; each UV has a separate entry. All other glyph names are
+# mapped to one UV each.
+#
+# The Unicode Standard version 2.1 is used for all UVs outside of the Private
+# Use area, except for 4 entries (see Revision History for 1.2 below).
+#
+# There are 1051 entries in this list, 171 of which are in the Corporate Use
+# subarea (CUS). Refer to the document "Unicode Corporate Use Subarea as used
+# by Adobe Systems," at
+# http://www.adobe.com/supportservice/devrelations/typeforum/corporateuse.txt
+# for compatibility decompositions for these characters, and to the document
+# "Unicode and Glyph Names" for more information the CUS.
+#
+# Format: Semicolon-delimited fields:
+#
+# (1) Standard UV or CUS UV. (4 uppercase hexadecimal digits)
+#
+# (2) Glyph name. (upper- and lowercase letters, digits)
+#
+# (3) Character names: Unicode character names for standard UVs, and
+# descriptive names for CUS UVs. (uppercase letters, hyphen, space)
+#
+# (4) [optional] Comment. A comment of "Duplicate" indicates one of two
+# UVs of a double-mapping. It is the UV that may be given a uni<CODE>
+# override, or the UV that is in the CUS, as described in the document
+# "Unicode and Glyph Names."
+#
+# The entries are sorted by glyph name in increasing ASCII order; entries
+# with the same glyph name are sorted in decreasing priority order.
+#
+# Lines starting with "#" are comments; blank lines should be ignored.
+#
+# Revision History:
+#
+# 1.2 [22 Oct 1998]
+#
+# Some Central European glyph names were remapped and the glyph "dotlessj"
+# was added. Some entries in the table below have not changed but are
+# included to provide a complete context for other glyphs that have been
+# remapped or double-mapped. "-" means that the entry for that UV does not
+# exist in the AGL.
+#
+# -------- ---------------------- ---------------- --------------
+# UV Character name AGL 1.1 AGL 1.2
+# (shortened) glyph name glyph name
+# -------- ---------------------- ---------------- --------------
+# 015E/F S/s with cedilla S/scommaaccent S/scedilla
+# 0162/3 T/t with cedilla T/tcommaaccent T/tcommaaccent
+# 0218/9 S/s with comma below - S/scommaaccent
+# 021A/B T/t with comma below - T/tcommaaccent
+# 1E9E/F S/s with comma below S/scedilla -
+# F6C1/2 S/s with cedilla S/scedilla S/scedilla
+# F6BE dotless j - dotlessj
+# -------- ---------------------- ---------------- --------------
+#
+# The characters at U+1E9E/F in AGL 1.1, LATIN CAPITAL/SMALL LETTER S WITH
+# COMMA BELOW, which are proposed new characters (see (b) in the notes for
+# AGL 1.1 below), have since been reassigned by the Unicode Standard to new
+# proposed values of U+0218/9. These characters, as well as U+021A/B, LATIN
+# CAPITAL/SMALL LETTER T WITH COMMA BELOW, are not in the Unicode Standard
+# 2.1.
+#
+# Entries with the same glyph name are now sorted in decreasing priority
+# order instead of in increasing UV order.
+#
+# 1.1 [24 Nov 1997]
+#
+# a. The "Euro" glyph's UV assignment is changed from U+20A0 (EURO-CURRENCY
+# SIGN) to U+20AC (EURO SIGN). While U+20AC is not defined in the
+# Unicode Standard 2.0, it has been accepted by the Unicode Technical
+# Committee for the next version of the Standard; it has not yet passed
+# the ISO approval process as of 7 November '97.
+#
+# b. Glyphs "Scedilla" and "scedilla", which were assigned in the Corporate
+# Use Subarea in AGL 1.0, are now additionally mapped to U+1E9E and
+# U+1E9F respectively. These two UVs share the same Unicode approval
+# status as the Euro glyph (see a. above).
+#
+# c. The "fraction" glyph is now additionally mapped to U+2215, to match
+# Windows Glyph List 4.
+#
+# d. The descriptive name for glyph "onefitted", in the Corporate Use
+# subarea, is changed from "TABULAR DIGIT ONE" to "PROPORTIONAL DIGIT
+# ONE".
+#
+# 1.0 [17 Jul 1997] Original version
+#
+0041;A;LATIN CAPITAL LETTER A
+00C6;AE;LATIN CAPITAL LETTER AE
+01FC;AEacute;LATIN CAPITAL LETTER AE WITH ACUTE
+F7E6;AEsmall;LATIN SMALL CAPITAL LETTER AE
+00C1;Aacute;LATIN CAPITAL LETTER A WITH ACUTE
+F7E1;Aacutesmall;LATIN SMALL CAPITAL LETTER A WITH ACUTE
+0102;Abreve;LATIN CAPITAL LETTER A WITH BREVE
+00C2;Acircumflex;LATIN CAPITAL LETTER A WITH CIRCUMFLEX
+F7E2;Acircumflexsmall;LATIN SMALL CAPITAL LETTER A WITH CIRCUMFLEX
+F6C9;Acute;CAPITAL ACUTE ACCENT
+F7B4;Acutesmall;SMALL CAPITAL ACUTE ACCENT
+00C4;Adieresis;LATIN CAPITAL LETTER A WITH DIAERESIS
+F7E4;Adieresissmall;LATIN SMALL CAPITAL LETTER A WITH DIAERESIS
+00C0;Agrave;LATIN CAPITAL LETTER A WITH GRAVE
+F7E0;Agravesmall;LATIN SMALL CAPITAL LETTER A WITH GRAVE
+0391;Alpha;GREEK CAPITAL LETTER ALPHA
+0386;Alphatonos;GREEK CAPITAL LETTER ALPHA WITH TONOS
+0100;Amacron;LATIN CAPITAL LETTER A WITH MACRON
+0104;Aogonek;LATIN CAPITAL LETTER A WITH OGONEK
+00C5;Aring;LATIN CAPITAL LETTER A WITH RING ABOVE
+01FA;Aringacute;LATIN CAPITAL LETTER A WITH RING ABOVE AND ACUTE
+F7E5;Aringsmall;LATIN SMALL CAPITAL LETTER A WITH RING ABOVE
+F761;Asmall;LATIN SMALL CAPITAL LETTER A
+00C3;Atilde;LATIN CAPITAL LETTER A WITH TILDE
+F7E3;Atildesmall;LATIN SMALL CAPITAL LETTER A WITH TILDE
+0042;B;LATIN CAPITAL LETTER B
+0392;Beta;GREEK CAPITAL LETTER BETA
+F6F4;Brevesmall;SMALL CAPITAL BREVE
+F762;Bsmall;LATIN SMALL CAPITAL LETTER B
+0043;C;LATIN CAPITAL LETTER C
+0106;Cacute;LATIN CAPITAL LETTER C WITH ACUTE
+F6CA;Caron;CAPITAL CARON
+F6F5;Caronsmall;SMALL CAPITAL CARON
+010C;Ccaron;LATIN CAPITAL LETTER C WITH CARON
+00C7;Ccedilla;LATIN CAPITAL LETTER C WITH CEDILLA
+F7E7;Ccedillasmall;LATIN SMALL CAPITAL LETTER C WITH CEDILLA
+0108;Ccircumflex;LATIN CAPITAL LETTER C WITH CIRCUMFLEX
+010A;Cdotaccent;LATIN CAPITAL LETTER C WITH DOT ABOVE
+F7B8;Cedillasmall;SMALL CAPITAL CEDILLA
+03A7;Chi;GREEK CAPITAL LETTER CHI
+F6F6;Circumflexsmall;SMALL CAPITAL MODIFIER LETTER CIRCUMFLEX ACCENT
+F763;Csmall;LATIN SMALL CAPITAL LETTER C
+0044;D;LATIN CAPITAL LETTER D
+010E;Dcaron;LATIN CAPITAL LETTER D WITH CARON
+0110;Dcroat;LATIN CAPITAL LETTER D WITH STROKE
+2206;Delta;INCREMENT
+0394;Delta;GREEK CAPITAL LETTER DELTA;Duplicate
+F6CB;Dieresis;CAPITAL DIAERESIS
+F6CC;DieresisAcute;CAPITAL DIAERESIS ACUTE ACCENT
+F6CD;DieresisGrave;CAPITAL DIAERESIS GRAVE ACCENT
+F7A8;Dieresissmall;SMALL CAPITAL DIAERESIS
+F6F7;Dotaccentsmall;SMALL CAPITAL DOT ABOVE
+F764;Dsmall;LATIN SMALL CAPITAL LETTER D
+0045;E;LATIN CAPITAL LETTER E
+00C9;Eacute;LATIN CAPITAL LETTER E WITH ACUTE
+F7E9;Eacutesmall;LATIN SMALL CAPITAL LETTER E WITH ACUTE
+0114;Ebreve;LATIN CAPITAL LETTER E WITH BREVE
+011A;Ecaron;LATIN CAPITAL LETTER E WITH CARON
+00CA;Ecircumflex;LATIN CAPITAL LETTER E WITH CIRCUMFLEX
+F7EA;Ecircumflexsmall;LATIN SMALL CAPITAL LETTER E WITH CIRCUMFLEX
+00CB;Edieresis;LATIN CAPITAL LETTER E WITH DIAERESIS
+F7EB;Edieresissmall;LATIN SMALL CAPITAL LETTER E WITH DIAERESIS
+0116;Edotaccent;LATIN CAPITAL LETTER E WITH DOT ABOVE
+00C8;Egrave;LATIN CAPITAL LETTER E WITH GRAVE
+F7E8;Egravesmall;LATIN SMALL CAPITAL LETTER E WITH GRAVE
+0112;Emacron;LATIN CAPITAL LETTER E WITH MACRON
+014A;Eng;LATIN CAPITAL LETTER ENG
+0118;Eogonek;LATIN CAPITAL LETTER E WITH OGONEK
+0395;Epsilon;GREEK CAPITAL LETTER EPSILON
+0388;Epsilontonos;GREEK CAPITAL LETTER EPSILON WITH TONOS
+F765;Esmall;LATIN SMALL CAPITAL LETTER E
+0397;Eta;GREEK CAPITAL LETTER ETA
+0389;Etatonos;GREEK CAPITAL LETTER ETA WITH TONOS
+00D0;Eth;LATIN CAPITAL LETTER ETH
+F7F0;Ethsmall;LATIN SMALL CAPITAL LETTER ETH
+20AC;Euro;EURO SIGN
+0046;F;LATIN CAPITAL LETTER F
+F766;Fsmall;LATIN SMALL CAPITAL LETTER F
+0047;G;LATIN CAPITAL LETTER G
+0393;Gamma;GREEK CAPITAL LETTER GAMMA
+011E;Gbreve;LATIN CAPITAL LETTER G WITH BREVE
+01E6;Gcaron;LATIN CAPITAL LETTER G WITH CARON
+011C;Gcircumflex;LATIN CAPITAL LETTER G WITH CIRCUMFLEX
+0122;Gcommaaccent;LATIN CAPITAL LETTER G WITH CEDILLA
+0120;Gdotaccent;LATIN CAPITAL LETTER G WITH DOT ABOVE
+F6CE;Grave;CAPITAL GRAVE ACCENT
+F760;Gravesmall;SMALL CAPITAL GRAVE ACCENT
+F767;Gsmall;LATIN SMALL CAPITAL LETTER G
+0048;H;LATIN CAPITAL LETTER H
+25CF;H18533;BLACK CIRCLE
+25AA;H18543;BLACK SMALL SQUARE
+25AB;H18551;WHITE SMALL SQUARE
+25A1;H22073;WHITE SQUARE
+0126;Hbar;LATIN CAPITAL LETTER H WITH STROKE
+0124;Hcircumflex;LATIN CAPITAL LETTER H WITH CIRCUMFLEX
+F768;Hsmall;LATIN SMALL CAPITAL LETTER H
+F6CF;Hungarumlaut;CAPITAL DOUBLE ACUTE ACCENT
+F6F8;Hungarumlautsmall;SMALL CAPITAL DOUBLE ACUTE ACCENT
+0049;I;LATIN CAPITAL LETTER I
+0132;IJ;LATIN CAPITAL LIGATURE IJ
+00CD;Iacute;LATIN CAPITAL LETTER I WITH ACUTE
+F7ED;Iacutesmall;LATIN SMALL CAPITAL LETTER I WITH ACUTE
+012C;Ibreve;LATIN CAPITAL LETTER I WITH BREVE
+00CE;Icircumflex;LATIN CAPITAL LETTER I WITH CIRCUMFLEX
+F7EE;Icircumflexsmall;LATIN SMALL CAPITAL LETTER I WITH CIRCUMFLEX
+00CF;Idieresis;LATIN CAPITAL LETTER I WITH DIAERESIS
+F7EF;Idieresissmall;LATIN SMALL CAPITAL LETTER I WITH DIAERESIS
+0130;Idotaccent;LATIN CAPITAL LETTER I WITH DOT ABOVE
+2111;Ifraktur;BLACK-LETTER CAPITAL I
+00CC;Igrave;LATIN CAPITAL LETTER I WITH GRAVE
+F7EC;Igravesmall;LATIN SMALL CAPITAL LETTER I WITH GRAVE
+012A;Imacron;LATIN CAPITAL LETTER I WITH MACRON
+012E;Iogonek;LATIN CAPITAL LETTER I WITH OGONEK
+0399;Iota;GREEK CAPITAL LETTER IOTA
+03AA;Iotadieresis;GREEK CAPITAL LETTER IOTA WITH DIALYTIKA
+038A;Iotatonos;GREEK CAPITAL LETTER IOTA WITH TONOS
+F769;Ismall;LATIN SMALL CAPITAL LETTER I
+0128;Itilde;LATIN CAPITAL LETTER I WITH TILDE
+004A;J;LATIN CAPITAL LETTER J
+0134;Jcircumflex;LATIN CAPITAL LETTER J WITH CIRCUMFLEX
+F76A;Jsmall;LATIN SMALL CAPITAL LETTER J
+004B;K;LATIN CAPITAL LETTER K
+039A;Kappa;GREEK CAPITAL LETTER KAPPA
+0136;Kcommaaccent;LATIN CAPITAL LETTER K WITH CEDILLA
+F76B;Ksmall;LATIN SMALL CAPITAL LETTER K
+004C;L;LATIN CAPITAL LETTER L
+F6BF;LL;LATIN CAPITAL LETTER LL
+0139;Lacute;LATIN CAPITAL LETTER L WITH ACUTE
+039B;Lambda;GREEK CAPITAL LETTER LAMDA
+013D;Lcaron;LATIN CAPITAL LETTER L WITH CARON
+013B;Lcommaaccent;LATIN CAPITAL LETTER L WITH CEDILLA
+013F;Ldot;LATIN CAPITAL LETTER L WITH MIDDLE DOT
+0141;Lslash;LATIN CAPITAL LETTER L WITH STROKE
+F6F9;Lslashsmall;LATIN SMALL CAPITAL LETTER L WITH STROKE
+F76C;Lsmall;LATIN SMALL CAPITAL LETTER L
+004D;M;LATIN CAPITAL LETTER M
+F6D0;Macron;CAPITAL MACRON
+F7AF;Macronsmall;SMALL CAPITAL MACRON
+F76D;Msmall;LATIN SMALL CAPITAL LETTER M
+039C;Mu;GREEK CAPITAL LETTER MU
+004E;N;LATIN CAPITAL LETTER N
+0143;Nacute;LATIN CAPITAL LETTER N WITH ACUTE
+0147;Ncaron;LATIN CAPITAL LETTER N WITH CARON
+0145;Ncommaaccent;LATIN CAPITAL LETTER N WITH CEDILLA
+F76E;Nsmall;LATIN SMALL CAPITAL LETTER N
+00D1;Ntilde;LATIN CAPITAL LETTER N WITH TILDE
+F7F1;Ntildesmall;LATIN SMALL CAPITAL LETTER N WITH TILDE
+039D;Nu;GREEK CAPITAL LETTER NU
+004F;O;LATIN CAPITAL LETTER O
+0152;OE;LATIN CAPITAL LIGATURE OE
+F6FA;OEsmall;LATIN SMALL CAPITAL LIGATURE OE
+00D3;Oacute;LATIN CAPITAL LETTER O WITH ACUTE
+F7F3;Oacutesmall;LATIN SMALL CAPITAL LETTER O WITH ACUTE
+014E;Obreve;LATIN CAPITAL LETTER O WITH BREVE
+00D4;Ocircumflex;LATIN CAPITAL LETTER O WITH CIRCUMFLEX
+F7F4;Ocircumflexsmall;LATIN SMALL CAPITAL LETTER O WITH CIRCUMFLEX
+00D6;Odieresis;LATIN CAPITAL LETTER O WITH DIAERESIS
+F7F6;Odieresissmall;LATIN SMALL CAPITAL LETTER O WITH DIAERESIS
+F6FB;Ogoneksmall;SMALL CAPITAL OGONEK
+00D2;Ograve;LATIN CAPITAL LETTER O WITH GRAVE
+F7F2;Ogravesmall;LATIN SMALL CAPITAL LETTER O WITH GRAVE
+01A0;Ohorn;LATIN CAPITAL LETTER O WITH HORN
+0150;Ohungarumlaut;LATIN CAPITAL LETTER O WITH DOUBLE ACUTE
+014C;Omacron;LATIN CAPITAL LETTER O WITH MACRON
+2126;Omega;OHM SIGN
+03A9;Omega;GREEK CAPITAL LETTER OMEGA;Duplicate
+038F;Omegatonos;GREEK CAPITAL LETTER OMEGA WITH TONOS
+039F;Omicron;GREEK CAPITAL LETTER OMICRON
+038C;Omicrontonos;GREEK CAPITAL LETTER OMICRON WITH TONOS
+00D8;Oslash;LATIN CAPITAL LETTER O WITH STROKE
+01FE;Oslashacute;LATIN CAPITAL LETTER O WITH STROKE AND ACUTE
+F7F8;Oslashsmall;LATIN SMALL CAPITAL LETTER O WITH STROKE
+F76F;Osmall;LATIN SMALL CAPITAL LETTER O
+00D5;Otilde;LATIN CAPITAL LETTER O WITH TILDE
+F7F5;Otildesmall;LATIN SMALL CAPITAL LETTER O WITH TILDE
+0050;P;LATIN CAPITAL LETTER P
+03A6;Phi;GREEK CAPITAL LETTER PHI
+03A0;Pi;GREEK CAPITAL LETTER PI
+03A8;Psi;GREEK CAPITAL LETTER PSI
+F770;Psmall;LATIN SMALL CAPITAL LETTER P
+0051;Q;LATIN CAPITAL LETTER Q
+F771;Qsmall;LATIN SMALL CAPITAL LETTER Q
+0052;R;LATIN CAPITAL LETTER R
+0154;Racute;LATIN CAPITAL LETTER R WITH ACUTE
+0158;Rcaron;LATIN CAPITAL LETTER R WITH CARON
+0156;Rcommaaccent;LATIN CAPITAL LETTER R WITH CEDILLA
+211C;Rfraktur;BLACK-LETTER CAPITAL R
+03A1;Rho;GREEK CAPITAL LETTER RHO
+F6FC;Ringsmall;SMALL CAPITAL RING ABOVE
+F772;Rsmall;LATIN SMALL CAPITAL LETTER R
+0053;S;LATIN CAPITAL LETTER S
+250C;SF010000;BOX DRAWINGS LIGHT DOWN AND RIGHT
+2514;SF020000;BOX DRAWINGS LIGHT UP AND RIGHT
+2510;SF030000;BOX DRAWINGS LIGHT DOWN AND LEFT
+2518;SF040000;BOX DRAWINGS LIGHT UP AND LEFT
+253C;SF050000;BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL
+252C;SF060000;BOX DRAWINGS LIGHT DOWN AND HORIZONTAL
+2534;SF070000;BOX DRAWINGS LIGHT UP AND HORIZONTAL
+251C;SF080000;BOX DRAWINGS LIGHT VERTICAL AND RIGHT
+2524;SF090000;BOX DRAWINGS LIGHT VERTICAL AND LEFT
+2500;SF100000;BOX DRAWINGS LIGHT HORIZONTAL
+2502;SF110000;BOX DRAWINGS LIGHT VERTICAL
+2561;SF190000;BOX DRAWINGS VERTICAL SINGLE AND LEFT DOUBLE
+2562;SF200000;BOX DRAWINGS VERTICAL DOUBLE AND LEFT SINGLE
+2556;SF210000;BOX DRAWINGS DOWN DOUBLE AND LEFT SINGLE
+2555;SF220000;BOX DRAWINGS DOWN SINGLE AND LEFT DOUBLE
+2563;SF230000;BOX DRAWINGS DOUBLE VERTICAL AND LEFT
+2551;SF240000;BOX DRAWINGS DOUBLE VERTICAL
+2557;SF250000;BOX DRAWINGS DOUBLE DOWN AND LEFT
+255D;SF260000;BOX DRAWINGS DOUBLE UP AND LEFT
+255C;SF270000;BOX DRAWINGS UP DOUBLE AND LEFT SINGLE
+255B;SF280000;BOX DRAWINGS UP SINGLE AND LEFT DOUBLE
+255E;SF360000;BOX DRAWINGS VERTICAL SINGLE AND RIGHT DOUBLE
+255F;SF370000;BOX DRAWINGS VERTICAL DOUBLE AND RIGHT SINGLE
+255A;SF380000;BOX DRAWINGS DOUBLE UP AND RIGHT
+2554;SF390000;BOX DRAWINGS DOUBLE DOWN AND RIGHT
+2569;SF400000;BOX DRAWINGS DOUBLE UP AND HORIZONTAL
+2566;SF410000;BOX DRAWINGS DOUBLE DOWN AND HORIZONTAL
+2560;SF420000;BOX DRAWINGS DOUBLE VERTICAL AND RIGHT
+2550;SF430000;BOX DRAWINGS DOUBLE HORIZONTAL
+256C;SF440000;BOX DRAWINGS DOUBLE VERTICAL AND HORIZONTAL
+2567;SF450000;BOX DRAWINGS UP SINGLE AND HORIZONTAL DOUBLE
+2568;SF460000;BOX DRAWINGS UP DOUBLE AND HORIZONTAL SINGLE
+2564;SF470000;BOX DRAWINGS DOWN SINGLE AND HORIZONTAL DOUBLE
+2565;SF480000;BOX DRAWINGS DOWN DOUBLE AND HORIZONTAL SINGLE
+2559;SF490000;BOX DRAWINGS UP DOUBLE AND RIGHT SINGLE
+2558;SF500000;BOX DRAWINGS UP SINGLE AND RIGHT DOUBLE
+2552;SF510000;BOX DRAWINGS DOWN SINGLE AND RIGHT DOUBLE
+2553;SF520000;BOX DRAWINGS DOWN DOUBLE AND RIGHT SINGLE
+256B;SF530000;BOX DRAWINGS VERTICAL DOUBLE AND HORIZONTAL SINGLE
+256A;SF540000;BOX DRAWINGS VERTICAL SINGLE AND HORIZONTAL DOUBLE
+015A;Sacute;LATIN CAPITAL LETTER S WITH ACUTE
+0160;Scaron;LATIN CAPITAL LETTER S WITH CARON
+F6FD;Scaronsmall;LATIN SMALL CAPITAL LETTER S WITH CARON
+015E;Scedilla;LATIN CAPITAL LETTER S WITH CEDILLA
+F6C1;Scedilla;LATIN CAPITAL LETTER S WITH CEDILLA;Duplicate
+015C;Scircumflex;LATIN CAPITAL LETTER S WITH CIRCUMFLEX
+0218;Scommaaccent;LATIN CAPITAL LETTER S WITH COMMA BELOW
+03A3;Sigma;GREEK CAPITAL LETTER SIGMA
+F773;Ssmall;LATIN SMALL CAPITAL LETTER S
+0054;T;LATIN CAPITAL LETTER T
+03A4;Tau;GREEK CAPITAL LETTER TAU
+0166;Tbar;LATIN CAPITAL LETTER T WITH STROKE
+0164;Tcaron;LATIN CAPITAL LETTER T WITH CARON
+0162;Tcommaaccent;LATIN CAPITAL LETTER T WITH CEDILLA
+021A;Tcommaaccent;LATIN CAPITAL LETTER T WITH COMMA BELOW;Duplicate
+0398;Theta;GREEK CAPITAL LETTER THETA
+00DE;Thorn;LATIN CAPITAL LETTER THORN
+F7FE;Thornsmall;LATIN SMALL CAPITAL LETTER THORN
+F6FE;Tildesmall;SMALL CAPITAL SMALL TILDE
+F774;Tsmall;LATIN SMALL CAPITAL LETTER T
+0055;U;LATIN CAPITAL LETTER U
+00DA;Uacute;LATIN CAPITAL LETTER U WITH ACUTE
+F7FA;Uacutesmall;LATIN SMALL CAPITAL LETTER U WITH ACUTE
+016C;Ubreve;LATIN CAPITAL LETTER U WITH BREVE
+00DB;Ucircumflex;LATIN CAPITAL LETTER U WITH CIRCUMFLEX
+F7FB;Ucircumflexsmall;LATIN SMALL CAPITAL LETTER U WITH CIRCUMFLEX
+00DC;Udieresis;LATIN CAPITAL LETTER U WITH DIAERESIS
+F7FC;Udieresissmall;LATIN SMALL CAPITAL LETTER U WITH DIAERESIS
+00D9;Ugrave;LATIN CAPITAL LETTER U WITH GRAVE
+F7F9;Ugravesmall;LATIN SMALL CAPITAL LETTER U WITH GRAVE
+01AF;Uhorn;LATIN CAPITAL LETTER U WITH HORN
+0170;Uhungarumlaut;LATIN CAPITAL LETTER U WITH DOUBLE ACUTE
+016A;Umacron;LATIN CAPITAL LETTER U WITH MACRON
+0172;Uogonek;LATIN CAPITAL LETTER U WITH OGONEK
+03A5;Upsilon;GREEK CAPITAL LETTER UPSILON
+03D2;Upsilon1;GREEK UPSILON WITH HOOK SYMBOL
+03AB;Upsilondieresis;GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA
+038E;Upsilontonos;GREEK CAPITAL LETTER UPSILON WITH TONOS
+016E;Uring;LATIN CAPITAL LETTER U WITH RING ABOVE
+F775;Usmall;LATIN SMALL CAPITAL LETTER U
+0168;Utilde;LATIN CAPITAL LETTER U WITH TILDE
+0056;V;LATIN CAPITAL LETTER V
+F776;Vsmall;LATIN SMALL CAPITAL LETTER V
+0057;W;LATIN CAPITAL LETTER W
+1E82;Wacute;LATIN CAPITAL LETTER W WITH ACUTE
+0174;Wcircumflex;LATIN CAPITAL LETTER W WITH CIRCUMFLEX
+1E84;Wdieresis;LATIN CAPITAL LETTER W WITH DIAERESIS
+1E80;Wgrave;LATIN CAPITAL LETTER W WITH GRAVE
+F777;Wsmall;LATIN SMALL CAPITAL LETTER W
+0058;X;LATIN CAPITAL LETTER X
+039E;Xi;GREEK CAPITAL LETTER XI
+F778;Xsmall;LATIN SMALL CAPITAL LETTER X
+0059;Y;LATIN CAPITAL LETTER Y
+00DD;Yacute;LATIN CAPITAL LETTER Y WITH ACUTE
+F7FD;Yacutesmall;LATIN SMALL CAPITAL LETTER Y WITH ACUTE
+0176;Ycircumflex;LATIN CAPITAL LETTER Y WITH CIRCUMFLEX
+0178;Ydieresis;LATIN CAPITAL LETTER Y WITH DIAERESIS
+F7FF;Ydieresissmall;LATIN SMALL CAPITAL LETTER Y WITH DIAERESIS
+1EF2;Ygrave;LATIN CAPITAL LETTER Y WITH GRAVE
+F779;Ysmall;LATIN SMALL CAPITAL LETTER Y
+005A;Z;LATIN CAPITAL LETTER Z
+0179;Zacute;LATIN CAPITAL LETTER Z WITH ACUTE
+017D;Zcaron;LATIN CAPITAL LETTER Z WITH CARON
+F6FF;Zcaronsmall;LATIN SMALL CAPITAL LETTER Z WITH CARON
+017B;Zdotaccent;LATIN CAPITAL LETTER Z WITH DOT ABOVE
+0396;Zeta;GREEK CAPITAL LETTER ZETA
+F77A;Zsmall;LATIN SMALL CAPITAL LETTER Z
+0061;a;LATIN SMALL LETTER A
+00E1;aacute;LATIN SMALL LETTER A WITH ACUTE
+0103;abreve;LATIN SMALL LETTER A WITH BREVE
+00E2;acircumflex;LATIN SMALL LETTER A WITH CIRCUMFLEX
+00B4;acute;ACUTE ACCENT
+0301;acutecomb;COMBINING ACUTE ACCENT
+00E4;adieresis;LATIN SMALL LETTER A WITH DIAERESIS
+00E6;ae;LATIN SMALL LETTER AE
+01FD;aeacute;LATIN SMALL LETTER AE WITH ACUTE
+2015;afii00208;HORIZONTAL BAR
+0410;afii10017;CYRILLIC CAPITAL LETTER A
+0411;afii10018;CYRILLIC CAPITAL LETTER BE
+0412;afii10019;CYRILLIC CAPITAL LETTER VE
+0413;afii10020;CYRILLIC CAPITAL LETTER GHE
+0414;afii10021;CYRILLIC CAPITAL LETTER DE
+0415;afii10022;CYRILLIC CAPITAL LETTER IE
+0401;afii10023;CYRILLIC CAPITAL LETTER IO
+0416;afii10024;CYRILLIC CAPITAL LETTER ZHE
+0417;afii10025;CYRILLIC CAPITAL LETTER ZE
+0418;afii10026;CYRILLIC CAPITAL LETTER I
+0419;afii10027;CYRILLIC CAPITAL LETTER SHORT I
+041A;afii10028;CYRILLIC CAPITAL LETTER KA
+041B;afii10029;CYRILLIC CAPITAL LETTER EL
+041C;afii10030;CYRILLIC CAPITAL LETTER EM
+041D;afii10031;CYRILLIC CAPITAL LETTER EN
+041E;afii10032;CYRILLIC CAPITAL LETTER O
+041F;afii10033;CYRILLIC CAPITAL LETTER PE
+0420;afii10034;CYRILLIC CAPITAL LETTER ER
+0421;afii10035;CYRILLIC CAPITAL LETTER ES
+0422;afii10036;CYRILLIC CAPITAL LETTER TE
+0423;afii10037;CYRILLIC CAPITAL LETTER U
+0424;afii10038;CYRILLIC CAPITAL LETTER EF
+0425;afii10039;CYRILLIC CAPITAL LETTER HA
+0426;afii10040;CYRILLIC CAPITAL LETTER TSE
+0427;afii10041;CYRILLIC CAPITAL LETTER CHE
+0428;afii10042;CYRILLIC CAPITAL LETTER SHA
+0429;afii10043;CYRILLIC CAPITAL LETTER SHCHA
+042A;afii10044;CYRILLIC CAPITAL LETTER HARD SIGN
+042B;afii10045;CYRILLIC CAPITAL LETTER YERU
+042C;afii10046;CYRILLIC CAPITAL LETTER SOFT SIGN
+042D;afii10047;CYRILLIC CAPITAL LETTER E
+042E;afii10048;CYRILLIC CAPITAL LETTER YU
+042F;afii10049;CYRILLIC CAPITAL LETTER YA
+0490;afii10050;CYRILLIC CAPITAL LETTER GHE WITH UPTURN
+0402;afii10051;CYRILLIC CAPITAL LETTER DJE
+0403;afii10052;CYRILLIC CAPITAL LETTER GJE
+0404;afii10053;CYRILLIC CAPITAL LETTER UKRAINIAN IE
+0405;afii10054;CYRILLIC CAPITAL LETTER DZE
+0406;afii10055;CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I
+0407;afii10056;CYRILLIC CAPITAL LETTER YI
+0408;afii10057;CYRILLIC CAPITAL LETTER JE
+0409;afii10058;CYRILLIC CAPITAL LETTER LJE
+040A;afii10059;CYRILLIC CAPITAL LETTER NJE
+040B;afii10060;CYRILLIC CAPITAL LETTER TSHE
+040C;afii10061;CYRILLIC CAPITAL LETTER KJE
+040E;afii10062;CYRILLIC CAPITAL LETTER SHORT U
+F6C4;afii10063;CYRILLIC SMALL LETTER GHE VARIANT
+F6C5;afii10064;CYRILLIC SMALL LETTER BE VARIANT
+0430;afii10065;CYRILLIC SMALL LETTER A
+0431;afii10066;CYRILLIC SMALL LETTER BE
+0432;afii10067;CYRILLIC SMALL LETTER VE
+0433;afii10068;CYRILLIC SMALL LETTER GHE
+0434;afii10069;CYRILLIC SMALL LETTER DE
+0435;afii10070;CYRILLIC SMALL LETTER IE
+0451;afii10071;CYRILLIC SMALL LETTER IO
+0436;afii10072;CYRILLIC SMALL LETTER ZHE
+0437;afii10073;CYRILLIC SMALL LETTER ZE
+0438;afii10074;CYRILLIC SMALL LETTER I
+0439;afii10075;CYRILLIC SMALL LETTER SHORT I
+043A;afii10076;CYRILLIC SMALL LETTER KA
+043B;afii10077;CYRILLIC SMALL LETTER EL
+043C;afii10078;CYRILLIC SMALL LETTER EM
+043D;afii10079;CYRILLIC SMALL LETTER EN
+043E;afii10080;CYRILLIC SMALL LETTER O
+043F;afii10081;CYRILLIC SMALL LETTER PE
+0440;afii10082;CYRILLIC SMALL LETTER ER
+0441;afii10083;CYRILLIC SMALL LETTER ES
+0442;afii10084;CYRILLIC SMALL LETTER TE
+0443;afii10085;CYRILLIC SMALL LETTER U
+0444;afii10086;CYRILLIC SMALL LETTER EF
+0445;afii10087;CYRILLIC SMALL LETTER HA
+0446;afii10088;CYRILLIC SMALL LETTER TSE
+0447;afii10089;CYRILLIC SMALL LETTER CHE
+0448;afii10090;CYRILLIC SMALL LETTER SHA
+0449;afii10091;CYRILLIC SMALL LETTER SHCHA
+044A;afii10092;CYRILLIC SMALL LETTER HARD SIGN
+044B;afii10093;CYRILLIC SMALL LETTER YERU
+044C;afii10094;CYRILLIC SMALL LETTER SOFT SIGN
+044D;afii10095;CYRILLIC SMALL LETTER E
+044E;afii10096;CYRILLIC SMALL LETTER YU
+044F;afii10097;CYRILLIC SMALL LETTER YA
+0491;afii10098;CYRILLIC SMALL LETTER GHE WITH UPTURN
+0452;afii10099;CYRILLIC SMALL LETTER DJE
+0453;afii10100;CYRILLIC SMALL LETTER GJE
+0454;afii10101;CYRILLIC SMALL LETTER UKRAINIAN IE
+0455;afii10102;CYRILLIC SMALL LETTER DZE
+0456;afii10103;CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I
+0457;afii10104;CYRILLIC SMALL LETTER YI
+0458;afii10105;CYRILLIC SMALL LETTER JE
+0459;afii10106;CYRILLIC SMALL LETTER LJE
+045A;afii10107;CYRILLIC SMALL LETTER NJE
+045B;afii10108;CYRILLIC SMALL LETTER TSHE
+045C;afii10109;CYRILLIC SMALL LETTER KJE
+045E;afii10110;CYRILLIC SMALL LETTER SHORT U
+040F;afii10145;CYRILLIC CAPITAL LETTER DZHE
+0462;afii10146;CYRILLIC CAPITAL LETTER YAT
+0472;afii10147;CYRILLIC CAPITAL LETTER FITA
+0474;afii10148;CYRILLIC CAPITAL LETTER IZHITSA
+F6C6;afii10192;CYRILLIC SMALL LETTER DE VARIANT
+045F;afii10193;CYRILLIC SMALL LETTER DZHE
+0463;afii10194;CYRILLIC SMALL LETTER YAT
+0473;afii10195;CYRILLIC SMALL LETTER FITA
+0475;afii10196;CYRILLIC SMALL LETTER IZHITSA
+F6C7;afii10831;CYRILLIC SMALL LETTER PE VARIANT
+F6C8;afii10832;CYRILLIC SMALL LETTER TE VARIANT
+04D9;afii10846;CYRILLIC SMALL LETTER SCHWA
+200E;afii299;LEFT-TO-RIGHT MARK
+200F;afii300;RIGHT-TO-LEFT MARK
+200D;afii301;ZERO WIDTH JOINER
+066A;afii57381;ARABIC PERCENT SIGN
+060C;afii57388;ARABIC COMMA
+0660;afii57392;ARABIC-INDIC DIGIT ZERO
+0661;afii57393;ARABIC-INDIC DIGIT ONE
+0662;afii57394;ARABIC-INDIC DIGIT TWO
+0663;afii57395;ARABIC-INDIC DIGIT THREE
+0664;afii57396;ARABIC-INDIC DIGIT FOUR
+0665;afii57397;ARABIC-INDIC DIGIT FIVE
+0666;afii57398;ARABIC-INDIC DIGIT SIX
+0667;afii57399;ARABIC-INDIC DIGIT SEVEN
+0668;afii57400;ARABIC-INDIC DIGIT EIGHT
+0669;afii57401;ARABIC-INDIC DIGIT NINE
+061B;afii57403;ARABIC SEMICOLON
+061F;afii57407;ARABIC QUESTION MARK
+0621;afii57409;ARABIC LETTER HAMZA
+0622;afii57410;ARABIC LETTER ALEF WITH MADDA ABOVE
+0623;afii57411;ARABIC LETTER ALEF WITH HAMZA ABOVE
+0624;afii57412;ARABIC LETTER WAW WITH HAMZA ABOVE
+0625;afii57413;ARABIC LETTER ALEF WITH HAMZA BELOW
+0626;afii57414;ARABIC LETTER YEH WITH HAMZA ABOVE
+0627;afii57415;ARABIC LETTER ALEF
+0628;afii57416;ARABIC LETTER BEH
+0629;afii57417;ARABIC LETTER TEH MARBUTA
+062A;afii57418;ARABIC LETTER TEH
+062B;afii57419;ARABIC LETTER THEH
+062C;afii57420;ARABIC LETTER JEEM
+062D;afii57421;ARABIC LETTER HAH
+062E;afii57422;ARABIC LETTER KHAH
+062F;afii57423;ARABIC LETTER DAL
+0630;afii57424;ARABIC LETTER THAL
+0631;afii57425;ARABIC LETTER REH
+0632;afii57426;ARABIC LETTER ZAIN
+0633;afii57427;ARABIC LETTER SEEN
+0634;afii57428;ARABIC LETTER SHEEN
+0635;afii57429;ARABIC LETTER SAD
+0636;afii57430;ARABIC LETTER DAD
+0637;afii57431;ARABIC LETTER TAH
+0638;afii57432;ARABIC LETTER ZAH
+0639;afii57433;ARABIC LETTER AIN
+063A;afii57434;ARABIC LETTER GHAIN
+0640;afii57440;ARABIC TATWEEL
+0641;afii57441;ARABIC LETTER FEH
+0642;afii57442;ARABIC LETTER QAF
+0643;afii57443;ARABIC LETTER KAF
+0644;afii57444;ARABIC LETTER LAM
+0645;afii57445;ARABIC LETTER MEEM
+0646;afii57446;ARABIC LETTER NOON
+0648;afii57448;ARABIC LETTER WAW
+0649;afii57449;ARABIC LETTER ALEF MAKSURA
+064A;afii57450;ARABIC LETTER YEH
+064B;afii57451;ARABIC FATHATAN
+064C;afii57452;ARABIC DAMMATAN
+064D;afii57453;ARABIC KASRATAN
+064E;afii57454;ARABIC FATHA
+064F;afii57455;ARABIC DAMMA
+0650;afii57456;ARABIC KASRA
+0651;afii57457;ARABIC SHADDA
+0652;afii57458;ARABIC SUKUN
+0647;afii57470;ARABIC LETTER HEH
+06A4;afii57505;ARABIC LETTER VEH
+067E;afii57506;ARABIC LETTER PEH
+0686;afii57507;ARABIC LETTER TCHEH
+0698;afii57508;ARABIC LETTER JEH
+06AF;afii57509;ARABIC LETTER GAF
+0679;afii57511;ARABIC LETTER TTEH
+0688;afii57512;ARABIC LETTER DDAL
+0691;afii57513;ARABIC LETTER RREH
+06BA;afii57514;ARABIC LETTER NOON GHUNNA
+06D2;afii57519;ARABIC LETTER YEH BARREE
+06D5;afii57534;ARABIC LETTER AE
+20AA;afii57636;NEW SHEQEL SIGN
+05BE;afii57645;HEBREW PUNCTUATION MAQAF
+05C3;afii57658;HEBREW PUNCTUATION SOF PASUQ
+05D0;afii57664;HEBREW LETTER ALEF
+05D1;afii57665;HEBREW LETTER BET
+05D2;afii57666;HEBREW LETTER GIMEL
+05D3;afii57667;HEBREW LETTER DALET
+05D4;afii57668;HEBREW LETTER HE
+05D5;afii57669;HEBREW LETTER VAV
+05D6;afii57670;HEBREW LETTER ZAYIN
+05D7;afii57671;HEBREW LETTER HET
+05D8;afii57672;HEBREW LETTER TET
+05D9;afii57673;HEBREW LETTER YOD
+05DA;afii57674;HEBREW LETTER FINAL KAF
+05DB;afii57675;HEBREW LETTER KAF
+05DC;afii57676;HEBREW LETTER LAMED
+05DD;afii57677;HEBREW LETTER FINAL MEM
+05DE;afii57678;HEBREW LETTER MEM
+05DF;afii57679;HEBREW LETTER FINAL NUN
+05E0;afii57680;HEBREW LETTER NUN
+05E1;afii57681;HEBREW LETTER SAMEKH
+05E2;afii57682;HEBREW LETTER AYIN
+05E3;afii57683;HEBREW LETTER FINAL PE
+05E4;afii57684;HEBREW LETTER PE
+05E5;afii57685;HEBREW LETTER FINAL TSADI
+05E6;afii57686;HEBREW LETTER TSADI
+05E7;afii57687;HEBREW LETTER QOF
+05E8;afii57688;HEBREW LETTER RESH
+05E9;afii57689;HEBREW LETTER SHIN
+05EA;afii57690;HEBREW LETTER TAV
+FB2A;afii57694;HEBREW LETTER SHIN WITH SHIN DOT
+FB2B;afii57695;HEBREW LETTER SHIN WITH SIN DOT
+FB4B;afii57700;HEBREW LETTER VAV WITH HOLAM
+FB1F;afii57705;HEBREW LIGATURE YIDDISH YOD YOD PATAH
+05F0;afii57716;HEBREW LIGATURE YIDDISH DOUBLE VAV
+05F1;afii57717;HEBREW LIGATURE YIDDISH VAV YOD
+05F2;afii57718;HEBREW LIGATURE YIDDISH DOUBLE YOD
+FB35;afii57723;HEBREW LETTER VAV WITH DAGESH
+05B4;afii57793;HEBREW POINT HIRIQ
+05B5;afii57794;HEBREW POINT TSERE
+05B6;afii57795;HEBREW POINT SEGOL
+05BB;afii57796;HEBREW POINT QUBUTS
+05B8;afii57797;HEBREW POINT QAMATS
+05B7;afii57798;HEBREW POINT PATAH
+05B0;afii57799;HEBREW POINT SHEVA
+05B2;afii57800;HEBREW POINT HATAF PATAH
+05B1;afii57801;HEBREW POINT HATAF SEGOL
+05B3;afii57802;HEBREW POINT HATAF QAMATS
+05C2;afii57803;HEBREW POINT SIN DOT
+05C1;afii57804;HEBREW POINT SHIN DOT
+05B9;afii57806;HEBREW POINT HOLAM
+05BC;afii57807;HEBREW POINT DAGESH OR MAPIQ
+05BD;afii57839;HEBREW POINT METEG
+05BF;afii57841;HEBREW POINT RAFE
+05C0;afii57842;HEBREW PUNCTUATION PASEQ
+02BC;afii57929;MODIFIER LETTER APOSTROPHE
+2105;afii61248;CARE OF
+2113;afii61289;SCRIPT SMALL L
+2116;afii61352;NUMERO SIGN
+202C;afii61573;POP DIRECTIONAL FORMATTING
+202D;afii61574;LEFT-TO-RIGHT OVERRIDE
+202E;afii61575;RIGHT-TO-LEFT OVERRIDE
+200C;afii61664;ZERO WIDTH NON-JOINER
+066D;afii63167;ARABIC FIVE POINTED STAR
+02BD;afii64937;MODIFIER LETTER REVERSED COMMA
+00E0;agrave;LATIN SMALL LETTER A WITH GRAVE
+2135;aleph;ALEF SYMBOL
+03B1;alpha;GREEK SMALL LETTER ALPHA
+03AC;alphatonos;GREEK SMALL LETTER ALPHA WITH TONOS
+0101;amacron;LATIN SMALL LETTER A WITH MACRON
+0026;ampersand;AMPERSAND
+F726;ampersandsmall;SMALL CAPITAL AMPERSAND
+2220;angle;ANGLE
+2329;angleleft;LEFT-POINTING ANGLE BRACKET
+232A;angleright;RIGHT-POINTING ANGLE BRACKET
+0387;anoteleia;GREEK ANO TELEIA
+0105;aogonek;LATIN SMALL LETTER A WITH OGONEK
+2248;approxequal;ALMOST EQUAL TO
+00E5;aring;LATIN SMALL LETTER A WITH RING ABOVE
+01FB;aringacute;LATIN SMALL LETTER A WITH RING ABOVE AND ACUTE
+2194;arrowboth;LEFT RIGHT ARROW
+21D4;arrowdblboth;LEFT RIGHT DOUBLE ARROW
+21D3;arrowdbldown;DOWNWARDS DOUBLE ARROW
+21D0;arrowdblleft;LEFTWARDS DOUBLE ARROW
+21D2;arrowdblright;RIGHTWARDS DOUBLE ARROW
+21D1;arrowdblup;UPWARDS DOUBLE ARROW
+2193;arrowdown;DOWNWARDS ARROW
+F8E7;arrowhorizex;HORIZONTAL ARROW EXTENDER
+2190;arrowleft;LEFTWARDS ARROW
+2192;arrowright;RIGHTWARDS ARROW
+2191;arrowup;UPWARDS ARROW
+2195;arrowupdn;UP DOWN ARROW
+21A8;arrowupdnbse;UP DOWN ARROW WITH BASE
+F8E6;arrowvertex;VERTICAL ARROW EXTENDER
+005E;asciicircum;CIRCUMFLEX ACCENT
+007E;asciitilde;TILDE
+002A;asterisk;ASTERISK
+2217;asteriskmath;ASTERISK OPERATOR
+F6E9;asuperior;SUPERSCRIPT LATIN SMALL LETTER A
+0040;at;COMMERCIAL AT
+00E3;atilde;LATIN SMALL LETTER A WITH TILDE
+0062;b;LATIN SMALL LETTER B
+005C;backslash;REVERSE SOLIDUS
+007C;bar;VERTICAL LINE
+03B2;beta;GREEK SMALL LETTER BETA
+2588;block;FULL BLOCK
+F8F4;braceex;CURLY BRACKET EXTENDER
+007B;braceleft;LEFT CURLY BRACKET
+F8F3;braceleftbt;LEFT CURLY BRACKET BOTTOM
+F8F2;braceleftmid;LEFT CURLY BRACKET MID
+F8F1;bracelefttp;LEFT CURLY BRACKET TOP
+007D;braceright;RIGHT CURLY BRACKET
+F8FE;bracerightbt;RIGHT CURLY BRACKET BOTTOM
+F8FD;bracerightmid;RIGHT CURLY BRACKET MID
+F8FC;bracerighttp;RIGHT CURLY BRACKET TOP
+005B;bracketleft;LEFT SQUARE BRACKET
+F8F0;bracketleftbt;LEFT SQUARE BRACKET BOTTOM
+F8EF;bracketleftex;LEFT SQUARE BRACKET EXTENDER
+F8EE;bracketlefttp;LEFT SQUARE BRACKET TOP
+005D;bracketright;RIGHT SQUARE BRACKET
+F8FB;bracketrightbt;RIGHT SQUARE BRACKET BOTTOM
+F8FA;bracketrightex;RIGHT SQUARE BRACKET EXTENDER
+F8F9;bracketrighttp;RIGHT SQUARE BRACKET TOP
+02D8;breve;BREVE
+00A6;brokenbar;BROKEN BAR
+F6EA;bsuperior;SUPERSCRIPT LATIN SMALL LETTER B
+2022;bullet;BULLET
+0063;c;LATIN SMALL LETTER C
+0107;cacute;LATIN SMALL LETTER C WITH ACUTE
+02C7;caron;CARON
+21B5;carriagereturn;DOWNWARDS ARROW WITH CORNER LEFTWARDS
+010D;ccaron;LATIN SMALL LETTER C WITH CARON
+00E7;ccedilla;LATIN SMALL LETTER C WITH CEDILLA
+0109;ccircumflex;LATIN SMALL LETTER C WITH CIRCUMFLEX
+010B;cdotaccent;LATIN SMALL LETTER C WITH DOT ABOVE
+00B8;cedilla;CEDILLA
+00A2;cent;CENT SIGN
+F6DF;centinferior;SUBSCRIPT CENT SIGN
+F7A2;centoldstyle;OLDSTYLE CENT SIGN
+F6E0;centsuperior;SUPERSCRIPT CENT SIGN
+03C7;chi;GREEK SMALL LETTER CHI
+25CB;circle;WHITE CIRCLE
+2297;circlemultiply;CIRCLED TIMES
+2295;circleplus;CIRCLED PLUS
+02C6;circumflex;MODIFIER LETTER CIRCUMFLEX ACCENT
+2663;club;BLACK CLUB SUIT
+003A;colon;COLON
+20A1;colonmonetary;COLON SIGN
+002C;comma;COMMA
+F6C3;commaaccent;COMMA BELOW
+F6E1;commainferior;SUBSCRIPT COMMA
+F6E2;commasuperior;SUPERSCRIPT COMMA
+2245;congruent;APPROXIMATELY EQUAL TO
+00A9;copyright;COPYRIGHT SIGN
+F8E9;copyrightsans;COPYRIGHT SIGN SANS SERIF
+F6D9;copyrightserif;COPYRIGHT SIGN SERIF
+00A4;currency;CURRENCY SIGN
+F6D1;cyrBreve;CAPITAL CYRILLIC BREVE
+F6D2;cyrFlex;CAPITAL CYRILLIC CIRCUMFLEX
+F6D4;cyrbreve;CYRILLIC BREVE
+F6D5;cyrflex;CYRILLIC CIRCUMFLEX
+0064;d;LATIN SMALL LETTER D
+2020;dagger;DAGGER
+2021;daggerdbl;DOUBLE DAGGER
+F6D3;dblGrave;CAPITAL DOUBLE GRAVE ACCENT
+F6D6;dblgrave;DOUBLE GRAVE ACCENT
+010F;dcaron;LATIN SMALL LETTER D WITH CARON
+0111;dcroat;LATIN SMALL LETTER D WITH STROKE
+00B0;degree;DEGREE SIGN
+03B4;delta;GREEK SMALL LETTER DELTA
+2666;diamond;BLACK DIAMOND SUIT
+00A8;dieresis;DIAERESIS
+F6D7;dieresisacute;DIAERESIS ACUTE ACCENT
+F6D8;dieresisgrave;DIAERESIS GRAVE ACCENT
+0385;dieresistonos;GREEK DIALYTIKA TONOS
+00F7;divide;DIVISION SIGN
+2593;dkshade;DARK SHADE
+2584;dnblock;LOWER HALF BLOCK
+0024;dollar;DOLLAR SIGN
+F6E3;dollarinferior;SUBSCRIPT DOLLAR SIGN
+F724;dollaroldstyle;OLDSTYLE DOLLAR SIGN
+F6E4;dollarsuperior;SUPERSCRIPT DOLLAR SIGN
+20AB;dong;DONG SIGN
+02D9;dotaccent;DOT ABOVE
+0323;dotbelowcomb;COMBINING DOT BELOW
+0131;dotlessi;LATIN SMALL LETTER DOTLESS I
+F6BE;dotlessj;LATIN SMALL LETTER DOTLESS J
+22C5;dotmath;DOT OPERATOR
+F6EB;dsuperior;SUPERSCRIPT LATIN SMALL LETTER D
+0065;e;LATIN SMALL LETTER E
+00E9;eacute;LATIN SMALL LETTER E WITH ACUTE
+0115;ebreve;LATIN SMALL LETTER E WITH BREVE
+011B;ecaron;LATIN SMALL LETTER E WITH CARON
+00EA;ecircumflex;LATIN SMALL LETTER E WITH CIRCUMFLEX
+00EB;edieresis;LATIN SMALL LETTER E WITH DIAERESIS
+0117;edotaccent;LATIN SMALL LETTER E WITH DOT ABOVE
+00E8;egrave;LATIN SMALL LETTER E WITH GRAVE
+0038;eight;DIGIT EIGHT
+2088;eightinferior;SUBSCRIPT EIGHT
+F738;eightoldstyle;OLDSTYLE DIGIT EIGHT
+2078;eightsuperior;SUPERSCRIPT EIGHT
+2208;element;ELEMENT OF
+2026;ellipsis;HORIZONTAL ELLIPSIS
+0113;emacron;LATIN SMALL LETTER E WITH MACRON
+2014;emdash;EM DASH
+2205;emptyset;EMPTY SET
+2013;endash;EN DASH
+014B;eng;LATIN SMALL LETTER ENG
+0119;eogonek;LATIN SMALL LETTER E WITH OGONEK
+03B5;epsilon;GREEK SMALL LETTER EPSILON
+03AD;epsilontonos;GREEK SMALL LETTER EPSILON WITH TONOS
+003D;equal;EQUALS SIGN
+2261;equivalence;IDENTICAL TO
+212E;estimated;ESTIMATED SYMBOL
+F6EC;esuperior;SUPERSCRIPT LATIN SMALL LETTER E
+03B7;eta;GREEK SMALL LETTER ETA
+03AE;etatonos;GREEK SMALL LETTER ETA WITH TONOS
+00F0;eth;LATIN SMALL LETTER ETH
+0021;exclam;EXCLAMATION MARK
+203C;exclamdbl;DOUBLE EXCLAMATION MARK
+00A1;exclamdown;INVERTED EXCLAMATION MARK
+F7A1;exclamdownsmall;SMALL CAPITAL INVERTED EXCLAMATION MARK
+F721;exclamsmall;SMALL CAPITAL EXCLAMATION MARK
+2203;existential;THERE EXISTS
+0066;f;LATIN SMALL LETTER F
+2640;female;FEMALE SIGN
+FB00;ff;LATIN SMALL LIGATURE FF
+FB03;ffi;LATIN SMALL LIGATURE FFI
+FB04;ffl;LATIN SMALL LIGATURE FFL
+FB01;fi;LATIN SMALL LIGATURE FI
+2012;figuredash;FIGURE DASH
+25A0;filledbox;BLACK SQUARE
+25AC;filledrect;BLACK RECTANGLE
+0035;five;DIGIT FIVE
+215D;fiveeighths;VULGAR FRACTION FIVE EIGHTHS
+2085;fiveinferior;SUBSCRIPT FIVE
+F735;fiveoldstyle;OLDSTYLE DIGIT FIVE
+2075;fivesuperior;SUPERSCRIPT FIVE
+FB02;fl;LATIN SMALL LIGATURE FL
+0192;florin;LATIN SMALL LETTER F WITH HOOK
+0034;four;DIGIT FOUR
+2084;fourinferior;SUBSCRIPT FOUR
+F734;fouroldstyle;OLDSTYLE DIGIT FOUR
+2074;foursuperior;SUPERSCRIPT FOUR
+2044;fraction;FRACTION SLASH
+2215;fraction;DIVISION SLASH;Duplicate
+20A3;franc;FRENCH FRANC SIGN
+0067;g;LATIN SMALL LETTER G
+03B3;gamma;GREEK SMALL LETTER GAMMA
+011F;gbreve;LATIN SMALL LETTER G WITH BREVE
+01E7;gcaron;LATIN SMALL LETTER G WITH CARON
+011D;gcircumflex;LATIN SMALL LETTER G WITH CIRCUMFLEX
+0123;gcommaaccent;LATIN SMALL LETTER G WITH CEDILLA
+0121;gdotaccent;LATIN SMALL LETTER G WITH DOT ABOVE
+00DF;germandbls;LATIN SMALL LETTER SHARP S
+2207;gradient;NABLA
+0060;grave;GRAVE ACCENT
+0300;gravecomb;COMBINING GRAVE ACCENT
+003E;greater;GREATER-THAN SIGN
+2265;greaterequal;GREATER-THAN OR EQUAL TO
+00AB;guillemotleft;LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
+00BB;guillemotright;RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
+2039;guilsinglleft;SINGLE LEFT-POINTING ANGLE QUOTATION MARK
+203A;guilsinglright;SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
+0068;h;LATIN SMALL LETTER H
+0127;hbar;LATIN SMALL LETTER H WITH STROKE
+0125;hcircumflex;LATIN SMALL LETTER H WITH CIRCUMFLEX
+2665;heart;BLACK HEART SUIT
+0309;hookabovecomb;COMBINING HOOK ABOVE
+2302;house;HOUSE
+02DD;hungarumlaut;DOUBLE ACUTE ACCENT
+002D;hyphen;HYPHEN-MINUS
+00AD;hyphen;SOFT HYPHEN;Duplicate
+F6E5;hypheninferior;SUBSCRIPT HYPHEN-MINUS
+F6E6;hyphensuperior;SUPERSCRIPT HYPHEN-MINUS
+0069;i;LATIN SMALL LETTER I
+00ED;iacute;LATIN SMALL LETTER I WITH ACUTE
+012D;ibreve;LATIN SMALL LETTER I WITH BREVE
+00EE;icircumflex;LATIN SMALL LETTER I WITH CIRCUMFLEX
+00EF;idieresis;LATIN SMALL LETTER I WITH DIAERESIS
+00EC;igrave;LATIN SMALL LETTER I WITH GRAVE
+0133;ij;LATIN SMALL LIGATURE IJ
+012B;imacron;LATIN SMALL LETTER I WITH MACRON
+221E;infinity;INFINITY
+222B;integral;INTEGRAL
+2321;integralbt;BOTTOM HALF INTEGRAL
+F8F5;integralex;INTEGRAL EXTENDER
+2320;integraltp;TOP HALF INTEGRAL
+2229;intersection;INTERSECTION
+25D8;invbullet;INVERSE BULLET
+25D9;invcircle;INVERSE WHITE CIRCLE
+263B;invsmileface;BLACK SMILING FACE
+012F;iogonek;LATIN SMALL LETTER I WITH OGONEK
+03B9;iota;GREEK SMALL LETTER IOTA
+03CA;iotadieresis;GREEK SMALL LETTER IOTA WITH DIALYTIKA
+0390;iotadieresistonos;GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS
+03AF;iotatonos;GREEK SMALL LETTER IOTA WITH TONOS
+F6ED;isuperior;SUPERSCRIPT LATIN SMALL LETTER I
+0129;itilde;LATIN SMALL LETTER I WITH TILDE
+006A;j;LATIN SMALL LETTER J
+0135;jcircumflex;LATIN SMALL LETTER J WITH CIRCUMFLEX
+006B;k;LATIN SMALL LETTER K
+03BA;kappa;GREEK SMALL LETTER KAPPA
+0137;kcommaaccent;LATIN SMALL LETTER K WITH CEDILLA
+0138;kgreenlandic;LATIN SMALL LETTER KRA
+006C;l;LATIN SMALL LETTER L
+013A;lacute;LATIN SMALL LETTER L WITH ACUTE
+03BB;lambda;GREEK SMALL LETTER LAMDA
+013E;lcaron;LATIN SMALL LETTER L WITH CARON
+013C;lcommaaccent;LATIN SMALL LETTER L WITH CEDILLA
+0140;ldot;LATIN SMALL LETTER L WITH MIDDLE DOT
+003C;less;LESS-THAN SIGN
+2264;lessequal;LESS-THAN OR EQUAL TO
+258C;lfblock;LEFT HALF BLOCK
+20A4;lira;LIRA SIGN
+F6C0;ll;LATIN SMALL LETTER LL
+2227;logicaland;LOGICAL AND
+00AC;logicalnot;NOT SIGN
+2228;logicalor;LOGICAL OR
+017F;longs;LATIN SMALL LETTER LONG S
+25CA;lozenge;LOZENGE
+0142;lslash;LATIN SMALL LETTER L WITH STROKE
+F6EE;lsuperior;SUPERSCRIPT LATIN SMALL LETTER L
+2591;ltshade;LIGHT SHADE
+006D;m;LATIN SMALL LETTER M
+00AF;macron;MACRON
+02C9;macron;MODIFIER LETTER MACRON;Duplicate
+2642;male;MALE SIGN
+2212;minus;MINUS SIGN
+2032;minute;PRIME
+F6EF;msuperior;SUPERSCRIPT LATIN SMALL LETTER M
+00B5;mu;MICRO SIGN
+03BC;mu;GREEK SMALL LETTER MU;Duplicate
+00D7;multiply;MULTIPLICATION SIGN
+266A;musicalnote;EIGHTH NOTE
+266B;musicalnotedbl;BEAMED EIGHTH NOTES
+006E;n;LATIN SMALL LETTER N
+0144;nacute;LATIN SMALL LETTER N WITH ACUTE
+0149;napostrophe;LATIN SMALL LETTER N PRECEDED BY APOSTROPHE
+0148;ncaron;LATIN SMALL LETTER N WITH CARON
+0146;ncommaaccent;LATIN SMALL LETTER N WITH CEDILLA
+0039;nine;DIGIT NINE
+2089;nineinferior;SUBSCRIPT NINE
+F739;nineoldstyle;OLDSTYLE DIGIT NINE
+2079;ninesuperior;SUPERSCRIPT NINE
+2209;notelement;NOT AN ELEMENT OF
+2260;notequal;NOT EQUAL TO
+2284;notsubset;NOT A SUBSET OF
+207F;nsuperior;SUPERSCRIPT LATIN SMALL LETTER N
+00F1;ntilde;LATIN SMALL LETTER N WITH TILDE
+03BD;nu;GREEK SMALL LETTER NU
+0023;numbersign;NUMBER SIGN
+006F;o;LATIN SMALL LETTER O
+00F3;oacute;LATIN SMALL LETTER O WITH ACUTE
+014F;obreve;LATIN SMALL LETTER O WITH BREVE
+00F4;ocircumflex;LATIN SMALL LETTER O WITH CIRCUMFLEX
+00F6;odieresis;LATIN SMALL LETTER O WITH DIAERESIS
+0153;oe;LATIN SMALL LIGATURE OE
+02DB;ogonek;OGONEK
+00F2;ograve;LATIN SMALL LETTER O WITH GRAVE
+01A1;ohorn;LATIN SMALL LETTER O WITH HORN
+0151;ohungarumlaut;LATIN SMALL LETTER O WITH DOUBLE ACUTE
+014D;omacron;LATIN SMALL LETTER O WITH MACRON
+03C9;omega;GREEK SMALL LETTER OMEGA
+03D6;omega1;GREEK PI SYMBOL
+03CE;omegatonos;GREEK SMALL LETTER OMEGA WITH TONOS
+03BF;omicron;GREEK SMALL LETTER OMICRON
+03CC;omicrontonos;GREEK SMALL LETTER OMICRON WITH TONOS
+0031;one;DIGIT ONE
+2024;onedotenleader;ONE DOT LEADER
+215B;oneeighth;VULGAR FRACTION ONE EIGHTH
+F6DC;onefitted;PROPORTIONAL DIGIT ONE
+00BD;onehalf;VULGAR FRACTION ONE HALF
+2081;oneinferior;SUBSCRIPT ONE
+F731;oneoldstyle;OLDSTYLE DIGIT ONE
+00BC;onequarter;VULGAR FRACTION ONE QUARTER
+00B9;onesuperior;SUPERSCRIPT ONE
+2153;onethird;VULGAR FRACTION ONE THIRD
+25E6;openbullet;WHITE BULLET
+00AA;ordfeminine;FEMININE ORDINAL INDICATOR
+00BA;ordmasculine;MASCULINE ORDINAL INDICATOR
+221F;orthogonal;RIGHT ANGLE
+00F8;oslash;LATIN SMALL LETTER O WITH STROKE
+01FF;oslashacute;LATIN SMALL LETTER O WITH STROKE AND ACUTE
+F6F0;osuperior;SUPERSCRIPT LATIN SMALL LETTER O
+00F5;otilde;LATIN SMALL LETTER O WITH TILDE
+0070;p;LATIN SMALL LETTER P
+00B6;paragraph;PILCROW SIGN
+0028;parenleft;LEFT PARENTHESIS
+F8ED;parenleftbt;LEFT PAREN BOTTOM
+F8EC;parenleftex;LEFT PAREN EXTENDER
+208D;parenleftinferior;SUBSCRIPT LEFT PARENTHESIS
+207D;parenleftsuperior;SUPERSCRIPT LEFT PARENTHESIS
+F8EB;parenlefttp;LEFT PAREN TOP
+0029;parenright;RIGHT PARENTHESIS
+F8F8;parenrightbt;RIGHT PAREN BOTTOM
+F8F7;parenrightex;RIGHT PAREN EXTENDER
+208E;parenrightinferior;SUBSCRIPT RIGHT PARENTHESIS
+207E;parenrightsuperior;SUPERSCRIPT RIGHT PARENTHESIS
+F8F6;parenrighttp;RIGHT PAREN TOP
+2202;partialdiff;PARTIAL DIFFERENTIAL
+0025;percent;PERCENT SIGN
+002E;period;FULL STOP
+00B7;periodcentered;MIDDLE DOT
+2219;periodcentered;BULLET OPERATOR;Duplicate
+F6E7;periodinferior;SUBSCRIPT FULL STOP
+F6E8;periodsuperior;SUPERSCRIPT FULL STOP
+22A5;perpendicular;UP TACK
+2030;perthousand;PER MILLE SIGN
+20A7;peseta;PESETA SIGN
+03C6;phi;GREEK SMALL LETTER PHI
+03D5;phi1;GREEK PHI SYMBOL
+03C0;pi;GREEK SMALL LETTER PI
+002B;plus;PLUS SIGN
+00B1;plusminus;PLUS-MINUS SIGN
+211E;prescription;PRESCRIPTION TAKE
+220F;product;N-ARY PRODUCT
+2282;propersubset;SUBSET OF
+2283;propersuperset;SUPERSET OF
+221D;proportional;PROPORTIONAL TO
+03C8;psi;GREEK SMALL LETTER PSI
+0071;q;LATIN SMALL LETTER Q
+003F;question;QUESTION MARK
+00BF;questiondown;INVERTED QUESTION MARK
+F7BF;questiondownsmall;SMALL CAPITAL INVERTED QUESTION MARK
+F73F;questionsmall;SMALL CAPITAL QUESTION MARK
+0022;quotedbl;QUOTATION MARK
+201E;quotedblbase;DOUBLE LOW-9 QUOTATION MARK
+201C;quotedblleft;LEFT DOUBLE QUOTATION MARK
+201D;quotedblright;RIGHT DOUBLE QUOTATION MARK
+2018;quoteleft;LEFT SINGLE QUOTATION MARK
+201B;quotereversed;SINGLE HIGH-REVERSED-9 QUOTATION MARK
+2019;quoteright;RIGHT SINGLE QUOTATION MARK
+201A;quotesinglbase;SINGLE LOW-9 QUOTATION MARK
+0027;quotesingle;APOSTROPHE
+0072;r;LATIN SMALL LETTER R
+0155;racute;LATIN SMALL LETTER R WITH ACUTE
+221A;radical;SQUARE ROOT
+F8E5;radicalex;RADICAL EXTENDER
+0159;rcaron;LATIN SMALL LETTER R WITH CARON
+0157;rcommaaccent;LATIN SMALL LETTER R WITH CEDILLA
+2286;reflexsubset;SUBSET OF OR EQUAL TO
+2287;reflexsuperset;SUPERSET OF OR EQUAL TO
+00AE;registered;REGISTERED SIGN
+F8E8;registersans;REGISTERED SIGN SANS SERIF
+F6DA;registerserif;REGISTERED SIGN SERIF
+2310;revlogicalnot;REVERSED NOT SIGN
+03C1;rho;GREEK SMALL LETTER RHO
+02DA;ring;RING ABOVE
+F6F1;rsuperior;SUPERSCRIPT LATIN SMALL LETTER R
+2590;rtblock;RIGHT HALF BLOCK
+F6DD;rupiah;RUPIAH SIGN
+0073;s;LATIN SMALL LETTER S
+015B;sacute;LATIN SMALL LETTER S WITH ACUTE
+0161;scaron;LATIN SMALL LETTER S WITH CARON
+015F;scedilla;LATIN SMALL LETTER S WITH CEDILLA
+F6C2;scedilla;LATIN SMALL LETTER S WITH CEDILLA;Duplicate
+015D;scircumflex;LATIN SMALL LETTER S WITH CIRCUMFLEX
+0219;scommaaccent;LATIN SMALL LETTER S WITH COMMA BELOW
+2033;second;DOUBLE PRIME
+00A7;section;SECTION SIGN
+003B;semicolon;SEMICOLON
+0037;seven;DIGIT SEVEN
+215E;seveneighths;VULGAR FRACTION SEVEN EIGHTHS
+2087;seveninferior;SUBSCRIPT SEVEN
+F737;sevenoldstyle;OLDSTYLE DIGIT SEVEN
+2077;sevensuperior;SUPERSCRIPT SEVEN
+2592;shade;MEDIUM SHADE
+03C3;sigma;GREEK SMALL LETTER SIGMA
+03C2;sigma1;GREEK SMALL LETTER FINAL SIGMA
+223C;similar;TILDE OPERATOR
+0036;six;DIGIT SIX
+2086;sixinferior;SUBSCRIPT SIX
+F736;sixoldstyle;OLDSTYLE DIGIT SIX
+2076;sixsuperior;SUPERSCRIPT SIX
+002F;slash;SOLIDUS
+263A;smileface;WHITE SMILING FACE
+0020;space;SPACE
+00A0;space;NO-BREAK SPACE;Duplicate
+2660;spade;BLACK SPADE SUIT
+F6F2;ssuperior;SUPERSCRIPT LATIN SMALL LETTER S
+00A3;sterling;POUND SIGN
+220B;suchthat;CONTAINS AS MEMBER
+2211;summation;N-ARY SUMMATION
+263C;sun;WHITE SUN WITH RAYS
+0074;t;LATIN SMALL LETTER T
+03C4;tau;GREEK SMALL LETTER TAU
+0167;tbar;LATIN SMALL LETTER T WITH STROKE
+0165;tcaron;LATIN SMALL LETTER T WITH CARON
+0163;tcommaaccent;LATIN SMALL LETTER T WITH CEDILLA
+021B;tcommaaccent;LATIN SMALL LETTER T WITH COMMA BELOW;Duplicate
+2234;therefore;THEREFORE
+03B8;theta;GREEK SMALL LETTER THETA
+03D1;theta1;GREEK THETA SYMBOL
+00FE;thorn;LATIN SMALL LETTER THORN
+0033;three;DIGIT THREE
+215C;threeeighths;VULGAR FRACTION THREE EIGHTHS
+2083;threeinferior;SUBSCRIPT THREE
+F733;threeoldstyle;OLDSTYLE DIGIT THREE
+00BE;threequarters;VULGAR FRACTION THREE QUARTERS
+F6DE;threequartersemdash;THREE QUARTERS EM DASH
+00B3;threesuperior;SUPERSCRIPT THREE
+02DC;tilde;SMALL TILDE
+0303;tildecomb;COMBINING TILDE
+0384;tonos;GREEK TONOS
+2122;trademark;TRADE MARK SIGN
+F8EA;trademarksans;TRADE MARK SIGN SANS SERIF
+F6DB;trademarkserif;TRADE MARK SIGN SERIF
+25BC;triagdn;BLACK DOWN-POINTING TRIANGLE
+25C4;triaglf;BLACK LEFT-POINTING POINTER
+25BA;triagrt;BLACK RIGHT-POINTING POINTER
+25B2;triagup;BLACK UP-POINTING TRIANGLE
+F6F3;tsuperior;SUPERSCRIPT LATIN SMALL LETTER T
+0032;two;DIGIT TWO
+2025;twodotenleader;TWO DOT LEADER
+2082;twoinferior;SUBSCRIPT TWO
+F732;twooldstyle;OLDSTYLE DIGIT TWO
+00B2;twosuperior;SUPERSCRIPT TWO
+2154;twothirds;VULGAR FRACTION TWO THIRDS
+0075;u;LATIN SMALL LETTER U
+00FA;uacute;LATIN SMALL LETTER U WITH ACUTE
+016D;ubreve;LATIN SMALL LETTER U WITH BREVE
+00FB;ucircumflex;LATIN SMALL LETTER U WITH CIRCUMFLEX
+00FC;udieresis;LATIN SMALL LETTER U WITH DIAERESIS
+00F9;ugrave;LATIN SMALL LETTER U WITH GRAVE
+01B0;uhorn;LATIN SMALL LETTER U WITH HORN
+0171;uhungarumlaut;LATIN SMALL LETTER U WITH DOUBLE ACUTE
+016B;umacron;LATIN SMALL LETTER U WITH MACRON
+005F;underscore;LOW LINE
+2017;underscoredbl;DOUBLE LOW LINE
+222A;union;UNION
+2200;universal;FOR ALL
+0173;uogonek;LATIN SMALL LETTER U WITH OGONEK
+2580;upblock;UPPER HALF BLOCK
+03C5;upsilon;GREEK SMALL LETTER UPSILON
+03CB;upsilondieresis;GREEK SMALL LETTER UPSILON WITH DIALYTIKA
+03B0;upsilondieresistonos;GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS
+03CD;upsilontonos;GREEK SMALL LETTER UPSILON WITH TONOS
+016F;uring;LATIN SMALL LETTER U WITH RING ABOVE
+0169;utilde;LATIN SMALL LETTER U WITH TILDE
+0076;v;LATIN SMALL LETTER V
+0077;w;LATIN SMALL LETTER W
+1E83;wacute;LATIN SMALL LETTER W WITH ACUTE
+0175;wcircumflex;LATIN SMALL LETTER W WITH CIRCUMFLEX
+1E85;wdieresis;LATIN SMALL LETTER W WITH DIAERESIS
+2118;weierstrass;SCRIPT CAPITAL P
+1E81;wgrave;LATIN SMALL LETTER W WITH GRAVE
+0078;x;LATIN SMALL LETTER X
+03BE;xi;GREEK SMALL LETTER XI
+0079;y;LATIN SMALL LETTER Y
+00FD;yacute;LATIN SMALL LETTER Y WITH ACUTE
+0177;ycircumflex;LATIN SMALL LETTER Y WITH CIRCUMFLEX
+00FF;ydieresis;LATIN SMALL LETTER Y WITH DIAERESIS
+00A5;yen;YEN SIGN
+1EF3;ygrave;LATIN SMALL LETTER Y WITH GRAVE
+007A;z;LATIN SMALL LETTER Z
+017A;zacute;LATIN SMALL LETTER Z WITH ACUTE
+017E;zcaron;LATIN SMALL LETTER Z WITH CARON
+017C;zdotaccent;LATIN SMALL LETTER Z WITH DOT ABOVE
+0030;zero;DIGIT ZERO
+2080;zeroinferior;SUBSCRIPT ZERO
+F730;zerooldstyle;OLDSTYLE DIGIT ZERO
+2070;zerosuperior;SUPERSCRIPT ZERO
+03B6;zeta;GREEK SMALL LETTER ZETA
+"""
+
+
+AGLError = "AGLError"
+
+AGL2UV = {}
+UV2AGL = {}
+
+def _builddicts():
+ import re
+ import string
+
+ lines = string.split(_aglText, "\n")
+
+ parseAGL_RE = re.compile("([0-9A-F]{4});([A-Za-z_0-9.]+);.*?$")
+
+ for line in lines:
+ if not line:
+ break
+ if line[:1] == '#':
+ continue
+ m = parseAGL_RE.match(line)
+ if not m:
+ raise AGLError, "syntax error in glyphlist.txt: %s" % repr(line[:20])
+ unicode = m.group(1)
+ assert len(unicode) == 4
+ unicode = string.atoi(unicode, 16)
+ glyphName = m.group(2)
+ if AGL2UV.has_key(glyphName):
+ assert type(AGL2UV[glyphName]) == type(0)
+ AGL2UV[glyphName] = AGL2UV[glyphName], unicode
+ else:
+ AGL2UV[glyphName] = unicode
+ UV2AGL[unicode] = glyphName
+
+_builddicts()
+
diff --git a/Lib/fontTools/cffLib.py b/Lib/fontTools/cffLib.py
new file mode 100644
index 0000000..76476c3
--- /dev/null
+++ b/Lib/fontTools/cffLib.py
@@ -0,0 +1,459 @@
+"""cffLib.py -- read/write tools for Adobe CFF fonts."""
+
+__version__ = "1.0b1"
+__author__ = "jvr"
+
+import struct, sstruct
+import string
+import types
+import psCharStrings
+
+
+cffHeaderFormat = """
+ major: B
+ minor: B
+ hdrSize: B
+ offSize: B
+"""
+
+class CFFFontSet:
+
+ def __init__(self):
+ self.fonts = {}
+
+ def decompile(self, data):
+ sstruct.unpack(cffHeaderFormat, data[:4], self)
+ assert self.major == 1 and self.minor == 0, \
+ "unknown CFF format: %d.%d" % (self.major, self.minor)
+ restdata = data[self.hdrSize:]
+
+ self.fontNames, restdata = readINDEX(restdata)
+ topDicts, restdata = readINDEX(restdata)
+ strings, restdata = readINDEX(restdata)
+ strings = IndexedStrings(strings)
+ globalSubrs, restdata = readINDEX(restdata)
+ self.GlobalSubrs = map(psCharStrings.T2CharString, globalSubrs)
+
+ for i in range(len(topDicts)):
+ font = self.fonts[self.fontNames[i]] = CFFFont()
+ font.GlobalSubrs = self.GlobalSubrs # Hmm.
+ font.decompile(data, topDicts[i], strings, self) # maybe only 'on demand'?
+
+
+ def compile(self):
+ strings = IndexedStrings()
+ XXXX
+
+ def toXML(self, xmlWriter, progress=None):
+ xmlWriter.newline()
+ for fontName in self.fontNames:
+ xmlWriter.begintag("CFFFont", name=fontName)
+ xmlWriter.newline()
+ font = self.fonts[fontName]
+ font.toXML(xmlWriter, progress)
+ xmlWriter.endtag("CFFFont")
+ xmlWriter.newline()
+ xmlWriter.newline()
+ xmlWriter.begintag("GlobalSubrs")
+ xmlWriter.newline()
+ for i in range(len(self.GlobalSubrs)):
+ xmlWriter.newline()
+ xmlWriter.begintag("CharString", id=i)
+ xmlWriter.newline()
+ self.GlobalSubrs[i].toXML(xmlWriter)
+ xmlWriter.endtag("CharString")
+ xmlWriter.newline()
+ xmlWriter.newline()
+ xmlWriter.endtag("GlobalSubrs")
+ xmlWriter.newline()
+ xmlWriter.newline()
+
+ def fromXML(self, (name, attrs, content)):
+ xxx
+
+
+class IndexedStrings:
+
+ def __init__(self, strings=None):
+ if strings is None:
+ strings = []
+ self.strings = strings
+
+ def __getitem__(self, SID):
+ if SID < cffStandardStringCount:
+ return cffStandardStrings[SID]
+ else:
+ return self.strings[SID - cffStandardStringCount]
+
+ def getSID(self, s):
+ if not hasattr(self, "stringMapping"):
+ self.buildStringMapping()
+ if cffStandardStringMapping.has_key(s):
+ SID = cffStandardStringMapping[s]
+ if self.stringMapping.has_key(s):
+ SID = self.stringMapping[s]
+ else:
+ SID = len(self.strings) + cffStandardStringCount
+ self.strings.append(s)
+ self.stringMapping[s] = SID
+ return SID
+
+ def getStrings(self):
+ return self.strings
+
+ def buildStringMapping(self):
+ self.stringMapping = {}
+ for index in range(len(self.strings)):
+ self.stringMapping[self.strings[index]] = index + cffStandardStringCount
+
+
+class CFFFont:
+
+ defaults = psCharStrings.topDictDefaults
+
+ def __init__(self):
+ pass
+
+ def __getattr__(self, attr):
+ if not self.defaults.has_key(attr):
+ raise AttributeError, attr
+ return self.defaults[attr]
+
+ def fromDict(self, dict):
+ self.__dict__.update(dict)
+
+ def decompile(self, data, topDictData, strings, fontSet):
+ top = psCharStrings.TopDictDecompiler(strings)
+ top.decompile(topDictData)
+ self.fromDict(top.getDict())
+
+ # get private dict
+ size, offset = self.Private
+ #print "YYY Private (size, offset):", size, offset
+ privateData = data[offset:offset+size]
+ self.Private = PrivateDict()
+ self.Private.decompile(data[offset:], privateData, strings)
+
+ # get raw charstrings
+ #print "YYYY CharStrings offset:", self.CharStrings
+ rawCharStrings, restdata = readINDEX(data[self.CharStrings:])
+ nGlyphs = len(rawCharStrings)
+
+ # get charset (or rather: get glyphNames)
+ charsetOffset = self.charset
+ if charsetOffset == 0:
+ xxx # standard charset
+ else:
+ #print "YYYYY charsetOffset:", charsetOffset
+ format = ord(data[charsetOffset])
+ if format == 0:
+ xxx
+ elif format == 1:
+ charSet = parseCharsetFormat1(nGlyphs,
+ data[charsetOffset+1:], strings)
+ elif format == 2:
+ charSet = parseCharsetFormat2(nGlyphs,
+ data[charsetOffset+1:], strings)
+ elif format == 3:
+ xxx
+ else:
+ xxx
+ self.charset = charSet
+
+ assert len(charSet) == nGlyphs
+ self.CharStrings = charStrings = {}
+ if self.CharstringType == 2:
+ # Type 2 CharStrings
+ charStringClass = psCharStrings.T2CharString
+ else:
+ # Type 1 CharStrings
+ charStringClass = psCharStrings.T1CharString
+ for i in range(nGlyphs):
+ charStrings[charSet[i]] = charStringClass(rawCharStrings[i])
+ assert len(charStrings) == nGlyphs
+
+ # XXX Encoding!
+ encoding = self.Encoding
+ if encoding not in (0, 1):
+ # encoding is an _offset_ from the beginning of 'data' to an encoding subtable
+ XXX
+ self.Encoding = encoding
+
+ def getGlyphOrder(self):
+ return self.charset
+
+ def setGlyphOrder(self, glyphOrder):
+ self.charset = glyphOrder
+
+ def decompileAllCharStrings(self):
+ if self.CharstringType == 2:
+ # Type 2 CharStrings
+ decompiler = psCharStrings.SimpleT2Decompiler(self.Private.Subrs, self.GlobalSubrs)
+ for charString in self.CharStrings.values():
+ if charString.needsDecompilation():
+ decompiler.reset()
+ decompiler.execute(charString)
+ else:
+ # Type 1 CharStrings
+ for charString in self.CharStrings.values():
+ charString.decompile()
+
+ def toXML(self, xmlWriter, progress=None):
+ xmlWriter.newline()
+ # first dump the simple values
+ self.toXMLSimpleValues(xmlWriter)
+
+ # dump charset
+ # XXX
+
+ # decompile all charstrings
+ if progress:
+ progress.setlabel("Decompiling CharStrings...")
+ self.decompileAllCharStrings()
+
+ # dump private dict
+ xmlWriter.begintag("Private")
+ xmlWriter.newline()
+ self.Private.toXML(xmlWriter)
+ xmlWriter.endtag("Private")
+ xmlWriter.newline()
+
+ self.toXMLCharStrings(xmlWriter, progress)
+
+ def toXMLSimpleValues(self, xmlWriter):
+ keys = self.__dict__.keys()
+ keys.remove("CharStrings")
+ keys.remove("Private")
+ keys.remove("charset")
+ keys.remove("GlobalSubrs")
+ keys.sort()
+ for key in keys:
+ value = getattr(self, key)
+ if key == "Encoding":
+ if value == 0:
+ # encoding is (Adobe) Standard Encoding
+ value = "StandardEncoding"
+ elif value == 1:
+ # encoding is Expert Encoding
+ value = "ExpertEncoding"
+ if type(value) == types.ListType:
+ value = string.join(map(str, value), " ")
+ else:
+ value = str(value)
+ xmlWriter.begintag(key)
+ if hasattr(value, "toXML"):
+ xmlWriter.newline()
+ value.toXML(xmlWriter)
+ xmlWriter.newline()
+ else:
+ xmlWriter.write(value)
+ xmlWriter.endtag(key)
+ xmlWriter.newline()
+ xmlWriter.newline()
+
+ def toXMLCharStrings(self, xmlWriter, progress=None):
+ charStrings = self.CharStrings
+ xmlWriter.newline()
+ xmlWriter.begintag("CharStrings")
+ xmlWriter.newline()
+ glyphNames = charStrings.keys()
+ glyphNames.sort()
+ for glyphName in glyphNames:
+ if progress:
+ progress.setlabel("Dumping 'CFF ' table... (%s)" % glyphName)
+ progress.increment()
+ xmlWriter.newline()
+ charString = charStrings[glyphName]
+ xmlWriter.begintag("CharString", name=glyphName)
+ xmlWriter.newline()
+ charString.toXML(xmlWriter)
+ xmlWriter.endtag("CharString")
+ xmlWriter.newline()
+ xmlWriter.newline()
+ xmlWriter.endtag("CharStrings")
+ xmlWriter.newline()
+
+
+class PrivateDict:
+
+ defaults = psCharStrings.privateDictDefaults
+
+ def __init__(self):
+ pass
+
+ def decompile(self, data, privateData, strings):
+ p = psCharStrings.PrivateDictDecompiler(strings)
+ p.decompile(privateData)
+ self.fromDict(p.getDict())
+
+ # get local subrs
+ #print "YYY Private.Subrs:", self.Subrs
+ chunk = data[self.Subrs:]
+ localSubrs, restdata = readINDEX(chunk)
+ self.Subrs = map(psCharStrings.T2CharString, localSubrs)
+
+ def toXML(self, xmlWriter):
+ xmlWriter.newline()
+ keys = self.__dict__.keys()
+ keys.remove("Subrs")
+ for key in keys:
+ value = getattr(self, key)
+ if type(value) == types.ListType:
+ value = string.join(map(str, value), " ")
+ else:
+ value = str(value)
+ xmlWriter.begintag(key)
+ xmlWriter.write(value)
+ xmlWriter.endtag(key)
+ xmlWriter.newline()
+ # write subroutines
+ xmlWriter.newline()
+ xmlWriter.begintag("Subrs")
+ xmlWriter.newline()
+ for i in range(len(self.Subrs)):
+ xmlWriter.newline()
+ xmlWriter.begintag("CharString", id=i)
+ xmlWriter.newline()
+ self.Subrs[i].toXML(xmlWriter)
+ xmlWriter.endtag("CharString")
+ xmlWriter.newline()
+ xmlWriter.newline()
+ xmlWriter.endtag("Subrs")
+ xmlWriter.newline()
+ xmlWriter.newline()
+
+ def __getattr__(self, attr):
+ if not self.defaults.has_key(attr):
+ raise AttributeError, attr
+ return self.defaults[attr]
+
+ def fromDict(self, dict):
+ self.__dict__.update(dict)
+
+
+def readINDEX(data):
+ count, = struct.unpack(">H", data[:2])
+ count = int(count)
+ offSize = ord(data[2])
+ data = data[3:]
+ offsets = []
+ for index in range(count+1):
+ chunk = data[index * offSize: (index+1) * offSize]
+ chunk = '\0' * (4 - offSize) + chunk
+ offset, = struct.unpack(">L", chunk)
+ offset = int(offset)
+ offsets.append(offset)
+ data = data[(count+1) * offSize:]
+ prev = offsets[0]
+ stuff = []
+ for next in offsets[1:]:
+ chunk = data[prev-1:next-1]
+ assert len(chunk) == next - prev
+ stuff.append(chunk)
+ prev = next
+ data = data[next-1:]
+ return stuff, data
+
+
+def parseCharsetFormat1(nGlyphs, data, strings):
+ charSet = ['.notdef']
+ count = 1
+ while count < nGlyphs:
+ first = int(struct.unpack(">H", data[:2])[0])
+ nLeft = ord(data[2])
+ data = data[3:]
+ for SID in range(first, first+nLeft+1):
+ charSet.append(strings[SID])
+ count = count + nLeft + 1
+ return charSet
+
+
+def parseCharsetFormat2(nGlyphs, data, strings):
+ charSet = ['.notdef']
+ count = 1
+ while count < nGlyphs:
+ first = int(struct.unpack(">H", data[:2])[0])
+ nLeft = int(struct.unpack(">H", data[2:4])[0])
+ data = data[4:]
+ for SID in range(first, first+nLeft+1):
+ charSet.append(strings[SID])
+ count = count + nLeft + 1
+ return charSet
+
+
+# The 391 Standard Strings as used in the CFF format.
+# from Adobe Technical None #5176, version 1.0, 18 March 1998
+
+cffStandardStrings = ['.notdef', 'space', 'exclam', 'quotedbl', 'numbersign',
+ 'dollar', 'percent', 'ampersand', 'quoteright', 'parenleft', 'parenright',
+ 'asterisk', 'plus', 'comma', 'hyphen', 'period', 'slash', 'zero', 'one',
+ 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'colon',
+ 'semicolon', 'less', 'equal', 'greater', 'question', 'at', 'A', 'B', 'C',
+ 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',
+ 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'bracketleft', 'backslash',
+ 'bracketright', 'asciicircum', 'underscore', 'quoteleft', 'a', 'b', 'c',
+ 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r',
+ 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'braceleft', 'bar', 'braceright',
+ 'asciitilde', 'exclamdown', 'cent', 'sterling', 'fraction', 'yen', 'florin',
+ 'section', 'currency', 'quotesingle', 'quotedblleft', 'guillemotleft',
+ 'guilsinglleft', 'guilsinglright', 'fi', 'fl', 'endash', 'dagger',
+ 'daggerdbl', 'periodcentered', 'paragraph', 'bullet', 'quotesinglbase',
+ 'quotedblbase', 'quotedblright', 'guillemotright', 'ellipsis', 'perthousand',
+ 'questiondown', 'grave', 'acute', 'circumflex', 'tilde', 'macron', 'breve',
+ 'dotaccent', 'dieresis', 'ring', 'cedilla', 'hungarumlaut', 'ogonek', 'caron',
+ 'emdash', 'AE', 'ordfeminine', 'Lslash', 'Oslash', 'OE', 'ordmasculine', 'ae',
+ 'dotlessi', 'lslash', 'oslash', 'oe', 'germandbls', 'onesuperior',
+ 'logicalnot', 'mu', 'trademark', 'Eth', 'onehalf', 'plusminus', 'Thorn',
+ 'onequarter', 'divide', 'brokenbar', 'degree', 'thorn', 'threequarters',
+ 'twosuperior', 'registered', 'minus', 'eth', 'multiply', 'threesuperior',
+ 'copyright', 'Aacute', 'Acircumflex', 'Adieresis', 'Agrave', 'Aring',
+ 'Atilde', 'Ccedilla', 'Eacute', 'Ecircumflex', 'Edieresis', 'Egrave',
+ 'Iacute', 'Icircumflex', 'Idieresis', 'Igrave', 'Ntilde', 'Oacute',
+ 'Ocircumflex', 'Odieresis', 'Ograve', 'Otilde', 'Scaron', 'Uacute',
+ 'Ucircumflex', 'Udieresis', 'Ugrave', 'Yacute', 'Ydieresis', 'Zcaron',
+ 'aacute', 'acircumflex', 'adieresis', 'agrave', 'aring', 'atilde', 'ccedilla',
+ 'eacute', 'ecircumflex', 'edieresis', 'egrave', 'iacute', 'icircumflex',
+ 'idieresis', 'igrave', 'ntilde', 'oacute', 'ocircumflex', 'odieresis',
+ 'ograve', 'otilde', 'scaron', 'uacute', 'ucircumflex', 'udieresis', 'ugrave',
+ 'yacute', 'ydieresis', 'zcaron', 'exclamsmall', 'Hungarumlautsmall',
+ 'dollaroldstyle', 'dollarsuperior', 'ampersandsmall', 'Acutesmall',
+ 'parenleftsuperior', 'parenrightsuperior', 'twodotenleader', 'onedotenleader',
+ 'zerooldstyle', 'oneoldstyle', 'twooldstyle', 'threeoldstyle', 'fouroldstyle',
+ 'fiveoldstyle', 'sixoldstyle', 'sevenoldstyle', 'eightoldstyle',
+ 'nineoldstyle', 'commasuperior', 'threequartersemdash', 'periodsuperior',
+ 'questionsmall', 'asuperior', 'bsuperior', 'centsuperior', 'dsuperior',
+ 'esuperior', 'isuperior', 'lsuperior', 'msuperior', 'nsuperior', 'osuperior',
+ 'rsuperior', 'ssuperior', 'tsuperior', 'ff', 'ffi', 'ffl', 'parenleftinferior',
+ 'parenrightinferior', 'Circumflexsmall', 'hyphensuperior', 'Gravesmall',
+ 'Asmall', 'Bsmall', 'Csmall', 'Dsmall', 'Esmall', 'Fsmall', 'Gsmall', 'Hsmall',
+ 'Ismall', 'Jsmall', 'Ksmall', 'Lsmall', 'Msmall', 'Nsmall', 'Osmall', 'Psmall',
+ 'Qsmall', 'Rsmall', 'Ssmall', 'Tsmall', 'Usmall', 'Vsmall', 'Wsmall', 'Xsmall',
+ 'Ysmall', 'Zsmall', 'colonmonetary', 'onefitted', 'rupiah', 'Tildesmall',
+ 'exclamdownsmall', 'centoldstyle', 'Lslashsmall', 'Scaronsmall', 'Zcaronsmall',
+ 'Dieresissmall', 'Brevesmall', 'Caronsmall', 'Dotaccentsmall', 'Macronsmall',
+ 'figuredash', 'hypheninferior', 'Ogoneksmall', 'Ringsmall', 'Cedillasmall',
+ 'questiondownsmall', 'oneeighth', 'threeeighths', 'fiveeighths', 'seveneighths',
+ 'onethird', 'twothirds', 'zerosuperior', 'foursuperior', 'fivesuperior',
+ 'sixsuperior', 'sevensuperior', 'eightsuperior', 'ninesuperior', 'zeroinferior',
+ 'oneinferior', 'twoinferior', 'threeinferior', 'fourinferior', 'fiveinferior',
+ 'sixinferior', 'seveninferior', 'eightinferior', 'nineinferior', 'centinferior',
+ 'dollarinferior', 'periodinferior', 'commainferior', 'Agravesmall',
+ 'Aacutesmall', 'Acircumflexsmall', 'Atildesmall', 'Adieresissmall', 'Aringsmall',
+ 'AEsmall', 'Ccedillasmall', 'Egravesmall', 'Eacutesmall', 'Ecircumflexsmall',
+ 'Edieresissmall', 'Igravesmall', 'Iacutesmall', 'Icircumflexsmall',
+ 'Idieresissmall', 'Ethsmall', 'Ntildesmall', 'Ogravesmall', 'Oacutesmall',
+ 'Ocircumflexsmall', 'Otildesmall', 'Odieresissmall', 'OEsmall', 'Oslashsmall',
+ 'Ugravesmall', 'Uacutesmall', 'Ucircumflexsmall', 'Udieresissmall',
+ 'Yacutesmall', 'Thornsmall', 'Ydieresissmall', '001.000', '001.001', '001.002',
+ '001.003', 'Black', 'Bold', 'Book', 'Light', 'Medium', 'Regular', 'Roman',
+ 'Semibold'
+]
+
+cffStandardStringCount = 391
+assert len(cffStandardStrings) == cffStandardStringCount
+# build reverse mapping
+cffStandardStringMapping = {}
+for _i in range(cffStandardStringCount):
+ cffStandardStringMapping[cffStandardStrings[_i]] = _i
+
+
diff --git a/Lib/fontTools/fondLib.py b/Lib/fontTools/fondLib.py
new file mode 100644
index 0000000..94468cb
--- /dev/null
+++ b/Lib/fontTools/fondLib.py
@@ -0,0 +1,553 @@
+import os
+import Res
+import struct, sstruct
+import string
+
+__version__ = "1.0b2"
+__author__ = "jvr"
+
+error = "fondLib.error"
+
+DEBUG = 0
+
+headerformat = """
+ ffFlags: h
+ ffFamID: h
+ ffFirstChar: h
+ ffLastChar: h
+ ffAscent: h
+ ffDescent: h
+ ffLeading: h
+ ffWidMax: h
+ ffWTabOff: l
+ ffKernOff: l
+ ffStylOff: l
+"""
+
+FONDheadersize = 52
+
+class FontFamily:
+
+ def __init__(self, theRes, mode = 'r'):
+ self.ID, type, self.name = theRes.GetResInfo()
+ if type <> 'FOND':
+ raise ValueError, "FOND resource required"
+ self.FOND = theRes
+ self.mode = mode
+ self.changed = 0
+
+ if DEBUG:
+ self.parsedthings = []
+
+ def parse(self):
+ self._getheader()
+ self._getfontassociationtable()
+ self._getoffsettable()
+ self._getboundingboxtable()
+ self._getglyphwidthtable()
+ self._getstylemappingtable()
+ self._getglyphencodingsubtable()
+ self._getkerningtables()
+
+ def minimalparse(self):
+ self._getheader()
+ self._getglyphwidthtable()
+ self._getstylemappingtable()
+
+ def __repr__(self):
+ return "<FontFamily instance of %s>" % self.name
+
+ def getflags(self):
+ return self.fondClass
+
+ def setflags(self, flags):
+ self.changed = 1
+ self.fondClass = flags
+
+ def save(self, destresfile = None):
+ if self.mode <> 'w':
+ raise error, "can't save font: no write permission"
+ self._buildfontassociationtable()
+ self._buildoffsettable()
+ self._buildboundingboxtable()
+ self._buildglyphwidthtable()
+ self._buildkerningtables()
+ self._buildstylemappingtable()
+ self._buildglyphencodingsubtable()
+ rawnames = [ "_rawheader",
+ "_rawfontassociationtable",
+ "_rawoffsettable",
+ "_rawglyphwidthtable",
+ "_rawstylemappingtable",
+ "_rawglyphencodingsubtable",
+ "_rawkerningtables"
+ ]
+ for name in rawnames[1:]: # skip header
+ data = getattr(self, name)
+ if len(data) & 1:
+ setattr(self, name, data + '\0')
+
+ self.ffWTabOff = FONDheadersize + len(self._rawfontassociationtable) + len(self._rawoffsettable)
+ self.ffStylOff = self.ffWTabOff + len(self._rawglyphwidthtable)
+ self.ffKernOff = self.ffStylOff + len(self._rawstylemappingtable) + len(self._rawglyphencodingsubtable)
+ self.glyphTableOffset = len(self._rawstylemappingtable)
+
+ if not self._rawglyphwidthtable:
+ self.ffWTabOff = 0
+ if not self._rawstylemappingtable:
+ self.ffStylOff = 0
+ if not self._rawglyphencodingsubtable:
+ self.glyphTableOffset = 0
+ if not self._rawkerningtables:
+ self.ffKernOff = 0
+
+ self._buildheader()
+
+ # glyphTableOffset has only just been calculated
+ self._updatestylemappingtable()
+
+ newdata = ""
+ for name in rawnames:
+ newdata = newdata + getattr(self, name)
+ if destresfile is None:
+ self.FOND.data = newdata
+ self.FOND.ChangedResource()
+ self.FOND.WriteResource()
+ else:
+ ID, type, name = self.FOND.GetResInfo()
+ self.FOND.DetachResource()
+ self.FOND.data = newdata
+ saveref = Res.CurResFile()
+ Res.UseResFile(destresfile)
+ self.FOND.AddResource(type, ID, name)
+ Res.UseResFile(saveref)
+ self.changed = 0
+
+ def _getheader(self):
+ data = self.FOND.data
+ sstruct.unpack(headerformat, data[:28], self)
+ self.ffProperty = struct.unpack("9h", data[28:46])
+ self.ffIntl = struct.unpack("hh", data[46:50])
+ self.ffVersion, = struct.unpack("h", data[50:FONDheadersize])
+
+ if DEBUG:
+ self._rawheader = data[:FONDheadersize]
+ self.parsedthings.append((0, FONDheadersize, 'header'))
+
+ def _buildheader(self):
+ header = sstruct.pack(headerformat, self)
+ header = header + apply(struct.pack, ("9h",) + self.ffProperty)
+ header = header + apply(struct.pack, ("hh",) + self.ffIntl)
+ header = header + struct.pack("h", self.ffVersion)
+ if DEBUG:
+ print "header is the same?", self._rawheader == header and 'yes.' or 'no.'
+ if self._rawheader <> header:
+ print len(self._rawheader), len(header)
+ self._rawheader = header
+
+ def _getfontassociationtable(self):
+ data = self.FOND.data
+ offset = FONDheadersize
+ numberofentries, = struct.unpack("h", data[offset:offset+2])
+ numberofentries = numberofentries + 1
+ size = numberofentries * 6
+ self.fontAssoc = []
+ for i in range(offset + 2, offset + size, 6):
+ self.fontAssoc.append(struct.unpack("3h", data[i:i+6]))
+
+ self._endoffontassociationtable = offset + size + 2
+ if DEBUG:
+ self._rawfontassociationtable = data[offset:self._endoffontassociationtable]
+ self.parsedthings.append((offset, self._endoffontassociationtable, 'fontassociationtable'))
+
+ def _buildfontassociationtable(self):
+ data = struct.pack("h", len(self.fontAssoc) - 1)
+ for size, stype, ID in self.fontAssoc:
+ data = data + struct.pack("3h", size, stype, ID)
+
+ if DEBUG:
+ print "font association table is the same?", self._rawfontassociationtable == data and 'yes.' or 'no.'
+ if self._rawfontassociationtable <> data:
+ print len(self._rawfontassociationtable), len(data)
+ self._rawfontassociationtable = data
+
+ def _getoffsettable(self):
+ if self.ffWTabOff == 0:
+ self._rawoffsettable = ""
+ return
+ data = self.FOND.data
+ # Quick'n'Dirty. What's the spec anyway? Can't find it...
+ offset = self._endoffontassociationtable
+ count = self.ffWTabOff
+ self._rawoffsettable = data[offset:count]
+ if DEBUG:
+ self.parsedthings.append((offset, count, 'offsettable&bbtable'))
+
+ def _buildoffsettable(self):
+ if not hasattr(self, "_rawoffsettable"):
+ self._rawoffsettable = ""
+
+ def _getboundingboxtable(self):
+ self.boundingBoxes = None
+ if self._rawoffsettable[:6] <> '\0\0\0\0\0\6': # XXX ????
+ return
+ boxes = {}
+ data = self._rawoffsettable[6:]
+ numstyles = struct.unpack("h", data[:2])[0] + 1
+ data = data[2:]
+ for i in range(numstyles):
+ style, l, b, r, t = struct.unpack("hhhhh", data[:10])
+ boxes[style] = (l, b, r, t)
+ data = data[10:]
+ self.boundingBoxes = boxes
+
+ def _buildboundingboxtable(self):
+ if self.boundingBoxes and self._rawoffsettable[:6] == '\0\0\0\0\0\6':
+ boxes = self.boundingBoxes.items()
+ boxes.sort()
+ data = '\0\0\0\0\0\6' + struct.pack("h", len(boxes) - 1)
+ for style, (l, b, r, t) in boxes:
+ data = data + struct.pack("hhhhh", style, l, b, r, t)
+ self._rawoffsettable = data
+
+ def _getglyphwidthtable(self):
+ self.widthTables = {}
+ if self.ffWTabOff == 0:
+ return
+ data = self.FOND.data
+ offset = self.ffWTabOff
+ numberofentries, = struct.unpack("h", data[offset:offset+2])
+ numberofentries = numberofentries + 1
+ count = offset + 2
+ for i in range(numberofentries):
+ stylecode, = struct.unpack("h", data[count:count+2])
+ widthtable = self.widthTables[stylecode] = []
+ count = count + 2
+ for j in range(3 + self.ffLastChar - self.ffFirstChar):
+ width, = struct.unpack("h", data[count:count+2])
+ widthtable.append(width)
+ count = count + 2
+
+ if DEBUG:
+ self._rawglyphwidthtable = data[offset:count]
+ self.parsedthings.append((offset, count, 'glyphwidthtable'))
+
+ def _buildglyphwidthtable(self):
+ if not self.widthTables:
+ self._rawglyphwidthtable = ""
+ return
+ numberofentries = len(self.widthTables)
+ data = struct.pack('h', numberofentries - 1)
+ tables = self.widthTables.items()
+ tables.sort()
+ for stylecode, table in tables:
+ data = data + struct.pack('h', stylecode)
+ if len(table) <> (3 + self.ffLastChar - self.ffFirstChar):
+ raise error, "width table has wrong length"
+ for width in table:
+ data = data + struct.pack('h', width)
+ if DEBUG:
+ print "glyph width table is the same?", self._rawglyphwidthtable == data and 'yes.' or 'no.'
+ self._rawglyphwidthtable = data
+
+ def _getkerningtables(self):
+ self.kernTables = {}
+ if self.ffKernOff == 0:
+ return
+ data = self.FOND.data
+ offset = self.ffKernOff
+ numberofentries, = struct.unpack("h", data[offset:offset+2])
+ numberofentries = numberofentries + 1
+ count = offset + 2
+ for i in range(numberofentries):
+ stylecode, = struct.unpack("h", data[count:count+2])
+ count = count + 2
+ numberofpairs, = struct.unpack("h", data[count:count+2])
+ count = count + 2
+ kerntable = self.kernTables[stylecode] = []
+ for j in range(numberofpairs):
+ firstchar, secondchar, kerndistance = struct.unpack("cch", data[count:count+4])
+ kerntable.append((ord(firstchar), ord(secondchar), kerndistance))
+ count = count + 4
+
+ if DEBUG:
+ self._rawkerningtables = data[offset:count]
+ self.parsedthings.append((offset, count, 'kerningtables'))
+
+ def _buildkerningtables(self):
+ if self.kernTables == {}:
+ self._rawkerningtables = ""
+ self.ffKernOff = 0
+ return
+ numberofentries = len(self.kernTables)
+ data = [struct.pack('h', numberofentries - 1)]
+ tables = self.kernTables.items()
+ tables.sort()
+ for stylecode, table in tables:
+ data.append(struct.pack('h', stylecode))
+ data.append(struct.pack('h', len(table))) # numberofpairs
+ for firstchar, secondchar, kerndistance in table:
+ data.append(struct.pack("cch", chr(firstchar), chr(secondchar), kerndistance))
+
+ data = string.join(data, '')
+
+ if DEBUG:
+ print "kerning table is the same?", self._rawkerningtables == data and 'yes.' or 'no.'
+ if self._rawkerningtables <> data:
+ print len(self._rawkerningtables), len(data)
+ self._rawkerningtables = data
+
+ def _getstylemappingtable(self):
+ offset = self.ffStylOff
+ self.styleStrings = []
+ self.styleIndices = ()
+ self.glyphTableOffset = 0
+ self.fondClass = 0
+ if offset == 0:
+ return
+ data = self.FOND.data
+ self.fondClass, self.glyphTableOffset, self.styleMappingReserved, = \
+ struct.unpack("hll", data[offset:offset+10])
+ self.styleIndices = struct.unpack('48b', data[offset + 10:offset + 58])
+ stringcount, = struct.unpack('h', data[offset+58:offset+60])
+
+ count = offset + 60
+ for i in range(stringcount):
+ str_len = ord(data[count])
+ self.styleStrings.append(data[count + 1:count + 1 + str_len])
+ count = count + 1 + str_len
+
+ self._unpackstylestrings()
+
+ data = data[offset:count]
+ if len(data) % 2:
+ data = data + '\0'
+ if DEBUG:
+ self._rawstylemappingtable = data
+ self.parsedthings.append((offset, count, 'stylemappingtable'))
+
+ def _buildstylemappingtable(self):
+ if not self.styleIndices:
+ self._rawstylemappingtable = ""
+ return
+ data = struct.pack("hll", self.fondClass, self.glyphTableOffset,
+ self.styleMappingReserved)
+
+ self._packstylestrings()
+ data = data + apply(struct.pack, ("48b",) + self.styleIndices)
+
+ stringcount = len(self.styleStrings)
+ data = data + struct.pack("h", stringcount)
+ for string in self.styleStrings:
+ data = data + chr(len(string)) + string
+
+ if len(data) % 2:
+ data = data + '\0'
+
+ if DEBUG:
+ print "style mapping table is the same?", self._rawstylemappingtable == data and 'yes.' or 'no.'
+ self._rawstylemappingtable = data
+
+ def _unpackstylestrings(self):
+ psNames = {}
+ self.ffFamilyName = self.styleStrings[0]
+ for i in self.widthTables.keys():
+ index = self.styleIndices[i]
+ if index == 1:
+ psNames[i] = self.styleStrings[0]
+ else:
+ style = self.styleStrings[0]
+ codes = map(ord, self.styleStrings[index - 1])
+ for code in codes:
+ style = style + self.styleStrings[code - 1]
+ psNames[i] = style
+ self.psNames = psNames
+
+ def _packstylestrings(self):
+ nameparts = {}
+ splitnames = {}
+ for style, name in self.psNames.items():
+ split = splitname(name, self.ffFamilyName)
+ splitnames[style] = split
+ for part in split:
+ nameparts[part] = None
+ del nameparts[self.ffFamilyName]
+ nameparts = nameparts.keys()
+ nameparts.sort()
+ items = splitnames.items()
+ items.sort()
+ numindices = 0
+ for style, split in items:
+ if len(split) > 1:
+ numindices = numindices + 1
+ styleStrings = [self.ffFamilyName] + numindices * [None] + nameparts
+ # XXX the next bit goes wrong for MM fonts.
+ for style, split in items:
+ if len(split) == 1:
+ continue
+ indices = ""
+ for part in split[1:]:
+ indices = indices + chr(nameparts.index(part) + numindices + 2)
+ styleStrings[self.styleIndices[style] - 1] = indices
+ self.styleStrings = styleStrings
+
+ def _updatestylemappingtable(self):
+ # Update the glyphTableOffset field.
+ # This is neccesary since we have to build this table to
+ # know what the glyphTableOffset will be.
+ # And we don't want to build it twice, do we?
+ data = self._rawstylemappingtable
+ if not data:
+ return
+ data = data[:2] + struct.pack("l", self.glyphTableOffset) + data[6:]
+ self._rawstylemappingtable = data
+
+ def _getglyphencodingsubtable(self):
+ glyphEncoding = self.glyphEncoding = {}
+ if not self.glyphTableOffset:
+ return
+ offset = self.ffStylOff + self.glyphTableOffset
+ data = self.FOND.data
+ numberofentries, = struct.unpack("h", data[offset:offset+2])
+ count = offset + 2
+ for i in range(numberofentries):
+ glyphcode = ord(data[count])
+ count = count + 1
+ strlen = ord(data[count])
+ count = count + 1
+ glyphname = data[count:count+strlen]
+ glyphEncoding[glyphcode] = glyphname
+ count = count + strlen
+
+ if DEBUG:
+ self._rawglyphencodingsubtable = data[offset:count]
+ self.parsedthings.append((offset, count, 'glyphencodingsubtable'))
+
+ def _buildglyphencodingsubtable(self):
+ if not self.glyphEncoding:
+ self._rawglyphencodingsubtable = ""
+ return
+ numberofentries = len(self.glyphEncoding)
+ data = struct.pack("h", numberofentries)
+ items = self.glyphEncoding.items()
+ items.sort()
+ for glyphcode, glyphname in items:
+ data = data + chr(glyphcode) + chr(len(glyphname)) + glyphname
+ self._rawglyphencodingsubtable = data
+
+
+uppercase = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+
+def splitname(name, famname = None):
+ # XXX this goofs up MM font names: but how should it be done??
+ if famname:
+ if name[:len(famname)] <> famname:
+ raise error, "first part of name should be same as family name"
+ name = name[len(famname):]
+ split = [famname]
+ else:
+ split = []
+ current = ""
+ for c in name:
+ if c == '-' or c in uppercase:
+ if current:
+ split.append(current)
+ current = ""
+ current = current + c
+ if current:
+ split.append(current)
+ return split
+
+def makeLWFNfilename(name):
+ split = splitname(name)
+ lwfnname = split[0][:5]
+ for part in split[1:]:
+ if part <> '-':
+ lwfnname = lwfnname + part[:3]
+ return lwfnname
+
+class BitmapFontFile:
+
+ def __init__(self, path, mode = 'r'):
+ import macfs
+
+ if mode == 'r':
+ permission = 1 # read only
+ elif mode == 'w':
+ permission = 3 # exclusive r/w
+ else:
+ raise error, 'mode should be either "r" or "w"'
+ self.mode = mode
+ fss = macfs.FSSpec(path)
+ self.resref = Res.FSpOpenResFile(fss, permission)
+ Res.UseResFile(self.resref)
+ self.path = path
+ self.fonds = []
+ self.getFONDs()
+
+ def getFONDs(self):
+ FONDcount = Res.Count1Resources('FOND')
+ for i in range(FONDcount):
+ fond = FontFamily(Res.Get1IndResource('FOND', i + 1), self.mode)
+ self.fonds.append(fond)
+
+ def parse(self):
+ self.fondsbyname = {}
+ for fond in self.fonds:
+ fond.parse()
+ if hasattr(fond, "psNames") and fond.psNames:
+ psNames = fond.psNames.values()
+ psNames.sort()
+ self.fondsbyname[psNames[0]] = fond
+
+ def minimalparse(self):
+ for fond in self.fonds:
+ fond.minimalparse()
+
+ def close(self):
+ if self.resref <> None:
+ try:
+ Res.CloseResFile(self.resref)
+ except Res.Error:
+ pass
+ self.resref = None
+
+
+class FondSelector:
+
+ def __init__(self, fondlist):
+ import W
+ if not fondlist:
+ raise ValueError, "expected at least one FOND entry"
+ if len(fondlist) == 1:
+ self.choice = 0
+ return
+ fonds = []
+ for fond in fondlist:
+ fonds.append(fond.name)
+ self.w = W.ModalDialog((200, 200), "aaa")
+ self.w.donebutton = W.Button((-70, -26, 60, 16), "Done", self.close)
+ self.w.l = W.List((10, 10, -10, -36), fonds, self.listhit)
+ self.w.setdefaultbutton(self.w.donebutton)
+ self.w.l.setselection([0])
+ self.w.open()
+
+ def close(self):
+ self.checksel()
+ sel = self.w.l.getselection()
+ self.choice = sel[0]
+ self.w.close()
+
+ def listhit(self, isDbl):
+ if isDbl:
+ self.w.donebutton.push()
+ else:
+ self.checksel()
+
+ def checksel(self):
+ sel = self.w.l.getselection()
+ if not sel:
+ self.w.l.setselection([0])
+ elif len(sel) <> 1:
+ self.w.l.setselection([sel[0]])
+
diff --git a/Lib/fontTools/misc/__init__.py b/Lib/fontTools/misc/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Lib/fontTools/misc/__init__.py
diff --git a/Lib/fontTools/misc/textTools.py b/Lib/fontTools/misc/textTools.py
new file mode 100644
index 0000000..304f45a
--- /dev/null
+++ b/Lib/fontTools/misc/textTools.py
@@ -0,0 +1,89 @@
+"""fontTools.misc.textTools.py -- miscelaneous routines."""
+
+
+import string
+
+
+def safeEval(data, eval=eval):
+ """A safe replacement for eval."""
+ return eval(data, {"__builtins__":{}}, {})
+
+
+def readHex(content):
+ """Convert a list of hex strings to binary data."""
+ hexdata = ""
+ for chunk in content:
+ if type(chunk) == type(""):
+ hexdata = hexdata + chunk
+ return deHexStr(hexdata)
+
+def deHexStr(hexdata):
+ """Convert a hex string to binary data."""
+ parts = string.split(hexdata)
+ hexdata = string.join(parts, "")
+ if len(hexdata) % 2:
+ hexdata = hexdata + "0"
+ data = ""
+ for i in range(0, len(hexdata), 2):
+ data = data + chr(string.atoi(hexdata[i:i+2], 16))
+ return data
+
+def hexStr(data):
+ """Convert binary data to a hex string."""
+ h = string.hexdigits
+ r = ''
+ for c in data:
+ i = ord(c)
+ r = r + h[(i >> 4) & 0xF] + h[i & 0xF]
+ return r
+
+
+def num2binary(l, bits=32):
+ all = []
+ bin = ""
+ for i in range(bits):
+ if l & 0x1:
+ bin = "1" + bin
+ else:
+ bin = "0" + bin
+ l = l >> 1
+ if not ((i+1) % 8):
+ all.append(bin)
+ bin = ""
+ all.reverse()
+ assert l in (0, -1), "number doesn't fit in number of bits"
+ return string.join(all, " ")
+
+
+def binary2num(bin):
+ bin = string.join(string.split(bin), "")
+ l = 0
+ for digit in bin:
+ l = l << 1
+ if digit <> "0":
+ l = l | 0x1
+ return l
+
+
+def caselessSort(alist):
+ """Return a sorted copy of a list. If there are only strings
+ in the list, it will not consider case.
+ """
+
+ try:
+ # turn ['FOO', 'aaBc', 'ABcD'] into
+ # [('foo', 'FOO'), ('aabc', 'aaBc'), ('abcd', 'ABcD')],
+ # but only if all elements are strings
+ tupledlist = map(lambda item, lower = string.lower:
+ (lower(item), item), alist)
+ except TypeError:
+ # at least one element in alist is not a string, proceed the normal way...
+ alist = alist[:]
+ alist.sort()
+ return alist
+ else:
+ tupledlist.sort()
+ # turn [('aabc', 'aaBc'), ('abcd', 'ABcD'), ('foo', 'FOO')] into
+ # ['aaBc', 'ABcD', 'FOO']
+ return map(lambda x: x[1], tupledlist)
+
diff --git a/Lib/fontTools/nfntLib.py b/Lib/fontTools/nfntLib.py
new file mode 100644
index 0000000..08a54ba
--- /dev/null
+++ b/Lib/fontTools/nfntLib.py
@@ -0,0 +1,144 @@
+import Res
+import macfs
+import struct
+import Qd
+from types import *
+
+
+class NFNT:
+
+ def __init__(self, nfnt, name = "", _type = 'NFNT'):
+ if type(nfnt) == type(Res.Resource("")):
+ theID, theType, name = nfnt.GetResInfo()
+ if theType <> _type:
+ raise TypeError, 'resource of wrong type; expected ' + _type
+ data = nfnt.data
+ elif type(nfnt) == StringType:
+ fss = macfs.FSSpec(nfnt)
+ data = readnfntresource(nfnt, name, _type)
+ elif type(nfnt) == type(macfs.FSSpec(':')):
+ data = readnfntresource(nfnt, name, _type)
+ else:
+ raise TypeError, 'expected resource, string or fss; found ' + type(nfnt).__name__
+ self.parse_nfnt(data)
+
+ def parse_nfnt(self, data):
+ # header; FontRec
+ ( self.fontType,
+ self.firstChar,
+ self.lastChar,
+ self.widMax,
+ self.kernMax,
+ self.nDescent,
+ fRectWidth,
+ self.fRectHeight,
+ owTLoc,
+ self.ascent,
+ self.descent,
+ self.leading,
+ self.rowWords ) = struct.unpack("13h", data[:26])
+ if owTLoc < 0:
+ owTLoc = owTLoc + 0x8000 # unsigned short
+
+ # rest
+ tablesize = 2 * (self.lastChar - self.firstChar + 3)
+ bitmapsize = 2 * self.rowWords * self.fRectHeight
+
+ self.bits = data[26:26 + bitmapsize]
+ self.bitImage = Qd.BitMap(self.bits, 2 * self.rowWords, (0, 0, self.rowWords * 16, self.fRectHeight))
+
+ owTable = data[26 + bitmapsize + tablesize:26 + bitmapsize + 2 * tablesize]
+ if len(owTable) <> tablesize:
+ raise ValueError, 'invalid NFNT resource'
+
+ locTable = data[26 + bitmapsize:26 + bitmapsize + tablesize]
+ if len(locTable) <> tablesize:
+ raise ValueError, 'invalid NFNT resource'
+
+ # fill tables
+ self.offsettable = []
+ self.widthtable = []
+ self.locationtable = []
+ for i in range(0, tablesize, 2):
+ self.offsettable.append(ord(owTable[i]))
+ self.widthtable.append(ord(owTable[i+1]))
+ loc, = struct.unpack('h', locTable[i:i+2])
+ self.locationtable.append(loc)
+
+ def drawstring(self, astring, destbits, xoffset = 0, yoffset = 0):
+ drawchar = self.drawchar
+ for ch in astring:
+ xoffset = drawchar(ch, destbits, xoffset, yoffset)
+ return xoffset
+
+ def drawchar(self, ch, destbits, xoffset, yoffset = 0):
+ width, bounds, destbounds = self.getcharbounds(ch)
+ destbounds = Qd.OffsetRect(destbounds, xoffset, yoffset)
+ Qd.CopyBits(self.bitImage, destbits, bounds, destbounds, 1, None)
+ return xoffset + width
+
+ def stringwidth(self, astring):
+ charwidth = self.charwidth
+ width = 0
+ for ch in astring:
+ width = width + charwidth(ch)
+ return width
+
+ def charwidth(self, ch):
+ cindex = ord(ch) - self.firstChar
+ if cindex > self.lastChar or \
+ (self.offsettable[cindex] == 255 and self.widthtable[cindex] == 255):
+ cindex = -2 # missing char
+ return self.widthtable[cindex]
+
+ def getcharbounds(self, ch):
+ cindex = ord(ch) - self.firstChar
+ if cindex > self.lastChar or \
+ (self.offsettable[cindex] == 255 and self.widthtable[cindex] == 255):
+ return self.getcharboundsindex(-2) # missing char
+ return self.getcharboundsindex(cindex)
+
+ def getcharboundsindex(self, cindex):
+ offset = self.offsettable[cindex]
+ width = self.widthtable[cindex]
+ if offset == 255 and width == 255:
+ raise ValueError, "character not defined"
+ location0 = self.locationtable[cindex]
+ location1 = self.locationtable[cindex + 1]
+ srcbounds = (location0, 0, location1, self.fRectHeight)
+ destbounds = ( offset + self.kernMax,
+ 0,
+ offset + self.kernMax + location1 - location0,
+ self.fRectHeight )
+ return width, srcbounds, destbounds
+
+
+def readnfntresource(fss, name, _type = 'NFNT'):
+ resref = Res.FSpOpenResFile(fss, 1) # readonly
+ Res.UseResFile(resref)
+ try:
+ if name:
+ res = Res.Get1NamedResource(_type, name)
+ else:
+ # just take the first in the file
+ res = Res.Get1IndResource(_type, 1)
+ theID, theType, name = res.GetResInfo()
+ if theType <> _type:
+ raise TypeError, 'resource of wrong type; expected ' + _type
+ data = res.data
+ finally:
+ Res.CloseResFile(resref)
+ return data
+
+
+if 0:
+ import Win
+ fss, ok = macfs.StandardGetFile('FFIL')
+ if ok:
+ n = NFNT(fss)
+ s = "!!!ABCDEFGHIJKLMN01234 hemeltje lief...x.."
+ x = 10
+ y = 40
+ destbits = Win.FrontWindow().GetWindowPort().portBits
+ n.drawstring(s, destbits, x, y)
+ print n.stringwidth(s)
diff --git a/Lib/fontTools/psCharStrings.py b/Lib/fontTools/psCharStrings.py
new file mode 100644
index 0000000..d618784
--- /dev/null
+++ b/Lib/fontTools/psCharStrings.py
@@ -0,0 +1,974 @@
+"""psCharStrings.py -- module implementing various kinds of CharStrings:
+CFF dictionary data and Type1/Type2 CharStrings.
+"""
+
+__version__ = "1.0b1"
+__author__ = "jvr"
+
+
+import types
+import struct
+import string
+
+
+t1OperandEncoding = [None] * 256
+t1OperandEncoding[0:32] = (32) * ["do_operator"]
+t1OperandEncoding[32:247] = (247 - 32) * ["read_byte"]
+t1OperandEncoding[247:251] = (251 - 247) * ["read_smallInt1"]
+t1OperandEncoding[251:255] = (255 - 251) * ["read_smallInt2"]
+t1OperandEncoding[255] = "read_longInt"
+assert len(t1OperandEncoding) == 256
+
+t2OperandEncoding = t1OperandEncoding[:]
+t2OperandEncoding[28] = "read_shortInt"
+
+cffDictOperandEncoding = t2OperandEncoding[:]
+cffDictOperandEncoding[29] = "read_longInt"
+cffDictOperandEncoding[30] = "read_realNumber"
+cffDictOperandEncoding[255] = "reserved"
+
+
+realNibbles = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ '.', 'E', 'E-', None, '-']
+
+
+class ByteCodeDecompilerBase:
+
+ def read_byte(self, b0, data, index):
+ return b0 - 139, index
+
+ def read_smallInt1(self, b0, data, index):
+ b1 = ord(data[index])
+ return (b0-247)*256 + b1 + 108, index+1
+
+ def read_smallInt2(self, b0, data, index):
+ b1 = ord(data[index])
+ return -(b0-251)*256 - b1 - 108, index+1
+
+ def read_shortInt(self, b0, data, index):
+ bin = data[index] + data[index+1]
+ value, = struct.unpack(">h", bin)
+ return value, index+2
+
+ def read_longInt(self, b0, data, index):
+ bin = data[index] + data[index+1] + data[index+2] + data[index+3]
+ value, = struct.unpack(">l", bin)
+ return value, index+4
+
+ def read_realNumber(self, b0, data, index):
+ number = ''
+ while 1:
+ b = ord(data[index])
+ index = index + 1
+ nibble0 = (b & 0xf0) >> 4
+ nibble1 = b & 0x0f
+ if nibble0 == 0xf:
+ break
+ number = number + realNibbles[nibble0]
+ if nibble1 == 0xf:
+ break
+ number = number + realNibbles[nibble1]
+ return string.atof(number), index
+
+
+def _buildOperatorDict(operatorList):
+ dict = {}
+ for item in operatorList:
+ if len(item) == 2:
+ dict[item[0]] = item[1]
+ else:
+ dict[item[0]] = item[1:]
+ return dict
+
+
+t2Operators = [
+# opcode name
+ (1, 'hstem'),
+ (3, 'vstem'),
+ (4, 'vmoveto'),
+ (5, 'rlineto'),
+ (6, 'hlineto'),
+ (7, 'vlineto'),
+ (8, 'rrcurveto'),
+ (10, 'callsubr'),
+ (11, 'return'),
+ (14, 'endchar'),
+ (16, 'blend'),
+ (18, 'hstemhm'),
+ (19, 'hintmask'),
+ (20, 'cntrmask'),
+ (21, 'rmoveto'),
+ (22, 'hmoveto'),
+ (23, 'vstemhm'),
+ (24, 'rcurveline'),
+ (25, 'rlinecurve'),
+ (26, 'vvcurveto'),
+ (27, 'hhcurveto'),
+# (28, 'shortint'), # not really an operator
+ (29, 'callgsubr'),
+ (30, 'vhcurveto'),
+ (31, 'hvcurveto'),
+ ((12, 3), 'and'),
+ ((12, 4), 'or'),
+ ((12, 5), 'not'),
+ ((12, 8), 'store'),
+ ((12, 9), 'abs'),
+ ((12, 10), 'add'),
+ ((12, 11), 'sub'),
+ ((12, 12), 'div'),
+ ((12, 13), 'load'),
+ ((12, 14), 'neg'),
+ ((12, 15), 'eq'),
+ ((12, 18), 'drop'),
+ ((12, 20), 'put'),
+ ((12, 21), 'get'),
+ ((12, 22), 'ifelse'),
+ ((12, 23), 'random'),
+ ((12, 24), 'mul'),
+ ((12, 26), 'sqrt'),
+ ((12, 27), 'dup'),
+ ((12, 28), 'exch'),
+ ((12, 29), 'index'),
+ ((12, 30), 'roll'),
+ ((12, 34), 'hflex'),
+ ((12, 35), 'flex'),
+ ((12, 36), 'hflex1'),
+ ((12, 37), 'flex1'),
+]
+
+class T2CharString(ByteCodeDecompilerBase):
+
+ operandEncoding = t2OperandEncoding
+ operators = _buildOperatorDict(t2Operators)
+
+ def __init__(self, bytecode=None, program=None):
+ if program is None:
+ program = []
+ self.bytecode = bytecode
+ self.program = program
+
+ def __repr__(self):
+ if self.bytecode is None:
+ return "<%s (source) at %x>" % (self.__class__.__name__, id(self))
+ else:
+ return "<%s (bytecode) at %x>" % (self.__class__.__name__, id(self))
+
+ def needsDecompilation(self):
+ return self.bytecode is not None
+
+ def setProgram(self, program):
+ self.program = program
+ self.bytecode = None
+
+ def getToken(self, index,
+ len=len, ord=ord, getattr=getattr, type=type, StringType=types.StringType):
+ if self.bytecode is not None:
+ if index >= len(self.bytecode):
+ return None, 0, 0
+ b0 = ord(self.bytecode[index])
+ index = index + 1
+ code = self.operandEncoding[b0]
+ handler = getattr(self, code)
+ token, index = handler(b0, self.bytecode, index)
+ else:
+ if index >= len(self.program):
+ return None, 0, 0
+ token = self.program[index]
+ index = index + 1
+ isOperator = type(token) == StringType
+ return token, isOperator, index
+
+ def getBytes(self, index, nBytes):
+ if self.bytecode is not None:
+ newIndex = index + nBytes
+ bytes = self.bytecode[index:newIndex]
+ index = newIndex
+ else:
+ bytes = self.program[index]
+ index = index + 1
+ assert len(bytes) == nBytes
+ return bytes, index
+
+ def do_operator(self, b0, data, index):
+ if b0 == 12:
+ op = (b0, ord(data[index]))
+ index = index+1
+ else:
+ op = b0
+ operator = self.operators[op]
+ return operator, index
+
+ def toXML(self, xmlWriter):
+ from misc.textTools import num2binary
+ if self.bytecode is not None:
+ xmlWriter.dumphex(self.bytecode)
+ else:
+ index = 0
+ args = []
+ while 1:
+ token, isOperator, index = self.getToken(index)
+ if token is None:
+ break
+ if isOperator:
+ args = map(str, args)
+ if token in ('hintmask', 'cntrmask'):
+ hintMask, isOperator, index = self.getToken(index)
+ bits = []
+ for byte in hintMask:
+ bits.append(num2binary(ord(byte), 8))
+ hintMask = repr(string.join(bits, ""))
+ line = string.join(args + [token, hintMask], " ")
+ else:
+ line = string.join(args + [token], " ")
+ xmlWriter.write(line)
+ xmlWriter.newline()
+ args = []
+ else:
+ args.append(token)
+
+
+t1Operators = [
+# opcode name
+ (1, 'hstem'),
+ (3, 'vstem'),
+ (4, 'vmoveto'),
+ (5, 'rlineto'),
+ (6, 'hlineto'),
+ (7, 'vlineto'),
+ (8, 'rrcurveto'),
+ (9, 'closepath'),
+ (10, 'callsubr'),
+ (11, 'return'),
+ (13, 'hsbw'),
+ (14, 'endchar'),
+ (21, 'rmoveto'),
+ (22, 'hmoveto'),
+ (30, 'vhcurveto'),
+ (31, 'hvcurveto'),
+ ((12, 0), 'dotsection'),
+ ((12, 1), 'vstem3'),
+ ((12, 2), 'hstem3'),
+ ((12, 6), 'seac'),
+ ((12, 7), 'sbw'),
+ ((12, 12), 'div'),
+ ((12, 16), 'callothersubr'),
+ ((12, 17), 'pop'),
+ ((12, 33), 'setcurrentpoint'),
+]
+
+class T1CharString(T2CharString):
+
+ operandEncoding = t1OperandEncoding
+ operators = _buildOperatorDict(t1Operators)
+
+ def decompile(self):
+ if hasattr(self, "program"):
+ return
+ program = []
+ index = 0
+ while 1:
+ token, isOperator, index = self.getToken(index)
+ if token is None:
+ break
+ program.append(token)
+ self.setProgram(program)
+
+
+class SimpleT2Decompiler:
+
+ def __init__(self, localSubrs, globalSubrs):
+ self.localSubrs = localSubrs
+ self.localBias = calcSubrBias(localSubrs)
+ self.globalSubrs = globalSubrs
+ self.globalBias = calcSubrBias(globalSubrs)
+ self.reset()
+
+ def reset(self):
+ self.callingStack = []
+ self.operandStack = []
+ self.hintCount = 0
+ self.hintMaskBytes = 0
+
+ def execute(self, charString):
+ self.callingStack.append(charString)
+ needsDecompilation = charString.needsDecompilation()
+ if needsDecompilation:
+ program = []
+ pushToProgram = program.append
+ else:
+ pushToProgram = lambda x: None
+ pushToStack = self.operandStack.append
+ index = 0
+ while 1:
+ token, isOperator, index = charString.getToken(index)
+ if token is None:
+ break # we're done!
+ pushToProgram(token)
+ if isOperator:
+ handlerName = "op_" + token
+ if hasattr(self, handlerName):
+ handler = getattr(self, handlerName)
+ rv = handler(index)
+ if rv:
+ hintMaskBytes, index = rv
+ pushToProgram(hintMaskBytes)
+ else:
+ self.popall()
+ else:
+ pushToStack(token)
+ if needsDecompilation:
+ charString.setProgram(program)
+ assert program[-1] in ("endchar", "return", "callsubr", "callgsubr", "seac")
+ del self.callingStack[-1]
+
+ def pop(self):
+ value = self.operandStack[-1]
+ del self.operandStack[-1]
+ return value
+
+ def popall(self):
+ stack = self.operandStack[:]
+ self.operandStack[:] = []
+ return stack
+
+ def push(self, value):
+ self.operandStack.append(value)
+
+ def op_return(self, index):
+ if self.operandStack:
+ pass
+
+ def op_endchar(self, index):
+ pass
+
+ def op_callsubr(self, index):
+ subrIndex = self.pop()
+ subr = self.localSubrs[subrIndex+self.localBias]
+ self.execute(subr)
+
+ def op_callgsubr(self, index):
+ subrIndex = self.pop()
+ subr = self.globalSubrs[subrIndex+self.globalBias]
+ self.execute(subr)
+
+ def op_hstemhm(self, index):
+ self.countHints()
+
+ op_vstemhm = op_hstemhm
+
+ def op_hintmask(self, index):
+ if not self.hintMaskBytes:
+ self.countHints()
+ self.hintMaskBytes = (self.hintCount + 7) / 8
+ hintMaskBytes, index = self.callingStack[-1].getBytes(index, self.hintMaskBytes)
+ return hintMaskBytes, index
+
+ op_cntrmask = op_hintmask
+
+ def countHints(self):
+ assert self.hintMaskBytes == 0
+ args = self.popall()
+ self.hintCount = self.hintCount + len(args) / 2
+
+
+class T2OutlineExtractor(SimpleT2Decompiler):
+
+ def __init__(self, localSubrs, globalSubrs, nominalWidthX, defaultWidthX):
+ SimpleT2Decompiler.__init__(self, localSubrs, globalSubrs)
+ self.nominalWidthX = nominalWidthX
+ self.defaultWidthX = defaultWidthX
+
+ def reset(self):
+ import Numeric
+ SimpleT2Decompiler.reset(self)
+ self.hints = []
+ self.gotWidth = 0
+ self.width = 0
+ self.currentPoint = Numeric.array((0, 0), Numeric.Int16)
+ self.contours = []
+
+ def getContours(self):
+ return self.contours
+
+ def newPath(self):
+ self.contours.append([[], [], 0])
+
+ def closePath(self):
+ if self.contours and self.contours[-1][2] == 0:
+ self.contours[-1][2] = 1
+
+ def appendPoint(self, point, isPrimary):
+ import Numeric
+ point = self.currentPoint + Numeric.array(point, Numeric.Int16)
+ self.currentPoint = point
+ points, flags, isClosed = self.contours[-1]
+ points.append(point)
+ flags.append(isPrimary)
+
+ def popallWidth(self, evenOdd=0):
+ args = self.popall()
+ if not self.gotWidth:
+ if evenOdd ^ (len(args) % 2):
+ self.width = self.nominalWidthX + args[0]
+ args = args[1:]
+ else:
+ self.width = self.defaultWidthX
+ self.gotWidth = 1
+ return args
+
+ def countHints(self):
+ assert self.hintMaskBytes == 0
+ args = self.popallWidth()
+ self.hintCount = self.hintCount + len(args) / 2
+
+ #
+ # hint operators
+ #
+ def op_hstem(self, index):
+ self.popallWidth() # XXX
+ def op_vstem(self, index):
+ self.popallWidth() # XXX
+ def op_hstemhm(self, index):
+ self.countHints()
+ #XXX
+ def op_vstemhm(self, index):
+ self.countHints()
+ #XXX
+ #def op_hintmask(self, index):
+ # self.countHints()
+ #def op_cntrmask(self, index):
+ # self.countHints()
+
+ #
+ # path constructors, moveto
+ #
+ def op_rmoveto(self, index):
+ self.closePath()
+ self.newPath()
+ self.appendPoint(self.popallWidth(), 1)
+ def op_hmoveto(self, index):
+ self.closePath()
+ self.newPath()
+ self.appendPoint((self.popallWidth(1)[0], 0), 1)
+ def op_vmoveto(self, index):
+ self.closePath()
+ self.newPath()
+ self.appendPoint((0, self.popallWidth(1)[0]), 1)
+ def op_endchar(self, index):
+ self.closePath()
+
+ #
+ # path constructors, lines
+ #
+ def op_rlineto(self, index):
+ args = self.popall()
+ for i in range(0, len(args), 2):
+ point = args[i:i+2]
+ self.appendPoint(point, 1)
+
+ def op_hlineto(self, index):
+ self.alternatingLineto(1)
+ def op_vlineto(self, index):
+ self.alternatingLineto(0)
+
+ #
+ # path constructors, curves
+ #
+ def op_rrcurveto(self, index):
+ """{dxa dya dxb dyb dxc dyc}+ rrcurveto"""
+ args = self.popall()
+ for i in range(0, len(args), 6):
+ dxa, dya, dxb, dyb, dxc, dyc, = args[i:i+6]
+ self.rrcurveto((dxa, dya), (dxb, dyb), (dxc, dyc))
+
+ def op_rcurveline(self, index):
+ """{dxa dya dxb dyb dxc dyc}+ dxd dyd rcurveline"""
+ args = self.popall()
+ for i in range(0, len(args)-2, 6):
+ dxb, dyb, dxc, dyc, dxd, dyd = args[i:i+6]
+ self.rrcurveto((dxb, dyb), (dxc, dyc), (dxd, dyd))
+ self.appendPoint(args[-2:], 1)
+
+ def op_rlinecurve(self, index):
+ """{dxa dya}+ dxb dyb dxc dyc dxd dyd rlinecurve"""
+ args = self.popall()
+ lineArgs = args[:-6]
+ for i in range(0, len(lineArgs), 2):
+ self.appendPoint(lineArgs[i:i+2], 1)
+ dxb, dyb, dxc, dyc, dxd, dyd = args[-6:]
+ self.rrcurveto((dxb, dyb), (dxc, dyc), (dxd, dyd))
+
+ def op_vvcurveto(self, index):
+ "dx1? {dya dxb dyb dyc}+ vvcurveto"
+ args = self.popall()
+ if len(args) % 2:
+ dx1 = args[0]
+ args = args[1:]
+ else:
+ dx1 = 0
+ for i in range(0, len(args), 4):
+ dya, dxb, dyb, dyc = args[i:i+4]
+ self.rrcurveto((dx1, dya), (dxb, dyb), (0, dyc))
+ dx1 = 0
+
+ def op_hhcurveto(self, index):
+ """dy1? {dxa dxb dyb dxc}+ hhcurveto"""
+ args = self.popall()
+ if len(args) % 2:
+ dy1 = args[0]
+ args = args[1:]
+ else:
+ dy1 = 0
+ for i in range(0, len(args), 4):
+ dxa, dxb, dyb, dxc = args[i:i+4]
+ self.rrcurveto((dxa, dy1), (dxb, dyb), (dxc, 0))
+ dy1 = 0
+
+ def op_vhcurveto(self, index):
+ """dy1 dx2 dy2 dx3 {dxa dxb dyb dyc dyd dxe dye dxf}* dyf? vhcurveto (30)
+ {dya dxb dyb dxc dxd dxe dye dyf}+ dxf? vhcurveto
+ """
+ args = self.popall()
+ while args:
+ args = self.vcurveto(args)
+ if args:
+ args = self.hcurveto(args)
+
+ def op_hvcurveto(self, index):
+ """dx1 dx2 dy2 dy3 {dya dxb dyb dxc dxd dxe dye dyf}* dxf?
+ {dxa dxb dyb dyc dyd dxe dye dxf}+ dyf?
+ """
+ args = self.popall()
+ while args:
+ args = self.hcurveto(args)
+ if args:
+ args = self.vcurveto(args)
+
+ #
+ # path constructors, flex
+ #
+ def op_hflex(self, index):
+ XXX
+ def op_flex(self, index):
+ XXX
+ def op_hflex1(self, index):
+ XXX
+ def op_flex1(self, index):
+ XXX
+
+ #
+ # MultipleMaster. Well...
+ #
+ def op_blend(self, index):
+ XXX
+
+ # misc
+ def op_and(self, index):
+ XXX
+ def op_or(self, index):
+ XXX
+ def op_not(self, index):
+ XXX
+ def op_store(self, index):
+ XXX
+ def op_abs(self, index):
+ XXX
+ def op_add(self, index):
+ XXX
+ def op_sub(self, index):
+ XXX
+ def op_div(self, index):
+ num2 = self.pop()
+ num1 = self.pop()
+ d1 = num1/num2
+ d2 = float(num1)/num2
+ if d1 == d2:
+ self.push(d1)
+ else:
+ self.push(d2)
+ def op_load(self, index):
+ XXX
+ def op_neg(self, index):
+ XXX
+ def op_eq(self, index):
+ XXX
+ def op_drop(self, index):
+ XXX
+ def op_put(self, index):
+ XXX
+ def op_get(self, index):
+ XXX
+ def op_ifelse(self, index):
+ XXX
+ def op_random(self, index):
+ XXX
+ def op_mul(self, index):
+ XXX
+ def op_sqrt(self, index):
+ XXX
+ def op_dup(self, index):
+ XXX
+ def op_exch(self, index):
+ XXX
+ def op_index(self, index):
+ XXX
+ def op_roll(self, index):
+ XXX
+
+ #
+ # miscelaneous helpers
+ #
+ def alternatingLineto(self, isHorizontal):
+ args = self.popall()
+ for arg in args:
+ if isHorizontal:
+ point = (arg, 0)
+ else:
+ point = (0, arg)
+ self.appendPoint(point, 1)
+ isHorizontal = not isHorizontal
+
+ def rrcurveto(self, p1, p2, p3):
+ self.appendPoint(p1, 0)
+ self.appendPoint(p2, 0)
+ self.appendPoint(p3, 1)
+
+ def vcurveto(self, args):
+ dya, dxb, dyb, dxc = args[:4]
+ args = args[4:]
+ if len(args) == 1:
+ dyc = args[0]
+ args = []
+ else:
+ dyc = 0
+ self.rrcurveto((0, dya), (dxb, dyb), (dxc, dyc))
+ return args
+
+ def hcurveto(self, args):
+ dxa, dxb, dyb, dyc = args[:4]
+ args = args[4:]
+ if len(args) == 1:
+ dxc = args[0]
+ args = []
+ else:
+ dxc = 0
+ self.rrcurveto((dxa, 0), (dxb, dyb), (dxc, dyc))
+ return args
+
+
+class T1OutlineExtractor(T2OutlineExtractor):
+
+ def __init__(self, subrs):
+ self.subrs = subrs
+ self.reset()
+
+ def reset(self):
+ self.flexing = 0
+ self.width = 0
+ self.sbx = 0
+ T2OutlineExtractor.reset(self)
+
+ def popallWidth(self, evenOdd=0):
+ return self.popall()
+
+ def exch(self):
+ stack = self.operandStack
+ stack[-1], stack[-2] = stack[-2], stack[-1]
+
+ #
+ # path constructors
+ #
+ def op_rmoveto(self, index):
+ if self.flexing:
+ return
+ self.newPath()
+ self.appendPoint(self.popall(), 1)
+ def op_hmoveto(self, index):
+ if self.flexing:
+ # We must add a parameter to the stack if we are flexing
+ self.push(0)
+ return
+ self.newPath()
+ self.appendPoint((self.popall()[0], 0), 1)
+ def op_vmoveto(self, index):
+ if self.flexing:
+ # We must add a parameter to the stack if we are flexing
+ self.push(0)
+ self.exch()
+ return
+ self.newPath()
+ self.appendPoint((0, self.popall()[0]), 1)
+ def op_closepath(self, index):
+ self.closePath()
+ def op_setcurrentpoint(self, index):
+ args = self.popall()
+ x, y = args
+ self.currentPoint[0] = x
+ self.currentPoint[1] = y
+
+ def op_endchar(self, index):
+ self.closePath()
+
+ def op_hsbw(self, index):
+ sbx, wx = self.popall()
+ self.width = wx
+ self.sbx = sbx
+ self.currentPoint[0] = sbx
+ def op_sbw(self, index):
+ self.popall() # XXX
+
+ #
+ def op_callsubr(self, index):
+ subrIndex = self.pop()
+ subr = self.subrs[subrIndex]
+ self.execute(subr)
+ def op_callothersubr(self, index):
+ subrIndex = self.pop()
+ nArgs = self.pop()
+ #print nArgs, subrIndex, "callothersubr"
+ if subrIndex == 0 and nArgs == 3:
+ self.doFlex()
+ self.flexing = 0
+ elif subrIndex == 1 and nArgs == 0:
+ self.flexing = 1
+ # ignore...
+ def op_pop(self, index):
+ pass # ignore...
+
+ def doFlex(self):
+ finaly = self.pop()
+ finalx = self.pop()
+ self.pop() # flex height is unused
+
+ p3y = self.pop()
+ p3x = self.pop()
+ bcp4y = self.pop()
+ bcp4x = self.pop()
+ bcp3y = self.pop()
+ bcp3x = self.pop()
+ p2y = self.pop()
+ p2x = self.pop()
+ bcp2y = self.pop()
+ bcp2x = self.pop()
+ bcp1y = self.pop()
+ bcp1x = self.pop()
+ rpy = self.pop()
+ rpx = self.pop()
+
+ # call rrcurveto
+ self.push(bcp1x+rpx)
+ self.push(bcp1y+rpy)
+ self.push(bcp2x)
+ self.push(bcp2y)
+ self.push(p2x)
+ self.push(p2y)
+ self.op_rrcurveto(None)
+
+ # call rrcurveto
+ self.push(bcp3x)
+ self.push(bcp3y)
+ self.push(bcp4x)
+ self.push(bcp4y)
+ self.push(p3x)
+ self.push(p3y)
+ self.op_rrcurveto(None)
+
+ # Push back final coords so subr 0 can find them
+ self.push(finalx)
+ self.push(finaly)
+
+ def op_dotsection(self, index):
+ self.popall() # XXX
+ def op_hstem3(self, index):
+ self.popall() # XXX
+ def op_seac(self, index):
+ "asb adx ady bchar achar seac"
+ asb, adx, ady, bchar, achar = self.popall() # XXX
+ self.contours.append([(asb, adx, ady, bchar, achar), None, -1])
+ def op_vstem3(self, index):
+ self.popall() # XXX
+
+
+class DictDecompiler(ByteCodeDecompilerBase):
+
+ operandEncoding = cffDictOperandEncoding
+ dictDefaults = {}
+
+ def __init__(self, strings):
+ self.stack = []
+ self.strings = strings
+ self.dict = {}
+
+ def getDict(self):
+ assert len(self.stack) == 0, "non-empty stack"
+ return self.dict
+
+ def decompile(self, data):
+ index = 0
+ lenData = len(data)
+ push = self.stack.append
+ while index < lenData:
+ b0 = ord(data[index])
+ index = index + 1
+ code = self.operandEncoding[b0]
+ handler = getattr(self, code)
+ value, index = handler(b0, data, index)
+ if value is not None:
+ push(value)
+
+ def pop(self):
+ value = self.stack[-1]
+ del self.stack[-1]
+ return value
+
+ def popall(self):
+ all = self.stack[:]
+ del self.stack[:]
+ return all
+
+ def do_operator(self, b0, data, index):
+ if b0 == 12:
+ op = (b0, ord(data[index]))
+ index = index+1
+ else:
+ op = b0
+ operator, argType = self.operators[op]
+ self.handle_operator(operator, argType)
+ return None, index
+
+ def handle_operator(self, operator, argType):
+ if type(argType) == type(()):
+ value = ()
+ for arg in argType:
+ arghandler = getattr(self, "arg_" + arg)
+ value = (arghandler(operator),) + value
+ else:
+ arghandler = getattr(self, "arg_" + argType)
+ value = arghandler(operator)
+ self.dict[operator] = value
+
+ def arg_number(self, name):
+ return self.pop()
+ def arg_SID(self, name):
+ return self.strings[self.pop()]
+ def arg_array(self, name):
+ return self.popall()
+
+
+topDictOperators = [
+# opcode name argument type
+ (0, 'version', 'SID'),
+ (1, 'Notice', 'SID'),
+ (2, 'FullName', 'SID'),
+ (3, 'FamilyName', 'SID'),
+ (4, 'Weight', 'SID'),
+ (5, 'FontBBox', 'array'),
+ (13, 'UniqueID', 'number'),
+ (14, 'XUID', 'array'),
+ (15, 'charset', 'number'),
+ (16, 'Encoding', 'number'),
+ (17, 'CharStrings', 'number'),
+ (18, 'Private', ('number', 'number')),
+ ((12, 0), 'Copyright', 'SID'),
+ ((12, 1), 'isFixedPitch', 'number'),
+ ((12, 2), 'ItalicAngle', 'number'),
+ ((12, 3), 'UnderlinePosition', 'number'),
+ ((12, 4), 'UnderlineThickness', 'number'),
+ ((12, 5), 'PaintType', 'number'),
+ ((12, 6), 'CharstringType', 'number'),
+ ((12, 7), 'FontMatrix', 'array'),
+ ((12, 8), 'StrokeWidth', 'number'),
+ ((12, 20), 'SyntheticBase', 'number'),
+ ((12, 21), 'PostScript', 'SID'),
+ ((12, 22), 'BaseFontName', 'SID'),
+ # CID additions
+ ((12, 30), 'ROS', ('SID', 'SID', 'number')),
+ ((12, 31), 'CIDFontVersion', 'number'),
+ ((12, 32), 'CIDFontRevision', 'number'),
+ ((12, 33), 'CIDFontType', 'number'),
+ ((12, 34), 'CIDCount', 'number'),
+ ((12, 35), 'UIDBase', 'number'),
+ ((12, 36), 'FDArray', 'number'),
+ ((12, 37), 'FDSelect', 'number'),
+ ((12, 38), 'FontName', 'SID'),
+ # MM, Chameleon. Pft.
+]
+
+topDictDefaults = {
+ 'isFixedPitch': 0,
+ 'ItalicAngle': 0,
+ 'UnderlineThickness': 50,
+ 'PaintType': 0,
+ 'CharstringType': 2,
+ 'FontMatrix': [0.001, 0, 0, 0.001, 0, 0],
+ 'FontBBox': [0, 0, 0, 0],
+ 'StrokeWidth': 0,
+ 'charset': 0,
+ 'Encoding': 0,
+ # CID defaults
+ 'CIDFontVersion': 0,
+ 'CIDFontRevision': 0,
+ 'CIDFontType': 0,
+ 'CIDCount': 8720,
+}
+
+class TopDictDecompiler(DictDecompiler):
+
+ operators = _buildOperatorDict(topDictOperators)
+ dictDefaults = topDictDefaults
+
+
+privateDictOperators = [
+# opcode name argument type
+ (6, 'BlueValues', 'array'),
+ (7, 'OtherBlues', 'array'),
+ (8, 'FamilyBlues', 'array'),
+ (9, 'FamilyOtherBlues', 'array'),
+ (10, 'StdHW', 'number'),
+ (11, 'StdVW', 'number'),
+ (19, 'Subrs', 'number'),
+ (20, 'defaultWidthX', 'number'),
+ (21, 'nominalWidthX', 'number'),
+ ((12, 9), 'BlueScale', 'number'),
+ ((12, 10), 'BlueShift', 'number'),
+ ((12, 11), 'BlueFuzz', 'number'),
+ ((12, 12), 'StemSnapH', 'array'),
+ ((12, 13), 'StemSnapV', 'array'),
+ ((12, 14), 'ForceBold', 'number'),
+ ((12, 15), 'ForceBoldThreshold', 'number'),
+ ((12, 16), 'lenIV', 'number'),
+ ((12, 17), 'LanguageGroup', 'number'),
+ ((12, 18), 'ExpansionFactor', 'number'),
+ ((12, 19), 'initialRandomSeed', 'number'),
+]
+
+privateDictDefaults = {
+ 'defaultWidthX': 0,
+ 'nominalWidthX': 0,
+ 'BlueScale': 0.039625,
+ 'BlueShift': 7,
+ 'BlueFuzz': 1,
+ 'ForceBold': 0,
+ 'ForceBoldThreshold': 0,
+ 'lenIV': -1,
+ 'LanguageGroup': 0,
+ 'ExpansionFactor': 0.06,
+ 'initialRandomSeed': 0,
+}
+
+class PrivateDictDecompiler(DictDecompiler):
+
+ operators = _buildOperatorDict(privateDictOperators)
+ dictDefaults = privateDictDefaults
+
+
+def calcSubrBias(subrs):
+ nSubrs = len(subrs)
+ if nSubrs < 1240:
+ bias = 107
+ elif nSubrs < 33900:
+ bias = 1131
+ else:
+ bias = 32768
+ return bias
+
diff --git a/Lib/fontTools/psLib.py b/Lib/fontTools/psLib.py
new file mode 100644
index 0000000..8408680
--- /dev/null
+++ b/Lib/fontTools/psLib.py
@@ -0,0 +1,346 @@
+import StringIO
+import regex
+import string
+import eexec
+import types
+from psOperators import *
+
+
+ps_special = '()<>[]{}%' # / is one too, but we take care of that one differently
+
+whitespace = string.whitespace
+skipwhiteRE = regex.compile("[%s]*" % whitespace)
+
+endofthingPat = "[^][(){}<>/%s%s]*" % ('%', whitespace)
+endofthingRE = regex.compile(endofthingPat)
+
+commentRE = regex.compile("%[^\n\r]*")
+
+# XXX This not entirely correct:
+stringPat = """
+ (
+ \(
+ \(
+ [^()]* \\\\ [()]
+ \)
+ \|
+ \(
+ [^()]* ( [^()]* )
+ \)
+ \)*
+ [^()]*
+ )
+"""
+stringPat = string.join(string.split(stringPat), '')
+stringRE = regex.compile(stringPat)
+
+hexstringRE = regex.compile("<[%s0-9A-Fa-f]*>" % whitespace)
+
+ps_tokenerror = 'ps_tokenerror'
+ps_error = 'ps_error'
+
+class PSTokenizer(StringIO.StringIO):
+
+ def getnexttoken(self,
+ # localize some stuff, for performance
+ len = len,
+ ps_special = ps_special,
+ stringmatch = stringRE.match,
+ hexstringmatch = hexstringRE.match,
+ commentmatch = commentRE.match,
+ endmatch = endofthingRE.match,
+ whitematch = skipwhiteRE.match):
+
+ self.pos = self.pos + whitematch(self.buf, self.pos)
+ if self.pos >= self.len:
+ return None, None
+ pos = self.pos
+ buf = self.buf
+ char = buf[pos]
+ if char in ps_special:
+ if char in '{}[]':
+ tokentype = 'do_special'
+ token = char
+ elif char == '%':
+ tokentype = 'do_comment'
+ commentlen = commentmatch(buf, pos)
+ token = buf[pos:pos+commentlen]
+ elif char == '(':
+ tokentype = 'do_string'
+ strlen = stringmatch(buf, pos)
+ if strlen < 0:
+ raise ps_tokenerror, 'bad string at character %d' % pos
+ token = buf[pos:pos+strlen]
+ elif char == '<':
+ tokentype = 'do_hexstring'
+ strlen = hexstringmatch(buf, pos)
+ if strlen < 0:
+ raise ps_tokenerror, 'bad hexstring at character %d' % pos
+ token = buf[pos:pos+strlen]
+ else:
+ raise ps_tokenerror, 'bad token at character %d' % pos
+ else:
+ if char == '/':
+ tokentype = 'do_literal'
+ endofthing = endmatch(buf, pos + 1) + 1
+ else:
+ tokentype = ''
+ endofthing = endmatch(buf, pos)
+ if endofthing <= 0:
+ raise ps_tokenerror, 'bad token at character %d' % pos
+ token = buf[pos:pos + endofthing]
+ self.pos = pos + len(token)
+ return tokentype, token
+
+ def skipwhite(self, whitematch = skipwhiteRE.match):
+ self.pos = self.pos + whitematch(self.buf, self.pos)
+
+ def starteexec(self):
+ self.pos = self.pos + 1
+ #self.skipwhite()
+ self.dirtybuf = self.buf[self.pos:]
+ self.buf, R = eexec.Decrypt(self.dirtybuf, 55665)
+ self.len = len(self.buf)
+ self.pos = 4
+
+ def stopeexec(self):
+ if not hasattr(self, 'dirtybuf'):
+ return
+ self.buf = self.dirtybuf
+ del self.dirtybuf
+
+ def flush(self):
+ if self.buflist:
+ self.buf = self.buf + string.join(self.buflist, '')
+ self.buflist = []
+
+
+class PSInterpreter(PSOperators):
+
+ def __init__(self):
+ systemdict = {}
+ userdict = {}
+ self.dictstack = [systemdict, userdict]
+ self.stack = []
+ self.proclevel = 0
+ self.procmark = ps_procmark()
+ self.fillsystemdict()
+
+ def fillsystemdict(self):
+ systemdict = self.dictstack[0]
+ systemdict['['] = systemdict['mark'] = self.mark = ps_mark()
+ systemdict[']'] = ps_operator(']', self.do_makearray)
+ systemdict['true'] = ps_boolean(1)
+ systemdict['false'] = ps_boolean(0)
+ systemdict['StandardEncoding'] = ps_array(ps_StandardEncoding)
+ systemdict['FontDirectory'] = ps_dict({})
+ self.suckoperators(systemdict, self.__class__)
+
+ def suckoperators(self, systemdict, klass):
+ for name in dir(klass):
+ attr = getattr(self, name)
+ if callable(attr) and name[:3] == 'ps_':
+ name = name[3:]
+ systemdict[name] = ps_operator(name, attr)
+ for baseclass in klass.__bases__:
+ self.suckoperators(systemdict, baseclass)
+
+ def interpret(self, data, getattr = getattr):
+ tokenizer = self.tokenizer = PSTokenizer(data)
+ getnexttoken = tokenizer.getnexttoken
+ do_token = self.do_token
+ handle_object = self.handle_object
+ try:
+ while 1:
+ tokentype, token = getnexttoken()
+ #print token
+ if not token:
+ break
+ if tokentype:
+ handler = getattr(self, tokentype)
+ object = handler(token)
+ else:
+ object = do_token(token)
+ if object is not None:
+ handle_object(object)
+ tokenizer.close()
+ self.tokenizer = None
+ finally:
+ if self.tokenizer is not None:
+ print 'ps error:\n- - - - - - -'
+ print self.tokenizer.buf[self.tokenizer.pos-50:self.tokenizer.pos]
+ print '>>>'
+ print self.tokenizer.buf[self.tokenizer.pos:self.tokenizer.pos+50]
+ print '- - - - - - -'
+
+ def handle_object(self, object):
+ if not (self.proclevel or object.literal or object.type == 'proceduretype'):
+ if object.type <> 'operatortype':
+ object = self.resolve_name(object.value)
+ if object.literal:
+ self.push(object)
+ else:
+ if object.type == 'proceduretype':
+ self.call_procedure(object)
+ else:
+ object.function()
+ else:
+ self.push(object)
+
+ def call_procedure(self, proc):
+ handle_object = self.handle_object
+ for item in proc.value:
+ handle_object(item)
+
+ def resolve_name(self, name):
+ dictstack = self.dictstack
+ for i in range(len(dictstack)-1, -1, -1):
+ if dictstack[i].has_key(name):
+ return dictstack[i][name]
+ raise ps_error, 'name error: ' + str(name)
+
+ def do_token(self, token,
+ atoi = string.atoi,
+ atof = string.atof,
+ ps_name = ps_name,
+ ps_integer = ps_integer,
+ ps_real = ps_real):
+ try:
+ num = atoi(token)
+ except (ValueError, OverflowError):
+ try:
+ num = atof(token)
+ except (ValueError, OverflowError):
+ if '#' in token:
+ hashpos = string.find(token, '#')
+ try:
+ base = string.atoi(token[:hashpos])
+ num = string.atoi(token[hashpos+1:], base)
+ except (ValueError, OverflowError):
+ return ps_name(token)
+ else:
+ return ps_integer(num)
+ else:
+ return ps_name(token)
+ else:
+ return ps_real(num)
+ else:
+ return ps_integer(num)
+
+ def do_comment(self, token):
+ pass
+
+ def do_literal(self, token):
+ return ps_literal(token[1:])
+
+ def do_string(self, token):
+ return ps_string(token[1:-1])
+
+ def do_hexstring(self, token):
+ hexStr = string.join(string.split(token[1:-1]), '')
+ if len(hexStr) % 2:
+ hexStr = hexStr + '0'
+ cleanstr = []
+ for i in range(0, len(hexStr), 2):
+ cleanstr.append(chr(string.atoi(hexStr[i:i+2], 16)))
+ cleanstr = string.join(cleanstr, '')
+ return ps_string(cleanstr)
+
+ def do_special(self, token):
+ if token == '{':
+ self.proclevel = self.proclevel + 1
+ return self.procmark
+ elif token == '}':
+ proc = []
+ while 1:
+ topobject = self.pop()
+ if topobject == self.procmark:
+ break
+ proc.append(topobject)
+ self.proclevel = self.proclevel - 1
+ proc.reverse()
+ return ps_procedure(proc)
+ elif token == '[':
+ return self.mark
+ elif token == ']':
+ return ps_name(']')
+ else:
+ raise ps_tokenerror, 'huh?'
+
+ def push(self, object):
+ self.stack.append(object)
+
+ def pop(self, *types):
+ stack = self.stack
+ if not stack:
+ raise ps_error, 'stack underflow'
+ object = stack[-1]
+ if types:
+ if object.type not in types:
+ raise ps_error, 'typecheck, expected %s, found %s' % (`types`, object.type)
+ del stack[-1]
+ return object
+
+ def do_makearray(self):
+ array = []
+ while 1:
+ topobject = self.pop()
+ if topobject == self.mark:
+ break
+ array.append(topobject)
+ array.reverse()
+ self.push(ps_array(array))
+
+ def close(self):
+ """Remove circular references."""
+ del self.stack
+ del self.dictstack
+
+
+def unpack_item(item):
+ tp = type(item.value)
+ if tp == types.DictionaryType:
+ newitem = {}
+ for key, value in item.value.items():
+ newitem[key] = unpack_item(value)
+ elif tp == types.ListType:
+ newitem = [None] * len(item.value)
+ for i in range(len(item.value)):
+ newitem[i] = unpack_item(item.value[i])
+ if item.type == 'proceduretype':
+ newitem = tuple(newitem)
+ else:
+ newitem = item.value
+ return newitem
+
+def suckfont(data):
+ import re
+ m = re.search(r"/FontName\s+/([^ \t\n\r]+)\s+def", data)
+ if m:
+ fontName = m.group(1)
+ else:
+ fontName = None
+ interpreter = PSInterpreter()
+ interpreter.interpret("/Helvetica 4 dict dup /Encoding StandardEncoding put definefont pop")
+ interpreter.interpret(data)
+ fontdir = interpreter.dictstack[0]['FontDirectory'].value
+ if fontdir.has_key(fontName):
+ rawfont = fontdir[fontName]
+ else:
+ # fall back, in case fontName wasn't found
+ fontNames = fontdir.keys()
+ if len(fontNames) > 1:
+ fontNames.remove("Helvetica")
+ fontNames.sort()
+ rawfont = fontdir[fontNames[0]]
+ interpreter.close()
+ return unpack_item(rawfont)
+
+
+if __name__ == "__main__":
+ import macfs
+ fss, ok = macfs.StandardGetFile("LWFN")
+ if ok:
+ import t1Lib
+ data, kind = t1Lib.read(fss.as_pathname())
+ font = suckfont(data)
diff --git a/Lib/fontTools/psOperators.py b/Lib/fontTools/psOperators.py
new file mode 100644
index 0000000..533f484
--- /dev/null
+++ b/Lib/fontTools/psOperators.py
@@ -0,0 +1,580 @@
+import string
+
+
+_accessstrings = {0: "", 1: "readonly", 2: "executeonly", 3: "noaccess"}
+
+class ps_object:
+
+ literal = 1
+ access = 0
+ value = None
+
+ def __init__(self, value):
+ self.value = value
+ self.type = self.__class__.__name__[3:] + "type"
+
+ def __repr__(self):
+ return "<%s %s>" % (self.__class__.__name__[3:], repr(self.value))
+
+
+class ps_operator(ps_object):
+
+ literal = 0
+
+ def __init__(self, name, function):
+ self.name = name
+ self.function = function
+ self.type = self.__class__.__name__[3:] + "type"
+ def __repr__(self):
+ return "<operator %s>" % self.name
+
+class ps_procedure(ps_object):
+ literal = 0
+ def __repr__(self):
+ return "<procedure>"
+ def __str__(self):
+ psstring = '{'
+ for i in range(len(self.value)):
+ if i:
+ psstring = psstring + ' ' + str(self.value[i])
+ else:
+ psstring = psstring + str(self.value[i])
+ return psstring + '}'
+
+class ps_name(ps_object):
+ literal = 0
+ def __str__(self):
+ if self.literal:
+ return '/' + self.value
+ else:
+ return self.value
+
+class ps_literal(ps_object):
+ def __str__(self):
+ return '/' + self.value
+
+class ps_array(ps_object):
+ def __str__(self):
+ psstring = '['
+ for i in range(len(self.value)):
+ item = self.value[i]
+ access = _accessstrings[item.access]
+ if access:
+ access = ' ' + access
+ if i:
+ psstring = psstring + ' ' + str(item) + access
+ else:
+ psstring = psstring + str(item) + access
+ return psstring + ']'
+ def __repr__(self):
+ return "<array>"
+
+_type1_pre_eexec_order = [
+ "FontInfo",
+ "FontName",
+ "Encoding",
+ "PaintType",
+ "FontType",
+ "FontMatrix",
+ "FontBBox",
+ "UniqueID",
+ "Metrics",
+ "StrokeWidth"
+ ]
+
+_type1_fontinfo_order = [
+ "version",
+ "Notice",
+ "FullName",
+ "FamilyName",
+ "Weight",
+ "ItalicAngle",
+ "isFixedPitch",
+ "UnderlinePosition",
+ "UnderlineThickness"
+ ]
+
+_type1_post_eexec_order = [
+ "Private",
+ "CharStrings",
+ "FID"
+ ]
+
+def _type1_item_repr(key, value):
+ psstring = ""
+ access = _accessstrings[value.access]
+ if access:
+ access = access + ' '
+ if key == 'CharStrings':
+ psstring = psstring + "/%s %s def\n" % (key, _type1_CharString_repr(value.value))
+ elif key == 'Encoding':
+ psstring = psstring + _type1_Encoding_repr(value, access)
+ else:
+ psstring = psstring + "/%s %s %sdef\n" % (str(key), str(value), access)
+ return psstring
+
+def _type1_Encoding_repr(encoding, access):
+ encoding = encoding.value
+ psstring = "/Encoding 256 array\n0 1 255 {1 index exch /.notdef put} for\n"
+ for i in range(256):
+ name = encoding[i].value
+ if name <> '.notdef':
+ psstring = psstring + "dup %d /%s put\n" % (i, name)
+ return psstring + access + "def\n"
+
+def _type1_CharString_repr(charstrings):
+ items = charstrings.items()
+ items.sort()
+ return 'xxx'
+
+class ps_font(ps_object):
+ def __str__(self):
+ psstring = "%d dict dup begin\n" % len(self.value)
+ for key in _type1_pre_eexec_order:
+ try:
+ value = self.value[key]
+ except KeyError:
+ pass
+ else:
+ psstring = psstring + _type1_item_repr(key, value)
+ items = self.value.items()
+ items.sort()
+ for key, value in items:
+ if key not in _type1_pre_eexec_order + _type1_post_eexec_order:
+ psstring = psstring + _type1_item_repr(key, value)
+ psstring = psstring + "currentdict end\ncurrentfile eexec\ndup "
+ for key in _type1_post_eexec_order:
+ try:
+ value = self.value[key]
+ except KeyError:
+ pass
+ else:
+ psstring = psstring + _type1_item_repr(key, value)
+ return psstring + 'dup/FontName get exch definefont pop\nmark currentfile closefile\n' + \
+ 8 * (64 * '0' + '\n') + 'cleartomark' + '\n'
+ def __repr__(self):
+ return '<font>'
+
+class ps_file(ps_object):
+ pass
+
+class ps_dict(ps_object):
+ def __str__(self):
+ psstring = "%d dict dup begin\n" % len(self.value)
+ items = self.value.items()
+ items.sort()
+ dictrepr = "%d dict dup begin\n" % len(items)
+ for key, value in items:
+ access = _accessstrings[value.access]
+ if access:
+ access = access + ' '
+ psstring = psstring + "/%s %s %sdef\n" % (str(key), str(value), access)
+ return psstring + 'end '
+ def __repr__(self):
+ return "<dict>"
+
+class ps_mark(ps_object):
+ def __init__(self):
+ self.value = 'mark'
+ self.type = self.__class__.__name__[3:] + "type"
+
+class ps_procmark(ps_object):
+ def __init__(self):
+ self.value = 'procmark'
+ self.type = self.__class__.__name__[3:] + "type"
+
+class ps_null(ps_object):
+ def __init__(self):
+ self.type = self.__class__.__name__[3:] + "type"
+
+class ps_boolean(ps_object):
+ def __str__(self):
+ if self.value:
+ return 'true'
+ else:
+ return 'false'
+
+class ps_string(ps_object):
+ def __str__(self):
+ return "(%s)" % `self.value`[1:-1]
+
+class ps_integer(ps_object):
+ def __str__(self):
+ return `self.value`
+
+class ps_real(ps_object):
+ def __str__(self):
+ return `self.value`
+
+
+class PSOperators:
+
+ def ps_def(self):
+ object = self.pop()
+ name = self.pop()
+ self.dictstack[-1][name.value] = object
+
+ def ps_bind(self):
+ proc = self.pop('proceduretype')
+ self.proc_bind(proc)
+ self.push(proc)
+
+ def proc_bind(self, proc):
+ for i in range(len(proc.value)):
+ item = proc.value[i]
+ if item.type == 'proceduretype':
+ self.proc_bind(item)
+ else:
+ if not item.literal:
+ try:
+ object = self.resolve_name(item.value)
+ except:
+ pass
+ else:
+ if object.type == 'operatortype':
+ proc.value[i] = object
+
+ def ps_exch(self):
+ if len(self.stack) < 2:
+ raise RuntimeError, 'stack underflow'
+ obj1 = self.pop()
+ obj2 = self.pop()
+ self.push(obj1)
+ self.push(obj2)
+
+ def ps_dup(self):
+ if not self.stack:
+ raise RuntimeError, 'stack underflow'
+ self.push(self.stack[-1])
+
+ def ps_exec(self):
+ object = self.pop()
+ if object.type == 'proceduretype':
+ self.call_procedure(object)
+ else:
+ self.handle_object(object)
+
+ def ps_count(self):
+ self.push(ps_integer(len(self.stack)))
+
+ def ps_eq(self):
+ any1 = self.pop()
+ any2 = self.pop()
+ self.push(ps_boolean(any1.value == any2.value))
+
+ def ps_ne(self):
+ any1 = self.pop()
+ any2 = self.pop()
+ self.push(ps_boolean(any1.value <> any2.value))
+
+ def ps_cvx(self):
+ obj = self.pop()
+ obj.literal = 0
+ self.push(obj)
+
+ def ps_matrix(self):
+ matrix = [ps_real(1.0), ps_integer(0), ps_integer(0), ps_real(1.0), ps_integer(0), ps_integer(0)]
+ self.push(ps_array(matrix))
+
+ def ps_string(self):
+ num = self.pop('integertype').value
+ self.push(ps_string('\0' * num))
+
+ def ps_type(self):
+ obj = self.pop()
+ self.push(ps_string(obj.type))
+
+ def ps_store(self):
+ value = self.pop()
+ key = self.pop()
+ name = key.value
+ for i in range(len(self.dictstack)-1, -1, -1):
+ if self.dictstack[i].has_key(name):
+ self.dictstack[i][name] = value
+ break
+ self.dictstack[-1][name] = value
+
+ def ps_where(self):
+ name = self.pop()
+ # XXX
+ self.push(ps_boolean(0))
+
+ def ps_systemdict(self):
+ self.push(ps_dict(self.dictstack[0]))
+
+ def ps_userdict(self):
+ self.push(ps_dict(self.dictstack[1]))
+
+ def ps_currentdict(self):
+ self.push(ps_dict(self.dictstack[-1]))
+
+ def ps_currentfile(self):
+ self.push(ps_file(self.tokenizer))
+
+ def ps_eexec(self):
+ file = self.pop('filetype').value
+ file.starteexec()
+
+ def ps_closefile(self):
+ file = self.pop('filetype').value
+ file.skipwhite()
+ file.stopeexec()
+
+ def ps_cleartomark(self):
+ obj = self.pop()
+ while obj <> self.mark:
+ obj = self.pop()
+
+ def ps_readstring(self,
+ ps_boolean = ps_boolean,
+ len = len):
+ string = self.pop('stringtype')
+ oldstr = string.value
+ file = self.pop('filetype')
+ #pad = file.value.read(1)
+ # for StringIO, this is faster
+ file.value.pos = file.value.pos + 1
+ newstr = file.value.read(len(oldstr))
+ string.value = newstr
+ self.push(string)
+ self.push(ps_boolean(len(oldstr) == len(newstr)))
+
+ def ps_known(self):
+ key = self.pop()
+ dict = self.pop('dicttype', 'fonttype')
+ self.push(ps_boolean(dict.value.has_key(key.value)))
+
+ def ps_if(self):
+ proc = self.pop('proceduretype')
+ bool = self.pop('booleantype')
+ if bool.value:
+ self.call_procedure(proc)
+
+ def ps_ifelse(self):
+ proc2 = self.pop('proceduretype')
+ proc1 = self.pop('proceduretype')
+ bool = self.pop('booleantype')
+ if bool.value:
+ self.call_procedure(proc1)
+ else:
+ self.call_procedure(proc2)
+
+ def ps_readonly(self):
+ obj = self.pop()
+ if obj.access < 1:
+ obj.access = 1
+ self.push(obj)
+
+ def ps_executeonly(self):
+ obj = self.pop()
+ if obj.access < 2:
+ obj.access = 2
+ self.push(obj)
+
+ def ps_noaccess(self):
+ obj = self.pop()
+ if obj.access < 3:
+ obj.access = 3
+ self.push(obj)
+
+ def ps_not(self):
+ obj = self.pop('booleantype', 'integertype')
+ if obj.type == 'booleantype':
+ self.push(ps_boolean(not obj.value))
+ else:
+ self.push(ps_integer(~obj.value))
+
+ def ps_print(self):
+ str = self.pop('stringtype')
+ print 'PS output --->', str.value
+
+ def ps_anchorsearch(self):
+ seek = self.pop('stringtype')
+ string = self.pop('stringtype')
+ seeklen = len(seek.value)
+ if string.value[:seeklen] == seek.value:
+ self.push(ps_string(string.value[seeklen:]))
+ self.push(seek)
+ self.push(ps_boolean(1))
+ else:
+ self.push(string)
+ self.push(ps_boolean(0))
+
+ def ps_array(self):
+ num = self.pop('integertype')
+ array = ps_array([None] * num.value)
+ self.push(array)
+
+ def ps_astore(self):
+ array = self.pop('arraytype')
+ for i in range(len(array.value)-1, -1, -1):
+ array.value[i] = self.pop()
+ self.push(array)
+
+ def ps_load(self):
+ name = self.pop()
+ object = self.resolve_name(name.value)
+ self.push(object)
+
+ def ps_put(self):
+ obj1 = self.pop()
+ obj2 = self.pop()
+ obj3 = self.pop('arraytype', 'dicttype', 'stringtype', 'proceduretype')
+ tp = obj3.type
+ if tp == 'arraytype' or tp == 'proceduretype':
+ obj3.value[obj2.value] = obj1
+ elif tp == 'dicttype':
+ obj3.value[obj2.value] = obj1
+ elif tp == 'stringtype':
+ index = obj2.value
+ obj3.value = obj3.value[:index] + chr(obj1.value) + obj3.value[index+1:]
+
+ def ps_get(self):
+ obj1 = self.pop()
+ if obj1.value == "Encoding":
+ pass
+ obj2 = self.pop('arraytype', 'dicttype', 'stringtype', 'proceduretype', 'fonttype')
+ tp = obj2.type
+ if tp in ('arraytype', 'proceduretype'):
+ self.push(obj2.value[obj1.value])
+ elif tp in ('dicttype', 'fonttype'):
+ self.push(obj2.value[obj1.value])
+ elif tp == 'stringtype':
+ self.push(ps_integer(ord(obj2.value[obj1.value])))
+ else:
+ assert 0, "shouldn't get here"
+
+ def ps_getinterval(self):
+ obj1 = self.pop('integertype')
+ obj2 = self.pop('integertype')
+ obj3 = self.pop('arraytype', 'stringtype')
+ tp = obj3.type
+ if tp == 'arraytype':
+ self.push(ps_array(obj3.value[obj2.value:obj2.value + obj1.value]))
+ elif tp == 'stringtype':
+ self.push(ps_string(obj3.value[obj2.value:obj2.value + obj1.value]))
+
+ def ps_putinterval(self):
+ obj1 = self.pop('arraytype', 'stringtype')
+ obj2 = self.pop('integertype')
+ obj3 = self.pop('arraytype', 'stringtype')
+ tp = obj3.type
+ if tp == 'arraytype':
+ obj3.value[obj2.value:obj2.value + len(obj1.value)] = obj1.value
+ elif tp == 'stringtype':
+ newstr = obj3.value[:obj2.value]
+ newstr = newstr + obj1.value
+ newstr = newstr + obj3.value[obj2.value + len(obj1.value):]
+ obj3.value = newstr
+
+ def ps_cvn(self):
+ str = self.pop('stringtype')
+ self.push(ps_name(str.value))
+
+ def ps_index(self):
+ n = self.pop('integertype').value
+ if n < 0:
+ raise RuntimeError, 'index may not be negative'
+ self.push(self.stack[-1-n])
+
+ def ps_for(self):
+ proc = self.pop('proceduretype')
+ limit = self.pop('integertype', 'realtype').value
+ increment = self.pop('integertype', 'realtype').value
+ i = self.pop('integertype', 'realtype').value
+ while 1:
+ if increment > 0:
+ if i > limit:
+ break
+ else:
+ if i < limit:
+ break
+ if type(i) == type(0.0):
+ self.push(ps_real(i))
+ else:
+ self.push(ps_integer(i))
+ self.call_procedure(proc)
+ i = i + increment
+
+ def ps_forall(self):
+ proc = self.pop('proceduretype')
+ obj = self.pop('arraytype', 'stringtype', 'dicttype')
+ tp = obj.type
+ if tp == 'arraytype':
+ for item in obj.value:
+ self.push(item)
+ self.call_procedure(proc)
+ elif tp == 'stringtype':
+ for item in obj.value:
+ self.push(ps_integer(ord(item)))
+ self.call_procedure(proc)
+ elif tp == 'dicttype':
+ for key, value in obj.value.items():
+ self.push(ps_name(key))
+ self.push(value)
+ self.call_procedure(proc)
+
+ def ps_definefont(self):
+ font = self.pop('dicttype')
+ name = self.pop()
+ font = ps_font(font.value)
+ self.dictstack[0]['FontDirectory'].value[name.value] = font
+ self.push(font)
+
+ def ps_findfont(self):
+ name = self.pop()
+ font = self.dictstack[0]['FontDirectory'].value[name.value]
+ self.push(font)
+
+ def ps_pop(self):
+ self.pop()
+
+ def ps_dict(self):
+ num = self.pop('integertype')
+ dict = ps_dict({})
+ self.push(dict)
+
+ def ps_begin(self):
+ dict = self.pop('dicttype')
+ self.dictstack.append(dict.value)
+
+ def ps_end(self):
+ if len(self.dictstack) > 2:
+ del self.dictstack[-1]
+ else:
+ raise RuntimeError, 'dictstack underflow'
+
+notdef = '.notdef'
+StandardEncoding = [ notdef, notdef, notdef, notdef, notdef, notdef,
+ notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
+ notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
+ notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
+ notdef, notdef, 'space', 'exclam', 'quotedbl', 'numbersign', 'dollar', 'percent',
+ 'ampersand', 'quoteright', 'parenleft', 'parenright', 'asterisk', 'plus', 'comma',
+ 'hyphen', 'period', 'slash', 'zero', 'one', 'two', 'three', 'four', 'five', 'six',
+ 'seven', 'eight', 'nine', 'colon', 'semicolon', 'less', 'equal', 'greater', 'question',
+ 'at', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q',
+ 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'bracketleft', 'backslash', 'bracketright',
+ 'asciicircum', 'underscore', 'quoteleft', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i',
+ 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
+ 'braceleft', 'bar', 'braceright', 'asciitilde', notdef, notdef, notdef,
+ notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
+ notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
+ notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
+ notdef, notdef, notdef, notdef, notdef, notdef, notdef, 'exclamdown',
+ 'cent', 'sterling', 'fraction', 'yen', 'florin', 'section', 'currency', 'quotesingle',
+ 'quotedblleft', 'guillemotleft', 'guilsinglleft', 'guilsinglright', 'fi', 'fl', notdef,
+ 'endash', 'dagger', 'daggerdbl', 'periodcentered', notdef, 'paragraph', 'bullet',
+ 'quotesinglbase', 'quotedblbase', 'quotedblright', 'guillemotright', 'ellipsis',
+ 'perthousand', notdef, 'questiondown', notdef, 'grave', 'acute', 'circumflex',
+ 'tilde', 'macron', 'breve', 'dotaccent', 'dieresis', notdef, 'ring', 'cedilla',
+ notdef, 'hungarumlaut', 'ogonek', 'caron', 'emdash', notdef, notdef, notdef,
+ notdef, notdef, notdef, notdef, notdef, notdef, notdef, notdef,
+ notdef, notdef, notdef, notdef, notdef, 'AE', notdef, 'ordfeminine',
+ notdef, notdef, notdef, notdef, 'Lslash', 'Oslash', 'OE', 'ordmasculine',
+ notdef, notdef, notdef, notdef, notdef, 'ae', notdef, notdef,
+ notdef, 'dotlessi', notdef, notdef, 'lslash', 'oslash', 'oe', 'germandbls',
+ notdef, notdef, notdef, notdef ]
+
+ps_StandardEncoding = map(ps_name, StandardEncoding)
+
diff --git a/Lib/fontTools/t1Lib.py b/Lib/fontTools/t1Lib.py
new file mode 100644
index 0000000..a2c63fb
--- /dev/null
+++ b/Lib/fontTools/t1Lib.py
@@ -0,0 +1,347 @@
+"""fontTools.t1Lib.py -- Tools for PostScript Type 1 fonts
+
+Functions for reading and writing raw Type 1 data:
+
+read(path)
+ reads any Type 1 font file, returns the raw data and a type indicator:
+ 'LWFN', 'PFB' or 'OTHER', depending on the format of the file pointed
+ to by 'path'.
+ Raises an error when the file does not contain valid Type 1 data.
+
+write(path, data, kind = 'OTHER', dohex = 0)
+ writes raw Type 1 data to the file pointed to by 'path'.
+ 'kind' can be one of 'LWFN', 'PFB' or 'OTHER'; it defaults to 'OTHER'.
+ 'dohex' is a flag which determines whether the eexec encrypted
+ part should be written as hexadecimal or binary, but only if kind
+ is 'LWFN' or 'PFB'.
+"""
+
+__author__ = "jvr"
+__version__ = "1.0b2"
+DEBUG = 0
+
+import eexec
+import string
+import re
+import os
+
+if os.name == 'mac':
+ import Res
+ import macfs
+
+error = 't1Lib.error'
+
+# work in progress
+
+
+class T1Font:
+
+ """Type 1 font class.
+ XXX This is work in progress! For now just use the read()
+ and write() functions as described above, they are stable.
+ """
+
+ def __init__(self, path=None):
+ if path is not None:
+ self.data, type = read(path)
+ else:
+ pass # XXX
+
+ def saveAs(self, path, type):
+ self.write(path, self.getData(), type)
+
+ def getData(self):
+ return self.data
+
+ def __getitem__(self, key):
+ if not hasattr(self, "font"):
+ self.parse()
+ return self.font[key]
+ else:
+ return self.font[key]
+
+ def parse(self):
+ import psLib
+ import psCharStrings
+ self.font = psLib.suckfont(self.data)
+ charStrings = self.font["CharStrings"]
+ lenIV = self.font["Private"].get("lenIV", 4)
+ assert lenIV >= 0
+ for glyphName, charString in charStrings.items():
+ charString, R = eexec.Decrypt(charString, 4330)
+ charStrings[glyphName] = psCharStrings.T1CharString(charString[lenIV:])
+ subrs = self.font["Private"]["Subrs"]
+ for i in range(len(subrs)):
+ charString, R = eexec.Decrypt(subrs[i], 4330)
+ subrs[i] = psCharStrings.T1CharString(charString[lenIV:])
+ del self.data
+
+
+
+# public functions
+
+def read(path):
+ """reads any Type 1 font file, returns raw data"""
+ normpath = string.lower(path)
+ if os.name == 'mac':
+ fss = macfs.FSSpec(path)
+ creator, type = fss.GetCreatorType()
+ if type == 'LWFN':
+ return readlwfn(path), 'LWFN'
+ if normpath[-4:] == '.pfb':
+ return readpfb(path), 'PFB'
+ else:
+ return readother(path), 'OTHER'
+
+def write(path, data, kind='OTHER', dohex=0):
+ asserttype1(data)
+ kind = string.upper(kind)
+ try:
+ os.remove(path)
+ except os.error:
+ pass
+ err = 1
+ try:
+ if kind == 'LWFN':
+ writelwfn(path, data)
+ elif kind == 'PFB':
+ writepfb(path, data)
+ else:
+ writeother(path, data, dohex)
+ err = 0
+ finally:
+ if err and not DEBUG:
+ try:
+ os.remove(path)
+ except os.error:
+ pass
+
+
+# -- internal --
+
+LWFNCHUNKSIZE = 2000
+HEXLINELENGTH = 80
+
+
+def readlwfn(path):
+ """reads an LWFN font file, returns raw data"""
+ resref = Res.OpenResFile(path)
+ try:
+ Res.UseResFile(resref)
+ n = Res.Count1Resources('POST')
+ data = []
+ for i in range(501, 501 + n):
+ res = Res.Get1Resource('POST', i)
+ code = ord(res.data[0])
+ if ord(res.data[1]) <> 0:
+ raise error, 'corrupt LWFN file'
+ if code in [1, 2]:
+ data.append(res.data[2:])
+ elif code in [3, 5]:
+ break
+ elif code == 4:
+ f = open(path, "rb")
+ data.append(f.read())
+ f.close()
+ elif code == 0:
+ pass # comment, ignore
+ else:
+ raise error, 'bad chunk code: ' + `code`
+ finally:
+ Res.CloseResFile(resref)
+ data = string.join(data, '')
+ asserttype1(data)
+ return data
+
+def readpfb(path):
+ """reads a PFB font file, returns raw data"""
+ f = open(path, "rb")
+ data = []
+ while 1:
+ if f.read(1) <> chr(128):
+ raise error, 'corrupt PFB file'
+ code = ord(f.read(1))
+ if code in [1, 2]:
+ chunklen = string2long(f.read(4))
+ data.append(f.read(chunklen))
+ elif code == 3:
+ break
+ else:
+ raise error, 'bad chunk code: ' + `code`
+ f.close()
+ data = string.join(data, '')
+ asserttype1(data)
+ return data
+
+def readother(path):
+ """reads any (font) file, returns raw data"""
+ f = open(path, "rb")
+ data = f.read()
+ f.close()
+ asserttype1(data)
+
+ chunks = findencryptedchunks(data)
+ data = []
+ for isencrypted, chunk in chunks:
+ if isencrypted and ishex(chunk[:4]):
+ data.append(dehexstring(chunk))
+ else:
+ data.append(chunk)
+ return string.join(data, '')
+
+# file writing tools
+
+def writelwfn(path, data):
+ Res.CreateResFile(path)
+ fss = macfs.FSSpec(path)
+ fss.SetCreatorType('just', 'LWFN')
+ resref = Res.OpenResFile(path)
+ try:
+ Res.UseResFile(resref)
+ resID = 501
+ chunks = findencryptedchunks(data)
+ for isencrypted, chunk in chunks:
+ if isencrypted:
+ code = 2
+ else:
+ code = 1
+ while chunk:
+ res = Res.Resource(chr(code) + '\0' + chunk[:LWFNCHUNKSIZE - 2])
+ res.AddResource('POST', resID, '')
+ chunk = chunk[LWFNCHUNKSIZE - 2:]
+ resID = resID + 1
+ res = Res.Resource(chr(5) + '\0')
+ res.AddResource('POST', resID, '')
+ finally:
+ Res.CloseResFile(resref)
+
+def writepfb(path, data):
+ chunks = findencryptedchunks(data)
+ f = open(dstpath, "wb")
+ try:
+ for isencrypted, chunk in chunks:
+ if isencrypted:
+ code = 2
+ else:
+ code = 1
+ f.write(chr(128) + chr(code))
+ f.write(long2string(len(chunk)))
+ f.write(chunk)
+ f.write(chr(128) + chr(3))
+ finally:
+ f.close()
+ if os.name == 'mac':
+ fss = macfs.FSSpec(dstpath)
+ fss.SetCreatorType('mdos', 'BINA')
+
+def writeother(path, data, dohex = 0):
+ chunks = findencryptedchunks(data)
+ f = open(path, "wb")
+ try:
+ hexlinelen = HEXLINELENGTH / 2
+ for isencrypted, chunk in chunks:
+ if isencrypted:
+ code = 2
+ else:
+ code = 1
+ if code == 2 and dohex:
+ while chunk:
+ f.write(eexec.hexstring(chunk[:hexlinelen]))
+ f.write('\r')
+ chunk = chunk[hexlinelen:]
+ else:
+ f.write(chunk)
+ finally:
+ f.close()
+ if os.name == 'mac':
+ fss = macfs.FSSpec(path)
+ fss.SetCreatorType('R*ch', 'TEXT') # BBEdit text file
+
+
+# decryption tools
+
+EEXECBEGIN = "currentfile eexec"
+EEXECEND = '0' * 64
+EEXECINTERNALEND = "currentfile closefile"
+EEXECBEGINMARKER = "%-- eexec start\r"
+EEXECENDMARKER = "%-- eexec end\r"
+
+_ishexRE = re.compile('[0-9A-Fa-f]*$')
+
+def ishex(text):
+ return _ishexRE.match(text) is not None
+
+
+def decrypttype1(data):
+ chunks = findencryptedchunks(data)
+ data = []
+ for isencrypted, chunk in chunks:
+ if isencrypted:
+ if ishex(chunk[:4]):
+ chunk = dehexstring(chunk)
+ decrypted, R = eexec.Decrypt(chunk, 55665)
+ decrypted = decrypted[4:]
+ if decrypted[-len(EEXECINTERNALEND)-1:-1] <> EEXECINTERNALEND \
+ and decrypted[-len(EEXECINTERNALEND)-2:-2] <> EEXECINTERNALEND:
+ raise error, "invalid end of eexec part"
+ decrypted = decrypted[:-len(EEXECINTERNALEND)-2] + '\r'
+ data.append(EEXECBEGINMARKER + decrypted + EEXECENDMARKER)
+ else:
+ if chunk[-len(EEXECBEGIN)-1:-1] == EEXECBEGIN:
+ data.append(chunk[:-len(EEXECBEGIN)-1])
+ else:
+ data.append(chunk)
+ return string.join(data, '')
+
+def findencryptedchunks(data):
+ chunks = []
+ while 1:
+ ebegin = string.find(data, EEXECBEGIN)
+ if ebegin < 0:
+ break
+ eend = string.find(data, EEXECEND, ebegin)
+ if eend < 0:
+ raise error, "can't find end of eexec part"
+ chunks.append((0, data[:ebegin + len(EEXECBEGIN) + 1]))
+ chunks.append((1, data[ebegin + len(EEXECBEGIN) + 1:eend]))
+ data = data[eend:]
+ chunks.append((0, data))
+ return chunks
+
+def dehexstring(hexstring):
+ return eexec.dehexstring(string.join(string.split(hexstring), ""))
+
+
+# Type 1 assertion
+
+_fontType1RE = re.compile(r"/FontType\s+1\s+def")
+
+def asserttype1(data):
+ for head in ['%!PS-AdobeFont', '%!FontType1-1.0']:
+ if data[:len(head)] == head:
+ break
+ else:
+ raise error, "not a PostScript font"
+ if not _fontType1RE.search(data):
+ raise error, "not a Type 1 font"
+ if string.find(data, "currentfile eexec") < 0:
+ raise error, "not an encrypted Type 1 font"
+ # XXX what else?
+ return data
+
+
+# pfb helpers
+
+def long2string(long):
+ str = ""
+ for i in range(4):
+ str = str + chr((long & (0xff << (i * 8))) >> i * 8)
+ return str
+
+def string2long(str):
+ if len(str) <> 4:
+ raise ValueError, 'string must be 4 bytes long'
+ long = 0
+ for i in range(4):
+ long = long + (ord(str[i]) << (i * 8))
+ return long
diff --git a/Lib/fontTools/ttLib/__init__.py b/Lib/fontTools/ttLib/__init__.py
new file mode 100644
index 0000000..e087465
--- /dev/null
+++ b/Lib/fontTools/ttLib/__init__.py
@@ -0,0 +1,555 @@
+"""ttLib -- a package for dealing with TrueType fonts.
+
+This package offers translators to convert TrueType fonts to Python
+objects and vice versa, and additionally from Python to XML and vice versa.
+
+Example interactive session:
+
+Python 1.5.2c1 (#43, Mar 9 1999, 13:06:43) [CW PPC w/GUSI w/MSL]
+Copyright 1991-1995 Stichting Mathematisch Centrum, Amsterdam
+>>> from fontTools import ttLib
+>>> tt = ttLib.TTFont("afont.ttf")
+>>> tt['maxp'].numGlyphs
+242
+>>> tt['OS/2'].achVendID
+'B&H\000'
+>>> tt['head'].unitsPerEm
+2048
+>>> tt.saveXML("afont.xml")
+Dumping 'LTSH' table...
+Dumping 'OS/2' table...
+Dumping 'VDMX' table...
+Dumping 'cmap' table...
+Dumping 'cvt ' table...
+Dumping 'fpgm' table...
+Dumping 'glyf' table...
+Dumping 'hdmx' table...
+Dumping 'head' table...
+Dumping 'hhea' table...
+Dumping 'hmtx' table...
+Dumping 'loca' table...
+Dumping 'maxp' table...
+Dumping 'name' table...
+Dumping 'post' table...
+Dumping 'prep' table...
+>>> tt2 = ttLib.TTFont()
+>>> tt2.importXML("afont.xml")
+>>> tt2['maxp'].numGlyphs
+242
+>>>
+
+"""
+
+__author__ = "Just van Rossum, just@letterror.com"
+__version__ = "1.0a5"
+
+
+import os
+import stat
+import types
+
+class TTLibError(Exception): pass
+
+
+class TTFont:
+
+ """The main font object. It manages file input and output, and offers
+ a convenient way of accessing tables.
+ Tables will be only decompiled when neccesary, ie. when they're actually
+ accessed. This means that simple operations can be extremely fast.
+ """
+
+ def __init__(self, file=None, res_name_or_index=None,
+ sfntVersion="\000\001\000\000", checkchecksums=0, verbose=0):
+
+ """The constructor can be called with a few different arguments.
+ When reading a font from disk, 'file' should be either a pathname
+ pointing to a file, or a readable file object.
+
+ It we're running on a Macintosh, 'res_name_or_index' maybe an sfnt
+ resource name or an sfnt resource index number or zero. The latter
+ case will cause TTLib to autodetect whether the file is a flat file
+ or a suitcase. (If it's a suitcase, only the first 'sfnt' resource
+ will be read!)
+
+ The 'checkchecksums' argument is used to specify how sfnt
+ checksums are treated upon reading a file from disk:
+ 0: don't check (default)
+ 1: check, print warnings if a wrong checksum is found (default)
+ 2: check, raise an exception if a wrong checksum is found.
+
+ The TTFont constructor can also be called without a 'file'
+ argument: this is the way to create a new empty font.
+ In this case you can optionally supply the 'sfntVersion' argument.
+ """
+
+ import sfnt
+ self.verbose = verbose
+ self.tables = {}
+ self.reader = None
+ if not file:
+ self.sfntVersion = sfntVersion
+ return
+ if type(file) == types.StringType:
+ if os.name == "mac" and res_name_or_index is not None:
+ # on the mac, we deal with sfnt resources as well as flat files
+ import macUtils
+ if res_name_or_index == 0:
+ if macUtils.getSFNTResIndices(file):
+ # get the first available sfnt font.
+ file = macUtils.SFNTResourceReader(file, 1)
+ else:
+ file = open(file, "rb")
+ else:
+ file = macUtils.SFNTResourceReader(file, res_name_or_index)
+ else:
+ file = open(file, "rb")
+ else:
+ pass # assume "file" is a readable file object
+ self.reader = sfnt.SFNTReader(file, checkchecksums)
+ self.sfntVersion = self.reader.sfntVersion
+
+ def close(self):
+ """If we still have a reader object, close it."""
+ if self.reader is not None:
+ self.reader.close()
+
+ def save(self, file, make_suitcase=0):
+ """Save the font to disk. Similarly to the constructor,
+ the 'file' argument can be either a pathname or a writable
+ file object.
+
+ On the Mac, if make_suitcase is non-zero, a suitcase file will
+ we made instead of a flat .ttf file.
+ """
+ import sfnt
+ if type(file) == types.StringType:
+ if os.name == "mac" and make_suitcase:
+ import macUtils
+ file = macUtils.SFNTResourceWriter(file, self)
+ else:
+ file = open(file, "wb")
+ if os.name == "mac":
+ import macfs
+ fss = macfs.FSSpec(file.name)
+ fss.SetCreatorType('mdos', 'BINA')
+ else:
+ pass # assume "file" is a writable file object
+
+ tags = self.keys()
+ numTables = len(tags)
+ writer = sfnt.SFNTWriter(file, numTables, self.sfntVersion)
+
+ done = []
+ for tag in tags:
+ self._writeTable(tag, writer, done)
+
+ writer.close()
+
+ def saveXML(self, file, progress=None, tables=None):
+ """Export the font as an XML-based text file.
+ """
+ import xmlWriter
+ writer = xmlWriter.XMLWriter(file)
+ writer.begintag("ttFont", sfntVersion=`self.sfntVersion`[1:-1],
+ ttlibVersion=__version__)
+ writer.newline()
+ writer.newline()
+ if not tables:
+ tables = self.keys()
+ numTables = len(tables)
+ numGlyphs = self['maxp'].numGlyphs
+ if progress:
+ progress.set(0, numTables * numGlyphs)
+ for i in range(numTables):
+ tag = tables[i]
+ table = self[tag]
+ report = "Dumping '%s' table..." % tag
+ if progress:
+ progress.setlabel(report)
+ elif self.verbose:
+ debugmsg(report)
+ else:
+ print report
+ xmltag = tag2xmltag(tag)
+ writer.begintag(xmltag)
+ writer.newline()
+ if tag == "glyf":
+ table.toXML(writer, self, progress)
+ elif tag == "CFF ":
+ table.toXML(writer, self, progress)
+ else:
+ table.toXML(writer, self)
+ writer.endtag(xmltag)
+ writer.newline()
+ writer.newline()
+ if progress:
+ progress.set(i * numGlyphs, numTables * numGlyphs)
+ writer.endtag("ttFont")
+ writer.newline()
+ writer.close()
+ if self.verbose:
+ debugmsg("Done dumping XML")
+
+ def importXML(self, file, progress=None):
+ """Import an XML-based text file, so as to recreate
+ a font object.
+ """
+ if self.tables:
+ raise error, "Can't import XML into existing font."
+ import xmlImport
+ from xml.parsers.xmlproc import xmlproc
+ builder = xmlImport.XMLApplication(self, progress)
+ if progress:
+ progress.set(0, os.stat(file)[stat.ST_SIZE] / 100 or 1)
+ proc = xmlImport.UnicodeProcessor()
+ proc.set_application(builder)
+ proc.set_error_handler(xmlImport.XMLErrorHandler(proc))
+ dir, filename = os.path.split(file)
+ if dir:
+ olddir = os.getcwd()
+ os.chdir(dir)
+ try:
+ proc.parse_resource(filename)
+ root = builder.root
+ finally:
+ if dir:
+ os.chdir(olddir)
+ # remove circular references
+ proc.deref()
+ del builder.progress
+
+ def isLoaded(self, tag):
+ """Return true if the table identified by 'tag' has been
+ decompiled and loaded into memory."""
+ return self.tables.has_key(tag)
+
+ def has_key(self, tag):
+ """Pretend we're a dictionary."""
+ if self.isLoaded(tag):
+ return 1
+ elif self.reader and self.reader.has_key(tag):
+ return 1
+ else:
+ return 0
+
+ def keys(self):
+ """Pretend we're a dictionary."""
+ keys = self.tables.keys()
+ if self.reader:
+ for key in self.reader.keys():
+ if key not in keys:
+ keys.append(key)
+ keys.sort()
+ return keys
+
+ def __len__(self):
+ """Pretend we're a dictionary."""
+ return len(self.keys())
+
+ def __getitem__(self, tag):
+ """Pretend we're a dictionary."""
+ try:
+ return self.tables[tag]
+ except KeyError:
+ if self.reader is not None:
+ if self.verbose:
+ debugmsg("reading '%s' table from disk" % tag)
+ data = self.reader[tag]
+ tableclass = getTableClass(tag)
+ table = tableclass(tag)
+ self.tables[tag] = table
+ if self.verbose:
+ debugmsg("decompiling '%s' table" % tag)
+ table.decompile(data, self)
+ return table
+ else:
+ raise KeyError, "'%s' table not found" % tag
+
+ def __setitem__(self, tag, table):
+ """Pretend we're a dictionary."""
+ self.tables[tag] = table
+
+ def __delitem__(self, tag):
+ """Pretend we're a dictionary."""
+ del self.tables[tag]
+
+ def setGlyphOrder(self, glyphOrder):
+ self.glyphOrder = glyphOrder
+ if self.has_key('CFF '):
+ self['CFF '].setGlyphOrder(glyphOrder)
+ if self.has_key('glyf'):
+ self['glyf'].setGlyphOrder(glyphOrder)
+
+ def getGlyphOrder(self):
+ if not hasattr(self, "glyphOrder"):
+ if self.has_key('CFF '):
+ # CFF OpenType font
+ self.glyphOrder = self['CFF '].getGlyphOrder()
+ else:
+ # TrueType font
+ glyphOrder = self['post'].getGlyphOrder()
+ if glyphOrder is None:
+ #
+ # No names found in the 'post' table.
+ # Try to create glyph names from the unicode cmap (if available)
+ # in combination with the Adobe Glyph List (AGL).
+ #
+ self._getGlyphNamesFromCmap()
+ else:
+ self.glyphOrder = glyphOrder
+ # XXX what if a font contains 'glyf'/'post' table *and* CFF?
+ return self.glyphOrder
+
+ def _getGlyphNamesFromCmap(self):
+ # Make up glyph names based on glyphID, which will be used
+ # in case we don't find a unicode cmap.
+ numGlyphs = int(self['maxp'].numGlyphs)
+ glyphOrder = [None] * numGlyphs
+ glyphOrder[0] = ".notdef"
+ for i in range(1, numGlyphs):
+ glyphOrder[i] = "glyph%.5d" % i
+ # Set the glyph order, so the cmap parser has something
+ # to work with
+ self.glyphOrder = glyphOrder
+ # Get the temporary cmap (based on the just invented names)
+ tempcmap = self['cmap'].getcmap(3, 1)
+ if tempcmap is not None:
+ # we have a unicode cmap
+ import agl, string
+ cmap = tempcmap.cmap
+ # create a reverse cmap dict
+ reversecmap = {}
+ for unicode, name in cmap.items():
+ reversecmap[name] = unicode
+ assert len(reversecmap) == len(cmap)
+ for i in range(numGlyphs):
+ tempName = glyphOrder[i]
+ if reversecmap.has_key(tempName):
+ unicode = reversecmap[tempName]
+ if agl.UV2AGL.has_key(unicode):
+ # get name from the Adobe Glyph List
+ glyphOrder[i] = agl.UV2AGL[unicode]
+ else:
+ # create uni<CODE> name
+ glyphOrder[i] = "uni" + string.upper(string.zfill(hex(unicode)[2:], 4))
+ # Delete the cmap table from the cache, so it can be
+ # parsed again with the right names.
+ del self.tables['cmap']
+ else:
+ pass # no unicode cmap available, stick with the invented names
+ self.glyphOrder = glyphOrder
+
+ def getGlyphNames(self):
+ """Get a list of glyph names, sorted alphabetically."""
+ glyphNames = self.getGlyphOrder()[:]
+ glyphNames.sort()
+ return glyphNames
+
+ def getGlyphNames2(self):
+ """Get a list of glyph names, sorted alphabetically, but not case sensitive."""
+ from fontTools.misc import textTools
+ return textTools.caselessSort(self.getGlyphOrder())
+
+ def getGlyphName(self, glyphID):
+ return self.getGlyphOrder()[glyphID]
+
+ def getGlyphID(self, glyphName):
+ if not hasattr(self, "_reverseGlyphOrderDict"):
+ self._buildReverseGlyphOrderDict()
+ glyphOrder = self.getGlyphOrder()
+ d = self._reverseGlyphOrderDict
+ if not d.has_key(glyphName):
+ if glyphName in glyphOrder:
+ self._buildReverseGlyphOrderDict()
+ return self.getGlyphID(glyphName)
+ else:
+ raise KeyError, glyphName
+ glyphID = d[glyphName]
+ if glyphName <> glyphOrder[glyphID]:
+ self._buildReverseGlyphOrderDict()
+ return self.getGlyphID(glyphName)
+ return glyphID
+
+ def _buildReverseGlyphOrderDict(self):
+ self._reverseGlyphOrderDict = d = {}
+ glyphOrder = self.getGlyphOrder()
+ for glyphID in range(len(glyphOrder)):
+ d[glyphOrder[glyphID]] = glyphID
+
+ def _writeTable(self, tag, writer, done):
+ """Internal helper function for self.save(). Keeps track of
+ inter-table dependencies.
+ """
+ if tag in done:
+ return
+ tableclass = getTableClass(tag)
+ for masterTable in tableclass.dependencies:
+ if masterTable not in done:
+ if self.has_key(masterTable):
+ self._writeTable(masterTable, writer, done)
+ else:
+ done.append(masterTable)
+ tabledata = self._getTableData(tag)
+ if self.verbose:
+ debugmsg("writing '%s' table to disk" % tag)
+ writer[tag] = tabledata
+ done.append(tag)
+
+ def _getTableData(self, tag):
+ """Internal helper function. Returns raw table data,
+ whether compiled or directly read from disk.
+ """
+ if self.isLoaded(tag):
+ if self.verbose:
+ debugmsg("compiling '%s' table" % tag)
+ return self.tables[tag].compile(self)
+ elif self.reader and self.reader.has_key(tag):
+ if self.verbose:
+ debugmsg("reading '%s' table from disk" % tag)
+ return self.reader[tag]
+ else:
+ raise KeyError, tag
+
+
+def _test_endianness():
+ """Test the endianness of the machine. This is crucial to know
+ since TrueType data is always big endian, even on little endian
+ machines. There are quite a few situations where we explicitly
+ need to swap some bytes.
+ """
+ import struct
+ data = struct.pack("h", 0x01)
+ if data == "\000\001":
+ return "big"
+ elif data == "\001\000":
+ return "little"
+ else:
+ assert 0, "endian confusion!"
+
+endian = _test_endianness()
+
+
+def getTableModule(tag):
+ """Fetch the packer/unpacker module for a table.
+ Return None when no module is found.
+ """
+ import imp
+ import tables
+ py_tag = tag2identifier(tag)
+ try:
+ f, path, kind = imp.find_module(py_tag, tables.__path__)
+ if f:
+ f.close()
+ except ImportError:
+ return None
+ else:
+ module = __import__("fontTools.ttLib.tables." + py_tag)
+ return getattr(tables, py_tag)
+
+
+def getTableClass(tag):
+ """Fetch the packer/unpacker class for a table.
+ Return None when no class is found.
+ """
+ module = getTableModule(tag)
+ if module is None:
+ from tables.DefaultTable import DefaultTable
+ return DefaultTable
+ py_tag = tag2identifier(tag)
+ tableclass = getattr(module, "table_" + py_tag)
+ return tableclass
+
+
+def newtable(tag):
+ """Return a new instance of a table."""
+ tableclass = getTableClass(tag)
+ return tableclass(tag)
+
+
+def _escapechar(c):
+ """Helper function for tag2identifier()"""
+ import re
+ if re.match("[a-z0-9]", c):
+ return "_" + c
+ elif re.match("[A-Z]", c):
+ return c + "_"
+ else:
+ return hex(ord(c))[2:]
+
+
+def tag2identifier(tag):
+ """Convert a table tag to a valid (but UGLY) python identifier,
+ as well as a filename that's guaranteed to be unique even on a
+ caseless file system. Each character is mapped to two characters.
+ Lowercase letters get an underscore before the letter, uppercase
+ letters get an underscore after the letter. Trailing spaces are
+ trimmed. Illegal characters are escaped as two hex bytes. If the
+ result starts with a number (as the result of a hex escape), an
+ extra underscore is prepended. Examples:
+ 'glyf' -> '_g_l_y_f'
+ 'cvt ' -> '_c_v_t'
+ 'OS/2' -> 'O_S_2f_2'
+ """
+ import re
+ assert len(tag) == 4, "tag should be 4 characters long"
+ while len(tag) > 1 and tag[-1] == ' ':
+ tag = tag[:-1]
+ ident = ""
+ for c in tag:
+ ident = ident + _escapechar(c)
+ if re.match("[0-9]", ident):
+ ident = "_" + ident
+ return ident
+
+
+def identifier2tag(ident):
+ """the opposite of tag2identifier()"""
+ import string
+ if len(ident) % 2 and ident[0] == "_":
+ ident = ident[1:]
+ assert not (len(ident) % 2)
+ tag = ""
+ for i in range(0, len(ident), 2):
+ if ident[i] == "_":
+ tag = tag + ident[i+1]
+ elif ident[i+1] == "_":
+ tag = tag + ident[i]
+ else:
+ # assume hex
+ tag = tag + chr(string.atoi(ident[i:i+2], 16))
+ # append trailing spaces
+ tag = tag + (4 - len(tag)) * ' '
+ return tag
+
+
+def tag2xmltag(tag):
+ """Similarly to tag2identifier(), this converts a TT tag
+ to a valid XML element name. Since XML element names are
+ case sensitive, this is a fairly simple/readable translation.
+ """
+ import string, re
+ if tag == "OS/2":
+ return "OS_2"
+ if re.match("[A-Za-z_][A-Za-z_0-9]* *$", tag):
+ return string.strip(tag)
+ else:
+ return tag2identifier(tag)
+
+
+def xmltag2tag(tag):
+ """The opposite of tag2xmltag()"""
+ if tag == "OS_2":
+ return "OS/2"
+ if len(tag) == 8:
+ return identifier2tag(tag)
+ else:
+ return tag + " " * (4 - len(tag))
+ return tag
+
+
+def debugmsg(msg):
+ import time
+ print msg + time.strftime(" (%H:%M:%S)", time.localtime(time.time()))
+
+
diff --git a/Lib/fontTools/ttLib/macUtils.py b/Lib/fontTools/ttLib/macUtils.py
new file mode 100644
index 0000000..bace3b4
--- /dev/null
+++ b/Lib/fontTools/ttLib/macUtils.py
@@ -0,0 +1,212 @@
+"""ttLib.macUtils.py -- Various Mac-specific stuff."""
+
+
+import os
+if os.name <> "mac":
+ raise ImportError, "This module is Mac-only!"
+
+import Res, macfs
+import cStringIO
+
+
+def getSFNTResIndices(path):
+ """Determine whether a file has a resource fork or not."""
+ fss = macfs.FSSpec(path)
+ try:
+ resref = Res.FSpOpenResFile(fss, 1) # read only
+ except Res.Error:
+ return []
+ Res.UseResFile(resref)
+ numSFNTs = Res.Count1Resources('sfnt')
+ Res.CloseResFile(resref)
+ return range(1, numSFNTs + 1)
+
+
+def openTTFonts(path):
+ """Given a pathname, return a list of TTFont objects. In the case
+ of a flat TTF/OTF file, the list will contain just one font object;
+ but in the case of a Mac font suitcase it will contain as many
+ font objects as there are sfnt resources in the file.
+ """
+ from fontTools import ttLib
+ fonts = []
+ sfnts = getSFNTResIndices(path)
+ if not sfnts:
+ fonts.append(ttLib.TTFont(path))
+ else:
+ for index in sfnts:
+ fonts.append(ttLib.TTFont(path, index))
+ if not fonts:
+ raise ttLib.TTLibError, "no fonts found in file '%s'" % path
+ return fonts
+
+
+class ProgressBar:
+
+ def __init__(self, title, maxval=100):
+ import EasyDialogs
+ self.bar = EasyDialogs.ProgressBar(title, maxval=maxval)
+
+ def set(self, val, maxval=None):
+ if maxval <> None:
+ self.bar.maxval = maxval
+ self.bar.set(val)
+
+ def increment(self, val=1):
+ self.bar.inc(val)
+
+ def setlabel(self, text):
+ self.bar.label(text)
+
+ def close(self):
+ self.bar.d.HideWindow()
+ del self.bar
+
+
+class SFNTResourceReader:
+
+ """Simple (Mac-only) read-only file wrapper for 'sfnt' resources."""
+
+ def __init__(self, path, res_name_or_index):
+ fss = macfs.FSSpec(path)
+ resref = Res.FSpOpenResFile(fss, 1) # read-only
+ Res.UseResFile(resref)
+ if type(res_name_or_index) == type(""):
+ res = Res.Get1NamedResource('sfnt', res_name_or_index)
+ else:
+ res = Res.Get1IndResource('sfnt', res_name_or_index)
+ self.file = cStringIO.StringIO(res.data)
+ Res.CloseResFile(resref)
+ self.name = path
+
+ def __getattr__(self, attr):
+ # cheap inheritance
+ return getattr(self.file, attr)
+
+
+class SFNTResourceWriter:
+
+ """Simple (Mac-only) file wrapper for 'sfnt' resources."""
+
+ def __init__(self, path, ttFont, res_id=None):
+ self.file = cStringIO.StringIO()
+ self.name = path
+ self.closed = 0
+ fullname = ttFont['name'].getname(4, 1, 0) # Full name, mac, default encoding
+ familyname = ttFont['name'].getname(1, 1, 0) # Fam. name, mac, default encoding
+ psname = ttFont['name'].getname(6, 1, 0) # PostScript name, etc.
+ if fullname is None or fullname is None or psname is None:
+ from fontTools import ttLib
+ raise ttLib.TTLibError, "can't make 'sfnt' resource, no Macintosh 'name' table found"
+ self.fullname = fullname.string
+ self.familyname = familyname.string
+ self.psname = psname.string
+ if self.familyname <> self.psname[:len(self.familyname)]:
+ # ugh. force fam name to be the same as first part of ps name,
+ # fondLib otherwise barfs.
+ for i in range(min(len(self.psname), len(self.familyname))):
+ if self.familyname[i] <> self.psname[i]:
+ break
+ self.familyname = self.psname[:i]
+
+ self.ttFont = ttFont
+ self.res_id = res_id
+ fss = macfs.FSSpec(self.name)
+ if os.path.exists(self.name):
+ os.remove(self.name)
+ Res.FSpCreateResFile(fss, 'DMOV', 'FFIL', 0)
+ self.resref = Res.FSpOpenResFile(fss, 3) # exclusive read/write permission
+
+ def close(self):
+ if self.closed:
+ return
+ Res.UseResFile(self.resref)
+ try:
+ res = Res.Get1NamedResource('sfnt', self.fullname)
+ except Res.Error:
+ pass
+ else:
+ res.RemoveResource()
+ res = Res.Resource(self.file.getvalue())
+ if self.res_id is None:
+ self.res_id = Res.Unique1ID('sfnt')
+ res.AddResource('sfnt', self.res_id, self.fullname)
+ res.ChangedResource()
+
+ self.createFond()
+ del self.ttFont
+ Res.CloseResFile(self.resref)
+ self.file.close()
+ self.closed = 1
+
+ def createFond(self):
+ fond_res = Res.Resource("")
+ fond_res.AddResource('FOND', self.res_id, self.fullname)
+
+ from fontTools import fondLib
+ fond = fondLib.FontFamily(fond_res, "w")
+
+ fond.ffFirstChar = 0
+ fond.ffLastChar = 255
+ fond.fondClass = 0
+ fond.fontAssoc = [(0, 0, self.res_id)]
+ fond.ffFlags = 20480 # XXX ???
+ fond.ffIntl = (0, 0)
+ fond.ffLeading = 0
+ fond.ffProperty = (0, 0, 0, 0, 0, 0, 0, 0, 0)
+ fond.ffVersion = 0
+ fond.glyphEncoding = {}
+ if self.familyname == self.psname:
+ fond.styleIndices = (1,) * 48 # uh-oh, fondLib is too dumb.
+ else:
+ fond.styleIndices = (2,) * 48
+ fond.styleStrings = []
+ fond.boundingBoxes = None
+ fond.ffFamID = self.res_id
+ fond.changed = 1
+ fond.glyphTableOffset = 0
+ fond.styleMappingReserved = 0
+
+ # calc:
+ scale = 4096.0 / self.ttFont['head'].unitsPerEm
+ fond.ffAscent = scale * self.ttFont['hhea'].ascent
+ fond.ffDescent = scale * self.ttFont['hhea'].descent
+ fond.ffWidMax = scale * self.ttFont['hhea'].advanceWidthMax
+
+ fond.ffFamilyName = self.familyname
+ fond.psNames = {0: self.psname}
+
+ fond.widthTables = {}
+ fond.kernTables = {}
+ cmap = self.ttFont['cmap'].getcmap(1, 0)
+ if cmap:
+ names = {}
+ for code, name in cmap.cmap.items():
+ names[name] = code
+ if self.ttFont.has_key('kern'):
+ kern = self.ttFont['kern'].getkern(0)
+ if kern:
+ fondkerning = []
+ for (left, right), value in kern.kernTable.items():
+ if names.has_key(left) and names.has_key(right):
+ fondkerning.append((names[left], names[right], scale * value))
+ fondkerning.sort()
+ fond.kernTables = {0: fondkerning}
+ if self.ttFont.has_key('hmtx'):
+ hmtx = self.ttFont['hmtx']
+ fondwidths = [2048] * 256 + [0, 0] # default width, + plus two zeros.
+ for name, (width, lsb) in hmtx.metrics.items():
+ if names.has_key(name):
+ fondwidths[names[name]] = scale * width
+ fond.widthTables = {0: fondwidths}
+ fond.save()
+
+ def __del__(self):
+ if not self.closed:
+ self.close()
+
+ def __getattr__(self, attr):
+ # cheap inheritance
+ return getattr(self.file, attr)
+
+
diff --git a/Lib/fontTools/ttLib/sfnt.py b/Lib/fontTools/ttLib/sfnt.py
new file mode 100644
index 0000000..2fdeb99
--- /dev/null
+++ b/Lib/fontTools/ttLib/sfnt.py
@@ -0,0 +1,230 @@
+"""ttLib/sfnt.py -- low-level module to deal with the sfnt file format.
+
+Defines two public classes:
+ SFNTReader
+ SFNTWriter
+
+(Normally you don't have to use these classes explicitly; they are
+used automatically by ttLib.TTFont.)
+
+The reading and writing of sfnt files is separated in two distinct
+classes, since whenever to number of tables changes or whenever
+a table's length chages you need to rewrite the whole file anyway.
+"""
+
+import struct, sstruct
+import Numeric
+import os
+
+class SFNTReader:
+
+ def __init__(self, file, checkchecksums=1):
+ self.file = file
+ self.checkchecksums = checkchecksums
+ data = self.file.read(sfntDirectorySize)
+ if len(data) <> sfntDirectorySize:
+ from fontTools import ttLib
+ raise ttLib.TTLibError, "Not a TrueType or OpenType font (not enough data)"
+ sstruct.unpack(sfntDirectoryFormat, data, self)
+ if self.sfntVersion not in ("\000\001\000\000", "OTTO", "true"):
+ from fontTools import ttLib
+ raise ttLib.TTLibError, "Not a TrueType or OpenType font (bad sfntVersion)"
+ self.tables = {}
+ for i in range(self.numTables):
+ entry = SFNTDirectoryEntry()
+ entry.fromfile(self.file)
+ self.tables[entry.tag] = entry
+
+ def has_key(self, tag):
+ return self.tables.has_key(tag)
+
+ def keys(self):
+ return self.tables.keys()
+
+ def __getitem__(self, tag):
+ """Fetch the raw table data."""
+ entry = self.tables[tag]
+ self.file.seek(entry.offset)
+ data = self.file.read(entry.length)
+ if self.checkchecksums:
+ if tag == 'head':
+ # Beh: we have to special-case the 'head' table.
+ checksum = calcchecksum(data[:8] + '\0\0\0\0' + data[12:])
+ else:
+ checksum = calcchecksum(data)
+ if self.checkchecksums > 1:
+ # Be obnoxious, and barf when it's wrong
+ assert checksum == entry.checksum, "bad checksum for '%s' table" % tag
+ elif checksum <> entry.checkSum:
+ # Be friendly, and just print a warning.
+ print "bad checksum for '%s' table" % tag
+ return data
+
+ def close(self):
+ self.file.close()
+
+
+class SFNTWriter:
+
+ def __init__(self, file, numTables, sfntVersion="\000\001\000\000"):
+ self.file = file
+ self.numTables = numTables
+ self.sfntVersion = sfntVersion
+ self.searchRange, self.entrySelector, self.rangeShift = getsearchrange(numTables)
+ self.nextTableOffset = sfntDirectorySize + numTables * sfntDirectoryEntrySize
+ # clear out directory area
+ self.file.seek(self.nextTableOffset)
+ # make sure we're actually where we want to be. (XXX old cStringIO bug)
+ self.file.write('\0' * (self.nextTableOffset - self.file.tell()))
+ self.tables = {}
+
+ def __setitem__(self, tag, data):
+ """Write raw table data to disk."""
+ if self.tables.has_key(tag):
+ # We've written this table to file before. If the length
+ # of the data is still the same, we allow overwritng it.
+ entry = self.tables[tag]
+ if len(data) <> entry.length:
+ from fontTools import ttLib
+ raise ttLib.TTLibError, "cannot rewrite '%s' table: length does not match directory entry" % tag
+ else:
+ entry = SFNTDirectoryEntry()
+ entry.tag = tag
+ entry.offset = self.nextTableOffset
+ entry.length = len(data)
+ self.nextTableOffset = self.nextTableOffset + ((len(data) + 3) & ~3)
+ self.file.seek(entry.offset)
+ self.file.write(data)
+ self.file.seek(self.nextTableOffset)
+ # make sure we're actually where we want to be. (XXX old cStringIO bug)
+ self.file.write('\0' * (self.nextTableOffset - self.file.tell()))
+
+ if tag == 'head':
+ entry.checkSum = calcchecksum(data[:8] + '\0\0\0\0' + data[12:])
+ else:
+ entry.checkSum = calcchecksum(data)
+ self.tables[tag] = entry
+
+ def close(self):
+ """All tables must have been written to disk. Now write the
+ directory.
+ """
+ tables = self.tables.items()
+ tables.sort()
+ if len(tables) <> self.numTables:
+ from fontTools import ttLib
+ raise ttLib.TTLibError, "wrong number of tables; expected %d, found %d" % (self.numTables, len(tables))
+
+ directory = sstruct.pack(sfntDirectoryFormat, self)
+
+ self.file.seek(sfntDirectorySize)
+ for tag, entry in tables:
+ directory = directory + entry.tostring()
+ self.calcmasterchecksum(directory)
+ self.file.seek(0)
+ self.file.write(directory)
+ self.file.close()
+
+ def calcmasterchecksum(self, directory):
+ # calculate checkSumAdjustment
+ tags = self.tables.keys()
+ checksums = Numeric.zeros(len(tags)+1)
+ for i in range(len(tags)):
+ checksums[i] = self.tables[tags[i]].checkSum
+
+ directory_end = sfntDirectorySize + len(self.tables) * sfntDirectoryEntrySize
+ assert directory_end == len(directory)
+
+ checksums[-1] = calcchecksum(directory)
+ checksum = Numeric.add.reduce(checksums)
+ # BiboAfba!
+ checksumadjustment = Numeric.array(0xb1b0afba) - checksum
+ # write the checksum to the file
+ self.file.seek(self.tables['head'].offset + 8)
+ self.file.write(struct.pack("l", checksumadjustment))
+
+
+# -- sfnt directory helpers and cruft
+
+sfntDirectoryFormat = """
+ > # big endian
+ sfntVersion: 4s
+ numTables: H # number of tables
+ searchRange: H # (max2 <= numTables)*16
+ entrySelector: H # log2(max2 <= numTables)
+ rangeShift: H # numTables*16-searchRange
+"""
+
+sfntDirectorySize = sstruct.calcsize(sfntDirectoryFormat)
+
+sfntDirectoryEntryFormat = """
+ > # big endian
+ tag: 4s
+ checkSum: l
+ offset: l
+ length: l
+"""
+
+sfntDirectoryEntrySize = sstruct.calcsize(sfntDirectoryEntryFormat)
+
+class SFNTDirectoryEntry:
+
+ def fromfile(self, file):
+ sstruct.unpack(sfntDirectoryEntryFormat,
+ file.read(sfntDirectoryEntrySize), self)
+
+ def fromstring(self, str):
+ sstruct.unpack(sfntDirectoryEntryFormat, str, self)
+
+ def tostring(self):
+ return sstruct.pack(sfntDirectoryEntryFormat, self)
+
+ def __repr__(self):
+ if hasattr(self, "tag"):
+ return "<SFNTDirectoryEntry '%s' at %x>" % (self.tag, id(self))
+ else:
+ return "<SFNTDirectoryEntry at %x>" % id(self)
+
+
+def calcchecksum(data, start=0):
+ """Calculate the checksum for an arbitrary block of data.
+ Optionally takes a 'start' argument, which allows you to
+ calculate a checksum in chunks by feeding it a previous
+ result.
+
+ If the data length is not a multiple of four, it assumes
+ it is to be padded with null byte.
+ """
+ from fontTools import ttLib
+ remainder = len(data) % 4
+ if remainder:
+ data = data + '\0' * (4-remainder)
+ a = Numeric.fromstring(struct.pack(">l", start) + data, Numeric.Int32)
+ if ttLib.endian <> "big":
+ a = a.byteswapped()
+ return Numeric.add.reduce(a)
+
+
+def maxpoweroftwo(x):
+ """Return the highest exponent of two, so that
+ (2 ** exponent) <= x
+ """
+ exponent = 0
+ while x:
+ x = x >> 1
+ exponent = exponent + 1
+ return exponent - 1
+
+
+def getsearchrange(n):
+ """Calculate searchRange, entrySelector, rangeShift for the
+ sfnt directory. 'n' is the number of tables.
+ """
+ # This stuff needs to be stored in the file, because?
+ import math
+ exponent = maxpoweroftwo(n)
+ searchRange = (2 ** exponent) * 16
+ entrySelector = exponent
+ rangeShift = n * 16 - searchRange
+ return searchRange, entrySelector, rangeShift
+
diff --git a/Lib/fontTools/ttLib/standardGlyphOrder.py b/Lib/fontTools/ttLib/standardGlyphOrder.py
new file mode 100644
index 0000000..4ef2eb4
--- /dev/null
+++ b/Lib/fontTools/ttLib/standardGlyphOrder.py
@@ -0,0 +1,263 @@
+# XXX is this *really* correct?
+
+standardGlyphOrder = [
+ ".notdef", # 0
+ ".null", # 1
+ "nonmarkingreturn", # 2
+ "space", # 3
+ "exclam", # 4
+ "quotedbl", # 5
+ "numbersign", # 6
+ "dollar", # 7
+ "percent", # 8
+ "ampersand", # 9
+ "quotesingle", # 10
+ "parenleft", # 11
+ "parenright", # 12
+ "asterisk", # 13
+ "plus", # 14
+ "comma", # 15
+ "hyphen", # 16
+ "period", # 17
+ "slash", # 18
+ "zero", # 19
+ "one", # 20
+ "two", # 21
+ "three", # 22
+ "four", # 23
+ "five", # 24
+ "six", # 25
+ "seven", # 26
+ "eight", # 27
+ "nine", # 28
+ "colon", # 29
+ "semicolon", # 30
+ "less", # 31
+ "equal", # 32
+ "greater", # 33
+ "question", # 34
+ "at", # 35
+ "A", # 36
+ "B", # 37
+ "C", # 38
+ "D", # 39
+ "E", # 40
+ "F", # 41
+ "G", # 42
+ "H", # 43
+ "I", # 44
+ "J", # 45
+ "K", # 46
+ "L", # 47
+ "M", # 48
+ "N", # 49
+ "O", # 50
+ "P", # 51
+ "Q", # 52
+ "R", # 53
+ "S", # 54
+ "T", # 55
+ "U", # 56
+ "V", # 57
+ "W", # 58
+ "X", # 59
+ "Y", # 60
+ "Z", # 61
+ "bracketleft", # 62
+ "backslash", # 63
+ "bracketright", # 64
+ "asciicircum", # 65
+ "underscore", # 66
+ "grave", # 67
+ "a", # 68
+ "b", # 69
+ "c", # 70
+ "d", # 71
+ "e", # 72
+ "f", # 73
+ "g", # 74
+ "h", # 75
+ "i", # 76
+ "j", # 77
+ "k", # 78
+ "l", # 79
+ "m", # 80
+ "n", # 81
+ "o", # 82
+ "p", # 83
+ "q", # 84
+ "r", # 85
+ "s", # 86
+ "t", # 87
+ "u", # 88
+ "v", # 89
+ "w", # 90
+ "x", # 91
+ "y", # 92
+ "z", # 93
+ "braceleft", # 94
+ "bar", # 95
+ "braceright", # 96
+ "asciitilde", # 97
+ "Adieresis", # 98
+ "Aring", # 99
+ "Ccedilla", # 100
+ "Eacute", # 101
+ "Ntilde", # 102
+ "Odieresis", # 103
+ "Udieresis", # 104
+ "aacute", # 105
+ "agrave", # 106
+ "acircumflex", # 107
+ "adieresis", # 108
+ "atilde", # 109
+ "aring", # 110
+ "ccedilla", # 111
+ "eacute", # 112
+ "egrave", # 113
+ "ecircumflex", # 114
+ "edieresis", # 115
+ "iacute", # 116
+ "igrave", # 117
+ "icircumflex", # 118
+ "idieresis", # 119
+ "ntilde", # 120
+ "oacute", # 121
+ "ograve", # 122
+ "ocircumflex", # 123
+ "odieresis", # 124
+ "otilde", # 125
+ "uacute", # 126
+ "ugrave", # 127
+ "ucircumflex", # 128
+ "udieresis", # 129
+ "dagger", # 130
+ "degree", # 131
+ "cent", # 132
+ "sterling", # 133
+ "section", # 134
+ "bullet", # 135
+ "paragraph", # 136
+ "germandbls", # 137
+ "registered", # 138
+ "copyright", # 139
+ "trademark", # 140
+ "acute", # 141
+ "dieresis", # 142
+ "notequal", # 143
+ "AE", # 144
+ "Oslash", # 145
+ "infinity", # 146
+ "plusminus", # 147
+ "lessequal", # 148
+ "greaterequal", # 149
+ "yen", # 150
+ "mu", # 151
+ "partialdiff", # 152
+ "summation", # 153
+ "product", # 154
+ "pi", # 155
+ "integral", # 156
+ "ordfeminine", # 157
+ "ordmasculine", # 158
+ "Omega", # 159
+ "ae", # 160
+ "oslash", # 161
+ "questiondown", # 162
+ "exclamdown", # 163
+ "logicalnot", # 164
+ "radical", # 165
+ "florin", # 166
+ "approxequal", # 167
+ "Delta", # 168
+ "guillemotleft", # 169
+ "guillemotright", # 170
+ "ellipsis", # 171
+ "nonbreakingspace", # 172
+ "Agrave", # 173
+ "Atilde", # 174
+ "Otilde", # 175
+ "OE", # 176
+ "oe", # 177
+ "endash", # 178
+ "emdash", # 179
+ "quotedblleft", # 180
+ "quotedblright", # 181
+ "quoteleft", # 182
+ "quoteright", # 183
+ "divide", # 184
+ "lozenge", # 185
+ "ydieresis", # 186
+ "Ydieresis", # 187
+ "fraction", # 188
+ "currency", # 189
+ "guilsinglleft", # 190
+ "guilsinglright", # 191
+ "fi", # 192
+ "fl", # 193
+ "daggerdbl", # 194
+ "periodcentered", # 195
+ "quotesinglbase", # 196
+ "quotedblbase", # 197
+ "perthousand", # 198
+ "Acircumflex", # 199
+ "Ecircumflex", # 200
+ "Aacute", # 201
+ "Edieresis", # 202
+ "Egrave", # 203
+ "Iacute", # 204
+ "Icircumflex", # 205
+ "Idieresis", # 206
+ "Igrave", # 207
+ "Oacute", # 208
+ "Ocircumflex", # 209
+ "apple", # 210
+ "Ograve", # 211
+ "Uacute", # 212
+ "Ucircumflex", # 213
+ "Ugrave", # 214
+ "dotlessi", # 215
+ "circumflex", # 216
+ "tilde", # 217
+ "macron", # 218
+ "breve", # 219
+ "dotaccent", # 220
+ "ring", # 221
+ "cedilla", # 222
+ "hungarumlaut", # 223
+ "ogonek", # 224
+ "caron", # 225
+ "Lslash", # 226
+ "lslash", # 227
+ "Scaron", # 228
+ "scaron", # 229
+ "Zcaron", # 230
+ "zcaron", # 231
+ "brokenbar", # 232
+ "Eth", # 233
+ "eth", # 234
+ "Yacute", # 235
+ "yacute", # 236
+ "Thorn", # 237
+ "thorn", # 238
+ "minus", # 239
+ "multiply", # 240
+ "onesuperior", # 241
+ "twosuperior", # 242
+ "threesuperior", # 243
+ "onehalf", # 244
+ "onequarter", # 245
+ "threequarters", # 246
+ "franc", # 247
+ "Gbreve", # 248
+ "gbreve", # 249
+ "Idotaccent", # 250
+ "Scedilla", # 251
+ "scedilla", # 252
+ "Cacute", # 253
+ "cacute", # 254
+ "Ccaron", # 255
+ "ccaron", # 256
+ "dcroat" # 257
+]
+
diff --git a/Lib/fontTools/ttLib/tables/C_F_F_.py b/Lib/fontTools/ttLib/tables/C_F_F_.py
new file mode 100644
index 0000000..8148f06
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/C_F_F_.py
@@ -0,0 +1,35 @@
+import DefaultTable
+from fontTools import cffLib
+
+
+class table_C_F_F_(DefaultTable.DefaultTable, cffLib.CFFFontSet):
+
+ def __init__(self, tag):
+ DefaultTable.DefaultTable.__init__(self, tag)
+ cffLib.CFFFontSet.__init__(self)
+ self._gaveGlyphOrder = 0
+
+ def decompile(self, data, otFont):
+ self.data = data # XXX while work is in progress...
+ cffLib.CFFFontSet.decompile(self, data)
+ assert len(self.fonts) == 1, "can't deal with multi-font CFF tables."
+
+ #def compile(self, otFont):
+ # xxx
+
+ def getGlyphOrder(self):
+ if self._gaveGlyphOrder:
+ from fontTools import ttLib
+ raise ttLib.TTLibError, "illegal use of getGlyphOrder()"
+ self._gaveGlyphOrder = 1
+ return self.fonts[self.fontNames[0]].getGlyphOrder()
+
+ def setGlyphOrder(self, glyphOrder):
+ self.fonts[self.fontNames[0]].setGlyphOrder(glyphOrder)
+
+ def toXML(self, writer, otFont, progress=None):
+ cffLib.CFFFontSet.toXML(self, writer, progress)
+
+ #def fromXML(self, (name, attrs, content), otFont):
+ # xxx
+
diff --git a/Lib/fontTools/ttLib/tables/D_S_I_G_.py b/Lib/fontTools/ttLib/tables/D_S_I_G_.py
new file mode 100644
index 0000000..bea5654
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/D_S_I_G_.py
@@ -0,0 +1,12 @@
+import DefaultTable
+
+class table_D_S_I_G_(DefaultTable.DefaultTable):
+
+ def toXML(self, xmlWriter, ttFont):
+ xmlWriter.comment("note that the Digital Signature will be invalid after recompilation!")
+ xmlWriter.newline()
+ xmlWriter.begintag("hexdata")
+ xmlWriter.newline()
+ xmlWriter.dumphex(self.compile(ttFont))
+ xmlWriter.endtag("hexdata")
+ xmlWriter.newline()
diff --git a/Lib/fontTools/ttLib/tables/DefaultTable.py b/Lib/fontTools/ttLib/tables/DefaultTable.py
new file mode 100644
index 0000000..745f237
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/DefaultTable.py
@@ -0,0 +1,36 @@
+import string
+import sys
+
+class DefaultTable:
+
+ dependencies = []
+
+ def __init__(self, tag):
+ self.tableTag = tag
+
+ def decompile(self, data, ttFont):
+ self.data = data
+
+ def compile(self, ttFont):
+ return self.data
+
+ def toXML(self, writer, ttFont):
+ writer.begintag("hexdata")
+ writer.newline()
+ writer.dumphex(self.compile(ttFont))
+ writer.endtag("hexdata")
+ writer.newline()
+
+ def fromXML(self, (name, attrs, content), ttFont):
+ from fontTools.misc.textTools import readHex
+ from fontTools import ttLib
+ if name <> "hexdata":
+ raise ttLib.TTLibError, "can't handle '%s' element" % name
+ self.decompile(readHex(content), ttFont)
+
+ def __repr__(self):
+ return "<'%s' table at %x>" % (self.tableTag, id(self))
+
+ def __cmp__(self, other):
+ return cmp(self.__dict__, other.__dict__)
+
diff --git a/Lib/fontTools/ttLib/tables/G_P_O_S_.py b/Lib/fontTools/ttLib/tables/G_P_O_S_.py
new file mode 100644
index 0000000..7bec887
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/G_P_O_S_.py
@@ -0,0 +1,294 @@
+import otCommon
+
+
+class table_G_P_O_S_(otCommon.base_GPOS_GSUB):
+
+ def getLookupTypeClass(self, lookupType):
+ return lookupTypeClasses[lookupType]
+
+
+class SinglePos:
+
+ def decompile(self, reader, otFont):
+ pass
+
+ def compile(self, otFont):
+ xxx
+
+ def toXML(self, xmlWriter, otFont):
+ xmlWriter.comment("XXX")
+ xmlWriter.newline()
+
+ def fromXML(self, (name, attrs, content), otFont):
+ xxx
+
+
+class PairPos:
+
+ def decompile(self, reader, otFont):
+ self.format = reader.readUShort()
+ if self.format == 1:
+ self.decompileFormat1(reader, otFont)
+ elif self.format == 2:
+ self.decompileFormat2(reader, otFont)
+ else:
+ from fontTools import ttLib
+ raise ttLib.TTLibError, "unknown PairPos format: %d" % self.format
+
+ def decompileFormat1(self, reader, otFont):
+ coverage = reader.readTable(otCommon.CoverageTable, otFont)
+ glyphNames = coverage.glyphNames
+ valueFactory1 = ValueRecordFactory(reader.readUShort())
+ valueFactory2 = ValueRecordFactory(reader.readUShort())
+ self.pairs = pairs = {}
+ for i in range(reader.readUShort()):
+ firstGlyphName = glyphNames[i]
+ offset = reader.readOffset()
+ setData = reader.getSubString(offset)
+ set = PairSet()
+ set.decompile(setData, otFont, valueFactory1, valueFactory2)
+ pairs[firstGlyphName] = set.values
+
+ def decompileFormat2(self, reader, otFont):
+ coverage = reader.readTable(otCommon.CoverageTable, otFont)
+ glyphNames = coverage.glyphNames
+ valueFactory1 = ValueRecordFactory(reader.readUShort())
+ valueFactory2 = ValueRecordFactory(reader.readUShort())
+ self.classDef1 = reader.readTable(otCommon.ClassDefinitionTable, otFont)
+ self.classDef2 = reader.readTable(otCommon.ClassDefinitionTable, otFont)
+ class1Count = reader.readUShort()
+ class2Count = reader.readUShort()
+ self.pairs = pairs = {} # sparse matrix
+ for i in range(class1Count):
+ row = {}
+ for j in range(class2Count):
+ value1 = valueFactory1.getValueRecord(reader)
+ value2 = valueFactory2.getValueRecord(reader)
+ if value1 or value2:
+ row[j] = (value1, value2)
+ if row:
+ pairs[i] = row
+
+ def compile(self, otFont):
+ xxx
+
+ def toXML(self, xmlWriter, otFont):
+ if self.format == 1:
+ self.toXMLFormat1(xmlWriter, otFont)
+ elif self.format == 2:
+ self.toXMLFormat2(xmlWriter, otFont)
+ else:
+ from fontTools import ttLib
+ raise ttLib.TTLibError, "unknown PairPos format: %d" % self.format
+
+ def toXMLFormat1(self, xmlWriter, otFont):
+ pairs = self.pairs.items()
+ pairs.sort()
+ for firstGlyph, secondGlyphs in pairs:
+ for secondGlyph, value1, value2 in secondGlyphs:
+ #XXXXXXXXX
+ xmlWriter.begintag("Pair", first=firstGlyph, second=secondGlyph)
+ xmlWriter.newline()
+ if value1:
+ value1.toXML(xmlWriter, otFont)
+ if value2:
+ value2.toXML(xmlWriter, otFont)
+ xmlWriter.endtag("Pair")
+ xmlWriter.newline()
+
+ def toXMLFormat2(self, xmlWriter, otFont):
+ xmlWriter.comment("XXX")
+ xmlWriter.newline()
+
+ def fromXML(self, (name, attrs, content), otFont):
+ xxx
+
+
+class PairSet:
+
+ def decompile(self, reader, otFont, valueFactory1, valueFactory2):
+ pairValueCount = reader.readUShort()
+ self.values = values = []
+ for j in range(pairValueCount):
+ secondGlyphID = reader.readUShort()
+ secondGlyphName = otFont.getGlyphName(secondGlyphID)
+ value1 = valueFactory1.getValueRecord(reader)
+ value2 = valueFactory2.getValueRecord(reader)
+ values.append((secondGlyphName, value1, value2))
+
+ def compile(self, otFont):
+ xxx
+
+#
+# ------------------
+#
+
+class CursivePos:
+
+ def decompile(self, reader, otFont):
+ pass
+
+ def compile(self, otFont):
+ xxx
+
+ def toXML(self, xmlWriter, otFont):
+ xmlWriter.comment("XXX")
+ xmlWriter.newline()
+
+
+class MarkBasePos:
+
+ def decompile(self, reader, otFont):
+ pass
+
+ def compile(self, otFont):
+ xxx
+
+ def toXML(self, xmlWriter, otFont):
+ xmlWriter.comment("XXX")
+ xmlWriter.newline()
+
+
+class MarkLigPos:
+
+ def decompile(self, reader, otFont):
+ pass
+
+ def compile(self, otFont):
+ xxx
+
+ def toXML(self, xmlWriter, otFont):
+ xmlWriter.comment("XXX")
+ xmlWriter.newline()
+
+
+class MarkMarkPos:
+
+ def decompile(self, reader, otFont):
+ pass
+
+ def compile(self, otFont):
+ xxx
+
+ def toXML(self, xmlWriter, otFont):
+ xmlWriter.comment("XXX")
+ xmlWriter.newline()
+
+
+class ContextPos:
+
+ def decompile(self, reader, otFont):
+ pass
+
+ def compile(self, otFont):
+ xxx
+
+ def toXML(self, xmlWriter, otFont):
+ xmlWriter.comment("XXX")
+ xmlWriter.newline()
+
+
+class ChainContextPos:
+
+ def decompile(self, reader, otFont):
+ pass
+
+ def compile(self, otFont):
+ xxx
+
+ def toXML(self, xmlWriter, otFont):
+ xmlWriter.comment("XXX")
+ xmlWriter.newline()
+
+
+lookupTypeClasses = {
+ 1: SinglePos,
+ 2: PairPos,
+ 3: CursivePos,
+ 4: MarkBasePos,
+ 5: MarkLigPos,
+ 6: MarkMarkPos,
+ 7: ContextPos,
+ 8: ChainContextPos,
+}
+
+
+valueRecordFormat = [
+# Mask Name struct format char
+ (0x0001, "XPlacement", "h"),
+ (0x0002, "YPlacement", "h"),
+ (0x0004, "XAdvance", "h"),
+ (0x0008, "YAdvance", "h"),
+ (0x0010, "XPlaDevice", "H"),
+ (0x0020, "YPlaDevice", "H"),
+ (0x0040, "XAdvDevice", "H"),
+ (0x0080, "YAdvDevice", "H"),
+# reserved:
+ (0x0100, "Reserved1", "H"),
+ (0x0200, "Reserved2", "H"),
+ (0x0400, "Reserved3", "H"),
+ (0x0800, "Reserved4", "H"),
+ (0x1000, "Reserved5", "H"),
+ (0x2000, "Reserved6", "H"),
+ (0x4000, "Reserved7", "H"),
+ (0x8000, "Reserved8", "H"),
+]
+
+
+class ValueRecordFactory:
+
+ def __init__(self, valueFormat):
+ format = ">"
+ names = []
+ for mask, name, formatChar in valueRecordFormat:
+ if valueFormat & mask:
+ names.append(name)
+ format = format + formatChar
+ self.names, self.format = names, format
+ self.size = 2 * len(names)
+
+ def getValueRecord(self, reader):
+ names = self.names
+ if not names:
+ return None
+ values = reader.readStruct(self.format, self.size)
+ values = map(int, values)
+ valueRecord = ValueRecord()
+ items = map(None, names, values)
+ for name, value in items:
+ setattr(valueRecord, name, value)
+ return valueRecord
+
+
+class ValueRecord:
+ # see ValueRecordFactory
+
+ def __nonzero__(self):
+ for value in self.__dict__.values():
+ if value:
+ return 1
+ return 0
+
+ def toXML(self, xmlWriter, otFont):
+ simpleItems = []
+ for mask, name, format in valueRecordFormat[:4]: # "simple" values
+ if hasattr(self, name):
+ simpleItems.append((name, getattr(self, name)))
+ deviceItems = []
+ for mask, name, format in valueRecordFormat[4:8]: # device records
+ if hasattr(self, name):
+ deviceItems.append((name, getattr(self, name)))
+ if deviceItems:
+ xmlWriter.begintag("ValueRecord", simpleItems)
+ xmlWriter.newline()
+ for name, deviceRecord in deviceItems:
+ xxx
+ xmlWriter.endtag("ValueRecord")
+ xmlWriter.newline()
+ else:
+ xmlWriter.simpletag("ValueRecord", simpleItems)
+ xmlWriter.newline()
+
+ def __repr__(self):
+ return "<ValueRecord>"
+
diff --git a/Lib/fontTools/ttLib/tables/G_S_U_B_.py b/Lib/fontTools/ttLib/tables/G_S_U_B_.py
new file mode 100644
index 0000000..d0c4dce
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/G_S_U_B_.py
@@ -0,0 +1,283 @@
+import otCommon
+
+
+class table_G_S_U_B_(otCommon.base_GPOS_GSUB):
+
+ def getLookupTypeClass(self, lookupType):
+ return lookupTypeClasses[lookupType]
+
+
+class SingleSubst:
+
+ def decompile(self, reader, otFont):
+ self.format = reader.readUShort()
+ if self.format == 1:
+ self.decompileFormat1(reader, otFont)
+ elif self.format == 2:
+ self.decompileFormat2(reader, otFont)
+ else:
+ from fontTools import ttLib
+ raise ttLib.TTLibError, "unknown SingleSub format: %d" % self.format
+
+ def decompileFormat1(self, reader, otFont):
+ coverage = reader.readTable(otCommon.CoverageTable, otFont)
+ glyphIDs = coverage.getGlyphIDs()
+ glyphNames = coverage.getGlyphNames()
+ self.substitutions = substitutions = {}
+ deltaGlyphID = reader.readShort()
+ for i in range(len(glyphIDs)):
+ input = glyphNames[i]
+ output = otFont.getGlyphName(glyphIDs[i] + deltaGlyphID)
+ substitutions[input] = output
+
+ def decompileFormat2(self, reader, otFont):
+ coverage = reader.readTable(otCommon.CoverageTable, otFont)
+ glyphNames = coverage.getGlyphNames()
+ glyphCount = reader.readUShort()
+ self.substitutions = substitutions = {}
+ for i in range(glyphCount):
+ glyphID = reader.readUShort()
+ output = otFont.getGlyphName(glyphID)
+ input = glyphNames[i]
+ substitutions[input] = output
+
+ def compile(self, otFont):
+ xxx
+
+ def toXML(self, xmlWriter, otFont):
+ substitutions = self.substitutions.items()
+ substitutions.sort()
+ for input, output in substitutions:
+ xmlWriter.simpletag("Subst", [("in", input), ("out", output)])
+ xmlWriter.newline()
+
+ def fromXML(self, (name, attrs, content), otFont):
+ xxx
+
+
+class MultipleSubst:
+
+ def decompile(self, reader, otFont):
+ format = reader.readUShort()
+ if format <> 1:
+ from fontTools import ttLib
+ raise ttLib.TTLibError, "unknown MultipleSubst format: %d" % format
+ glyphNames = reader.readTable(otCommon.CoverageTable, otFont).getGlyphNames()
+ sequenceCount = reader.readUShort()
+ self.substitutions = substitutions = {}
+ for i in range(sequenceCount):
+ sequence = reader.readTable(Sequence, otFont)
+ substitutions[glyphNames[i]] = sequence.glyphs
+
+ def compile(self, otFont):
+ xxx
+
+ def toXML(self, xmlWriter, otFont):
+ import string
+ items = self.substitutions.items()
+ items.sort()
+ for input, output in items:
+ xmlWriter.simpletag("Subst", [("in", input), ("out", string.join(output, ","))])
+ xmlWriter.newline()
+
+
+class Sequence:
+
+ def decompile(self, reader, otFont):
+ self.glyphs = []
+ for i in range(reader.readUShort()):
+ self.glyphs.append(otFont.getGlyphName(reader.readUShort()))
+
+ def compile(self, otFont):
+ xxx
+
+
+class AlternateSubst:
+
+ def decompile(self, reader, otFont):
+ format = reader.readUShort()
+ if format <> 1:
+ from fontTools import ttLib
+ raise ttLib.TTLibError, "unknown AlternateSubst format: %d" % format
+ coverage = reader.readTable(otCommon.CoverageTable, otFont)
+ glyphNames = coverage.getGlyphNames()
+ alternateSetCount = reader.readUShort()
+ self.alternateSet = alternateSet = {}
+ for i in range(alternateSetCount):
+ set = reader.readTable(AlternateSet, otFont)
+ alternateSet[glyphNames[i]] = set.glyphs
+
+ def compile(self, otFont):
+ xxx
+
+ def toXML(self, xmlWriter, otFont):
+ alternates = self.alternateSet.items()
+ alternates.sort()
+ for input, substList in alternates:
+ xmlWriter.begintag("AlternateSet", [("in", input)])
+ xmlWriter.newline()
+ for output in substList:
+ xmlWriter.simpletag("Subst", out=output)
+ xmlWriter.newline()
+ xmlWriter.endtag("AlternateSet")
+ xmlWriter.newline()
+
+
+class AlternateSet:
+
+ def decompile(self, reader, otFont):
+ glyphCount = reader.readUShort()
+ glyphIDs = reader.readUShortArray(glyphCount)
+ self.glyphs = map(otFont.getGlyphName, glyphIDs)
+
+ def compile(self, otFont):
+ xxx
+
+
+class LigatureSubst:
+
+ def decompile(self, reader, otFont):
+ format = reader.readUShort()
+ if format <> 1:
+ from fontTools import ttLib
+ raise ttLib.TTLibError, "unknown LigatureSubst format: %d" % format
+ coverage = reader.readTable(otCommon.CoverageTable, otFont)
+ glyphNames = coverage.getGlyphNames()
+ ligSetCount = reader.readUShort()
+ self.ligatures = ligatures = []
+ for i in range(ligSetCount):
+ firstGlyph = glyphNames[i]
+ ligSet = reader.readTable(LigatureSet, otFont)
+ for ligatureGlyph, components in ligSet.ligatures:
+ ligatures.append(((firstGlyph,) + tuple(components)), ligatureGlyph)
+
+ def compile(self, otFont):
+ xxx
+
+ def toXML(self, xmlWriter, otFont):
+ import string
+ for input, output in self.ligatures:
+ xmlWriter.simpletag("Subst", [("in", string.join(input, ",")), ("out", output)])
+ xmlWriter.newline()
+
+
+class LigatureSet:
+
+ def decompile(self, reader, otFont):
+ ligatureCount = reader.readUShort()
+ self.ligatures = ligatures = []
+ for i in range(ligatureCount):
+ lig = reader.readTable(Ligature, otFont)
+ ligatures.append((lig.ligatureGlyph, lig.components))
+
+ def compile(self, otFont):
+ xxx
+
+
+class Ligature:
+
+ def decompile(self, reader, otFont):
+ self.ligatureGlyph = otFont.getGlyphName(reader.readUShort())
+ compCount = reader.readUShort()
+ self.components = components = []
+ for i in range(compCount-1):
+ components.append(otFont.getGlyphName(reader.readUShort()))
+
+ def compile(self, otFont):
+ xxx
+
+
+class ContextSubst:
+
+ def decompile(self, reader, otFont):
+ format = reader.readUShort()
+ if format == 1:
+ self.decompileFormat1(reader, otFont)
+ elif format == 2:
+ self.decompileFormat2(reader, otFont)
+ elif format == 3:
+ self.decompileFormat3(reader, otFont)
+ else:
+ from fontTools import ttLib
+ raise ttLib.TTLibError, "unknown ContextSubst format: %d" % format
+
+ def decompileFormat1(self, reader, otFont):
+ xxx
+
+ def decompileFormat2(self, reader, otFont):
+ xxx
+
+ def decompileFormat3(self, reader, otFont):
+ glyphCount = reader.readUShort()
+ substCount = reader.readUShort()
+ coverage = []
+ for i in range(glyphCount):
+ coverage.append(reader.readTable(otCommon.CoverageTable, otFont))
+ self.substitutions = substitutions = []
+ for i in range(substCount):
+ lookupRecord = SubstLookupRecord()
+ lookupRecord.decompile(reader, otFont)
+ substitutions.append((coverage[i].getGlyphNames(), lookupRecord))
+
+ def compile(self, otFont):
+ xxx
+
+ def toXML(self, xmlWriter, otFont):
+ xmlWriter.comment("XXX")
+ xmlWriter.newline()
+
+
+class ChainContextSubst:
+
+ def decompile(self, reader, otFont):
+ self.format = reader.readUShort()
+ if self.format == 1:
+ self.decompileFormat1(reader, otFont)
+ elif self.format == 2:
+ self.decompileFormat2(reader, otFont)
+ elif self.format == 3:
+ self.decompileFormat3(reader, otFont)
+ else:
+ from fontTools import ttLib
+ raise ttLib.TTLibError, "unknown ChainContextSubst format: %d" % self.format
+
+ def decompileFormat1(self, reader, otFont):
+ pass
+
+ def decompileFormat2(self, reader, otFont):
+ pass
+
+ def decompileFormat3(self, reader, otFont):
+ pass
+
+ def compile(self, otFont):
+ xxx
+
+ def toXML(self, xmlWriter, otFont):
+ xmlWriter.comment("XXX")
+ xmlWriter.newline()
+
+
+lookupTypeClasses = {
+ 1: SingleSubst,
+ 2: MultipleSubst,
+ 3: AlternateSubst,
+ 4: LigatureSubst,
+ 5: ContextSubst,
+ 6: ChainContextSubst,
+}
+
+
+#
+# Shared classes
+#
+
+class SubstLookupRecord:
+
+ def decompile(self, reader, otFont):
+ self.sequenceIndex = reader.readUShort()
+ self.lookupListIndex = reader.readUShort()
+
+ def compile(self, otFont):
+ xxx
+
diff --git a/Lib/fontTools/ttLib/tables/L_T_S_H_.py b/Lib/fontTools/ttLib/tables/L_T_S_H_.py
new file mode 100644
index 0000000..20701d6
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/L_T_S_H_.py
@@ -0,0 +1,50 @@
+import DefaultTable
+import array
+import struct
+from fontTools.misc.textTools import safeEval
+
+# XXX I've lowered the strictness, to make sure Apple's own Chicago
+# XXX gets through. They're looking into it, I hope to raise the standards
+# XXX back to normal eventually.
+
+class table_L_T_S_H_(DefaultTable.DefaultTable):
+
+ def decompile(self, data, ttFont):
+ version, numGlyphs = struct.unpack(">HH", data[:4])
+ data = data[4:]
+ assert version == 0
+ assert len(data) == numGlyphs
+ # ouch: the assertion is not true in Chicago!
+ #assert numGlyphs == ttFont['maxp'].numGlyphs
+ yPels = array.array("B")
+ yPels.fromstring(data)
+ self.yPels = {}
+ for i in range(numGlyphs):
+ self.yPels[ttFont.getGlyphName(i)] = yPels[i]
+
+ def compile(self, ttFont):
+ version = 0
+ names = self.yPels.keys()
+ numGlyphs = len(names)
+ yPels = [0] * numGlyphs
+ # ouch: the assertion is not true in Chicago!
+ #assert len(self.yPels) == ttFont['maxp'].numGlyphs == numGlyphs
+ for name in names:
+ yPels[ttFont.getGlyphID(name)] = self.yPels[name]
+ yPels = array.array("B", yPels)
+ return struct.pack(">HH", version, numGlyphs) + yPels.tostring()
+
+ def toXML(self, writer, ttFont):
+ names = self.yPels.keys()
+ names.sort()
+ for name in names:
+ writer.simpletag("yPel", name=name, value=self.yPels[name])
+ writer.newline()
+
+ def fromXML(self, (name, attrs, content), ttFont):
+ if not hasattr(self, "yPels"):
+ self.yPels = {}
+ if name <> "yPel":
+ return # ignore unknown tags
+ self.yPels[attrs["name"]] = safeEval(attrs["value"])
+
diff --git a/Lib/fontTools/ttLib/tables/O_S_2f_2.py b/Lib/fontTools/ttLib/tables/O_S_2f_2.py
new file mode 100644
index 0000000..498275f
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/O_S_2f_2.py
@@ -0,0 +1,164 @@
+import DefaultTable
+import sstruct
+from fontTools.misc.textTools import safeEval, num2binary, binary2num
+
+# panose classification
+
+panoseFormat = """
+ bFamilyType: B
+ bSerifStyle: B
+ bWeight: B
+ bProportion: B
+ bContrast: B
+ bStrokeVariation: B
+ bArmStyle: B
+ bLetterForm: B
+ bMidline: B
+ bXHeight: B
+"""
+
+class Panose:
+
+ def toXML(self, writer, ttFont):
+ formatstring, names, fixes = sstruct.getformat(panoseFormat)
+ for name in names:
+ writer.simpletag(name, value=getattr(self, name))
+ writer.newline()
+
+ def fromXML(self, (name, attrs, content), ttFont):
+ setattr(self, name, safeEval(attrs["value"]))
+
+
+# 'sfnt' OS/2 and Windows Metrics table - 'OS/2'
+
+OS2_format_0 = """
+ > # big endian
+ version: H # version
+ xAvgCharWidth: h # average character width
+ usWeightClass: H # degree of thickness of strokes
+ usWidthClass: H # aspect ratio
+ fsType: h # type flags
+ ySubscriptXSize: h # subscript horizontal font size
+ ySubscriptYSize: h # subscript vertical font size
+ ySubscriptXOffset: h # subscript x offset
+ ySubscriptYOffset: h # subscript y offset
+ ySuperscriptXSize: h # superscript horizontal font size
+ ySuperscriptYSize: h # superscript vertical font size
+ ySuperscriptXOffset: h # superscript x offset
+ ySuperscriptYOffset: h # superscript y offset
+ yStrikeoutSize: h # strikeout size
+ yStrikeoutPosition: h # strikeout position
+ sFamilyClass: h # font family class and subclass
+ panose: 10s # panose classification number
+ ulUnicodeRange1: l # character range
+ ulUnicodeRange2: l # character range
+ ulUnicodeRange3: l # character range
+ ulUnicodeRange4: l # character range
+ achVendID: 4s # font vendor identification
+ fsSelection: H # font selection flags
+ fsFirstCharIndex: H # first unicode character index
+ fsLastCharIndex: H # last unicode character index
+ usTypoAscender: H # typographic ascender
+ usTypoDescender: H # typographic descender
+ usTypoLineGap: H # typographic line gap
+ usWinAscent: H # Windows ascender
+ usWinDescent: H # Windows descender
+"""
+
+OS2_format_1_addition = """
+ ulCodePageRange1: l
+ ulCodePageRange2: l
+"""
+
+OS2_format_2_addition = OS2_format_1_addition + """
+ sxHeight: h
+ sCapHeight: h
+ usDefaultChar: H
+ usBreakChar: H
+ usMaxContex: H
+"""
+
+bigendian = " > # big endian\n"
+
+OS2_format_1 = OS2_format_0 + OS2_format_1_addition
+OS2_format_2 = OS2_format_0 + OS2_format_2_addition
+OS2_format_1_addition = bigendian + OS2_format_1_addition
+OS2_format_2_addition = bigendian + OS2_format_2_addition
+
+
+class table_O_S_2f_2(DefaultTable.DefaultTable):
+
+ """the OS/2 table"""
+
+ def decompile(self, data, ttFont):
+ dummy, data = sstruct.unpack2(OS2_format_0, data, self)
+ if self.version == 1:
+ sstruct.unpack(OS2_format_1_addition, data, self)
+ elif self.version == 2:
+ sstruct.unpack(OS2_format_2_addition, data, self)
+ elif self.version <> 0:
+ from fontTools import ttLib
+ raise ttLib.TTLibError, "unknown format for OS/2 table: version %s" % self.version
+ self.panose = sstruct.unpack(panoseFormat, self.panose, Panose())
+
+ def compile(self, ttFont):
+ panose = self.panose
+ self.panose = sstruct.pack(panoseFormat, self.panose)
+ if self.version == 0:
+ data = sstruct.pack(OS2_format_0, self)
+ elif self.version == 1:
+ data = sstruct.pack(OS2_format_1, self)
+ elif self.version == 2:
+ data = sstruct.pack(OS2_format_2, self)
+ else:
+ from fontTools import ttLib
+ raise ttLib.TTLibError, "unknown format for OS/2 table: version %s" % self.version
+ self.panose = panose
+ return data
+
+ def toXML(self, writer, ttFont):
+ if self.version == 1:
+ format = OS2_format_1
+ elif self.version == 2:
+ format = OS2_format_2
+ else:
+ format = OS2_format_0
+ formatstring, names, fixes = sstruct.getformat(format)
+ for name in names:
+ value = getattr(self, name)
+ if type(value) == type(0L):
+ value = int(value)
+ if name=="panose":
+ writer.begintag("panose")
+ writer.newline()
+ value.toXML(writer, ttFont)
+ writer.endtag("panose")
+ elif name in ("ulUnicodeRange1", "ulUnicodeRange2",
+ "ulUnicodeRange3", "ulUnicodeRange4",
+ "ulCodePageRange1", "ulCodePageRange2"):
+ writer.simpletag(name, value=num2binary(value))
+ elif name in ("fsType", "fsSelection"):
+ writer.simpletag(name, value=num2binary(value, 16))
+ elif name == "achVendID":
+ writer.simpletag(name, value=repr(value)[1:-1])
+ else:
+ writer.simpletag(name, value=value)
+ writer.newline()
+
+ def fromXML(self, (name, attrs, content), ttFont):
+ if name == "panose":
+ self.panose = panose = Panose()
+ for element in content:
+ if type(element) == type(()):
+ panose.fromXML(element, ttFont)
+ elif name in ("ulUnicodeRange1", "ulUnicodeRange2",
+ "ulUnicodeRange3", "ulUnicodeRange4",
+ "ulCodePageRange1", "ulCodePageRange2",
+ "fsType", "fsSelection"):
+ setattr(self, name, binary2num(attrs["value"]))
+ elif name == "achVendID":
+ setattr(self, name, safeEval("'''" + attrs["value"] + "'''"))
+ else:
+ setattr(self, name, safeEval(attrs["value"]))
+
+
diff --git a/Lib/fontTools/ttLib/tables/T_S_I__0.py b/Lib/fontTools/ttLib/tables/T_S_I__0.py
new file mode 100644
index 0000000..988d29a
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/T_S_I__0.py
@@ -0,0 +1,45 @@
+import DefaultTable
+import struct
+
+tsi0Format = '>HHl'
+
+def fixlongs((glyphID, textLength, textOffset)):
+ return int(glyphID), int(textLength), textOffset
+
+
+class table_T_S_I__0(DefaultTable.DefaultTable):
+
+ dependencies = ["TSI1"]
+
+ def decompile(self, data, ttFont):
+ numGlyphs = ttFont['maxp'].numGlyphs
+ indices = []
+ size = struct.calcsize(tsi0Format)
+ for i in range(numGlyphs + 5):
+ glyphID, textLength, textOffset = fixlongs(struct.unpack(tsi0Format, data[:size]))
+ indices.append((glyphID, textLength, textOffset))
+ data = data[size:]
+ assert len(data) == 0
+ assert indices[-5] == (0XFFFE, 0, 0xABFC1F34), "bad magic number"
+ self.indices = indices[:-5]
+ self.extra_indices = indices[-4:]
+
+ def compile(self, ttFont):
+ data = ""
+ size = struct.calcsize(tsi0Format)
+ for index, textLength, textOffset in self.indices:
+ data = data + struct.pack(tsi0Format, index, textLength, textOffset)
+ data = data + struct.pack(tsi0Format, 0XFFFE, 0, 0xABFC1F34)
+ for index, textLength, textOffset in self.extra_indices:
+ data = data + struct.pack(tsi0Format, index, textLength, textOffset)
+ return data
+
+ def set(self, indices, extra_indices):
+ # gets called by 'TSI1' or 'TSI3'
+ self.indices = indices
+ self.extra_indices = extra_indices
+
+ def toXML(self, writer, ttFont):
+ writer.comment("This table will be calculated by the compiler")
+ writer.newline()
+
diff --git a/Lib/fontTools/ttLib/tables/T_S_I__1.py b/Lib/fontTools/ttLib/tables/T_S_I__1.py
new file mode 100644
index 0000000..8ee4e16
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/T_S_I__1.py
@@ -0,0 +1,116 @@
+import DefaultTable
+import string
+
+class table_T_S_I__1(DefaultTable.DefaultTable):
+
+ extras = {0xfffa: "ppgm", 0xfffb: "cvt", 0xfffc: "reserved", 0xfffd: "fpgm"}
+
+ indextable = "TSI0"
+
+ def decompile(self, data, ttFont):
+ indextable = ttFont[self.indextable]
+ self.glyphPrograms = {}
+ for i in range(len(indextable.indices)):
+ glyphID, textLength, textOffset = indextable.indices[i]
+ if textLength == 0x8000:
+ # Ugh. Hi Beat!
+ textLength = indextable.indices[i+1][1]
+ if textLength > 0x8000:
+ pass # XXX Hmmm.
+ text = data[textOffset:textOffset+textLength]
+ assert len(text) == textLength
+ if text:
+ self.glyphPrograms[ttFont.getGlyphName(glyphID)] = text
+
+ self.extraPrograms = {}
+ for i in range(len(indextable.extra_indices)):
+ extraCode, textLength, textOffset = indextable.extra_indices[i]
+ if textLength == 0x8000:
+ if extraName == "fpgm": # this is the last one
+ textLength = len(data) - textOffset
+ else:
+ textLength = indextable.extra_indices[i+1][1]
+ text = data[textOffset:textOffset+textLength]
+ assert len(text) == textLength
+ if text:
+ self.extraPrograms[self.extras[extraCode]] = text
+
+ def compile(self, ttFont):
+ data = ''
+ indextable = ttFont[self.indextable]
+ glyphNames = ttFont.getGlyphOrder()
+
+ indices = []
+ for i in range(len(glyphNames)):
+ if len(data) % 2:
+ data = data + "\015" # align on 2-byte boundaries, fill with return chars. Yum.
+ name = glyphNames[i]
+ if self.glyphPrograms.has_key(name):
+ text = self.glyphPrograms[name]
+ else:
+ text = ""
+ textLength = len(text)
+ if textLength >= 0x8000:
+ textLength = 0x8000 # XXX ???
+ indices.append((i, textLength, len(data)))
+ data = data + text
+
+ extra_indices = []
+ codes = self.extras.items()
+ codes.sort()
+ for i in range(len(codes)):
+ if len(data) % 2:
+ data = data + "\015" # align on 2-byte boundaries, fill with return chars.
+ code, name = codes[i]
+ if self.extraPrograms.has_key(name):
+ text = self.extraPrograms[name]
+ else:
+ text = ""
+ textLength = len(text)
+ if textLength >= 0x8000:
+ textLength = 0x8000 # XXX ???
+ extra_indices.append((code, textLength, len(data)))
+ data = data + text
+ indextable.set(indices, extra_indices)
+ return data
+
+ def toXML(self, writer, ttFont):
+ names = self.glyphPrograms.keys()
+ names.sort()
+ writer.newline()
+ for name in names:
+ text = self.glyphPrograms[name]
+ if not text:
+ continue
+ writer.begintag("glyphProgram", name=name)
+ writer.newline()
+ writer.write_noindent(string.replace(text, "\r", "\n"))
+ writer.newline()
+ writer.endtag("glyphProgram")
+ writer.newline()
+ writer.newline()
+ extra_names = self.extraPrograms.keys()
+ extra_names.sort()
+ for name in extra_names:
+ text = self.extraPrograms[name]
+ if not text:
+ continue
+ writer.begintag("extraProgram", name=name)
+ writer.newline()
+ writer.write_noindent(string.replace(text, "\r", "\n"))
+ writer.newline()
+ writer.endtag("extraProgram")
+ writer.newline()
+ writer.newline()
+
+ def fromXML(self, (name, attrs, content), ttFont):
+ if not hasattr(self, "glyphPrograms"):
+ self.glyphPrograms = {}
+ self.extraPrograms = {}
+ lines = string.split(string.replace(string.join(content, ""), "\r", "\n"), "\n")
+ text = string.join(lines[1:-1], "\r")
+ if name == "glyphProgram":
+ self.glyphPrograms[attrs["name"]] = text
+ elif name == "extraProgram":
+ self.extraPrograms[attrs["name"]] = text
+
diff --git a/Lib/fontTools/ttLib/tables/T_S_I__2.py b/Lib/fontTools/ttLib/tables/T_S_I__2.py
new file mode 100644
index 0000000..15c02ab
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/T_S_I__2.py
@@ -0,0 +1,8 @@
+from fontTools import ttLib
+
+superclass = ttLib.getTableClass("TSI0")
+
+class table_T_S_I__2(superclass):
+
+ dependencies = ["TSI3"]
+
diff --git a/Lib/fontTools/ttLib/tables/T_S_I__3.py b/Lib/fontTools/ttLib/tables/T_S_I__3.py
new file mode 100644
index 0000000..eb4087c
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/T_S_I__3.py
@@ -0,0 +1,11 @@
+from fontTools import ttLib
+
+superclass = ttLib.getTableClass("TSI1")
+
+class table_T_S_I__3(superclass):
+
+ extras = {0xfffa: "reserved0", 0xfffb: "reserved1", 0xfffc: "reserved2", 0xfffd: "reserved3"}
+
+ indextable = "TSI2"
+
+
diff --git a/Lib/fontTools/ttLib/tables/T_S_I__5.py b/Lib/fontTools/ttLib/tables/T_S_I__5.py
new file mode 100644
index 0000000..35b9ee1
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/T_S_I__5.py
@@ -0,0 +1,42 @@
+import DefaultTable
+import array
+from fontTools import ttLib
+from fontTools.misc.textTools import safeEval
+
+
+class table_T_S_I__5(DefaultTable.DefaultTable):
+
+ def decompile(self, data, ttFont):
+ numGlyphs = ttFont['maxp'].numGlyphs
+ assert len(data) == 2 * numGlyphs
+ a = array.array("H")
+ a.fromstring(data)
+ if ttLib.endian <> "big":
+ a.byteswap()
+ self.glyphGrouping = {}
+ for i in range(numGlyphs):
+ self.glyphGrouping[ttFont.getGlyphName(i)] = a[i]
+
+ def compile(self, ttFont):
+ glyphNames = ttFont.getGlyphOrder()
+ a = array.array("H")
+ for i in range(len(glyphNames)):
+ a.append(self.glyphGrouping[glyphNames[i]])
+ if ttLib.endian <> "big":
+ a.byteswap()
+ return a.tostring()
+
+ def toXML(self, writer, ttFont):
+ names = self.glyphGrouping.keys()
+ names.sort()
+ for glyphName in names:
+ writer.simpletag("glyphgroup", name=glyphName, value=self.glyphGrouping[glyphName])
+ writer.newline()
+
+ def fromXML(self, (name, attrs, content), ttFont):
+ if not hasattr(self, "glyphGrouping"):
+ self.glyphGrouping = {}
+ if name <> "glyphgroup":
+ return
+ self.glyphGrouping[attrs["name"]] = safeEval(attrs["value"])
+
diff --git a/Lib/fontTools/ttLib/tables/__init__.py b/Lib/fontTools/ttLib/tables/__init__.py
new file mode 100644
index 0000000..2f17637
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/__init__.py
@@ -0,0 +1 @@
+# dummy file, so Python knows ttLib.tables is a subpackage
diff --git a/Lib/fontTools/ttLib/tables/_c_m_a_p.py b/Lib/fontTools/ttLib/tables/_c_m_a_p.py
new file mode 100644
index 0000000..b067ac6
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/_c_m_a_p.py
@@ -0,0 +1,397 @@
+import DefaultTable
+import struct
+import string
+import array
+from fontTools import ttLib
+from fontTools.misc.textTools import safeEval, readHex
+
+
+class table__c_m_a_p(DefaultTable.DefaultTable):
+
+ def getcmap(self, platformID, platEncID):
+ for subtable in self.tables:
+ if (subtable.platformID == platformID and
+ subtable.platEncID == platEncID):
+ return subtable
+ return None # not found
+
+ def decompile(self, data, ttFont):
+ tableVersion, numSubTables = struct.unpack(">HH", data[:4])
+ self.tableVersion = int(tableVersion)
+ self.tables = tables = []
+ for i in range(numSubTables):
+ platformID, platEncID, offset = struct.unpack(
+ ">HHl", data[4+i*8:4+(i+1)*8])
+ platformID, platEncID = int(platformID), int(platEncID)
+ format, length = struct.unpack(">HH", data[offset:offset+4])
+ if not cmap_classes.has_key(format):
+ table = cmap_format_unknown(format)
+ else:
+ table = cmap_classes[format](format)
+ table.platformID = platformID
+ table.platEncID = platEncID
+ table.decompile(data[offset:offset+int(length)], ttFont)
+ tables.append(table)
+
+ def compile(self, ttFont):
+ self.tables.sort() # sort according to the spec; see CmapSubtable.__cmp__()
+ numSubTables = len(self.tables)
+ totalOffset = 4 + 8 * numSubTables
+ data = struct.pack(">HH", self.tableVersion, numSubTables)
+ tableData = ""
+ done = {} # remember the data so we can reuse the "pointers"
+ for table in self.tables:
+ chunk = table.compile(ttFont)
+ if done.has_key(chunk):
+ offset = done[chunk]
+ else:
+ offset = done[chunk] = totalOffset + len(tableData)
+ tableData = tableData + chunk
+ data = data + struct.pack(">HHl", table.platformID, table.platEncID, offset)
+ return data + tableData
+
+ def toXML(self, writer, ttFont):
+ writer.simpletag("tableVersion", version=self.tableVersion)
+ writer.newline()
+ for table in self.tables:
+ table.toXML(writer, ttFont)
+
+ def fromXML(self, (name, attrs, content), ttFont):
+ if name == "tableVersion":
+ self.tableVersion = safeEval(attrs["version"])
+ return
+ if name[:12] <> "cmap_format_":
+ return
+ if not hasattr(self, "tables"):
+ self.tables = []
+ format = safeEval(name[12])
+ if not cmap_classes.has_key(format):
+ table = cmap_format_unknown(format)
+ else:
+ table = cmap_classes[format](format)
+ table.platformID = safeEval(attrs["platformID"])
+ table.platEncID = safeEval(attrs["platEncID"])
+ table.fromXML((name, attrs, content), ttFont)
+ self.tables.append(table)
+
+
+class CmapSubtable:
+
+ def __init__(self, format):
+ self.format = format
+
+ def toXML(self, writer, ttFont):
+ writer.begintag(self.__class__.__name__, [
+ ("platformID", self.platformID),
+ ("platEncID", self.platEncID),
+ ])
+ writer.newline()
+ writer.dumphex(self.compile(ttFont))
+ writer.endtag(self.__class__.__name__)
+ writer.newline()
+
+ def fromXML(self, (name, attrs, content), ttFont):
+ self.decompile(readHex(content), ttFont)
+
+ def __cmp__(self, other):
+ # implemented so that list.sort() sorts according to the cmap spec.
+ selfTuple = (
+ self.platformID,
+ self.platEncID,
+ self.version,
+ self.__dict__)
+ otherTuple = (
+ other.platformID,
+ other.platEncID,
+ other.version,
+ other.__dict__)
+ return cmp(selfTuple, otherTuple)
+
+
+class cmap_format_0(CmapSubtable):
+
+ def decompile(self, data, ttFont):
+ format, length, version = struct.unpack(">HHH", data[:6])
+ self.version = int(version)
+ assert len(data) == 262 == length
+ glyphIdArray = array.array("B")
+ glyphIdArray.fromstring(data[6:])
+ self.cmap = cmap = {}
+ for charCode in range(len(glyphIdArray)):
+ cmap[charCode] = ttFont.getGlyphName(glyphIdArray[charCode])
+
+ def compile(self, ttFont):
+ charCodes = self.cmap.keys()
+ charCodes.sort()
+ assert charCodes == range(256) # charCodes[charCode] == charCode
+ for charCode in charCodes:
+ # reusing the charCodes list!
+ charCodes[charCode] = ttFont.getGlyphID(self.cmap[charCode])
+ glyphIdArray = array.array("B", charCodes)
+ data = struct.pack(">HHH", 0, 262, self.version) + glyphIdArray.tostring()
+ assert len(data) == 262
+ return data
+
+ def toXML(self, writer, ttFont):
+ writer.begintag(self.__class__.__name__, [
+ ("platformID", self.platformID),
+ ("platEncID", self.platEncID),
+ ("version", self.version),
+ ])
+ writer.newline()
+ items = self.cmap.items()
+ items.sort()
+ for code, name in items:
+ writer.simpletag("map", code=hex(code), name=name)
+ writer.newline()
+ writer.endtag(self.__class__.__name__)
+ writer.newline()
+
+ def fromXML(self, (name, attrs, content), ttFont):
+ self.version = safeEval(attrs["version"])
+ self.cmap = {}
+ for element in content:
+ if type(element) <> type(()):
+ continue
+ name, attrs, content = element
+ if name <> "map":
+ continue
+ self.cmap[safeEval(attrs["code"])] = attrs["name"]
+
+
+
+class cmap_format_2(CmapSubtable):
+
+ def decompile(self, data, ttFont):
+ format, length, version = struct.unpack(">HHH", data[:6])
+ self.version = int(version)
+ self.data = data
+
+ def compile(self, ttFont):
+ return self.data
+
+
+cmap_format_4_format = ">7H"
+
+#uint16 endCode[segCount] # Ending character code for each segment, last = 0xFFFF.
+#uint16 reservedPad # This value should be zero
+#uint16 startCode[segCount] # Starting character code for each segment
+#uint16 idDelta[segCount] # Delta for all character codes in segment
+#uint16 idRangeOffset[segCount] # Offset in bytes to glyph indexArray, or 0
+#uint16 glyphIndexArray[variable] # Glyph index array
+
+class cmap_format_4(CmapSubtable):
+
+ def decompile(self, data, ttFont):
+ (format, length, self.version, segCountX2,
+ searchRange, entrySelector, rangeShift) = \
+ struct.unpack(cmap_format_4_format, data[:14])
+ assert len(data) == length, "corrupt cmap table (%d, %d)" % (len(data), length)
+ data = data[14:]
+ segCountX2 = int(segCountX2)
+ segCount = segCountX2 / 2
+
+ allcodes = array.array("H")
+ allcodes.fromstring(data)
+ if ttLib.endian <> "big":
+ allcodes.byteswap()
+
+ # divide the data
+ endCode = allcodes[:segCount]
+ allcodes = allcodes[segCount+1:]
+ startCode = allcodes[:segCount]
+ allcodes = allcodes[segCount:]
+ idDelta = allcodes[:segCount]
+ allcodes = allcodes[segCount:]
+ idRangeOffset = allcodes[:segCount]
+ glyphIndexArray = allcodes[segCount:]
+
+ # build 2-byte character mapping
+ cmap = {}
+ for i in range(len(startCode) - 1): # don't do 0xffff!
+ for charCode in range(startCode[i], endCode[i] + 1):
+ rangeOffset = idRangeOffset[i]
+ if rangeOffset == 0:
+ glyphID = charCode + idDelta[i]
+ else:
+ # *someone* needs to get killed.
+ index = idRangeOffset[i] / 2 + (charCode - startCode[i]) + i - len(idRangeOffset)
+ if glyphIndexArray[index] <> 0: # if not missing glyph
+ glyphID = glyphIndexArray[index] + idDelta[i]
+ else:
+ glyphID = 0 # missing glyph
+ cmap[charCode] = ttFont.getGlyphName(glyphID % 0x10000)
+ self.cmap = cmap
+
+ def compile(self, ttFont):
+ from fontTools.ttLib.sfnt import maxpoweroftwo
+
+ codes = self.cmap.items()
+ codes.sort()
+
+ # build startCode and endCode lists
+ last = codes[0][0]
+ endCode = []
+ startCode = [last]
+ for charCode, glyphName in codes[1:]: # skip the first code, it's the first start code
+ if charCode == last + 1:
+ last = charCode
+ continue
+ endCode.append(last)
+ startCode.append(charCode)
+ last = charCode
+ endCode.append(last)
+ startCode.append(0xffff)
+ endCode.append(0xffff)
+
+ # build up rest of cruft.
+ idDelta = []
+ idRangeOffset = []
+ glyphIndexArray = []
+
+ for i in range(len(endCode)-1): # skip the closing codes (0xffff)
+ indices = []
+ for charCode in range(startCode[i], endCode[i]+1):
+ indices.append(ttFont.getGlyphID(self.cmap[charCode]))
+ if indices == range(indices[0], indices[0] + len(indices)):
+ idDelta.append((indices[0] - startCode[i]) % 0x10000)
+ idRangeOffset.append(0)
+ else:
+ # someone *definitely* needs to get killed.
+ idDelta.append(0)
+ idRangeOffset.append(2 * (len(endCode) + len(glyphIndexArray) - i))
+ glyphIndexArray = glyphIndexArray + indices
+ idDelta.append(1) # 0xffff + 1 == (tadaa!) 0. So this end code maps to .notdef
+ idRangeOffset.append(0)
+
+ # Insane.
+ segCount = len(endCode)
+ segCountX2 = segCount * 2
+ maxexponent = maxpoweroftwo(segCount)
+ searchRange = 2 * (2 ** maxexponent)
+ entrySelector = maxexponent
+ rangeShift = 2 * segCount - searchRange
+
+ allcodes = array.array("H",
+ endCode + [0] + startCode + idDelta + idRangeOffset + glyphIndexArray)
+ if ttLib.endian <> "big":
+ allcodes.byteswap()
+ data = allcodes.tostring()
+ length = struct.calcsize(cmap_format_4_format) + len(data)
+ header = struct.pack(cmap_format_4_format, self.format, length, self.version,
+ segCountX2, searchRange, entrySelector, rangeShift)
+ return header + data
+
+ def toXML(self, writer, ttFont):
+ from fontTools.unicode import Unicode
+ codes = self.cmap.items()
+ codes.sort()
+ writer.begintag(self.__class__.__name__, [
+ ("platformID", self.platformID),
+ ("platEncID", self.platEncID),
+ ("version", self.version),
+ ])
+ writer.newline()
+
+ for code, name in codes:
+ writer.simpletag("map", code=hex(code), name=name)
+ writer.comment(Unicode[code])
+ writer.newline()
+
+ writer.endtag(self.__class__.__name__)
+ writer.newline()
+
+ def fromXML(self, (name, attrs, content), ttFont):
+ self.version = safeEval(attrs["version"])
+ self.cmap = {}
+ for element in content:
+ if type(element) <> type(()):
+ continue
+ name, attrs, content = element
+ if name <> "map":
+ continue
+ self.cmap[safeEval(attrs["code"])] = attrs["name"]
+
+
+class cmap_format_6(CmapSubtable):
+
+ def decompile(self, data, ttFont):
+ format, length, version, firstCode, entryCount = struct.unpack(
+ ">HHHHH", data[:10])
+ self.version = int(version)
+ firstCode = int(firstCode)
+ self.version = int(version)
+ data = data[10:]
+ assert len(data) == 2 * entryCount
+ glyphIndexArray = array.array("H")
+ glyphIndexArray.fromstring(data)
+ if ttLib.endian <> "big":
+ glyphIndexArray.byteswap()
+ self.cmap = cmap = {}
+ for i in range(len(glyphIndexArray)):
+ glyphID = glyphIndexArray[i]
+ glyphName = ttFont.getGlyphName(glyphID)
+ cmap[i+firstCode] = glyphName
+
+ def compile(self, ttFont):
+ codes = self.cmap.keys()
+ codes.sort()
+ assert codes == range(codes[0], codes[0] + len(codes))
+ glyphIndexArray = array.array("H", [0] * len(codes))
+ firstCode = codes[0]
+ for i in range(len(codes)):
+ code = codes[i]
+ glyphIndexArray[code-firstCode] = ttFont.getGlyphID(self.cmap[code])
+ if ttLib.endian <> "big":
+ glyphIndexArray.byteswap()
+ data = glyphIndexArray.tostring()
+ header = struct.pack(">HHHHH",
+ 6, len(data) + 10, self.version, firstCode, len(self.cmap))
+ return header + data
+
+ def toXML(self, writer, ttFont):
+ codes = self.cmap.items()
+ codes.sort()
+ writer.begintag(self.__class__.__name__, [
+ ("platformID", self.platformID),
+ ("platEncID", self.platEncID),
+ ("version", self.version),
+ ])
+ writer.newline()
+
+ for code, name in codes:
+ writer.simpletag("map", code=hex(code), name=name)
+ writer.newline()
+
+ writer.endtag(self.__class__.__name__)
+ writer.newline()
+
+ def fromXML(self, (name, attrs, content), ttFont):
+ self.version = safeEval(attrs["version"])
+ self.cmap = {}
+ for element in content:
+ if type(element) <> type(()):
+ continue
+ name, attrs, content = element
+ if name <> "map":
+ continue
+ self.cmap[safeEval(attrs["code"])] = attrs["name"]
+
+
+class cmap_format_unknown(CmapSubtable):
+
+ def decompile(self, data, ttFont):
+ self.data = data
+
+ def compile(self, ttFont):
+ return self.data
+
+
+cmap_classes = {
+ 0: cmap_format_0,
+ 2: cmap_format_2,
+ 4: cmap_format_4,
+ 6: cmap_format_6,
+ }
+
+
diff --git a/Lib/fontTools/ttLib/tables/_c_v_t.py b/Lib/fontTools/ttLib/tables/_c_v_t.py
new file mode 100644
index 0000000..ced5523
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/_c_v_t.py
@@ -0,0 +1,48 @@
+import DefaultTable
+import array
+from fontTools import ttLib
+from fontTools.misc.textTools import safeEval
+
+class table__c_v_t(DefaultTable.DefaultTable):
+
+ def decompile(self, data, ttFont):
+ values = array.array("h")
+ values.fromstring(data)
+ if ttLib.endian <> "big":
+ values.byteswap()
+ self.values = values
+
+ def compile(self, ttFont):
+ values = self.values[:]
+ if ttLib.endian <> "big":
+ values.byteswap()
+ return values.tostring()
+
+ def toXML(self, writer, ttFont):
+ for i in range(len(self.values)):
+ value = self.values[i]
+ writer.simpletag("cv", value=value, index=i)
+ writer.newline()
+
+ def fromXML(self, (name, attrs, content), ttFont):
+ if not hasattr(self, "values"):
+ self.values = array.array("h")
+ if name == "cv":
+ index = safeEval(attrs["index"])
+ value = safeEval(attrs["value"])
+ for i in range(1 + index - len(self.values)):
+ self.values.append(0)
+ self.values[index] = value
+
+ def __len__(self):
+ return len(self.values)
+
+ def __getitem__(self, index):
+ return self.values[index]
+
+ def __setitem__(self, index, value):
+ self.values[index] = value
+
+ def __delitem__(self, index):
+ del self.values[index]
+
diff --git a/Lib/fontTools/ttLib/tables/_f_p_g_m.py b/Lib/fontTools/ttLib/tables/_f_p_g_m.py
new file mode 100644
index 0000000..f7cc8a3
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/_f_p_g_m.py
@@ -0,0 +1,14 @@
+import DefaultTable
+import array
+
+class table__f_p_g_m(DefaultTable.DefaultTable):
+
+ def decompile(self, data, ttFont):
+ self.fpgm = data
+
+ def compile(self, ttFont):
+ return self.fpgm
+
+ def __len__(self):
+ return len(self.fpgm)
+
diff --git a/Lib/fontTools/ttLib/tables/_g_a_s_p.py b/Lib/fontTools/ttLib/tables/_g_a_s_p.py
new file mode 100644
index 0000000..e49cceb
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/_g_a_s_p.py
@@ -0,0 +1,46 @@
+import DefaultTable
+import struct
+from fontTools.misc.textTools import safeEval
+
+
+GASP_DOGRAY = 0x0002
+GASP_GRIDFIT = 0x0001
+
+class table__g_a_s_p(DefaultTable.DefaultTable):
+
+ def decompile(self, data, ttFont):
+ self.version, numRanges = struct.unpack(">HH", data[:4])
+ assert self.version == 0, "unknown 'gasp' format: %s" % self.version
+ data = data[4:]
+ self.gaspRange = {}
+ for i in range(numRanges):
+ rangeMaxPPEM, rangeGaspBehavior = struct.unpack(">HH", data[:4])
+ self.gaspRange[int(rangeMaxPPEM)] = int(rangeGaspBehavior)
+ data = data[4:]
+ assert not data, "too much data"
+
+ def compile(self, ttFont):
+ numRanges = len(self.gaspRange)
+ data = struct.pack(">HH", 0, numRanges)
+ items = self.gaspRange.items()
+ items.sort()
+ for rangeMaxPPEM, rangeGaspBehavior in items:
+ data = data + struct.pack(">HH", rangeMaxPPEM, rangeGaspBehavior)
+ return data
+
+ def toXML(self, writer, ttFont):
+ items = self.gaspRange.items()
+ items.sort()
+ for rangeMaxPPEM, rangeGaspBehavior in items:
+ writer.simpletag("gaspRange", [
+ ("rangeMaxPPEM", rangeMaxPPEM),
+ ("rangeGaspBehavior", rangeGaspBehavior)])
+ writer.newline()
+
+ def fromXML(self, (name, attrs, content), ttFont):
+ if name <> "gaspRange":
+ return
+ if not hasattr(self, "gaspRange"):
+ self.gaspRange = {}
+ self.gaspRange[safeEval(attrs["rangeMaxPPEM"])] = safeEval(attrs["rangeGaspBehavior"])
+
diff --git a/Lib/fontTools/ttLib/tables/_g_l_y_f.py b/Lib/fontTools/ttLib/tables/_g_l_y_f.py
new file mode 100644
index 0000000..29f9be5
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/_g_l_y_f.py
@@ -0,0 +1,778 @@
+"""_g_l_y_f.py -- Converter classes for the 'glyf' table."""
+
+
+#
+# The Apple and MS rasterizers behave differently for
+# scaled composite components: one does scale first and then translate
+# and the other does it vice versa. MS defined some flags to indicate
+# the difference, but it seems nobody actually _sets_ those flags.
+#
+# Funny thing: Apple seems to _only_ do their thing in the
+# WE_HAVE_A_SCALE (eg. Chicago) case, and not when it's WE_HAVE_AN_X_AND_Y_SCALE
+# (eg. Charcoal)...
+#
+SCALE_COMPONENT_OFFSET_DEFAULT = 0 # 0 == MS, 1 == Apple
+
+
+import struct, sstruct
+import DefaultTable
+from fontTools import ttLib
+from fontTools.misc.textTools import safeEval, readHex
+import array
+import Numeric
+import types
+
+class table__g_l_y_f(DefaultTable.DefaultTable):
+
+ def decompile(self, data, ttFont):
+ loca = ttFont['loca']
+ last = loca[0]
+ self.glyphs = {}
+ self.glyphOrder = []
+ self.glyphOrder = glyphOrder = ttFont.getGlyphOrder()
+ for i in range(0, len(loca)-1):
+ glyphName = glyphOrder[i]
+ next = loca[i+1]
+ glyphdata = data[last:next]
+ if len(glyphdata) <> (next - last):
+ raise ttLib.TTLibError, "not enough 'glyf' table data"
+ glyph = Glyph(glyphdata)
+ self.glyphs[glyphName] = glyph
+ last = next
+ if len(data) > next:
+ raise ttLib.TTLibError, "too much 'glyf' table data"
+
+ def compile(self, ttFont):
+ import string
+ locations = []
+ currentLocation = 0
+ dataList = []
+ for glyphName in ttFont.getGlyphOrder():
+ glyph = self[glyphName]
+ glyphData = glyph.compile(self)
+ locations.append(currentLocation)
+ currentLocation = currentLocation + len(glyphData)
+ dataList.append(glyphData)
+ locations.append(currentLocation)
+ data = string.join(dataList, "")
+ ttFont['loca'].set(locations)
+ ttFont['maxp'].numGlyphs = len(self.glyphs)
+ return data
+
+ def toXML(self, writer, ttFont, progress=None, compactGlyphs=0):
+ writer.newline()
+ glyphOrder = ttFont.getGlyphOrder()
+ writer.begintag("GlyphOrder")
+ writer.newline()
+ for i in range(len(glyphOrder)):
+ glyphName = glyphOrder[i]
+ writer.simpletag("GlyphID", id=i, name=glyphName)
+ writer.newline()
+ writer.endtag("GlyphOrder")
+ writer.newline()
+ writer.newline()
+ glyphNames = ttFont.getGlyphNames()
+ writer.comment("The xMin, yMin, xMax and yMax values\nwill be recalculated by the compiler.")
+ writer.newline()
+ writer.newline()
+ for glyphName in glyphNames:
+ if progress:
+ progress.setlabel("Dumping 'glyf' table... (%s)" % glyphName)
+ progress.increment()
+ glyph = self[glyphName]
+ if glyph.numberOfContours:
+ writer.begintag('TTGlyph', [
+ ("name", glyphName),
+ ("xMin", glyph.xMin),
+ ("yMin", glyph.yMin),
+ ("xMax", glyph.xMax),
+ ("yMax", glyph.yMax),
+ ])
+ writer.newline()
+ glyph.toXML(writer, ttFont)
+ if compactGlyphs:
+ glyph.compact(self)
+ writer.endtag('TTGlyph')
+ writer.newline()
+ else:
+ writer.simpletag('TTGlyph', name=glyphName)
+ writer.comment("contains no outline data")
+ writer.newline()
+ writer.newline()
+
+ def fromXML(self, (name, attrs, content), ttFont):
+ if name == "GlyphOrder":
+ glyphOrder = []
+ for element in content:
+ if type(element) == types.StringType:
+ continue
+ name, attrs, content = element
+ if name == "GlyphID":
+ index = safeEval(attrs["id"])
+ glyphName = attrs["name"]
+ glyphOrder = glyphOrder + (1 + index - len(glyphOrder)) * [".notdef"]
+ glyphOrder[index] = glyphName
+ ttFont.setGlyphOrder(glyphOrder)
+ elif name == "TTGlyph":
+ if not hasattr(self, "glyphs"):
+ self.glyphs = {}
+ glyphName = attrs["name"]
+ if ttFont.verbose:
+ ttLib.debugmsg("unpacking glyph '%s'" % glyphName)
+ glyph = Glyph()
+ for attr in ['xMin', 'yMin', 'xMax', 'yMax']:
+ setattr(glyph, attr, safeEval(attrs.get(attr, '0')))
+ self.glyphs[glyphName] = glyph
+ for element in content:
+ if type(element) == types.StringType:
+ continue
+ glyph.fromXML(element, ttFont)
+
+ def setGlyphOrder(self, glyphOrder):
+ self.glyphOrder = glyphOrder
+
+ def getGlyphName(self, glyphID):
+ return self.glyphOrder[glyphID]
+
+ def getGlyphID(self, glyphName):
+ # XXX optimize with reverse dict!!!
+ return self.glyphOrder.index(glyphName)
+
+ #def keys(self):
+ # return self.glyphOrder[:]
+ #
+ #def has_key(self, glyphName):
+ # return self.glyphs.has_key(glyphName)
+ #
+ def __getitem__(self, glyphName):
+ glyph = self.glyphs[glyphName]
+ glyph.expand(self)
+ return glyph
+
+ def __setitem__(self, glyphName, glyph):
+ self.glyphs[glyphName] = glyph
+ if glyphName not in self.glyphOrder:
+ self.glyphOrder.append(glyphName)
+
+ def __delitem__(self, glyphName):
+ del self.glyphs[glyphName]
+ self.glyphOrder.remove(glyphName)
+
+ def __len__(self):
+ assert len(self.glyphOrder) == len(self.glyphs)
+ return len(self.glyphs)
+
+
+glyphHeaderFormat = """
+ > # big endian
+ numberOfContours: h
+ xMin: h
+ yMin: h
+ xMax: h
+ yMax: h
+"""
+
+# flags
+flagOnCurve = 0x01
+flagXShort = 0x02
+flagYShort = 0x04
+flagRepeat = 0x08
+flagXsame = 0x10
+flagYsame = 0x20
+flagReserved1 = 0x40
+flagReserved2 = 0x80
+
+
+ARG_1_AND_2_ARE_WORDS = 0x0001 # if set args are words otherwise they are bytes
+ARGS_ARE_XY_VALUES = 0x0002 # if set args are xy values, otherwise they are points
+ROUND_XY_TO_GRID = 0x0004 # for the xy values if above is true
+WE_HAVE_A_SCALE = 0x0008 # Sx = Sy, otherwise scale == 1.0
+NON_OVERLAPPING = 0x0010 # set to same value for all components (obsolete!)
+MORE_COMPONENTS = 0x0020 # indicates at least one more glyph after this one
+WE_HAVE_AN_X_AND_Y_SCALE = 0x0040 # Sx, Sy
+WE_HAVE_A_TWO_BY_TWO = 0x0080 # t00, t01, t10, t11
+WE_HAVE_INSTRUCTIONS = 0x0100 # instructions follow
+USE_MY_METRICS = 0x0200 # apply these metrics to parent glyph
+OVERLAP_COMPOUND = 0x0400 # used by Apple in GX fonts
+SCALED_COMPONENT_OFFSET = 0x0800 # composite designed to have the component offset scaled (designed for Apple)
+UNSCALED_COMPONENT_OFFSET = 0x1000 # composite designed not to have the component offset scaled (designed for MS)
+
+
+class Glyph:
+
+ def __init__(self, data=""):
+ if not data:
+ # empty char
+ self.numberOfContours = 0
+ return
+ self.data = data
+
+ def compact(self, glyfTable):
+ data = self.compile(glyfTable)
+ self.__dict__.clear()
+ self.data = data
+
+ def expand(self, glyfTable):
+ if not hasattr(self, "data"):
+ # already unpacked
+ return
+ dummy, data = sstruct.unpack2(glyphHeaderFormat, self.data, self)
+ del self.data
+ if self.numberOfContours == -1:
+ self.decompileComponents(data, glyfTable)
+ else:
+ self.decompileCoordinates(data)
+
+ def compile(self, glyfTable):
+ if hasattr(self, "data"):
+ return self.data
+ if self.numberOfContours == 0:
+ return ""
+ self.recalcBounds(glyfTable)
+ data = sstruct.pack(glyphHeaderFormat, self)
+ if self.numberOfContours == -1:
+ data = data + self.compileComponents(glyfTable)
+ else:
+ data = data + self.compileCoordinates()
+ # from the spec: "Note that the local offsets should be word-aligned"
+ if len(data) % 2:
+ # ...so if the length of the data is odd, append a null byte
+ data = data + "\0"
+ return data
+
+ def toXML(self, writer, ttFont):
+ if self.numberOfContours == -1:
+ for compo in self.components:
+ compo.toXML(writer, ttFont)
+ if hasattr(self, "instructions"):
+ writer.begintag("instructions")
+ writer.newline()
+ writer.dumphex(self.instructions)
+ writer.endtag("instructions")
+ writer.newline()
+ else:
+ last = 0
+ for i in range(self.numberOfContours):
+ writer.begintag("contour")
+ writer.newline()
+ for j in range(last, self.endPtsOfContours[i] + 1):
+ writer.simpletag("pt", [
+ ("x", self.coordinates[j][0]),
+ ("y", self.coordinates[j][1]),
+ ("on", self.flags[j] & flagOnCurve)])
+ writer.newline()
+ last = self.endPtsOfContours[i] + 1
+ writer.endtag("contour")
+ writer.newline()
+ if self.numberOfContours:
+ writer.begintag("instructions")
+ writer.newline()
+ writer.dumphex(self.instructions)
+ writer.endtag("instructions")
+ writer.newline()
+
+ def fromXML(self, (name, attrs, content), ttFont):
+ if name == "contour":
+ self.numberOfContours = self.numberOfContours + 1
+ if self.numberOfContours < 0:
+ raise ttLib.TTLibError, "can't mix composites and contours in glyph"
+ coordinates = []
+ flags = []
+ for element in content:
+ if type(element) == types.StringType:
+ continue
+ name, attrs, content = element
+ if name <> "pt":
+ continue # ignore anything but "pt"
+ coordinates.append([safeEval(attrs["x"]), safeEval(attrs["y"])])
+ flags.append(not not safeEval(attrs["on"]))
+ coordinates = Numeric.array(coordinates, Numeric.Int16)
+ flags = Numeric.array(flags, Numeric.Int8)
+ if not hasattr(self, "coordinates"):
+ self.coordinates = coordinates
+ self.flags = flags
+ self.endPtsOfContours = [len(coordinates)-1]
+ else:
+ self.coordinates = Numeric.concatenate((self.coordinates, coordinates))
+ self.flags = Numeric.concatenate((self.flags, flags))
+ self.endPtsOfContours.append(len(self.coordinates)-1)
+ elif name == "component":
+ if self.numberOfContours > 0:
+ raise ttLib.TTLibError, "can't mix composites and contours in glyph"
+ self.numberOfContours = -1
+ if not hasattr(self, "components"):
+ self.components = []
+ component = GlyphComponent()
+ self.components.append(component)
+ component.fromXML((name, attrs, content), ttFont)
+ elif name == "instructions":
+ self.instructions = readHex(content)
+
+ def getCompositeMaxpValues(self, glyfTable, maxComponentDepth=1):
+ assert self.numberOfContours == -1
+ nContours = 0
+ nPoints = 0
+ for compo in self.components:
+ baseGlyph = glyfTable[compo.glyphName]
+ if baseGlyph.numberOfContours == 0:
+ continue
+ elif baseGlyph.numberOfContours > 0:
+ nP, nC = baseGlyph.getMaxpValues()
+ else:
+ nP, nC, maxComponentDepth = baseGlyph.getCompositeMaxpValues(
+ glyfTable, maxComponentDepth + 1)
+ nPoints = nPoints + nP
+ nContours = nContours + nC
+ return nPoints, nContours, maxComponentDepth
+
+ def getMaxpValues(self):
+ assert self.numberOfContours > 0
+ return len(self.coordinates), len(self.endPtsOfContours)
+
+ def decompileComponents(self, data, glyfTable):
+ self.components = []
+ more = 1
+ haveInstructions = 0
+ while more:
+ component = GlyphComponent()
+ more, haveInstr, data = component.decompile(data, glyfTable)
+ haveInstructions = haveInstructions | haveInstr
+ self.components.append(component)
+ if haveInstructions:
+ numInstructions, = struct.unpack(">h", data[:2])
+ data = data[2:]
+ self.instructions = data[:numInstructions]
+ data = data[numInstructions:]
+ assert len(data) in (0, 1), "bad composite data"
+
+ def decompileCoordinates(self, data):
+ endPtsOfContours = array.array("h")
+ endPtsOfContours.fromstring(data[:2*self.numberOfContours])
+ if ttLib.endian <> "big":
+ endPtsOfContours.byteswap()
+ self.endPtsOfContours = endPtsOfContours.tolist()
+
+ data = data[2*self.numberOfContours:]
+
+ instructionLength, = struct.unpack(">h", data[:2])
+ data = data[2:]
+ self.instructions = data[:instructionLength]
+ data = data[instructionLength:]
+ nCoordinates = self.endPtsOfContours[-1] + 1
+ flags, xCoordinates, yCoordinates = \
+ self.decompileCoordinatesRaw(nCoordinates, data)
+
+ # fill in repetitions and apply signs
+ coordinates = Numeric.zeros((nCoordinates, 2), Numeric.Int16)
+ xIndex = 0
+ yIndex = 0
+ for i in range(nCoordinates):
+ flag = flags[i]
+ # x coordinate
+ if flag & flagXShort:
+ if flag & flagXsame:
+ x = xCoordinates[xIndex]
+ else:
+ x = -xCoordinates[xIndex]
+ xIndex = xIndex + 1
+ elif flag & flagXsame:
+ x = 0
+ else:
+ x = xCoordinates[xIndex]
+ xIndex = xIndex + 1
+ # y coordinate
+ if flag & flagYShort:
+ if flag & flagYsame:
+ y = yCoordinates[yIndex]
+ else:
+ y = -yCoordinates[yIndex]
+ yIndex = yIndex + 1
+ elif flag & flagYsame:
+ y = 0
+ else:
+ y = yCoordinates[yIndex]
+ yIndex = yIndex + 1
+ coordinates[i] = (x, y)
+ assert xIndex == len(xCoordinates)
+ assert yIndex == len(yCoordinates)
+ # convert relative to absolute coordinates
+ self.coordinates = Numeric.add.accumulate(coordinates)
+ # discard all flags but for "flagOnCurve"
+ if hasattr(Numeric, "__version__"):
+ self.flags = Numeric.bitwise_and(flags, flagOnCurve).astype(Numeric.Int8)
+ else:
+ self.flags = Numeric.boolean_and(flags, flagOnCurve).astype(Numeric.Int8)
+
+ def decompileCoordinatesRaw(self, nCoordinates, data):
+ # unpack flags and prepare unpacking of coordinates
+ flags = Numeric.array([0] * nCoordinates, Numeric.Int8)
+ # Warning: deep Python trickery going on. We use the struct module to unpack
+ # the coordinates. We build a format string based on the flags, so we can
+ # unpack the coordinates in one struct.unpack() call.
+ xFormat = ">" # big endian
+ yFormat = ">" # big endian
+ i = j = 0
+ while 1:
+ flag = ord(data[i])
+ i = i + 1
+ repeat = 1
+ if flag & flagRepeat:
+ repeat = ord(data[i]) + 1
+ i = i + 1
+ for k in range(repeat):
+ if flag & flagXShort:
+ xFormat = xFormat + 'B'
+ elif not (flag & flagXsame):
+ xFormat = xFormat + 'h'
+ if flag & flagYShort:
+ yFormat = yFormat + 'B'
+ elif not (flag & flagYsame):
+ yFormat = yFormat + 'h'
+ flags[j] = flag
+ j = j + 1
+ if j >= nCoordinates:
+ break
+ assert j == nCoordinates, "bad glyph flags"
+ data = data[i:]
+ # unpack raw coordinates, krrrrrr-tching!
+ xDataLen = struct.calcsize(xFormat)
+ yDataLen = struct.calcsize(yFormat)
+ if (len(data) - (xDataLen + yDataLen)) not in (0, 1):
+ raise ttLib.TTLibError, "bad glyph record"
+ xCoordinates = struct.unpack(xFormat, data[:xDataLen])
+ yCoordinates = struct.unpack(yFormat, data[xDataLen:xDataLen+yDataLen])
+ return flags, xCoordinates, yCoordinates
+
+ def compileComponents(self, glyfTable):
+ data = ""
+ lastcomponent = len(self.components) - 1
+ more = 1
+ haveInstructions = 0
+ for i in range(len(self.components)):
+ if i == lastcomponent:
+ haveInstructions = hasattr(self, "instructions")
+ more = 0
+ compo = self.components[i]
+ data = data + compo.compile(more, haveInstructions, glyfTable)
+ if haveInstructions:
+ data = data + struct.pack(">h", len(self.instructions)) + self.instructions
+ return data
+
+
+ def compileCoordinates(self):
+ assert len(self.coordinates) == len(self.flags)
+ data = ""
+ endPtsOfContours = array.array("h", self.endPtsOfContours)
+ if ttLib.endian <> "big":
+ endPtsOfContours.byteswap()
+ data = data + endPtsOfContours.tostring()
+ data = data + struct.pack(">h", len(self.instructions))
+ data = data + self.instructions
+ nCoordinates = len(self.coordinates)
+
+ # make a copy
+ coordinates = self.coordinates.astype(self.coordinates.typecode())
+ # absolute to relative coordinates
+ coordinates[1:] = Numeric.subtract(coordinates[1:], coordinates[:-1])
+ flags = self.flags
+ compressedflags = []
+ xPoints = []
+ yPoints = []
+ xFormat = ">"
+ yFormat = ">"
+ lastflag = None
+ repeat = 0
+ for i in range(len(coordinates)):
+ # Oh, the horrors of TrueType
+ flag = self.flags[i]
+ x, y = coordinates[i]
+ # do x
+ if x == 0:
+ flag = flag | flagXsame
+ elif -255 <= x <= 255:
+ flag = flag | flagXShort
+ if x > 0:
+ flag = flag | flagXsame
+ else:
+ x = -x
+ xPoints.append(x)
+ xFormat = xFormat + 'B'
+ else:
+ xPoints.append(x)
+ xFormat = xFormat + 'h'
+ # do y
+ if y == 0:
+ flag = flag | flagYsame
+ elif -255 <= y <= 255:
+ flag = flag | flagYShort
+ if y > 0:
+ flag = flag | flagYsame
+ else:
+ y = -y
+ yPoints.append(y)
+ yFormat = yFormat + 'B'
+ else:
+ yPoints.append(y)
+ yFormat = yFormat + 'h'
+ # handle repeating flags
+ if flag == lastflag:
+ repeat = repeat + 1
+ if repeat == 1:
+ compressedflags.append(flag)
+ elif repeat > 1:
+ compressedflags[-2] = flag | flagRepeat
+ compressedflags[-1] = repeat
+ else:
+ compressedflags[-1] = repeat
+ else:
+ repeat = 0
+ compressedflags.append(flag)
+ lastflag = flag
+ data = data + array.array("B", compressedflags).tostring()
+ data = data + apply(struct.pack, (xFormat,)+tuple(xPoints))
+ data = data + apply(struct.pack, (yFormat,)+tuple(yPoints))
+ return data
+
+ def recalcBounds(self, glyfTable):
+ coordinates, endPts, flags = self.getCoordinates(glyfTable)
+ self.xMin, self.yMin = Numeric.minimum.reduce(coordinates)
+ self.xMax, self.yMax = Numeric.maximum.reduce(coordinates)
+
+ def getCoordinates(self, glyfTable):
+ if self.numberOfContours > 0:
+ return self.coordinates, self.endPtsOfContours, self.flags
+ elif self.numberOfContours == -1:
+ # it's a composite
+ allCoords = None
+ allFlags = None
+ allEndPts = None
+ for compo in self.components:
+ g = glyfTable[compo.glyphName]
+ coordinates, endPts, flags = g.getCoordinates(glyfTable)
+ if hasattr(compo, "firstpt"):
+ # move according to two reference points
+ move = allCoords[compo.firstpt] - coordinates[compo.secondpt]
+ else:
+ move = compo.x, compo.y
+
+ if not hasattr(compo, "transform"):
+ coordinates = coordinates + move # I love NumPy!
+ else:
+ apple_way = compo.flags & SCALED_COMPONENT_OFFSET
+ ms_way = compo.flags & UNSCALED_COMPONENT_OFFSET
+ assert not (apple_way and ms_way)
+ if not (apple_way or ms_way):
+ scale_component_offset = SCALE_COMPONENT_OFFSET_DEFAULT # see top of this file
+ else:
+ scale_component_offset = apple_way
+ if scale_component_offset:
+ # the Apple way: first move, then scale (ie. scale the component offset)
+ coordinates = coordinates + move
+ coordinates = Numeric.dot(coordinates, compo.transform)
+ else:
+ # the MS way: first scale, then move
+ coordinates = Numeric.dot(coordinates, compo.transform)
+ coordinates = coordinates + move
+ # due to the transformation the coords. are now floats;
+ # round them off nicely, and cast to short
+ coordinates = Numeric.floor(coordinates + 0.5).astype(Numeric.Int16)
+ if allCoords is None:
+ allCoords = coordinates
+ allEndPts = endPts
+ allFlags = flags
+ else:
+ allEndPts = allEndPts + (Numeric.array(endPts) + len(allCoords)).tolist()
+ allCoords = Numeric.concatenate((allCoords, coordinates))
+ allFlags = Numeric.concatenate((allFlags, flags))
+ return allCoords, allEndPts, allFlags
+ else:
+ return Numeric.array([], Numeric.Int16), [], Numeric.array([], Numeric.Int8)
+
+ def __cmp__(self, other):
+ if self.numberOfContours <= 0:
+ return cmp(self.__dict__, other.__dict__)
+ else:
+ if cmp(len(self.coordinates), len(other.coordinates)):
+ return 1
+ ctest = Numeric.alltrue(Numeric.alltrue(Numeric.equal(self.coordinates, other.coordinates)))
+ ftest = Numeric.alltrue(Numeric.equal(self.flags, other.flags))
+ if not ctest or not ftest:
+ return 1
+ return (
+ cmp(self.endPtsOfContours, other.endPtsOfContours) or
+ cmp(self.instructions, other.instructions)
+ )
+
+
+class GlyphComponent:
+
+ def __init__(self):
+ pass
+
+ def decompile(self, data, glyfTable):
+ flags, glyphID = struct.unpack(">HH", data[:4])
+ self.flags = int(flags)
+ glyphID = int(glyphID)
+ self.glyphName = glyfTable.getGlyphName(int(glyphID))
+ #print ">>", reprflag(self.flags)
+ data = data[4:]
+
+ if self.flags & ARG_1_AND_2_ARE_WORDS:
+ if self.flags & ARGS_ARE_XY_VALUES:
+ self.x, self.y = struct.unpack(">hh", data[:4])
+ else:
+ x, y = struct.unpack(">HH", data[:4])
+ self.firstpt, self.secondpt = int(x), int(y)
+ data = data[4:]
+ else:
+ if self.flags & ARGS_ARE_XY_VALUES:
+ self.x, self.y = struct.unpack(">bb", data[:2])
+ else:
+ x, y = struct.unpack(">BB", data[:4])
+ self.firstpt, self.secondpt = int(x), int(y)
+ data = data[2:]
+
+ if self.flags & WE_HAVE_A_SCALE:
+ scale, = struct.unpack(">h", data[:2])
+ self.transform = Numeric.array(
+ [[scale, 0], [0, scale]]) / float(0x4000) # fixed 2.14
+ data = data[2:]
+ elif self.flags & WE_HAVE_AN_X_AND_Y_SCALE:
+ xscale, yscale = struct.unpack(">hh", data[:4])
+ self.transform = Numeric.array(
+ [[xscale, 0], [0, yscale]]) / float(0x4000) # fixed 2.14
+ data = data[4:]
+ elif self.flags & WE_HAVE_A_TWO_BY_TWO:
+ (xscale, scale01,
+ scale10, yscale) = struct.unpack(">hhhh", data[:8])
+ self.transform = Numeric.array(
+ [[xscale, scale01], [scale10, yscale]]) / float(0x4000) # fixed 2.14
+ data = data[8:]
+ more = self.flags & MORE_COMPONENTS
+ haveInstructions = self.flags & WE_HAVE_INSTRUCTIONS
+ self.flags = self.flags & (ROUND_XY_TO_GRID | USE_MY_METRICS |
+ SCALED_COMPONENT_OFFSET | UNSCALED_COMPONENT_OFFSET |
+ NON_OVERLAPPING)
+ return more, haveInstructions, data
+
+ def compile(self, more, haveInstructions, glyfTable):
+ data = ""
+
+ # reset all flags we will calculate ourselves
+ flags = self.flags & (ROUND_XY_TO_GRID | USE_MY_METRICS |
+ SCALED_COMPONENT_OFFSET | UNSCALED_COMPONENT_OFFSET |
+ NON_OVERLAPPING)
+ if more:
+ flags = flags | MORE_COMPONENTS
+ if haveInstructions:
+ flags = flags | WE_HAVE_INSTRUCTIONS
+
+ if hasattr(self, "firstpt"):
+ if (0 <= self.firstpt <= 255) and (0 <= self.secondpt <= 255):
+ data = data + struct.pack(">BB", self.firstpt, self.secondpt)
+ else:
+ data = data + struct.pack(">HH", self.firstpt, self.secondpt)
+ flags = flags | ARG_1_AND_2_ARE_WORDS
+ else:
+ flags = flags | ARGS_ARE_XY_VALUES
+ if (-128 <= self.x <= 127) and (-128 <= self.y <= 127):
+ data = data + struct.pack(">bb", self.x, self.y)
+ else:
+ data = data + struct.pack(">hh", self.x, self.y)
+ flags = flags | ARG_1_AND_2_ARE_WORDS
+
+ if hasattr(self, "transform"):
+ # XXX needs more testing
+ transform = Numeric.floor(self.transform * 0x4000 + 0.5)
+ if transform[0][1] or transform[1][0]:
+ flags = flags | WE_HAVE_A_TWO_BY_TWO
+ data = data + struct.pack(">hhhh",
+ transform[0][0], transform[0][1],
+ transform[1][0], transform[1][1])
+ elif transform[0][0] <> transform[1][1]:
+ flags = flags | WE_HAVE_AN_X_AND_Y_SCALE
+ data = data + struct.pack(">hh",
+ transform[0][0], transform[1][1])
+ else:
+ flags = flags | WE_HAVE_A_SCALE
+ data = data + struct.pack(">h",
+ transform[0][0])
+
+ glyphID = glyfTable.getGlyphID(self.glyphName)
+ return struct.pack(">HH", flags, glyphID) + data
+
+ def toXML(self, writer, ttFont):
+ attrs = [("glyphName", self.glyphName)]
+ if not hasattr(self, "firstpt"):
+ attrs = attrs + [("x", self.x), ("y", self.y)]
+ else:
+ attrs = attrs + [("firstpt", self.firstpt), ("secondpt", self.secondpt)]
+
+ if hasattr(self, "transform"):
+ # XXX needs more testing
+ transform = self.transform
+ if transform[0][1] or transform[1][0]:
+ attrs = attrs + [
+ ("scalex", transform[0][0]), ("scale01", transform[0][1]),
+ ("scale10", transform[1][0]), ("scaley", transform[1][1]),
+ ]
+ elif transform[0][0] <> transform[1][1]:
+ attrs = attrs + [
+ ("scalex", transform[0][0]), ("scaley", transform[1][1]),
+ ]
+ else:
+ attrs = attrs + [("scale", transform[0][0])]
+ attrs = attrs + [("flags", hex(self.flags))]
+ writer.simpletag("component", attrs)
+ writer.newline()
+
+ def fromXML(self, (name, attrs, content), ttFont):
+ self.glyphName = attrs["glyphName"]
+ if attrs.has_key("firstpt"):
+ self.firstpt = safeEval(attrs["firstpt"])
+ self.secondpt = safeEval(attrs["secondpt"])
+ else:
+ self.x = safeEval(attrs["x"])
+ self.y = safeEval(attrs["y"])
+ if attrs.has_key("scale01"):
+ scalex = safeEval(attrs["scalex"])
+ scale01 = safeEval(attrs["scale01"])
+ scale10 = safeEval(attrs["scale10"])
+ scaley = safeEval(attrs["scaley"])
+ self.transform = Numeric.array([[scalex, scale01], [scale10, scaley]])
+ elif attrs.has_key("scalex"):
+ scalex = safeEval(attrs["scalex"])
+ scaley = safeEval(attrs["scaley"])
+ self.transform = Numeric.array([[scalex, 0], [0, scaley]])
+ elif attrs.has_key("scale"):
+ scale = safeEval(attrs["scale"])
+ self.transform = Numeric.array([[scale, 0], [0, scale]])
+ self.flags = safeEval(attrs["flags"])
+
+ def __cmp__(self, other):
+ if hasattr(self, "transform"):
+ if Numeric.alltrue(Numeric.equal(self.transform, other.transform)):
+ selfdict = self.__dict__.copy()
+ otherdict = other.__dict__.copy()
+ del selfdict["transform"]
+ del otherdict["transform"]
+ return cmp(selfdict, otherdict)
+ else:
+ return 1
+ else:
+ return cmp(self.__dict__, other.__dict__)
+
+
+def reprflag(flag):
+ bin = ""
+ if type(flag) == types.StringType:
+ flag = ord(flag)
+ while flag:
+ if flag & 0x01:
+ bin = "1" + bin
+ else:
+ bin = "0" + bin
+ flag = flag >> 1
+ bin = (14 - len(bin)) * "0" + bin
+ return bin
+
diff --git a/Lib/fontTools/ttLib/tables/_h_d_m_x.py b/Lib/fontTools/ttLib/tables/_h_d_m_x.py
new file mode 100644
index 0000000..e5c396c
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/_h_d_m_x.py
@@ -0,0 +1,95 @@
+import DefaultTable
+import sstruct
+import string
+
+hdmxHeaderFormat = """
+ version: H
+ numRecords: H
+ recordSize: l
+"""
+
+class table__h_d_m_x(DefaultTable.DefaultTable):
+
+ def decompile(self, data, ttFont):
+ numGlyphs = ttFont['maxp'].numGlyphs
+ glyphOrder = ttFont.getGlyphOrder()
+ dummy, data = sstruct.unpack2(hdmxHeaderFormat, data, self)
+ self.hdmx = {}
+ for i in range(self.numRecords):
+ ppem = ord(data[0])
+ maxSize = ord(data[1])
+ widths = {}
+ for glyphID in range(numGlyphs):
+ widths[glyphOrder[glyphID]] = ord(data[glyphID+2])
+ self.hdmx[ppem] = widths
+ data = data[self.recordSize:]
+ assert len(data) == 0, "too much hdmx data"
+
+ def compile(self, ttFont):
+ self.version = 0
+ numGlyphs = ttFont['maxp'].numGlyphs
+ glyphOrder = ttFont.getGlyphOrder()
+ self.recordSize = 4 * ((2 + numGlyphs + 3) / 4)
+ pad = (self.recordSize - 2 - numGlyphs) * "\0"
+ self.numRecords = len(self.hdmx)
+ data = sstruct.pack(hdmxHeaderFormat, self)
+ items = self.hdmx.items()
+ items.sort()
+ for ppem, widths in items:
+ data = data + chr(ppem) + chr(max(widths.values()))
+ for glyphID in range(len(glyphOrder)):
+ width = widths[glyphOrder[glyphID]]
+ data = data + chr(width)
+ data = data + pad
+ return data
+
+ def toXML(self, writer, ttFont):
+ writer.begintag("hdmxData")
+ writer.newline()
+ ppems = self.hdmx.keys()
+ ppems.sort()
+ records = []
+ format = ""
+ for ppem in ppems:
+ widths = self.hdmx[ppem]
+ records.append(widths)
+ format = format + "%4d"
+ glyphNames = ttFont.getGlyphOrder()[:]
+ glyphNames.sort()
+ maxNameLen = max(map(len, glyphNames))
+ format = "%" + `maxNameLen` + 's:' + format + ' ;'
+ writer.write(format % (("ppem",) + tuple(ppems)))
+ writer.newline()
+ writer.newline()
+ for glyphName in glyphNames:
+ row = []
+ for ppem in ppems:
+ widths = self.hdmx[ppem]
+ row.append(widths[glyphName])
+ writer.write(format % ((glyphName,) + tuple(row)))
+ writer.newline()
+ writer.endtag("hdmxData")
+ writer.newline()
+
+ def fromXML(self, (name, attrs, content), ttFont):
+ if name <> "hdmxData":
+ return
+ content = string.join(content, " ")
+ lines = string.split(content, ";")
+ topRow = string.split(lines[0])
+ assert topRow[0] == "ppem:", "illegal hdmx format"
+ ppems = map(int, topRow[1:])
+ self.hdmx = hdmx = {}
+ for ppem in ppems:
+ hdmx[ppem] = {}
+ lines = map(string.split, lines[1:])
+ for line in lines:
+ if not line:
+ continue
+ assert line[0][-1] == ":", "illegal hdmx format"
+ glyphName = line[0][:-1]
+ line = map(int, line[1:])
+ assert len(line) == len(ppems), "illegal hdmx format"
+ for i in range(len(ppems)):
+ hdmx[ppems[i]][glyphName] = line[i]
+
diff --git a/Lib/fontTools/ttLib/tables/_h_e_a_d.py b/Lib/fontTools/ttLib/tables/_h_e_a_d.py
new file mode 100644
index 0000000..fc33edb
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/_h_e_a_d.py
@@ -0,0 +1,136 @@
+import DefaultTable
+import sstruct
+import time
+import string
+import calendar
+from fontTools.misc.textTools import safeEval, num2binary, binary2num
+
+
+headFormat = """
+ > # big endian
+ tableVersion: 16.16F
+ fontRevision: 16.16F
+ checkSumAdjustment: l
+ magicNumber: l
+ x # pad byte
+ flags: b
+ unitsPerEm: H
+ created: 8s
+ modified: 8s
+ xMin: h
+ yMin: h
+ xMax: h
+ yMax: h
+ macStyle: H
+ lowestRecPPEM: H
+ fontDirectionHint: h
+ indexToLocFormat: h
+ glyphDataFormat: h
+"""
+
+class table__h_e_a_d(DefaultTable.DefaultTable):
+
+ dependencies = ['maxp', 'loca']
+
+ def decompile(self, data, ttFont):
+ sstruct.unpack(headFormat, data, self)
+ self.unitsPerEm = int(self.unitsPerEm)
+ self.strings2dates()
+
+ def compile(self, ttFont):
+ self.modified = long(time.time() - mac_epoch_diff)
+ self.dates2strings()
+ data = sstruct.pack(headFormat, self)
+ self.strings2dates()
+ return data
+
+ def strings2dates(self):
+ self.created = bin2long(self.created)
+ self.modified = bin2long(self.modified)
+
+ def dates2strings(self):
+ self.created = long2bin(self.created)
+ self.modified = long2bin(self.modified)
+
+ def toXML(self, writer, ttFont):
+ writer.comment("Most of this table will be recalculated by the compiler")
+ writer.newline()
+ formatstring, names, fixes = sstruct.getformat(headFormat)
+ for name in names:
+ value = getattr(self, name)
+ if name in ("created", "modified"):
+ value = time.asctime(time.gmtime(max(0, value + mac_epoch_diff)))
+ if type(value) == type(0L):
+ value=int(value)
+ if name in ("magicNumber", "checkSumAdjustment"):
+ value = hex(value)
+ elif name == "flags":
+ value = num2binary(value, 16)
+ writer.simpletag(name, value=value)
+ writer.newline()
+
+ def fromXML(self, (name, attrs, content), ttFont):
+ value = attrs["value"]
+ if name in ("created", "modified"):
+ value = parse_date(value) - mac_epoch_diff
+ elif name == "flags":
+ value = binary2num(value)
+ else:
+ value = safeEval(value)
+ setattr(self, name, value)
+
+ def __cmp__(self, other):
+ selfdict = self.__dict__.copy()
+ otherdict = other.__dict__.copy()
+ # for testing purposes, compare without the modified and checkSumAdjustment
+ # fields, since they are allowed to be different.
+ for key in ["modified", "checkSumAdjustment"]:
+ del selfdict[key]
+ del otherdict[key]
+ return cmp(selfdict, otherdict)
+
+
+def calc_mac_epoch_diff():
+ """calculate the difference between the original Mac epoch (1904)
+ to the epoch on this machine.
+ """
+ safe_epoch_t = (1971, 1, 1, 0, 0, 0, 0, 0, 0)
+ safe_epoch = time.mktime(safe_epoch_t) - time.timezone
+ assert time.gmtime(safe_epoch)[:6] == safe_epoch_t[:6]
+ seconds1904to1971 = 60 * 60 * 24 * (365 * (1971-1904) + 17) # thanks, Laurence!
+ return long(safe_epoch - seconds1904to1971)
+
+mac_epoch_diff = calc_mac_epoch_diff()
+
+
+_months = map(string.lower, calendar.month_abbr)
+_weekdays = map(string.lower, calendar.day_abbr)
+
+def parse_date(datestring):
+ datestring = string.lower(datestring)
+ weekday, month, day, tim, year = string.split(datestring)
+ weekday = _weekdays.index(weekday)
+ month = _months.index(month)
+ year = int(year)
+ day = int(day)
+ hour, minute, second = map(int, string.split(tim, ":"))
+ t = (year, month, day, hour, minute, second, weekday, 0, 0)
+ return long(time.mktime(t) - time.timezone)
+
+
+def bin2long(data):
+ # thanks </F>!
+ v = 0L
+ for i in map(ord, data):
+ v = v<<8 | i
+ return v
+
+def long2bin(v, bytes=8):
+ data = ""
+ while v:
+ data = chr(v & 0xff) + data
+ v = v >> 8
+ data = (bytes - len(data)) * "\0" + data
+ assert len(data) == 8, "long too long"
+ return data
+
diff --git a/Lib/fontTools/ttLib/tables/_h_h_e_a.py b/Lib/fontTools/ttLib/tables/_h_h_e_a.py
new file mode 100644
index 0000000..fca75cb
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/_h_h_e_a.py
@@ -0,0 +1,78 @@
+import DefaultTable
+import sstruct
+from fontTools.misc.textTools import safeEval
+
+hheaFormat = """
+ > # big endian
+ tableVersion: 16.16F
+ ascent: h
+ descent: h
+ lineGap: h
+ advanceWidthMax: H
+ minLeftSideBearing: h
+ minRightSideBearing: h
+ xMaxExtent: h
+ caretSlopeRise: h
+ caretSlopeRun: h
+ reserved0: h
+ reserved1: h
+ reserved2: h
+ reserved3: h
+ reserved4: h
+ metricDataFormat: h
+ numberOfHMetrics: H
+"""
+
+class table__h_h_e_a(DefaultTable.DefaultTable):
+
+ dependencies = ['hmtx', 'glyf']
+
+ def decompile(self, data, ttFont):
+ sstruct.unpack(hheaFormat, data, self)
+
+ def compile(self, ttFont):
+ self.recalc(ttFont)
+ return sstruct.pack(hheaFormat, self)
+
+ def recalc(self, ttFont):
+ hmtxTable = ttFont['hmtx']
+ if ttFont.has_key('glyf'):
+ if not ttFont.isLoaded('glyf'):
+ return
+ glyfTable = ttFont['glyf']
+ advanceWidthMax = -100000 # arbitrary big negative number
+ minLeftSideBearing = 100000 # arbitrary big number
+ minRightSideBearing = 100000 # arbitrary big number
+ xMaxExtent = -100000 # arbitrary big negative number
+
+ for name in ttFont.getGlyphOrder():
+ width, lsb = hmtxTable[name]
+ g = glyfTable[name]
+ if g.numberOfContours <= 0:
+ continue
+ advanceWidthMax = max(advanceWidthMax, width)
+ minLeftSideBearing = min(minLeftSideBearing, lsb)
+ rsb = width - lsb - (g.xMax - g.xMin)
+ minRightSideBearing = min(minRightSideBearing, rsb)
+ extent = lsb + (g.xMax - g.xMin)
+ xMaxExtent = max(xMaxExtent, extent)
+ self.advanceWidthMax = advanceWidthMax
+ self.minLeftSideBearing = minLeftSideBearing
+ self.minRightSideBearing = minRightSideBearing
+ self.xMaxExtent = xMaxExtent
+ else:
+ # XXX CFF recalc...
+ pass
+
+ def toXML(self, writer, ttFont):
+ formatstring, names, fixes = sstruct.getformat(hheaFormat)
+ for name in names:
+ value = getattr(self, name)
+ if type(value) == type(0L):
+ value = int(value)
+ writer.simpletag(name, value=value)
+ writer.newline()
+
+ def fromXML(self, (name, attrs, content), ttFont):
+ setattr(self, name, safeEval(attrs["value"]))
+
diff --git a/Lib/fontTools/ttLib/tables/_h_m_t_x.py b/Lib/fontTools/ttLib/tables/_h_m_t_x.py
new file mode 100644
index 0000000..35fa8cd
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/_h_m_t_x.py
@@ -0,0 +1,94 @@
+import DefaultTable
+import Numeric
+from fontTools import ttLib
+from fontTools.misc.textTools import safeEval
+
+
+class table__h_m_t_x(DefaultTable.DefaultTable):
+
+ headerTag = 'hhea'
+ advanceName = 'width'
+ sideBearingName = 'lsb'
+ numberOfMetricsName = 'numberOfHMetrics'
+
+ def decompile(self, data, ttFont):
+ numberOfMetrics = int(getattr(ttFont[self.headerTag], self.numberOfMetricsName))
+ metrics = Numeric.fromstring(data[:4 * numberOfMetrics],
+ Numeric.Int16)
+ if ttLib.endian <> "big":
+ metrics = metrics.byteswapped()
+ metrics.shape = (numberOfMetrics, 2)
+ data = data[4 * numberOfMetrics:]
+ numberOfSideBearings = ttFont['maxp'].numGlyphs - numberOfMetrics
+ numberOfSideBearings = int(numberOfSideBearings)
+ if numberOfSideBearings:
+ assert numberOfSideBearings > 0, "bad hmtx/vmtx table"
+ lastAdvance = metrics[-1][0]
+ advances = Numeric.array([lastAdvance] * numberOfSideBearings,
+ Numeric.Int16)
+ sideBearings = Numeric.fromstring(data[:2 * numberOfSideBearings],
+ Numeric.Int16)
+ if ttLib.endian <> "big":
+ sideBearings = sideBearings.byteswapped()
+ data = data[2 * numberOfSideBearings:]
+ additionalMetrics = Numeric.array([advances, sideBearings],
+ Numeric.Int16)
+ metrics = Numeric.concatenate((metrics,
+ Numeric.transpose(additionalMetrics)))
+ if data:
+ raise ttLib.TTLibError, "too much data for hmtx/vmtx table"
+ metrics = metrics.tolist()
+ self.metrics = {}
+ for i in range(len(metrics)):
+ glyphName = ttFont.getGlyphName(i)
+ self.metrics[glyphName] = metrics[i]
+
+ def compile(self, ttFont):
+ metrics = []
+ for glyphName in ttFont.getGlyphOrder():
+ metrics.append(self.metrics[glyphName])
+ lastAdvance = metrics[-1][0]
+ lastIndex = len(metrics)
+ while metrics[lastIndex-2][0] == lastAdvance:
+ lastIndex = lastIndex - 1
+ additionalMetrics = metrics[lastIndex:]
+ additionalMetrics = map(lambda (advance, sb): sb, additionalMetrics)
+ metrics = metrics[:lastIndex]
+ setattr(ttFont[self.headerTag], self.numberOfMetricsName, len(metrics))
+
+ metrics = Numeric.array(metrics, Numeric.Int16)
+ if ttLib.endian <> "big":
+ metrics = metrics.byteswapped()
+ data = metrics.tostring()
+
+ additionalMetrics = Numeric.array(additionalMetrics, Numeric.Int16)
+ if ttLib.endian <> "big":
+ additionalMetrics = additionalMetrics.byteswapped()
+ data = data + additionalMetrics.tostring()
+ return data
+
+ def toXML(self, writer, ttFont):
+ names = self.metrics.keys()
+ names.sort()
+ for glyphName in names:
+ advance, sb = self.metrics[glyphName]
+ writer.simpletag("mtx", [
+ ("name", glyphName),
+ (self.advanceName, advance),
+ (self.sideBearingName, sb),
+ ])
+ writer.newline()
+
+ def fromXML(self, (name, attrs, content), ttFont):
+ if not hasattr(self, "metrics"):
+ self.metrics = {}
+ if name == "mtx":
+ self.metrics[attrs["name"]] = [safeEval(attrs[self.advanceName]),
+ safeEval(attrs[self.sideBearingName])]
+
+ def __getitem__(self, glyphName):
+ return self.metrics[glyphName]
+
+ def __setitem__(self, glyphName, (advance, sb)):
+ self.metrics[glyphName] = advance, sb
+
diff --git a/Lib/fontTools/ttLib/tables/_k_e_r_n.py b/Lib/fontTools/ttLib/tables/_k_e_r_n.py
new file mode 100644
index 0000000..283c52f
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/_k_e_r_n.py
@@ -0,0 +1,186 @@
+import DefaultTable
+import struct
+import ttLib.sfnt
+from fontTools.misc.textTools import safeEval, readHex
+
+
+class table__k_e_r_n(DefaultTable.DefaultTable):
+
+ def getkern(self, format):
+ for subtable in self.kernTables:
+ if subtable.version == format:
+ return subtable
+ return None # not found
+
+ def decompile(self, data, ttFont):
+ version, nTables = struct.unpack(">HH", data[:4])
+ if version == 1:
+ # Apple's new format. Hm.
+ version, nTables = struct.unpack(">ll", data[:8])
+ self.version = version / float(0x10000)
+ data = data[8:]
+ else:
+ self.version = version
+ data = data[4:]
+ tablesIndex = []
+ self.kernTables = []
+ for i in range(nTables):
+ version, length = struct.unpack(">HH", data[:4])
+ length = int(length)
+ if not kern_classes.has_key(version):
+ subtable = KernTable_format_unkown()
+ else:
+ subtable = kern_classes[version]()
+ subtable.decompile(data[:length], ttFont)
+ self.kernTables.append(subtable)
+ data = data[length:]
+
+ def compile(self, ttFont):
+ nTables = len(self.kernTables)
+ if self.version == 1.0:
+ # Apple's new format.
+ data = struct.pack(">ll", self.version * 0x1000, nTables)
+ else:
+ data = struct.pack(">HH", self.version, nTables)
+ for subtable in self.kernTables:
+ data = data + subtable.compile(ttFont)
+ return data
+
+ def toXML(self, writer, ttFont):
+ writer.simpletag("version", value=self.version)
+ writer.newline()
+ for subtable in self.kernTables:
+ subtable.toXML(writer, ttFont)
+
+ def fromXML(self, (name, attrs, content), ttFont):
+ if name == "version":
+ self.version = safeEval(attrs["value"])
+ return
+ if name <> "kernsubtable":
+ return
+ if not hasattr(self, "kernTables"):
+ self.kernTables = []
+ format = safeEval(attrs["format"])
+ if not kern_classes.has_key(format):
+ subtable = KernTable_format_unkown()
+ else:
+ subtable = kern_classes[format]()
+ self.kernTables.append(subtable)
+ subtable.fromXML((name, attrs, content), ttFont)
+
+
+class KernTable_format_0:
+
+ def decompile(self, data, ttFont):
+ version, length, coverage = struct.unpack(">HHH", data[:6])
+ self.version, self.coverage = int(version), int(coverage)
+ data = data[6:]
+
+ self.kernTable = kernTable = {}
+
+ nPairs, searchRange, entrySelector, rangeShift = struct.unpack(">HHHH", data[:8])
+ data = data[8:]
+
+ for k in range(nPairs):
+ left, right, value = struct.unpack(">HHh", data[:6])
+ data = data[6:]
+ left, right = int(left), int(right)
+ kernTable[(ttFont.getGlyphName(left), ttFont.getGlyphName(right))] = value
+ assert len(data) == 0
+
+ def compile(self, ttFont):
+ nPairs = len(self.kernTable)
+ entrySelector = ttLib.sfnt.maxpoweroftwo(nPairs)
+ searchRange = (2 ** entrySelector) * 6
+ rangeShift = (nPairs - (2 ** entrySelector)) * 6
+ data = struct.pack(">HHHH", nPairs, searchRange, entrySelector, rangeShift)
+
+ # yeehee! (I mean, turn names into indices)
+ kernTable = map(lambda ((left, right), value), getGlyphID=ttFont.getGlyphID:
+ (getGlyphID(left), getGlyphID(right), value),
+ self.kernTable.items())
+ kernTable.sort()
+ for left, right, value in kernTable:
+ data = data + struct.pack(">HHh", left, right, value)
+ return struct.pack(">HHH", self.version, len(data) + 6, self.coverage) + data
+
+ def toXML(self, writer, ttFont):
+ writer.begintag("kernsubtable", coverage=self.coverage, format=0)
+ writer.newline()
+ items = self.kernTable.items()
+ items.sort()
+ for (left, right), value in items:
+ writer.simpletag("pair", [
+ ("l", left),
+ ("r", right),
+ ("v", value)
+ ])
+ writer.newline()
+ writer.endtag("kernsubtable")
+ writer.newline()
+
+ def fromXML(self, (name, attrs, content), ttFont):
+ self.coverage = safeEval(attrs["coverage"])
+ self.version = safeEval(attrs["format"])
+ if not hasattr(self, "kernTable"):
+ self.kernTable = {}
+ for element in content:
+ if type(element) <> type(()):
+ continue
+ name, attrs, content = element
+ self.kernTable[(attrs["l"], attrs["r"])] = safeEval(attrs["v"])
+
+ def __getitem__(self, pair):
+ return self.kernTable[pair]
+
+ def __setitem__(self, pair, value):
+ self.kernTable[pair] = value
+
+ def __delitem__(self, pair):
+ del self.kernTable[pair]
+
+ def __cmp__(self, other):
+ return cmp(self.__dict__, other.__dict__)
+
+
+class KernTable_format_2:
+
+ def decompile(self, data, ttFont):
+ self.data = data
+
+ def compile(self, ttFont, ttFont):
+ return data
+
+ def toXML(self, writer):
+ writer.begintag("kernsubtable", format=2)
+ writer.newline()
+ writer.dumphex(self.data)
+ writer.endtag("kernsubtable")
+ writer.newline()
+
+ def fromXML(self, (name, attrs, content), ttFont):
+ self.decompile(readHex(content))
+
+
+class KernTable_format_unkown:
+
+ def decompile(self, data, ttFont):
+ self.data = data
+
+ def compile(self, ttFont):
+ return data
+
+ def toXML(self, writer, ttFont):
+ writer.begintag("kernsubtable", format="-1")
+ writer.newline()
+ writer.comment("unknown 'kern' subtable format")
+ writer.dumphex(self.data)
+ writer.endtag("kernsubtable")
+ writer.newline()
+
+ def fromXML(self, (name, attrs, content), ttFont):
+ self.decompile(readHex(content))
+
+
+
+kern_classes = {0: KernTable_format_0, 1: KernTable_format_2}
diff --git a/Lib/fontTools/ttLib/tables/_l_o_c_a.py b/Lib/fontTools/ttLib/tables/_l_o_c_a.py
new file mode 100644
index 0000000..332a708
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/_l_o_c_a.py
@@ -0,0 +1,55 @@
+import DefaultTable
+import array
+import Numeric
+from fontTools import ttLib
+import struct
+
+class table__l_o_c_a(DefaultTable.DefaultTable):
+
+ dependencies = ['glyf']
+
+ def decompile(self, data, ttFont):
+ longFormat = ttFont['head'].indexToLocFormat
+ if longFormat:
+ format = "l"
+ else:
+ format = "H"
+ locations = array.array(format)
+ locations.fromstring(data)
+ if ttLib.endian <> "big":
+ locations.byteswap()
+ locations = Numeric.array(locations, Numeric.Int32)
+ if not longFormat:
+ locations = locations * 2
+ if len(locations) <> (ttFont['maxp'].numGlyphs + 1):
+ raise ttLib.TTLibError, "corrupt 'loca' table"
+ self.locations = locations
+
+ def compile(self, ttFont):
+ locations = self.locations
+ if max(locations) < 0x20000:
+ locations = locations / 2
+ locations = locations.astype(Numeric.Int16)
+ ttFont['head'].indexToLocFormat = 0
+ else:
+ ttFont['head'].indexToLocFormat = 1
+ if ttLib.endian <> "big":
+ locations = locations.byteswapped()
+ return locations.tostring()
+
+ def set(self, locations):
+ self.locations = Numeric.array(locations, Numeric.Int32)
+
+ def toXML(self, writer, ttFont):
+ writer.comment("The 'loca' table will be calculated by the compiler")
+ writer.newline()
+
+ def __getitem__(self, index):
+ return self.locations[index]
+
+ def __len__(self):
+ return len(self.locations)
+
+ def __cmp__(self, other):
+ return cmp(len(self), len(other)) or not Numeric.alltrue(Numeric.equal(self.locations, other.locations))
+
diff --git a/Lib/fontTools/ttLib/tables/_m_a_x_p.py b/Lib/fontTools/ttLib/tables/_m_a_x_p.py
new file mode 100644
index 0000000..1c75e6d
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/_m_a_x_p.py
@@ -0,0 +1,138 @@
+import DefaultTable
+import sstruct
+from fontTools.misc.textTools import safeEval
+
+maxpFormat_0_5 = """
+ > # big endian
+ tableVersion: i
+ numGlyphs: H
+"""
+
+maxpFormat_1_0_add = """
+ > # big endian
+ maxPoints: H
+ maxContours: H
+ maxCompositePoints: H
+ maxCompositeContours: H
+ maxZones: H
+ maxTwilightPoints: H
+ maxStorage: H
+ maxFunctionDefs: H
+ maxInstructionDefs: H
+ maxStackElements: H
+ maxSizeOfInstructions: H
+ maxComponentElements: H
+ maxComponentDepth: H
+"""
+
+
+class table__m_a_x_p(DefaultTable.DefaultTable):
+
+ dependencies = ['glyf']
+
+ def decompile(self, data, ttFont):
+ dummy, data = sstruct.unpack2(maxpFormat_0_5, data, self)
+ self.numGlyphs = int(self.numGlyphs)
+ if self.tableVersion == 0x00010000:
+ dummy, data = sstruct.unpack2(maxpFormat_1_0_add, data, self)
+ else:
+ assert self.tableVersion == 0x00005000, "unknown 'maxp' format: %x" % self.tableVersion
+ assert len(data) == 0
+
+ def compile(self, ttFont):
+ if ttFont.has_key('glyf'):
+ if ttFont.isLoaded('glyf'):
+ self.recalc(ttFont)
+ else:
+ pass # XXX CFF!!!
+ data = sstruct.pack(maxpFormat_0_5, self)
+ if self.tableVersion == 0x00010000:
+ data = data + sstruct.pack(maxpFormat_1_0_add, self)
+ else:
+ assert self.tableVersion == 0x00005000, "unknown 'maxp' format: %f" % self.tableVersion
+ return data
+
+ def recalc(self, ttFont):
+ """Recalculate the font bounding box, and most other maxp values except
+ for the TT instructions values. Also recalculate the value of bit 1
+ of the flags field of the 'head' table.
+ """
+ glyfTable = ttFont['glyf']
+ hmtxTable = ttFont['hmtx']
+ headTable = ttFont['head']
+ self.numGlyphs = len(glyfTable)
+ xMin = 100000
+ yMin = 100000
+ xMax = -100000
+ yMax = -100000
+ maxPoints = 0
+ maxContours = 0
+ maxCompositePoints = 0
+ maxCompositeContours = 0
+ maxComponentElements = 0
+ maxComponentDepth = 0
+ allXMaxIsLsb = 1
+ for glyphName in ttFont.getGlyphOrder():
+ g = glyfTable[glyphName]
+ if g.numberOfContours:
+ if hmtxTable[glyphName][1] <> g.xMin:
+ allXMaxIsLsb = 0
+ xMin = min(xMin, g.xMin)
+ yMin = min(yMin, g.yMin)
+ xMax = max(xMax, g.xMax)
+ yMax = max(yMax, g.yMax)
+ if g.numberOfContours > 0:
+ nPoints, nContours = g.getMaxpValues()
+ maxPoints = max(maxPoints, nPoints)
+ maxContours = max(maxContours, nContours)
+ else:
+ nPoints, nContours, componentDepth = g.getCompositeMaxpValues(glyfTable)
+ maxCompositePoints = max(maxCompositePoints, nPoints)
+ maxCompositeContours = max(maxCompositeContours, nContours)
+ maxComponentElements = max(maxComponentElements, len(g.components))
+ maxComponentDepth = max(maxComponentDepth, componentDepth)
+ self.xMin = xMin
+ self.yMin = yMin
+ self.xMax = xMax
+ self.yMax = yMax
+ self.maxPoints = maxPoints
+ self.maxContours = maxContours
+ self.maxCompositePoints = maxCompositePoints
+ self.maxCompositeContours = maxCompositeContours
+ self.maxComponentDepth = maxComponentDepth
+ if allXMaxIsLsb:
+ headTable.flags = headTable.flags | 0x2
+ else:
+ headTable.flags = headTable.flags & ~0x2
+
+ def testrepr(self):
+ items = self.__dict__.items()
+ items.sort()
+ print ". . . . . . . . ."
+ for combo in items:
+ print " %s: %s" % combo
+ print ". . . . . . . . ."
+
+ def toXML(self, writer, ttFont):
+ if self.tableVersion <> 0x00005000:
+ writer.comment("Most of this table will be recalculated by the compiler")
+ writer.newline()
+ formatstring, names, fixes = sstruct.getformat(maxpFormat_0_5)
+ if self.tableVersion == 0x00010000:
+ formatstring, names_1_0, fixes = sstruct.getformat(maxpFormat_1_0_add)
+ names = names + names_1_0
+ else:
+ assert self.tableVersion == 0x00005000, "unknown 'maxp' format: %f" % self.tableVersion
+ for name in names:
+ value = getattr(self, name)
+ if type(value) == type(0L):
+ value=int(value)
+ if name == "tableVersion":
+ value = hex(value)
+ writer.simpletag(name, value=value)
+ writer.newline()
+
+ def fromXML(self, (name, attrs, content), ttFont):
+ setattr(self, name, safeEval(attrs["value"]))
+
+
diff --git a/Lib/fontTools/ttLib/tables/_n_a_m_e.py b/Lib/fontTools/ttLib/tables/_n_a_m_e.py
new file mode 100644
index 0000000..05cde33
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/_n_a_m_e.py
@@ -0,0 +1,136 @@
+import DefaultTable
+import struct, sstruct
+from fontTools.misc.textTools import safeEval
+import string
+import types
+
+nameRecordFormat = """
+ > # big endian
+ platformID: H
+ platEncID: H
+ langID: H
+ nameID: H
+ length: H
+ offset: H
+"""
+
+class table__n_a_m_e(DefaultTable.DefaultTable):
+
+ def decompile(self, data, ttFont):
+ format, n, stringoffset = struct.unpack(">HHH", data[:6])
+ stringoffset = int(stringoffset)
+ stringData = data[stringoffset:]
+ data = data[6:stringoffset]
+ self.names = []
+ for i in range(n):
+ name, data = sstruct.unpack2(nameRecordFormat, data, NameRecord())
+ name.fixlongs()
+ name.string = stringData[name.offset:name.offset+name.length]
+ del name.offset, name.length
+ self.names.append(name)
+
+ def compile(self, ttFont):
+ self.names.sort() # sort according to the spec; see NameRecord.__cmp__()
+ stringData = ""
+ format = 0
+ n = len(self.names)
+ stringoffset = 6 + n * sstruct.calcsize(nameRecordFormat)
+ data = struct.pack(">HHH", format, n, stringoffset)
+ lastoffset = 0
+ done = {} # remember the data so we can reuse the "pointers"
+ for name in self.names:
+ if done.has_key(name.string):
+ name.offset, name.length = done[name.string]
+ else:
+ name.offset, name.length = done[name.string] = len(stringData), len(name.string)
+ stringData = stringData + name.string
+ data = data + sstruct.pack(nameRecordFormat, name)
+ return data + stringData
+
+ def toXML(self, writer, ttFont):
+ for name in self.names:
+ name.toXML(writer, ttFont)
+
+ def fromXML(self, (name, attrs, content), ttFont):
+ if name <> "namerecord":
+ return # ignore unknown tags
+ if not hasattr(self, "names"):
+ self.names = []
+ name = NameRecord()
+ self.names.append(name)
+ name.fromXML((name, attrs, content), ttFont)
+
+ def getname(self, nameID, platformID, platEncID, langID=None):
+ for namerecord in self.names:
+ if ( namerecord.nameID == nameID and
+ namerecord.platformID == platformID and
+ namerecord.platEncID == platEncID):
+ if langID is None or namerecord.langID == langID:
+ return namerecord
+ return None # not found
+
+ def __cmp__(self, other):
+ return cmp(self.names, other.names)
+
+
+class NameRecord:
+
+ def toXML(self, writer, ttFont):
+ writer.begintag("namerecord", [
+ ("nameID", self.nameID),
+ ("platformID", self.platformID),
+ ("platEncID", self.platEncID),
+ ("langID", hex(self.langID)),
+ ])
+ writer.newline()
+ if self.platformID == 0 or (self.platformID == 3 and self.platEncID == 1):
+ writer.write16bit(self.string)
+ else:
+ writer.write8bit(self.string)
+ writer.newline()
+ writer.endtag("namerecord")
+ writer.newline()
+
+ def fromXML(self, (name, attrs, content), ttFont):
+ self.nameID = safeEval(attrs["nameID"])
+ self.platformID = safeEval(attrs["platformID"])
+ self.platEncID = safeEval(attrs["platEncID"])
+ self.langID = safeEval(attrs["langID"])
+ if self.platformID == 0 or (self.platformID == 3 and self.platEncID == 1):
+ from fontTools.ttLib.xmlImport import UnicodeString
+ str = UnicodeString("")
+ for element in content:
+ str = str + element
+ self.string = str.stripped().tostring()
+ else:
+ self.string = string.strip(string.join(content, ""))
+
+ def __cmp__(self, other):
+ """Compare method, so a list of NameRecords can be sorted
+ according to the spec by just sorting it..."""
+ selftuple = (self.platformID,
+ self.platEncID,
+ self.langID,
+ self.nameID,
+ self.string)
+ othertuple = (other.platformID,
+ other.platEncID,
+ other.langID,
+ other.nameID,
+ other.string)
+ return cmp(selftuple, othertuple)
+
+ def __repr__(self):
+ return "<NameRecord NameID=%d; PlatformID=%d; LanguageID=%d>" % (
+ self.nameID, self.platformID, self.langID)
+
+ def fixlongs(self):
+ """correct effects from bug in Python 1.5.1, where "H"
+ returns a Python Long int.
+ This has been fixed in Python 1.5.2.
+ """
+ for attr in dir(self):
+ val = getattr(self, attr)
+ if type(val) == types.LongType:
+ setattr(self, attr, int(val))
+
diff --git a/Lib/fontTools/ttLib/tables/_p_o_s_t.py b/Lib/fontTools/ttLib/tables/_p_o_s_t.py
new file mode 100644
index 0000000..273de79
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/_p_o_s_t.py
@@ -0,0 +1,214 @@
+from fontTools.ttLib.standardGlyphOrder import standardGlyphOrder
+import DefaultTable
+import struct, sstruct
+import array
+from fontTools import ttLib
+from fontTools.misc.textTools import safeEval, readHex
+
+
+postFormat = """
+ >
+ formatType: 16.16F
+ italicAngle: 16.16F # italic angle in degrees
+ underlinePosition: h
+ underlineThickness: h
+ isFixedPitch: l
+ minMemType42: l # minimum memory if TrueType font is downloaded
+ maxMemType42: l # maximum memory if TrueType font is downloaded
+ minMemType1: l # minimum memory if Type1 font is downloaded
+ maxMemType1: l # maximum memory if Type1 font is downloaded
+"""
+
+postFormatSize = sstruct.calcsize(postFormat)
+
+
+class table__p_o_s_t(DefaultTable.DefaultTable):
+
+ def decompile(self, data, ttFont):
+ sstruct.unpack(postFormat, data[:postFormatSize], self)
+ data = data[postFormatSize:]
+ if self.formatType == 1.0:
+ self.decode_format_1_0(data, ttFont)
+ elif self.formatType == 2.0:
+ self.decode_format_2_0(data, ttFont)
+ elif self.formatType == 3.0:
+ self.decode_format_3_0(data, ttFont)
+ else:
+ # supported format
+ raise ttLib.TTLibError, "'post' table format %f not supported" % self.formatType
+
+ def compile(self, ttFont):
+ data = sstruct.pack(postFormat, self)
+ if self.formatType == 1.0:
+ pass # we're done
+ elif self.formatType == 2.0:
+ data = data + self.encode_format_2_0(ttFont)
+ elif self.formatType == 3.0:
+ pass # we're done
+ else:
+ # supported format
+ raise ttLib.TTLibError, "'post' table format %f not supported" % self.formatType
+ return data
+
+ def getGlyphOrder(self):
+ """This function will get called by a ttLib.TTFont instance.
+ Do not call this function yourself, use TTFont().getGlyphOrder()
+ or its relatives instead!
+ """
+ if not hasattr(self, "glyphOrder"):
+ raise ttLib.TTLibError, "illegal use of getGlyphOrder()"
+ glyphOrder = self.glyphOrder
+ del self.glyphOrder
+ return glyphOrder
+
+ def decode_format_1_0(self, data, ttFont):
+ self.glyphOrder = standardGlyphOrder[:]
+
+ def decode_format_2_0(self, data, ttFont):
+ numGlyphs, = struct.unpack(">H", data[:2])
+ numGlyphs = int(numGlyphs)
+ data = data[2:]
+ indices = array.array("H")
+ indices.fromstring(data[:2*numGlyphs])
+ if ttLib.endian <> "big":
+ indices.byteswap()
+ data = data[2*numGlyphs:]
+ self.extraNames = extraNames = unpackPStrings(data)
+ self.glyphOrder = glyphOrder = [None] * int(ttFont['maxp'].numGlyphs)
+ for glyphID in range(numGlyphs):
+ index = indices[glyphID]
+ if index > 257:
+ name = extraNames[index-258]
+ else:
+ # fetch names from standard list
+ name = standardGlyphOrder[index]
+ glyphOrder[glyphID] = name
+ #AL990511: code added to handle the case of new glyphs without
+ # entries into the 'post' table
+ if numGlyphs < ttFont['maxp'].numGlyphs:
+ for i in range(numGlyphs, ttFont['maxp'].numGlyphs):
+ glyphOrder[i] = "glyph#%.5d" % i
+ self.extraNames.append(glyphOrder[i])
+ self.build_psNameMapping(ttFont)
+
+ def build_psNameMapping(self, ttFont):
+ mapping = {}
+ allNames = {}
+ for i in range(ttFont['maxp'].numGlyphs):
+ glyphName = psName = self.glyphOrder[i]
+ if allNames.has_key(glyphName):
+ # make up a new glyphName that's unique
+ n = 1
+ while allNames.has_key(glyphName + "#" + `n`):
+ n = n + 1
+ glyphName = glyphName + "#" + `n`
+ self.glyphOrder[i] = glyphName
+ mapping[glyphName] = psName
+ allNames[glyphName] = psName
+ self.mapping = mapping
+
+ def decode_format_3_0(self, data, ttFont):
+ # Setting self.glyphOrder to None will cause the TTFont object
+ # try and construct glyph names from a Unicode cmap table.
+ self.glyphOrder = None
+
+ def encode_format_2_0(self, ttFont):
+ numGlyphs = ttFont['maxp'].numGlyphs
+ glyphOrder = ttFont.getGlyphOrder()
+ assert len(glyphOrder) == numGlyphs
+ indices = array.array("H")
+ for glyphID in range(numGlyphs):
+ glyphName = glyphOrder[glyphID]
+ if self.mapping.has_key(glyphName):
+ psName = self.mapping[glyphName]
+ else:
+ psName = glyphName
+ if psName in self.extraNames:
+ index = 258 + self.extraNames.index(psName)
+ elif psName in standardGlyphOrder:
+ index = standardGlyphOrder.index(psName)
+ else:
+ index = 258 + len(self.extraNames)
+ extraNames.append(psName)
+ indices.append(index)
+ if ttLib.endian <> "big":
+ indices.byteswap()
+ return struct.pack(">H", numGlyphs) + indices.tostring() + packPStrings(self.extraNames)
+
+ def toXML(self, writer, ttFont):
+ formatstring, names, fixes = sstruct.getformat(postFormat)
+ for name in names:
+ value = getattr(self, name)
+ writer.simpletag(name, value=value)
+ writer.newline()
+ if hasattr(self, "mapping"):
+ writer.begintag("psNames")
+ writer.newline()
+ writer.comment("This file uses unique glyph names based on the information\n"
+ "found in the 'post' table. Since these names might not be unique,\n"
+ "we have to invent artificial names in case of clashes. In order to\n"
+ "be able to retain the original information, we need a name to\n"
+ "ps name mapping for those cases where they differ. That's what\n"
+ "you see below.\n")
+ writer.newline()
+ items = self.mapping.items()
+ items.sort()
+ for name, psName in items:
+ writer.simpletag("psName", name=name, psName=psName)
+ writer.newline()
+ writer.endtag("psNames")
+ writer.newline()
+ if hasattr(self, "extraNames"):
+ writer.begintag("extraNames")
+ writer.newline()
+ writer.comment("following are the name that are not taken from the standard Mac glyph order")
+ writer.newline()
+ for name in self.extraNames:
+ writer.simpletag("psName", name=name)
+ writer.newline()
+ writer.endtag("extraNames")
+ writer.newline()
+ if hasattr(self, "data"):
+ writer.begintag("hexdata")
+ writer.newline()
+ writer.dumphex(self.data)
+ writer.endtag("hexdata")
+ writer.newline()
+
+ def fromXML(self, (name, attrs, content), ttFont):
+ if name not in ("psNames", "extraNames", "hexdata"):
+ setattr(self, name, safeEval(attrs["value"]))
+ elif name == "psNames":
+ self.mapping = {}
+ for element in content:
+ if type(element) <> type(()):
+ continue
+ name, attrs, content = element
+ if name == "psName":
+ self.mapping[attrs["name"]] = attrs["psName"]
+ elif name == "extraNames":
+ self.extraNames = []
+ for element in content:
+ if type(element) <> type(()):
+ continue
+ name, attrs, content = element
+ if name == "psName":
+ self.extraNames.append(attrs["name"])
+ else:
+ self.data = readHex(content)
+
+
+def unpackPStrings(data):
+ strings = []
+ while data:
+ length = ord(data[0])
+ strings.append(data[1:1+length])
+ data = data[1+length:]
+ return strings
+
+def packPStrings(strings):
+ data = ""
+ for s in strings:
+ data = data + chr(len(s)) + s
+ return data
+
diff --git a/Lib/fontTools/ttLib/tables/_p_r_e_p.py b/Lib/fontTools/ttLib/tables/_p_r_e_p.py
new file mode 100644
index 0000000..005c8ee
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/_p_r_e_p.py
@@ -0,0 +1,14 @@
+import DefaultTable
+import array
+
+class table__p_r_e_p(DefaultTable.DefaultTable):
+
+ def decompile(self, data, ttFont):
+ self.prep = data
+
+ def compile(self, ttFont):
+ return self.prep
+
+ def __len__(self):
+ return len(self.prep)
+
diff --git a/Lib/fontTools/ttLib/tables/_v_h_e_a.py b/Lib/fontTools/ttLib/tables/_v_h_e_a.py
new file mode 100644
index 0000000..43fd614
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/_v_h_e_a.py
@@ -0,0 +1,78 @@
+import DefaultTable
+import sstruct
+from fontTools.misc.textTools import safeEval
+
+vheaFormat = """
+ > # big endian
+ tableVersion: 16.16F
+ ascent: h
+ descent: h
+ lineGap: h
+ advanceHeightMax: H
+ minTopSideBearing: h
+ minBottomSideBearing: h
+ yMaxExtent: h
+ caretSlopeRise: h
+ caretSlopeRun: h
+ reserved0: h
+ reserved1: h
+ reserved2: h
+ reserved3: h
+ reserved4: h
+ metricDataFormat: h
+ numberOfVMetrics: H
+"""
+
+class table__v_h_e_a(DefaultTable.DefaultTable):
+
+ dependencies = ['vmtx', 'glyf']
+
+ def decompile(self, data, ttFont):
+ sstruct.unpack(vheaFormat, data, self)
+
+ def compile(self, ttFont):
+ self.recalc(ttFont)
+ return sstruct.pack(vheaFormat, self)
+
+ def recalc(self, ttFont):
+ vtmxTable = ttFont['vmtx']
+ if ttFont.has_key('glyf'):
+ if not ttFont.isLoaded('glyf'):
+ return
+ glyfTable = ttFont['glyf']
+ advanceHeightMax = -100000 # arbitrary big negative number
+ minTopSideBearing = 100000 # arbitrary big number
+ minBottomSideBearing = 100000 # arbitrary big number
+ yMaxExtent = -100000 # arbitrary big negative number
+
+ for name in ttFont.getGlyphOrder():
+ height, tsb = vtmxTable[name]
+ g = glyfTable[name]
+ if g.numberOfContours <= 0:
+ continue
+ advanceHeightMax = max(advanceHeightMax, height)
+ minTopSideBearing = min(minTopSideBearing, tsb)
+ rsb = height - tsb - (g.yMax - g.yMin)
+ minBottomSideBearing = min(minBottomSideBearing, rsb)
+ extent = tsb + (g.yMax - g.yMin)
+ yMaxExtent = max(yMaxExtent, extent)
+ self.advanceHeightMax = advanceHeightMax
+ self.minTopSideBearing = minTopSideBearing
+ self.minBottomSideBearing = minBottomSideBearing
+ self.yMaxExtent = yMaxExtent
+ else:
+ # XXX CFF recalc...
+ pass
+
+ def toXML(self, writer, ttFont):
+ formatstring, names, fixes = sstruct.getformat(vheaFormat)
+ for name in names:
+ value = getattr(self, name)
+ if type(value) == type(0L):
+ value = int(value)
+ writer.simpletag(name, value=value)
+ writer.newline()
+
+ def fromXML(self, (name, attrs, content), ttFont):
+ setattr(self, name, safeEval(attrs["value"]))
+
diff --git a/Lib/fontTools/ttLib/tables/_v_m_t_x.py b/Lib/fontTools/ttLib/tables/_v_m_t_x.py
new file mode 100644
index 0000000..dabefe6
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/_v_m_t_x.py
@@ -0,0 +1,14 @@
+import DefaultTable
+import Numeric
+from fontTools import ttLib
+from fontTools.misc.textTools import safeEval
+
+superclass = ttLib.getTableClass("hmtx")
+
+class table__v_m_t_x(superclass):
+
+ headerTag = 'vhea'
+ advanceName = 'height'
+ sideBearingName = 'tsb'
+ numberOfMetricsName = 'numberOfVMetrics'
+
diff --git a/Lib/fontTools/ttLib/tables/otCommon.py b/Lib/fontTools/ttLib/tables/otCommon.py
new file mode 100644
index 0000000..c312c63
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/otCommon.py
@@ -0,0 +1,605 @@
+"""ttLib.tables.otCommon.py -- Various data structures used by various OpenType tables.
+"""
+
+import struct, sstruct
+import DefaultTable
+from fontTools import ttLib
+
+
+class base_GPOS_GSUB(DefaultTable.DefaultTable):
+
+ """Base class for GPOS and GSUB tables; they share the same high-level structure."""
+
+ version = 0x00010000
+
+ def decompile(self, data, otFont):
+ reader = OTTableReader(data)
+ self.version = reader.readLong()
+ if self.version <> 0x00010000:
+ raise ttLib.TTLibError, "unknown table version: 0x%8x" % self.version
+
+ self.scriptList = reader.readTable(ScriptList, otFont, self.tableTag)
+ self.featureList = reader.readTable(FeatureList, otFont, self.tableTag)
+ self.lookupList = reader.readTable(LookupList, otFont, self.tableTag)
+
+ def compile(self, otFont):
+ XXXXXX
+
+ def toXML(self, xmlWriter, otFont):
+ names = [("ScriptList", "scriptList"),
+ ("FeatureList", "featureList"),
+ ("LookupList", "lookupList")]
+ for name, attr in names:
+ xmlWriter.newline()
+ xmlWriter.begintag(name)
+ xmlWriter.newline()
+ table = getattr(self, attr)
+ table.toXML(xmlWriter, otFont)
+ xmlWriter.endtag(name)
+ xmlWriter.newline()
+ xmlWriter.newline()
+
+ def fromXML(self, (name, attrs, content), otFont):
+ xxx
+
+
+#
+# Script List and subtables
+#
+
+class ScriptList:
+
+ def __init__(self, parentTag):
+ self.parentTag = parentTag
+
+ def decompile(self, reader, otFont):
+ scriptCount = reader.readUShort()
+ self.scripts = reader.readTagList(scriptCount, Script, otFont)
+
+ def compile(self, otFont):
+ XXXXXX
+
+ def toXML(self, xmlWriter, otFont):
+ for tag, script in self.scripts:
+ xmlWriter.begintag("Script", tag=tag)
+ xmlWriter.newline()
+ script.toXML(xmlWriter, otFont)
+ xmlWriter.endtag("Script")
+ xmlWriter.newline()
+
+ def fromXML(self, (name, attrs, content), otFont):
+ xxx
+
+
+class Script:
+
+ def decompile(self, reader, otFont):
+ self.defaultLangSystem = None
+ self.defaultLangSystem = reader.readTable(LanguageSystem, otFont)
+ langSysCount = reader.readUShort()
+ self.languageSystems = reader.readTagList(langSysCount, LanguageSystem, otFont)
+
+ def compile(self, otFont):
+ XXXXX
+
+ def toXML(self, xmlWriter, otFont):
+ xmlWriter.begintag("DefaultLanguageSystem")
+ xmlWriter.newline()
+ self.defaultLangSystem.toXML(xmlWriter, otFont)
+ xmlWriter.endtag("DefaultLanguageSystem")
+ xmlWriter.newline()
+ for tag, langSys in self.languageSystems:
+ xmlWriter.begintag("LanguageSystem", tag=tag)
+ xmlWriter.newline()
+ langSys.toXML(xmlWriter, otFont)
+ xmlWriter.endtag("LanguageSystem")
+ xmlWriter.newline()
+
+
+class LanguageSystem:
+
+ def decompile(self, reader, otFont):
+ self.lookupOrder = reader.readUShort()
+ self.reqFeatureIndex = reader.readUShort()
+ featureCount = reader.readUShort()
+ self.featureIndex = reader.readUShortArray(featureCount)
+
+ def compile(self, otFont):
+ xxx
+
+ def toXML(self, xmlWriter, otFont):
+ xmlWriter.simpletag("LookupOrder", value=self.lookupOrder)
+ xmlWriter.newline()
+ xmlWriter.simpletag("ReqFeature", index=hex(self.reqFeatureIndex))
+ xmlWriter.newline()
+ for index in self.featureIndex:
+ xmlWriter.simpletag("Feature", index=index)
+ xmlWriter.newline()
+
+
+#
+# Feature List and subtables
+#
+
+class FeatureList:
+
+ def __init__(self, parentTag):
+ self.parentTag = parentTag
+
+ def decompile(self, reader, otFont):
+ featureCount = reader.readUShort()
+ self.features = reader.readTagList(featureCount, Feature, otFont)
+
+ def compile(self, otFont):
+ XXXXX
+
+ def toXML(self, xmlWriter, otFont):
+ for index in range(len(self.features)):
+ tag, feature = self.features[index]
+ xmlWriter.begintag("Feature", index=index, tag=tag)
+ xmlWriter.newline()
+ feature.toXML(xmlWriter, otFont)
+ xmlWriter.endtag("Feature")
+ xmlWriter.newline()
+
+ def fromXML(self, (name, attrs, content), otFont):
+ xxx
+
+
+class Feature:
+
+ def decompile(self, reader, otFont):
+ self.featureParams = reader.readUShort()
+ lookupCount = reader.readUShort()
+ self.lookupListIndex = reader.readUShortArray(lookupCount)
+
+ def compile(self, otFont):
+ XXXXX
+
+ def toXML(self, xmlWriter, otFont):
+ xmlWriter.simpletag("FeatureParams", value=hex(self.featureParams))
+ xmlWriter.newline()
+ for lookupIndex in self.lookupListIndex:
+ xmlWriter.simpletag("LookupTable", index=lookupIndex)
+ xmlWriter.newline()
+
+ def fromXML(self, (name, attrs, content), otFont):
+ xxx
+
+
+#
+# Lookup List and subtables
+#
+
+class LookupList:
+
+ def __init__(self, parentTag):
+ self.parentTag = parentTag
+
+ def decompile(self, reader, otFont):
+ lookupCount = reader.readUShort()
+ self.lookup = lookup = []
+ for i in range(lookupCount):
+ lookup.append(reader.readTable(LookupTable, otFont, self.parentTag))
+
+ def compile(self, otFont):
+ XXXXX
+
+ def toXML(self, xmlWriter, otFont):
+ for i in range(len(self.lookup)):
+ xmlWriter.newline()
+ lookupTable = self.lookup[i]
+ xmlWriter.begintag("LookupTable", index=i)
+ xmlWriter.newline()
+ lookupTable.toXML(xmlWriter, otFont)
+ xmlWriter.endtag("LookupTable")
+ xmlWriter.newline()
+ xmlWriter.newline()
+
+ def fromXML(self, (name, attrs, content), otFont):
+ xxx
+
+
+class LookupTable:
+
+ def __init__(self, parentTag):
+ self.parentTag = parentTag
+
+ def decompile(self, reader, otFont):
+ parentTable = otFont[self.parentTag]
+ self.lookupType = reader.readUShort()
+ self.lookupFlag = reader.readUShort()
+ subTableCount = reader.readUShort()
+ self.subTables = subTables = []
+ lookupTypeClass = parentTable.getLookupTypeClass(self.lookupType)
+ for i in range(subTableCount):
+ subTables.append(reader.readTable(lookupTypeClass, otFont))
+
+ def compile(self, otFont):
+ XXXXXX
+
+ def __repr__(self):
+ if not hasattr(self, "lookupTypeName"):
+ m = ttLib.getTableModule(self.parentTag)
+ self.lookupTypeName = m.lookupTypeClasses[self.lookupType].__name__
+ return "<%s LookupTable at %x>" % (self.lookupTypeName, id(self))
+
+ def toXML(self, xmlWriter, otFont):
+ xmlWriter.simpletag("LookupFlag", value=hex(self.lookupFlag))
+ xmlWriter.newline()
+ for subTable in self.subTables:
+ name = subTable.__class__.__name__
+ xmlWriter.begintag(name)
+ xmlWriter.newline()
+ subTable.toXML(xmlWriter, otFont)
+ xmlWriter.endtag(name)
+ xmlWriter.newline()
+
+ def fromXML(self, (name, attrs, content), otFont):
+ xxx
+
+
+#
+# Other common formats
+#
+
+class CoverageTable:
+
+ def getGlyphIDs(self):
+ return self.glyphIDs
+
+ def getGlyphNames(self):
+ return self.glyphNames
+
+ def makeGlyphNames(self, otFont):
+ self.glyphNames = map(lambda i, o=otFont.getGlyphOrder(): o[i], self.glyphIDs)
+
+ def decompile(self, reader, otFont):
+ format = reader.readUShort()
+ if format == 1:
+ self.decompileFormat1(reader, otFont)
+ elif format == 2:
+ self.decompileFormat2(reader, otFont)
+ else:
+ raise ttLib.TTLibError, "unknown Coverage table format: %d" % format
+ self.makeGlyphNames(otFont)
+
+ def decompileFormat1(self, reader, otFont):
+ glyphCount = reader.readUShort()
+ self.glyphIDs = glyphIDs = []
+ for i in range(glyphCount):
+ glyphID = reader.readUShort()
+ glyphIDs.append(glyphID)
+
+ def decompileFormat2(self, reader, otFont):
+ rangeCount = reader.readUShort()
+ self.glyphIDs = glyphIDs = []
+ for i in range(rangeCount):
+ startID = reader.readUShort()
+ endID = reader.readUShort()
+ startCoverageIndex = reader.readUShort()
+ for glyphID in range(startID, endID + 1):
+ glyphIDs.append(glyphID)
+
+ def compile(self, otFont):
+ # brute force ;-)
+ data1 = self.compileFormat1(otFont)
+ data2 = self.compileFormat2(otFont)
+ if len(data1) <= len(data2):
+ format = 1
+ reader = data1
+ else:
+ format = 2
+ reader = data2
+ return struct.pack(">H", format) + reader
+
+ def compileFormat1(self, otFont):
+ xxxxx
+ glyphIDs = map(otFont.getGlyphID, self.glyphNames)
+ data = pack(">H", len(glyphIDs))
+ pack = struct.pack
+ for glyphID in glyphIDs:
+ data = data + pack(">H", glyphID)
+ return data
+
+ def compileFormat2(self, otFont):
+ xxxxx
+ glyphIDs = map(otFont.getGlyphID, self.glyphNames)
+ ranges = []
+ lastID = startID = glyphIDs[0]
+ startCoverageIndex = 0
+ glyphCount = len(glyphIDs)
+ for i in range(1, glyphCount+1):
+ if i == glyphCount:
+ glyphID = 0x1ffff # arbitrary, but larger than 0x10000
+ else:
+ glyphID = glyphIDs[i]
+ if glyphID <> (lastID + 1):
+ ranges.append((startID, lastID, startCoverageIndex))
+ startCoverageIndex = i
+ startID = glyphID
+ lastID = glyphID
+ ranges.sort() # sort by startID
+ rangeData = ""
+ for startID, endID, startCoverageIndex in ranges:
+ rangeData = rangeData + struct.pack(">HHH", startID, endID, startCoverageIndex)
+ return pack(">H", len(ranges)) + rangeData
+
+
+class ClassDefinitionTable:
+
+ def decompile(self, reader, otFont):
+ format = reader.readUShort()
+ if format == 1:
+ self.decompileFormat1(reader, otFont)
+ elif format == 2:
+ self.decompileFormat2(reader, otFont)
+ else:
+ raise ttLib.TTLibError, "unknown Class table format: %d" % format
+ self.reverse()
+
+ def reverse(self):
+ classDefs = {}
+ for glyphName, classCode in self.classDefs:
+ try:
+ classDefs[classCode].append(glyphName)
+ except KeyError:
+ classDefs[classCode] = [glyphName]
+ self.classDefs = classDefs
+
+ def decompileFormat1(self, reader, otFont):
+ self.classDefs = classDefs = []
+ startGlyphID = reader.readUShort()
+ glyphCount = reader.readUShort()
+ for i in range(glyphCount):
+ glyphName = otFont.getglyphName(startGlyphID + i)
+ classValue = reader.readUShort()
+ if classValue:
+ classDefs.append((glyphName, classValue))
+
+ def decompileFormat2(self, reader, otFont):
+ self.classDefs = classDefs = []
+ classRangeCount = reader.readUShort()
+ for i in range(classRangeCount):
+ startID = reader.readUShort()
+ endID = reader.readUShort()
+ classValue = reader.readUShort()
+ for glyphID in range(startID, endID + 1):
+ if classValue:
+ glyphName = otFont.getGlyphName(glyphID)
+ classDefs.append((glyphName, classValue))
+
+ def compile(self, otFont):
+ # brute force again
+ data1 = self.compileFormat1(otFont)
+ data2 = self.compileFormat2(otFont)
+ if len(data1) <= len(data2):
+ format = 1
+ data = data1
+ else:
+ format = 2
+ data = data2
+ return struct.pack(">H", format) + data
+
+ def compileFormat1(self, otFont):
+ items = map(lambda (glyphName, classValue), getGlyphID=otFont.getGlyphID:
+ (getGlyphID(glyphName), classValue), self.glyphs.items())
+ items.sort()
+ startGlyphID = items[0][0]
+ endGlyphID = items[-1][0]
+ data = ""
+ lastID = startGlyphID
+ for glyphID, classValue in items:
+ for i in range(lastID + 1, glyphID - 1):
+ data = data + "\0\0" # 0 == default class
+ data = data + struct.pack(">H", classValue)
+ lastID = glyphID
+ return struct.pack(">H", endGlyphID - startGlyphID + 1) + data
+
+ def compileFormat2(self, otFont):
+ items = map(lambda (glyphName, classValue), getGlyphID=otFont.getGlyphID:
+ (getGlyphID(glyphName), classValue), self.glyphs.items())
+ items.sort()
+ ranges = []
+ lastID, lastClassValue = items[0][0]
+ startID = lastID
+ itemCount = len(items)
+ for i in range(1, itemCount+1):
+ if i == itemCount:
+ glyphID = 0x1ffff # arbitrary, but larger than 0x10000
+ classValue = 0
+ else:
+ glyphID, classValue = items[i]
+ if glyphID <> (lastID + 1) or lastClassValue <> classValue:
+ ranges.append((startID, lastID, lastClassValue))
+ startID = glyphID
+ lastClassValue = classValue
+ lastID = glyphID
+ lastClassValue = classValue
+ rangeData = ""
+ for startID, endID, classValue in ranges:
+ rangeData = rangeData + struct.pack(">HHH", startID, endID, classValue)
+ return pack(">H", len(ranges)) + rangeData
+
+ def __getitem__(self, glyphName):
+ if self.glyphs.has_key(glyphName):
+ return self.glyphs[glyphName]
+ else:
+ return 0 # default class
+
+
+class DeviceTable:
+
+ def decompile(self, reader, otFont):
+ xxxxxx
+ self.startSize = unpack_uint16(reader[:2])
+ endSize = unpack_uint16(reader[2:4])
+ deltaFormat = unpack_uint16(reader[4:6])
+ reader = reader[6:]
+ if deltaFormat == 1:
+ bits = 2
+ elif deltaFormat == 2:
+ bits = 4
+ elif deltaFormat == 3:
+ bits = 8
+ else:
+ raise ttLib.TTLibError, "unknown Device table delta format: %d" % deltaFormat
+ numCount = 16 / bits
+ deltaCount = endSize - self.startSize + 1
+ deltaValues = []
+ mask = (1 << bits) - 1
+ threshold = (1 << bits) / 2
+ shift = 1 << bits
+ for i in range(0, deltaCount, numCount):
+ offset = 2*i/numCount
+ chunk = unpack_uint16(reader[offset:offset+2])
+ deltas = []
+ for j in range(numCount):
+ delta = chunk & mask
+ if delta >= threshold:
+ delta = delta - shift
+ deltas.append(delta)
+ chunk = chunk >> bits
+ deltas.reverse()
+ deltaValues = deltaValues + deltas
+ self.deltaValues = deltaValues[:deltaCount]
+
+ def compile(self, otFont):
+ deltaValues = self.deltaValues
+ startSize = self.startSize
+ endSize = startSize + len(deltaValues) - 1
+ smallestDelta = min(deltas)
+ largestDelta = ma(deltas)
+ if smallestDelta >= -2 and largestDelta < 2:
+ deltaFormat = 1
+ bits = 2
+ elif smallestDelta >= -8 and largestDelta < 8:
+ deltaFormat = 2
+ bits = 4
+ elif smallestDelta >= -128 and largestDelta < 128:
+ deltaFormat = 3
+ bits = 8
+ else:
+ raise ttLib.TTLibError, "delta value too large: min=%d, max=%d" % (smallestDelta, largestDelta)
+ data = struct.pack(">HHH", startSize, endSize, deltaFormat)
+ numCount = 16 / bits
+ # pad the list to a multiple of numCount values
+ remainder = len(deltaValues) % numCount
+ if remainder:
+ deltaValues = deltaValues + [0] * (numCount - remainder)
+ deltaData = ""
+ for i in range(0, len(deltaValues), numCount):
+ chunk = 0
+ for j in range(numCount):
+ chunk = chunk << bits
+ chunk = chunk | deltaValues[i+j]
+ deltaData = deltaData + struct.pack(">H", chunk)
+ return data + deltaData
+
+
+#
+# Miscelaneous helper routines and classes
+#
+
+class OTTableReader:
+
+ """Data wrapper, mostly designed to make reading OT data less cumbersome."""
+
+ def __init__(self, data, offset=0):
+ self.data = data
+ self.offset = offset
+ self.pos = offset
+
+ def readUShort(self):
+ pos = self.pos
+ newpos = pos + 2
+ value = int(struct.unpack(">H", self.data[pos:newpos])[0])
+ self.pos = newpos
+ return value
+
+ readOffset = readUShort
+
+ def readShort(self):
+ pos = self.pos
+ newpos = pos + 2
+ value = int(struct.unpack(">h", self.data[pos:newpos])[0])
+ self.pos = newpos
+ return value
+
+ def readLong(self):
+ pos = self.pos
+ newpos = pos + 4
+ value = int(struct.unpack(">l", self.data[pos:newpos])[0])
+ self.pos = newpos
+ return value
+
+ def readTag(self):
+ pos = self.pos
+ newpos = pos + 4
+ value = self.data[pos:newpos]
+ assert len(value) == 4
+ self.pos = newpos
+ return value
+
+ def readUShortArray(self, count):
+ return self.readArray(count, "H")
+
+ readOffsetArray = readUShortArray
+
+ def readShortArray(self, count):
+ return self.readArray(count, "h")
+
+ def readArray(self, count, format):
+ assert format in "Hh"
+ from array import array
+ pos = self.pos
+ newpos = pos + 2 * count
+ a = array(format)
+ a.fromstring(self.data[pos:newpos])
+ if ttLib.endian <> 'big':
+ a.byteswap()
+ self.pos = newpos
+ return a.tolist()
+
+ def readTable(self, tableClass, otFont, *args):
+ offset = self.readOffset()
+ if offset == 0:
+ return None
+ newReader = self.getSubString(offset)
+ table = apply(tableClass, args)
+ table.decompile(newReader, otFont)
+ return table
+
+ def readTableArray(self, count, tableClass, otFont, *args):
+ list = []
+ for i in range(count):
+ list.append(apply(self.readTable, (tableClass, otFont) + args))
+ return list
+
+ def readTagList(self, count, tableClass, otFont, *args):
+ list = []
+ for i in range(count):
+ tag = self.readTag()
+ table = apply(self.readTable, (tableClass, otFont) + args)
+ list.append((tag, table))
+ return list
+
+ def readStruct(self, format, size=None):
+ if size is None:
+ size = struct.calcsize(format)
+ else:
+ assert size == struct.calcsize(format)
+ pos = self.pos
+ newpos = pos + size
+ values = struct.unpack(format, self.data[pos:newpos])
+ self.pos = newpos
+ return values
+
+ def getSubString(self, offset):
+ return self.__class__(self.data, self.offset+offset)
+
+ def seek(self, n):
+ """Relative seek."""
+ self.pos = self.pos + n
+
+
diff --git a/Lib/fontTools/ttLib/tables/table_API_readme.txt b/Lib/fontTools/ttLib/tables/table_API_readme.txt
new file mode 100644
index 0000000..2c04bf8
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/table_API_readme.txt
@@ -0,0 +1,91 @@
+This folder is a subpackage of ttLib. Each module here is a
+specialized TT/OT table converter: they can convert raw data
+to Python objects and vice versa. Usually you don't need to
+use the modules directly: they are imported and used
+automatically when needed by ttLib.
+
+If you are writing you own table converter the following is
+important.
+
+The modules here have pretty strange names: this is due to the
+fact that we need to map TT table tags (which are case sensitive)
+to filenames (which on Mac and Win aren't case sensitive) as well
+as to Python identifiers. The latter means it can only contain
+[A-Za-z0-9_] and cannot start with a number.
+
+ttLib provides functions to expand a tag into the format used here:
+
+>>> from fontTools import ttLib
+>>> ttLib.tag2identifier("FOO ")
+'F_O_O_'
+>>> ttLib.tag2identifier("cvt ")
+'_c_v_t'
+>>> ttLib.tag2identifier("OS/2")
+'O_S_2f_2'
+>>> ttLib.tag2identifier("glyf")
+'_g_l_y_f'
+>>>
+
+And vice versa:
+
+>>> ttLib.identifier2tag("F_O_O_")
+'FOO '
+>>> ttLib.identifier2tag("_c_v_t")
+'cvt '
+>>> ttLib.identifier2tag("O_S_2f_2")
+'OS/2'
+>>> ttLib.identifier2tag("_g_l_y_f")
+'glyf'
+>>>
+
+Eg. the 'glyf' table converter lives in a Python file called:
+
+ _g_l_y_f.py
+
+The converter itself is a class, named "table_" + expandedtag. Eg:
+
+ class table__g_l_y_f:
+ etc.
+
+Note that if you _do_ need to use such modules or classes manually,
+there are two convenient API functions that let you find them by tag:
+
+>>> ttLib.getTableModule('glyf')
+<module 'ttLib.tables._g_l_y_f'>
+>>> ttLib.getTableClass('glyf')
+<class ttLib.tables._g_l_y_f.table__g_l_y_f at 645f400>
+>>>
+
+You must subclass from DefaultTable.DefaultTable. It provides some default
+behavior, as well as a constructor method (__init__) that you don't need to
+override.
+
+Your converter should minimally provide two methods:
+
+class table_F_O_O_(DefaultTable.DefaultTable): # converter for table 'FOO '
+
+ def decompile(self, data, ttFont):
+ # 'data' is the raw table data. Unpack it into a
+ # Python data structure.
+ # 'ttFont' is a ttLib.TTfile instance, enabling you to
+ # refer to other tables. Do ***not*** keep a reference to
+ # it: it will cause a circular reference (ttFont saves
+ # a reference to us), and that means we'll be leaking
+ # memory. If you need to use it in other methods, just
+ # pass it around as a method argument.
+
+ def compile(self, ttFont):
+ # Return the raw data, as converted from the Python
+ # data structure.
+ # Again, 'ttFont' is there so you can access other tables.
+ # Same warning applies.
+
+If you want to support XML import/export as well, you need to provide two
+additional methods:
+
+ def toXML(self, writer, ttFont):
+ # XXX
+
+ def fromXML(self, (name, attrs, content), ttFont):
+ # XXX
+
diff --git a/Lib/fontTools/ttLib/tables/ttProgram.py b/Lib/fontTools/ttLib/tables/ttProgram.py
new file mode 100644
index 0000000..8dd9faa
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/ttProgram.py
@@ -0,0 +1,249 @@
+"""ttLib.tables.ttProgram.py -- Assembler/disassembler for TrueType bytecode programs."""
+
+import array
+
+
+# first, the list of instructions that eat bytes or words from the instruction stream
+
+streamInstructions = [
+# ------ ----------- ----- ------------------------ --- ------ ---------------------------------- --------------
+# opcode mnemonic argbits descriptive name pops pushes eats from instruction stream pushes
+# ------ ----------- ----- ------------------------ --- ------ ---------------------------------- --------------
+ (0x40, 'NPUSHB', 0, 'PushNBytes', 0, -1), # n, b1, b2,...bn b1,b2...bn
+ (0x41, 'NPUSHW', 0, 'PushNWords', 0, -1), # n, w1, w2,...w w1,w2...wn
+ (0xb0, 'PUSHB', 3, 'PushBytes', 0, -1), # b0, b1,..bn b0, b1, ...,bn
+ (0xb8, 'PUSHW', 3, 'PushWords', 0, -1), # w0,w1,..wn w0 ,w1, ...wn
+# ------ ----------- ----- ------------------------ --- ------ ---------------------------------- --------------
+]
+
+
+# next, the list of "normal" instructions
+
+instructions = [
+# ------ ----------- ----- ------------------------ --- ------ ---------------------------------- --------------
+# opcode mnemonic argbits descriptive name pops pushes pops pushes
+# ------ ----------- ----- ------------------------ --- ------ ---------------------------------- --------------
+ (0x7f, 'AA', 0, 'AdjustAngle', 1, 0), # p -
+ (0x64, 'ABS', 0, 'Absolute', 1, 1), # n |n|
+ (0x60, 'ADD', 0, 'Add', 2, 1), # n2, n1 (n1 + n2)
+ (0x27, 'ALIGNPTS', 0, 'AlignPts', 2, 0), # p2, p1 -
+ (0x3c, 'ALIGNRP', 0, 'AlignRelativePt', -1, 0), # p1, p2, ... , ploopvalue -
+ (0x5a, 'AND', 0, 'LogicalAnd', 2, 1), # e2, e1 b
+ (0x2b, 'CALL', 0, 'CallFunction', 1, 0), # f -
+ (0x67, 'CEILING', 0, 'Ceiling', 1, 1), # n ceil(n)
+ (0x25, 'CINDEX', 0, 'CopyXToTopStack', 1, 1), # k ek
+ (0x22, 'CLEAR', 0, 'ClearStack', -1, 0), # all items on the stack -
+ (0x4f, 'DEBUG', 0, 'DebugCall', 1, 0), # n -
+ (0x73, 'DELTAC1', 0, 'DeltaExceptionC1', -1, 0), # argn, cn, argn-1,cn-1, , arg1, c1 -
+ (0x74, 'DELTAC2', 0, 'DeltaExceptionC2', -1, 0), # argn, cn, argn-1,cn-1, , arg1, c1 -
+ (0x75, 'DELTAC3', 0, 'DeltaExceptionC3', -1, 0), # argn, cn, argn-1,cn-1, , arg1, c1 -
+ (0x5d, 'DELTAP1', 0, 'DeltaExceptionP1', -1, 0), # argn, pn, argn-1, pn-1, , arg1, p1 -
+ (0x71, 'DELTAP2', 0, 'DeltaExceptionP2', -1, 0), # argn, pn, argn-1, pn-1, , arg1, p1 -
+ (0x72, 'DELTAP3', 0, 'DeltaExceptionP3', -1, 0), # argn, pn, argn-1, pn-1, , arg1, p1 -
+ (0x24, 'DEPTH', 0, 'GetDepthStack', 0, 1), # - n
+ (0x62, 'DIV', 0, 'Divide', 2, 1), # n2, n1 (n1 * 64)/ n2
+ (0x20, 'DUP', 0, 'DuplicateTopStack', 1, 2), # e e, e
+ (0x59, 'EIF', 0, 'EndIf', 0, 0), # - -
+ (0x1b, 'ELSE', 0, 'Else', 0, 0), # - -
+ (0x2d, 'ENDF', 0, 'EndFunctionDefinition', 0, 0), # - -
+ (0x54, 'EQ', 0, 'Equal', 2, 1), # e2, e1 b
+ (0x57, 'EVEN', 0, 'Even', 1, 1), # e b
+ (0x2c, 'FDEF', 0, 'FunctionDefinition', 1, 0), # f -
+ (0x4e, 'FLIPOFF', 0, 'SetAutoFlipOff', 0, 0), # - -
+ (0x4d, 'FLIPON', 0, 'SetAutoFlipOn', 0, 0), # - -
+ (0x80, 'FLIPPT', 0, 'FlipPoint', -1, 0), # p1, p2, ..., ploopvalue -
+ (0x82, 'FLIPRGOFF', 0, 'FlipRangeOff', 2, 0), # h, l -
+ (0x81, 'FLIPRGON', 0, 'FlipRangeOn', 2, 0), # h, l -
+ (0x66, 'FLOOR', 0, 'Floor', 1, 1), # n floor(n)
+ (0x46, 'GC', 1, 'GetCoordOnPVector', 1, 1), # p c
+ (0x88, 'GETINFO', 0, 'GetInfo', 1, 1), # selector result
+ (0x0d, 'GFV', 0, 'GetFVector', 0, 2), # - px, py
+ (0x0c, 'GPV', 0, 'GetPVector', 0, 2), # - px, py
+ (0x52, 'GT', 0, 'GreaterThan', 2, 1), # e2, e1 b
+ (0x53, 'GTEQ', 0, 'GreaterThanOrEqual', 2, 1), # e2, e1 b
+ (0x89, 'IDEF', 0, 'InstructionDefinition', 1, 0), # f -
+ (0x58, 'IF', 0, 'If', 1, 0), # e -
+ (0x8e, 'INSTCTRL', 0, 'SetInstrExecControl', 2, 0), # s, v -
+ (0x39, 'IP', 0, 'InterpolatePts', -1, 0), # p1, p2, ... , ploopvalue -
+ (0x0f, 'ISECT', 0, 'MovePtToIntersect', 5, 0), # a1, a0, b1, b0, p -
+ (0x30, 'IUP', 1, 'InterpolateUntPts', 0, 0), # - -
+ (0x1c, 'JMPR', 0, 'Jump', 1, 0), # offset -
+ (0x79, 'JROF', 0, 'JumpRelativeOnFalse', 2, 0), # e, offset -
+ (0x78, 'JROT', 0, 'JumpRelativeOnTrue', 2, 0), # e, offset -
+ (0x2a, 'LOOPCALL', 0, 'LoopAndCallFunction', 2, 0), # f, count -
+ (0x50, 'LT', 0, 'LessThan', 2, 1), # e2, e1 b
+ (0x51, 'LTEQ', 0, 'LessThenOrEqual', 2, 1), # e2, e1 b
+ (0x8b, 'MAX', 0, 'Maximum', 2, 1), # e2, e1 max(e1, e2)
+ (0x49, 'MD', 1, 'MeasureDistance', 2, 1), # p2,p1 d
+ (0x2e, 'MDAP', 1, 'MoveDirectAbsPt', 1, 0), # p -
+ (0xc0, 'MDRP', 5, 'MoveDirectRelPt', 1, 0), # p -
+ (0x3e, 'MIAP', 1, 'MoveIndirectAbsPt', 2, 0), # n, p -
+ (0x8c, 'MIN', 0, 'Minimum', 2, 1), # e2, e1 min(e1, e2)
+ (0x26, 'MINDEX', 0, 'MoveXToTopStack', 2, 1), # k ek
+ (0xe0, 'MIRP', 5, 'MoveIndirectRelPt', 1, 0), # n, p -
+ (0x4b, 'MPPEM', 0, 'MeasurePixelPerEm', 0, 1), # - ppem
+ (0x4c, 'MPS', 0, 'MeasurePointSize', 0, 1), # - pointSize
+ (0x3a, 'MSIRP', 1, 'MoveStackIndirRelPt', 2, 0), # d, p -
+ (0x63, 'MUL', 0, 'Multiply', 2, 1), # n2, n1 (n1 * n2)/64
+ (0x65, 'NEG', 0, 'Negate', 1, 1), # n -n
+ (0x55, 'NEQ', 0, 'NotEqual', 2, 1), # e2, e1 b
+ (0x5c, 'NOT', 0, 'LogicalNot', 1, 1), # e ( not e )
+ (0x6c, 'NROUND', 2, 'NoRound', 1, 1), # n1 n2
+ (0x56, 'ODD', 0, 'Odd', 1, 1), # e b
+ (0x5b, 'OR', 0, 'LogicalOr', 2, 1), # e2, e1 b
+ (0x21, 'POP', 0, 'PopTopStack', 1, 0), # e -
+ (0x45, 'RCVT', 0, 'ReadCVT', 1, 1), # location value
+ (0x7d, 'RDTG', 0, 'RoundDownToGrid', 0, 0), # - -
+ (0x7a, 'ROFF', 0, 'RoundOff', 0, 0), # - -
+ (0x8a, 'ROLL', 0, 'RollTopThreeStack', 3, 3), # a,b,c b,a,c
+ (0x68, 'ROUND', 2, 'Round', 1, 1), # n1 n2
+ (0x43, 'RS', 0, 'ReadStore', 1, 1), # n v
+ (0x3d, 'RTDG', 0, 'RoundToDoubleGrid', 0, 0), # - -
+ (0x18, 'RTG', 0, 'RoundToGrid', 0, 0), # - -
+ (0x19, 'RTHG', 0, 'RoundToHalfGrid', 0, 0), # - -
+ (0x7c, 'RUTG', 0, 'RoundUpToGrid', 0, 0), # - -
+ (0x77, 'S45ROUND', 0, 'SuperRound45Degrees', 1, 0), # n -
+ (0x7e, 'SANGW', 0, 'SetAngleWeight', 1, 0), # weight -
+ (0x85, 'SCANCTRL', 0, 'ScanConversionControl', 1, 0), # n -
+ (0x8d, 'SCANTYPE', 0, 'ScanType', 1, 0), # n -
+ (0x48, 'SCFS', 0, 'SetCoordFromStackFP', 2, 0), # c, p -
+ (0x1d, 'SCVTCI', 0, 'SetCVTCutIn', 1, 0), # n -
+ (0x5e, 'SDB', 0, 'SetDeltaBaseInGState', 1, 0), # n -
+ (0x86, 'SDPVTL', 1, 'SetDualPVectorToLine', 2, 0), # p2, p1 -
+ (0x5f, 'SDS', 0, 'SetDeltaShiftInGState', 1, 0), # n -
+ (0x0b, 'SFVFS', 0, 'SetFVectorFromStack', 2, 0), # y, x -
+ (0x04, 'SFVTCA', 1, 'SetFVectorToAxis', 0, 0), # - -
+ (0x08, 'SFVTL', 1, 'SetFVectorToLine', 2, 0), # p2, p1 -
+ (0x0e, 'SFVTPV', 0, 'SetFVectorToPVector', 0, 0), # - -
+ (0x34, 'SHC', 1, 'ShiftContourByLastPt', 1, 0), # c -
+ (0x32, 'SHP', 1, 'ShiftPointByLastPoint', -1, 0), # p1, p2, ..., ploopvalue -
+ (0x38, 'SHPIX', 0, 'ShiftZoneByPixel', -1, 0), # d, p1, p2, ..., ploopvalue -
+ (0x36, 'SHZ', 1, 'ShiftZoneByLastPoint', 1, 0), # e -
+ (0x17, 'SLOOP', 0, 'SetLoopVariable', 1, 0), # n -
+ (0x1a, 'SMD', 0, 'SetMinimumDistance', 1, 0), # distance -
+ (0x0a, 'SPVFS', 0, 'SetPVectorFromStack', 2, 0), # y, x -
+ (0x02, 'SPVTCA', 1, 'SetPVectorToAxis', 0, 0), # - -
+ (0x06, 'SPVTL', 1, 'SetPVectorToLine', 2, 0), # p2, p1 -
+ (0x76, 'SROUND', 0, 'SuperRound', 1, 0), # n -
+ (0x10, 'SRP0', 0, 'SetRefPoint0', 1, 0), # p -
+ (0x11, 'SRP1', 0, 'SetRefPoint1', 1, 0), # p -
+ (0x12, 'SRP2', 0, 'SetRefPoint2', 1, 0), # p -
+ (0x1f, 'SSW', 0, 'SetSingleWidth', 1, 0), # n -
+ (0x1e, 'SSWCI', 0, 'SetSingleWidthCutIn', 1, 0), # n -
+ (0x61, 'SUB', 0, 'Subtract', 2, 1), # n2, n1 (n1 - n2)
+ (0x00, 'SVTCA', 1, 'SetFPVectorToAxis', 0, 0), # - -
+ (0x23, 'SWAP', 0, 'SwapTopStack', 2, 2), # e2, e1 e1, e2
+ (0x13, 'SZP0', 0, 'SetZonePointer0', 1, 0), # n -
+ (0x14, 'SZP1', 0, 'SetZonePointer1', 1, 0), # n -
+ (0x15, 'SZP2', 0, 'SetZonePointer2', 1, 0), # n -
+ (0x16, 'SZPS', 0, 'SetZonePointerS', 1, 0), # n -
+ (0x29, 'UTP', 0, 'UnTouchPt', 1, 0), # p -
+ (0x70, 'WCVTF', 0, 'WriteCVTInFUnits', 2, 0), # n, l -
+ (0x44, 'WCVTP', 0, 'WriteCVTInPixels', 2, 0), # v, l -
+ (0x42, 'WS', 0, 'WriteStore', 2, 0), # v, l -
+# ------ ----------- ----- ------------------------ --- ------ ---------------------------------- --------------
+]
+
+
+def bitRepr(value, bits):
+ s = ""
+ for i in range(bits):
+ s = "01"[value & 0x1] + s
+ value = value >> 1
+ return s
+
+def makeOpcodeDict(instructionList):
+ opcodeDict = {}
+ for op, mnemonic, argbits, name, pops, pushes in instructionList:
+ if argbits:
+ argoffset = op
+ for i in range(1 << argbits):
+ opcodeDict[op+i] = mnemonic, argbits, argoffset, name
+ else:
+ opcodeDict[op] = mnemonic, 0, 0, name
+ return opcodeDict
+
+streamOpcodeDict = makeOpcodeDict(streamInstructions)
+opcodeDict = makeOpcodeDict(instructions)
+
+tt_instructions_error = "TT instructions error"
+
+
+class Program:
+
+ def __init__(self):
+ pass
+
+ def fromBytecode(self, bytecode):
+ self.bytecode = array.array("B")
+ self.bytecode.fromstring(bytecode)
+
+ def fromAssembly(self, assembly):
+ self.assembly = assembly
+
+ def getBytecode(self):
+ if not hasattr(self, "bytecode"):
+ self._assemble()
+ return self.bytecode.tostring()
+
+ def getAssembly(self):
+ if not hasattr(self, "assembly"):
+ self._disassemble()
+ return self.assembly
+
+ def _assemble(self):
+ xxx
+
+ def _disassemble(self):
+ assembly = []
+ i = 0
+ bytecode = self.bytecode
+ numBytecode = len(bytecode)
+ while i < numBytecode:
+ op = bytecode[i]
+ arg = 0
+ try:
+ mnemonic, argbits, argoffset, name = opcodeDict[op]
+ except KeyError:
+ try:
+ mnemonic, argbits, argoffset, name = streamOpcodeDict[op]
+ except KeyError:
+ raise tt_instructions_error, "illegal opcode: 0x%.2x" % op
+ pushbytes = pushwords = 0
+ if argbits:
+ if mnemonic == "PUSHB":
+ pushbytes = op - argoffset + 1
+ else:
+ pushwords = op - argoffset + 1
+ else:
+ i = i + 1
+ if mnemonic == "NPUSHB":
+ pushbytes = bytecode[i]
+ else:
+ pushwords = bytecode[i]
+ i = i + 1
+ assembly.append(mnemonic + "[ ]")
+ for j in range(pushbytes):
+ assembly.append(`bytecode[i]`)
+ i = i + 1
+ for j in range(0, pushwords, 2):
+ assembly.append(`(bytecode[i] << 8) + bytecode[i+1]`)
+ i = i + 2
+ else:
+ if argbits:
+ assembly.append(mnemonic + "[%s]" % bitRepr(op - argoffset, argbits))
+ else:
+ assembly.append(mnemonic + "[ ]")
+ i = i + 1
+ self.assembly = assembly
+ del self.bytecode
+
+
+fpgm = '@\01476&%\037\023\022\015\014\005\004\002, \260\003%E#E#ah\212 Eh \212#D`D-,KRXED\033!!Y-, EhD \260\001` E\260Fvh\030\212E`D-,\260\022+\260\002%E\260\002%Ej\260@\213`\260\002%#D!!!-,\260\023+\260\002%E\260\002%Ej\270\377\300\214`\260\002%#D!!!-,\261\000\003%EhTX\260\003%E\260\003%E`h \260\004%#D\260\004%#D\033\260\003% Eh \212#D\260\003%Eh`\260\003%#DY-,\260\003% Eh \212#D\260\003%Eh`\260\003%#D-,KRXED\033!!Y-,F#F`\212\212F# F\212`\212a\270\377\200b# \020#\212\261KK\212pE` \260\000PX\260\001a\270\377\272\213\033\260F\214Y\260\020`h\001:-, E\260\003%FRX?\033!\021Y-,KS#KQZX E\212`D\033!!Y-,KS#KQZX8\033!!Y-'
+gpgm = '@\022\011\003\207@\005\200\004\207\000\010\007\202\001\010\004\202\000\010\000\020\320\355\020\336\355\001\020\336\375\032}\336\032\030\375\31610'
+
+p = Program()
+p.fromBytecode(fpgm)
+for line in p.getAssembly():
+ print line
+
diff --git a/Lib/fontTools/ttLib/test/ttBrowser.py b/Lib/fontTools/ttLib/test/ttBrowser.py
new file mode 100644
index 0000000..1c54375
--- /dev/null
+++ b/Lib/fontTools/ttLib/test/ttBrowser.py
@@ -0,0 +1,332 @@
+from fontTools import ttLib
+from fontTools.ttLib import macUtils
+import macfs
+import PyBrowser
+import W, Lists
+import os
+import ATM
+import Numeric
+import Qd
+from rf.views.wGlyphList import GlyphList
+
+
+class TableBrowser:
+
+ def __init__(self, path=None, ttFont=None, res_index=None):
+ W.SetCursor('watch')
+ if path is None:
+ self.ttFont = ttFont
+ self.filename = "????"
+ else:
+ self.ttFont = ttLib.TTFont(path, res_index)
+ if res_index is None:
+ self.filename = os.path.basename(path)
+ else:
+ self.filename = os.path.basename(path) + " - " + str(res_index)
+ self.currentglyph = None
+ self.glyphs = {}
+ self.buildinterface()
+
+ def buildinterface(self):
+ buttonwidth = 120
+ glyphlistwidth = 150
+ hmargin = 10
+ vmargin = 8
+ title = self.filename
+ tables = self.ttFont.keys()
+ tables.sort()
+ self.w = w = W.Window((500, 300), title, minsize = (400, 200))
+ w.browsetablebutton = W.Button((hmargin, 32, buttonwidth, 16), "Browse tableŠ",
+ self.browsetable)
+ w.browsefontbutton = W.Button((hmargin, vmargin, buttonwidth, 16), "Browse fontŠ",
+ self.browsefont)
+ w.tablelist = W.List((hmargin, 56, buttonwidth, -128), tables, self.tablelisthit)
+
+ w.divline1 = W.VerticalLine((buttonwidth + 2 * hmargin, vmargin, 1, -vmargin))
+
+ gleft = buttonwidth + 3 * hmargin + 1
+
+ hasGlyfTable = self.ttFont.has_key('glyf')
+
+ glyphnames = self.ttFont.getGlyphNames2() # caselessly sorted glyph names
+
+ if hasGlyfTable:
+ w.glyphlist = GlyphList((gleft, 56, glyphlistwidth, -vmargin),
+ glyphnames, self.glyphlisthit)
+
+ w.divline2 = W.VerticalLine((buttonwidth + glyphlistwidth + 4 * hmargin + 2,
+ vmargin, 1, -vmargin))
+
+ yMin = self.ttFont['head'].yMin
+ yMax = self.ttFont['head'].yMax
+ w.gviewer = GlyphViewer((buttonwidth + glyphlistwidth + 5 * hmargin + 3,
+ vmargin, -hmargin, -vmargin), yMin, yMax)
+
+ w.showpoints = W.CheckBox((gleft, vmargin, glyphlistwidth, 16), "Show points",
+ self.w.gviewer.toggleshowpoints)
+ w.showpoints.set(self.w.gviewer.showpoints)
+ w.showlines = W.CheckBox((gleft, vmargin + 24, glyphlistwidth, 16), "Show lines",
+ self.w.gviewer.toggleshowlines)
+ w.showlines.set(self.w.gviewer.showlines)
+ else:
+ w.glyphlist = GlyphList((gleft, 56, glyphlistwidth, -vmargin),
+ glyphnames)
+ w.noGlyphTable = W.TextBox((gleft, vmargin, -20, 20), "no 'glyf' table found")
+
+
+ w.setdefaultbutton(w.browsetablebutton)
+
+ w.tocurrentfont = W.Button((hmargin, -120, buttonwidth, 16), "Copy to current font", self.copytocurrentfont)
+ w.fromcurrentfont = W.Button((hmargin, -96, buttonwidth, 16), "Copy from current font", self.copyfromcurrentfont)
+ w.saveflat = W.Button((hmargin, -72, buttonwidth, 16), "Save as flat fileŠ", self.saveflat)
+ w.savesuitcasebutton = W.Button((hmargin, -48, buttonwidth, 16), "Save as suitcaseŠ", self.savesuitcase)
+ w.savexmlbutton = W.Button((hmargin, -24, buttonwidth, 16), "Save as XMLŠ", self.saveXML)
+
+ w.open()
+ w.browsetablebutton.enable(0)
+
+ def browsetable(self):
+ self.tablelisthit(1)
+
+ def browsefont(self):
+ PyBrowser.Browser(self.ttFont)
+
+ def copytocurrentfont(self):
+ pass
+
+ def copyfromcurrentfont(self):
+ pass
+
+ def saveflat(self):
+ path = putfile("Save font as flat file:", self.filename, ".TTF")
+ if path:
+ W.SetCursor('watch')
+ self.ttFont.save(path)
+
+ def savesuitcase(self):
+ path = putfile("Save font as suitcase:", self.filename, ".suit")
+ if path:
+ W.SetCursor('watch')
+ self.ttFont.save(path, 1)
+
+ def saveXML(self):
+ path = putfile("Save font as XML text file:", self.filename, ".xml")
+ if path:
+ W.SetCursor('watch')
+ pb = macUtils.ProgressBar("Saving %s as XMLŠ" % self.filename)
+ try:
+ self.ttFont.saveXML(path, pb)
+ finally:
+ pb.close()
+
+ def glyphlisthit(self, isDbl):
+ sel = self.w.glyphlist.getselectedobjects()
+ if not sel or sel[0] == self.currentglyph:
+ return
+ self.currentglyph = sel[0]
+ if self.glyphs.has_key(self.currentglyph):
+ g = self.glyphs[self.currentglyph]
+ else:
+ g = Glyph(self.ttFont, self.currentglyph)
+ self.glyphs[self.currentglyph] = g
+ self.w.gviewer.setglyph(g)
+
+ def tablelisthit(self, isdbl):
+ if isdbl:
+ for tag in self.w.tablelist.getselectedobjects():
+ table = self.ttFont[tag]
+ if tag == 'glyf':
+ W.SetCursor('watch')
+ for glyphname in self.ttFont.getGlyphOrder():
+ try:
+ glyph = table[glyphname]
+ except KeyError:
+ pass # incomplete font, oh well.
+ PyBrowser.Browser(table)
+ else:
+ sel = self.w.tablelist.getselection()
+ if sel:
+ self.w.browsetablebutton.enable(1)
+ else:
+ self.w.browsetablebutton.enable(0)
+
+
+class Glyph:
+
+ def __init__(self, ttFont, glyphName):
+ ttglyph = ttFont['glyf'][glyphName]
+ self.iscomposite = ttglyph.numberOfContours == -1
+ self.width, self.lsb = ttFont['hmtx'][glyphName]
+ if ttglyph.numberOfContours == 0:
+ self.xMin = 0
+ self.contours = []
+ return
+ self.xMin = ttglyph.xMin
+ coordinates, endPts, flags = ttglyph.getCoordinates(ttFont['glyf'])
+ self.contours = []
+ self.flags = []
+ startpt = 0
+ for endpt in endPts:
+ self.contours.append(Numeric.array(coordinates[startpt:endpt+1]))
+ self.flags.append(flags[startpt:endpt+1])
+ startpt = endpt + 1
+
+ def getcontours(self, scale, move):
+ contours = []
+ for i in range(len(self.contours)):
+ contours.append((self.contours[i] * Numeric.array(scale) + move), self.flags[i])
+ return contours
+
+
+class GlyphViewer(W.Widget):
+
+ def __init__(self, possize, yMin, yMax):
+ W.Widget.__init__(self, possize)
+ self.glyph = None
+ extra = 0.02 * (yMax-yMin)
+ self.yMin, self.yMax = yMin - extra, yMax + extra
+ self.showpoints = 1
+ self.showlines = 1
+
+ def toggleshowpoints(self, onoff):
+ self.showpoints = onoff
+ self.SetPort()
+ self.draw()
+
+ def toggleshowlines(self, onoff):
+ self.showlines = onoff
+ self.SetPort()
+ self.draw()
+
+ def setglyph(self, glyph):
+ self.glyph = glyph
+ self.SetPort()
+ self.draw()
+
+ def draw(self, visRgn=None):
+ # This a HELL of a routine, but it's pretty damn fast...
+ import Qd
+ if not self._visible:
+ return
+ Qd.EraseRect(Qd.InsetRect(self._bounds, 1, 1))
+ cliprgn = Qd.NewRgn()
+ savergn = Qd.NewRgn()
+ Qd.RectRgn(cliprgn, self._bounds)
+ Qd.GetClip(savergn)
+ Qd.SetClip(cliprgn)
+ try:
+ if self.glyph:
+ l, t, r, b = Qd.InsetRect(self._bounds, 1, 1)
+ height = b - t
+ scale = float(height) / (self.yMax - self.yMin)
+ topoffset = t + scale * self.yMax
+ width = scale * self.glyph.width
+ lsb = scale * self.glyph.lsb
+ xMin = scale * self.glyph.xMin
+ # XXXX this is not correct when USE_MY_METRICS is set in component!
+ leftoffset = l + 0.5 * (r - l - width)
+ gleftoffset = leftoffset - xMin + lsb
+ if self.showlines:
+ Qd.RGBForeColor((0xafff, 0xafff, 0xafff))
+ # left sidebearing
+ Qd.MoveTo(leftoffset, t)
+ Qd.LineTo(leftoffset, b - 1)
+ # right sidebearing
+ Qd.MoveTo(leftoffset + width, t)
+ Qd.LineTo(leftoffset + width, b - 1)
+ # baseline
+ Qd.MoveTo(l, topoffset)
+ Qd.LineTo(r - 1, topoffset)
+
+ # origin
+ Qd.RGBForeColor((0x5fff, 0, 0))
+ Qd.MoveTo(gleftoffset, topoffset - 16)
+ Qd.LineTo(gleftoffset, topoffset + 16)
+ # reset color
+ Qd.RGBForeColor((0, 0, 0))
+
+ if self.glyph.iscomposite:
+ Qd.RGBForeColor((0x7fff, 0x7fff, 0x7fff))
+
+ ATM.startFillATM()
+ contours = self.glyph.getcontours((scale, -scale), (gleftoffset, topoffset))
+ for contour, flags in contours:
+ currentpoint = None
+ done_moveto = 0
+ i = 0
+ nPoints = len(contour)
+ while i < nPoints:
+ pt = contour[i]
+ if flags[i]:
+ # onCurve
+ currentpoint = lineto(pt, done_moveto)
+ else:
+ if not currentpoint:
+ if not flags[i-1]:
+ currentpoint = 0.5 * (contour[i-1] + pt)
+ else:
+ currentpoint = contour[i-1]
+ if not flags[(i+1) % nPoints]:
+ endPt = 0.5 * (pt + contour[(i+1) % nPoints])
+ else:
+ endPt = contour[(i+1) % nPoints]
+ i = i + 1
+ # offCurve
+ currentpoint = qcurveto(currentpoint,
+ pt, endPt, done_moveto)
+ done_moveto = 1
+ i = i + 1
+ ATM.fillClosePathATM()
+ ATM.endFillATM()
+ # draw point markers
+ if self.showpoints:
+ for contour, flags in contours:
+ Qd.RGBForeColor((0, 0xffff, 0))
+ for i in range(len(contour)):
+ (x, y) = contour[i]
+ onCurve = flags[i] & 0x1
+ if onCurve:
+ Qd.PaintRect(Qd.InsetRect((x, y, x, y), -2, -2))
+ else:
+ Qd.PaintOval(Qd.InsetRect((x, y, x, y), -2, -2))
+ Qd.RGBForeColor((0xffff, 0, 0))
+ Qd.RGBForeColor((0, 0, 0))
+ Qd.FrameRect(self._bounds)
+ finally:
+ Qd.SetClip(savergn)
+ Qd.DisposeRgn(cliprgn)
+ Qd.DisposeRgn(savergn)
+
+
+extensions = [".suit", ".xml", ".TTF", ".ttf"]
+
+def putfile(prompt, filename, newextension):
+ for ext in extensions:
+ if filename[-len(ext):] == ext:
+ filename = filename[:-len(ext)] + newextension
+ break
+ else:
+ filename = filename + newextension
+ fss, ok = macfs.StandardPutFile(prompt, filename)
+ if ok:
+ return fss.as_pathname()
+
+
+def lineto(pt, done_moveto):
+ x, y = pt
+ if done_moveto:
+ ATM.fillLineToATM((x, y))
+ else:
+ ATM.fillMoveToATM((x, y))
+ return pt
+
+def qcurveto(pt0, pt1, pt2, done_moveto):
+ if not done_moveto:
+ x0, y0 = pt0
+ ATM.fillMoveToATM((x0, y0))
+ x1a, y1a = pt0 + 0.6666666666667 * (pt1 - pt0)
+ x1b, y1b = pt2 + 0.6666666666667 * (pt1 - pt2)
+ x2, y2 = pt2
+ ATM.fillCurveToATM((x1a, y1a), (x1b, y1b), (x2, y2))
+ return pt2
+
diff --git a/Lib/fontTools/ttLib/xmlImport.py b/Lib/fontTools/ttLib/xmlImport.py
new file mode 100644
index 0000000..92d3b42
--- /dev/null
+++ b/Lib/fontTools/ttLib/xmlImport.py
@@ -0,0 +1,196 @@
+from fontTools import ttLib
+from fontTools.misc.textTools import safeEval
+import types
+import string
+import Numeric, array
+from xml.parsers.xmlproc import xmlproc
+
+
+xmlerror = "xmlerror"
+xml_parse_error = "XML parse error"
+
+
+class UnicodeString:
+
+ def __init__(self, value):
+ if isinstance(value, UnicodeString):
+ self.value = value.value
+ else:
+ if type(value) == types.StringType:
+ # Since Numeric interprets char codes as *signed*,
+ # we feed it through the array module.
+ value = array.array("B", value)
+ self.value = Numeric.array(value, Numeric.Int16)
+
+ def __len__(self):
+ return len(self.value)
+
+ #def __hash__(self):
+ # return hash(self.value.tostring())
+ #
+ #def __cmp__(self, other):
+ # if not isinstance(other, UnicodeString):
+ # return 1
+ # else:
+ # return not Numeric.alltrue(
+ # Numeric.equal(self.value, other.value))
+
+ def __add__(self, other):
+ if not isinstance(other, UnicodeString):
+ other = self.__class__(other)
+ return self.__class__(Numeric.concatenate((self.value, other.value)))
+
+ def __radd__(self, other):
+ if not isinstance(other, UnicodeString):
+ other = self.__class__(other)
+ return self.__class__(Numeric.concatenate((other.value, self.value)))
+
+ def __getslice__(self, i, j):
+ return self.__class__(self.value[i:j])
+
+ def __getitem__(self, i):
+ return self.__class__(self.value[i:i+1])
+
+ def tostring(self):
+ value = self.value
+ if ttLib.endian <> "big":
+ value = value.byteswapped()
+ return value.tostring()
+
+ def stripped(self):
+ value = self.value
+ i = 0
+ for i in range(len(value)):
+ if value[i] not in (0xa, 0xd, 0x9, 0x20):
+ break
+ value = value[i:]
+ i = 0
+ for i in range(len(value)-1, -1, -1):
+ if value[i] not in (0xa, 0xd, 0x9, 0x20):
+ break
+ value = value[:i+1]
+ return self.__class__(value)
+
+ def __repr__(self):
+ return "<%s %s at %x>" % (self.__class__.__name__, `self.value.tostring()`, id(self))
+
+
+class UnicodeProcessor(xmlproc.XMLProcessor):
+
+ def parse_charref(self):
+ "Parses a character reference."
+
+ if self.now_at("x"):
+ digs=unhex(self.get_match(xmlproc.reg_hex_digits))
+ else:
+ try:
+ digs=string.atoi(self.get_match(xmlproc.reg_digits))
+ except ValueError,e:
+ self.report_error(3027)
+ digs=None
+ if digs == 169:
+ pass
+ if not self.now_at(";"): self.report_error(3005,";")
+ if digs==None: return
+
+ if not (digs==9 or digs==10 or digs==13 or \
+ (digs>=32 and digs<=255)):
+ if digs>255:
+ self.app.handle_data(UnicodeString([digs]),0,1)
+ else:
+ # hrm, I need to let some null bytes go through...
+ self.app.handle_data(chr(digs),0,1)
+ #self.report_error(3018,digs)
+ else:
+ if self.stack==[]:
+ self.report_error(3028)
+ self.app.handle_data(chr(digs),0,1)
+
+
+class XMLErrorHandler(xmlproc.ErrorHandler):
+
+ def fatal(self, msg):
+ "Handles a fatal error message."
+ # we don't want no stinkin' sys.exit(1)
+ raise xml_parse_error, msg
+
+
+class XMLApplication(xmlproc.Application):
+
+ def __init__(self, ttFont, progress=None):
+ self.ttFont = ttFont
+ self.progress = progress
+ self.root = None
+ self.content_stack = []
+ self.lastpos = 0
+
+ def handle_start_tag(self, name, attrs):
+ if self.progress:
+ pos = self.locator.pos + self.locator.block_offset
+ if (pos - self.lastpos) > 4000:
+ self.progress.set(pos / 100)
+ self.lastpos = pos
+ stack = self.locator.stack
+ stacksize = len(stack)
+ if not stacksize:
+ if name <> "ttFont":
+ raise xml_parse_error, "illegal root tag: %s" % name
+ sfntVersion = attrs.get("sfntVersion", "\000\001\000\000")
+ if len(sfntVersion) <> 4:
+ sfntVersion = safeEval('"' + sfntVersion + '"')
+ self.ttFont.sfntVersion = sfntVersion
+ self.content_stack.append([])
+ elif stacksize == 1:
+ msg = "Parsing '%s' table..." % ttLib.xmltag2tag(name)
+ if self.progress:
+ self.progress.setlabel(msg)
+ elif self.ttFont.verbose:
+ ttLib.debugmsg(msg)
+ else:
+ print msg
+ tag = ttLib.xmltag2tag(name)
+ tableclass = ttLib.getTableClass(tag)
+ if tableclass is None:
+ from fontTools.ttLib.tables.DefaultTable import DefaultTable
+ tableclass = DefaultTable
+ self.current_table = tableclass(tag)
+ self.ttFont[tag] = self.current_table
+ self.content_stack.append([])
+ elif stacksize == 2:
+ self.content_stack.append([])
+ self.root = (name, attrs, self.content_stack[-1])
+ else:
+ list = []
+ self.content_stack[-1].append(name, attrs, list)
+ self.content_stack.append(list)
+
+ def handle_data(self, data, start, end):
+ if len(self.locator.stack) > 1:
+ self.content_stack[-1].append(data[start:end])
+
+ def handle_end_tag(self, name):
+ del self.content_stack[-1]
+ stack = self.locator.stack
+ stacksize = len(stack)
+ if stacksize == 1:
+ self.root = None
+ elif stacksize == 2:
+ self.current_table.fromXML(self.root, self.ttFont)
+ self.root = None
+
+
+class ProgressPrinter:
+
+ def __init__(self, title, maxval=100):
+ print title
+
+ def set(self, val, maxval=None):
+ pass
+
+ def increment(self, val=1):
+ pass
+
+ def setlabel(self, text):
+ print text
+
+
diff --git a/Lib/fontTools/unicode.py b/Lib/fontTools/unicode.py
new file mode 100644
index 0000000..dc6ba0c
--- /dev/null
+++ b/Lib/fontTools/unicode.py
@@ -0,0 +1,6646 @@
+"""Unicode version 2.1.2"""
+
+_unicode = """\
+0000 <control>
+0001 <control>
+0002 <control>
+0003 <control>
+0004 <control>
+0005 <control>
+0006 <control>
+0007 <control>
+0008 <control>
+0009 <control>
+000A <control>
+000B <control>
+000C <control>
+000D <control>
+000E <control>
+000F <control>
+0010 <control>
+0011 <control>
+0012 <control>
+0013 <control>
+0014 <control>
+0015 <control>
+0016 <control>
+0017 <control>
+0018 <control>
+0019 <control>
+001A <control>
+001B <control>
+001C <control>
+001D <control>
+001E <control>
+001F <control>
+0020 SPACE
+0021 EXCLAMATION MARK
+0022 QUOTATION MARK
+0023 NUMBER SIGN
+0024 DOLLAR SIGN
+0025 PERCENT SIGN
+0026 AMPERSAND
+0027 APOSTROPHE
+0028 LEFT PARENTHESIS
+0029 RIGHT PARENTHESIS
+002A ASTERISK
+002B PLUS SIGN
+002C COMMA
+002D HYPHEN-MINUS
+002E FULL STOP
+002F SOLIDUS
+0030 DIGIT ZERO
+0031 DIGIT ONE
+0032 DIGIT TWO
+0033 DIGIT THREE
+0034 DIGIT FOUR
+0035 DIGIT FIVE
+0036 DIGIT SIX
+0037 DIGIT SEVEN
+0038 DIGIT EIGHT
+0039 DIGIT NINE
+003A COLON
+003B SEMICOLON
+003C LESS-THAN SIGN
+003D EQUALS SIGN
+003E GREATER-THAN SIGN
+003F QUESTION MARK
+0040 COMMERCIAL AT
+0041 LATIN CAPITAL LETTER A
+0042 LATIN CAPITAL LETTER B
+0043 LATIN CAPITAL LETTER C
+0044 LATIN CAPITAL LETTER D
+0045 LATIN CAPITAL LETTER E
+0046 LATIN CAPITAL LETTER F
+0047 LATIN CAPITAL LETTER G
+0048 LATIN CAPITAL LETTER H
+0049 LATIN CAPITAL LETTER I
+004A LATIN CAPITAL LETTER J
+004B LATIN CAPITAL LETTER K
+004C LATIN CAPITAL LETTER L
+004D LATIN CAPITAL LETTER M
+004E LATIN CAPITAL LETTER N
+004F LATIN CAPITAL LETTER O
+0050 LATIN CAPITAL LETTER P
+0051 LATIN CAPITAL LETTER Q
+0052 LATIN CAPITAL LETTER R
+0053 LATIN CAPITAL LETTER S
+0054 LATIN CAPITAL LETTER T
+0055 LATIN CAPITAL LETTER U
+0056 LATIN CAPITAL LETTER V
+0057 LATIN CAPITAL LETTER W
+0058 LATIN CAPITAL LETTER X
+0059 LATIN CAPITAL LETTER Y
+005A LATIN CAPITAL LETTER Z
+005B LEFT SQUARE BRACKET
+005C REVERSE SOLIDUS
+005D RIGHT SQUARE BRACKET
+005E CIRCUMFLEX ACCENT
+005F LOW LINE
+0060 GRAVE ACCENT
+0061 LATIN SMALL LETTER A
+0062 LATIN SMALL LETTER B
+0063 LATIN SMALL LETTER C
+0064 LATIN SMALL LETTER D
+0065 LATIN SMALL LETTER E
+0066 LATIN SMALL LETTER F
+0067 LATIN SMALL LETTER G
+0068 LATIN SMALL LETTER H
+0069 LATIN SMALL LETTER I
+006A LATIN SMALL LETTER J
+006B LATIN SMALL LETTER K
+006C LATIN SMALL LETTER L
+006D LATIN SMALL LETTER M
+006E LATIN SMALL LETTER N
+006F LATIN SMALL LETTER O
+0070 LATIN SMALL LETTER P
+0071 LATIN SMALL LETTER Q
+0072 LATIN SMALL LETTER R
+0073 LATIN SMALL LETTER S
+0074 LATIN SMALL LETTER T
+0075 LATIN SMALL LETTER U
+0076 LATIN SMALL LETTER V
+0077 LATIN SMALL LETTER W
+0078 LATIN SMALL LETTER X
+0079 LATIN SMALL LETTER Y
+007A LATIN SMALL LETTER Z
+007B LEFT CURLY BRACKET
+007C VERTICAL LINE
+007D RIGHT CURLY BRACKET
+007E TILDE
+007F <control>
+0080 <control>
+0081 <control>
+0082 <control>
+0083 <control>
+0084 <control>
+0085 <control>
+0086 <control>
+0087 <control>
+0088 <control>
+0089 <control>
+008A <control>
+008B <control>
+008C <control>
+008D <control>
+008E <control>
+008F <control>
+0090 <control>
+0091 <control>
+0092 <control>
+0093 <control>
+0094 <control>
+0095 <control>
+0096 <control>
+0097 <control>
+0098 <control>
+0099 <control>
+009A <control>
+009B <control>
+009C <control>
+009D <control>
+009E <control>
+009F <control>
+00A0 NO-BREAK SPACE
+00A1 INVERTED EXCLAMATION MARK
+00A2 CENT SIGN
+00A3 POUND SIGN
+00A4 CURRENCY SIGN
+00A5 YEN SIGN
+00A6 BROKEN BAR
+00A7 SECTION SIGN
+00A8 DIAERESIS
+00A9 COPYRIGHT SIGN
+00AA FEMININE ORDINAL INDICATOR
+00AB LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
+00AC NOT SIGN
+00AD SOFT HYPHEN
+00AE REGISTERED SIGN
+00AF MACRON
+00B0 DEGREE SIGN
+00B1 PLUS-MINUS SIGN
+00B2 SUPERSCRIPT TWO
+00B3 SUPERSCRIPT THREE
+00B4 ACUTE ACCENT
+00B5 MICRO SIGN
+00B6 PILCROW SIGN
+00B7 MIDDLE DOT
+00B8 CEDILLA
+00B9 SUPERSCRIPT ONE
+00BA MASCULINE ORDINAL INDICATOR
+00BB RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
+00BC VULGAR FRACTION ONE QUARTER
+00BD VULGAR FRACTION ONE HALF
+00BE VULGAR FRACTION THREE QUARTERS
+00BF INVERTED QUESTION MARK
+00C0 LATIN CAPITAL LETTER A WITH GRAVE
+00C1 LATIN CAPITAL LETTER A WITH ACUTE
+00C2 LATIN CAPITAL LETTER A WITH CIRCUMFLEX
+00C3 LATIN CAPITAL LETTER A WITH TILDE
+00C4 LATIN CAPITAL LETTER A WITH DIAERESIS
+00C5 LATIN CAPITAL LETTER A WITH RING ABOVE
+00C6 LATIN CAPITAL LETTER AE
+00C7 LATIN CAPITAL LETTER C WITH CEDILLA
+00C8 LATIN CAPITAL LETTER E WITH GRAVE
+00C9 LATIN CAPITAL LETTER E WITH ACUTE
+00CA LATIN CAPITAL LETTER E WITH CIRCUMFLEX
+00CB LATIN CAPITAL LETTER E WITH DIAERESIS
+00CC LATIN CAPITAL LETTER I WITH GRAVE
+00CD LATIN CAPITAL LETTER I WITH ACUTE
+00CE LATIN CAPITAL LETTER I WITH CIRCUMFLEX
+00CF LATIN CAPITAL LETTER I WITH DIAERESIS
+00D0 LATIN CAPITAL LETTER ETH
+00D1 LATIN CAPITAL LETTER N WITH TILDE
+00D2 LATIN CAPITAL LETTER O WITH GRAVE
+00D3 LATIN CAPITAL LETTER O WITH ACUTE
+00D4 LATIN CAPITAL LETTER O WITH CIRCUMFLEX
+00D5 LATIN CAPITAL LETTER O WITH TILDE
+00D6 LATIN CAPITAL LETTER O WITH DIAERESIS
+00D7 MULTIPLICATION SIGN
+00D8 LATIN CAPITAL LETTER O WITH STROKE
+00D9 LATIN CAPITAL LETTER U WITH GRAVE
+00DA LATIN CAPITAL LETTER U WITH ACUTE
+00DB LATIN CAPITAL LETTER U WITH CIRCUMFLEX
+00DC LATIN CAPITAL LETTER U WITH DIAERESIS
+00DD LATIN CAPITAL LETTER Y WITH ACUTE
+00DE LATIN CAPITAL LETTER THORN
+00DF LATIN SMALL LETTER SHARP S
+00E0 LATIN SMALL LETTER A WITH GRAVE
+00E1 LATIN SMALL LETTER A WITH ACUTE
+00E2 LATIN SMALL LETTER A WITH CIRCUMFLEX
+00E3 LATIN SMALL LETTER A WITH TILDE
+00E4 LATIN SMALL LETTER A WITH DIAERESIS
+00E5 LATIN SMALL LETTER A WITH RING ABOVE
+00E6 LATIN SMALL LETTER AE
+00E7 LATIN SMALL LETTER C WITH CEDILLA
+00E8 LATIN SMALL LETTER E WITH GRAVE
+00E9 LATIN SMALL LETTER E WITH ACUTE
+00EA LATIN SMALL LETTER E WITH CIRCUMFLEX
+00EB LATIN SMALL LETTER E WITH DIAERESIS
+00EC LATIN SMALL LETTER I WITH GRAVE
+00ED LATIN SMALL LETTER I WITH ACUTE
+00EE LATIN SMALL LETTER I WITH CIRCUMFLEX
+00EF LATIN SMALL LETTER I WITH DIAERESIS
+00F0 LATIN SMALL LETTER ETH
+00F1 LATIN SMALL LETTER N WITH TILDE
+00F2 LATIN SMALL LETTER O WITH GRAVE
+00F3 LATIN SMALL LETTER O WITH ACUTE
+00F4 LATIN SMALL LETTER O WITH CIRCUMFLEX
+00F5 LATIN SMALL LETTER O WITH TILDE
+00F6 LATIN SMALL LETTER O WITH DIAERESIS
+00F7 DIVISION SIGN
+00F8 LATIN SMALL LETTER O WITH STROKE
+00F9 LATIN SMALL LETTER U WITH GRAVE
+00FA LATIN SMALL LETTER U WITH ACUTE
+00FB LATIN SMALL LETTER U WITH CIRCUMFLEX
+00FC LATIN SMALL LETTER U WITH DIAERESIS
+00FD LATIN SMALL LETTER Y WITH ACUTE
+00FE LATIN SMALL LETTER THORN
+00FF LATIN SMALL LETTER Y WITH DIAERESIS
+0100 LATIN CAPITAL LETTER A WITH MACRON
+0101 LATIN SMALL LETTER A WITH MACRON
+0102 LATIN CAPITAL LETTER A WITH BREVE
+0103 LATIN SMALL LETTER A WITH BREVE
+0104 LATIN CAPITAL LETTER A WITH OGONEK
+0105 LATIN SMALL LETTER A WITH OGONEK
+0106 LATIN CAPITAL LETTER C WITH ACUTE
+0107 LATIN SMALL LETTER C WITH ACUTE
+0108 LATIN CAPITAL LETTER C WITH CIRCUMFLEX
+0109 LATIN SMALL LETTER C WITH CIRCUMFLEX
+010A LATIN CAPITAL LETTER C WITH DOT ABOVE
+010B LATIN SMALL LETTER C WITH DOT ABOVE
+010C LATIN CAPITAL LETTER C WITH CARON
+010D LATIN SMALL LETTER C WITH CARON
+010E LATIN CAPITAL LETTER D WITH CARON
+010F LATIN SMALL LETTER D WITH CARON
+0110 LATIN CAPITAL LETTER D WITH STROKE
+0111 LATIN SMALL LETTER D WITH STROKE
+0112 LATIN CAPITAL LETTER E WITH MACRON
+0113 LATIN SMALL LETTER E WITH MACRON
+0114 LATIN CAPITAL LETTER E WITH BREVE
+0115 LATIN SMALL LETTER E WITH BREVE
+0116 LATIN CAPITAL LETTER E WITH DOT ABOVE
+0117 LATIN SMALL LETTER E WITH DOT ABOVE
+0118 LATIN CAPITAL LETTER E WITH OGONEK
+0119 LATIN SMALL LETTER E WITH OGONEK
+011A LATIN CAPITAL LETTER E WITH CARON
+011B LATIN SMALL LETTER E WITH CARON
+011C LATIN CAPITAL LETTER G WITH CIRCUMFLEX
+011D LATIN SMALL LETTER G WITH CIRCUMFLEX
+011E LATIN CAPITAL LETTER G WITH BREVE
+011F LATIN SMALL LETTER G WITH BREVE
+0120 LATIN CAPITAL LETTER G WITH DOT ABOVE
+0121 LATIN SMALL LETTER G WITH DOT ABOVE
+0122 LATIN CAPITAL LETTER G WITH CEDILLA
+0123 LATIN SMALL LETTER G WITH CEDILLA
+0124 LATIN CAPITAL LETTER H WITH CIRCUMFLEX
+0125 LATIN SMALL LETTER H WITH CIRCUMFLEX
+0126 LATIN CAPITAL LETTER H WITH STROKE
+0127 LATIN SMALL LETTER H WITH STROKE
+0128 LATIN CAPITAL LETTER I WITH TILDE
+0129 LATIN SMALL LETTER I WITH TILDE
+012A LATIN CAPITAL LETTER I WITH MACRON
+012B LATIN SMALL LETTER I WITH MACRON
+012C LATIN CAPITAL LETTER I WITH BREVE
+012D LATIN SMALL LETTER I WITH BREVE
+012E LATIN CAPITAL LETTER I WITH OGONEK
+012F LATIN SMALL LETTER I WITH OGONEK
+0130 LATIN CAPITAL LETTER I WITH DOT ABOVE
+0131 LATIN SMALL LETTER DOTLESS I
+0132 LATIN CAPITAL LIGATURE IJ
+0133 LATIN SMALL LIGATURE IJ
+0134 LATIN CAPITAL LETTER J WITH CIRCUMFLEX
+0135 LATIN SMALL LETTER J WITH CIRCUMFLEX
+0136 LATIN CAPITAL LETTER K WITH CEDILLA
+0137 LATIN SMALL LETTER K WITH CEDILLA
+0138 LATIN SMALL LETTER KRA
+0139 LATIN CAPITAL LETTER L WITH ACUTE
+013A LATIN SMALL LETTER L WITH ACUTE
+013B LATIN CAPITAL LETTER L WITH CEDILLA
+013C LATIN SMALL LETTER L WITH CEDILLA
+013D LATIN CAPITAL LETTER L WITH CARON
+013E LATIN SMALL LETTER L WITH CARON
+013F LATIN CAPITAL LETTER L WITH MIDDLE DOT
+0140 LATIN SMALL LETTER L WITH MIDDLE DOT
+0141 LATIN CAPITAL LETTER L WITH STROKE
+0142 LATIN SMALL LETTER L WITH STROKE
+0143 LATIN CAPITAL LETTER N WITH ACUTE
+0144 LATIN SMALL LETTER N WITH ACUTE
+0145 LATIN CAPITAL LETTER N WITH CEDILLA
+0146 LATIN SMALL LETTER N WITH CEDILLA
+0147 LATIN CAPITAL LETTER N WITH CARON
+0148 LATIN SMALL LETTER N WITH CARON
+0149 LATIN SMALL LETTER N PRECEDED BY APOSTROPHE
+014A LATIN CAPITAL LETTER ENG
+014B LATIN SMALL LETTER ENG
+014C LATIN CAPITAL LETTER O WITH MACRON
+014D LATIN SMALL LETTER O WITH MACRON
+014E LATIN CAPITAL LETTER O WITH BREVE
+014F LATIN SMALL LETTER O WITH BREVE
+0150 LATIN CAPITAL LETTER O WITH DOUBLE ACUTE
+0151 LATIN SMALL LETTER O WITH DOUBLE ACUTE
+0152 LATIN CAPITAL LIGATURE OE
+0153 LATIN SMALL LIGATURE OE
+0154 LATIN CAPITAL LETTER R WITH ACUTE
+0155 LATIN SMALL LETTER R WITH ACUTE
+0156 LATIN CAPITAL LETTER R WITH CEDILLA
+0157 LATIN SMALL LETTER R WITH CEDILLA
+0158 LATIN CAPITAL LETTER R WITH CARON
+0159 LATIN SMALL LETTER R WITH CARON
+015A LATIN CAPITAL LETTER S WITH ACUTE
+015B LATIN SMALL LETTER S WITH ACUTE
+015C LATIN CAPITAL LETTER S WITH CIRCUMFLEX
+015D LATIN SMALL LETTER S WITH CIRCUMFLEX
+015E LATIN CAPITAL LETTER S WITH CEDILLA
+015F LATIN SMALL LETTER S WITH CEDILLA
+0160 LATIN CAPITAL LETTER S WITH CARON
+0161 LATIN SMALL LETTER S WITH CARON
+0162 LATIN CAPITAL LETTER T WITH CEDILLA
+0163 LATIN SMALL LETTER T WITH CEDILLA
+0164 LATIN CAPITAL LETTER T WITH CARON
+0165 LATIN SMALL LETTER T WITH CARON
+0166 LATIN CAPITAL LETTER T WITH STROKE
+0167 LATIN SMALL LETTER T WITH STROKE
+0168 LATIN CAPITAL LETTER U WITH TILDE
+0169 LATIN SMALL LETTER U WITH TILDE
+016A LATIN CAPITAL LETTER U WITH MACRON
+016B LATIN SMALL LETTER U WITH MACRON
+016C LATIN CAPITAL LETTER U WITH BREVE
+016D LATIN SMALL LETTER U WITH BREVE
+016E LATIN CAPITAL LETTER U WITH RING ABOVE
+016F LATIN SMALL LETTER U WITH RING ABOVE
+0170 LATIN CAPITAL LETTER U WITH DOUBLE ACUTE
+0171 LATIN SMALL LETTER U WITH DOUBLE ACUTE
+0172 LATIN CAPITAL LETTER U WITH OGONEK
+0173 LATIN SMALL LETTER U WITH OGONEK
+0174 LATIN CAPITAL LETTER W WITH CIRCUMFLEX
+0175 LATIN SMALL LETTER W WITH CIRCUMFLEX
+0176 LATIN CAPITAL LETTER Y WITH CIRCUMFLEX
+0177 LATIN SMALL LETTER Y WITH CIRCUMFLEX
+0178 LATIN CAPITAL LETTER Y WITH DIAERESIS
+0179 LATIN CAPITAL LETTER Z WITH ACUTE
+017A LATIN SMALL LETTER Z WITH ACUTE
+017B LATIN CAPITAL LETTER Z WITH DOT ABOVE
+017C LATIN SMALL LETTER Z WITH DOT ABOVE
+017D LATIN CAPITAL LETTER Z WITH CARON
+017E LATIN SMALL LETTER Z WITH CARON
+017F LATIN SMALL LETTER LONG S
+0180 LATIN SMALL LETTER B WITH STROKE
+0181 LATIN CAPITAL LETTER B WITH HOOK
+0182 LATIN CAPITAL LETTER B WITH TOPBAR
+0183 LATIN SMALL LETTER B WITH TOPBAR
+0184 LATIN CAPITAL LETTER TONE SIX
+0185 LATIN SMALL LETTER TONE SIX
+0186 LATIN CAPITAL LETTER OPEN O
+0187 LATIN CAPITAL LETTER C WITH HOOK
+0188 LATIN SMALL LETTER C WITH HOOK
+0189 LATIN CAPITAL LETTER AFRICAN D
+018A LATIN CAPITAL LETTER D WITH HOOK
+018B LATIN CAPITAL LETTER D WITH TOPBAR
+018C LATIN SMALL LETTER D WITH TOPBAR
+018D LATIN SMALL LETTER TURNED DELTA
+018E LATIN CAPITAL LETTER REVERSED E
+018F LATIN CAPITAL LETTER SCHWA
+0190 LATIN CAPITAL LETTER OPEN E
+0191 LATIN CAPITAL LETTER F WITH HOOK
+0192 LATIN SMALL LETTER F WITH HOOK
+0193 LATIN CAPITAL LETTER G WITH HOOK
+0194 LATIN CAPITAL LETTER GAMMA
+0195 LATIN SMALL LETTER HV
+0196 LATIN CAPITAL LETTER IOTA
+0197 LATIN CAPITAL LETTER I WITH STROKE
+0198 LATIN CAPITAL LETTER K WITH HOOK
+0199 LATIN SMALL LETTER K WITH HOOK
+019A LATIN SMALL LETTER L WITH BAR
+019B LATIN SMALL LETTER LAMBDA WITH STROKE
+019C LATIN CAPITAL LETTER TURNED M
+019D LATIN CAPITAL LETTER N WITH LEFT HOOK
+019E LATIN SMALL LETTER N WITH LONG RIGHT LEG
+019F LATIN CAPITAL LETTER O WITH MIDDLE TILDE
+01A0 LATIN CAPITAL LETTER O WITH HORN
+01A1 LATIN SMALL LETTER O WITH HORN
+01A2 LATIN CAPITAL LETTER OI
+01A3 LATIN SMALL LETTER OI
+01A4 LATIN CAPITAL LETTER P WITH HOOK
+01A5 LATIN SMALL LETTER P WITH HOOK
+01A6 LATIN LETTER YR
+01A7 LATIN CAPITAL LETTER TONE TWO
+01A8 LATIN SMALL LETTER TONE TWO
+01A9 LATIN CAPITAL LETTER ESH
+01AA LATIN LETTER REVERSED ESH LOOP
+01AB LATIN SMALL LETTER T WITH PALATAL HOOK
+01AC LATIN CAPITAL LETTER T WITH HOOK
+01AD LATIN SMALL LETTER T WITH HOOK
+01AE LATIN CAPITAL LETTER T WITH RETROFLEX HOOK
+01AF LATIN CAPITAL LETTER U WITH HORN
+01B0 LATIN SMALL LETTER U WITH HORN
+01B1 LATIN CAPITAL LETTER UPSILON
+01B2 LATIN CAPITAL LETTER V WITH HOOK
+01B3 LATIN CAPITAL LETTER Y WITH HOOK
+01B4 LATIN SMALL LETTER Y WITH HOOK
+01B5 LATIN CAPITAL LETTER Z WITH STROKE
+01B6 LATIN SMALL LETTER Z WITH STROKE
+01B7 LATIN CAPITAL LETTER EZH
+01B8 LATIN CAPITAL LETTER EZH REVERSED
+01B9 LATIN SMALL LETTER EZH REVERSED
+01BA LATIN SMALL LETTER EZH WITH TAIL
+01BB LATIN LETTER TWO WITH STROKE
+01BC LATIN CAPITAL LETTER TONE FIVE
+01BD LATIN SMALL LETTER TONE FIVE
+01BE LATIN LETTER INVERTED GLOTTAL STOP WITH STROKE
+01BF LATIN LETTER WYNN
+01C0 LATIN LETTER DENTAL CLICK
+01C1 LATIN LETTER LATERAL CLICK
+01C2 LATIN LETTER ALVEOLAR CLICK
+01C3 LATIN LETTER RETROFLEX CLICK
+01C4 LATIN CAPITAL LETTER DZ WITH CARON
+01C5 LATIN CAPITAL LETTER D WITH SMALL LETTER Z WITH CARON
+01C6 LATIN SMALL LETTER DZ WITH CARON
+01C7 LATIN CAPITAL LETTER LJ
+01C8 LATIN CAPITAL LETTER L WITH SMALL LETTER J
+01C9 LATIN SMALL LETTER LJ
+01CA LATIN CAPITAL LETTER NJ
+01CB LATIN CAPITAL LETTER N WITH SMALL LETTER J
+01CC LATIN SMALL LETTER NJ
+01CD LATIN CAPITAL LETTER A WITH CARON
+01CE LATIN SMALL LETTER A WITH CARON
+01CF LATIN CAPITAL LETTER I WITH CARON
+01D0 LATIN SMALL LETTER I WITH CARON
+01D1 LATIN CAPITAL LETTER O WITH CARON
+01D2 LATIN SMALL LETTER O WITH CARON
+01D3 LATIN CAPITAL LETTER U WITH CARON
+01D4 LATIN SMALL LETTER U WITH CARON
+01D5 LATIN CAPITAL LETTER U WITH DIAERESIS AND MACRON
+01D6 LATIN SMALL LETTER U WITH DIAERESIS AND MACRON
+01D7 LATIN CAPITAL LETTER U WITH DIAERESIS AND ACUTE
+01D8 LATIN SMALL LETTER U WITH DIAERESIS AND ACUTE
+01D9 LATIN CAPITAL LETTER U WITH DIAERESIS AND CARON
+01DA LATIN SMALL LETTER U WITH DIAERESIS AND CARON
+01DB LATIN CAPITAL LETTER U WITH DIAERESIS AND GRAVE
+01DC LATIN SMALL LETTER U WITH DIAERESIS AND GRAVE
+01DD LATIN SMALL LETTER TURNED E
+01DE LATIN CAPITAL LETTER A WITH DIAERESIS AND MACRON
+01DF LATIN SMALL LETTER A WITH DIAERESIS AND MACRON
+01E0 LATIN CAPITAL LETTER A WITH DOT ABOVE AND MACRON
+01E1 LATIN SMALL LETTER A WITH DOT ABOVE AND MACRON
+01E2 LATIN CAPITAL LETTER AE WITH MACRON
+01E3 LATIN SMALL LETTER AE WITH MACRON
+01E4 LATIN CAPITAL LETTER G WITH STROKE
+01E5 LATIN SMALL LETTER G WITH STROKE
+01E6 LATIN CAPITAL LETTER G WITH CARON
+01E7 LATIN SMALL LETTER G WITH CARON
+01E8 LATIN CAPITAL LETTER K WITH CARON
+01E9 LATIN SMALL LETTER K WITH CARON
+01EA LATIN CAPITAL LETTER O WITH OGONEK
+01EB LATIN SMALL LETTER O WITH OGONEK
+01EC LATIN CAPITAL LETTER O WITH OGONEK AND MACRON
+01ED LATIN SMALL LETTER O WITH OGONEK AND MACRON
+01EE LATIN CAPITAL LETTER EZH WITH CARON
+01EF LATIN SMALL LETTER EZH WITH CARON
+01F0 LATIN SMALL LETTER J WITH CARON
+01F1 LATIN CAPITAL LETTER DZ
+01F2 LATIN CAPITAL LETTER D WITH SMALL LETTER Z
+01F3 LATIN SMALL LETTER DZ
+01F4 LATIN CAPITAL LETTER G WITH ACUTE
+01F5 LATIN SMALL LETTER G WITH ACUTE
+01FA LATIN CAPITAL LETTER A WITH RING ABOVE AND ACUTE
+01FB LATIN SMALL LETTER A WITH RING ABOVE AND ACUTE
+01FC LATIN CAPITAL LETTER AE WITH ACUTE
+01FD LATIN SMALL LETTER AE WITH ACUTE
+01FE LATIN CAPITAL LETTER O WITH STROKE AND ACUTE
+01FF LATIN SMALL LETTER O WITH STROKE AND ACUTE
+0200 LATIN CAPITAL LETTER A WITH DOUBLE GRAVE
+0201 LATIN SMALL LETTER A WITH DOUBLE GRAVE
+0202 LATIN CAPITAL LETTER A WITH INVERTED BREVE
+0203 LATIN SMALL LETTER A WITH INVERTED BREVE
+0204 LATIN CAPITAL LETTER E WITH DOUBLE GRAVE
+0205 LATIN SMALL LETTER E WITH DOUBLE GRAVE
+0206 LATIN CAPITAL LETTER E WITH INVERTED BREVE
+0207 LATIN SMALL LETTER E WITH INVERTED BREVE
+0208 LATIN CAPITAL LETTER I WITH DOUBLE GRAVE
+0209 LATIN SMALL LETTER I WITH DOUBLE GRAVE
+020A LATIN CAPITAL LETTER I WITH INVERTED BREVE
+020B LATIN SMALL LETTER I WITH INVERTED BREVE
+020C LATIN CAPITAL LETTER O WITH DOUBLE GRAVE
+020D LATIN SMALL LETTER O WITH DOUBLE GRAVE
+020E LATIN CAPITAL LETTER O WITH INVERTED BREVE
+020F LATIN SMALL LETTER O WITH INVERTED BREVE
+0210 LATIN CAPITAL LETTER R WITH DOUBLE GRAVE
+0211 LATIN SMALL LETTER R WITH DOUBLE GRAVE
+0212 LATIN CAPITAL LETTER R WITH INVERTED BREVE
+0213 LATIN SMALL LETTER R WITH INVERTED BREVE
+0214 LATIN CAPITAL LETTER U WITH DOUBLE GRAVE
+0215 LATIN SMALL LETTER U WITH DOUBLE GRAVE
+0216 LATIN CAPITAL LETTER U WITH INVERTED BREVE
+0217 LATIN SMALL LETTER U WITH INVERTED BREVE
+0250 LATIN SMALL LETTER TURNED A
+0251 LATIN SMALL LETTER ALPHA
+0252 LATIN SMALL LETTER TURNED ALPHA
+0253 LATIN SMALL LETTER B WITH HOOK
+0254 LATIN SMALL LETTER OPEN O
+0255 LATIN SMALL LETTER C WITH CURL
+0256 LATIN SMALL LETTER D WITH TAIL
+0257 LATIN SMALL LETTER D WITH HOOK
+0258 LATIN SMALL LETTER REVERSED E
+0259 LATIN SMALL LETTER SCHWA
+025A LATIN SMALL LETTER SCHWA WITH HOOK
+025B LATIN SMALL LETTER OPEN E
+025C LATIN SMALL LETTER REVERSED OPEN E
+025D LATIN SMALL LETTER REVERSED OPEN E WITH HOOK
+025E LATIN SMALL LETTER CLOSED REVERSED OPEN E
+025F LATIN SMALL LETTER DOTLESS J WITH STROKE
+0260 LATIN SMALL LETTER G WITH HOOK
+0261 LATIN SMALL LETTER SCRIPT G
+0262 LATIN LETTER SMALL CAPITAL G
+0263 LATIN SMALL LETTER GAMMA
+0264 LATIN SMALL LETTER RAMS HORN
+0265 LATIN SMALL LETTER TURNED H
+0266 LATIN SMALL LETTER H WITH HOOK
+0267 LATIN SMALL LETTER HENG WITH HOOK
+0268 LATIN SMALL LETTER I WITH STROKE
+0269 LATIN SMALL LETTER IOTA
+026A LATIN LETTER SMALL CAPITAL I
+026B LATIN SMALL LETTER L WITH MIDDLE TILDE
+026C LATIN SMALL LETTER L WITH BELT
+026D LATIN SMALL LETTER L WITH RETROFLEX HOOK
+026E LATIN SMALL LETTER LEZH
+026F LATIN SMALL LETTER TURNED M
+0270 LATIN SMALL LETTER TURNED M WITH LONG LEG
+0271 LATIN SMALL LETTER M WITH HOOK
+0272 LATIN SMALL LETTER N WITH LEFT HOOK
+0273 LATIN SMALL LETTER N WITH RETROFLEX HOOK
+0274 LATIN LETTER SMALL CAPITAL N
+0275 LATIN SMALL LETTER BARRED O
+0276 LATIN LETTER SMALL CAPITAL OE
+0277 LATIN SMALL LETTER CLOSED OMEGA
+0278 LATIN SMALL LETTER PHI
+0279 LATIN SMALL LETTER TURNED R
+027A LATIN SMALL LETTER TURNED R WITH LONG LEG
+027B LATIN SMALL LETTER TURNED R WITH HOOK
+027C LATIN SMALL LETTER R WITH LONG LEG
+027D LATIN SMALL LETTER R WITH TAIL
+027E LATIN SMALL LETTER R WITH FISHHOOK
+027F LATIN SMALL LETTER REVERSED R WITH FISHHOOK
+0280 LATIN LETTER SMALL CAPITAL R
+0281 LATIN LETTER SMALL CAPITAL INVERTED R
+0282 LATIN SMALL LETTER S WITH HOOK
+0283 LATIN SMALL LETTER ESH
+0284 LATIN SMALL LETTER DOTLESS J WITH STROKE AND HOOK
+0285 LATIN SMALL LETTER SQUAT REVERSED ESH
+0286 LATIN SMALL LETTER ESH WITH CURL
+0287 LATIN SMALL LETTER TURNED T
+0288 LATIN SMALL LETTER T WITH RETROFLEX HOOK
+0289 LATIN SMALL LETTER U BAR
+028A LATIN SMALL LETTER UPSILON
+028B LATIN SMALL LETTER V WITH HOOK
+028C LATIN SMALL LETTER TURNED V
+028D LATIN SMALL LETTER TURNED W
+028E LATIN SMALL LETTER TURNED Y
+028F LATIN LETTER SMALL CAPITAL Y
+0290 LATIN SMALL LETTER Z WITH RETROFLEX HOOK
+0291 LATIN SMALL LETTER Z WITH CURL
+0292 LATIN SMALL LETTER EZH
+0293 LATIN SMALL LETTER EZH WITH CURL
+0294 LATIN LETTER GLOTTAL STOP
+0295 LATIN LETTER PHARYNGEAL VOICED FRICATIVE
+0296 LATIN LETTER INVERTED GLOTTAL STOP
+0297 LATIN LETTER STRETCHED C
+0298 LATIN LETTER BILABIAL CLICK
+0299 LATIN LETTER SMALL CAPITAL B
+029A LATIN SMALL LETTER CLOSED OPEN E
+029B LATIN LETTER SMALL CAPITAL G WITH HOOK
+029C LATIN LETTER SMALL CAPITAL H
+029D LATIN SMALL LETTER J WITH CROSSED-TAIL
+029E LATIN SMALL LETTER TURNED K
+029F LATIN LETTER SMALL CAPITAL L
+02A0 LATIN SMALL LETTER Q WITH HOOK
+02A1 LATIN LETTER GLOTTAL STOP WITH STROKE
+02A2 LATIN LETTER REVERSED GLOTTAL STOP WITH STROKE
+02A3 LATIN SMALL LETTER DZ DIGRAPH
+02A4 LATIN SMALL LETTER DEZH DIGRAPH
+02A5 LATIN SMALL LETTER DZ DIGRAPH WITH CURL
+02A6 LATIN SMALL LETTER TS DIGRAPH
+02A7 LATIN SMALL LETTER TESH DIGRAPH
+02A8 LATIN SMALL LETTER TC DIGRAPH WITH CURL
+02B0 MODIFIER LETTER SMALL H
+02B1 MODIFIER LETTER SMALL H WITH HOOK
+02B2 MODIFIER LETTER SMALL J
+02B3 MODIFIER LETTER SMALL R
+02B4 MODIFIER LETTER SMALL TURNED R
+02B5 MODIFIER LETTER SMALL TURNED R WITH HOOK
+02B6 MODIFIER LETTER SMALL CAPITAL INVERTED R
+02B7 MODIFIER LETTER SMALL W
+02B8 MODIFIER LETTER SMALL Y
+02B9 MODIFIER LETTER PRIME
+02BA MODIFIER LETTER DOUBLE PRIME
+02BB MODIFIER LETTER TURNED COMMA
+02BC MODIFIER LETTER APOSTROPHE
+02BD MODIFIER LETTER REVERSED COMMA
+02BE MODIFIER LETTER RIGHT HALF RING
+02BF MODIFIER LETTER LEFT HALF RING
+02C0 MODIFIER LETTER GLOTTAL STOP
+02C1 MODIFIER LETTER REVERSED GLOTTAL STOP
+02C2 MODIFIER LETTER LEFT ARROWHEAD
+02C3 MODIFIER LETTER RIGHT ARROWHEAD
+02C4 MODIFIER LETTER UP ARROWHEAD
+02C5 MODIFIER LETTER DOWN ARROWHEAD
+02C6 MODIFIER LETTER CIRCUMFLEX ACCENT
+02C7 CARON
+02C8 MODIFIER LETTER VERTICAL LINE
+02C9 MODIFIER LETTER MACRON
+02CA MODIFIER LETTER ACUTE ACCENT
+02CB MODIFIER LETTER GRAVE ACCENT
+02CC MODIFIER LETTER LOW VERTICAL LINE
+02CD MODIFIER LETTER LOW MACRON
+02CE MODIFIER LETTER LOW GRAVE ACCENT
+02CF MODIFIER LETTER LOW ACUTE ACCENT
+02D0 MODIFIER LETTER TRIANGULAR COLON
+02D1 MODIFIER LETTER HALF TRIANGULAR COLON
+02D2 MODIFIER LETTER CENTRED RIGHT HALF RING
+02D3 MODIFIER LETTER CENTRED LEFT HALF RING
+02D4 MODIFIER LETTER UP TACK
+02D5 MODIFIER LETTER DOWN TACK
+02D6 MODIFIER LETTER PLUS SIGN
+02D7 MODIFIER LETTER MINUS SIGN
+02D8 BREVE
+02D9 DOT ABOVE
+02DA RING ABOVE
+02DB OGONEK
+02DC SMALL TILDE
+02DD DOUBLE ACUTE ACCENT
+02DE MODIFIER LETTER RHOTIC HOOK
+02E0 MODIFIER LETTER SMALL GAMMA
+02E1 MODIFIER LETTER SMALL L
+02E2 MODIFIER LETTER SMALL S
+02E3 MODIFIER LETTER SMALL X
+02E4 MODIFIER LETTER SMALL REVERSED GLOTTAL STOP
+02E5 MODIFIER LETTER EXTRA-HIGH TONE BAR
+02E6 MODIFIER LETTER HIGH TONE BAR
+02E7 MODIFIER LETTER MID TONE BAR
+02E8 MODIFIER LETTER LOW TONE BAR
+02E9 MODIFIER LETTER EXTRA-LOW TONE BAR
+0300 COMBINING GRAVE ACCENT
+0301 COMBINING ACUTE ACCENT
+0302 COMBINING CIRCUMFLEX ACCENT
+0303 COMBINING TILDE
+0304 COMBINING MACRON
+0305 COMBINING OVERLINE
+0306 COMBINING BREVE
+0307 COMBINING DOT ABOVE
+0308 COMBINING DIAERESIS
+0309 COMBINING HOOK ABOVE
+030A COMBINING RING ABOVE
+030B COMBINING DOUBLE ACUTE ACCENT
+030C COMBINING CARON
+030D COMBINING VERTICAL LINE ABOVE
+030E COMBINING DOUBLE VERTICAL LINE ABOVE
+030F COMBINING DOUBLE GRAVE ACCENT
+0310 COMBINING CANDRABINDU
+0311 COMBINING INVERTED BREVE
+0312 COMBINING TURNED COMMA ABOVE
+0313 COMBINING COMMA ABOVE
+0314 COMBINING REVERSED COMMA ABOVE
+0315 COMBINING COMMA ABOVE RIGHT
+0316 COMBINING GRAVE ACCENT BELOW
+0317 COMBINING ACUTE ACCENT BELOW
+0318 COMBINING LEFT TACK BELOW
+0319 COMBINING RIGHT TACK BELOW
+031A COMBINING LEFT ANGLE ABOVE
+031B COMBINING HORN
+031C COMBINING LEFT HALF RING BELOW
+031D COMBINING UP TACK BELOW
+031E COMBINING DOWN TACK BELOW
+031F COMBINING PLUS SIGN BELOW
+0320 COMBINING MINUS SIGN BELOW
+0321 COMBINING PALATALIZED HOOK BELOW
+0322 COMBINING RETROFLEX HOOK BELOW
+0323 COMBINING DOT BELOW
+0324 COMBINING DIAERESIS BELOW
+0325 COMBINING RING BELOW
+0326 COMBINING COMMA BELOW
+0327 COMBINING CEDILLA
+0328 COMBINING OGONEK
+0329 COMBINING VERTICAL LINE BELOW
+032A COMBINING BRIDGE BELOW
+032B COMBINING INVERTED DOUBLE ARCH BELOW
+032C COMBINING CARON BELOW
+032D COMBINING CIRCUMFLEX ACCENT BELOW
+032E COMBINING BREVE BELOW
+032F COMBINING INVERTED BREVE BELOW
+0330 COMBINING TILDE BELOW
+0331 COMBINING MACRON BELOW
+0332 COMBINING LOW LINE
+0333 COMBINING DOUBLE LOW LINE
+0334 COMBINING TILDE OVERLAY
+0335 COMBINING SHORT STROKE OVERLAY
+0336 COMBINING LONG STROKE OVERLAY
+0337 COMBINING SHORT SOLIDUS OVERLAY
+0338 COMBINING LONG SOLIDUS OVERLAY
+0339 COMBINING RIGHT HALF RING BELOW
+033A COMBINING INVERTED BRIDGE BELOW
+033B COMBINING SQUARE BELOW
+033C COMBINING SEAGULL BELOW
+033D COMBINING X ABOVE
+033E COMBINING VERTICAL TILDE
+033F COMBINING DOUBLE OVERLINE
+0340 COMBINING GRAVE TONE MARK
+0341 COMBINING ACUTE TONE MARK
+0342 COMBINING GREEK PERISPOMENI
+0343 COMBINING GREEK KORONIS
+0344 COMBINING GREEK DIALYTIKA TONOS
+0345 COMBINING GREEK YPOGEGRAMMENI
+0360 COMBINING DOUBLE TILDE
+0361 COMBINING DOUBLE INVERTED BREVE
+0374 GREEK NUMERAL SIGN
+0375 GREEK LOWER NUMERAL SIGN
+037A GREEK YPOGEGRAMMENI
+037E GREEK QUESTION MARK
+0384 GREEK TONOS
+0385 GREEK DIALYTIKA TONOS
+0386 GREEK CAPITAL LETTER ALPHA WITH TONOS
+0387 GREEK ANO TELEIA
+0388 GREEK CAPITAL LETTER EPSILON WITH TONOS
+0389 GREEK CAPITAL LETTER ETA WITH TONOS
+038A GREEK CAPITAL LETTER IOTA WITH TONOS
+038C GREEK CAPITAL LETTER OMICRON WITH TONOS
+038E GREEK CAPITAL LETTER UPSILON WITH TONOS
+038F GREEK CAPITAL LETTER OMEGA WITH TONOS
+0390 GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS
+0391 GREEK CAPITAL LETTER ALPHA
+0392 GREEK CAPITAL LETTER BETA
+0393 GREEK CAPITAL LETTER GAMMA
+0394 GREEK CAPITAL LETTER DELTA
+0395 GREEK CAPITAL LETTER EPSILON
+0396 GREEK CAPITAL LETTER ZETA
+0397 GREEK CAPITAL LETTER ETA
+0398 GREEK CAPITAL LETTER THETA
+0399 GREEK CAPITAL LETTER IOTA
+039A GREEK CAPITAL LETTER KAPPA
+039B GREEK CAPITAL LETTER LAMDA
+039C GREEK CAPITAL LETTER MU
+039D GREEK CAPITAL LETTER NU
+039E GREEK CAPITAL LETTER XI
+039F GREEK CAPITAL LETTER OMICRON
+03A0 GREEK CAPITAL LETTER PI
+03A1 GREEK CAPITAL LETTER RHO
+03A3 GREEK CAPITAL LETTER SIGMA
+03A4 GREEK CAPITAL LETTER TAU
+03A5 GREEK CAPITAL LETTER UPSILON
+03A6 GREEK CAPITAL LETTER PHI
+03A7 GREEK CAPITAL LETTER CHI
+03A8 GREEK CAPITAL LETTER PSI
+03A9 GREEK CAPITAL LETTER OMEGA
+03AA GREEK CAPITAL LETTER IOTA WITH DIALYTIKA
+03AB GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA
+03AC GREEK SMALL LETTER ALPHA WITH TONOS
+03AD GREEK SMALL LETTER EPSILON WITH TONOS
+03AE GREEK SMALL LETTER ETA WITH TONOS
+03AF GREEK SMALL LETTER IOTA WITH TONOS
+03B0 GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS
+03B1 GREEK SMALL LETTER ALPHA
+03B2 GREEK SMALL LETTER BETA
+03B3 GREEK SMALL LETTER GAMMA
+03B4 GREEK SMALL LETTER DELTA
+03B5 GREEK SMALL LETTER EPSILON
+03B6 GREEK SMALL LETTER ZETA
+03B7 GREEK SMALL LETTER ETA
+03B8 GREEK SMALL LETTER THETA
+03B9 GREEK SMALL LETTER IOTA
+03BA GREEK SMALL LETTER KAPPA
+03BB GREEK SMALL LETTER LAMDA
+03BC GREEK SMALL LETTER MU
+03BD GREEK SMALL LETTER NU
+03BE GREEK SMALL LETTER XI
+03BF GREEK SMALL LETTER OMICRON
+03C0 GREEK SMALL LETTER PI
+03C1 GREEK SMALL LETTER RHO
+03C2 GREEK SMALL LETTER FINAL SIGMA
+03C3 GREEK SMALL LETTER SIGMA
+03C4 GREEK SMALL LETTER TAU
+03C5 GREEK SMALL LETTER UPSILON
+03C6 GREEK SMALL LETTER PHI
+03C7 GREEK SMALL LETTER CHI
+03C8 GREEK SMALL LETTER PSI
+03C9 GREEK SMALL LETTER OMEGA
+03CA GREEK SMALL LETTER IOTA WITH DIALYTIKA
+03CB GREEK SMALL LETTER UPSILON WITH DIALYTIKA
+03CC GREEK SMALL LETTER OMICRON WITH TONOS
+03CD GREEK SMALL LETTER UPSILON WITH TONOS
+03CE GREEK SMALL LETTER OMEGA WITH TONOS
+03D0 GREEK BETA SYMBOL
+03D1 GREEK THETA SYMBOL
+03D2 GREEK UPSILON WITH HOOK SYMBOL
+03D3 GREEK UPSILON WITH ACUTE AND HOOK SYMBOL
+03D4 GREEK UPSILON WITH DIAERESIS AND HOOK SYMBOL
+03D5 GREEK PHI SYMBOL
+03D6 GREEK PI SYMBOL
+03DA GREEK LETTER STIGMA
+03DC GREEK LETTER DIGAMMA
+03DE GREEK LETTER KOPPA
+03E0 GREEK LETTER SAMPI
+03E2 COPTIC CAPITAL LETTER SHEI
+03E3 COPTIC SMALL LETTER SHEI
+03E4 COPTIC CAPITAL LETTER FEI
+03E5 COPTIC SMALL LETTER FEI
+03E6 COPTIC CAPITAL LETTER KHEI
+03E7 COPTIC SMALL LETTER KHEI
+03E8 COPTIC CAPITAL LETTER HORI
+03E9 COPTIC SMALL LETTER HORI
+03EA COPTIC CAPITAL LETTER GANGIA
+03EB COPTIC SMALL LETTER GANGIA
+03EC COPTIC CAPITAL LETTER SHIMA
+03ED COPTIC SMALL LETTER SHIMA
+03EE COPTIC CAPITAL LETTER DEI
+03EF COPTIC SMALL LETTER DEI
+03F0 GREEK KAPPA SYMBOL
+03F1 GREEK RHO SYMBOL
+03F2 GREEK LUNATE SIGMA SYMBOL
+03F3 GREEK LETTER YOT
+0401 CYRILLIC CAPITAL LETTER IO
+0402 CYRILLIC CAPITAL LETTER DJE
+0403 CYRILLIC CAPITAL LETTER GJE
+0404 CYRILLIC CAPITAL LETTER UKRAINIAN IE
+0405 CYRILLIC CAPITAL LETTER DZE
+0406 CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I
+0407 CYRILLIC CAPITAL LETTER YI
+0408 CYRILLIC CAPITAL LETTER JE
+0409 CYRILLIC CAPITAL LETTER LJE
+040A CYRILLIC CAPITAL LETTER NJE
+040B CYRILLIC CAPITAL LETTER TSHE
+040C CYRILLIC CAPITAL LETTER KJE
+040E CYRILLIC CAPITAL LETTER SHORT U
+040F CYRILLIC CAPITAL LETTER DZHE
+0410 CYRILLIC CAPITAL LETTER A
+0411 CYRILLIC CAPITAL LETTER BE
+0412 CYRILLIC CAPITAL LETTER VE
+0413 CYRILLIC CAPITAL LETTER GHE
+0414 CYRILLIC CAPITAL LETTER DE
+0415 CYRILLIC CAPITAL LETTER IE
+0416 CYRILLIC CAPITAL LETTER ZHE
+0417 CYRILLIC CAPITAL LETTER ZE
+0418 CYRILLIC CAPITAL LETTER I
+0419 CYRILLIC CAPITAL LETTER SHORT I
+041A CYRILLIC CAPITAL LETTER KA
+041B CYRILLIC CAPITAL LETTER EL
+041C CYRILLIC CAPITAL LETTER EM
+041D CYRILLIC CAPITAL LETTER EN
+041E CYRILLIC CAPITAL LETTER O
+041F CYRILLIC CAPITAL LETTER PE
+0420 CYRILLIC CAPITAL LETTER ER
+0421 CYRILLIC CAPITAL LETTER ES
+0422 CYRILLIC CAPITAL LETTER TE
+0423 CYRILLIC CAPITAL LETTER U
+0424 CYRILLIC CAPITAL LETTER EF
+0425 CYRILLIC CAPITAL LETTER HA
+0426 CYRILLIC CAPITAL LETTER TSE
+0427 CYRILLIC CAPITAL LETTER CHE
+0428 CYRILLIC CAPITAL LETTER SHA
+0429 CYRILLIC CAPITAL LETTER SHCHA
+042A CYRILLIC CAPITAL LETTER HARD SIGN
+042B CYRILLIC CAPITAL LETTER YERU
+042C CYRILLIC CAPITAL LETTER SOFT SIGN
+042D CYRILLIC CAPITAL LETTER E
+042E CYRILLIC CAPITAL LETTER YU
+042F CYRILLIC CAPITAL LETTER YA
+0430 CYRILLIC SMALL LETTER A
+0431 CYRILLIC SMALL LETTER BE
+0432 CYRILLIC SMALL LETTER VE
+0433 CYRILLIC SMALL LETTER GHE
+0434 CYRILLIC SMALL LETTER DE
+0435 CYRILLIC SMALL LETTER IE
+0436 CYRILLIC SMALL LETTER ZHE
+0437 CYRILLIC SMALL LETTER ZE
+0438 CYRILLIC SMALL LETTER I
+0439 CYRILLIC SMALL LETTER SHORT I
+043A CYRILLIC SMALL LETTER KA
+043B CYRILLIC SMALL LETTER EL
+043C CYRILLIC SMALL LETTER EM
+043D CYRILLIC SMALL LETTER EN
+043E CYRILLIC SMALL LETTER O
+043F CYRILLIC SMALL LETTER PE
+0440 CYRILLIC SMALL LETTER ER
+0441 CYRILLIC SMALL LETTER ES
+0442 CYRILLIC SMALL LETTER TE
+0443 CYRILLIC SMALL LETTER U
+0444 CYRILLIC SMALL LETTER EF
+0445 CYRILLIC SMALL LETTER HA
+0446 CYRILLIC SMALL LETTER TSE
+0447 CYRILLIC SMALL LETTER CHE
+0448 CYRILLIC SMALL LETTER SHA
+0449 CYRILLIC SMALL LETTER SHCHA
+044A CYRILLIC SMALL LETTER HARD SIGN
+044B CYRILLIC SMALL LETTER YERU
+044C CYRILLIC SMALL LETTER SOFT SIGN
+044D CYRILLIC SMALL LETTER E
+044E CYRILLIC SMALL LETTER YU
+044F CYRILLIC SMALL LETTER YA
+0451 CYRILLIC SMALL LETTER IO
+0452 CYRILLIC SMALL LETTER DJE
+0453 CYRILLIC SMALL LETTER GJE
+0454 CYRILLIC SMALL LETTER UKRAINIAN IE
+0455 CYRILLIC SMALL LETTER DZE
+0456 CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I
+0457 CYRILLIC SMALL LETTER YI
+0458 CYRILLIC SMALL LETTER JE
+0459 CYRILLIC SMALL LETTER LJE
+045A CYRILLIC SMALL LETTER NJE
+045B CYRILLIC SMALL LETTER TSHE
+045C CYRILLIC SMALL LETTER KJE
+045E CYRILLIC SMALL LETTER SHORT U
+045F CYRILLIC SMALL LETTER DZHE
+0460 CYRILLIC CAPITAL LETTER OMEGA
+0461 CYRILLIC SMALL LETTER OMEGA
+0462 CYRILLIC CAPITAL LETTER YAT
+0463 CYRILLIC SMALL LETTER YAT
+0464 CYRILLIC CAPITAL LETTER IOTIFIED E
+0465 CYRILLIC SMALL LETTER IOTIFIED E
+0466 CYRILLIC CAPITAL LETTER LITTLE YUS
+0467 CYRILLIC SMALL LETTER LITTLE YUS
+0468 CYRILLIC CAPITAL LETTER IOTIFIED LITTLE YUS
+0469 CYRILLIC SMALL LETTER IOTIFIED LITTLE YUS
+046A CYRILLIC CAPITAL LETTER BIG YUS
+046B CYRILLIC SMALL LETTER BIG YUS
+046C CYRILLIC CAPITAL LETTER IOTIFIED BIG YUS
+046D CYRILLIC SMALL LETTER IOTIFIED BIG YUS
+046E CYRILLIC CAPITAL LETTER KSI
+046F CYRILLIC SMALL LETTER KSI
+0470 CYRILLIC CAPITAL LETTER PSI
+0471 CYRILLIC SMALL LETTER PSI
+0472 CYRILLIC CAPITAL LETTER FITA
+0473 CYRILLIC SMALL LETTER FITA
+0474 CYRILLIC CAPITAL LETTER IZHITSA
+0475 CYRILLIC SMALL LETTER IZHITSA
+0476 CYRILLIC CAPITAL LETTER IZHITSA WITH DOUBLE GRAVE ACCENT
+0477 CYRILLIC SMALL LETTER IZHITSA WITH DOUBLE GRAVE ACCENT
+0478 CYRILLIC CAPITAL LETTER UK
+0479 CYRILLIC SMALL LETTER UK
+047A CYRILLIC CAPITAL LETTER ROUND OMEGA
+047B CYRILLIC SMALL LETTER ROUND OMEGA
+047C CYRILLIC CAPITAL LETTER OMEGA WITH TITLO
+047D CYRILLIC SMALL LETTER OMEGA WITH TITLO
+047E CYRILLIC CAPITAL LETTER OT
+047F CYRILLIC SMALL LETTER OT
+0480 CYRILLIC CAPITAL LETTER KOPPA
+0481 CYRILLIC SMALL LETTER KOPPA
+0482 CYRILLIC THOUSANDS SIGN
+0483 COMBINING CYRILLIC TITLO
+0484 COMBINING CYRILLIC PALATALIZATION
+0485 COMBINING CYRILLIC DASIA PNEUMATA
+0486 COMBINING CYRILLIC PSILI PNEUMATA
+0490 CYRILLIC CAPITAL LETTER GHE WITH UPTURN
+0491 CYRILLIC SMALL LETTER GHE WITH UPTURN
+0492 CYRILLIC CAPITAL LETTER GHE WITH STROKE
+0493 CYRILLIC SMALL LETTER GHE WITH STROKE
+0494 CYRILLIC CAPITAL LETTER GHE WITH MIDDLE HOOK
+0495 CYRILLIC SMALL LETTER GHE WITH MIDDLE HOOK
+0496 CYRILLIC CAPITAL LETTER ZHE WITH DESCENDER
+0497 CYRILLIC SMALL LETTER ZHE WITH DESCENDER
+0498 CYRILLIC CAPITAL LETTER ZE WITH DESCENDER
+0499 CYRILLIC SMALL LETTER ZE WITH DESCENDER
+049A CYRILLIC CAPITAL LETTER KA WITH DESCENDER
+049B CYRILLIC SMALL LETTER KA WITH DESCENDER
+049C CYRILLIC CAPITAL LETTER KA WITH VERTICAL STROKE
+049D CYRILLIC SMALL LETTER KA WITH VERTICAL STROKE
+049E CYRILLIC CAPITAL LETTER KA WITH STROKE
+049F CYRILLIC SMALL LETTER KA WITH STROKE
+04A0 CYRILLIC CAPITAL LETTER BASHKIR KA
+04A1 CYRILLIC SMALL LETTER BASHKIR KA
+04A2 CYRILLIC CAPITAL LETTER EN WITH DESCENDER
+04A3 CYRILLIC SMALL LETTER EN WITH DESCENDER
+04A4 CYRILLIC CAPITAL LIGATURE EN GHE
+04A5 CYRILLIC SMALL LIGATURE EN GHE
+04A6 CYRILLIC CAPITAL LETTER PE WITH MIDDLE HOOK
+04A7 CYRILLIC SMALL LETTER PE WITH MIDDLE HOOK
+04A8 CYRILLIC CAPITAL LETTER ABKHASIAN HA
+04A9 CYRILLIC SMALL LETTER ABKHASIAN HA
+04AA CYRILLIC CAPITAL LETTER ES WITH DESCENDER
+04AB CYRILLIC SMALL LETTER ES WITH DESCENDER
+04AC CYRILLIC CAPITAL LETTER TE WITH DESCENDER
+04AD CYRILLIC SMALL LETTER TE WITH DESCENDER
+04AE CYRILLIC CAPITAL LETTER STRAIGHT U
+04AF CYRILLIC SMALL LETTER STRAIGHT U
+04B0 CYRILLIC CAPITAL LETTER STRAIGHT U WITH STROKE
+04B1 CYRILLIC SMALL LETTER STRAIGHT U WITH STROKE
+04B2 CYRILLIC CAPITAL LETTER HA WITH DESCENDER
+04B3 CYRILLIC SMALL LETTER HA WITH DESCENDER
+04B4 CYRILLIC CAPITAL LIGATURE TE TSE
+04B5 CYRILLIC SMALL LIGATURE TE TSE
+04B6 CYRILLIC CAPITAL LETTER CHE WITH DESCENDER
+04B7 CYRILLIC SMALL LETTER CHE WITH DESCENDER
+04B8 CYRILLIC CAPITAL LETTER CHE WITH VERTICAL STROKE
+04B9 CYRILLIC SMALL LETTER CHE WITH VERTICAL STROKE
+04BA CYRILLIC CAPITAL LETTER SHHA
+04BB CYRILLIC SMALL LETTER SHHA
+04BC CYRILLIC CAPITAL LETTER ABKHASIAN CHE
+04BD CYRILLIC SMALL LETTER ABKHASIAN CHE
+04BE CYRILLIC CAPITAL LETTER ABKHASIAN CHE WITH DESCENDER
+04BF CYRILLIC SMALL LETTER ABKHASIAN CHE WITH DESCENDER
+04C0 CYRILLIC LETTER PALOCHKA
+04C1 CYRILLIC CAPITAL LETTER ZHE WITH BREVE
+04C2 CYRILLIC SMALL LETTER ZHE WITH BREVE
+04C3 CYRILLIC CAPITAL LETTER KA WITH HOOK
+04C4 CYRILLIC SMALL LETTER KA WITH HOOK
+04C7 CYRILLIC CAPITAL LETTER EN WITH HOOK
+04C8 CYRILLIC SMALL LETTER EN WITH HOOK
+04CB CYRILLIC CAPITAL LETTER KHAKASSIAN CHE
+04CC CYRILLIC SMALL LETTER KHAKASSIAN CHE
+04D0 CYRILLIC CAPITAL LETTER A WITH BREVE
+04D1 CYRILLIC SMALL LETTER A WITH BREVE
+04D2 CYRILLIC CAPITAL LETTER A WITH DIAERESIS
+04D3 CYRILLIC SMALL LETTER A WITH DIAERESIS
+04D4 CYRILLIC CAPITAL LIGATURE A IE
+04D5 CYRILLIC SMALL LIGATURE A IE
+04D6 CYRILLIC CAPITAL LETTER IE WITH BREVE
+04D7 CYRILLIC SMALL LETTER IE WITH BREVE
+04D8 CYRILLIC CAPITAL LETTER SCHWA
+04D9 CYRILLIC SMALL LETTER SCHWA
+04DA CYRILLIC CAPITAL LETTER SCHWA WITH DIAERESIS
+04DB CYRILLIC SMALL LETTER SCHWA WITH DIAERESIS
+04DC CYRILLIC CAPITAL LETTER ZHE WITH DIAERESIS
+04DD CYRILLIC SMALL LETTER ZHE WITH DIAERESIS
+04DE CYRILLIC CAPITAL LETTER ZE WITH DIAERESIS
+04DF CYRILLIC SMALL LETTER ZE WITH DIAERESIS
+04E0 CYRILLIC CAPITAL LETTER ABKHASIAN DZE
+04E1 CYRILLIC SMALL LETTER ABKHASIAN DZE
+04E2 CYRILLIC CAPITAL LETTER I WITH MACRON
+04E3 CYRILLIC SMALL LETTER I WITH MACRON
+04E4 CYRILLIC CAPITAL LETTER I WITH DIAERESIS
+04E5 CYRILLIC SMALL LETTER I WITH DIAERESIS
+04E6 CYRILLIC CAPITAL LETTER O WITH DIAERESIS
+04E7 CYRILLIC SMALL LETTER O WITH DIAERESIS
+04E8 CYRILLIC CAPITAL LETTER BARRED O
+04E9 CYRILLIC SMALL LETTER BARRED O
+04EA CYRILLIC CAPITAL LETTER BARRED O WITH DIAERESIS
+04EB CYRILLIC SMALL LETTER BARRED O WITH DIAERESIS
+04EE CYRILLIC CAPITAL LETTER U WITH MACRON
+04EF CYRILLIC SMALL LETTER U WITH MACRON
+04F0 CYRILLIC CAPITAL LETTER U WITH DIAERESIS
+04F1 CYRILLIC SMALL LETTER U WITH DIAERESIS
+04F2 CYRILLIC CAPITAL LETTER U WITH DOUBLE ACUTE
+04F3 CYRILLIC SMALL LETTER U WITH DOUBLE ACUTE
+04F4 CYRILLIC CAPITAL LETTER CHE WITH DIAERESIS
+04F5 CYRILLIC SMALL LETTER CHE WITH DIAERESIS
+04F8 CYRILLIC CAPITAL LETTER YERU WITH DIAERESIS
+04F9 CYRILLIC SMALL LETTER YERU WITH DIAERESIS
+0531 ARMENIAN CAPITAL LETTER AYB
+0532 ARMENIAN CAPITAL LETTER BEN
+0533 ARMENIAN CAPITAL LETTER GIM
+0534 ARMENIAN CAPITAL LETTER DA
+0535 ARMENIAN CAPITAL LETTER ECH
+0536 ARMENIAN CAPITAL LETTER ZA
+0537 ARMENIAN CAPITAL LETTER EH
+0538 ARMENIAN CAPITAL LETTER ET
+0539 ARMENIAN CAPITAL LETTER TO
+053A ARMENIAN CAPITAL LETTER ZHE
+053B ARMENIAN CAPITAL LETTER INI
+053C ARMENIAN CAPITAL LETTER LIWN
+053D ARMENIAN CAPITAL LETTER XEH
+053E ARMENIAN CAPITAL LETTER CA
+053F ARMENIAN CAPITAL LETTER KEN
+0540 ARMENIAN CAPITAL LETTER HO
+0541 ARMENIAN CAPITAL LETTER JA
+0542 ARMENIAN CAPITAL LETTER GHAD
+0543 ARMENIAN CAPITAL LETTER CHEH
+0544 ARMENIAN CAPITAL LETTER MEN
+0545 ARMENIAN CAPITAL LETTER YI
+0546 ARMENIAN CAPITAL LETTER NOW
+0547 ARMENIAN CAPITAL LETTER SHA
+0548 ARMENIAN CAPITAL LETTER VO
+0549 ARMENIAN CAPITAL LETTER CHA
+054A ARMENIAN CAPITAL LETTER PEH
+054B ARMENIAN CAPITAL LETTER JHEH
+054C ARMENIAN CAPITAL LETTER RA
+054D ARMENIAN CAPITAL LETTER SEH
+054E ARMENIAN CAPITAL LETTER VEW
+054F ARMENIAN CAPITAL LETTER TIWN
+0550 ARMENIAN CAPITAL LETTER REH
+0551 ARMENIAN CAPITAL LETTER CO
+0552 ARMENIAN CAPITAL LETTER YIWN
+0553 ARMENIAN CAPITAL LETTER PIWR
+0554 ARMENIAN CAPITAL LETTER KEH
+0555 ARMENIAN CAPITAL LETTER OH
+0556 ARMENIAN CAPITAL LETTER FEH
+0559 ARMENIAN MODIFIER LETTER LEFT HALF RING
+055A ARMENIAN APOSTROPHE
+055B ARMENIAN EMPHASIS MARK
+055C ARMENIAN EXCLAMATION MARK
+055D ARMENIAN COMMA
+055E ARMENIAN QUESTION MARK
+055F ARMENIAN ABBREVIATION MARK
+0561 ARMENIAN SMALL LETTER AYB
+0562 ARMENIAN SMALL LETTER BEN
+0563 ARMENIAN SMALL LETTER GIM
+0564 ARMENIAN SMALL LETTER DA
+0565 ARMENIAN SMALL LETTER ECH
+0566 ARMENIAN SMALL LETTER ZA
+0567 ARMENIAN SMALL LETTER EH
+0568 ARMENIAN SMALL LETTER ET
+0569 ARMENIAN SMALL LETTER TO
+056A ARMENIAN SMALL LETTER ZHE
+056B ARMENIAN SMALL LETTER INI
+056C ARMENIAN SMALL LETTER LIWN
+056D ARMENIAN SMALL LETTER XEH
+056E ARMENIAN SMALL LETTER CA
+056F ARMENIAN SMALL LETTER KEN
+0570 ARMENIAN SMALL LETTER HO
+0571 ARMENIAN SMALL LETTER JA
+0572 ARMENIAN SMALL LETTER GHAD
+0573 ARMENIAN SMALL LETTER CHEH
+0574 ARMENIAN SMALL LETTER MEN
+0575 ARMENIAN SMALL LETTER YI
+0576 ARMENIAN SMALL LETTER NOW
+0577 ARMENIAN SMALL LETTER SHA
+0578 ARMENIAN SMALL LETTER VO
+0579 ARMENIAN SMALL LETTER CHA
+057A ARMENIAN SMALL LETTER PEH
+057B ARMENIAN SMALL LETTER JHEH
+057C ARMENIAN SMALL LETTER RA
+057D ARMENIAN SMALL LETTER SEH
+057E ARMENIAN SMALL LETTER VEW
+057F ARMENIAN SMALL LETTER TIWN
+0580 ARMENIAN SMALL LETTER REH
+0581 ARMENIAN SMALL LETTER CO
+0582 ARMENIAN SMALL LETTER YIWN
+0583 ARMENIAN SMALL LETTER PIWR
+0584 ARMENIAN SMALL LETTER KEH
+0585 ARMENIAN SMALL LETTER OH
+0586 ARMENIAN SMALL LETTER FEH
+0587 ARMENIAN SMALL LIGATURE ECH YIWN
+0589 ARMENIAN FULL STOP
+0591 HEBREW ACCENT ETNAHTA
+0592 HEBREW ACCENT SEGOL
+0593 HEBREW ACCENT SHALSHELET
+0594 HEBREW ACCENT ZAQEF QATAN
+0595 HEBREW ACCENT ZAQEF GADOL
+0596 HEBREW ACCENT TIPEHA
+0597 HEBREW ACCENT REVIA
+0598 HEBREW ACCENT ZARQA
+0599 HEBREW ACCENT PASHTA
+059A HEBREW ACCENT YETIV
+059B HEBREW ACCENT TEVIR
+059C HEBREW ACCENT GERESH
+059D HEBREW ACCENT GERESH MUQDAM
+059E HEBREW ACCENT GERSHAYIM
+059F HEBREW ACCENT QARNEY PARA
+05A0 HEBREW ACCENT TELISHA GEDOLA
+05A1 HEBREW ACCENT PAZER
+05A3 HEBREW ACCENT MUNAH
+05A4 HEBREW ACCENT MAHAPAKH
+05A5 HEBREW ACCENT MERKHA
+05A6 HEBREW ACCENT MERKHA KEFULA
+05A7 HEBREW ACCENT DARGA
+05A8 HEBREW ACCENT QADMA
+05A9 HEBREW ACCENT TELISHA QETANA
+05AA HEBREW ACCENT YERAH BEN YOMO
+05AB HEBREW ACCENT OLE
+05AC HEBREW ACCENT ILUY
+05AD HEBREW ACCENT DEHI
+05AE HEBREW ACCENT ZINOR
+05AF HEBREW MARK MASORA CIRCLE
+05B0 HEBREW POINT SHEVA
+05B1 HEBREW POINT HATAF SEGOL
+05B2 HEBREW POINT HATAF PATAH
+05B3 HEBREW POINT HATAF QAMATS
+05B4 HEBREW POINT HIRIQ
+05B5 HEBREW POINT TSERE
+05B6 HEBREW POINT SEGOL
+05B7 HEBREW POINT PATAH
+05B8 HEBREW POINT QAMATS
+05B9 HEBREW POINT HOLAM
+05BB HEBREW POINT QUBUTS
+05BC HEBREW POINT DAGESH OR MAPIQ
+05BD HEBREW POINT METEG
+05BE HEBREW PUNCTUATION MAQAF
+05BF HEBREW POINT RAFE
+05C0 HEBREW PUNCTUATION PASEQ
+05C1 HEBREW POINT SHIN DOT
+05C2 HEBREW POINT SIN DOT
+05C3 HEBREW PUNCTUATION SOF PASUQ
+05C4 HEBREW MARK UPPER DOT
+05D0 HEBREW LETTER ALEF
+05D1 HEBREW LETTER BET
+05D2 HEBREW LETTER GIMEL
+05D3 HEBREW LETTER DALET
+05D4 HEBREW LETTER HE
+05D5 HEBREW LETTER VAV
+05D6 HEBREW LETTER ZAYIN
+05D7 HEBREW LETTER HET
+05D8 HEBREW LETTER TET
+05D9 HEBREW LETTER YOD
+05DA HEBREW LETTER FINAL KAF
+05DB HEBREW LETTER KAF
+05DC HEBREW LETTER LAMED
+05DD HEBREW LETTER FINAL MEM
+05DE HEBREW LETTER MEM
+05DF HEBREW LETTER FINAL NUN
+05E0 HEBREW LETTER NUN
+05E1 HEBREW LETTER SAMEKH
+05E2 HEBREW LETTER AYIN
+05E3 HEBREW LETTER FINAL PE
+05E4 HEBREW LETTER PE
+05E5 HEBREW LETTER FINAL TSADI
+05E6 HEBREW LETTER TSADI
+05E7 HEBREW LETTER QOF
+05E8 HEBREW LETTER RESH
+05E9 HEBREW LETTER SHIN
+05EA HEBREW LETTER TAV
+05F0 HEBREW LIGATURE YIDDISH DOUBLE VAV
+05F1 HEBREW LIGATURE YIDDISH VAV YOD
+05F2 HEBREW LIGATURE YIDDISH DOUBLE YOD
+05F3 HEBREW PUNCTUATION GERESH
+05F4 HEBREW PUNCTUATION GERSHAYIM
+060C ARABIC COMMA
+061B ARABIC SEMICOLON
+061F ARABIC QUESTION MARK
+0621 ARABIC LETTER HAMZA
+0622 ARABIC LETTER ALEF WITH MADDA ABOVE
+0623 ARABIC LETTER ALEF WITH HAMZA ABOVE
+0624 ARABIC LETTER WAW WITH HAMZA ABOVE
+0625 ARABIC LETTER ALEF WITH HAMZA BELOW
+0626 ARABIC LETTER YEH WITH HAMZA ABOVE
+0627 ARABIC LETTER ALEF
+0628 ARABIC LETTER BEH
+0629 ARABIC LETTER TEH MARBUTA
+062A ARABIC LETTER TEH
+062B ARABIC LETTER THEH
+062C ARABIC LETTER JEEM
+062D ARABIC LETTER HAH
+062E ARABIC LETTER KHAH
+062F ARABIC LETTER DAL
+0630 ARABIC LETTER THAL
+0631 ARABIC LETTER REH
+0632 ARABIC LETTER ZAIN
+0633 ARABIC LETTER SEEN
+0634 ARABIC LETTER SHEEN
+0635 ARABIC LETTER SAD
+0636 ARABIC LETTER DAD
+0637 ARABIC LETTER TAH
+0638 ARABIC LETTER ZAH
+0639 ARABIC LETTER AIN
+063A ARABIC LETTER GHAIN
+0640 ARABIC TATWEEL
+0641 ARABIC LETTER FEH
+0642 ARABIC LETTER QAF
+0643 ARABIC LETTER KAF
+0644 ARABIC LETTER LAM
+0645 ARABIC LETTER MEEM
+0646 ARABIC LETTER NOON
+0647 ARABIC LETTER HEH
+0648 ARABIC LETTER WAW
+0649 ARABIC LETTER ALEF MAKSURA
+064A ARABIC LETTER YEH
+064B ARABIC FATHATAN
+064C ARABIC DAMMATAN
+064D ARABIC KASRATAN
+064E ARABIC FATHA
+064F ARABIC DAMMA
+0650 ARABIC KASRA
+0651 ARABIC SHADDA
+0652 ARABIC SUKUN
+0660 ARABIC-INDIC DIGIT ZERO
+0661 ARABIC-INDIC DIGIT ONE
+0662 ARABIC-INDIC DIGIT TWO
+0663 ARABIC-INDIC DIGIT THREE
+0664 ARABIC-INDIC DIGIT FOUR
+0665 ARABIC-INDIC DIGIT FIVE
+0666 ARABIC-INDIC DIGIT SIX
+0667 ARABIC-INDIC DIGIT SEVEN
+0668 ARABIC-INDIC DIGIT EIGHT
+0669 ARABIC-INDIC DIGIT NINE
+066A ARABIC PERCENT SIGN
+066B ARABIC DECIMAL SEPARATOR
+066C ARABIC THOUSANDS SEPARATOR
+066D ARABIC FIVE POINTED STAR
+0670 ARABIC LETTER SUPERSCRIPT ALEF
+0671 ARABIC LETTER ALEF WASLA
+0672 ARABIC LETTER ALEF WITH WAVY HAMZA ABOVE
+0673 ARABIC LETTER ALEF WITH WAVY HAMZA BELOW
+0674 ARABIC LETTER HIGH HAMZA
+0675 ARABIC LETTER HIGH HAMZA ALEF
+0676 ARABIC LETTER HIGH HAMZA WAW
+0677 ARABIC LETTER U WITH HAMZA ABOVE
+0678 ARABIC LETTER HIGH HAMZA YEH
+0679 ARABIC LETTER TTEH
+067A ARABIC LETTER TTEHEH
+067B ARABIC LETTER BEEH
+067C ARABIC LETTER TEH WITH RING
+067D ARABIC LETTER TEH WITH THREE DOTS ABOVE DOWNWARDS
+067E ARABIC LETTER PEH
+067F ARABIC LETTER TEHEH
+0680 ARABIC LETTER BEHEH
+0681 ARABIC LETTER HAH WITH HAMZA ABOVE
+0682 ARABIC LETTER HAH WITH TWO DOTS VERTICAL ABOVE
+0683 ARABIC LETTER NYEH
+0684 ARABIC LETTER DYEH
+0685 ARABIC LETTER HAH WITH THREE DOTS ABOVE
+0686 ARABIC LETTER TCHEH
+0687 ARABIC LETTER TCHEHEH
+0688 ARABIC LETTER DDAL
+0689 ARABIC LETTER DAL WITH RING
+068A ARABIC LETTER DAL WITH DOT BELOW
+068B ARABIC LETTER DAL WITH DOT BELOW AND SMALL TAH
+068C ARABIC LETTER DAHAL
+068D ARABIC LETTER DDAHAL
+068E ARABIC LETTER DUL
+068F ARABIC LETTER DAL WITH THREE DOTS ABOVE DOWNWARDS
+0690 ARABIC LETTER DAL WITH FOUR DOTS ABOVE
+0691 ARABIC LETTER RREH
+0692 ARABIC LETTER REH WITH SMALL V
+0693 ARABIC LETTER REH WITH RING
+0694 ARABIC LETTER REH WITH DOT BELOW
+0695 ARABIC LETTER REH WITH SMALL V BELOW
+0696 ARABIC LETTER REH WITH DOT BELOW AND DOT ABOVE
+0697 ARABIC LETTER REH WITH TWO DOTS ABOVE
+0698 ARABIC LETTER JEH
+0699 ARABIC LETTER REH WITH FOUR DOTS ABOVE
+069A ARABIC LETTER SEEN WITH DOT BELOW AND DOT ABOVE
+069B ARABIC LETTER SEEN WITH THREE DOTS BELOW
+069C ARABIC LETTER SEEN WITH THREE DOTS BELOW AND THREE DOTS ABOVE
+069D ARABIC LETTER SAD WITH TWO DOTS BELOW
+069E ARABIC LETTER SAD WITH THREE DOTS ABOVE
+069F ARABIC LETTER TAH WITH THREE DOTS ABOVE
+06A0 ARABIC LETTER AIN WITH THREE DOTS ABOVE
+06A1 ARABIC LETTER DOTLESS FEH
+06A2 ARABIC LETTER FEH WITH DOT MOVED BELOW
+06A3 ARABIC LETTER FEH WITH DOT BELOW
+06A4 ARABIC LETTER VEH
+06A5 ARABIC LETTER FEH WITH THREE DOTS BELOW
+06A6 ARABIC LETTER PEHEH
+06A7 ARABIC LETTER QAF WITH DOT ABOVE
+06A8 ARABIC LETTER QAF WITH THREE DOTS ABOVE
+06A9 ARABIC LETTER KEHEH
+06AA ARABIC LETTER SWASH KAF
+06AB ARABIC LETTER KAF WITH RING
+06AC ARABIC LETTER KAF WITH DOT ABOVE
+06AD ARABIC LETTER NG
+06AE ARABIC LETTER KAF WITH THREE DOTS BELOW
+06AF ARABIC LETTER GAF
+06B0 ARABIC LETTER GAF WITH RING
+06B1 ARABIC LETTER NGOEH
+06B2 ARABIC LETTER GAF WITH TWO DOTS BELOW
+06B3 ARABIC LETTER GUEH
+06B4 ARABIC LETTER GAF WITH THREE DOTS ABOVE
+06B5 ARABIC LETTER LAM WITH SMALL V
+06B6 ARABIC LETTER LAM WITH DOT ABOVE
+06B7 ARABIC LETTER LAM WITH THREE DOTS ABOVE
+06BA ARABIC LETTER NOON GHUNNA
+06BB ARABIC LETTER RNOON
+06BC ARABIC LETTER NOON WITH RING
+06BD ARABIC LETTER NOON WITH THREE DOTS ABOVE
+06BE ARABIC LETTER HEH DOACHASHMEE
+06C0 ARABIC LETTER HEH WITH YEH ABOVE
+06C1 ARABIC LETTER HEH GOAL
+06C2 ARABIC LETTER HEH GOAL WITH HAMZA ABOVE
+06C3 ARABIC LETTER TEH MARBUTA GOAL
+06C4 ARABIC LETTER WAW WITH RING
+06C5 ARABIC LETTER KIRGHIZ OE
+06C6 ARABIC LETTER OE
+06C7 ARABIC LETTER U
+06C8 ARABIC LETTER YU
+06C9 ARABIC LETTER KIRGHIZ YU
+06CA ARABIC LETTER WAW WITH TWO DOTS ABOVE
+06CB ARABIC LETTER VE
+06CC ARABIC LETTER FARSI YEH
+06CD ARABIC LETTER YEH WITH TAIL
+06CE ARABIC LETTER YEH WITH SMALL V
+06D0 ARABIC LETTER E
+06D1 ARABIC LETTER YEH WITH THREE DOTS BELOW
+06D2 ARABIC LETTER YEH BARREE
+06D3 ARABIC LETTER YEH BARREE WITH HAMZA ABOVE
+06D4 ARABIC FULL STOP
+06D5 ARABIC LETTER AE
+06D6 ARABIC SMALL HIGH LIGATURE SAD WITH LAM WITH ALEF MAKSURA
+06D7 ARABIC SMALL HIGH LIGATURE QAF WITH LAM WITH ALEF MAKSURA
+06D8 ARABIC SMALL HIGH MEEM INITIAL FORM
+06D9 ARABIC SMALL HIGH LAM ALEF
+06DA ARABIC SMALL HIGH JEEM
+06DB ARABIC SMALL HIGH THREE DOTS
+06DC ARABIC SMALL HIGH SEEN
+06DD ARABIC END OF AYAH
+06DE ARABIC START OF RUB EL HIZB
+06DF ARABIC SMALL HIGH ROUNDED ZERO
+06E0 ARABIC SMALL HIGH UPRIGHT RECTANGULAR ZERO
+06E1 ARABIC SMALL HIGH DOTLESS HEAD OF KHAH
+06E2 ARABIC SMALL HIGH MEEM ISOLATED FORM
+06E3 ARABIC SMALL LOW SEEN
+06E4 ARABIC SMALL HIGH MADDA
+06E5 ARABIC SMALL WAW
+06E6 ARABIC SMALL YEH
+06E7 ARABIC SMALL HIGH YEH
+06E8 ARABIC SMALL HIGH NOON
+06E9 ARABIC PLACE OF SAJDAH
+06EA ARABIC EMPTY CENTRE LOW STOP
+06EB ARABIC EMPTY CENTRE HIGH STOP
+06EC ARABIC ROUNDED HIGH STOP WITH FILLED CENTRE
+06ED ARABIC SMALL LOW MEEM
+06F0 EXTENDED ARABIC-INDIC DIGIT ZERO
+06F1 EXTENDED ARABIC-INDIC DIGIT ONE
+06F2 EXTENDED ARABIC-INDIC DIGIT TWO
+06F3 EXTENDED ARABIC-INDIC DIGIT THREE
+06F4 EXTENDED ARABIC-INDIC DIGIT FOUR
+06F5 EXTENDED ARABIC-INDIC DIGIT FIVE
+06F6 EXTENDED ARABIC-INDIC DIGIT SIX
+06F7 EXTENDED ARABIC-INDIC DIGIT SEVEN
+06F8 EXTENDED ARABIC-INDIC DIGIT EIGHT
+06F9 EXTENDED ARABIC-INDIC DIGIT NINE
+0901 DEVANAGARI SIGN CANDRABINDU
+0902 DEVANAGARI SIGN ANUSVARA
+0903 DEVANAGARI SIGN VISARGA
+0905 DEVANAGARI LETTER A
+0906 DEVANAGARI LETTER AA
+0907 DEVANAGARI LETTER I
+0908 DEVANAGARI LETTER II
+0909 DEVANAGARI LETTER U
+090A DEVANAGARI LETTER UU
+090B DEVANAGARI LETTER VOCALIC R
+090C DEVANAGARI LETTER VOCALIC L
+090D DEVANAGARI LETTER CANDRA E
+090E DEVANAGARI LETTER SHORT E
+090F DEVANAGARI LETTER E
+0910 DEVANAGARI LETTER AI
+0911 DEVANAGARI LETTER CANDRA O
+0912 DEVANAGARI LETTER SHORT O
+0913 DEVANAGARI LETTER O
+0914 DEVANAGARI LETTER AU
+0915 DEVANAGARI LETTER KA
+0916 DEVANAGARI LETTER KHA
+0917 DEVANAGARI LETTER GA
+0918 DEVANAGARI LETTER GHA
+0919 DEVANAGARI LETTER NGA
+091A DEVANAGARI LETTER CA
+091B DEVANAGARI LETTER CHA
+091C DEVANAGARI LETTER JA
+091D DEVANAGARI LETTER JHA
+091E DEVANAGARI LETTER NYA
+091F DEVANAGARI LETTER TTA
+0920 DEVANAGARI LETTER TTHA
+0921 DEVANAGARI LETTER DDA
+0922 DEVANAGARI LETTER DDHA
+0923 DEVANAGARI LETTER NNA
+0924 DEVANAGARI LETTER TA
+0925 DEVANAGARI LETTER THA
+0926 DEVANAGARI LETTER DA
+0927 DEVANAGARI LETTER DHA
+0928 DEVANAGARI LETTER NA
+0929 DEVANAGARI LETTER NNNA
+092A DEVANAGARI LETTER PA
+092B DEVANAGARI LETTER PHA
+092C DEVANAGARI LETTER BA
+092D DEVANAGARI LETTER BHA
+092E DEVANAGARI LETTER MA
+092F DEVANAGARI LETTER YA
+0930 DEVANAGARI LETTER RA
+0931 DEVANAGARI LETTER RRA
+0932 DEVANAGARI LETTER LA
+0933 DEVANAGARI LETTER LLA
+0934 DEVANAGARI LETTER LLLA
+0935 DEVANAGARI LETTER VA
+0936 DEVANAGARI LETTER SHA
+0937 DEVANAGARI LETTER SSA
+0938 DEVANAGARI LETTER SA
+0939 DEVANAGARI LETTER HA
+093C DEVANAGARI SIGN NUKTA
+093D DEVANAGARI SIGN AVAGRAHA
+093E DEVANAGARI VOWEL SIGN AA
+093F DEVANAGARI VOWEL SIGN I
+0940 DEVANAGARI VOWEL SIGN II
+0941 DEVANAGARI VOWEL SIGN U
+0942 DEVANAGARI VOWEL SIGN UU
+0943 DEVANAGARI VOWEL SIGN VOCALIC R
+0944 DEVANAGARI VOWEL SIGN VOCALIC RR
+0945 DEVANAGARI VOWEL SIGN CANDRA E
+0946 DEVANAGARI VOWEL SIGN SHORT E
+0947 DEVANAGARI VOWEL SIGN E
+0948 DEVANAGARI VOWEL SIGN AI
+0949 DEVANAGARI VOWEL SIGN CANDRA O
+094A DEVANAGARI VOWEL SIGN SHORT O
+094B DEVANAGARI VOWEL SIGN O
+094C DEVANAGARI VOWEL SIGN AU
+094D DEVANAGARI SIGN VIRAMA
+0950 DEVANAGARI OM
+0951 DEVANAGARI STRESS SIGN UDATTA
+0952 DEVANAGARI STRESS SIGN ANUDATTA
+0953 DEVANAGARI GRAVE ACCENT
+0954 DEVANAGARI ACUTE ACCENT
+0958 DEVANAGARI LETTER QA
+0959 DEVANAGARI LETTER KHHA
+095A DEVANAGARI LETTER GHHA
+095B DEVANAGARI LETTER ZA
+095C DEVANAGARI LETTER DDDHA
+095D DEVANAGARI LETTER RHA
+095E DEVANAGARI LETTER FA
+095F DEVANAGARI LETTER YYA
+0960 DEVANAGARI LETTER VOCALIC RR
+0961 DEVANAGARI LETTER VOCALIC LL
+0962 DEVANAGARI VOWEL SIGN VOCALIC L
+0963 DEVANAGARI VOWEL SIGN VOCALIC LL
+0964 DEVANAGARI DANDA
+0965 DEVANAGARI DOUBLE DANDA
+0966 DEVANAGARI DIGIT ZERO
+0967 DEVANAGARI DIGIT ONE
+0968 DEVANAGARI DIGIT TWO
+0969 DEVANAGARI DIGIT THREE
+096A DEVANAGARI DIGIT FOUR
+096B DEVANAGARI DIGIT FIVE
+096C DEVANAGARI DIGIT SIX
+096D DEVANAGARI DIGIT SEVEN
+096E DEVANAGARI DIGIT EIGHT
+096F DEVANAGARI DIGIT NINE
+0970 DEVANAGARI ABBREVIATION SIGN
+0981 BENGALI SIGN CANDRABINDU
+0982 BENGALI SIGN ANUSVARA
+0983 BENGALI SIGN VISARGA
+0985 BENGALI LETTER A
+0986 BENGALI LETTER AA
+0987 BENGALI LETTER I
+0988 BENGALI LETTER II
+0989 BENGALI LETTER U
+098A BENGALI LETTER UU
+098B BENGALI LETTER VOCALIC R
+098C BENGALI LETTER VOCALIC L
+098F BENGALI LETTER E
+0990 BENGALI LETTER AI
+0993 BENGALI LETTER O
+0994 BENGALI LETTER AU
+0995 BENGALI LETTER KA
+0996 BENGALI LETTER KHA
+0997 BENGALI LETTER GA
+0998 BENGALI LETTER GHA
+0999 BENGALI LETTER NGA
+099A BENGALI LETTER CA
+099B BENGALI LETTER CHA
+099C BENGALI LETTER JA
+099D BENGALI LETTER JHA
+099E BENGALI LETTER NYA
+099F BENGALI LETTER TTA
+09A0 BENGALI LETTER TTHA
+09A1 BENGALI LETTER DDA
+09A2 BENGALI LETTER DDHA
+09A3 BENGALI LETTER NNA
+09A4 BENGALI LETTER TA
+09A5 BENGALI LETTER THA
+09A6 BENGALI LETTER DA
+09A7 BENGALI LETTER DHA
+09A8 BENGALI LETTER NA
+09AA BENGALI LETTER PA
+09AB BENGALI LETTER PHA
+09AC BENGALI LETTER BA
+09AD BENGALI LETTER BHA
+09AE BENGALI LETTER MA
+09AF BENGALI LETTER YA
+09B0 BENGALI LETTER RA
+09B2 BENGALI LETTER LA
+09B6 BENGALI LETTER SHA
+09B7 BENGALI LETTER SSA
+09B8 BENGALI LETTER SA
+09B9 BENGALI LETTER HA
+09BC BENGALI SIGN NUKTA
+09BE BENGALI VOWEL SIGN AA
+09BF BENGALI VOWEL SIGN I
+09C0 BENGALI VOWEL SIGN II
+09C1 BENGALI VOWEL SIGN U
+09C2 BENGALI VOWEL SIGN UU
+09C3 BENGALI VOWEL SIGN VOCALIC R
+09C4 BENGALI VOWEL SIGN VOCALIC RR
+09C7 BENGALI VOWEL SIGN E
+09C8 BENGALI VOWEL SIGN AI
+09CB BENGALI VOWEL SIGN O
+09CC BENGALI VOWEL SIGN AU
+09CD BENGALI SIGN VIRAMA
+09D7 BENGALI AU LENGTH MARK
+09DC BENGALI LETTER RRA
+09DD BENGALI LETTER RHA
+09DF BENGALI LETTER YYA
+09E0 BENGALI LETTER VOCALIC RR
+09E1 BENGALI LETTER VOCALIC LL
+09E2 BENGALI VOWEL SIGN VOCALIC L
+09E3 BENGALI VOWEL SIGN VOCALIC LL
+09E6 BENGALI DIGIT ZERO
+09E7 BENGALI DIGIT ONE
+09E8 BENGALI DIGIT TWO
+09E9 BENGALI DIGIT THREE
+09EA BENGALI DIGIT FOUR
+09EB BENGALI DIGIT FIVE
+09EC BENGALI DIGIT SIX
+09ED BENGALI DIGIT SEVEN
+09EE BENGALI DIGIT EIGHT
+09EF BENGALI DIGIT NINE
+09F0 BENGALI LETTER RA WITH MIDDLE DIAGONAL
+09F1 BENGALI LETTER RA WITH LOWER DIAGONAL
+09F2 BENGALI RUPEE MARK
+09F3 BENGALI RUPEE SIGN
+09F4 BENGALI CURRENCY NUMERATOR ONE
+09F5 BENGALI CURRENCY NUMERATOR TWO
+09F6 BENGALI CURRENCY NUMERATOR THREE
+09F7 BENGALI CURRENCY NUMERATOR FOUR
+09F8 BENGALI CURRENCY NUMERATOR ONE LESS THAN THE DENOMINATOR
+09F9 BENGALI CURRENCY DENOMINATOR SIXTEEN
+09FA BENGALI ISSHAR
+0A02 GURMUKHI SIGN BINDI
+0A05 GURMUKHI LETTER A
+0A06 GURMUKHI LETTER AA
+0A07 GURMUKHI LETTER I
+0A08 GURMUKHI LETTER II
+0A09 GURMUKHI LETTER U
+0A0A GURMUKHI LETTER UU
+0A0F GURMUKHI LETTER EE
+0A10 GURMUKHI LETTER AI
+0A13 GURMUKHI LETTER OO
+0A14 GURMUKHI LETTER AU
+0A15 GURMUKHI LETTER KA
+0A16 GURMUKHI LETTER KHA
+0A17 GURMUKHI LETTER GA
+0A18 GURMUKHI LETTER GHA
+0A19 GURMUKHI LETTER NGA
+0A1A GURMUKHI LETTER CA
+0A1B GURMUKHI LETTER CHA
+0A1C GURMUKHI LETTER JA
+0A1D GURMUKHI LETTER JHA
+0A1E GURMUKHI LETTER NYA
+0A1F GURMUKHI LETTER TTA
+0A20 GURMUKHI LETTER TTHA
+0A21 GURMUKHI LETTER DDA
+0A22 GURMUKHI LETTER DDHA
+0A23 GURMUKHI LETTER NNA
+0A24 GURMUKHI LETTER TA
+0A25 GURMUKHI LETTER THA
+0A26 GURMUKHI LETTER DA
+0A27 GURMUKHI LETTER DHA
+0A28 GURMUKHI LETTER NA
+0A2A GURMUKHI LETTER PA
+0A2B GURMUKHI LETTER PHA
+0A2C GURMUKHI LETTER BA
+0A2D GURMUKHI LETTER BHA
+0A2E GURMUKHI LETTER MA
+0A2F GURMUKHI LETTER YA
+0A30 GURMUKHI LETTER RA
+0A32 GURMUKHI LETTER LA
+0A33 GURMUKHI LETTER LLA
+0A35 GURMUKHI LETTER VA
+0A36 GURMUKHI LETTER SHA
+0A38 GURMUKHI LETTER SA
+0A39 GURMUKHI LETTER HA
+0A3C GURMUKHI SIGN NUKTA
+0A3E GURMUKHI VOWEL SIGN AA
+0A3F GURMUKHI VOWEL SIGN I
+0A40 GURMUKHI VOWEL SIGN II
+0A41 GURMUKHI VOWEL SIGN U
+0A42 GURMUKHI VOWEL SIGN UU
+0A47 GURMUKHI VOWEL SIGN EE
+0A48 GURMUKHI VOWEL SIGN AI
+0A4B GURMUKHI VOWEL SIGN OO
+0A4C GURMUKHI VOWEL SIGN AU
+0A4D GURMUKHI SIGN VIRAMA
+0A59 GURMUKHI LETTER KHHA
+0A5A GURMUKHI LETTER GHHA
+0A5B GURMUKHI LETTER ZA
+0A5C GURMUKHI LETTER RRA
+0A5E GURMUKHI LETTER FA
+0A66 GURMUKHI DIGIT ZERO
+0A67 GURMUKHI DIGIT ONE
+0A68 GURMUKHI DIGIT TWO
+0A69 GURMUKHI DIGIT THREE
+0A6A GURMUKHI DIGIT FOUR
+0A6B GURMUKHI DIGIT FIVE
+0A6C GURMUKHI DIGIT SIX
+0A6D GURMUKHI DIGIT SEVEN
+0A6E GURMUKHI DIGIT EIGHT
+0A6F GURMUKHI DIGIT NINE
+0A70 GURMUKHI TIPPI
+0A71 GURMUKHI ADDAK
+0A72 GURMUKHI IRI
+0A73 GURMUKHI URA
+0A74 GURMUKHI EK ONKAR
+0A81 GUJARATI SIGN CANDRABINDU
+0A82 GUJARATI SIGN ANUSVARA
+0A83 GUJARATI SIGN VISARGA
+0A85 GUJARATI LETTER A
+0A86 GUJARATI LETTER AA
+0A87 GUJARATI LETTER I
+0A88 GUJARATI LETTER II
+0A89 GUJARATI LETTER U
+0A8A GUJARATI LETTER UU
+0A8B GUJARATI LETTER VOCALIC R
+0A8D GUJARATI VOWEL CANDRA E
+0A8F GUJARATI LETTER E
+0A90 GUJARATI LETTER AI
+0A91 GUJARATI VOWEL CANDRA O
+0A93 GUJARATI LETTER O
+0A94 GUJARATI LETTER AU
+0A95 GUJARATI LETTER KA
+0A96 GUJARATI LETTER KHA
+0A97 GUJARATI LETTER GA
+0A98 GUJARATI LETTER GHA
+0A99 GUJARATI LETTER NGA
+0A9A GUJARATI LETTER CA
+0A9B GUJARATI LETTER CHA
+0A9C GUJARATI LETTER JA
+0A9D GUJARATI LETTER JHA
+0A9E GUJARATI LETTER NYA
+0A9F GUJARATI LETTER TTA
+0AA0 GUJARATI LETTER TTHA
+0AA1 GUJARATI LETTER DDA
+0AA2 GUJARATI LETTER DDHA
+0AA3 GUJARATI LETTER NNA
+0AA4 GUJARATI LETTER TA
+0AA5 GUJARATI LETTER THA
+0AA6 GUJARATI LETTER DA
+0AA7 GUJARATI LETTER DHA
+0AA8 GUJARATI LETTER NA
+0AAA GUJARATI LETTER PA
+0AAB GUJARATI LETTER PHA
+0AAC GUJARATI LETTER BA
+0AAD GUJARATI LETTER BHA
+0AAE GUJARATI LETTER MA
+0AAF GUJARATI LETTER YA
+0AB0 GUJARATI LETTER RA
+0AB2 GUJARATI LETTER LA
+0AB3 GUJARATI LETTER LLA
+0AB5 GUJARATI LETTER VA
+0AB6 GUJARATI LETTER SHA
+0AB7 GUJARATI LETTER SSA
+0AB8 GUJARATI LETTER SA
+0AB9 GUJARATI LETTER HA
+0ABC GUJARATI SIGN NUKTA
+0ABD GUJARATI SIGN AVAGRAHA
+0ABE GUJARATI VOWEL SIGN AA
+0ABF GUJARATI VOWEL SIGN I
+0AC0 GUJARATI VOWEL SIGN II
+0AC1 GUJARATI VOWEL SIGN U
+0AC2 GUJARATI VOWEL SIGN UU
+0AC3 GUJARATI VOWEL SIGN VOCALIC R
+0AC4 GUJARATI VOWEL SIGN VOCALIC RR
+0AC5 GUJARATI VOWEL SIGN CANDRA E
+0AC7 GUJARATI VOWEL SIGN E
+0AC8 GUJARATI VOWEL SIGN AI
+0AC9 GUJARATI VOWEL SIGN CANDRA O
+0ACB GUJARATI VOWEL SIGN O
+0ACC GUJARATI VOWEL SIGN AU
+0ACD GUJARATI SIGN VIRAMA
+0AD0 GUJARATI OM
+0AE0 GUJARATI LETTER VOCALIC RR
+0AE6 GUJARATI DIGIT ZERO
+0AE7 GUJARATI DIGIT ONE
+0AE8 GUJARATI DIGIT TWO
+0AE9 GUJARATI DIGIT THREE
+0AEA GUJARATI DIGIT FOUR
+0AEB GUJARATI DIGIT FIVE
+0AEC GUJARATI DIGIT SIX
+0AED GUJARATI DIGIT SEVEN
+0AEE GUJARATI DIGIT EIGHT
+0AEF GUJARATI DIGIT NINE
+0B01 ORIYA SIGN CANDRABINDU
+0B02 ORIYA SIGN ANUSVARA
+0B03 ORIYA SIGN VISARGA
+0B05 ORIYA LETTER A
+0B06 ORIYA LETTER AA
+0B07 ORIYA LETTER I
+0B08 ORIYA LETTER II
+0B09 ORIYA LETTER U
+0B0A ORIYA LETTER UU
+0B0B ORIYA LETTER VOCALIC R
+0B0C ORIYA LETTER VOCALIC L
+0B0F ORIYA LETTER E
+0B10 ORIYA LETTER AI
+0B13 ORIYA LETTER O
+0B14 ORIYA LETTER AU
+0B15 ORIYA LETTER KA
+0B16 ORIYA LETTER KHA
+0B17 ORIYA LETTER GA
+0B18 ORIYA LETTER GHA
+0B19 ORIYA LETTER NGA
+0B1A ORIYA LETTER CA
+0B1B ORIYA LETTER CHA
+0B1C ORIYA LETTER JA
+0B1D ORIYA LETTER JHA
+0B1E ORIYA LETTER NYA
+0B1F ORIYA LETTER TTA
+0B20 ORIYA LETTER TTHA
+0B21 ORIYA LETTER DDA
+0B22 ORIYA LETTER DDHA
+0B23 ORIYA LETTER NNA
+0B24 ORIYA LETTER TA
+0B25 ORIYA LETTER THA
+0B26 ORIYA LETTER DA
+0B27 ORIYA LETTER DHA
+0B28 ORIYA LETTER NA
+0B2A ORIYA LETTER PA
+0B2B ORIYA LETTER PHA
+0B2C ORIYA LETTER BA
+0B2D ORIYA LETTER BHA
+0B2E ORIYA LETTER MA
+0B2F ORIYA LETTER YA
+0B30 ORIYA LETTER RA
+0B32 ORIYA LETTER LA
+0B33 ORIYA LETTER LLA
+0B36 ORIYA LETTER SHA
+0B37 ORIYA LETTER SSA
+0B38 ORIYA LETTER SA
+0B39 ORIYA LETTER HA
+0B3C ORIYA SIGN NUKTA
+0B3D ORIYA SIGN AVAGRAHA
+0B3E ORIYA VOWEL SIGN AA
+0B3F ORIYA VOWEL SIGN I
+0B40 ORIYA VOWEL SIGN II
+0B41 ORIYA VOWEL SIGN U
+0B42 ORIYA VOWEL SIGN UU
+0B43 ORIYA VOWEL SIGN VOCALIC R
+0B47 ORIYA VOWEL SIGN E
+0B48 ORIYA VOWEL SIGN AI
+0B4B ORIYA VOWEL SIGN O
+0B4C ORIYA VOWEL SIGN AU
+0B4D ORIYA SIGN VIRAMA
+0B56 ORIYA AI LENGTH MARK
+0B57 ORIYA AU LENGTH MARK
+0B5C ORIYA LETTER RRA
+0B5D ORIYA LETTER RHA
+0B5F ORIYA LETTER YYA
+0B60 ORIYA LETTER VOCALIC RR
+0B61 ORIYA LETTER VOCALIC LL
+0B66 ORIYA DIGIT ZERO
+0B67 ORIYA DIGIT ONE
+0B68 ORIYA DIGIT TWO
+0B69 ORIYA DIGIT THREE
+0B6A ORIYA DIGIT FOUR
+0B6B ORIYA DIGIT FIVE
+0B6C ORIYA DIGIT SIX
+0B6D ORIYA DIGIT SEVEN
+0B6E ORIYA DIGIT EIGHT
+0B6F ORIYA DIGIT NINE
+0B70 ORIYA ISSHAR
+0B82 TAMIL SIGN ANUSVARA
+0B83 TAMIL SIGN VISARGA
+0B85 TAMIL LETTER A
+0B86 TAMIL LETTER AA
+0B87 TAMIL LETTER I
+0B88 TAMIL LETTER II
+0B89 TAMIL LETTER U
+0B8A TAMIL LETTER UU
+0B8E TAMIL LETTER E
+0B8F TAMIL LETTER EE
+0B90 TAMIL LETTER AI
+0B92 TAMIL LETTER O
+0B93 TAMIL LETTER OO
+0B94 TAMIL LETTER AU
+0B95 TAMIL LETTER KA
+0B99 TAMIL LETTER NGA
+0B9A TAMIL LETTER CA
+0B9C TAMIL LETTER JA
+0B9E TAMIL LETTER NYA
+0B9F TAMIL LETTER TTA
+0BA3 TAMIL LETTER NNA
+0BA4 TAMIL LETTER TA
+0BA8 TAMIL LETTER NA
+0BA9 TAMIL LETTER NNNA
+0BAA TAMIL LETTER PA
+0BAE TAMIL LETTER MA
+0BAF TAMIL LETTER YA
+0BB0 TAMIL LETTER RA
+0BB1 TAMIL LETTER RRA
+0BB2 TAMIL LETTER LA
+0BB3 TAMIL LETTER LLA
+0BB4 TAMIL LETTER LLLA
+0BB5 TAMIL LETTER VA
+0BB7 TAMIL LETTER SSA
+0BB8 TAMIL LETTER SA
+0BB9 TAMIL LETTER HA
+0BBE TAMIL VOWEL SIGN AA
+0BBF TAMIL VOWEL SIGN I
+0BC0 TAMIL VOWEL SIGN II
+0BC1 TAMIL VOWEL SIGN U
+0BC2 TAMIL VOWEL SIGN UU
+0BC6 TAMIL VOWEL SIGN E
+0BC7 TAMIL VOWEL SIGN EE
+0BC8 TAMIL VOWEL SIGN AI
+0BCA TAMIL VOWEL SIGN O
+0BCB TAMIL VOWEL SIGN OO
+0BCC TAMIL VOWEL SIGN AU
+0BCD TAMIL SIGN VIRAMA
+0BD7 TAMIL AU LENGTH MARK
+0BE7 TAMIL DIGIT ONE
+0BE8 TAMIL DIGIT TWO
+0BE9 TAMIL DIGIT THREE
+0BEA TAMIL DIGIT FOUR
+0BEB TAMIL DIGIT FIVE
+0BEC TAMIL DIGIT SIX
+0BED TAMIL DIGIT SEVEN
+0BEE TAMIL DIGIT EIGHT
+0BEF TAMIL DIGIT NINE
+0BF0 TAMIL NUMBER TEN
+0BF1 TAMIL NUMBER ONE HUNDRED
+0BF2 TAMIL NUMBER ONE THOUSAND
+0C01 TELUGU SIGN CANDRABINDU
+0C02 TELUGU SIGN ANUSVARA
+0C03 TELUGU SIGN VISARGA
+0C05 TELUGU LETTER A
+0C06 TELUGU LETTER AA
+0C07 TELUGU LETTER I
+0C08 TELUGU LETTER II
+0C09 TELUGU LETTER U
+0C0A TELUGU LETTER UU
+0C0B TELUGU LETTER VOCALIC R
+0C0C TELUGU LETTER VOCALIC L
+0C0E TELUGU LETTER E
+0C0F TELUGU LETTER EE
+0C10 TELUGU LETTER AI
+0C12 TELUGU LETTER O
+0C13 TELUGU LETTER OO
+0C14 TELUGU LETTER AU
+0C15 TELUGU LETTER KA
+0C16 TELUGU LETTER KHA
+0C17 TELUGU LETTER GA
+0C18 TELUGU LETTER GHA
+0C19 TELUGU LETTER NGA
+0C1A TELUGU LETTER CA
+0C1B TELUGU LETTER CHA
+0C1C TELUGU LETTER JA
+0C1D TELUGU LETTER JHA
+0C1E TELUGU LETTER NYA
+0C1F TELUGU LETTER TTA
+0C20 TELUGU LETTER TTHA
+0C21 TELUGU LETTER DDA
+0C22 TELUGU LETTER DDHA
+0C23 TELUGU LETTER NNA
+0C24 TELUGU LETTER TA
+0C25 TELUGU LETTER THA
+0C26 TELUGU LETTER DA
+0C27 TELUGU LETTER DHA
+0C28 TELUGU LETTER NA
+0C2A TELUGU LETTER PA
+0C2B TELUGU LETTER PHA
+0C2C TELUGU LETTER BA
+0C2D TELUGU LETTER BHA
+0C2E TELUGU LETTER MA
+0C2F TELUGU LETTER YA
+0C30 TELUGU LETTER RA
+0C31 TELUGU LETTER RRA
+0C32 TELUGU LETTER LA
+0C33 TELUGU LETTER LLA
+0C35 TELUGU LETTER VA
+0C36 TELUGU LETTER SHA
+0C37 TELUGU LETTER SSA
+0C38 TELUGU LETTER SA
+0C39 TELUGU LETTER HA
+0C3E TELUGU VOWEL SIGN AA
+0C3F TELUGU VOWEL SIGN I
+0C40 TELUGU VOWEL SIGN II
+0C41 TELUGU VOWEL SIGN U
+0C42 TELUGU VOWEL SIGN UU
+0C43 TELUGU VOWEL SIGN VOCALIC R
+0C44 TELUGU VOWEL SIGN VOCALIC RR
+0C46 TELUGU VOWEL SIGN E
+0C47 TELUGU VOWEL SIGN EE
+0C48 TELUGU VOWEL SIGN AI
+0C4A TELUGU VOWEL SIGN O
+0C4B TELUGU VOWEL SIGN OO
+0C4C TELUGU VOWEL SIGN AU
+0C4D TELUGU SIGN VIRAMA
+0C55 TELUGU LENGTH MARK
+0C56 TELUGU AI LENGTH MARK
+0C60 TELUGU LETTER VOCALIC RR
+0C61 TELUGU LETTER VOCALIC LL
+0C66 TELUGU DIGIT ZERO
+0C67 TELUGU DIGIT ONE
+0C68 TELUGU DIGIT TWO
+0C69 TELUGU DIGIT THREE
+0C6A TELUGU DIGIT FOUR
+0C6B TELUGU DIGIT FIVE
+0C6C TELUGU DIGIT SIX
+0C6D TELUGU DIGIT SEVEN
+0C6E TELUGU DIGIT EIGHT
+0C6F TELUGU DIGIT NINE
+0C82 KANNADA SIGN ANUSVARA
+0C83 KANNADA SIGN VISARGA
+0C85 KANNADA LETTER A
+0C86 KANNADA LETTER AA
+0C87 KANNADA LETTER I
+0C88 KANNADA LETTER II
+0C89 KANNADA LETTER U
+0C8A KANNADA LETTER UU
+0C8B KANNADA LETTER VOCALIC R
+0C8C KANNADA LETTER VOCALIC L
+0C8E KANNADA LETTER E
+0C8F KANNADA LETTER EE
+0C90 KANNADA LETTER AI
+0C92 KANNADA LETTER O
+0C93 KANNADA LETTER OO
+0C94 KANNADA LETTER AU
+0C95 KANNADA LETTER KA
+0C96 KANNADA LETTER KHA
+0C97 KANNADA LETTER GA
+0C98 KANNADA LETTER GHA
+0C99 KANNADA LETTER NGA
+0C9A KANNADA LETTER CA
+0C9B KANNADA LETTER CHA
+0C9C KANNADA LETTER JA
+0C9D KANNADA LETTER JHA
+0C9E KANNADA LETTER NYA
+0C9F KANNADA LETTER TTA
+0CA0 KANNADA LETTER TTHA
+0CA1 KANNADA LETTER DDA
+0CA2 KANNADA LETTER DDHA
+0CA3 KANNADA LETTER NNA
+0CA4 KANNADA LETTER TA
+0CA5 KANNADA LETTER THA
+0CA6 KANNADA LETTER DA
+0CA7 KANNADA LETTER DHA
+0CA8 KANNADA LETTER NA
+0CAA KANNADA LETTER PA
+0CAB KANNADA LETTER PHA
+0CAC KANNADA LETTER BA
+0CAD KANNADA LETTER BHA
+0CAE KANNADA LETTER MA
+0CAF KANNADA LETTER YA
+0CB0 KANNADA LETTER RA
+0CB1 KANNADA LETTER RRA
+0CB2 KANNADA LETTER LA
+0CB3 KANNADA LETTER LLA
+0CB5 KANNADA LETTER VA
+0CB6 KANNADA LETTER SHA
+0CB7 KANNADA LETTER SSA
+0CB8 KANNADA LETTER SA
+0CB9 KANNADA LETTER HA
+0CBE KANNADA VOWEL SIGN AA
+0CBF KANNADA VOWEL SIGN I
+0CC0 KANNADA VOWEL SIGN II
+0CC1 KANNADA VOWEL SIGN U
+0CC2 KANNADA VOWEL SIGN UU
+0CC3 KANNADA VOWEL SIGN VOCALIC R
+0CC4 KANNADA VOWEL SIGN VOCALIC RR
+0CC6 KANNADA VOWEL SIGN E
+0CC7 KANNADA VOWEL SIGN EE
+0CC8 KANNADA VOWEL SIGN AI
+0CCA KANNADA VOWEL SIGN O
+0CCB KANNADA VOWEL SIGN OO
+0CCC KANNADA VOWEL SIGN AU
+0CCD KANNADA SIGN VIRAMA
+0CD5 KANNADA LENGTH MARK
+0CD6 KANNADA AI LENGTH MARK
+0CDE KANNADA LETTER FA
+0CE0 KANNADA LETTER VOCALIC RR
+0CE1 KANNADA LETTER VOCALIC LL
+0CE6 KANNADA DIGIT ZERO
+0CE7 KANNADA DIGIT ONE
+0CE8 KANNADA DIGIT TWO
+0CE9 KANNADA DIGIT THREE
+0CEA KANNADA DIGIT FOUR
+0CEB KANNADA DIGIT FIVE
+0CEC KANNADA DIGIT SIX
+0CED KANNADA DIGIT SEVEN
+0CEE KANNADA DIGIT EIGHT
+0CEF KANNADA DIGIT NINE
+0D02 MALAYALAM SIGN ANUSVARA
+0D03 MALAYALAM SIGN VISARGA
+0D05 MALAYALAM LETTER A
+0D06 MALAYALAM LETTER AA
+0D07 MALAYALAM LETTER I
+0D08 MALAYALAM LETTER II
+0D09 MALAYALAM LETTER U
+0D0A MALAYALAM LETTER UU
+0D0B MALAYALAM LETTER VOCALIC R
+0D0C MALAYALAM LETTER VOCALIC L
+0D0E MALAYALAM LETTER E
+0D0F MALAYALAM LETTER EE
+0D10 MALAYALAM LETTER AI
+0D12 MALAYALAM LETTER O
+0D13 MALAYALAM LETTER OO
+0D14 MALAYALAM LETTER AU
+0D15 MALAYALAM LETTER KA
+0D16 MALAYALAM LETTER KHA
+0D17 MALAYALAM LETTER GA
+0D18 MALAYALAM LETTER GHA
+0D19 MALAYALAM LETTER NGA
+0D1A MALAYALAM LETTER CA
+0D1B MALAYALAM LETTER CHA
+0D1C MALAYALAM LETTER JA
+0D1D MALAYALAM LETTER JHA
+0D1E MALAYALAM LETTER NYA
+0D1F MALAYALAM LETTER TTA
+0D20 MALAYALAM LETTER TTHA
+0D21 MALAYALAM LETTER DDA
+0D22 MALAYALAM LETTER DDHA
+0D23 MALAYALAM LETTER NNA
+0D24 MALAYALAM LETTER TA
+0D25 MALAYALAM LETTER THA
+0D26 MALAYALAM LETTER DA
+0D27 MALAYALAM LETTER DHA
+0D28 MALAYALAM LETTER NA
+0D2A MALAYALAM LETTER PA
+0D2B MALAYALAM LETTER PHA
+0D2C MALAYALAM LETTER BA
+0D2D MALAYALAM LETTER BHA
+0D2E MALAYALAM LETTER MA
+0D2F MALAYALAM LETTER YA
+0D30 MALAYALAM LETTER RA
+0D31 MALAYALAM LETTER RRA
+0D32 MALAYALAM LETTER LA
+0D33 MALAYALAM LETTER LLA
+0D34 MALAYALAM LETTER LLLA
+0D35 MALAYALAM LETTER VA
+0D36 MALAYALAM LETTER SHA
+0D37 MALAYALAM LETTER SSA
+0D38 MALAYALAM LETTER SA
+0D39 MALAYALAM LETTER HA
+0D3E MALAYALAM VOWEL SIGN AA
+0D3F MALAYALAM VOWEL SIGN I
+0D40 MALAYALAM VOWEL SIGN II
+0D41 MALAYALAM VOWEL SIGN U
+0D42 MALAYALAM VOWEL SIGN UU
+0D43 MALAYALAM VOWEL SIGN VOCALIC R
+0D46 MALAYALAM VOWEL SIGN E
+0D47 MALAYALAM VOWEL SIGN EE
+0D48 MALAYALAM VOWEL SIGN AI
+0D4A MALAYALAM VOWEL SIGN O
+0D4B MALAYALAM VOWEL SIGN OO
+0D4C MALAYALAM VOWEL SIGN AU
+0D4D MALAYALAM SIGN VIRAMA
+0D57 MALAYALAM AU LENGTH MARK
+0D60 MALAYALAM LETTER VOCALIC RR
+0D61 MALAYALAM LETTER VOCALIC LL
+0D66 MALAYALAM DIGIT ZERO
+0D67 MALAYALAM DIGIT ONE
+0D68 MALAYALAM DIGIT TWO
+0D69 MALAYALAM DIGIT THREE
+0D6A MALAYALAM DIGIT FOUR
+0D6B MALAYALAM DIGIT FIVE
+0D6C MALAYALAM DIGIT SIX
+0D6D MALAYALAM DIGIT SEVEN
+0D6E MALAYALAM DIGIT EIGHT
+0D6F MALAYALAM DIGIT NINE
+0E01 THAI CHARACTER KO KAI
+0E02 THAI CHARACTER KHO KHAI
+0E03 THAI CHARACTER KHO KHUAT
+0E04 THAI CHARACTER KHO KHWAI
+0E05 THAI CHARACTER KHO KHON
+0E06 THAI CHARACTER KHO RAKHANG
+0E07 THAI CHARACTER NGO NGU
+0E08 THAI CHARACTER CHO CHAN
+0E09 THAI CHARACTER CHO CHING
+0E0A THAI CHARACTER CHO CHANG
+0E0B THAI CHARACTER SO SO
+0E0C THAI CHARACTER CHO CHOE
+0E0D THAI CHARACTER YO YING
+0E0E THAI CHARACTER DO CHADA
+0E0F THAI CHARACTER TO PATAK
+0E10 THAI CHARACTER THO THAN
+0E11 THAI CHARACTER THO NANGMONTHO
+0E12 THAI CHARACTER THO PHUTHAO
+0E13 THAI CHARACTER NO NEN
+0E14 THAI CHARACTER DO DEK
+0E15 THAI CHARACTER TO TAO
+0E16 THAI CHARACTER THO THUNG
+0E17 THAI CHARACTER THO THAHAN
+0E18 THAI CHARACTER THO THONG
+0E19 THAI CHARACTER NO NU
+0E1A THAI CHARACTER BO BAIMAI
+0E1B THAI CHARACTER PO PLA
+0E1C THAI CHARACTER PHO PHUNG
+0E1D THAI CHARACTER FO FA
+0E1E THAI CHARACTER PHO PHAN
+0E1F THAI CHARACTER FO FAN
+0E20 THAI CHARACTER PHO SAMPHAO
+0E21 THAI CHARACTER MO MA
+0E22 THAI CHARACTER YO YAK
+0E23 THAI CHARACTER RO RUA
+0E24 THAI CHARACTER RU
+0E25 THAI CHARACTER LO LING
+0E26 THAI CHARACTER LU
+0E27 THAI CHARACTER WO WAEN
+0E28 THAI CHARACTER SO SALA
+0E29 THAI CHARACTER SO RUSI
+0E2A THAI CHARACTER SO SUA
+0E2B THAI CHARACTER HO HIP
+0E2C THAI CHARACTER LO CHULA
+0E2D THAI CHARACTER O ANG
+0E2E THAI CHARACTER HO NOKHUK
+0E2F THAI CHARACTER PAIYANNOI
+0E30 THAI CHARACTER SARA A
+0E31 THAI CHARACTER MAI HAN-AKAT
+0E32 THAI CHARACTER SARA AA
+0E33 THAI CHARACTER SARA AM
+0E34 THAI CHARACTER SARA I
+0E35 THAI CHARACTER SARA II
+0E36 THAI CHARACTER SARA UE
+0E37 THAI CHARACTER SARA UEE
+0E38 THAI CHARACTER SARA U
+0E39 THAI CHARACTER SARA UU
+0E3A THAI CHARACTER PHINTHU
+0E3F THAI CURRENCY SYMBOL BAHT
+0E40 THAI CHARACTER SARA E
+0E41 THAI CHARACTER SARA AE
+0E42 THAI CHARACTER SARA O
+0E43 THAI CHARACTER SARA AI MAIMUAN
+0E44 THAI CHARACTER SARA AI MAIMALAI
+0E45 THAI CHARACTER LAKKHANGYAO
+0E46 THAI CHARACTER MAIYAMOK
+0E47 THAI CHARACTER MAITAIKHU
+0E48 THAI CHARACTER MAI EK
+0E49 THAI CHARACTER MAI THO
+0E4A THAI CHARACTER MAI TRI
+0E4B THAI CHARACTER MAI CHATTAWA
+0E4C THAI CHARACTER THANTHAKHAT
+0E4D THAI CHARACTER NIKHAHIT
+0E4E THAI CHARACTER YAMAKKAN
+0E4F THAI CHARACTER FONGMAN
+0E50 THAI DIGIT ZERO
+0E51 THAI DIGIT ONE
+0E52 THAI DIGIT TWO
+0E53 THAI DIGIT THREE
+0E54 THAI DIGIT FOUR
+0E55 THAI DIGIT FIVE
+0E56 THAI DIGIT SIX
+0E57 THAI DIGIT SEVEN
+0E58 THAI DIGIT EIGHT
+0E59 THAI DIGIT NINE
+0E5A THAI CHARACTER ANGKHANKHU
+0E5B THAI CHARACTER KHOMUT
+0E81 LAO LETTER KO
+0E82 LAO LETTER KHO SUNG
+0E84 LAO LETTER KHO TAM
+0E87 LAO LETTER NGO
+0E88 LAO LETTER CO
+0E8A LAO LETTER SO TAM
+0E8D LAO LETTER NYO
+0E94 LAO LETTER DO
+0E95 LAO LETTER TO
+0E96 LAO LETTER THO SUNG
+0E97 LAO LETTER THO TAM
+0E99 LAO LETTER NO
+0E9A LAO LETTER BO
+0E9B LAO LETTER PO
+0E9C LAO LETTER PHO SUNG
+0E9D LAO LETTER FO TAM
+0E9E LAO LETTER PHO TAM
+0E9F LAO LETTER FO SUNG
+0EA1 LAO LETTER MO
+0EA2 LAO LETTER YO
+0EA3 LAO LETTER LO LING
+0EA5 LAO LETTER LO LOOT
+0EA7 LAO LETTER WO
+0EAA LAO LETTER SO SUNG
+0EAB LAO LETTER HO SUNG
+0EAD LAO LETTER O
+0EAE LAO LETTER HO TAM
+0EAF LAO ELLIPSIS
+0EB0 LAO VOWEL SIGN A
+0EB1 LAO VOWEL SIGN MAI KAN
+0EB2 LAO VOWEL SIGN AA
+0EB3 LAO VOWEL SIGN AM
+0EB4 LAO VOWEL SIGN I
+0EB5 LAO VOWEL SIGN II
+0EB6 LAO VOWEL SIGN Y
+0EB7 LAO VOWEL SIGN YY
+0EB8 LAO VOWEL SIGN U
+0EB9 LAO VOWEL SIGN UU
+0EBB LAO VOWEL SIGN MAI KON
+0EBC LAO SEMIVOWEL SIGN LO
+0EBD LAO SEMIVOWEL SIGN NYO
+0EC0 LAO VOWEL SIGN E
+0EC1 LAO VOWEL SIGN EI
+0EC2 LAO VOWEL SIGN O
+0EC3 LAO VOWEL SIGN AY
+0EC4 LAO VOWEL SIGN AI
+0EC6 LAO KO LA
+0EC8 LAO TONE MAI EK
+0EC9 LAO TONE MAI THO
+0ECA LAO TONE MAI TI
+0ECB LAO TONE MAI CATAWA
+0ECC LAO CANCELLATION MARK
+0ECD LAO NIGGAHITA
+0ED0 LAO DIGIT ZERO
+0ED1 LAO DIGIT ONE
+0ED2 LAO DIGIT TWO
+0ED3 LAO DIGIT THREE
+0ED4 LAO DIGIT FOUR
+0ED5 LAO DIGIT FIVE
+0ED6 LAO DIGIT SIX
+0ED7 LAO DIGIT SEVEN
+0ED8 LAO DIGIT EIGHT
+0ED9 LAO DIGIT NINE
+0EDC LAO HO NO
+0EDD LAO HO MO
+0F00 TIBETAN SYLLABLE OM
+0F01 TIBETAN MARK GTER YIG MGO TRUNCATED A
+0F02 TIBETAN MARK GTER YIG MGO -UM RNAM BCAD MA
+0F03 TIBETAN MARK GTER YIG MGO -UM GTER TSHEG MA
+0F04 TIBETAN MARK INITIAL YIG MGO MDUN MA
+0F05 TIBETAN MARK CLOSING YIG MGO SGAB MA
+0F06 TIBETAN MARK CARET YIG MGO PHUR SHAD MA
+0F07 TIBETAN MARK YIG MGO TSHEG SHAD MA
+0F08 TIBETAN MARK SBRUL SHAD
+0F09 TIBETAN MARK BSKUR YIG MGO
+0F0A TIBETAN MARK BKA- SHOG YIG MGO
+0F0B TIBETAN MARK INTERSYLLABIC TSHEG
+0F0C TIBETAN MARK DELIMITER TSHEG BSTAR
+0F0D TIBETAN MARK SHAD
+0F0E TIBETAN MARK NYIS SHAD
+0F0F TIBETAN MARK TSHEG SHAD
+0F10 TIBETAN MARK NYIS TSHEG SHAD
+0F11 TIBETAN MARK RIN CHEN SPUNGS SHAD
+0F12 TIBETAN MARK RGYA GRAM SHAD
+0F13 TIBETAN MARK CARET -DZUD RTAGS ME LONG CAN
+0F14 TIBETAN MARK GTER TSHEG
+0F15 TIBETAN LOGOTYPE SIGN CHAD RTAGS
+0F16 TIBETAN LOGOTYPE SIGN LHAG RTAGS
+0F17 TIBETAN ASTROLOGICAL SIGN SGRA GCAN -CHAR RTAGS
+0F18 TIBETAN ASTROLOGICAL SIGN -KHYUD PA
+0F19 TIBETAN ASTROLOGICAL SIGN SDONG TSHUGS
+0F1A TIBETAN SIGN RDEL DKAR GCIG
+0F1B TIBETAN SIGN RDEL DKAR GNYIS
+0F1C TIBETAN SIGN RDEL DKAR GSUM
+0F1D TIBETAN SIGN RDEL NAG GCIG
+0F1E TIBETAN SIGN RDEL NAG GNYIS
+0F1F TIBETAN SIGN RDEL DKAR RDEL NAG
+0F20 TIBETAN DIGIT ZERO
+0F21 TIBETAN DIGIT ONE
+0F22 TIBETAN DIGIT TWO
+0F23 TIBETAN DIGIT THREE
+0F24 TIBETAN DIGIT FOUR
+0F25 TIBETAN DIGIT FIVE
+0F26 TIBETAN DIGIT SIX
+0F27 TIBETAN DIGIT SEVEN
+0F28 TIBETAN DIGIT EIGHT
+0F29 TIBETAN DIGIT NINE
+0F2A TIBETAN DIGIT HALF ONE
+0F2B TIBETAN DIGIT HALF TWO
+0F2C TIBETAN DIGIT HALF THREE
+0F2D TIBETAN DIGIT HALF FOUR
+0F2E TIBETAN DIGIT HALF FIVE
+0F2F TIBETAN DIGIT HALF SIX
+0F30 TIBETAN DIGIT HALF SEVEN
+0F31 TIBETAN DIGIT HALF EIGHT
+0F32 TIBETAN DIGIT HALF NINE
+0F33 TIBETAN DIGIT HALF ZERO
+0F34 TIBETAN MARK BSDUS RTAGS
+0F35 TIBETAN MARK NGAS BZUNG NYI ZLA
+0F36 TIBETAN MARK CARET -DZUD RTAGS BZHI MIG CAN
+0F37 TIBETAN MARK NGAS BZUNG SGOR RTAGS
+0F38 TIBETAN MARK CHE MGO
+0F39 TIBETAN MARK TSA -PHRU
+0F3A TIBETAN MARK GUG RTAGS GYON
+0F3B TIBETAN MARK GUG RTAGS GYAS
+0F3C TIBETAN MARK ANG KHANG GYON
+0F3D TIBETAN MARK ANG KHANG GYAS
+0F3E TIBETAN SIGN YAR TSHES
+0F3F TIBETAN SIGN MAR TSHES
+0F40 TIBETAN LETTER KA
+0F41 TIBETAN LETTER KHA
+0F42 TIBETAN LETTER GA
+0F43 TIBETAN LETTER GHA
+0F44 TIBETAN LETTER NGA
+0F45 TIBETAN LETTER CA
+0F46 TIBETAN LETTER CHA
+0F47 TIBETAN LETTER JA
+0F49 TIBETAN LETTER NYA
+0F4A TIBETAN LETTER TTA
+0F4B TIBETAN LETTER TTHA
+0F4C TIBETAN LETTER DDA
+0F4D TIBETAN LETTER DDHA
+0F4E TIBETAN LETTER NNA
+0F4F TIBETAN LETTER TA
+0F50 TIBETAN LETTER THA
+0F51 TIBETAN LETTER DA
+0F52 TIBETAN LETTER DHA
+0F53 TIBETAN LETTER NA
+0F54 TIBETAN LETTER PA
+0F55 TIBETAN LETTER PHA
+0F56 TIBETAN LETTER BA
+0F57 TIBETAN LETTER BHA
+0F58 TIBETAN LETTER MA
+0F59 TIBETAN LETTER TSA
+0F5A TIBETAN LETTER TSHA
+0F5B TIBETAN LETTER DZA
+0F5C TIBETAN LETTER DZHA
+0F5D TIBETAN LETTER WA
+0F5E TIBETAN LETTER ZHA
+0F5F TIBETAN LETTER ZA
+0F60 TIBETAN LETTER -A
+0F61 TIBETAN LETTER YA
+0F62 TIBETAN LETTER RA
+0F63 TIBETAN LETTER LA
+0F64 TIBETAN LETTER SHA
+0F65 TIBETAN LETTER SSA
+0F66 TIBETAN LETTER SA
+0F67 TIBETAN LETTER HA
+0F68 TIBETAN LETTER A
+0F69 TIBETAN LETTER KSSA
+0F71 TIBETAN VOWEL SIGN AA
+0F72 TIBETAN VOWEL SIGN I
+0F73 TIBETAN VOWEL SIGN II
+0F74 TIBETAN VOWEL SIGN U
+0F75 TIBETAN VOWEL SIGN UU
+0F76 TIBETAN VOWEL SIGN VOCALIC R
+0F77 TIBETAN VOWEL SIGN VOCALIC RR
+0F78 TIBETAN VOWEL SIGN VOCALIC L
+0F79 TIBETAN VOWEL SIGN VOCALIC LL
+0F7A TIBETAN VOWEL SIGN E
+0F7B TIBETAN VOWEL SIGN EE
+0F7C TIBETAN VOWEL SIGN O
+0F7D TIBETAN VOWEL SIGN OO
+0F7E TIBETAN SIGN RJES SU NGA RO
+0F7F TIBETAN SIGN RNAM BCAD
+0F80 TIBETAN VOWEL SIGN REVERSED I
+0F81 TIBETAN VOWEL SIGN REVERSED II
+0F82 TIBETAN SIGN NYI ZLA NAA DA
+0F83 TIBETAN SIGN SNA LDAN
+0F84 TIBETAN MARK HALANTA
+0F85 TIBETAN MARK PALUTA
+0F86 TIBETAN SIGN LCI RTAGS
+0F87 TIBETAN SIGN YANG RTAGS
+0F88 TIBETAN SIGN LCE TSA CAN
+0F89 TIBETAN SIGN MCHU CAN
+0F8A TIBETAN SIGN GRU CAN RGYINGS
+0F8B TIBETAN SIGN GRU MED RGYINGS
+0F90 TIBETAN SUBJOINED LETTER KA
+0F91 TIBETAN SUBJOINED LETTER KHA
+0F92 TIBETAN SUBJOINED LETTER GA
+0F93 TIBETAN SUBJOINED LETTER GHA
+0F94 TIBETAN SUBJOINED LETTER NGA
+0F95 TIBETAN SUBJOINED LETTER CA
+0F97 TIBETAN SUBJOINED LETTER JA
+0F99 TIBETAN SUBJOINED LETTER NYA
+0F9A TIBETAN SUBJOINED LETTER TTA
+0F9B TIBETAN SUBJOINED LETTER TTHA
+0F9C TIBETAN SUBJOINED LETTER DDA
+0F9D TIBETAN SUBJOINED LETTER DDHA
+0F9E TIBETAN SUBJOINED LETTER NNA
+0F9F TIBETAN SUBJOINED LETTER TA
+0FA0 TIBETAN SUBJOINED LETTER THA
+0FA1 TIBETAN SUBJOINED LETTER DA
+0FA2 TIBETAN SUBJOINED LETTER DHA
+0FA3 TIBETAN SUBJOINED LETTER NA
+0FA4 TIBETAN SUBJOINED LETTER PA
+0FA5 TIBETAN SUBJOINED LETTER PHA
+0FA6 TIBETAN SUBJOINED LETTER BA
+0FA7 TIBETAN SUBJOINED LETTER BHA
+0FA8 TIBETAN SUBJOINED LETTER MA
+0FA9 TIBETAN SUBJOINED LETTER TSA
+0FAA TIBETAN SUBJOINED LETTER TSHA
+0FAB TIBETAN SUBJOINED LETTER DZA
+0FAC TIBETAN SUBJOINED LETTER DZHA
+0FAD TIBETAN SUBJOINED LETTER WA
+0FB1 TIBETAN SUBJOINED LETTER YA
+0FB2 TIBETAN SUBJOINED LETTER RA
+0FB3 TIBETAN SUBJOINED LETTER LA
+0FB4 TIBETAN SUBJOINED LETTER SHA
+0FB5 TIBETAN SUBJOINED LETTER SSA
+0FB6 TIBETAN SUBJOINED LETTER SA
+0FB7 TIBETAN SUBJOINED LETTER HA
+0FB9 TIBETAN SUBJOINED LETTER KSSA
+10A0 GEORGIAN CAPITAL LETTER AN
+10A1 GEORGIAN CAPITAL LETTER BAN
+10A2 GEORGIAN CAPITAL LETTER GAN
+10A3 GEORGIAN CAPITAL LETTER DON
+10A4 GEORGIAN CAPITAL LETTER EN
+10A5 GEORGIAN CAPITAL LETTER VIN
+10A6 GEORGIAN CAPITAL LETTER ZEN
+10A7 GEORGIAN CAPITAL LETTER TAN
+10A8 GEORGIAN CAPITAL LETTER IN
+10A9 GEORGIAN CAPITAL LETTER KAN
+10AA GEORGIAN CAPITAL LETTER LAS
+10AB GEORGIAN CAPITAL LETTER MAN
+10AC GEORGIAN CAPITAL LETTER NAR
+10AD GEORGIAN CAPITAL LETTER ON
+10AE GEORGIAN CAPITAL LETTER PAR
+10AF GEORGIAN CAPITAL LETTER ZHAR
+10B0 GEORGIAN CAPITAL LETTER RAE
+10B1 GEORGIAN CAPITAL LETTER SAN
+10B2 GEORGIAN CAPITAL LETTER TAR
+10B3 GEORGIAN CAPITAL LETTER UN
+10B4 GEORGIAN CAPITAL LETTER PHAR
+10B5 GEORGIAN CAPITAL LETTER KHAR
+10B6 GEORGIAN CAPITAL LETTER GHAN
+10B7 GEORGIAN CAPITAL LETTER QAR
+10B8 GEORGIAN CAPITAL LETTER SHIN
+10B9 GEORGIAN CAPITAL LETTER CHIN
+10BA GEORGIAN CAPITAL LETTER CAN
+10BB GEORGIAN CAPITAL LETTER JIL
+10BC GEORGIAN CAPITAL LETTER CIL
+10BD GEORGIAN CAPITAL LETTER CHAR
+10BE GEORGIAN CAPITAL LETTER XAN
+10BF GEORGIAN CAPITAL LETTER JHAN
+10C0 GEORGIAN CAPITAL LETTER HAE
+10C1 GEORGIAN CAPITAL LETTER HE
+10C2 GEORGIAN CAPITAL LETTER HIE
+10C3 GEORGIAN CAPITAL LETTER WE
+10C4 GEORGIAN CAPITAL LETTER HAR
+10C5 GEORGIAN CAPITAL LETTER HOE
+10D0 GEORGIAN LETTER AN
+10D1 GEORGIAN LETTER BAN
+10D2 GEORGIAN LETTER GAN
+10D3 GEORGIAN LETTER DON
+10D4 GEORGIAN LETTER EN
+10D5 GEORGIAN LETTER VIN
+10D6 GEORGIAN LETTER ZEN
+10D7 GEORGIAN LETTER TAN
+10D8 GEORGIAN LETTER IN
+10D9 GEORGIAN LETTER KAN
+10DA GEORGIAN LETTER LAS
+10DB GEORGIAN LETTER MAN
+10DC GEORGIAN LETTER NAR
+10DD GEORGIAN LETTER ON
+10DE GEORGIAN LETTER PAR
+10DF GEORGIAN LETTER ZHAR
+10E0 GEORGIAN LETTER RAE
+10E1 GEORGIAN LETTER SAN
+10E2 GEORGIAN LETTER TAR
+10E3 GEORGIAN LETTER UN
+10E4 GEORGIAN LETTER PHAR
+10E5 GEORGIAN LETTER KHAR
+10E6 GEORGIAN LETTER GHAN
+10E7 GEORGIAN LETTER QAR
+10E8 GEORGIAN LETTER SHIN
+10E9 GEORGIAN LETTER CHIN
+10EA GEORGIAN LETTER CAN
+10EB GEORGIAN LETTER JIL
+10EC GEORGIAN LETTER CIL
+10ED GEORGIAN LETTER CHAR
+10EE GEORGIAN LETTER XAN
+10EF GEORGIAN LETTER JHAN
+10F0 GEORGIAN LETTER HAE
+10F1 GEORGIAN LETTER HE
+10F2 GEORGIAN LETTER HIE
+10F3 GEORGIAN LETTER WE
+10F4 GEORGIAN LETTER HAR
+10F5 GEORGIAN LETTER HOE
+10F6 GEORGIAN LETTER FI
+10FB GEORGIAN PARAGRAPH SEPARATOR
+1100 HANGUL CHOSEONG KIYEOK
+1101 HANGUL CHOSEONG SSANGKIYEOK
+1102 HANGUL CHOSEONG NIEUN
+1103 HANGUL CHOSEONG TIKEUT
+1104 HANGUL CHOSEONG SSANGTIKEUT
+1105 HANGUL CHOSEONG RIEUL
+1106 HANGUL CHOSEONG MIEUM
+1107 HANGUL CHOSEONG PIEUP
+1108 HANGUL CHOSEONG SSANGPIEUP
+1109 HANGUL CHOSEONG SIOS
+110A HANGUL CHOSEONG SSANGSIOS
+110B HANGUL CHOSEONG IEUNG
+110C HANGUL CHOSEONG CIEUC
+110D HANGUL CHOSEONG SSANGCIEUC
+110E HANGUL CHOSEONG CHIEUCH
+110F HANGUL CHOSEONG KHIEUKH
+1110 HANGUL CHOSEONG THIEUTH
+1111 HANGUL CHOSEONG PHIEUPH
+1112 HANGUL CHOSEONG HIEUH
+1113 HANGUL CHOSEONG NIEUN-KIYEOK
+1114 HANGUL CHOSEONG SSANGNIEUN
+1115 HANGUL CHOSEONG NIEUN-TIKEUT
+1116 HANGUL CHOSEONG NIEUN-PIEUP
+1117 HANGUL CHOSEONG TIKEUT-KIYEOK
+1118 HANGUL CHOSEONG RIEUL-NIEUN
+1119 HANGUL CHOSEONG SSANGRIEUL
+111A HANGUL CHOSEONG RIEUL-HIEUH
+111B HANGUL CHOSEONG KAPYEOUNRIEUL
+111C HANGUL CHOSEONG MIEUM-PIEUP
+111D HANGUL CHOSEONG KAPYEOUNMIEUM
+111E HANGUL CHOSEONG PIEUP-KIYEOK
+111F HANGUL CHOSEONG PIEUP-NIEUN
+1120 HANGUL CHOSEONG PIEUP-TIKEUT
+1121 HANGUL CHOSEONG PIEUP-SIOS
+1122 HANGUL CHOSEONG PIEUP-SIOS-KIYEOK
+1123 HANGUL CHOSEONG PIEUP-SIOS-TIKEUT
+1124 HANGUL CHOSEONG PIEUP-SIOS-PIEUP
+1125 HANGUL CHOSEONG PIEUP-SSANGSIOS
+1126 HANGUL CHOSEONG PIEUP-SIOS-CIEUC
+1127 HANGUL CHOSEONG PIEUP-CIEUC
+1128 HANGUL CHOSEONG PIEUP-CHIEUCH
+1129 HANGUL CHOSEONG PIEUP-THIEUTH
+112A HANGUL CHOSEONG PIEUP-PHIEUPH
+112B HANGUL CHOSEONG KAPYEOUNPIEUP
+112C HANGUL CHOSEONG KAPYEOUNSSANGPIEUP
+112D HANGUL CHOSEONG SIOS-KIYEOK
+112E HANGUL CHOSEONG SIOS-NIEUN
+112F HANGUL CHOSEONG SIOS-TIKEUT
+1130 HANGUL CHOSEONG SIOS-RIEUL
+1131 HANGUL CHOSEONG SIOS-MIEUM
+1132 HANGUL CHOSEONG SIOS-PIEUP
+1133 HANGUL CHOSEONG SIOS-PIEUP-KIYEOK
+1134 HANGUL CHOSEONG SIOS-SSANGSIOS
+1135 HANGUL CHOSEONG SIOS-IEUNG
+1136 HANGUL CHOSEONG SIOS-CIEUC
+1137 HANGUL CHOSEONG SIOS-CHIEUCH
+1138 HANGUL CHOSEONG SIOS-KHIEUKH
+1139 HANGUL CHOSEONG SIOS-THIEUTH
+113A HANGUL CHOSEONG SIOS-PHIEUPH
+113B HANGUL CHOSEONG SIOS-HIEUH
+113C HANGUL CHOSEONG CHITUEUMSIOS
+113D HANGUL CHOSEONG CHITUEUMSSANGSIOS
+113E HANGUL CHOSEONG CEONGCHIEUMSIOS
+113F HANGUL CHOSEONG CEONGCHIEUMSSANGSIOS
+1140 HANGUL CHOSEONG PANSIOS
+1141 HANGUL CHOSEONG IEUNG-KIYEOK
+1142 HANGUL CHOSEONG IEUNG-TIKEUT
+1143 HANGUL CHOSEONG IEUNG-MIEUM
+1144 HANGUL CHOSEONG IEUNG-PIEUP
+1145 HANGUL CHOSEONG IEUNG-SIOS
+1146 HANGUL CHOSEONG IEUNG-PANSIOS
+1147 HANGUL CHOSEONG SSANGIEUNG
+1148 HANGUL CHOSEONG IEUNG-CIEUC
+1149 HANGUL CHOSEONG IEUNG-CHIEUCH
+114A HANGUL CHOSEONG IEUNG-THIEUTH
+114B HANGUL CHOSEONG IEUNG-PHIEUPH
+114C HANGUL CHOSEONG YESIEUNG
+114D HANGUL CHOSEONG CIEUC-IEUNG
+114E HANGUL CHOSEONG CHITUEUMCIEUC
+114F HANGUL CHOSEONG CHITUEUMSSANGCIEUC
+1150 HANGUL CHOSEONG CEONGCHIEUMCIEUC
+1151 HANGUL CHOSEONG CEONGCHIEUMSSANGCIEUC
+1152 HANGUL CHOSEONG CHIEUCH-KHIEUKH
+1153 HANGUL CHOSEONG CHIEUCH-HIEUH
+1154 HANGUL CHOSEONG CHITUEUMCHIEUCH
+1155 HANGUL CHOSEONG CEONGCHIEUMCHIEUCH
+1156 HANGUL CHOSEONG PHIEUPH-PIEUP
+1157 HANGUL CHOSEONG KAPYEOUNPHIEUPH
+1158 HANGUL CHOSEONG SSANGHIEUH
+1159 HANGUL CHOSEONG YEORINHIEUH
+115F HANGUL CHOSEONG FILLER
+1160 HANGUL JUNGSEONG FILLER
+1161 HANGUL JUNGSEONG A
+1162 HANGUL JUNGSEONG AE
+1163 HANGUL JUNGSEONG YA
+1164 HANGUL JUNGSEONG YAE
+1165 HANGUL JUNGSEONG EO
+1166 HANGUL JUNGSEONG E
+1167 HANGUL JUNGSEONG YEO
+1168 HANGUL JUNGSEONG YE
+1169 HANGUL JUNGSEONG O
+116A HANGUL JUNGSEONG WA
+116B HANGUL JUNGSEONG WAE
+116C HANGUL JUNGSEONG OE
+116D HANGUL JUNGSEONG YO
+116E HANGUL JUNGSEONG U
+116F HANGUL JUNGSEONG WEO
+1170 HANGUL JUNGSEONG WE
+1171 HANGUL JUNGSEONG WI
+1172 HANGUL JUNGSEONG YU
+1173 HANGUL JUNGSEONG EU
+1174 HANGUL JUNGSEONG YI
+1175 HANGUL JUNGSEONG I
+1176 HANGUL JUNGSEONG A-O
+1177 HANGUL JUNGSEONG A-U
+1178 HANGUL JUNGSEONG YA-O
+1179 HANGUL JUNGSEONG YA-YO
+117A HANGUL JUNGSEONG EO-O
+117B HANGUL JUNGSEONG EO-U
+117C HANGUL JUNGSEONG EO-EU
+117D HANGUL JUNGSEONG YEO-O
+117E HANGUL JUNGSEONG YEO-U
+117F HANGUL JUNGSEONG O-EO
+1180 HANGUL JUNGSEONG O-E
+1181 HANGUL JUNGSEONG O-YE
+1182 HANGUL JUNGSEONG O-O
+1183 HANGUL JUNGSEONG O-U
+1184 HANGUL JUNGSEONG YO-YA
+1185 HANGUL JUNGSEONG YO-YAE
+1186 HANGUL JUNGSEONG YO-YEO
+1187 HANGUL JUNGSEONG YO-O
+1188 HANGUL JUNGSEONG YO-I
+1189 HANGUL JUNGSEONG U-A
+118A HANGUL JUNGSEONG U-AE
+118B HANGUL JUNGSEONG U-EO-EU
+118C HANGUL JUNGSEONG U-YE
+118D HANGUL JUNGSEONG U-U
+118E HANGUL JUNGSEONG YU-A
+118F HANGUL JUNGSEONG YU-EO
+1190 HANGUL JUNGSEONG YU-E
+1191 HANGUL JUNGSEONG YU-YEO
+1192 HANGUL JUNGSEONG YU-YE
+1193 HANGUL JUNGSEONG YU-U
+1194 HANGUL JUNGSEONG YU-I
+1195 HANGUL JUNGSEONG EU-U
+1196 HANGUL JUNGSEONG EU-EU
+1197 HANGUL JUNGSEONG YI-U
+1198 HANGUL JUNGSEONG I-A
+1199 HANGUL JUNGSEONG I-YA
+119A HANGUL JUNGSEONG I-O
+119B HANGUL JUNGSEONG I-U
+119C HANGUL JUNGSEONG I-EU
+119D HANGUL JUNGSEONG I-ARAEA
+119E HANGUL JUNGSEONG ARAEA
+119F HANGUL JUNGSEONG ARAEA-EO
+11A0 HANGUL JUNGSEONG ARAEA-U
+11A1 HANGUL JUNGSEONG ARAEA-I
+11A2 HANGUL JUNGSEONG SSANGARAEA
+11A8 HANGUL JONGSEONG KIYEOK
+11A9 HANGUL JONGSEONG SSANGKIYEOK
+11AA HANGUL JONGSEONG KIYEOK-SIOS
+11AB HANGUL JONGSEONG NIEUN
+11AC HANGUL JONGSEONG NIEUN-CIEUC
+11AD HANGUL JONGSEONG NIEUN-HIEUH
+11AE HANGUL JONGSEONG TIKEUT
+11AF HANGUL JONGSEONG RIEUL
+11B0 HANGUL JONGSEONG RIEUL-KIYEOK
+11B1 HANGUL JONGSEONG RIEUL-MIEUM
+11B2 HANGUL JONGSEONG RIEUL-PIEUP
+11B3 HANGUL JONGSEONG RIEUL-SIOS
+11B4 HANGUL JONGSEONG RIEUL-THIEUTH
+11B5 HANGUL JONGSEONG RIEUL-PHIEUPH
+11B6 HANGUL JONGSEONG RIEUL-HIEUH
+11B7 HANGUL JONGSEONG MIEUM
+11B8 HANGUL JONGSEONG PIEUP
+11B9 HANGUL JONGSEONG PIEUP-SIOS
+11BA HANGUL JONGSEONG SIOS
+11BB HANGUL JONGSEONG SSANGSIOS
+11BC HANGUL JONGSEONG IEUNG
+11BD HANGUL JONGSEONG CIEUC
+11BE HANGUL JONGSEONG CHIEUCH
+11BF HANGUL JONGSEONG KHIEUKH
+11C0 HANGUL JONGSEONG THIEUTH
+11C1 HANGUL JONGSEONG PHIEUPH
+11C2 HANGUL JONGSEONG HIEUH
+11C3 HANGUL JONGSEONG KIYEOK-RIEUL
+11C4 HANGUL JONGSEONG KIYEOK-SIOS-KIYEOK
+11C5 HANGUL JONGSEONG NIEUN-KIYEOK
+11C6 HANGUL JONGSEONG NIEUN-TIKEUT
+11C7 HANGUL JONGSEONG NIEUN-SIOS
+11C8 HANGUL JONGSEONG NIEUN-PANSIOS
+11C9 HANGUL JONGSEONG NIEUN-THIEUTH
+11CA HANGUL JONGSEONG TIKEUT-KIYEOK
+11CB HANGUL JONGSEONG TIKEUT-RIEUL
+11CC HANGUL JONGSEONG RIEUL-KIYEOK-SIOS
+11CD HANGUL JONGSEONG RIEUL-NIEUN
+11CE HANGUL JONGSEONG RIEUL-TIKEUT
+11CF HANGUL JONGSEONG RIEUL-TIKEUT-HIEUH
+11D0 HANGUL JONGSEONG SSANGRIEUL
+11D1 HANGUL JONGSEONG RIEUL-MIEUM-KIYEOK
+11D2 HANGUL JONGSEONG RIEUL-MIEUM-SIOS
+11D3 HANGUL JONGSEONG RIEUL-PIEUP-SIOS
+11D4 HANGUL JONGSEONG RIEUL-PIEUP-HIEUH
+11D5 HANGUL JONGSEONG RIEUL-KAPYEOUNPIEUP
+11D6 HANGUL JONGSEONG RIEUL-SSANGSIOS
+11D7 HANGUL JONGSEONG RIEUL-PANSIOS
+11D8 HANGUL JONGSEONG RIEUL-KHIEUKH
+11D9 HANGUL JONGSEONG RIEUL-YEORINHIEUH
+11DA HANGUL JONGSEONG MIEUM-KIYEOK
+11DB HANGUL JONGSEONG MIEUM-RIEUL
+11DC HANGUL JONGSEONG MIEUM-PIEUP
+11DD HANGUL JONGSEONG MIEUM-SIOS
+11DE HANGUL JONGSEONG MIEUM-SSANGSIOS
+11DF HANGUL JONGSEONG MIEUM-PANSIOS
+11E0 HANGUL JONGSEONG MIEUM-CHIEUCH
+11E1 HANGUL JONGSEONG MIEUM-HIEUH
+11E2 HANGUL JONGSEONG KAPYEOUNMIEUM
+11E3 HANGUL JONGSEONG PIEUP-RIEUL
+11E4 HANGUL JONGSEONG PIEUP-PHIEUPH
+11E5 HANGUL JONGSEONG PIEUP-HIEUH
+11E6 HANGUL JONGSEONG KAPYEOUNPIEUP
+11E7 HANGUL JONGSEONG SIOS-KIYEOK
+11E8 HANGUL JONGSEONG SIOS-TIKEUT
+11E9 HANGUL JONGSEONG SIOS-RIEUL
+11EA HANGUL JONGSEONG SIOS-PIEUP
+11EB HANGUL JONGSEONG PANSIOS
+11EC HANGUL JONGSEONG IEUNG-KIYEOK
+11ED HANGUL JONGSEONG IEUNG-SSANGKIYEOK
+11EE HANGUL JONGSEONG SSANGIEUNG
+11EF HANGUL JONGSEONG IEUNG-KHIEUKH
+11F0 HANGUL JONGSEONG YESIEUNG
+11F1 HANGUL JONGSEONG YESIEUNG-SIOS
+11F2 HANGUL JONGSEONG YESIEUNG-PANSIOS
+11F3 HANGUL JONGSEONG PHIEUPH-PIEUP
+11F4 HANGUL JONGSEONG KAPYEOUNPHIEUPH
+11F5 HANGUL JONGSEONG HIEUH-NIEUN
+11F6 HANGUL JONGSEONG HIEUH-RIEUL
+11F7 HANGUL JONGSEONG HIEUH-MIEUM
+11F8 HANGUL JONGSEONG HIEUH-PIEUP
+11F9 HANGUL JONGSEONG YEORINHIEUH
+1E00 LATIN CAPITAL LETTER A WITH RING BELOW
+1E01 LATIN SMALL LETTER A WITH RING BELOW
+1E02 LATIN CAPITAL LETTER B WITH DOT ABOVE
+1E03 LATIN SMALL LETTER B WITH DOT ABOVE
+1E04 LATIN CAPITAL LETTER B WITH DOT BELOW
+1E05 LATIN SMALL LETTER B WITH DOT BELOW
+1E06 LATIN CAPITAL LETTER B WITH LINE BELOW
+1E07 LATIN SMALL LETTER B WITH LINE BELOW
+1E08 LATIN CAPITAL LETTER C WITH CEDILLA AND ACUTE
+1E09 LATIN SMALL LETTER C WITH CEDILLA AND ACUTE
+1E0A LATIN CAPITAL LETTER D WITH DOT ABOVE
+1E0B LATIN SMALL LETTER D WITH DOT ABOVE
+1E0C LATIN CAPITAL LETTER D WITH DOT BELOW
+1E0D LATIN SMALL LETTER D WITH DOT BELOW
+1E0E LATIN CAPITAL LETTER D WITH LINE BELOW
+1E0F LATIN SMALL LETTER D WITH LINE BELOW
+1E10 LATIN CAPITAL LETTER D WITH CEDILLA
+1E11 LATIN SMALL LETTER D WITH CEDILLA
+1E12 LATIN CAPITAL LETTER D WITH CIRCUMFLEX BELOW
+1E13 LATIN SMALL LETTER D WITH CIRCUMFLEX BELOW
+1E14 LATIN CAPITAL LETTER E WITH MACRON AND GRAVE
+1E15 LATIN SMALL LETTER E WITH MACRON AND GRAVE
+1E16 LATIN CAPITAL LETTER E WITH MACRON AND ACUTE
+1E17 LATIN SMALL LETTER E WITH MACRON AND ACUTE
+1E18 LATIN CAPITAL LETTER E WITH CIRCUMFLEX BELOW
+1E19 LATIN SMALL LETTER E WITH CIRCUMFLEX BELOW
+1E1A LATIN CAPITAL LETTER E WITH TILDE BELOW
+1E1B LATIN SMALL LETTER E WITH TILDE BELOW
+1E1C LATIN CAPITAL LETTER E WITH CEDILLA AND BREVE
+1E1D LATIN SMALL LETTER E WITH CEDILLA AND BREVE
+1E1E LATIN CAPITAL LETTER F WITH DOT ABOVE
+1E1F LATIN SMALL LETTER F WITH DOT ABOVE
+1E20 LATIN CAPITAL LETTER G WITH MACRON
+1E21 LATIN SMALL LETTER G WITH MACRON
+1E22 LATIN CAPITAL LETTER H WITH DOT ABOVE
+1E23 LATIN SMALL LETTER H WITH DOT ABOVE
+1E24 LATIN CAPITAL LETTER H WITH DOT BELOW
+1E25 LATIN SMALL LETTER H WITH DOT BELOW
+1E26 LATIN CAPITAL LETTER H WITH DIAERESIS
+1E27 LATIN SMALL LETTER H WITH DIAERESIS
+1E28 LATIN CAPITAL LETTER H WITH CEDILLA
+1E29 LATIN SMALL LETTER H WITH CEDILLA
+1E2A LATIN CAPITAL LETTER H WITH BREVE BELOW
+1E2B LATIN SMALL LETTER H WITH BREVE BELOW
+1E2C LATIN CAPITAL LETTER I WITH TILDE BELOW
+1E2D LATIN SMALL LETTER I WITH TILDE BELOW
+1E2E LATIN CAPITAL LETTER I WITH DIAERESIS AND ACUTE
+1E2F LATIN SMALL LETTER I WITH DIAERESIS AND ACUTE
+1E30 LATIN CAPITAL LETTER K WITH ACUTE
+1E31 LATIN SMALL LETTER K WITH ACUTE
+1E32 LATIN CAPITAL LETTER K WITH DOT BELOW
+1E33 LATIN SMALL LETTER K WITH DOT BELOW
+1E34 LATIN CAPITAL LETTER K WITH LINE BELOW
+1E35 LATIN SMALL LETTER K WITH LINE BELOW
+1E36 LATIN CAPITAL LETTER L WITH DOT BELOW
+1E37 LATIN SMALL LETTER L WITH DOT BELOW
+1E38 LATIN CAPITAL LETTER L WITH DOT BELOW AND MACRON
+1E39 LATIN SMALL LETTER L WITH DOT BELOW AND MACRON
+1E3A LATIN CAPITAL LETTER L WITH LINE BELOW
+1E3B LATIN SMALL LETTER L WITH LINE BELOW
+1E3C LATIN CAPITAL LETTER L WITH CIRCUMFLEX BELOW
+1E3D LATIN SMALL LETTER L WITH CIRCUMFLEX BELOW
+1E3E LATIN CAPITAL LETTER M WITH ACUTE
+1E3F LATIN SMALL LETTER M WITH ACUTE
+1E40 LATIN CAPITAL LETTER M WITH DOT ABOVE
+1E41 LATIN SMALL LETTER M WITH DOT ABOVE
+1E42 LATIN CAPITAL LETTER M WITH DOT BELOW
+1E43 LATIN SMALL LETTER M WITH DOT BELOW
+1E44 LATIN CAPITAL LETTER N WITH DOT ABOVE
+1E45 LATIN SMALL LETTER N WITH DOT ABOVE
+1E46 LATIN CAPITAL LETTER N WITH DOT BELOW
+1E47 LATIN SMALL LETTER N WITH DOT BELOW
+1E48 LATIN CAPITAL LETTER N WITH LINE BELOW
+1E49 LATIN SMALL LETTER N WITH LINE BELOW
+1E4A LATIN CAPITAL LETTER N WITH CIRCUMFLEX BELOW
+1E4B LATIN SMALL LETTER N WITH CIRCUMFLEX BELOW
+1E4C LATIN CAPITAL LETTER O WITH TILDE AND ACUTE
+1E4D LATIN SMALL LETTER O WITH TILDE AND ACUTE
+1E4E LATIN CAPITAL LETTER O WITH TILDE AND DIAERESIS
+1E4F LATIN SMALL LETTER O WITH TILDE AND DIAERESIS
+1E50 LATIN CAPITAL LETTER O WITH MACRON AND GRAVE
+1E51 LATIN SMALL LETTER O WITH MACRON AND GRAVE
+1E52 LATIN CAPITAL LETTER O WITH MACRON AND ACUTE
+1E53 LATIN SMALL LETTER O WITH MACRON AND ACUTE
+1E54 LATIN CAPITAL LETTER P WITH ACUTE
+1E55 LATIN SMALL LETTER P WITH ACUTE
+1E56 LATIN CAPITAL LETTER P WITH DOT ABOVE
+1E57 LATIN SMALL LETTER P WITH DOT ABOVE
+1E58 LATIN CAPITAL LETTER R WITH DOT ABOVE
+1E59 LATIN SMALL LETTER R WITH DOT ABOVE
+1E5A LATIN CAPITAL LETTER R WITH DOT BELOW
+1E5B LATIN SMALL LETTER R WITH DOT BELOW
+1E5C LATIN CAPITAL LETTER R WITH DOT BELOW AND MACRON
+1E5D LATIN SMALL LETTER R WITH DOT BELOW AND MACRON
+1E5E LATIN CAPITAL LETTER R WITH LINE BELOW
+1E5F LATIN SMALL LETTER R WITH LINE BELOW
+1E60 LATIN CAPITAL LETTER S WITH DOT ABOVE
+1E61 LATIN SMALL LETTER S WITH DOT ABOVE
+1E62 LATIN CAPITAL LETTER S WITH DOT BELOW
+1E63 LATIN SMALL LETTER S WITH DOT BELOW
+1E64 LATIN CAPITAL LETTER S WITH ACUTE AND DOT ABOVE
+1E65 LATIN SMALL LETTER S WITH ACUTE AND DOT ABOVE
+1E66 LATIN CAPITAL LETTER S WITH CARON AND DOT ABOVE
+1E67 LATIN SMALL LETTER S WITH CARON AND DOT ABOVE
+1E68 LATIN CAPITAL LETTER S WITH DOT BELOW AND DOT ABOVE
+1E69 LATIN SMALL LETTER S WITH DOT BELOW AND DOT ABOVE
+1E6A LATIN CAPITAL LETTER T WITH DOT ABOVE
+1E6B LATIN SMALL LETTER T WITH DOT ABOVE
+1E6C LATIN CAPITAL LETTER T WITH DOT BELOW
+1E6D LATIN SMALL LETTER T WITH DOT BELOW
+1E6E LATIN CAPITAL LETTER T WITH LINE BELOW
+1E6F LATIN SMALL LETTER T WITH LINE BELOW
+1E70 LATIN CAPITAL LETTER T WITH CIRCUMFLEX BELOW
+1E71 LATIN SMALL LETTER T WITH CIRCUMFLEX BELOW
+1E72 LATIN CAPITAL LETTER U WITH DIAERESIS BELOW
+1E73 LATIN SMALL LETTER U WITH DIAERESIS BELOW
+1E74 LATIN CAPITAL LETTER U WITH TILDE BELOW
+1E75 LATIN SMALL LETTER U WITH TILDE BELOW
+1E76 LATIN CAPITAL LETTER U WITH CIRCUMFLEX BELOW
+1E77 LATIN SMALL LETTER U WITH CIRCUMFLEX BELOW
+1E78 LATIN CAPITAL LETTER U WITH TILDE AND ACUTE
+1E79 LATIN SMALL LETTER U WITH TILDE AND ACUTE
+1E7A LATIN CAPITAL LETTER U WITH MACRON AND DIAERESIS
+1E7B LATIN SMALL LETTER U WITH MACRON AND DIAERESIS
+1E7C LATIN CAPITAL LETTER V WITH TILDE
+1E7D LATIN SMALL LETTER V WITH TILDE
+1E7E LATIN CAPITAL LETTER V WITH DOT BELOW
+1E7F LATIN SMALL LETTER V WITH DOT BELOW
+1E80 LATIN CAPITAL LETTER W WITH GRAVE
+1E81 LATIN SMALL LETTER W WITH GRAVE
+1E82 LATIN CAPITAL LETTER W WITH ACUTE
+1E83 LATIN SMALL LETTER W WITH ACUTE
+1E84 LATIN CAPITAL LETTER W WITH DIAERESIS
+1E85 LATIN SMALL LETTER W WITH DIAERESIS
+1E86 LATIN CAPITAL LETTER W WITH DOT ABOVE
+1E87 LATIN SMALL LETTER W WITH DOT ABOVE
+1E88 LATIN CAPITAL LETTER W WITH DOT BELOW
+1E89 LATIN SMALL LETTER W WITH DOT BELOW
+1E8A LATIN CAPITAL LETTER X WITH DOT ABOVE
+1E8B LATIN SMALL LETTER X WITH DOT ABOVE
+1E8C LATIN CAPITAL LETTER X WITH DIAERESIS
+1E8D LATIN SMALL LETTER X WITH DIAERESIS
+1E8E LATIN CAPITAL LETTER Y WITH DOT ABOVE
+1E8F LATIN SMALL LETTER Y WITH DOT ABOVE
+1E90 LATIN CAPITAL LETTER Z WITH CIRCUMFLEX
+1E91 LATIN SMALL LETTER Z WITH CIRCUMFLEX
+1E92 LATIN CAPITAL LETTER Z WITH DOT BELOW
+1E93 LATIN SMALL LETTER Z WITH DOT BELOW
+1E94 LATIN CAPITAL LETTER Z WITH LINE BELOW
+1E95 LATIN SMALL LETTER Z WITH LINE BELOW
+1E96 LATIN SMALL LETTER H WITH LINE BELOW
+1E97 LATIN SMALL LETTER T WITH DIAERESIS
+1E98 LATIN SMALL LETTER W WITH RING ABOVE
+1E99 LATIN SMALL LETTER Y WITH RING ABOVE
+1E9A LATIN SMALL LETTER A WITH RIGHT HALF RING
+1E9B LATIN SMALL LETTER LONG S WITH DOT ABOVE
+1EA0 LATIN CAPITAL LETTER A WITH DOT BELOW
+1EA1 LATIN SMALL LETTER A WITH DOT BELOW
+1EA2 LATIN CAPITAL LETTER A WITH HOOK ABOVE
+1EA3 LATIN SMALL LETTER A WITH HOOK ABOVE
+1EA4 LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND ACUTE
+1EA5 LATIN SMALL LETTER A WITH CIRCUMFLEX AND ACUTE
+1EA6 LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND GRAVE
+1EA7 LATIN SMALL LETTER A WITH CIRCUMFLEX AND GRAVE
+1EA8 LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND HOOK ABOVE
+1EA9 LATIN SMALL LETTER A WITH CIRCUMFLEX AND HOOK ABOVE
+1EAA LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND TILDE
+1EAB LATIN SMALL LETTER A WITH CIRCUMFLEX AND TILDE
+1EAC LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND DOT BELOW
+1EAD LATIN SMALL LETTER A WITH CIRCUMFLEX AND DOT BELOW
+1EAE LATIN CAPITAL LETTER A WITH BREVE AND ACUTE
+1EAF LATIN SMALL LETTER A WITH BREVE AND ACUTE
+1EB0 LATIN CAPITAL LETTER A WITH BREVE AND GRAVE
+1EB1 LATIN SMALL LETTER A WITH BREVE AND GRAVE
+1EB2 LATIN CAPITAL LETTER A WITH BREVE AND HOOK ABOVE
+1EB3 LATIN SMALL LETTER A WITH BREVE AND HOOK ABOVE
+1EB4 LATIN CAPITAL LETTER A WITH BREVE AND TILDE
+1EB5 LATIN SMALL LETTER A WITH BREVE AND TILDE
+1EB6 LATIN CAPITAL LETTER A WITH BREVE AND DOT BELOW
+1EB7 LATIN SMALL LETTER A WITH BREVE AND DOT BELOW
+1EB8 LATIN CAPITAL LETTER E WITH DOT BELOW
+1EB9 LATIN SMALL LETTER E WITH DOT BELOW
+1EBA LATIN CAPITAL LETTER E WITH HOOK ABOVE
+1EBB LATIN SMALL LETTER E WITH HOOK ABOVE
+1EBC LATIN CAPITAL LETTER E WITH TILDE
+1EBD LATIN SMALL LETTER E WITH TILDE
+1EBE LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND ACUTE
+1EBF LATIN SMALL LETTER E WITH CIRCUMFLEX AND ACUTE
+1EC0 LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND GRAVE
+1EC1 LATIN SMALL LETTER E WITH CIRCUMFLEX AND GRAVE
+1EC2 LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND HOOK ABOVE
+1EC3 LATIN SMALL LETTER E WITH CIRCUMFLEX AND HOOK ABOVE
+1EC4 LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND TILDE
+1EC5 LATIN SMALL LETTER E WITH CIRCUMFLEX AND TILDE
+1EC6 LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND DOT BELOW
+1EC7 LATIN SMALL LETTER E WITH CIRCUMFLEX AND DOT BELOW
+1EC8 LATIN CAPITAL LETTER I WITH HOOK ABOVE
+1EC9 LATIN SMALL LETTER I WITH HOOK ABOVE
+1ECA LATIN CAPITAL LETTER I WITH DOT BELOW
+1ECB LATIN SMALL LETTER I WITH DOT BELOW
+1ECC LATIN CAPITAL LETTER O WITH DOT BELOW
+1ECD LATIN SMALL LETTER O WITH DOT BELOW
+1ECE LATIN CAPITAL LETTER O WITH HOOK ABOVE
+1ECF LATIN SMALL LETTER O WITH HOOK ABOVE
+1ED0 LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND ACUTE
+1ED1 LATIN SMALL LETTER O WITH CIRCUMFLEX AND ACUTE
+1ED2 LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND GRAVE
+1ED3 LATIN SMALL LETTER O WITH CIRCUMFLEX AND GRAVE
+1ED4 LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND HOOK ABOVE
+1ED5 LATIN SMALL LETTER O WITH CIRCUMFLEX AND HOOK ABOVE
+1ED6 LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND TILDE
+1ED7 LATIN SMALL LETTER O WITH CIRCUMFLEX AND TILDE
+1ED8 LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND DOT BELOW
+1ED9 LATIN SMALL LETTER O WITH CIRCUMFLEX AND DOT BELOW
+1EDA LATIN CAPITAL LETTER O WITH HORN AND ACUTE
+1EDB LATIN SMALL LETTER O WITH HORN AND ACUTE
+1EDC LATIN CAPITAL LETTER O WITH HORN AND GRAVE
+1EDD LATIN SMALL LETTER O WITH HORN AND GRAVE
+1EDE LATIN CAPITAL LETTER O WITH HORN AND HOOK ABOVE
+1EDF LATIN SMALL LETTER O WITH HORN AND HOOK ABOVE
+1EE0 LATIN CAPITAL LETTER O WITH HORN AND TILDE
+1EE1 LATIN SMALL LETTER O WITH HORN AND TILDE
+1EE2 LATIN CAPITAL LETTER O WITH HORN AND DOT BELOW
+1EE3 LATIN SMALL LETTER O WITH HORN AND DOT BELOW
+1EE4 LATIN CAPITAL LETTER U WITH DOT BELOW
+1EE5 LATIN SMALL LETTER U WITH DOT BELOW
+1EE6 LATIN CAPITAL LETTER U WITH HOOK ABOVE
+1EE7 LATIN SMALL LETTER U WITH HOOK ABOVE
+1EE8 LATIN CAPITAL LETTER U WITH HORN AND ACUTE
+1EE9 LATIN SMALL LETTER U WITH HORN AND ACUTE
+1EEA LATIN CAPITAL LETTER U WITH HORN AND GRAVE
+1EEB LATIN SMALL LETTER U WITH HORN AND GRAVE
+1EEC LATIN CAPITAL LETTER U WITH HORN AND HOOK ABOVE
+1EED LATIN SMALL LETTER U WITH HORN AND HOOK ABOVE
+1EEE LATIN CAPITAL LETTER U WITH HORN AND TILDE
+1EEF LATIN SMALL LETTER U WITH HORN AND TILDE
+1EF0 LATIN CAPITAL LETTER U WITH HORN AND DOT BELOW
+1EF1 LATIN SMALL LETTER U WITH HORN AND DOT BELOW
+1EF2 LATIN CAPITAL LETTER Y WITH GRAVE
+1EF3 LATIN SMALL LETTER Y WITH GRAVE
+1EF4 LATIN CAPITAL LETTER Y WITH DOT BELOW
+1EF5 LATIN SMALL LETTER Y WITH DOT BELOW
+1EF6 LATIN CAPITAL LETTER Y WITH HOOK ABOVE
+1EF7 LATIN SMALL LETTER Y WITH HOOK ABOVE
+1EF8 LATIN CAPITAL LETTER Y WITH TILDE
+1EF9 LATIN SMALL LETTER Y WITH TILDE
+1F00 GREEK SMALL LETTER ALPHA WITH PSILI
+1F01 GREEK SMALL LETTER ALPHA WITH DASIA
+1F02 GREEK SMALL LETTER ALPHA WITH PSILI AND VARIA
+1F03 GREEK SMALL LETTER ALPHA WITH DASIA AND VARIA
+1F04 GREEK SMALL LETTER ALPHA WITH PSILI AND OXIA
+1F05 GREEK SMALL LETTER ALPHA WITH DASIA AND OXIA
+1F06 GREEK SMALL LETTER ALPHA WITH PSILI AND PERISPOMENI
+1F07 GREEK SMALL LETTER ALPHA WITH DASIA AND PERISPOMENI
+1F08 GREEK CAPITAL LETTER ALPHA WITH PSILI
+1F09 GREEK CAPITAL LETTER ALPHA WITH DASIA
+1F0A GREEK CAPITAL LETTER ALPHA WITH PSILI AND VARIA
+1F0B GREEK CAPITAL LETTER ALPHA WITH DASIA AND VARIA
+1F0C GREEK CAPITAL LETTER ALPHA WITH PSILI AND OXIA
+1F0D GREEK CAPITAL LETTER ALPHA WITH DASIA AND OXIA
+1F0E GREEK CAPITAL LETTER ALPHA WITH PSILI AND PERISPOMENI
+1F0F GREEK CAPITAL LETTER ALPHA WITH DASIA AND PERISPOMENI
+1F10 GREEK SMALL LETTER EPSILON WITH PSILI
+1F11 GREEK SMALL LETTER EPSILON WITH DASIA
+1F12 GREEK SMALL LETTER EPSILON WITH PSILI AND VARIA
+1F13 GREEK SMALL LETTER EPSILON WITH DASIA AND VARIA
+1F14 GREEK SMALL LETTER EPSILON WITH PSILI AND OXIA
+1F15 GREEK SMALL LETTER EPSILON WITH DASIA AND OXIA
+1F18 GREEK CAPITAL LETTER EPSILON WITH PSILI
+1F19 GREEK CAPITAL LETTER EPSILON WITH DASIA
+1F1A GREEK CAPITAL LETTER EPSILON WITH PSILI AND VARIA
+1F1B GREEK CAPITAL LETTER EPSILON WITH DASIA AND VARIA
+1F1C GREEK CAPITAL LETTER EPSILON WITH PSILI AND OXIA
+1F1D GREEK CAPITAL LETTER EPSILON WITH DASIA AND OXIA
+1F20 GREEK SMALL LETTER ETA WITH PSILI
+1F21 GREEK SMALL LETTER ETA WITH DASIA
+1F22 GREEK SMALL LETTER ETA WITH PSILI AND VARIA
+1F23 GREEK SMALL LETTER ETA WITH DASIA AND VARIA
+1F24 GREEK SMALL LETTER ETA WITH PSILI AND OXIA
+1F25 GREEK SMALL LETTER ETA WITH DASIA AND OXIA
+1F26 GREEK SMALL LETTER ETA WITH PSILI AND PERISPOMENI
+1F27 GREEK SMALL LETTER ETA WITH DASIA AND PERISPOMENI
+1F28 GREEK CAPITAL LETTER ETA WITH PSILI
+1F29 GREEK CAPITAL LETTER ETA WITH DASIA
+1F2A GREEK CAPITAL LETTER ETA WITH PSILI AND VARIA
+1F2B GREEK CAPITAL LETTER ETA WITH DASIA AND VARIA
+1F2C GREEK CAPITAL LETTER ETA WITH PSILI AND OXIA
+1F2D GREEK CAPITAL LETTER ETA WITH DASIA AND OXIA
+1F2E GREEK CAPITAL LETTER ETA WITH PSILI AND PERISPOMENI
+1F2F GREEK CAPITAL LETTER ETA WITH DASIA AND PERISPOMENI
+1F30 GREEK SMALL LETTER IOTA WITH PSILI
+1F31 GREEK SMALL LETTER IOTA WITH DASIA
+1F32 GREEK SMALL LETTER IOTA WITH PSILI AND VARIA
+1F33 GREEK SMALL LETTER IOTA WITH DASIA AND VARIA
+1F34 GREEK SMALL LETTER IOTA WITH PSILI AND OXIA
+1F35 GREEK SMALL LETTER IOTA WITH DASIA AND OXIA
+1F36 GREEK SMALL LETTER IOTA WITH PSILI AND PERISPOMENI
+1F37 GREEK SMALL LETTER IOTA WITH DASIA AND PERISPOMENI
+1F38 GREEK CAPITAL LETTER IOTA WITH PSILI
+1F39 GREEK CAPITAL LETTER IOTA WITH DASIA
+1F3A GREEK CAPITAL LETTER IOTA WITH PSILI AND VARIA
+1F3B GREEK CAPITAL LETTER IOTA WITH DASIA AND VARIA
+1F3C GREEK CAPITAL LETTER IOTA WITH PSILI AND OXIA
+1F3D GREEK CAPITAL LETTER IOTA WITH DASIA AND OXIA
+1F3E GREEK CAPITAL LETTER IOTA WITH PSILI AND PERISPOMENI
+1F3F GREEK CAPITAL LETTER IOTA WITH DASIA AND PERISPOMENI
+1F40 GREEK SMALL LETTER OMICRON WITH PSILI
+1F41 GREEK SMALL LETTER OMICRON WITH DASIA
+1F42 GREEK SMALL LETTER OMICRON WITH PSILI AND VARIA
+1F43 GREEK SMALL LETTER OMICRON WITH DASIA AND VARIA
+1F44 GREEK SMALL LETTER OMICRON WITH PSILI AND OXIA
+1F45 GREEK SMALL LETTER OMICRON WITH DASIA AND OXIA
+1F48 GREEK CAPITAL LETTER OMICRON WITH PSILI
+1F49 GREEK CAPITAL LETTER OMICRON WITH DASIA
+1F4A GREEK CAPITAL LETTER OMICRON WITH PSILI AND VARIA
+1F4B GREEK CAPITAL LETTER OMICRON WITH DASIA AND VARIA
+1F4C GREEK CAPITAL LETTER OMICRON WITH PSILI AND OXIA
+1F4D GREEK CAPITAL LETTER OMICRON WITH DASIA AND OXIA
+1F50 GREEK SMALL LETTER UPSILON WITH PSILI
+1F51 GREEK SMALL LETTER UPSILON WITH DASIA
+1F52 GREEK SMALL LETTER UPSILON WITH PSILI AND VARIA
+1F53 GREEK SMALL LETTER UPSILON WITH DASIA AND VARIA
+1F54 GREEK SMALL LETTER UPSILON WITH PSILI AND OXIA
+1F55 GREEK SMALL LETTER UPSILON WITH DASIA AND OXIA
+1F56 GREEK SMALL LETTER UPSILON WITH PSILI AND PERISPOMENI
+1F57 GREEK SMALL LETTER UPSILON WITH DASIA AND PERISPOMENI
+1F59 GREEK CAPITAL LETTER UPSILON WITH DASIA
+1F5B GREEK CAPITAL LETTER UPSILON WITH DASIA AND VARIA
+1F5D GREEK CAPITAL LETTER UPSILON WITH DASIA AND OXIA
+1F5F GREEK CAPITAL LETTER UPSILON WITH DASIA AND PERISPOMENI
+1F60 GREEK SMALL LETTER OMEGA WITH PSILI
+1F61 GREEK SMALL LETTER OMEGA WITH DASIA
+1F62 GREEK SMALL LETTER OMEGA WITH PSILI AND VARIA
+1F63 GREEK SMALL LETTER OMEGA WITH DASIA AND VARIA
+1F64 GREEK SMALL LETTER OMEGA WITH PSILI AND OXIA
+1F65 GREEK SMALL LETTER OMEGA WITH DASIA AND OXIA
+1F66 GREEK SMALL LETTER OMEGA WITH PSILI AND PERISPOMENI
+1F67 GREEK SMALL LETTER OMEGA WITH DASIA AND PERISPOMENI
+1F68 GREEK CAPITAL LETTER OMEGA WITH PSILI
+1F69 GREEK CAPITAL LETTER OMEGA WITH DASIA
+1F6A GREEK CAPITAL LETTER OMEGA WITH PSILI AND VARIA
+1F6B GREEK CAPITAL LETTER OMEGA WITH DASIA AND VARIA
+1F6C GREEK CAPITAL LETTER OMEGA WITH PSILI AND OXIA
+1F6D GREEK CAPITAL LETTER OMEGA WITH DASIA AND OXIA
+1F6E GREEK CAPITAL LETTER OMEGA WITH PSILI AND PERISPOMENI
+1F6F GREEK CAPITAL LETTER OMEGA WITH DASIA AND PERISPOMENI
+1F70 GREEK SMALL LETTER ALPHA WITH VARIA
+1F71 GREEK SMALL LETTER ALPHA WITH OXIA
+1F72 GREEK SMALL LETTER EPSILON WITH VARIA
+1F73 GREEK SMALL LETTER EPSILON WITH OXIA
+1F74 GREEK SMALL LETTER ETA WITH VARIA
+1F75 GREEK SMALL LETTER ETA WITH OXIA
+1F76 GREEK SMALL LETTER IOTA WITH VARIA
+1F77 GREEK SMALL LETTER IOTA WITH OXIA
+1F78 GREEK SMALL LETTER OMICRON WITH VARIA
+1F79 GREEK SMALL LETTER OMICRON WITH OXIA
+1F7A GREEK SMALL LETTER UPSILON WITH VARIA
+1F7B GREEK SMALL LETTER UPSILON WITH OXIA
+1F7C GREEK SMALL LETTER OMEGA WITH VARIA
+1F7D GREEK SMALL LETTER OMEGA WITH OXIA
+1F80 GREEK SMALL LETTER ALPHA WITH PSILI AND YPOGEGRAMMENI
+1F81 GREEK SMALL LETTER ALPHA WITH DASIA AND YPOGEGRAMMENI
+1F82 GREEK SMALL LETTER ALPHA WITH PSILI AND VARIA AND YPOGEGRAMMENI
+1F83 GREEK SMALL LETTER ALPHA WITH DASIA AND VARIA AND YPOGEGRAMMENI
+1F84 GREEK SMALL LETTER ALPHA WITH PSILI AND OXIA AND YPOGEGRAMMENI
+1F85 GREEK SMALL LETTER ALPHA WITH DASIA AND OXIA AND YPOGEGRAMMENI
+1F86 GREEK SMALL LETTER ALPHA WITH PSILI AND PERISPOMENI AND YPOGEGRAMMENI
+1F87 GREEK SMALL LETTER ALPHA WITH DASIA AND PERISPOMENI AND YPOGEGRAMMENI
+1F88 GREEK CAPITAL LETTER ALPHA WITH PSILI AND PROSGEGRAMMENI
+1F89 GREEK CAPITAL LETTER ALPHA WITH DASIA AND PROSGEGRAMMENI
+1F8A GREEK CAPITAL LETTER ALPHA WITH PSILI AND VARIA AND PROSGEGRAMMENI
+1F8B GREEK CAPITAL LETTER ALPHA WITH DASIA AND VARIA AND PROSGEGRAMMENI
+1F8C GREEK CAPITAL LETTER ALPHA WITH PSILI AND OXIA AND PROSGEGRAMMENI
+1F8D GREEK CAPITAL LETTER ALPHA WITH DASIA AND OXIA AND PROSGEGRAMMENI
+1F8E GREEK CAPITAL LETTER ALPHA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI
+1F8F GREEK CAPITAL LETTER ALPHA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI
+1F90 GREEK SMALL LETTER ETA WITH PSILI AND YPOGEGRAMMENI
+1F91 GREEK SMALL LETTER ETA WITH DASIA AND YPOGEGRAMMENI
+1F92 GREEK SMALL LETTER ETA WITH PSILI AND VARIA AND YPOGEGRAMMENI
+1F93 GREEK SMALL LETTER ETA WITH DASIA AND VARIA AND YPOGEGRAMMENI
+1F94 GREEK SMALL LETTER ETA WITH PSILI AND OXIA AND YPOGEGRAMMENI
+1F95 GREEK SMALL LETTER ETA WITH DASIA AND OXIA AND YPOGEGRAMMENI
+1F96 GREEK SMALL LETTER ETA WITH PSILI AND PERISPOMENI AND YPOGEGRAMMENI
+1F97 GREEK SMALL LETTER ETA WITH DASIA AND PERISPOMENI AND YPOGEGRAMMENI
+1F98 GREEK CAPITAL LETTER ETA WITH PSILI AND PROSGEGRAMMENI
+1F99 GREEK CAPITAL LETTER ETA WITH DASIA AND PROSGEGRAMMENI
+1F9A GREEK CAPITAL LETTER ETA WITH PSILI AND VARIA AND PROSGEGRAMMENI
+1F9B GREEK CAPITAL LETTER ETA WITH DASIA AND VARIA AND PROSGEGRAMMENI
+1F9C GREEK CAPITAL LETTER ETA WITH PSILI AND OXIA AND PROSGEGRAMMENI
+1F9D GREEK CAPITAL LETTER ETA WITH DASIA AND OXIA AND PROSGEGRAMMENI
+1F9E GREEK CAPITAL LETTER ETA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI
+1F9F GREEK CAPITAL LETTER ETA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI
+1FA0 GREEK SMALL LETTER OMEGA WITH PSILI AND YPOGEGRAMMENI
+1FA1 GREEK SMALL LETTER OMEGA WITH DASIA AND YPOGEGRAMMENI
+1FA2 GREEK SMALL LETTER OMEGA WITH PSILI AND VARIA AND YPOGEGRAMMENI
+1FA3 GREEK SMALL LETTER OMEGA WITH DASIA AND VARIA AND YPOGEGRAMMENI
+1FA4 GREEK SMALL LETTER OMEGA WITH PSILI AND OXIA AND YPOGEGRAMMENI
+1FA5 GREEK SMALL LETTER OMEGA WITH DASIA AND OXIA AND YPOGEGRAMMENI
+1FA6 GREEK SMALL LETTER OMEGA WITH PSILI AND PERISPOMENI AND YPOGEGRAMMENI
+1FA7 GREEK SMALL LETTER OMEGA WITH DASIA AND PERISPOMENI AND YPOGEGRAMMENI
+1FA8 GREEK CAPITAL LETTER OMEGA WITH PSILI AND PROSGEGRAMMENI
+1FA9 GREEK CAPITAL LETTER OMEGA WITH DASIA AND PROSGEGRAMMENI
+1FAA GREEK CAPITAL LETTER OMEGA WITH PSILI AND VARIA AND PROSGEGRAMMENI
+1FAB GREEK CAPITAL LETTER OMEGA WITH DASIA AND VARIA AND PROSGEGRAMMENI
+1FAC GREEK CAPITAL LETTER OMEGA WITH PSILI AND OXIA AND PROSGEGRAMMENI
+1FAD GREEK CAPITAL LETTER OMEGA WITH DASIA AND OXIA AND PROSGEGRAMMENI
+1FAE GREEK CAPITAL LETTER OMEGA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI
+1FAF GREEK CAPITAL LETTER OMEGA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI
+1FB0 GREEK SMALL LETTER ALPHA WITH VRACHY
+1FB1 GREEK SMALL LETTER ALPHA WITH MACRON
+1FB2 GREEK SMALL LETTER ALPHA WITH VARIA AND YPOGEGRAMMENI
+1FB3 GREEK SMALL LETTER ALPHA WITH YPOGEGRAMMENI
+1FB4 GREEK SMALL LETTER ALPHA WITH OXIA AND YPOGEGRAMMENI
+1FB6 GREEK SMALL LETTER ALPHA WITH PERISPOMENI
+1FB7 GREEK SMALL LETTER ALPHA WITH PERISPOMENI AND YPOGEGRAMMENI
+1FB8 GREEK CAPITAL LETTER ALPHA WITH VRACHY
+1FB9 GREEK CAPITAL LETTER ALPHA WITH MACRON
+1FBA GREEK CAPITAL LETTER ALPHA WITH VARIA
+1FBB GREEK CAPITAL LETTER ALPHA WITH OXIA
+1FBC GREEK CAPITAL LETTER ALPHA WITH PROSGEGRAMMENI
+1FBD GREEK KORONIS
+1FBE GREEK PROSGEGRAMMENI
+1FBF GREEK PSILI
+1FC0 GREEK PERISPOMENI
+1FC1 GREEK DIALYTIKA AND PERISPOMENI
+1FC2 GREEK SMALL LETTER ETA WITH VARIA AND YPOGEGRAMMENI
+1FC3 GREEK SMALL LETTER ETA WITH YPOGEGRAMMENI
+1FC4 GREEK SMALL LETTER ETA WITH OXIA AND YPOGEGRAMMENI
+1FC6 GREEK SMALL LETTER ETA WITH PERISPOMENI
+1FC7 GREEK SMALL LETTER ETA WITH PERISPOMENI AND YPOGEGRAMMENI
+1FC8 GREEK CAPITAL LETTER EPSILON WITH VARIA
+1FC9 GREEK CAPITAL LETTER EPSILON WITH OXIA
+1FCA GREEK CAPITAL LETTER ETA WITH VARIA
+1FCB GREEK CAPITAL LETTER ETA WITH OXIA
+1FCC GREEK CAPITAL LETTER ETA WITH PROSGEGRAMMENI
+1FCD GREEK PSILI AND VARIA
+1FCE GREEK PSILI AND OXIA
+1FCF GREEK PSILI AND PERISPOMENI
+1FD0 GREEK SMALL LETTER IOTA WITH VRACHY
+1FD1 GREEK SMALL LETTER IOTA WITH MACRON
+1FD2 GREEK SMALL LETTER IOTA WITH DIALYTIKA AND VARIA
+1FD3 GREEK SMALL LETTER IOTA WITH DIALYTIKA AND OXIA
+1FD6 GREEK SMALL LETTER IOTA WITH PERISPOMENI
+1FD7 GREEK SMALL LETTER IOTA WITH DIALYTIKA AND PERISPOMENI
+1FD8 GREEK CAPITAL LETTER IOTA WITH VRACHY
+1FD9 GREEK CAPITAL LETTER IOTA WITH MACRON
+1FDA GREEK CAPITAL LETTER IOTA WITH VARIA
+1FDB GREEK CAPITAL LETTER IOTA WITH OXIA
+1FDD GREEK DASIA AND VARIA
+1FDE GREEK DASIA AND OXIA
+1FDF GREEK DASIA AND PERISPOMENI
+1FE0 GREEK SMALL LETTER UPSILON WITH VRACHY
+1FE1 GREEK SMALL LETTER UPSILON WITH MACRON
+1FE2 GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND VARIA
+1FE3 GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND OXIA
+1FE4 GREEK SMALL LETTER RHO WITH PSILI
+1FE5 GREEK SMALL LETTER RHO WITH DASIA
+1FE6 GREEK SMALL LETTER UPSILON WITH PERISPOMENI
+1FE7 GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND PERISPOMENI
+1FE8 GREEK CAPITAL LETTER UPSILON WITH VRACHY
+1FE9 GREEK CAPITAL LETTER UPSILON WITH MACRON
+1FEA GREEK CAPITAL LETTER UPSILON WITH VARIA
+1FEB GREEK CAPITAL LETTER UPSILON WITH OXIA
+1FEC GREEK CAPITAL LETTER RHO WITH DASIA
+1FED GREEK DIALYTIKA AND VARIA
+1FEE GREEK DIALYTIKA AND OXIA
+1FEF GREEK VARIA
+1FF2 GREEK SMALL LETTER OMEGA WITH VARIA AND YPOGEGRAMMENI
+1FF3 GREEK SMALL LETTER OMEGA WITH YPOGEGRAMMENI
+1FF4 GREEK SMALL LETTER OMEGA WITH OXIA AND YPOGEGRAMMENI
+1FF6 GREEK SMALL LETTER OMEGA WITH PERISPOMENI
+1FF7 GREEK SMALL LETTER OMEGA WITH PERISPOMENI AND YPOGEGRAMMENI
+1FF8 GREEK CAPITAL LETTER OMICRON WITH VARIA
+1FF9 GREEK CAPITAL LETTER OMICRON WITH OXIA
+1FFA GREEK CAPITAL LETTER OMEGA WITH VARIA
+1FFB GREEK CAPITAL LETTER OMEGA WITH OXIA
+1FFC GREEK CAPITAL LETTER OMEGA WITH PROSGEGRAMMENI
+1FFD GREEK OXIA
+1FFE GREEK DASIA
+2000 EN QUAD
+2001 EM QUAD
+2002 EN SPACE
+2003 EM SPACE
+2004 THREE-PER-EM SPACE
+2005 FOUR-PER-EM SPACE
+2006 SIX-PER-EM SPACE
+2007 FIGURE SPACE
+2008 PUNCTUATION SPACE
+2009 THIN SPACE
+200A HAIR SPACE
+200B ZERO WIDTH SPACE
+200C ZERO WIDTH NON-JOINER
+200D ZERO WIDTH JOINER
+200E LEFT-TO-RIGHT MARK
+200F RIGHT-TO-LEFT MARK
+2010 HYPHEN
+2011 NON-BREAKING HYPHEN
+2012 FIGURE DASH
+2013 EN DASH
+2014 EM DASH
+2015 HORIZONTAL BAR
+2016 DOUBLE VERTICAL LINE
+2017 DOUBLE LOW LINE
+2018 LEFT SINGLE QUOTATION MARK
+2019 RIGHT SINGLE QUOTATION MARK
+201A SINGLE LOW-9 QUOTATION MARK
+201B SINGLE HIGH-REVERSED-9 QUOTATION MARK
+201C LEFT DOUBLE QUOTATION MARK
+201D RIGHT DOUBLE QUOTATION MARK
+201E DOUBLE LOW-9 QUOTATION MARK
+201F DOUBLE HIGH-REVERSED-9 QUOTATION MARK
+2020 DAGGER
+2021 DOUBLE DAGGER
+2022 BULLET
+2023 TRIANGULAR BULLET
+2024 ONE DOT LEADER
+2025 TWO DOT LEADER
+2026 HORIZONTAL ELLIPSIS
+2027 HYPHENATION POINT
+2028 LINE SEPARATOR
+2029 PARAGRAPH SEPARATOR
+202A LEFT-TO-RIGHT EMBEDDING
+202B RIGHT-TO-LEFT EMBEDDING
+202C POP DIRECTIONAL FORMATTING
+202D LEFT-TO-RIGHT OVERRIDE
+202E RIGHT-TO-LEFT OVERRIDE
+2030 PER MILLE SIGN
+2031 PER TEN THOUSAND SIGN
+2032 PRIME
+2033 DOUBLE PRIME
+2034 TRIPLE PRIME
+2035 REVERSED PRIME
+2036 REVERSED DOUBLE PRIME
+2037 REVERSED TRIPLE PRIME
+2038 CARET
+2039 SINGLE LEFT-POINTING ANGLE QUOTATION MARK
+203A SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
+203B REFERENCE MARK
+203C DOUBLE EXCLAMATION MARK
+203D INTERROBANG
+203E OVERLINE
+203F UNDERTIE
+2040 CHARACTER TIE
+2041 CARET INSERTION POINT
+2042 ASTERISM
+2043 HYPHEN BULLET
+2044 FRACTION SLASH
+2045 LEFT SQUARE BRACKET WITH QUILL
+2046 RIGHT SQUARE BRACKET WITH QUILL
+206A INHIBIT SYMMETRIC SWAPPING
+206B ACTIVATE SYMMETRIC SWAPPING
+206C INHIBIT ARABIC FORM SHAPING
+206D ACTIVATE ARABIC FORM SHAPING
+206E NATIONAL DIGIT SHAPES
+206F NOMINAL DIGIT SHAPES
+2070 SUPERSCRIPT ZERO
+2074 SUPERSCRIPT FOUR
+2075 SUPERSCRIPT FIVE
+2076 SUPERSCRIPT SIX
+2077 SUPERSCRIPT SEVEN
+2078 SUPERSCRIPT EIGHT
+2079 SUPERSCRIPT NINE
+207A SUPERSCRIPT PLUS SIGN
+207B SUPERSCRIPT MINUS
+207C SUPERSCRIPT EQUALS SIGN
+207D SUPERSCRIPT LEFT PARENTHESIS
+207E SUPERSCRIPT RIGHT PARENTHESIS
+207F SUPERSCRIPT LATIN SMALL LETTER N
+2080 SUBSCRIPT ZERO
+2081 SUBSCRIPT ONE
+2082 SUBSCRIPT TWO
+2083 SUBSCRIPT THREE
+2084 SUBSCRIPT FOUR
+2085 SUBSCRIPT FIVE
+2086 SUBSCRIPT SIX
+2087 SUBSCRIPT SEVEN
+2088 SUBSCRIPT EIGHT
+2089 SUBSCRIPT NINE
+208A SUBSCRIPT PLUS SIGN
+208B SUBSCRIPT MINUS
+208C SUBSCRIPT EQUALS SIGN
+208D SUBSCRIPT LEFT PARENTHESIS
+208E SUBSCRIPT RIGHT PARENTHESIS
+20A0 EURO-CURRENCY SIGN
+20A1 COLON SIGN
+20A2 CRUZEIRO SIGN
+20A3 FRENCH FRANC SIGN
+20A4 LIRA SIGN
+20A5 MILL SIGN
+20A6 NAIRA SIGN
+20A7 PESETA SIGN
+20A8 RUPEE SIGN
+20A9 WON SIGN
+20AA NEW SHEQEL SIGN
+20AB DONG SIGN
+20AC EURO SIGN
+20D0 COMBINING LEFT HARPOON ABOVE
+20D1 COMBINING RIGHT HARPOON ABOVE
+20D2 COMBINING LONG VERTICAL LINE OVERLAY
+20D3 COMBINING SHORT VERTICAL LINE OVERLAY
+20D4 COMBINING ANTICLOCKWISE ARROW ABOVE
+20D5 COMBINING CLOCKWISE ARROW ABOVE
+20D6 COMBINING LEFT ARROW ABOVE
+20D7 COMBINING RIGHT ARROW ABOVE
+20D8 COMBINING RING OVERLAY
+20D9 COMBINING CLOCKWISE RING OVERLAY
+20DA COMBINING ANTICLOCKWISE RING OVERLAY
+20DB COMBINING THREE DOTS ABOVE
+20DC COMBINING FOUR DOTS ABOVE
+20DD COMBINING ENCLOSING CIRCLE
+20DE COMBINING ENCLOSING SQUARE
+20DF COMBINING ENCLOSING DIAMOND
+20E0 COMBINING ENCLOSING CIRCLE BACKSLASH
+20E1 COMBINING LEFT RIGHT ARROW ABOVE
+2100 ACCOUNT OF
+2101 ADDRESSED TO THE SUBJECT
+2102 DOUBLE-STRUCK CAPITAL C
+2103 DEGREE CELSIUS
+2104 CENTRE LINE SYMBOL
+2105 CARE OF
+2106 CADA UNA
+2107 EULER CONSTANT
+2108 SCRUPLE
+2109 DEGREE FAHRENHEIT
+210A SCRIPT SMALL G
+210B SCRIPT CAPITAL H
+210C BLACK-LETTER CAPITAL H
+210D DOUBLE-STRUCK CAPITAL H
+210E PLANCK CONSTANT
+210F PLANCK CONSTANT OVER TWO PI
+2110 SCRIPT CAPITAL I
+2111 BLACK-LETTER CAPITAL I
+2112 SCRIPT CAPITAL L
+2113 SCRIPT SMALL L
+2114 L B BAR SYMBOL
+2115 DOUBLE-STRUCK CAPITAL N
+2116 NUMERO SIGN
+2117 SOUND RECORDING COPYRIGHT
+2118 SCRIPT CAPITAL P
+2119 DOUBLE-STRUCK CAPITAL P
+211A DOUBLE-STRUCK CAPITAL Q
+211B SCRIPT CAPITAL R
+211C BLACK-LETTER CAPITAL R
+211D DOUBLE-STRUCK CAPITAL R
+211E PRESCRIPTION TAKE
+211F RESPONSE
+2120 SERVICE MARK
+2121 TELEPHONE SIGN
+2122 TRADE MARK SIGN
+2123 VERSICLE
+2124 DOUBLE-STRUCK CAPITAL Z
+2125 OUNCE SIGN
+2126 OHM SIGN
+2127 INVERTED OHM SIGN
+2128 BLACK-LETTER CAPITAL Z
+2129 TURNED GREEK SMALL LETTER IOTA
+212A KELVIN SIGN
+212B ANGSTROM SIGN
+212C SCRIPT CAPITAL B
+212D BLACK-LETTER CAPITAL C
+212E ESTIMATED SYMBOL
+212F SCRIPT SMALL E
+2130 SCRIPT CAPITAL E
+2131 SCRIPT CAPITAL F
+2132 TURNED CAPITAL F
+2133 SCRIPT CAPITAL M
+2134 SCRIPT SMALL O
+2135 ALEF SYMBOL
+2136 BET SYMBOL
+2137 GIMEL SYMBOL
+2138 DALET SYMBOL
+2153 VULGAR FRACTION ONE THIRD
+2154 VULGAR FRACTION TWO THIRDS
+2155 VULGAR FRACTION ONE FIFTH
+2156 VULGAR FRACTION TWO FIFTHS
+2157 VULGAR FRACTION THREE FIFTHS
+2158 VULGAR FRACTION FOUR FIFTHS
+2159 VULGAR FRACTION ONE SIXTH
+215A VULGAR FRACTION FIVE SIXTHS
+215B VULGAR FRACTION ONE EIGHTH
+215C VULGAR FRACTION THREE EIGHTHS
+215D VULGAR FRACTION FIVE EIGHTHS
+215E VULGAR FRACTION SEVEN EIGHTHS
+215F FRACTION NUMERATOR ONE
+2160 ROMAN NUMERAL ONE
+2161 ROMAN NUMERAL TWO
+2162 ROMAN NUMERAL THREE
+2163 ROMAN NUMERAL FOUR
+2164 ROMAN NUMERAL FIVE
+2165 ROMAN NUMERAL SIX
+2166 ROMAN NUMERAL SEVEN
+2167 ROMAN NUMERAL EIGHT
+2168 ROMAN NUMERAL NINE
+2169 ROMAN NUMERAL TEN
+216A ROMAN NUMERAL ELEVEN
+216B ROMAN NUMERAL TWELVE
+216C ROMAN NUMERAL FIFTY
+216D ROMAN NUMERAL ONE HUNDRED
+216E ROMAN NUMERAL FIVE HUNDRED
+216F ROMAN NUMERAL ONE THOUSAND
+2170 SMALL ROMAN NUMERAL ONE
+2171 SMALL ROMAN NUMERAL TWO
+2172 SMALL ROMAN NUMERAL THREE
+2173 SMALL ROMAN NUMERAL FOUR
+2174 SMALL ROMAN NUMERAL FIVE
+2175 SMALL ROMAN NUMERAL SIX
+2176 SMALL ROMAN NUMERAL SEVEN
+2177 SMALL ROMAN NUMERAL EIGHT
+2178 SMALL ROMAN NUMERAL NINE
+2179 SMALL ROMAN NUMERAL TEN
+217A SMALL ROMAN NUMERAL ELEVEN
+217B SMALL ROMAN NUMERAL TWELVE
+217C SMALL ROMAN NUMERAL FIFTY
+217D SMALL ROMAN NUMERAL ONE HUNDRED
+217E SMALL ROMAN NUMERAL FIVE HUNDRED
+217F SMALL ROMAN NUMERAL ONE THOUSAND
+2180 ROMAN NUMERAL ONE THOUSAND C D
+2181 ROMAN NUMERAL FIVE THOUSAND
+2182 ROMAN NUMERAL TEN THOUSAND
+2190 LEFTWARDS ARROW
+2191 UPWARDS ARROW
+2192 RIGHTWARDS ARROW
+2193 DOWNWARDS ARROW
+2194 LEFT RIGHT ARROW
+2195 UP DOWN ARROW
+2196 NORTH WEST ARROW
+2197 NORTH EAST ARROW
+2198 SOUTH EAST ARROW
+2199 SOUTH WEST ARROW
+219A LEFTWARDS ARROW WITH STROKE
+219B RIGHTWARDS ARROW WITH STROKE
+219C LEFTWARDS WAVE ARROW
+219D RIGHTWARDS WAVE ARROW
+219E LEFTWARDS TWO HEADED ARROW
+219F UPWARDS TWO HEADED ARROW
+21A0 RIGHTWARDS TWO HEADED ARROW
+21A1 DOWNWARDS TWO HEADED ARROW
+21A2 LEFTWARDS ARROW WITH TAIL
+21A3 RIGHTWARDS ARROW WITH TAIL
+21A4 LEFTWARDS ARROW FROM BAR
+21A5 UPWARDS ARROW FROM BAR
+21A6 RIGHTWARDS ARROW FROM BAR
+21A7 DOWNWARDS ARROW FROM BAR
+21A8 UP DOWN ARROW WITH BASE
+21A9 LEFTWARDS ARROW WITH HOOK
+21AA RIGHTWARDS ARROW WITH HOOK
+21AB LEFTWARDS ARROW WITH LOOP
+21AC RIGHTWARDS ARROW WITH LOOP
+21AD LEFT RIGHT WAVE ARROW
+21AE LEFT RIGHT ARROW WITH STROKE
+21AF DOWNWARDS ZIGZAG ARROW
+21B0 UPWARDS ARROW WITH TIP LEFTWARDS
+21B1 UPWARDS ARROW WITH TIP RIGHTWARDS
+21B2 DOWNWARDS ARROW WITH TIP LEFTWARDS
+21B3 DOWNWARDS ARROW WITH TIP RIGHTWARDS
+21B4 RIGHTWARDS ARROW WITH CORNER DOWNWARDS
+21B5 DOWNWARDS ARROW WITH CORNER LEFTWARDS
+21B6 ANTICLOCKWISE TOP SEMICIRCLE ARROW
+21B7 CLOCKWISE TOP SEMICIRCLE ARROW
+21B8 NORTH WEST ARROW TO LONG BAR
+21B9 LEFTWARDS ARROW TO BAR OVER RIGHTWARDS ARROW TO BAR
+21BA ANTICLOCKWISE OPEN CIRCLE ARROW
+21BB CLOCKWISE OPEN CIRCLE ARROW
+21BC LEFTWARDS HARPOON WITH BARB UPWARDS
+21BD LEFTWARDS HARPOON WITH BARB DOWNWARDS
+21BE UPWARDS HARPOON WITH BARB RIGHTWARDS
+21BF UPWARDS HARPOON WITH BARB LEFTWARDS
+21C0 RIGHTWARDS HARPOON WITH BARB UPWARDS
+21C1 RIGHTWARDS HARPOON WITH BARB DOWNWARDS
+21C2 DOWNWARDS HARPOON WITH BARB RIGHTWARDS
+21C3 DOWNWARDS HARPOON WITH BARB LEFTWARDS
+21C4 RIGHTWARDS ARROW OVER LEFTWARDS ARROW
+21C5 UPWARDS ARROW LEFTWARDS OF DOWNWARDS ARROW
+21C6 LEFTWARDS ARROW OVER RIGHTWARDS ARROW
+21C7 LEFTWARDS PAIRED ARROWS
+21C8 UPWARDS PAIRED ARROWS
+21C9 RIGHTWARDS PAIRED ARROWS
+21CA DOWNWARDS PAIRED ARROWS
+21CB LEFTWARDS HARPOON OVER RIGHTWARDS HARPOON
+21CC RIGHTWARDS HARPOON OVER LEFTWARDS HARPOON
+21CD LEFTWARDS DOUBLE ARROW WITH STROKE
+21CE LEFT RIGHT DOUBLE ARROW WITH STROKE
+21CF RIGHTWARDS DOUBLE ARROW WITH STROKE
+21D0 LEFTWARDS DOUBLE ARROW
+21D1 UPWARDS DOUBLE ARROW
+21D2 RIGHTWARDS DOUBLE ARROW
+21D3 DOWNWARDS DOUBLE ARROW
+21D4 LEFT RIGHT DOUBLE ARROW
+21D5 UP DOWN DOUBLE ARROW
+21D6 NORTH WEST DOUBLE ARROW
+21D7 NORTH EAST DOUBLE ARROW
+21D8 SOUTH EAST DOUBLE ARROW
+21D9 SOUTH WEST DOUBLE ARROW
+21DA LEFTWARDS TRIPLE ARROW
+21DB RIGHTWARDS TRIPLE ARROW
+21DC LEFTWARDS SQUIGGLE ARROW
+21DD RIGHTWARDS SQUIGGLE ARROW
+21DE UPWARDS ARROW WITH DOUBLE STROKE
+21DF DOWNWARDS ARROW WITH DOUBLE STROKE
+21E0 LEFTWARDS DASHED ARROW
+21E1 UPWARDS DASHED ARROW
+21E2 RIGHTWARDS DASHED ARROW
+21E3 DOWNWARDS DASHED ARROW
+21E4 LEFTWARDS ARROW TO BAR
+21E5 RIGHTWARDS ARROW TO BAR
+21E6 LEFTWARDS WHITE ARROW
+21E7 UPWARDS WHITE ARROW
+21E8 RIGHTWARDS WHITE ARROW
+21E9 DOWNWARDS WHITE ARROW
+21EA UPWARDS WHITE ARROW FROM BAR
+2200 FOR ALL
+2201 COMPLEMENT
+2202 PARTIAL DIFFERENTIAL
+2203 THERE EXISTS
+2204 THERE DOES NOT EXIST
+2205 EMPTY SET
+2206 INCREMENT
+2207 NABLA
+2208 ELEMENT OF
+2209 NOT AN ELEMENT OF
+220A SMALL ELEMENT OF
+220B CONTAINS AS MEMBER
+220C DOES NOT CONTAIN AS MEMBER
+220D SMALL CONTAINS AS MEMBER
+220E END OF PROOF
+220F N-ARY PRODUCT
+2210 N-ARY COPRODUCT
+2211 N-ARY SUMMATION
+2212 MINUS SIGN
+2213 MINUS-OR-PLUS SIGN
+2214 DOT PLUS
+2215 DIVISION SLASH
+2216 SET MINUS
+2217 ASTERISK OPERATOR
+2218 RING OPERATOR
+2219 BULLET OPERATOR
+221A SQUARE ROOT
+221B CUBE ROOT
+221C FOURTH ROOT
+221D PROPORTIONAL TO
+221E INFINITY
+221F RIGHT ANGLE
+2220 ANGLE
+2221 MEASURED ANGLE
+2222 SPHERICAL ANGLE
+2223 DIVIDES
+2224 DOES NOT DIVIDE
+2225 PARALLEL TO
+2226 NOT PARALLEL TO
+2227 LOGICAL AND
+2228 LOGICAL OR
+2229 INTERSECTION
+222A UNION
+222B INTEGRAL
+222C DOUBLE INTEGRAL
+222D TRIPLE INTEGRAL
+222E CONTOUR INTEGRAL
+222F SURFACE INTEGRAL
+2230 VOLUME INTEGRAL
+2231 CLOCKWISE INTEGRAL
+2232 CLOCKWISE CONTOUR INTEGRAL
+2233 ANTICLOCKWISE CONTOUR INTEGRAL
+2234 THEREFORE
+2235 BECAUSE
+2236 RATIO
+2237 PROPORTION
+2238 DOT MINUS
+2239 EXCESS
+223A GEOMETRIC PROPORTION
+223B HOMOTHETIC
+223C TILDE OPERATOR
+223D REVERSED TILDE
+223E INVERTED LAZY S
+223F SINE WAVE
+2240 WREATH PRODUCT
+2241 NOT TILDE
+2242 MINUS TILDE
+2243 ASYMPTOTICALLY EQUAL TO
+2244 NOT ASYMPTOTICALLY EQUAL TO
+2245 APPROXIMATELY EQUAL TO
+2246 APPROXIMATELY BUT NOT ACTUALLY EQUAL TO
+2247 NEITHER APPROXIMATELY NOR ACTUALLY EQUAL TO
+2248 ALMOST EQUAL TO
+2249 NOT ALMOST EQUAL TO
+224A ALMOST EQUAL OR EQUAL TO
+224B TRIPLE TILDE
+224C ALL EQUAL TO
+224D EQUIVALENT TO
+224E GEOMETRICALLY EQUIVALENT TO
+224F DIFFERENCE BETWEEN
+2250 APPROACHES THE LIMIT
+2251 GEOMETRICALLY EQUAL TO
+2252 APPROXIMATELY EQUAL TO OR THE IMAGE OF
+2253 IMAGE OF OR APPROXIMATELY EQUAL TO
+2254 COLON EQUALS
+2255 EQUALS COLON
+2256 RING IN EQUAL TO
+2257 RING EQUAL TO
+2258 CORRESPONDS TO
+2259 ESTIMATES
+225A EQUIANGULAR TO
+225B STAR EQUALS
+225C DELTA EQUAL TO
+225D EQUAL TO BY DEFINITION
+225E MEASURED BY
+225F QUESTIONED EQUAL TO
+2260 NOT EQUAL TO
+2261 IDENTICAL TO
+2262 NOT IDENTICAL TO
+2263 STRICTLY EQUIVALENT TO
+2264 LESS-THAN OR EQUAL TO
+2265 GREATER-THAN OR EQUAL TO
+2266 LESS-THAN OVER EQUAL TO
+2267 GREATER-THAN OVER EQUAL TO
+2268 LESS-THAN BUT NOT EQUAL TO
+2269 GREATER-THAN BUT NOT EQUAL TO
+226A MUCH LESS-THAN
+226B MUCH GREATER-THAN
+226C BETWEEN
+226D NOT EQUIVALENT TO
+226E NOT LESS-THAN
+226F NOT GREATER-THAN
+2270 NEITHER LESS-THAN NOR EQUAL TO
+2271 NEITHER GREATER-THAN NOR EQUAL TO
+2272 LESS-THAN OR EQUIVALENT TO
+2273 GREATER-THAN OR EQUIVALENT TO
+2274 NEITHER LESS-THAN NOR EQUIVALENT TO
+2275 NEITHER GREATER-THAN NOR EQUIVALENT TO
+2276 LESS-THAN OR GREATER-THAN
+2277 GREATER-THAN OR LESS-THAN
+2278 NEITHER LESS-THAN NOR GREATER-THAN
+2279 NEITHER GREATER-THAN NOR LESS-THAN
+227A PRECEDES
+227B SUCCEEDS
+227C PRECEDES OR EQUAL TO
+227D SUCCEEDS OR EQUAL TO
+227E PRECEDES OR EQUIVALENT TO
+227F SUCCEEDS OR EQUIVALENT TO
+2280 DOES NOT PRECEDE
+2281 DOES NOT SUCCEED
+2282 SUBSET OF
+2283 SUPERSET OF
+2284 NOT A SUBSET OF
+2285 NOT A SUPERSET OF
+2286 SUBSET OF OR EQUAL TO
+2287 SUPERSET OF OR EQUAL TO
+2288 NEITHER A SUBSET OF NOR EQUAL TO
+2289 NEITHER A SUPERSET OF NOR EQUAL TO
+228A SUBSET OF WITH NOT EQUAL TO
+228B SUPERSET OF WITH NOT EQUAL TO
+228C MULTISET
+228D MULTISET MULTIPLICATION
+228E MULTISET UNION
+228F SQUARE IMAGE OF
+2290 SQUARE ORIGINAL OF
+2291 SQUARE IMAGE OF OR EQUAL TO
+2292 SQUARE ORIGINAL OF OR EQUAL TO
+2293 SQUARE CAP
+2294 SQUARE CUP
+2295 CIRCLED PLUS
+2296 CIRCLED MINUS
+2297 CIRCLED TIMES
+2298 CIRCLED DIVISION SLASH
+2299 CIRCLED DOT OPERATOR
+229A CIRCLED RING OPERATOR
+229B CIRCLED ASTERISK OPERATOR
+229C CIRCLED EQUALS
+229D CIRCLED DASH
+229E SQUARED PLUS
+229F SQUARED MINUS
+22A0 SQUARED TIMES
+22A1 SQUARED DOT OPERATOR
+22A2 RIGHT TACK
+22A3 LEFT TACK
+22A4 DOWN TACK
+22A5 UP TACK
+22A6 ASSERTION
+22A7 MODELS
+22A8 TRUE
+22A9 FORCES
+22AA TRIPLE VERTICAL BAR RIGHT TURNSTILE
+22AB DOUBLE VERTICAL BAR DOUBLE RIGHT TURNSTILE
+22AC DOES NOT PROVE
+22AD NOT TRUE
+22AE DOES NOT FORCE
+22AF NEGATED DOUBLE VERTICAL BAR DOUBLE RIGHT TURNSTILE
+22B0 PRECEDES UNDER RELATION
+22B1 SUCCEEDS UNDER RELATION
+22B2 NORMAL SUBGROUP OF
+22B3 CONTAINS AS NORMAL SUBGROUP
+22B4 NORMAL SUBGROUP OF OR EQUAL TO
+22B5 CONTAINS AS NORMAL SUBGROUP OR EQUAL TO
+22B6 ORIGINAL OF
+22B7 IMAGE OF
+22B8 MULTIMAP
+22B9 HERMITIAN CONJUGATE MATRIX
+22BA INTERCALATE
+22BB XOR
+22BC NAND
+22BD NOR
+22BE RIGHT ANGLE WITH ARC
+22BF RIGHT TRIANGLE
+22C0 N-ARY LOGICAL AND
+22C1 N-ARY LOGICAL OR
+22C2 N-ARY INTERSECTION
+22C3 N-ARY UNION
+22C4 DIAMOND OPERATOR
+22C5 DOT OPERATOR
+22C6 STAR OPERATOR
+22C7 DIVISION TIMES
+22C8 BOWTIE
+22C9 LEFT NORMAL FACTOR SEMIDIRECT PRODUCT
+22CA RIGHT NORMAL FACTOR SEMIDIRECT PRODUCT
+22CB LEFT SEMIDIRECT PRODUCT
+22CC RIGHT SEMIDIRECT PRODUCT
+22CD REVERSED TILDE EQUALS
+22CE CURLY LOGICAL OR
+22CF CURLY LOGICAL AND
+22D0 DOUBLE SUBSET
+22D1 DOUBLE SUPERSET
+22D2 DOUBLE INTERSECTION
+22D3 DOUBLE UNION
+22D4 PITCHFORK
+22D5 EQUAL AND PARALLEL TO
+22D6 LESS-THAN WITH DOT
+22D7 GREATER-THAN WITH DOT
+22D8 VERY MUCH LESS-THAN
+22D9 VERY MUCH GREATER-THAN
+22DA LESS-THAN EQUAL TO OR GREATER-THAN
+22DB GREATER-THAN EQUAL TO OR LESS-THAN
+22DC EQUAL TO OR LESS-THAN
+22DD EQUAL TO OR GREATER-THAN
+22DE EQUAL TO OR PRECEDES
+22DF EQUAL TO OR SUCCEEDS
+22E0 DOES NOT PRECEDE OR EQUAL
+22E1 DOES NOT SUCCEED OR EQUAL
+22E2 NOT SQUARE IMAGE OF OR EQUAL TO
+22E3 NOT SQUARE ORIGINAL OF OR EQUAL TO
+22E4 SQUARE IMAGE OF OR NOT EQUAL TO
+22E5 SQUARE ORIGINAL OF OR NOT EQUAL TO
+22E6 LESS-THAN BUT NOT EQUIVALENT TO
+22E7 GREATER-THAN BUT NOT EQUIVALENT TO
+22E8 PRECEDES BUT NOT EQUIVALENT TO
+22E9 SUCCEEDS BUT NOT EQUIVALENT TO
+22EA NOT NORMAL SUBGROUP OF
+22EB DOES NOT CONTAIN AS NORMAL SUBGROUP
+22EC NOT NORMAL SUBGROUP OF OR EQUAL TO
+22ED DOES NOT CONTAIN AS NORMAL SUBGROUP OR EQUAL
+22EE VERTICAL ELLIPSIS
+22EF MIDLINE HORIZONTAL ELLIPSIS
+22F0 UP RIGHT DIAGONAL ELLIPSIS
+22F1 DOWN RIGHT DIAGONAL ELLIPSIS
+2300 DIAMETER SIGN
+2302 HOUSE
+2303 UP ARROWHEAD
+2304 DOWN ARROWHEAD
+2305 PROJECTIVE
+2306 PERSPECTIVE
+2307 WAVY LINE
+2308 LEFT CEILING
+2309 RIGHT CEILING
+230A LEFT FLOOR
+230B RIGHT FLOOR
+230C BOTTOM RIGHT CROP
+230D BOTTOM LEFT CROP
+230E TOP RIGHT CROP
+230F TOP LEFT CROP
+2310 REVERSED NOT SIGN
+2311 SQUARE LOZENGE
+2312 ARC
+2313 SEGMENT
+2314 SECTOR
+2315 TELEPHONE RECORDER
+2316 POSITION INDICATOR
+2317 VIEWDATA SQUARE
+2318 PLACE OF INTEREST SIGN
+2319 TURNED NOT SIGN
+231A WATCH
+231B HOURGLASS
+231C TOP LEFT CORNER
+231D TOP RIGHT CORNER
+231E BOTTOM LEFT CORNER
+231F BOTTOM RIGHT CORNER
+2320 TOP HALF INTEGRAL
+2321 BOTTOM HALF INTEGRAL
+2322 FROWN
+2323 SMILE
+2324 UP ARROWHEAD BETWEEN TWO HORIZONTAL BARS
+2325 OPTION KEY
+2326 ERASE TO THE RIGHT
+2327 X IN A RECTANGLE BOX
+2328 KEYBOARD
+2329 LEFT-POINTING ANGLE BRACKET
+232A RIGHT-POINTING ANGLE BRACKET
+232B ERASE TO THE LEFT
+232C BENZENE RING
+232D CYLINDRICITY
+232E ALL AROUND-PROFILE
+232F SYMMETRY
+2330 TOTAL RUNOUT
+2331 DIMENSION ORIGIN
+2332 CONICAL TAPER
+2333 SLOPE
+2334 COUNTERBORE
+2335 COUNTERSINK
+2336 APL FUNCTIONAL SYMBOL I-BEAM
+2337 APL FUNCTIONAL SYMBOL SQUISH QUAD
+2338 APL FUNCTIONAL SYMBOL QUAD EQUAL
+2339 APL FUNCTIONAL SYMBOL QUAD DIVIDE
+233A APL FUNCTIONAL SYMBOL QUAD DIAMOND
+233B APL FUNCTIONAL SYMBOL QUAD JOT
+233C APL FUNCTIONAL SYMBOL QUAD CIRCLE
+233D APL FUNCTIONAL SYMBOL CIRCLE STILE
+233E APL FUNCTIONAL SYMBOL CIRCLE JOT
+233F APL FUNCTIONAL SYMBOL SLASH BAR
+2340 APL FUNCTIONAL SYMBOL BACKSLASH BAR
+2341 APL FUNCTIONAL SYMBOL QUAD SLASH
+2342 APL FUNCTIONAL SYMBOL QUAD BACKSLASH
+2343 APL FUNCTIONAL SYMBOL QUAD LESS-THAN
+2344 APL FUNCTIONAL SYMBOL QUAD GREATER-THAN
+2345 APL FUNCTIONAL SYMBOL LEFTWARDS VANE
+2346 APL FUNCTIONAL SYMBOL RIGHTWARDS VANE
+2347 APL FUNCTIONAL SYMBOL QUAD LEFTWARDS ARROW
+2348 APL FUNCTIONAL SYMBOL QUAD RIGHTWARDS ARROW
+2349 APL FUNCTIONAL SYMBOL CIRCLE BACKSLASH
+234A APL FUNCTIONAL SYMBOL DOWN TACK UNDERBAR
+234B APL FUNCTIONAL SYMBOL DELTA STILE
+234C APL FUNCTIONAL SYMBOL QUAD DOWN CARET
+234D APL FUNCTIONAL SYMBOL QUAD DELTA
+234E APL FUNCTIONAL SYMBOL DOWN TACK JOT
+234F APL FUNCTIONAL SYMBOL UPWARDS VANE
+2350 APL FUNCTIONAL SYMBOL QUAD UPWARDS ARROW
+2351 APL FUNCTIONAL SYMBOL UP TACK OVERBAR
+2352 APL FUNCTIONAL SYMBOL DEL STILE
+2353 APL FUNCTIONAL SYMBOL QUAD UP CARET
+2354 APL FUNCTIONAL SYMBOL QUAD DEL
+2355 APL FUNCTIONAL SYMBOL UP TACK JOT
+2356 APL FUNCTIONAL SYMBOL DOWNWARDS VANE
+2357 APL FUNCTIONAL SYMBOL QUAD DOWNWARDS ARROW
+2358 APL FUNCTIONAL SYMBOL QUOTE UNDERBAR
+2359 APL FUNCTIONAL SYMBOL DELTA UNDERBAR
+235A APL FUNCTIONAL SYMBOL DIAMOND UNDERBAR
+235B APL FUNCTIONAL SYMBOL JOT UNDERBAR
+235C APL FUNCTIONAL SYMBOL CIRCLE UNDERBAR
+235D APL FUNCTIONAL SYMBOL UP SHOE JOT
+235E APL FUNCTIONAL SYMBOL QUOTE QUAD
+235F APL FUNCTIONAL SYMBOL CIRCLE STAR
+2360 APL FUNCTIONAL SYMBOL QUAD COLON
+2361 APL FUNCTIONAL SYMBOL UP TACK DIAERESIS
+2362 APL FUNCTIONAL SYMBOL DEL DIAERESIS
+2363 APL FUNCTIONAL SYMBOL STAR DIAERESIS
+2364 APL FUNCTIONAL SYMBOL JOT DIAERESIS
+2365 APL FUNCTIONAL SYMBOL CIRCLE DIAERESIS
+2366 APL FUNCTIONAL SYMBOL DOWN SHOE STILE
+2367 APL FUNCTIONAL SYMBOL LEFT SHOE STILE
+2368 APL FUNCTIONAL SYMBOL TILDE DIAERESIS
+2369 APL FUNCTIONAL SYMBOL GREATER-THAN DIAERESIS
+236A APL FUNCTIONAL SYMBOL COMMA BAR
+236B APL FUNCTIONAL SYMBOL DEL TILDE
+236C APL FUNCTIONAL SYMBOL ZILDE
+236D APL FUNCTIONAL SYMBOL STILE TILDE
+236E APL FUNCTIONAL SYMBOL SEMICOLON UNDERBAR
+236F APL FUNCTIONAL SYMBOL QUAD NOT EQUAL
+2370 APL FUNCTIONAL SYMBOL QUAD QUESTION
+2371 APL FUNCTIONAL SYMBOL DOWN CARET TILDE
+2372 APL FUNCTIONAL SYMBOL UP CARET TILDE
+2373 APL FUNCTIONAL SYMBOL IOTA
+2374 APL FUNCTIONAL SYMBOL RHO
+2375 APL FUNCTIONAL SYMBOL OMEGA
+2376 APL FUNCTIONAL SYMBOL ALPHA UNDERBAR
+2377 APL FUNCTIONAL SYMBOL EPSILON UNDERBAR
+2378 APL FUNCTIONAL SYMBOL IOTA UNDERBAR
+2379 APL FUNCTIONAL SYMBOL OMEGA UNDERBAR
+237A APL FUNCTIONAL SYMBOL ALPHA
+2400 SYMBOL FOR NULL
+2401 SYMBOL FOR START OF HEADING
+2402 SYMBOL FOR START OF TEXT
+2403 SYMBOL FOR END OF TEXT
+2404 SYMBOL FOR END OF TRANSMISSION
+2405 SYMBOL FOR ENQUIRY
+2406 SYMBOL FOR ACKNOWLEDGE
+2407 SYMBOL FOR BELL
+2408 SYMBOL FOR BACKSPACE
+2409 SYMBOL FOR HORIZONTAL TABULATION
+240A SYMBOL FOR LINE FEED
+240B SYMBOL FOR VERTICAL TABULATION
+240C SYMBOL FOR FORM FEED
+240D SYMBOL FOR CARRIAGE RETURN
+240E SYMBOL FOR SHIFT OUT
+240F SYMBOL FOR SHIFT IN
+2410 SYMBOL FOR DATA LINK ESCAPE
+2411 SYMBOL FOR DEVICE CONTROL ONE
+2412 SYMBOL FOR DEVICE CONTROL TWO
+2413 SYMBOL FOR DEVICE CONTROL THREE
+2414 SYMBOL FOR DEVICE CONTROL FOUR
+2415 SYMBOL FOR NEGATIVE ACKNOWLEDGE
+2416 SYMBOL FOR SYNCHRONOUS IDLE
+2417 SYMBOL FOR END OF TRANSMISSION BLOCK
+2418 SYMBOL FOR CANCEL
+2419 SYMBOL FOR END OF MEDIUM
+241A SYMBOL FOR SUBSTITUTE
+241B SYMBOL FOR ESCAPE
+241C SYMBOL FOR FILE SEPARATOR
+241D SYMBOL FOR GROUP SEPARATOR
+241E SYMBOL FOR RECORD SEPARATOR
+241F SYMBOL FOR UNIT SEPARATOR
+2420 SYMBOL FOR SPACE
+2421 SYMBOL FOR DELETE
+2422 BLANK SYMBOL
+2423 OPEN BOX
+2424 SYMBOL FOR NEWLINE
+2440 OCR HOOK
+2441 OCR CHAIR
+2442 OCR FORK
+2443 OCR INVERTED FORK
+2444 OCR BELT BUCKLE
+2445 OCR BOW TIE
+2446 OCR BRANCH BANK IDENTIFICATION
+2447 OCR AMOUNT OF CHECK
+2448 OCR DASH
+2449 OCR CUSTOMER ACCOUNT NUMBER
+244A OCR DOUBLE BACKSLASH
+2460 CIRCLED DIGIT ONE
+2461 CIRCLED DIGIT TWO
+2462 CIRCLED DIGIT THREE
+2463 CIRCLED DIGIT FOUR
+2464 CIRCLED DIGIT FIVE
+2465 CIRCLED DIGIT SIX
+2466 CIRCLED DIGIT SEVEN
+2467 CIRCLED DIGIT EIGHT
+2468 CIRCLED DIGIT NINE
+2469 CIRCLED NUMBER TEN
+246A CIRCLED NUMBER ELEVEN
+246B CIRCLED NUMBER TWELVE
+246C CIRCLED NUMBER THIRTEEN
+246D CIRCLED NUMBER FOURTEEN
+246E CIRCLED NUMBER FIFTEEN
+246F CIRCLED NUMBER SIXTEEN
+2470 CIRCLED NUMBER SEVENTEEN
+2471 CIRCLED NUMBER EIGHTEEN
+2472 CIRCLED NUMBER NINETEEN
+2473 CIRCLED NUMBER TWENTY
+2474 PARENTHESIZED DIGIT ONE
+2475 PARENTHESIZED DIGIT TWO
+2476 PARENTHESIZED DIGIT THREE
+2477 PARENTHESIZED DIGIT FOUR
+2478 PARENTHESIZED DIGIT FIVE
+2479 PARENTHESIZED DIGIT SIX
+247A PARENTHESIZED DIGIT SEVEN
+247B PARENTHESIZED DIGIT EIGHT
+247C PARENTHESIZED DIGIT NINE
+247D PARENTHESIZED NUMBER TEN
+247E PARENTHESIZED NUMBER ELEVEN
+247F PARENTHESIZED NUMBER TWELVE
+2480 PARENTHESIZED NUMBER THIRTEEN
+2481 PARENTHESIZED NUMBER FOURTEEN
+2482 PARENTHESIZED NUMBER FIFTEEN
+2483 PARENTHESIZED NUMBER SIXTEEN
+2484 PARENTHESIZED NUMBER SEVENTEEN
+2485 PARENTHESIZED NUMBER EIGHTEEN
+2486 PARENTHESIZED NUMBER NINETEEN
+2487 PARENTHESIZED NUMBER TWENTY
+2488 DIGIT ONE FULL STOP
+2489 DIGIT TWO FULL STOP
+248A DIGIT THREE FULL STOP
+248B DIGIT FOUR FULL STOP
+248C DIGIT FIVE FULL STOP
+248D DIGIT SIX FULL STOP
+248E DIGIT SEVEN FULL STOP
+248F DIGIT EIGHT FULL STOP
+2490 DIGIT NINE FULL STOP
+2491 NUMBER TEN FULL STOP
+2492 NUMBER ELEVEN FULL STOP
+2493 NUMBER TWELVE FULL STOP
+2494 NUMBER THIRTEEN FULL STOP
+2495 NUMBER FOURTEEN FULL STOP
+2496 NUMBER FIFTEEN FULL STOP
+2497 NUMBER SIXTEEN FULL STOP
+2498 NUMBER SEVENTEEN FULL STOP
+2499 NUMBER EIGHTEEN FULL STOP
+249A NUMBER NINETEEN FULL STOP
+249B NUMBER TWENTY FULL STOP
+249C PARENTHESIZED LATIN SMALL LETTER A
+249D PARENTHESIZED LATIN SMALL LETTER B
+249E PARENTHESIZED LATIN SMALL LETTER C
+249F PARENTHESIZED LATIN SMALL LETTER D
+24A0 PARENTHESIZED LATIN SMALL LETTER E
+24A1 PARENTHESIZED LATIN SMALL LETTER F
+24A2 PARENTHESIZED LATIN SMALL LETTER G
+24A3 PARENTHESIZED LATIN SMALL LETTER H
+24A4 PARENTHESIZED LATIN SMALL LETTER I
+24A5 PARENTHESIZED LATIN SMALL LETTER J
+24A6 PARENTHESIZED LATIN SMALL LETTER K
+24A7 PARENTHESIZED LATIN SMALL LETTER L
+24A8 PARENTHESIZED LATIN SMALL LETTER M
+24A9 PARENTHESIZED LATIN SMALL LETTER N
+24AA PARENTHESIZED LATIN SMALL LETTER O
+24AB PARENTHESIZED LATIN SMALL LETTER P
+24AC PARENTHESIZED LATIN SMALL LETTER Q
+24AD PARENTHESIZED LATIN SMALL LETTER R
+24AE PARENTHESIZED LATIN SMALL LETTER S
+24AF PARENTHESIZED LATIN SMALL LETTER T
+24B0 PARENTHESIZED LATIN SMALL LETTER U
+24B1 PARENTHESIZED LATIN SMALL LETTER V
+24B2 PARENTHESIZED LATIN SMALL LETTER W
+24B3 PARENTHESIZED LATIN SMALL LETTER X
+24B4 PARENTHESIZED LATIN SMALL LETTER Y
+24B5 PARENTHESIZED LATIN SMALL LETTER Z
+24B6 CIRCLED LATIN CAPITAL LETTER A
+24B7 CIRCLED LATIN CAPITAL LETTER B
+24B8 CIRCLED LATIN CAPITAL LETTER C
+24B9 CIRCLED LATIN CAPITAL LETTER D
+24BA CIRCLED LATIN CAPITAL LETTER E
+24BB CIRCLED LATIN CAPITAL LETTER F
+24BC CIRCLED LATIN CAPITAL LETTER G
+24BD CIRCLED LATIN CAPITAL LETTER H
+24BE CIRCLED LATIN CAPITAL LETTER I
+24BF CIRCLED LATIN CAPITAL LETTER J
+24C0 CIRCLED LATIN CAPITAL LETTER K
+24C1 CIRCLED LATIN CAPITAL LETTER L
+24C2 CIRCLED LATIN CAPITAL LETTER M
+24C3 CIRCLED LATIN CAPITAL LETTER N
+24C4 CIRCLED LATIN CAPITAL LETTER O
+24C5 CIRCLED LATIN CAPITAL LETTER P
+24C6 CIRCLED LATIN CAPITAL LETTER Q
+24C7 CIRCLED LATIN CAPITAL LETTER R
+24C8 CIRCLED LATIN CAPITAL LETTER S
+24C9 CIRCLED LATIN CAPITAL LETTER T
+24CA CIRCLED LATIN CAPITAL LETTER U
+24CB CIRCLED LATIN CAPITAL LETTER V
+24CC CIRCLED LATIN CAPITAL LETTER W
+24CD CIRCLED LATIN CAPITAL LETTER X
+24CE CIRCLED LATIN CAPITAL LETTER Y
+24CF CIRCLED LATIN CAPITAL LETTER Z
+24D0 CIRCLED LATIN SMALL LETTER A
+24D1 CIRCLED LATIN SMALL LETTER B
+24D2 CIRCLED LATIN SMALL LETTER C
+24D3 CIRCLED LATIN SMALL LETTER D
+24D4 CIRCLED LATIN SMALL LETTER E
+24D5 CIRCLED LATIN SMALL LETTER F
+24D6 CIRCLED LATIN SMALL LETTER G
+24D7 CIRCLED LATIN SMALL LETTER H
+24D8 CIRCLED LATIN SMALL LETTER I
+24D9 CIRCLED LATIN SMALL LETTER J
+24DA CIRCLED LATIN SMALL LETTER K
+24DB CIRCLED LATIN SMALL LETTER L
+24DC CIRCLED LATIN SMALL LETTER M
+24DD CIRCLED LATIN SMALL LETTER N
+24DE CIRCLED LATIN SMALL LETTER O
+24DF CIRCLED LATIN SMALL LETTER P
+24E0 CIRCLED LATIN SMALL LETTER Q
+24E1 CIRCLED LATIN SMALL LETTER R
+24E2 CIRCLED LATIN SMALL LETTER S
+24E3 CIRCLED LATIN SMALL LETTER T
+24E4 CIRCLED LATIN SMALL LETTER U
+24E5 CIRCLED LATIN SMALL LETTER V
+24E6 CIRCLED LATIN SMALL LETTER W
+24E7 CIRCLED LATIN SMALL LETTER X
+24E8 CIRCLED LATIN SMALL LETTER Y
+24E9 CIRCLED LATIN SMALL LETTER Z
+24EA CIRCLED DIGIT ZERO
+2500 BOX DRAWINGS LIGHT HORIZONTAL
+2501 BOX DRAWINGS HEAVY HORIZONTAL
+2502 BOX DRAWINGS LIGHT VERTICAL
+2503 BOX DRAWINGS HEAVY VERTICAL
+2504 BOX DRAWINGS LIGHT TRIPLE DASH HORIZONTAL
+2505 BOX DRAWINGS HEAVY TRIPLE DASH HORIZONTAL
+2506 BOX DRAWINGS LIGHT TRIPLE DASH VERTICAL
+2507 BOX DRAWINGS HEAVY TRIPLE DASH VERTICAL
+2508 BOX DRAWINGS LIGHT QUADRUPLE DASH HORIZONTAL
+2509 BOX DRAWINGS HEAVY QUADRUPLE DASH HORIZONTAL
+250A BOX DRAWINGS LIGHT QUADRUPLE DASH VERTICAL
+250B BOX DRAWINGS HEAVY QUADRUPLE DASH VERTICAL
+250C BOX DRAWINGS LIGHT DOWN AND RIGHT
+250D BOX DRAWINGS DOWN LIGHT AND RIGHT HEAVY
+250E BOX DRAWINGS DOWN HEAVY AND RIGHT LIGHT
+250F BOX DRAWINGS HEAVY DOWN AND RIGHT
+2510 BOX DRAWINGS LIGHT DOWN AND LEFT
+2511 BOX DRAWINGS DOWN LIGHT AND LEFT HEAVY
+2512 BOX DRAWINGS DOWN HEAVY AND LEFT LIGHT
+2513 BOX DRAWINGS HEAVY DOWN AND LEFT
+2514 BOX DRAWINGS LIGHT UP AND RIGHT
+2515 BOX DRAWINGS UP LIGHT AND RIGHT HEAVY
+2516 BOX DRAWINGS UP HEAVY AND RIGHT LIGHT
+2517 BOX DRAWINGS HEAVY UP AND RIGHT
+2518 BOX DRAWINGS LIGHT UP AND LEFT
+2519 BOX DRAWINGS UP LIGHT AND LEFT HEAVY
+251A BOX DRAWINGS UP HEAVY AND LEFT LIGHT
+251B BOX DRAWINGS HEAVY UP AND LEFT
+251C BOX DRAWINGS LIGHT VERTICAL AND RIGHT
+251D BOX DRAWINGS VERTICAL LIGHT AND RIGHT HEAVY
+251E BOX DRAWINGS UP HEAVY AND RIGHT DOWN LIGHT
+251F BOX DRAWINGS DOWN HEAVY AND RIGHT UP LIGHT
+2520 BOX DRAWINGS VERTICAL HEAVY AND RIGHT LIGHT
+2521 BOX DRAWINGS DOWN LIGHT AND RIGHT UP HEAVY
+2522 BOX DRAWINGS UP LIGHT AND RIGHT DOWN HEAVY
+2523 BOX DRAWINGS HEAVY VERTICAL AND RIGHT
+2524 BOX DRAWINGS LIGHT VERTICAL AND LEFT
+2525 BOX DRAWINGS VERTICAL LIGHT AND LEFT HEAVY
+2526 BOX DRAWINGS UP HEAVY AND LEFT DOWN LIGHT
+2527 BOX DRAWINGS DOWN HEAVY AND LEFT UP LIGHT
+2528 BOX DRAWINGS VERTICAL HEAVY AND LEFT LIGHT
+2529 BOX DRAWINGS DOWN LIGHT AND LEFT UP HEAVY
+252A BOX DRAWINGS UP LIGHT AND LEFT DOWN HEAVY
+252B BOX DRAWINGS HEAVY VERTICAL AND LEFT
+252C BOX DRAWINGS LIGHT DOWN AND HORIZONTAL
+252D BOX DRAWINGS LEFT HEAVY AND RIGHT DOWN LIGHT
+252E BOX DRAWINGS RIGHT HEAVY AND LEFT DOWN LIGHT
+252F BOX DRAWINGS DOWN LIGHT AND HORIZONTAL HEAVY
+2530 BOX DRAWINGS DOWN HEAVY AND HORIZONTAL LIGHT
+2531 BOX DRAWINGS RIGHT LIGHT AND LEFT DOWN HEAVY
+2532 BOX DRAWINGS LEFT LIGHT AND RIGHT DOWN HEAVY
+2533 BOX DRAWINGS HEAVY DOWN AND HORIZONTAL
+2534 BOX DRAWINGS LIGHT UP AND HORIZONTAL
+2535 BOX DRAWINGS LEFT HEAVY AND RIGHT UP LIGHT
+2536 BOX DRAWINGS RIGHT HEAVY AND LEFT UP LIGHT
+2537 BOX DRAWINGS UP LIGHT AND HORIZONTAL HEAVY
+2538 BOX DRAWINGS UP HEAVY AND HORIZONTAL LIGHT
+2539 BOX DRAWINGS RIGHT LIGHT AND LEFT UP HEAVY
+253A BOX DRAWINGS LEFT LIGHT AND RIGHT UP HEAVY
+253B BOX DRAWINGS HEAVY UP AND HORIZONTAL
+253C BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL
+253D BOX DRAWINGS LEFT HEAVY AND RIGHT VERTICAL LIGHT
+253E BOX DRAWINGS RIGHT HEAVY AND LEFT VERTICAL LIGHT
+253F BOX DRAWINGS VERTICAL LIGHT AND HORIZONTAL HEAVY
+2540 BOX DRAWINGS UP HEAVY AND DOWN HORIZONTAL LIGHT
+2541 BOX DRAWINGS DOWN HEAVY AND UP HORIZONTAL LIGHT
+2542 BOX DRAWINGS VERTICAL HEAVY AND HORIZONTAL LIGHT
+2543 BOX DRAWINGS LEFT UP HEAVY AND RIGHT DOWN LIGHT
+2544 BOX DRAWINGS RIGHT UP HEAVY AND LEFT DOWN LIGHT
+2545 BOX DRAWINGS LEFT DOWN HEAVY AND RIGHT UP LIGHT
+2546 BOX DRAWINGS RIGHT DOWN HEAVY AND LEFT UP LIGHT
+2547 BOX DRAWINGS DOWN LIGHT AND UP HORIZONTAL HEAVY
+2548 BOX DRAWINGS UP LIGHT AND DOWN HORIZONTAL HEAVY
+2549 BOX DRAWINGS RIGHT LIGHT AND LEFT VERTICAL HEAVY
+254A BOX DRAWINGS LEFT LIGHT AND RIGHT VERTICAL HEAVY
+254B BOX DRAWINGS HEAVY VERTICAL AND HORIZONTAL
+254C BOX DRAWINGS LIGHT DOUBLE DASH HORIZONTAL
+254D BOX DRAWINGS HEAVY DOUBLE DASH HORIZONTAL
+254E BOX DRAWINGS LIGHT DOUBLE DASH VERTICAL
+254F BOX DRAWINGS HEAVY DOUBLE DASH VERTICAL
+2550 BOX DRAWINGS DOUBLE HORIZONTAL
+2551 BOX DRAWINGS DOUBLE VERTICAL
+2552 BOX DRAWINGS DOWN SINGLE AND RIGHT DOUBLE
+2553 BOX DRAWINGS DOWN DOUBLE AND RIGHT SINGLE
+2554 BOX DRAWINGS DOUBLE DOWN AND RIGHT
+2555 BOX DRAWINGS DOWN SINGLE AND LEFT DOUBLE
+2556 BOX DRAWINGS DOWN DOUBLE AND LEFT SINGLE
+2557 BOX DRAWINGS DOUBLE DOWN AND LEFT
+2558 BOX DRAWINGS UP SINGLE AND RIGHT DOUBLE
+2559 BOX DRAWINGS UP DOUBLE AND RIGHT SINGLE
+255A BOX DRAWINGS DOUBLE UP AND RIGHT
+255B BOX DRAWINGS UP SINGLE AND LEFT DOUBLE
+255C BOX DRAWINGS UP DOUBLE AND LEFT SINGLE
+255D BOX DRAWINGS DOUBLE UP AND LEFT
+255E BOX DRAWINGS VERTICAL SINGLE AND RIGHT DOUBLE
+255F BOX DRAWINGS VERTICAL DOUBLE AND RIGHT SINGLE
+2560 BOX DRAWINGS DOUBLE VERTICAL AND RIGHT
+2561 BOX DRAWINGS VERTICAL SINGLE AND LEFT DOUBLE
+2562 BOX DRAWINGS VERTICAL DOUBLE AND LEFT SINGLE
+2563 BOX DRAWINGS DOUBLE VERTICAL AND LEFT
+2564 BOX DRAWINGS DOWN SINGLE AND HORIZONTAL DOUBLE
+2565 BOX DRAWINGS DOWN DOUBLE AND HORIZONTAL SINGLE
+2566 BOX DRAWINGS DOUBLE DOWN AND HORIZONTAL
+2567 BOX DRAWINGS UP SINGLE AND HORIZONTAL DOUBLE
+2568 BOX DRAWINGS UP DOUBLE AND HORIZONTAL SINGLE
+2569 BOX DRAWINGS DOUBLE UP AND HORIZONTAL
+256A BOX DRAWINGS VERTICAL SINGLE AND HORIZONTAL DOUBLE
+256B BOX DRAWINGS VERTICAL DOUBLE AND HORIZONTAL SINGLE
+256C BOX DRAWINGS DOUBLE VERTICAL AND HORIZONTAL
+256D BOX DRAWINGS LIGHT ARC DOWN AND RIGHT
+256E BOX DRAWINGS LIGHT ARC DOWN AND LEFT
+256F BOX DRAWINGS LIGHT ARC UP AND LEFT
+2570 BOX DRAWINGS LIGHT ARC UP AND RIGHT
+2571 BOX DRAWINGS LIGHT DIAGONAL UPPER RIGHT TO LOWER LEFT
+2572 BOX DRAWINGS LIGHT DIAGONAL UPPER LEFT TO LOWER RIGHT
+2573 BOX DRAWINGS LIGHT DIAGONAL CROSS
+2574 BOX DRAWINGS LIGHT LEFT
+2575 BOX DRAWINGS LIGHT UP
+2576 BOX DRAWINGS LIGHT RIGHT
+2577 BOX DRAWINGS LIGHT DOWN
+2578 BOX DRAWINGS HEAVY LEFT
+2579 BOX DRAWINGS HEAVY UP
+257A BOX DRAWINGS HEAVY RIGHT
+257B BOX DRAWINGS HEAVY DOWN
+257C BOX DRAWINGS LIGHT LEFT AND HEAVY RIGHT
+257D BOX DRAWINGS LIGHT UP AND HEAVY DOWN
+257E BOX DRAWINGS HEAVY LEFT AND LIGHT RIGHT
+257F BOX DRAWINGS HEAVY UP AND LIGHT DOWN
+2580 UPPER HALF BLOCK
+2581 LOWER ONE EIGHTH BLOCK
+2582 LOWER ONE QUARTER BLOCK
+2583 LOWER THREE EIGHTHS BLOCK
+2584 LOWER HALF BLOCK
+2585 LOWER FIVE EIGHTHS BLOCK
+2586 LOWER THREE QUARTERS BLOCK
+2587 LOWER SEVEN EIGHTHS BLOCK
+2588 FULL BLOCK
+2589 LEFT SEVEN EIGHTHS BLOCK
+258A LEFT THREE QUARTERS BLOCK
+258B LEFT FIVE EIGHTHS BLOCK
+258C LEFT HALF BLOCK
+258D LEFT THREE EIGHTHS BLOCK
+258E LEFT ONE QUARTER BLOCK
+258F LEFT ONE EIGHTH BLOCK
+2590 RIGHT HALF BLOCK
+2591 LIGHT SHADE
+2592 MEDIUM SHADE
+2593 DARK SHADE
+2594 UPPER ONE EIGHTH BLOCK
+2595 RIGHT ONE EIGHTH BLOCK
+25A0 BLACK SQUARE
+25A1 WHITE SQUARE
+25A2 WHITE SQUARE WITH ROUNDED CORNERS
+25A3 WHITE SQUARE CONTAINING BLACK SMALL SQUARE
+25A4 SQUARE WITH HORIZONTAL FILL
+25A5 SQUARE WITH VERTICAL FILL
+25A6 SQUARE WITH ORTHOGONAL CROSSHATCH FILL
+25A7 SQUARE WITH UPPER LEFT TO LOWER RIGHT FILL
+25A8 SQUARE WITH UPPER RIGHT TO LOWER LEFT FILL
+25A9 SQUARE WITH DIAGONAL CROSSHATCH FILL
+25AA BLACK SMALL SQUARE
+25AB WHITE SMALL SQUARE
+25AC BLACK RECTANGLE
+25AD WHITE RECTANGLE
+25AE BLACK VERTICAL RECTANGLE
+25AF WHITE VERTICAL RECTANGLE
+25B0 BLACK PARALLELOGRAM
+25B1 WHITE PARALLELOGRAM
+25B2 BLACK UP-POINTING TRIANGLE
+25B3 WHITE UP-POINTING TRIANGLE
+25B4 BLACK UP-POINTING SMALL TRIANGLE
+25B5 WHITE UP-POINTING SMALL TRIANGLE
+25B6 BLACK RIGHT-POINTING TRIANGLE
+25B7 WHITE RIGHT-POINTING TRIANGLE
+25B8 BLACK RIGHT-POINTING SMALL TRIANGLE
+25B9 WHITE RIGHT-POINTING SMALL TRIANGLE
+25BA BLACK RIGHT-POINTING POINTER
+25BB WHITE RIGHT-POINTING POINTER
+25BC BLACK DOWN-POINTING TRIANGLE
+25BD WHITE DOWN-POINTING TRIANGLE
+25BE BLACK DOWN-POINTING SMALL TRIANGLE
+25BF WHITE DOWN-POINTING SMALL TRIANGLE
+25C0 BLACK LEFT-POINTING TRIANGLE
+25C1 WHITE LEFT-POINTING TRIANGLE
+25C2 BLACK LEFT-POINTING SMALL TRIANGLE
+25C3 WHITE LEFT-POINTING SMALL TRIANGLE
+25C4 BLACK LEFT-POINTING POINTER
+25C5 WHITE LEFT-POINTING POINTER
+25C6 BLACK DIAMOND
+25C7 WHITE DIAMOND
+25C8 WHITE DIAMOND CONTAINING BLACK SMALL DIAMOND
+25C9 FISHEYE
+25CA LOZENGE
+25CB WHITE CIRCLE
+25CC DOTTED CIRCLE
+25CD CIRCLE WITH VERTICAL FILL
+25CE BULLSEYE
+25CF BLACK CIRCLE
+25D0 CIRCLE WITH LEFT HALF BLACK
+25D1 CIRCLE WITH RIGHT HALF BLACK
+25D2 CIRCLE WITH LOWER HALF BLACK
+25D3 CIRCLE WITH UPPER HALF BLACK
+25D4 CIRCLE WITH UPPER RIGHT QUADRANT BLACK
+25D5 CIRCLE WITH ALL BUT UPPER LEFT QUADRANT BLACK
+25D6 LEFT HALF BLACK CIRCLE
+25D7 RIGHT HALF BLACK CIRCLE
+25D8 INVERSE BULLET
+25D9 INVERSE WHITE CIRCLE
+25DA UPPER HALF INVERSE WHITE CIRCLE
+25DB LOWER HALF INVERSE WHITE CIRCLE
+25DC UPPER LEFT QUADRANT CIRCULAR ARC
+25DD UPPER RIGHT QUADRANT CIRCULAR ARC
+25DE LOWER RIGHT QUADRANT CIRCULAR ARC
+25DF LOWER LEFT QUADRANT CIRCULAR ARC
+25E0 UPPER HALF CIRCLE
+25E1 LOWER HALF CIRCLE
+25E2 BLACK LOWER RIGHT TRIANGLE
+25E3 BLACK LOWER LEFT TRIANGLE
+25E4 BLACK UPPER LEFT TRIANGLE
+25E5 BLACK UPPER RIGHT TRIANGLE
+25E6 WHITE BULLET
+25E7 SQUARE WITH LEFT HALF BLACK
+25E8 SQUARE WITH RIGHT HALF BLACK
+25E9 SQUARE WITH UPPER LEFT DIAGONAL HALF BLACK
+25EA SQUARE WITH LOWER RIGHT DIAGONAL HALF BLACK
+25EB WHITE SQUARE WITH VERTICAL BISECTING LINE
+25EC WHITE UP-POINTING TRIANGLE WITH DOT
+25ED UP-POINTING TRIANGLE WITH LEFT HALF BLACK
+25EE UP-POINTING TRIANGLE WITH RIGHT HALF BLACK
+25EF LARGE CIRCLE
+2600 BLACK SUN WITH RAYS
+2601 CLOUD
+2602 UMBRELLA
+2603 SNOWMAN
+2604 COMET
+2605 BLACK STAR
+2606 WHITE STAR
+2607 LIGHTNING
+2608 THUNDERSTORM
+2609 SUN
+260A ASCENDING NODE
+260B DESCENDING NODE
+260C CONJUNCTION
+260D OPPOSITION
+260E BLACK TELEPHONE
+260F WHITE TELEPHONE
+2610 BALLOT BOX
+2611 BALLOT BOX WITH CHECK
+2612 BALLOT BOX WITH X
+2613 SALTIRE
+261A BLACK LEFT POINTING INDEX
+261B BLACK RIGHT POINTING INDEX
+261C WHITE LEFT POINTING INDEX
+261D WHITE UP POINTING INDEX
+261E WHITE RIGHT POINTING INDEX
+261F WHITE DOWN POINTING INDEX
+2620 SKULL AND CROSSBONES
+2621 CAUTION SIGN
+2622 RADIOACTIVE SIGN
+2623 BIOHAZARD SIGN
+2624 CADUCEUS
+2625 ANKH
+2626 ORTHODOX CROSS
+2627 CHI RHO
+2628 CROSS OF LORRAINE
+2629 CROSS OF JERUSALEM
+262A STAR AND CRESCENT
+262B FARSI SYMBOL
+262C ADI SHAKTI
+262D HAMMER AND SICKLE
+262E PEACE SYMBOL
+262F YIN YANG
+2630 TRIGRAM FOR HEAVEN
+2631 TRIGRAM FOR LAKE
+2632 TRIGRAM FOR FIRE
+2633 TRIGRAM FOR THUNDER
+2634 TRIGRAM FOR WIND
+2635 TRIGRAM FOR WATER
+2636 TRIGRAM FOR MOUNTAIN
+2637 TRIGRAM FOR EARTH
+2638 WHEEL OF DHARMA
+2639 WHITE FROWNING FACE
+263A WHITE SMILING FACE
+263B BLACK SMILING FACE
+263C WHITE SUN WITH RAYS
+263D FIRST QUARTER MOON
+263E LAST QUARTER MOON
+263F MERCURY
+2640 FEMALE SIGN
+2641 EARTH
+2642 MALE SIGN
+2643 JUPITER
+2644 SATURN
+2645 URANUS
+2646 NEPTUNE
+2647 PLUTO
+2648 ARIES
+2649 TAURUS
+264A GEMINI
+264B CANCER
+264C LEO
+264D VIRGO
+264E LIBRA
+264F SCORPIUS
+2650 SAGITTARIUS
+2651 CAPRICORN
+2652 AQUARIUS
+2653 PISCES
+2654 WHITE CHESS KING
+2655 WHITE CHESS QUEEN
+2656 WHITE CHESS ROOK
+2657 WHITE CHESS BISHOP
+2658 WHITE CHESS KNIGHT
+2659 WHITE CHESS PAWN
+265A BLACK CHESS KING
+265B BLACK CHESS QUEEN
+265C BLACK CHESS ROOK
+265D BLACK CHESS BISHOP
+265E BLACK CHESS KNIGHT
+265F BLACK CHESS PAWN
+2660 BLACK SPADE SUIT
+2661 WHITE HEART SUIT
+2662 WHITE DIAMOND SUIT
+2663 BLACK CLUB SUIT
+2664 WHITE SPADE SUIT
+2665 BLACK HEART SUIT
+2666 BLACK DIAMOND SUIT
+2667 WHITE CLUB SUIT
+2668 HOT SPRINGS
+2669 QUARTER NOTE
+266A EIGHTH NOTE
+266B BEAMED EIGHTH NOTES
+266C BEAMED SIXTEENTH NOTES
+266D MUSIC FLAT SIGN
+266E MUSIC NATURAL SIGN
+266F MUSIC SHARP SIGN
+2701 UPPER BLADE SCISSORS
+2702 BLACK SCISSORS
+2703 LOWER BLADE SCISSORS
+2704 WHITE SCISSORS
+2706 TELEPHONE LOCATION SIGN
+2707 TAPE DRIVE
+2708 AIRPLANE
+2709 ENVELOPE
+270C VICTORY HAND
+270D WRITING HAND
+270E LOWER RIGHT PENCIL
+270F PENCIL
+2710 UPPER RIGHT PENCIL
+2711 WHITE NIB
+2712 BLACK NIB
+2713 CHECK MARK
+2714 HEAVY CHECK MARK
+2715 MULTIPLICATION X
+2716 HEAVY MULTIPLICATION X
+2717 BALLOT X
+2718 HEAVY BALLOT X
+2719 OUTLINED GREEK CROSS
+271A HEAVY GREEK CROSS
+271B OPEN CENTRE CROSS
+271C HEAVY OPEN CENTRE CROSS
+271D LATIN CROSS
+271E SHADOWED WHITE LATIN CROSS
+271F OUTLINED LATIN CROSS
+2720 MALTESE CROSS
+2721 STAR OF DAVID
+2722 FOUR TEARDROP-SPOKED ASTERISK
+2723 FOUR BALLOON-SPOKED ASTERISK
+2724 HEAVY FOUR BALLOON-SPOKED ASTERISK
+2725 FOUR CLUB-SPOKED ASTERISK
+2726 BLACK FOUR POINTED STAR
+2727 WHITE FOUR POINTED STAR
+2729 STRESS OUTLINED WHITE STAR
+272A CIRCLED WHITE STAR
+272B OPEN CENTRE BLACK STAR
+272C BLACK CENTRE WHITE STAR
+272D OUTLINED BLACK STAR
+272E HEAVY OUTLINED BLACK STAR
+272F PINWHEEL STAR
+2730 SHADOWED WHITE STAR
+2731 HEAVY ASTERISK
+2732 OPEN CENTRE ASTERISK
+2733 EIGHT SPOKED ASTERISK
+2734 EIGHT POINTED BLACK STAR
+2735 EIGHT POINTED PINWHEEL STAR
+2736 SIX POINTED BLACK STAR
+2737 EIGHT POINTED RECTILINEAR BLACK STAR
+2738 HEAVY EIGHT POINTED RECTILINEAR BLACK STAR
+2739 TWELVE POINTED BLACK STAR
+273A SIXTEEN POINTED ASTERISK
+273B TEARDROP-SPOKED ASTERISK
+273C OPEN CENTRE TEARDROP-SPOKED ASTERISK
+273D HEAVY TEARDROP-SPOKED ASTERISK
+273E SIX PETALLED BLACK AND WHITE FLORETTE
+273F BLACK FLORETTE
+2740 WHITE FLORETTE
+2741 EIGHT PETALLED OUTLINED BLACK FLORETTE
+2742 CIRCLED OPEN CENTRE EIGHT POINTED STAR
+2743 HEAVY TEARDROP-SPOKED PINWHEEL ASTERISK
+2744 SNOWFLAKE
+2745 TIGHT TRIFOLIATE SNOWFLAKE
+2746 HEAVY CHEVRON SNOWFLAKE
+2747 SPARKLE
+2748 HEAVY SPARKLE
+2749 BALLOON-SPOKED ASTERISK
+274A EIGHT TEARDROP-SPOKED PROPELLER ASTERISK
+274B HEAVY EIGHT TEARDROP-SPOKED PROPELLER ASTERISK
+274D SHADOWED WHITE CIRCLE
+274F LOWER RIGHT DROP-SHADOWED WHITE SQUARE
+2750 UPPER RIGHT DROP-SHADOWED WHITE SQUARE
+2751 LOWER RIGHT SHADOWED WHITE SQUARE
+2752 UPPER RIGHT SHADOWED WHITE SQUARE
+2756 BLACK DIAMOND MINUS WHITE X
+2758 LIGHT VERTICAL BAR
+2759 MEDIUM VERTICAL BAR
+275A HEAVY VERTICAL BAR
+275B HEAVY SINGLE TURNED COMMA QUOTATION MARK ORNAMENT
+275C HEAVY SINGLE COMMA QUOTATION MARK ORNAMENT
+275D HEAVY DOUBLE TURNED COMMA QUOTATION MARK ORNAMENT
+275E HEAVY DOUBLE COMMA QUOTATION MARK ORNAMENT
+2761 CURVED STEM PARAGRAPH SIGN ORNAMENT
+2762 HEAVY EXCLAMATION MARK ORNAMENT
+2763 HEAVY HEART EXCLAMATION MARK ORNAMENT
+2764 HEAVY BLACK HEART
+2765 ROTATED HEAVY BLACK HEART BULLET
+2766 FLORAL HEART
+2767 ROTATED FLORAL HEART BULLET
+2776 DINGBAT NEGATIVE CIRCLED DIGIT ONE
+2777 DINGBAT NEGATIVE CIRCLED DIGIT TWO
+2778 DINGBAT NEGATIVE CIRCLED DIGIT THREE
+2779 DINGBAT NEGATIVE CIRCLED DIGIT FOUR
+277A DINGBAT NEGATIVE CIRCLED DIGIT FIVE
+277B DINGBAT NEGATIVE CIRCLED DIGIT SIX
+277C DINGBAT NEGATIVE CIRCLED DIGIT SEVEN
+277D DINGBAT NEGATIVE CIRCLED DIGIT EIGHT
+277E DINGBAT NEGATIVE CIRCLED DIGIT NINE
+277F DINGBAT NEGATIVE CIRCLED NUMBER TEN
+2780 DINGBAT CIRCLED SANS-SERIF DIGIT ONE
+2781 DINGBAT CIRCLED SANS-SERIF DIGIT TWO
+2782 DINGBAT CIRCLED SANS-SERIF DIGIT THREE
+2783 DINGBAT CIRCLED SANS-SERIF DIGIT FOUR
+2784 DINGBAT CIRCLED SANS-SERIF DIGIT FIVE
+2785 DINGBAT CIRCLED SANS-SERIF DIGIT SIX
+2786 DINGBAT CIRCLED SANS-SERIF DIGIT SEVEN
+2787 DINGBAT CIRCLED SANS-SERIF DIGIT EIGHT
+2788 DINGBAT CIRCLED SANS-SERIF DIGIT NINE
+2789 DINGBAT CIRCLED SANS-SERIF NUMBER TEN
+278A DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT ONE
+278B DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT TWO
+278C DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT THREE
+278D DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT FOUR
+278E DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT FIVE
+278F DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT SIX
+2790 DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT SEVEN
+2791 DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT EIGHT
+2792 DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT NINE
+2793 DINGBAT NEGATIVE CIRCLED SANS-SERIF NUMBER TEN
+2794 HEAVY WIDE-HEADED RIGHTWARDS ARROW
+2798 HEAVY SOUTH EAST ARROW
+2799 HEAVY RIGHTWARDS ARROW
+279A HEAVY NORTH EAST ARROW
+279B DRAFTING POINT RIGHTWARDS ARROW
+279C HEAVY ROUND-TIPPED RIGHTWARDS ARROW
+279D TRIANGLE-HEADED RIGHTWARDS ARROW
+279E HEAVY TRIANGLE-HEADED RIGHTWARDS ARROW
+279F DASHED TRIANGLE-HEADED RIGHTWARDS ARROW
+27A0 HEAVY DASHED TRIANGLE-HEADED RIGHTWARDS ARROW
+27A1 BLACK RIGHTWARDS ARROW
+27A2 THREE-D TOP-LIGHTED RIGHTWARDS ARROWHEAD
+27A3 THREE-D BOTTOM-LIGHTED RIGHTWARDS ARROWHEAD
+27A4 BLACK RIGHTWARDS ARROWHEAD
+27A5 HEAVY BLACK CURVED DOWNWARDS AND RIGHTWARDS ARROW
+27A6 HEAVY BLACK CURVED UPWARDS AND RIGHTWARDS ARROW
+27A7 SQUAT BLACK RIGHTWARDS ARROW
+27A8 HEAVY CONCAVE-POINTED BLACK RIGHTWARDS ARROW
+27A9 RIGHT-SHADED WHITE RIGHTWARDS ARROW
+27AA LEFT-SHADED WHITE RIGHTWARDS ARROW
+27AB BACK-TILTED SHADOWED WHITE RIGHTWARDS ARROW
+27AC FRONT-TILTED SHADOWED WHITE RIGHTWARDS ARROW
+27AD HEAVY LOWER RIGHT-SHADOWED WHITE RIGHTWARDS ARROW
+27AE HEAVY UPPER RIGHT-SHADOWED WHITE RIGHTWARDS ARROW
+27AF NOTCHED LOWER RIGHT-SHADOWED WHITE RIGHTWARDS ARROW
+27B1 NOTCHED UPPER RIGHT-SHADOWED WHITE RIGHTWARDS ARROW
+27B2 CIRCLED HEAVY WHITE RIGHTWARDS ARROW
+27B3 WHITE-FEATHERED RIGHTWARDS ARROW
+27B4 BLACK-FEATHERED SOUTH EAST ARROW
+27B5 BLACK-FEATHERED RIGHTWARDS ARROW
+27B6 BLACK-FEATHERED NORTH EAST ARROW
+27B7 HEAVY BLACK-FEATHERED SOUTH EAST ARROW
+27B8 HEAVY BLACK-FEATHERED RIGHTWARDS ARROW
+27B9 HEAVY BLACK-FEATHERED NORTH EAST ARROW
+27BA TEARDROP-BARBED RIGHTWARDS ARROW
+27BB HEAVY TEARDROP-SHANKED RIGHTWARDS ARROW
+27BC WEDGE-TAILED RIGHTWARDS ARROW
+27BD HEAVY WEDGE-TAILED RIGHTWARDS ARROW
+27BE OPEN-OUTLINED RIGHTWARDS ARROW
+3000 IDEOGRAPHIC SPACE
+3001 IDEOGRAPHIC COMMA
+3002 IDEOGRAPHIC FULL STOP
+3003 DITTO MARK
+3004 JAPANESE INDUSTRIAL STANDARD SYMBOL
+3005 IDEOGRAPHIC ITERATION MARK
+3006 IDEOGRAPHIC CLOSING MARK
+3007 IDEOGRAPHIC NUMBER ZERO
+3008 LEFT ANGLE BRACKET
+3009 RIGHT ANGLE BRACKET
+300A LEFT DOUBLE ANGLE BRACKET
+300B RIGHT DOUBLE ANGLE BRACKET
+300C LEFT CORNER BRACKET
+300D RIGHT CORNER BRACKET
+300E LEFT WHITE CORNER BRACKET
+300F RIGHT WHITE CORNER BRACKET
+3010 LEFT BLACK LENTICULAR BRACKET
+3011 RIGHT BLACK LENTICULAR BRACKET
+3012 POSTAL MARK
+3013 GETA MARK
+3014 LEFT TORTOISE SHELL BRACKET
+3015 RIGHT TORTOISE SHELL BRACKET
+3016 LEFT WHITE LENTICULAR BRACKET
+3017 RIGHT WHITE LENTICULAR BRACKET
+3018 LEFT WHITE TORTOISE SHELL BRACKET
+3019 RIGHT WHITE TORTOISE SHELL BRACKET
+301A LEFT WHITE SQUARE BRACKET
+301B RIGHT WHITE SQUARE BRACKET
+301C WAVE DASH
+301D REVERSED DOUBLE PRIME QUOTATION MARK
+301E DOUBLE PRIME QUOTATION MARK
+301F LOW DOUBLE PRIME QUOTATION MARK
+3020 POSTAL MARK FACE
+3021 HANGZHOU NUMERAL ONE
+3022 HANGZHOU NUMERAL TWO
+3023 HANGZHOU NUMERAL THREE
+3024 HANGZHOU NUMERAL FOUR
+3025 HANGZHOU NUMERAL FIVE
+3026 HANGZHOU NUMERAL SIX
+3027 HANGZHOU NUMERAL SEVEN
+3028 HANGZHOU NUMERAL EIGHT
+3029 HANGZHOU NUMERAL NINE
+302A IDEOGRAPHIC LEVEL TONE MARK
+302B IDEOGRAPHIC RISING TONE MARK
+302C IDEOGRAPHIC DEPARTING TONE MARK
+302D IDEOGRAPHIC ENTERING TONE MARK
+302E HANGUL SINGLE DOT TONE MARK
+302F HANGUL DOUBLE DOT TONE MARK
+3030 WAVY DASH
+3031 VERTICAL KANA REPEAT MARK
+3032 VERTICAL KANA REPEAT WITH VOICED SOUND MARK
+3033 VERTICAL KANA REPEAT MARK UPPER HALF
+3034 VERTICAL KANA REPEAT WITH VOICED SOUND MARK UPPER HALF
+3035 VERTICAL KANA REPEAT MARK LOWER HALF
+3036 CIRCLED POSTAL MARK
+3037 IDEOGRAPHIC TELEGRAPH LINE FEED SEPARATOR SYMBOL
+303F IDEOGRAPHIC HALF FILL SPACE
+3041 HIRAGANA LETTER SMALL A
+3042 HIRAGANA LETTER A
+3043 HIRAGANA LETTER SMALL I
+3044 HIRAGANA LETTER I
+3045 HIRAGANA LETTER SMALL U
+3046 HIRAGANA LETTER U
+3047 HIRAGANA LETTER SMALL E
+3048 HIRAGANA LETTER E
+3049 HIRAGANA LETTER SMALL O
+304A HIRAGANA LETTER O
+304B HIRAGANA LETTER KA
+304C HIRAGANA LETTER GA
+304D HIRAGANA LETTER KI
+304E HIRAGANA LETTER GI
+304F HIRAGANA LETTER KU
+3050 HIRAGANA LETTER GU
+3051 HIRAGANA LETTER KE
+3052 HIRAGANA LETTER GE
+3053 HIRAGANA LETTER KO
+3054 HIRAGANA LETTER GO
+3055 HIRAGANA LETTER SA
+3056 HIRAGANA LETTER ZA
+3057 HIRAGANA LETTER SI
+3058 HIRAGANA LETTER ZI
+3059 HIRAGANA LETTER SU
+305A HIRAGANA LETTER ZU
+305B HIRAGANA LETTER SE
+305C HIRAGANA LETTER ZE
+305D HIRAGANA LETTER SO
+305E HIRAGANA LETTER ZO
+305F HIRAGANA LETTER TA
+3060 HIRAGANA LETTER DA
+3061 HIRAGANA LETTER TI
+3062 HIRAGANA LETTER DI
+3063 HIRAGANA LETTER SMALL TU
+3064 HIRAGANA LETTER TU
+3065 HIRAGANA LETTER DU
+3066 HIRAGANA LETTER TE
+3067 HIRAGANA LETTER DE
+3068 HIRAGANA LETTER TO
+3069 HIRAGANA LETTER DO
+306A HIRAGANA LETTER NA
+306B HIRAGANA LETTER NI
+306C HIRAGANA LETTER NU
+306D HIRAGANA LETTER NE
+306E HIRAGANA LETTER NO
+306F HIRAGANA LETTER HA
+3070 HIRAGANA LETTER BA
+3071 HIRAGANA LETTER PA
+3072 HIRAGANA LETTER HI
+3073 HIRAGANA LETTER BI
+3074 HIRAGANA LETTER PI
+3075 HIRAGANA LETTER HU
+3076 HIRAGANA LETTER BU
+3077 HIRAGANA LETTER PU
+3078 HIRAGANA LETTER HE
+3079 HIRAGANA LETTER BE
+307A HIRAGANA LETTER PE
+307B HIRAGANA LETTER HO
+307C HIRAGANA LETTER BO
+307D HIRAGANA LETTER PO
+307E HIRAGANA LETTER MA
+307F HIRAGANA LETTER MI
+3080 HIRAGANA LETTER MU
+3081 HIRAGANA LETTER ME
+3082 HIRAGANA LETTER MO
+3083 HIRAGANA LETTER SMALL YA
+3084 HIRAGANA LETTER YA
+3085 HIRAGANA LETTER SMALL YU
+3086 HIRAGANA LETTER YU
+3087 HIRAGANA LETTER SMALL YO
+3088 HIRAGANA LETTER YO
+3089 HIRAGANA LETTER RA
+308A HIRAGANA LETTER RI
+308B HIRAGANA LETTER RU
+308C HIRAGANA LETTER RE
+308D HIRAGANA LETTER RO
+308E HIRAGANA LETTER SMALL WA
+308F HIRAGANA LETTER WA
+3090 HIRAGANA LETTER WI
+3091 HIRAGANA LETTER WE
+3092 HIRAGANA LETTER WO
+3093 HIRAGANA LETTER N
+3094 HIRAGANA LETTER VU
+3099 COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK
+309A COMBINING KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK
+309B KATAKANA-HIRAGANA VOICED SOUND MARK
+309C KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK
+309D HIRAGANA ITERATION MARK
+309E HIRAGANA VOICED ITERATION MARK
+30A1 KATAKANA LETTER SMALL A
+30A2 KATAKANA LETTER A
+30A3 KATAKANA LETTER SMALL I
+30A4 KATAKANA LETTER I
+30A5 KATAKANA LETTER SMALL U
+30A6 KATAKANA LETTER U
+30A7 KATAKANA LETTER SMALL E
+30A8 KATAKANA LETTER E
+30A9 KATAKANA LETTER SMALL O
+30AA KATAKANA LETTER O
+30AB KATAKANA LETTER KA
+30AC KATAKANA LETTER GA
+30AD KATAKANA LETTER KI
+30AE KATAKANA LETTER GI
+30AF KATAKANA LETTER KU
+30B0 KATAKANA LETTER GU
+30B1 KATAKANA LETTER KE
+30B2 KATAKANA LETTER GE
+30B3 KATAKANA LETTER KO
+30B4 KATAKANA LETTER GO
+30B5 KATAKANA LETTER SA
+30B6 KATAKANA LETTER ZA
+30B7 KATAKANA LETTER SI
+30B8 KATAKANA LETTER ZI
+30B9 KATAKANA LETTER SU
+30BA KATAKANA LETTER ZU
+30BB KATAKANA LETTER SE
+30BC KATAKANA LETTER ZE
+30BD KATAKANA LETTER SO
+30BE KATAKANA LETTER ZO
+30BF KATAKANA LETTER TA
+30C0 KATAKANA LETTER DA
+30C1 KATAKANA LETTER TI
+30C2 KATAKANA LETTER DI
+30C3 KATAKANA LETTER SMALL TU
+30C4 KATAKANA LETTER TU
+30C5 KATAKANA LETTER DU
+30C6 KATAKANA LETTER TE
+30C7 KATAKANA LETTER DE
+30C8 KATAKANA LETTER TO
+30C9 KATAKANA LETTER DO
+30CA KATAKANA LETTER NA
+30CB KATAKANA LETTER NI
+30CC KATAKANA LETTER NU
+30CD KATAKANA LETTER NE
+30CE KATAKANA LETTER NO
+30CF KATAKANA LETTER HA
+30D0 KATAKANA LETTER BA
+30D1 KATAKANA LETTER PA
+30D2 KATAKANA LETTER HI
+30D3 KATAKANA LETTER BI
+30D4 KATAKANA LETTER PI
+30D5 KATAKANA LETTER HU
+30D6 KATAKANA LETTER BU
+30D7 KATAKANA LETTER PU
+30D8 KATAKANA LETTER HE
+30D9 KATAKANA LETTER BE
+30DA KATAKANA LETTER PE
+30DB KATAKANA LETTER HO
+30DC KATAKANA LETTER BO
+30DD KATAKANA LETTER PO
+30DE KATAKANA LETTER MA
+30DF KATAKANA LETTER MI
+30E0 KATAKANA LETTER MU
+30E1 KATAKANA LETTER ME
+30E2 KATAKANA LETTER MO
+30E3 KATAKANA LETTER SMALL YA
+30E4 KATAKANA LETTER YA
+30E5 KATAKANA LETTER SMALL YU
+30E6 KATAKANA LETTER YU
+30E7 KATAKANA LETTER SMALL YO
+30E8 KATAKANA LETTER YO
+30E9 KATAKANA LETTER RA
+30EA KATAKANA LETTER RI
+30EB KATAKANA LETTER RU
+30EC KATAKANA LETTER RE
+30ED KATAKANA LETTER RO
+30EE KATAKANA LETTER SMALL WA
+30EF KATAKANA LETTER WA
+30F0 KATAKANA LETTER WI
+30F1 KATAKANA LETTER WE
+30F2 KATAKANA LETTER WO
+30F3 KATAKANA LETTER N
+30F4 KATAKANA LETTER VU
+30F5 KATAKANA LETTER SMALL KA
+30F6 KATAKANA LETTER SMALL KE
+30F7 KATAKANA LETTER VA
+30F8 KATAKANA LETTER VI
+30F9 KATAKANA LETTER VE
+30FA KATAKANA LETTER VO
+30FB KATAKANA MIDDLE DOT
+30FC KATAKANA-HIRAGANA PROLONGED SOUND MARK
+30FD KATAKANA ITERATION MARK
+30FE KATAKANA VOICED ITERATION MARK
+3105 BOPOMOFO LETTER B
+3106 BOPOMOFO LETTER P
+3107 BOPOMOFO LETTER M
+3108 BOPOMOFO LETTER F
+3109 BOPOMOFO LETTER D
+310A BOPOMOFO LETTER T
+310B BOPOMOFO LETTER N
+310C BOPOMOFO LETTER L
+310D BOPOMOFO LETTER G
+310E BOPOMOFO LETTER K
+310F BOPOMOFO LETTER H
+3110 BOPOMOFO LETTER J
+3111 BOPOMOFO LETTER Q
+3112 BOPOMOFO LETTER X
+3113 BOPOMOFO LETTER ZH
+3114 BOPOMOFO LETTER CH
+3115 BOPOMOFO LETTER SH
+3116 BOPOMOFO LETTER R
+3117 BOPOMOFO LETTER Z
+3118 BOPOMOFO LETTER C
+3119 BOPOMOFO LETTER S
+311A BOPOMOFO LETTER A
+311B BOPOMOFO LETTER O
+311C BOPOMOFO LETTER E
+311D BOPOMOFO LETTER EH
+311E BOPOMOFO LETTER AI
+311F BOPOMOFO LETTER EI
+3120 BOPOMOFO LETTER AU
+3121 BOPOMOFO LETTER OU
+3122 BOPOMOFO LETTER AN
+3123 BOPOMOFO LETTER EN
+3124 BOPOMOFO LETTER ANG
+3125 BOPOMOFO LETTER ENG
+3126 BOPOMOFO LETTER ER
+3127 BOPOMOFO LETTER I
+3128 BOPOMOFO LETTER U
+3129 BOPOMOFO LETTER IU
+312A BOPOMOFO LETTER V
+312B BOPOMOFO LETTER NG
+312C BOPOMOFO LETTER GN
+3131 HANGUL LETTER KIYEOK
+3132 HANGUL LETTER SSANGKIYEOK
+3133 HANGUL LETTER KIYEOK-SIOS
+3134 HANGUL LETTER NIEUN
+3135 HANGUL LETTER NIEUN-CIEUC
+3136 HANGUL LETTER NIEUN-HIEUH
+3137 HANGUL LETTER TIKEUT
+3138 HANGUL LETTER SSANGTIKEUT
+3139 HANGUL LETTER RIEUL
+313A HANGUL LETTER RIEUL-KIYEOK
+313B HANGUL LETTER RIEUL-MIEUM
+313C HANGUL LETTER RIEUL-PIEUP
+313D HANGUL LETTER RIEUL-SIOS
+313E HANGUL LETTER RIEUL-THIEUTH
+313F HANGUL LETTER RIEUL-PHIEUPH
+3140 HANGUL LETTER RIEUL-HIEUH
+3141 HANGUL LETTER MIEUM
+3142 HANGUL LETTER PIEUP
+3143 HANGUL LETTER SSANGPIEUP
+3144 HANGUL LETTER PIEUP-SIOS
+3145 HANGUL LETTER SIOS
+3146 HANGUL LETTER SSANGSIOS
+3147 HANGUL LETTER IEUNG
+3148 HANGUL LETTER CIEUC
+3149 HANGUL LETTER SSANGCIEUC
+314A HANGUL LETTER CHIEUCH
+314B HANGUL LETTER KHIEUKH
+314C HANGUL LETTER THIEUTH
+314D HANGUL LETTER PHIEUPH
+314E HANGUL LETTER HIEUH
+314F HANGUL LETTER A
+3150 HANGUL LETTER AE
+3151 HANGUL LETTER YA
+3152 HANGUL LETTER YAE
+3153 HANGUL LETTER EO
+3154 HANGUL LETTER E
+3155 HANGUL LETTER YEO
+3156 HANGUL LETTER YE
+3157 HANGUL LETTER O
+3158 HANGUL LETTER WA
+3159 HANGUL LETTER WAE
+315A HANGUL LETTER OE
+315B HANGUL LETTER YO
+315C HANGUL LETTER U
+315D HANGUL LETTER WEO
+315E HANGUL LETTER WE
+315F HANGUL LETTER WI
+3160 HANGUL LETTER YU
+3161 HANGUL LETTER EU
+3162 HANGUL LETTER YI
+3163 HANGUL LETTER I
+3164 HANGUL FILLER
+3165 HANGUL LETTER SSANGNIEUN
+3166 HANGUL LETTER NIEUN-TIKEUT
+3167 HANGUL LETTER NIEUN-SIOS
+3168 HANGUL LETTER NIEUN-PANSIOS
+3169 HANGUL LETTER RIEUL-KIYEOK-SIOS
+316A HANGUL LETTER RIEUL-TIKEUT
+316B HANGUL LETTER RIEUL-PIEUP-SIOS
+316C HANGUL LETTER RIEUL-PANSIOS
+316D HANGUL LETTER RIEUL-YEORINHIEUH
+316E HANGUL LETTER MIEUM-PIEUP
+316F HANGUL LETTER MIEUM-SIOS
+3170 HANGUL LETTER MIEUM-PANSIOS
+3171 HANGUL LETTER KAPYEOUNMIEUM
+3172 HANGUL LETTER PIEUP-KIYEOK
+3173 HANGUL LETTER PIEUP-TIKEUT
+3174 HANGUL LETTER PIEUP-SIOS-KIYEOK
+3175 HANGUL LETTER PIEUP-SIOS-TIKEUT
+3176 HANGUL LETTER PIEUP-CIEUC
+3177 HANGUL LETTER PIEUP-THIEUTH
+3178 HANGUL LETTER KAPYEOUNPIEUP
+3179 HANGUL LETTER KAPYEOUNSSANGPIEUP
+317A HANGUL LETTER SIOS-KIYEOK
+317B HANGUL LETTER SIOS-NIEUN
+317C HANGUL LETTER SIOS-TIKEUT
+317D HANGUL LETTER SIOS-PIEUP
+317E HANGUL LETTER SIOS-CIEUC
+317F HANGUL LETTER PANSIOS
+3180 HANGUL LETTER SSANGIEUNG
+3181 HANGUL LETTER YESIEUNG
+3182 HANGUL LETTER YESIEUNG-SIOS
+3183 HANGUL LETTER YESIEUNG-PANSIOS
+3184 HANGUL LETTER KAPYEOUNPHIEUPH
+3185 HANGUL LETTER SSANGHIEUH
+3186 HANGUL LETTER YEORINHIEUH
+3187 HANGUL LETTER YO-YA
+3188 HANGUL LETTER YO-YAE
+3189 HANGUL LETTER YO-I
+318A HANGUL LETTER YU-YEO
+318B HANGUL LETTER YU-YE
+318C HANGUL LETTER YU-I
+318D HANGUL LETTER ARAEA
+318E HANGUL LETTER ARAEAE
+3190 IDEOGRAPHIC ANNOTATION LINKING MARK
+3191 IDEOGRAPHIC ANNOTATION REVERSE MARK
+3192 IDEOGRAPHIC ANNOTATION ONE MARK
+3193 IDEOGRAPHIC ANNOTATION TWO MARK
+3194 IDEOGRAPHIC ANNOTATION THREE MARK
+3195 IDEOGRAPHIC ANNOTATION FOUR MARK
+3196 IDEOGRAPHIC ANNOTATION TOP MARK
+3197 IDEOGRAPHIC ANNOTATION MIDDLE MARK
+3198 IDEOGRAPHIC ANNOTATION BOTTOM MARK
+3199 IDEOGRAPHIC ANNOTATION FIRST MARK
+319A IDEOGRAPHIC ANNOTATION SECOND MARK
+319B IDEOGRAPHIC ANNOTATION THIRD MARK
+319C IDEOGRAPHIC ANNOTATION FOURTH MARK
+319D IDEOGRAPHIC ANNOTATION HEAVEN MARK
+319E IDEOGRAPHIC ANNOTATION EARTH MARK
+319F IDEOGRAPHIC ANNOTATION MAN MARK
+3200 PARENTHESIZED HANGUL KIYEOK
+3201 PARENTHESIZED HANGUL NIEUN
+3202 PARENTHESIZED HANGUL TIKEUT
+3203 PARENTHESIZED HANGUL RIEUL
+3204 PARENTHESIZED HANGUL MIEUM
+3205 PARENTHESIZED HANGUL PIEUP
+3206 PARENTHESIZED HANGUL SIOS
+3207 PARENTHESIZED HANGUL IEUNG
+3208 PARENTHESIZED HANGUL CIEUC
+3209 PARENTHESIZED HANGUL CHIEUCH
+320A PARENTHESIZED HANGUL KHIEUKH
+320B PARENTHESIZED HANGUL THIEUTH
+320C PARENTHESIZED HANGUL PHIEUPH
+320D PARENTHESIZED HANGUL HIEUH
+320E PARENTHESIZED HANGUL KIYEOK A
+320F PARENTHESIZED HANGUL NIEUN A
+3210 PARENTHESIZED HANGUL TIKEUT A
+3211 PARENTHESIZED HANGUL RIEUL A
+3212 PARENTHESIZED HANGUL MIEUM A
+3213 PARENTHESIZED HANGUL PIEUP A
+3214 PARENTHESIZED HANGUL SIOS A
+3215 PARENTHESIZED HANGUL IEUNG A
+3216 PARENTHESIZED HANGUL CIEUC A
+3217 PARENTHESIZED HANGUL CHIEUCH A
+3218 PARENTHESIZED HANGUL KHIEUKH A
+3219 PARENTHESIZED HANGUL THIEUTH A
+321A PARENTHESIZED HANGUL PHIEUPH A
+321B PARENTHESIZED HANGUL HIEUH A
+321C PARENTHESIZED HANGUL CIEUC U
+3220 PARENTHESIZED IDEOGRAPH ONE
+3221 PARENTHESIZED IDEOGRAPH TWO
+3222 PARENTHESIZED IDEOGRAPH THREE
+3223 PARENTHESIZED IDEOGRAPH FOUR
+3224 PARENTHESIZED IDEOGRAPH FIVE
+3225 PARENTHESIZED IDEOGRAPH SIX
+3226 PARENTHESIZED IDEOGRAPH SEVEN
+3227 PARENTHESIZED IDEOGRAPH EIGHT
+3228 PARENTHESIZED IDEOGRAPH NINE
+3229 PARENTHESIZED IDEOGRAPH TEN
+322A PARENTHESIZED IDEOGRAPH MOON
+322B PARENTHESIZED IDEOGRAPH FIRE
+322C PARENTHESIZED IDEOGRAPH WATER
+322D PARENTHESIZED IDEOGRAPH WOOD
+322E PARENTHESIZED IDEOGRAPH METAL
+322F PARENTHESIZED IDEOGRAPH EARTH
+3230 PARENTHESIZED IDEOGRAPH SUN
+3231 PARENTHESIZED IDEOGRAPH STOCK
+3232 PARENTHESIZED IDEOGRAPH HAVE
+3233 PARENTHESIZED IDEOGRAPH SOCIETY
+3234 PARENTHESIZED IDEOGRAPH NAME
+3235 PARENTHESIZED IDEOGRAPH SPECIAL
+3236 PARENTHESIZED IDEOGRAPH FINANCIAL
+3237 PARENTHESIZED IDEOGRAPH CONGRATULATION
+3238 PARENTHESIZED IDEOGRAPH LABOR
+3239 PARENTHESIZED IDEOGRAPH REPRESENT
+323A PARENTHESIZED IDEOGRAPH CALL
+323B PARENTHESIZED IDEOGRAPH STUDY
+323C PARENTHESIZED IDEOGRAPH SUPERVISE
+323D PARENTHESIZED IDEOGRAPH ENTERPRISE
+323E PARENTHESIZED IDEOGRAPH RESOURCE
+323F PARENTHESIZED IDEOGRAPH ALLIANCE
+3240 PARENTHESIZED IDEOGRAPH FESTIVAL
+3241 PARENTHESIZED IDEOGRAPH REST
+3242 PARENTHESIZED IDEOGRAPH SELF
+3243 PARENTHESIZED IDEOGRAPH REACH
+3260 CIRCLED HANGUL KIYEOK
+3261 CIRCLED HANGUL NIEUN
+3262 CIRCLED HANGUL TIKEUT
+3263 CIRCLED HANGUL RIEUL
+3264 CIRCLED HANGUL MIEUM
+3265 CIRCLED HANGUL PIEUP
+3266 CIRCLED HANGUL SIOS
+3267 CIRCLED HANGUL IEUNG
+3268 CIRCLED HANGUL CIEUC
+3269 CIRCLED HANGUL CHIEUCH
+326A CIRCLED HANGUL KHIEUKH
+326B CIRCLED HANGUL THIEUTH
+326C CIRCLED HANGUL PHIEUPH
+326D CIRCLED HANGUL HIEUH
+326E CIRCLED HANGUL KIYEOK A
+326F CIRCLED HANGUL NIEUN A
+3270 CIRCLED HANGUL TIKEUT A
+3271 CIRCLED HANGUL RIEUL A
+3272 CIRCLED HANGUL MIEUM A
+3273 CIRCLED HANGUL PIEUP A
+3274 CIRCLED HANGUL SIOS A
+3275 CIRCLED HANGUL IEUNG A
+3276 CIRCLED HANGUL CIEUC A
+3277 CIRCLED HANGUL CHIEUCH A
+3278 CIRCLED HANGUL KHIEUKH A
+3279 CIRCLED HANGUL THIEUTH A
+327A CIRCLED HANGUL PHIEUPH A
+327B CIRCLED HANGUL HIEUH A
+327F KOREAN STANDARD SYMBOL
+3280 CIRCLED IDEOGRAPH ONE
+3281 CIRCLED IDEOGRAPH TWO
+3282 CIRCLED IDEOGRAPH THREE
+3283 CIRCLED IDEOGRAPH FOUR
+3284 CIRCLED IDEOGRAPH FIVE
+3285 CIRCLED IDEOGRAPH SIX
+3286 CIRCLED IDEOGRAPH SEVEN
+3287 CIRCLED IDEOGRAPH EIGHT
+3288 CIRCLED IDEOGRAPH NINE
+3289 CIRCLED IDEOGRAPH TEN
+328A CIRCLED IDEOGRAPH MOON
+328B CIRCLED IDEOGRAPH FIRE
+328C CIRCLED IDEOGRAPH WATER
+328D CIRCLED IDEOGRAPH WOOD
+328E CIRCLED IDEOGRAPH METAL
+328F CIRCLED IDEOGRAPH EARTH
+3290 CIRCLED IDEOGRAPH SUN
+3291 CIRCLED IDEOGRAPH STOCK
+3292 CIRCLED IDEOGRAPH HAVE
+3293 CIRCLED IDEOGRAPH SOCIETY
+3294 CIRCLED IDEOGRAPH NAME
+3295 CIRCLED IDEOGRAPH SPECIAL
+3296 CIRCLED IDEOGRAPH FINANCIAL
+3297 CIRCLED IDEOGRAPH CONGRATULATION
+3298 CIRCLED IDEOGRAPH LABOR
+3299 CIRCLED IDEOGRAPH SECRET
+329A CIRCLED IDEOGRAPH MALE
+329B CIRCLED IDEOGRAPH FEMALE
+329C CIRCLED IDEOGRAPH SUITABLE
+329D CIRCLED IDEOGRAPH EXCELLENT
+329E CIRCLED IDEOGRAPH PRINT
+329F CIRCLED IDEOGRAPH ATTENTION
+32A0 CIRCLED IDEOGRAPH ITEM
+32A1 CIRCLED IDEOGRAPH REST
+32A2 CIRCLED IDEOGRAPH COPY
+32A3 CIRCLED IDEOGRAPH CORRECT
+32A4 CIRCLED IDEOGRAPH HIGH
+32A5 CIRCLED IDEOGRAPH CENTRE
+32A6 CIRCLED IDEOGRAPH LOW
+32A7 CIRCLED IDEOGRAPH LEFT
+32A8 CIRCLED IDEOGRAPH RIGHT
+32A9 CIRCLED IDEOGRAPH MEDICINE
+32AA CIRCLED IDEOGRAPH RELIGION
+32AB CIRCLED IDEOGRAPH STUDY
+32AC CIRCLED IDEOGRAPH SUPERVISE
+32AD CIRCLED IDEOGRAPH ENTERPRISE
+32AE CIRCLED IDEOGRAPH RESOURCE
+32AF CIRCLED IDEOGRAPH ALLIANCE
+32B0 CIRCLED IDEOGRAPH NIGHT
+32C0 IDEOGRAPHIC TELEGRAPH SYMBOL FOR JANUARY
+32C1 IDEOGRAPHIC TELEGRAPH SYMBOL FOR FEBRUARY
+32C2 IDEOGRAPHIC TELEGRAPH SYMBOL FOR MARCH
+32C3 IDEOGRAPHIC TELEGRAPH SYMBOL FOR APRIL
+32C4 IDEOGRAPHIC TELEGRAPH SYMBOL FOR MAY
+32C5 IDEOGRAPHIC TELEGRAPH SYMBOL FOR JUNE
+32C6 IDEOGRAPHIC TELEGRAPH SYMBOL FOR JULY
+32C7 IDEOGRAPHIC TELEGRAPH SYMBOL FOR AUGUST
+32C8 IDEOGRAPHIC TELEGRAPH SYMBOL FOR SEPTEMBER
+32C9 IDEOGRAPHIC TELEGRAPH SYMBOL FOR OCTOBER
+32CA IDEOGRAPHIC TELEGRAPH SYMBOL FOR NOVEMBER
+32CB IDEOGRAPHIC TELEGRAPH SYMBOL FOR DECEMBER
+32D0 CIRCLED KATAKANA A
+32D1 CIRCLED KATAKANA I
+32D2 CIRCLED KATAKANA U
+32D3 CIRCLED KATAKANA E
+32D4 CIRCLED KATAKANA O
+32D5 CIRCLED KATAKANA KA
+32D6 CIRCLED KATAKANA KI
+32D7 CIRCLED KATAKANA KU
+32D8 CIRCLED KATAKANA KE
+32D9 CIRCLED KATAKANA KO
+32DA CIRCLED KATAKANA SA
+32DB CIRCLED KATAKANA SI
+32DC CIRCLED KATAKANA SU
+32DD CIRCLED KATAKANA SE
+32DE CIRCLED KATAKANA SO
+32DF CIRCLED KATAKANA TA
+32E0 CIRCLED KATAKANA TI
+32E1 CIRCLED KATAKANA TU
+32E2 CIRCLED KATAKANA TE
+32E3 CIRCLED KATAKANA TO
+32E4 CIRCLED KATAKANA NA
+32E5 CIRCLED KATAKANA NI
+32E6 CIRCLED KATAKANA NU
+32E7 CIRCLED KATAKANA NE
+32E8 CIRCLED KATAKANA NO
+32E9 CIRCLED KATAKANA HA
+32EA CIRCLED KATAKANA HI
+32EB CIRCLED KATAKANA HU
+32EC CIRCLED KATAKANA HE
+32ED CIRCLED KATAKANA HO
+32EE CIRCLED KATAKANA MA
+32EF CIRCLED KATAKANA MI
+32F0 CIRCLED KATAKANA MU
+32F1 CIRCLED KATAKANA ME
+32F2 CIRCLED KATAKANA MO
+32F3 CIRCLED KATAKANA YA
+32F4 CIRCLED KATAKANA YU
+32F5 CIRCLED KATAKANA YO
+32F6 CIRCLED KATAKANA RA
+32F7 CIRCLED KATAKANA RI
+32F8 CIRCLED KATAKANA RU
+32F9 CIRCLED KATAKANA RE
+32FA CIRCLED KATAKANA RO
+32FB CIRCLED KATAKANA WA
+32FC CIRCLED KATAKANA WI
+32FD CIRCLED KATAKANA WE
+32FE CIRCLED KATAKANA WO
+3300 SQUARE APAATO
+3301 SQUARE ARUHUA
+3302 SQUARE ANPEA
+3303 SQUARE AARU
+3304 SQUARE ININGU
+3305 SQUARE INTI
+3306 SQUARE UON
+3307 SQUARE ESUKUUDO
+3308 SQUARE EEKAA
+3309 SQUARE ONSU
+330A SQUARE OOMU
+330B SQUARE KAIRI
+330C SQUARE KARATTO
+330D SQUARE KARORII
+330E SQUARE GARON
+330F SQUARE GANMA
+3310 SQUARE GIGA
+3311 SQUARE GINII
+3312 SQUARE KYURII
+3313 SQUARE GIRUDAA
+3314 SQUARE KIRO
+3315 SQUARE KIROGURAMU
+3316 SQUARE KIROMEETORU
+3317 SQUARE KIROWATTO
+3318 SQUARE GURAMU
+3319 SQUARE GURAMUTON
+331A SQUARE KURUZEIRO
+331B SQUARE KUROONE
+331C SQUARE KEESU
+331D SQUARE KORUNA
+331E SQUARE KOOPO
+331F SQUARE SAIKURU
+3320 SQUARE SANTIIMU
+3321 SQUARE SIRINGU
+3322 SQUARE SENTI
+3323 SQUARE SENTO
+3324 SQUARE DAASU
+3325 SQUARE DESI
+3326 SQUARE DORU
+3327 SQUARE TON
+3328 SQUARE NANO
+3329 SQUARE NOTTO
+332A SQUARE HAITU
+332B SQUARE PAASENTO
+332C SQUARE PAATU
+332D SQUARE BAARERU
+332E SQUARE PIASUTORU
+332F SQUARE PIKURU
+3330 SQUARE PIKO
+3331 SQUARE BIRU
+3332 SQUARE HUARADDO
+3333 SQUARE HUIITO
+3334 SQUARE BUSSYERU
+3335 SQUARE HURAN
+3336 SQUARE HEKUTAARU
+3337 SQUARE PESO
+3338 SQUARE PENIHI
+3339 SQUARE HERUTU
+333A SQUARE PENSU
+333B SQUARE PEEZI
+333C SQUARE BEETA
+333D SQUARE POINTO
+333E SQUARE BORUTO
+333F SQUARE HON
+3340 SQUARE PONDO
+3341 SQUARE HOORU
+3342 SQUARE HOON
+3343 SQUARE MAIKURO
+3344 SQUARE MAIRU
+3345 SQUARE MAHHA
+3346 SQUARE MARUKU
+3347 SQUARE MANSYON
+3348 SQUARE MIKURON
+3349 SQUARE MIRI
+334A SQUARE MIRIBAARU
+334B SQUARE MEGA
+334C SQUARE MEGATON
+334D SQUARE MEETORU
+334E SQUARE YAADO
+334F SQUARE YAARU
+3350 SQUARE YUAN
+3351 SQUARE RITTORU
+3352 SQUARE RIRA
+3353 SQUARE RUPII
+3354 SQUARE RUUBURU
+3355 SQUARE REMU
+3356 SQUARE RENTOGEN
+3357 SQUARE WATTO
+3358 IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR ZERO
+3359 IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR ONE
+335A IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWO
+335B IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR THREE
+335C IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR FOUR
+335D IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR FIVE
+335E IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR SIX
+335F IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR SEVEN
+3360 IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR EIGHT
+3361 IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR NINE
+3362 IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TEN
+3363 IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR ELEVEN
+3364 IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWELVE
+3365 IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR THIRTEEN
+3366 IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR FOURTEEN
+3367 IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR FIFTEEN
+3368 IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR SIXTEEN
+3369 IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR SEVENTEEN
+336A IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR EIGHTEEN
+336B IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR NINETEEN
+336C IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWENTY
+336D IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWENTY-ONE
+336E IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWENTY-TWO
+336F IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWENTY-THREE
+3370 IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWENTY-FOUR
+3371 SQUARE HPA
+3372 SQUARE DA
+3373 SQUARE AU
+3374 SQUARE BAR
+3375 SQUARE OV
+3376 SQUARE PC
+337B SQUARE ERA NAME HEISEI
+337C SQUARE ERA NAME SYOUWA
+337D SQUARE ERA NAME TAISYOU
+337E SQUARE ERA NAME MEIZI
+337F SQUARE CORPORATION
+3380 SQUARE PA AMPS
+3381 SQUARE NA
+3382 SQUARE MU A
+3383 SQUARE MA
+3384 SQUARE KA
+3385 SQUARE KB
+3386 SQUARE MB
+3387 SQUARE GB
+3388 SQUARE CAL
+3389 SQUARE KCAL
+338A SQUARE PF
+338B SQUARE NF
+338C SQUARE MU F
+338D SQUARE MU G
+338E SQUARE MG
+338F SQUARE KG
+3390 SQUARE HZ
+3391 SQUARE KHZ
+3392 SQUARE MHZ
+3393 SQUARE GHZ
+3394 SQUARE THZ
+3395 SQUARE MU L
+3396 SQUARE ML
+3397 SQUARE DL
+3398 SQUARE KL
+3399 SQUARE FM
+339A SQUARE NM
+339B SQUARE MU M
+339C SQUARE MM
+339D SQUARE CM
+339E SQUARE KM
+339F SQUARE MM SQUARED
+33A0 SQUARE CM SQUARED
+33A1 SQUARE M SQUARED
+33A2 SQUARE KM SQUARED
+33A3 SQUARE MM CUBED
+33A4 SQUARE CM CUBED
+33A5 SQUARE M CUBED
+33A6 SQUARE KM CUBED
+33A7 SQUARE M OVER S
+33A8 SQUARE M OVER S SQUARED
+33A9 SQUARE PA
+33AA SQUARE KPA
+33AB SQUARE MPA
+33AC SQUARE GPA
+33AD SQUARE RAD
+33AE SQUARE RAD OVER S
+33AF SQUARE RAD OVER S SQUARED
+33B0 SQUARE PS
+33B1 SQUARE NS
+33B2 SQUARE MU S
+33B3 SQUARE MS
+33B4 SQUARE PV
+33B5 SQUARE NV
+33B6 SQUARE MU V
+33B7 SQUARE MV
+33B8 SQUARE KV
+33B9 SQUARE MV MEGA
+33BA SQUARE PW
+33BB SQUARE NW
+33BC SQUARE MU W
+33BD SQUARE MW
+33BE SQUARE KW
+33BF SQUARE MW MEGA
+33C0 SQUARE K OHM
+33C1 SQUARE M OHM
+33C2 SQUARE AM
+33C3 SQUARE BQ
+33C4 SQUARE CC
+33C5 SQUARE CD
+33C6 SQUARE C OVER KG
+33C7 SQUARE CO
+33C8 SQUARE DB
+33C9 SQUARE GY
+33CA SQUARE HA
+33CB SQUARE HP
+33CC SQUARE IN
+33CD SQUARE KK
+33CE SQUARE KM CAPITAL
+33CF SQUARE KT
+33D0 SQUARE LM
+33D1 SQUARE LN
+33D2 SQUARE LOG
+33D3 SQUARE LX
+33D4 SQUARE MB SMALL
+33D5 SQUARE MIL
+33D6 SQUARE MOL
+33D7 SQUARE PH
+33D8 SQUARE PM
+33D9 SQUARE PPM
+33DA SQUARE PR
+33DB SQUARE SR
+33DC SQUARE SV
+33DD SQUARE WB
+33E0 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY ONE
+33E1 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWO
+33E2 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY THREE
+33E3 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY FOUR
+33E4 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY FIVE
+33E5 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY SIX
+33E6 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY SEVEN
+33E7 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY EIGHT
+33E8 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY NINE
+33E9 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TEN
+33EA IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY ELEVEN
+33EB IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWELVE
+33EC IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY THIRTEEN
+33ED IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY FOURTEEN
+33EE IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY FIFTEEN
+33EF IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY SIXTEEN
+33F0 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY SEVENTEEN
+33F1 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY EIGHTEEN
+33F2 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY NINETEEN
+33F3 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY
+33F4 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-ONE
+33F5 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-TWO
+33F6 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-THREE
+33F7 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-FOUR
+33F8 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-FIVE
+33F9 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-SIX
+33FA IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-SEVEN
+33FB IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-EIGHT
+33FC IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-NINE
+33FD IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY THIRTY
+33FE IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY THIRTY-ONE
+4E00 <CJK Ideograph, First>
+9FA5 <CJK Ideograph, Last>
+AC00 <Hangul Syllable, First>
+D7A3 <Hangul Syllable, Last>
+D800 <Unassigned High Surrogate, First>
+DB7F <Unassigned High Surrogate, Last>
+DB80 <Private Use High Surrogate, First>
+DBFF <Private Use High Surrogate, Last>
+DC00 <Low Surrogate, First>
+DFFF <Low Surrogate, Last>
+E000 <Private Use, First>
+F8FF <Private Use, Last>
+F900 <CJK Compatibility Ideograph, First>
+FA2D <CJK Compatibility Ideograph, Last>
+FB00 LATIN SMALL LIGATURE FF
+FB01 LATIN SMALL LIGATURE FI
+FB02 LATIN SMALL LIGATURE FL
+FB03 LATIN SMALL LIGATURE FFI
+FB04 LATIN SMALL LIGATURE FFL
+FB05 LATIN SMALL LIGATURE LONG S T
+FB06 LATIN SMALL LIGATURE ST
+FB13 ARMENIAN SMALL LIGATURE MEN NOW
+FB14 ARMENIAN SMALL LIGATURE MEN ECH
+FB15 ARMENIAN SMALL LIGATURE MEN INI
+FB16 ARMENIAN SMALL LIGATURE VEW NOW
+FB17 ARMENIAN SMALL LIGATURE MEN XEH
+FB1E HEBREW POINT JUDEO-SPANISH VARIKA
+FB1F HEBREW LIGATURE YIDDISH YOD YOD PATAH
+FB20 HEBREW LETTER ALTERNATIVE AYIN
+FB21 HEBREW LETTER WIDE ALEF
+FB22 HEBREW LETTER WIDE DALET
+FB23 HEBREW LETTER WIDE HE
+FB24 HEBREW LETTER WIDE KAF
+FB25 HEBREW LETTER WIDE LAMED
+FB26 HEBREW LETTER WIDE FINAL MEM
+FB27 HEBREW LETTER WIDE RESH
+FB28 HEBREW LETTER WIDE TAV
+FB29 HEBREW LETTER ALTERNATIVE PLUS SIGN
+FB2A HEBREW LETTER SHIN WITH SHIN DOT
+FB2B HEBREW LETTER SHIN WITH SIN DOT
+FB2C HEBREW LETTER SHIN WITH DAGESH AND SHIN DOT
+FB2D HEBREW LETTER SHIN WITH DAGESH AND SIN DOT
+FB2E HEBREW LETTER ALEF WITH PATAH
+FB2F HEBREW LETTER ALEF WITH QAMATS
+FB30 HEBREW LETTER ALEF WITH MAPIQ
+FB31 HEBREW LETTER BET WITH DAGESH
+FB32 HEBREW LETTER GIMEL WITH DAGESH
+FB33 HEBREW LETTER DALET WITH DAGESH
+FB34 HEBREW LETTER HE WITH MAPIQ
+FB35 HEBREW LETTER VAV WITH DAGESH
+FB36 HEBREW LETTER ZAYIN WITH DAGESH
+FB38 HEBREW LETTER TET WITH DAGESH
+FB39 HEBREW LETTER YOD WITH DAGESH
+FB3A HEBREW LETTER FINAL KAF WITH DAGESH
+FB3B HEBREW LETTER KAF WITH DAGESH
+FB3C HEBREW LETTER LAMED WITH DAGESH
+FB3E HEBREW LETTER MEM WITH DAGESH
+FB40 HEBREW LETTER NUN WITH DAGESH
+FB41 HEBREW LETTER SAMEKH WITH DAGESH
+FB43 HEBREW LETTER FINAL PE WITH DAGESH
+FB44 HEBREW LETTER PE WITH DAGESH
+FB46 HEBREW LETTER TSADI WITH DAGESH
+FB47 HEBREW LETTER QOF WITH DAGESH
+FB48 HEBREW LETTER RESH WITH DAGESH
+FB49 HEBREW LETTER SHIN WITH DAGESH
+FB4A HEBREW LETTER TAV WITH DAGESH
+FB4B HEBREW LETTER VAV WITH HOLAM
+FB4C HEBREW LETTER BET WITH RAFE
+FB4D HEBREW LETTER KAF WITH RAFE
+FB4E HEBREW LETTER PE WITH RAFE
+FB4F HEBREW LIGATURE ALEF LAMED
+FB50 ARABIC LETTER ALEF WASLA ISOLATED FORM
+FB51 ARABIC LETTER ALEF WASLA FINAL FORM
+FB52 ARABIC LETTER BEEH ISOLATED FORM
+FB53 ARABIC LETTER BEEH FINAL FORM
+FB54 ARABIC LETTER BEEH INITIAL FORM
+FB55 ARABIC LETTER BEEH MEDIAL FORM
+FB56 ARABIC LETTER PEH ISOLATED FORM
+FB57 ARABIC LETTER PEH FINAL FORM
+FB58 ARABIC LETTER PEH INITIAL FORM
+FB59 ARABIC LETTER PEH MEDIAL FORM
+FB5A ARABIC LETTER BEHEH ISOLATED FORM
+FB5B ARABIC LETTER BEHEH FINAL FORM
+FB5C ARABIC LETTER BEHEH INITIAL FORM
+FB5D ARABIC LETTER BEHEH MEDIAL FORM
+FB5E ARABIC LETTER TTEHEH ISOLATED FORM
+FB5F ARABIC LETTER TTEHEH FINAL FORM
+FB60 ARABIC LETTER TTEHEH INITIAL FORM
+FB61 ARABIC LETTER TTEHEH MEDIAL FORM
+FB62 ARABIC LETTER TEHEH ISOLATED FORM
+FB63 ARABIC LETTER TEHEH FINAL FORM
+FB64 ARABIC LETTER TEHEH INITIAL FORM
+FB65 ARABIC LETTER TEHEH MEDIAL FORM
+FB66 ARABIC LETTER TTEH ISOLATED FORM
+FB67 ARABIC LETTER TTEH FINAL FORM
+FB68 ARABIC LETTER TTEH INITIAL FORM
+FB69 ARABIC LETTER TTEH MEDIAL FORM
+FB6A ARABIC LETTER VEH ISOLATED FORM
+FB6B ARABIC LETTER VEH FINAL FORM
+FB6C ARABIC LETTER VEH INITIAL FORM
+FB6D ARABIC LETTER VEH MEDIAL FORM
+FB6E ARABIC LETTER PEHEH ISOLATED FORM
+FB6F ARABIC LETTER PEHEH FINAL FORM
+FB70 ARABIC LETTER PEHEH INITIAL FORM
+FB71 ARABIC LETTER PEHEH MEDIAL FORM
+FB72 ARABIC LETTER DYEH ISOLATED FORM
+FB73 ARABIC LETTER DYEH FINAL FORM
+FB74 ARABIC LETTER DYEH INITIAL FORM
+FB75 ARABIC LETTER DYEH MEDIAL FORM
+FB76 ARABIC LETTER NYEH ISOLATED FORM
+FB77 ARABIC LETTER NYEH FINAL FORM
+FB78 ARABIC LETTER NYEH INITIAL FORM
+FB79 ARABIC LETTER NYEH MEDIAL FORM
+FB7A ARABIC LETTER TCHEH ISOLATED FORM
+FB7B ARABIC LETTER TCHEH FINAL FORM
+FB7C ARABIC LETTER TCHEH INITIAL FORM
+FB7D ARABIC LETTER TCHEH MEDIAL FORM
+FB7E ARABIC LETTER TCHEHEH ISOLATED FORM
+FB7F ARABIC LETTER TCHEHEH FINAL FORM
+FB80 ARABIC LETTER TCHEHEH INITIAL FORM
+FB81 ARABIC LETTER TCHEHEH MEDIAL FORM
+FB82 ARABIC LETTER DDAHAL ISOLATED FORM
+FB83 ARABIC LETTER DDAHAL FINAL FORM
+FB84 ARABIC LETTER DAHAL ISOLATED FORM
+FB85 ARABIC LETTER DAHAL FINAL FORM
+FB86 ARABIC LETTER DUL ISOLATED FORM
+FB87 ARABIC LETTER DUL FINAL FORM
+FB88 ARABIC LETTER DDAL ISOLATED FORM
+FB89 ARABIC LETTER DDAL FINAL FORM
+FB8A ARABIC LETTER JEH ISOLATED FORM
+FB8B ARABIC LETTER JEH FINAL FORM
+FB8C ARABIC LETTER RREH ISOLATED FORM
+FB8D ARABIC LETTER RREH FINAL FORM
+FB8E ARABIC LETTER KEHEH ISOLATED FORM
+FB8F ARABIC LETTER KEHEH FINAL FORM
+FB90 ARABIC LETTER KEHEH INITIAL FORM
+FB91 ARABIC LETTER KEHEH MEDIAL FORM
+FB92 ARABIC LETTER GAF ISOLATED FORM
+FB93 ARABIC LETTER GAF FINAL FORM
+FB94 ARABIC LETTER GAF INITIAL FORM
+FB95 ARABIC LETTER GAF MEDIAL FORM
+FB96 ARABIC LETTER GUEH ISOLATED FORM
+FB97 ARABIC LETTER GUEH FINAL FORM
+FB98 ARABIC LETTER GUEH INITIAL FORM
+FB99 ARABIC LETTER GUEH MEDIAL FORM
+FB9A ARABIC LETTER NGOEH ISOLATED FORM
+FB9B ARABIC LETTER NGOEH FINAL FORM
+FB9C ARABIC LETTER NGOEH INITIAL FORM
+FB9D ARABIC LETTER NGOEH MEDIAL FORM
+FB9E ARABIC LETTER NOON GHUNNA ISOLATED FORM
+FB9F ARABIC LETTER NOON GHUNNA FINAL FORM
+FBA0 ARABIC LETTER RNOON ISOLATED FORM
+FBA1 ARABIC LETTER RNOON FINAL FORM
+FBA2 ARABIC LETTER RNOON INITIAL FORM
+FBA3 ARABIC LETTER RNOON MEDIAL FORM
+FBA4 ARABIC LETTER HEH WITH YEH ABOVE ISOLATED FORM
+FBA5 ARABIC LETTER HEH WITH YEH ABOVE FINAL FORM
+FBA6 ARABIC LETTER HEH GOAL ISOLATED FORM
+FBA7 ARABIC LETTER HEH GOAL FINAL FORM
+FBA8 ARABIC LETTER HEH GOAL INITIAL FORM
+FBA9 ARABIC LETTER HEH GOAL MEDIAL FORM
+FBAA ARABIC LETTER HEH DOACHASHMEE ISOLATED FORM
+FBAB ARABIC LETTER HEH DOACHASHMEE FINAL FORM
+FBAC ARABIC LETTER HEH DOACHASHMEE INITIAL FORM
+FBAD ARABIC LETTER HEH DOACHASHMEE MEDIAL FORM
+FBAE ARABIC LETTER YEH BARREE ISOLATED FORM
+FBAF ARABIC LETTER YEH BARREE FINAL FORM
+FBB0 ARABIC LETTER YEH BARREE WITH HAMZA ABOVE ISOLATED FORM
+FBB1 ARABIC LETTER YEH BARREE WITH HAMZA ABOVE FINAL FORM
+FBD3 ARABIC LETTER NG ISOLATED FORM
+FBD4 ARABIC LETTER NG FINAL FORM
+FBD5 ARABIC LETTER NG INITIAL FORM
+FBD6 ARABIC LETTER NG MEDIAL FORM
+FBD7 ARABIC LETTER U ISOLATED FORM
+FBD8 ARABIC LETTER U FINAL FORM
+FBD9 ARABIC LETTER OE ISOLATED FORM
+FBDA ARABIC LETTER OE FINAL FORM
+FBDB ARABIC LETTER YU ISOLATED FORM
+FBDC ARABIC LETTER YU FINAL FORM
+FBDD ARABIC LETTER U WITH HAMZA ABOVE ISOLATED FORM
+FBDE ARABIC LETTER VE ISOLATED FORM
+FBDF ARABIC LETTER VE FINAL FORM
+FBE0 ARABIC LETTER KIRGHIZ OE ISOLATED FORM
+FBE1 ARABIC LETTER KIRGHIZ OE FINAL FORM
+FBE2 ARABIC LETTER KIRGHIZ YU ISOLATED FORM
+FBE3 ARABIC LETTER KIRGHIZ YU FINAL FORM
+FBE4 ARABIC LETTER E ISOLATED FORM
+FBE5 ARABIC LETTER E FINAL FORM
+FBE6 ARABIC LETTER E INITIAL FORM
+FBE7 ARABIC LETTER E MEDIAL FORM
+FBE8 ARABIC LETTER UIGHUR KAZAKH KIRGHIZ ALEF MAKSURA INITIAL FORM
+FBE9 ARABIC LETTER UIGHUR KAZAKH KIRGHIZ ALEF MAKSURA MEDIAL FORM
+FBEA ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH ALEF ISOLATED FORM
+FBEB ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH ALEF FINAL FORM
+FBEC ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH AE ISOLATED FORM
+FBED ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH AE FINAL FORM
+FBEE ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH WAW ISOLATED FORM
+FBEF ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH WAW FINAL FORM
+FBF0 ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH U ISOLATED FORM
+FBF1 ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH U FINAL FORM
+FBF2 ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH OE ISOLATED FORM
+FBF3 ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH OE FINAL FORM
+FBF4 ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH YU ISOLATED FORM
+FBF5 ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH YU FINAL FORM
+FBF6 ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH E ISOLATED FORM
+FBF7 ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH E FINAL FORM
+FBF8 ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH E INITIAL FORM
+FBF9 ARABIC LIGATURE UIGHUR KIRGHIZ YEH WITH HAMZA ABOVE WITH ALEF MAKSURA ISOLATED FORM
+FBFA ARABIC LIGATURE UIGHUR KIRGHIZ YEH WITH HAMZA ABOVE WITH ALEF MAKSURA FINAL FORM
+FBFB ARABIC LIGATURE UIGHUR KIRGHIZ YEH WITH HAMZA ABOVE WITH ALEF MAKSURA INITIAL FORM
+FBFC ARABIC LETTER FARSI YEH ISOLATED FORM
+FBFD ARABIC LETTER FARSI YEH FINAL FORM
+FBFE ARABIC LETTER FARSI YEH INITIAL FORM
+FBFF ARABIC LETTER FARSI YEH MEDIAL FORM
+FC00 ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH JEEM ISOLATED FORM
+FC01 ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH HAH ISOLATED FORM
+FC02 ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH MEEM ISOLATED FORM
+FC03 ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH ALEF MAKSURA ISOLATED FORM
+FC04 ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH YEH ISOLATED FORM
+FC05 ARABIC LIGATURE BEH WITH JEEM ISOLATED FORM
+FC06 ARABIC LIGATURE BEH WITH HAH ISOLATED FORM
+FC07 ARABIC LIGATURE BEH WITH KHAH ISOLATED FORM
+FC08 ARABIC LIGATURE BEH WITH MEEM ISOLATED FORM
+FC09 ARABIC LIGATURE BEH WITH ALEF MAKSURA ISOLATED FORM
+FC0A ARABIC LIGATURE BEH WITH YEH ISOLATED FORM
+FC0B ARABIC LIGATURE TEH WITH JEEM ISOLATED FORM
+FC0C ARABIC LIGATURE TEH WITH HAH ISOLATED FORM
+FC0D ARABIC LIGATURE TEH WITH KHAH ISOLATED FORM
+FC0E ARABIC LIGATURE TEH WITH MEEM ISOLATED FORM
+FC0F ARABIC LIGATURE TEH WITH ALEF MAKSURA ISOLATED FORM
+FC10 ARABIC LIGATURE TEH WITH YEH ISOLATED FORM
+FC11 ARABIC LIGATURE THEH WITH JEEM ISOLATED FORM
+FC12 ARABIC LIGATURE THEH WITH MEEM ISOLATED FORM
+FC13 ARABIC LIGATURE THEH WITH ALEF MAKSURA ISOLATED FORM
+FC14 ARABIC LIGATURE THEH WITH YEH ISOLATED FORM
+FC15 ARABIC LIGATURE JEEM WITH HAH ISOLATED FORM
+FC16 ARABIC LIGATURE JEEM WITH MEEM ISOLATED FORM
+FC17 ARABIC LIGATURE HAH WITH JEEM ISOLATED FORM
+FC18 ARABIC LIGATURE HAH WITH MEEM ISOLATED FORM
+FC19 ARABIC LIGATURE KHAH WITH JEEM ISOLATED FORM
+FC1A ARABIC LIGATURE KHAH WITH HAH ISOLATED FORM
+FC1B ARABIC LIGATURE KHAH WITH MEEM ISOLATED FORM
+FC1C ARABIC LIGATURE SEEN WITH JEEM ISOLATED FORM
+FC1D ARABIC LIGATURE SEEN WITH HAH ISOLATED FORM
+FC1E ARABIC LIGATURE SEEN WITH KHAH ISOLATED FORM
+FC1F ARABIC LIGATURE SEEN WITH MEEM ISOLATED FORM
+FC20 ARABIC LIGATURE SAD WITH HAH ISOLATED FORM
+FC21 ARABIC LIGATURE SAD WITH MEEM ISOLATED FORM
+FC22 ARABIC LIGATURE DAD WITH JEEM ISOLATED FORM
+FC23 ARABIC LIGATURE DAD WITH HAH ISOLATED FORM
+FC24 ARABIC LIGATURE DAD WITH KHAH ISOLATED FORM
+FC25 ARABIC LIGATURE DAD WITH MEEM ISOLATED FORM
+FC26 ARABIC LIGATURE TAH WITH HAH ISOLATED FORM
+FC27 ARABIC LIGATURE TAH WITH MEEM ISOLATED FORM
+FC28 ARABIC LIGATURE ZAH WITH MEEM ISOLATED FORM
+FC29 ARABIC LIGATURE AIN WITH JEEM ISOLATED FORM
+FC2A ARABIC LIGATURE AIN WITH MEEM ISOLATED FORM
+FC2B ARABIC LIGATURE GHAIN WITH JEEM ISOLATED FORM
+FC2C ARABIC LIGATURE GHAIN WITH MEEM ISOLATED FORM
+FC2D ARABIC LIGATURE FEH WITH JEEM ISOLATED FORM
+FC2E ARABIC LIGATURE FEH WITH HAH ISOLATED FORM
+FC2F ARABIC LIGATURE FEH WITH KHAH ISOLATED FORM
+FC30 ARABIC LIGATURE FEH WITH MEEM ISOLATED FORM
+FC31 ARABIC LIGATURE FEH WITH ALEF MAKSURA ISOLATED FORM
+FC32 ARABIC LIGATURE FEH WITH YEH ISOLATED FORM
+FC33 ARABIC LIGATURE QAF WITH HAH ISOLATED FORM
+FC34 ARABIC LIGATURE QAF WITH MEEM ISOLATED FORM
+FC35 ARABIC LIGATURE QAF WITH ALEF MAKSURA ISOLATED FORM
+FC36 ARABIC LIGATURE QAF WITH YEH ISOLATED FORM
+FC37 ARABIC LIGATURE KAF WITH ALEF ISOLATED FORM
+FC38 ARABIC LIGATURE KAF WITH JEEM ISOLATED FORM
+FC39 ARABIC LIGATURE KAF WITH HAH ISOLATED FORM
+FC3A ARABIC LIGATURE KAF WITH KHAH ISOLATED FORM
+FC3B ARABIC LIGATURE KAF WITH LAM ISOLATED FORM
+FC3C ARABIC LIGATURE KAF WITH MEEM ISOLATED FORM
+FC3D ARABIC LIGATURE KAF WITH ALEF MAKSURA ISOLATED FORM
+FC3E ARABIC LIGATURE KAF WITH YEH ISOLATED FORM
+FC3F ARABIC LIGATURE LAM WITH JEEM ISOLATED FORM
+FC40 ARABIC LIGATURE LAM WITH HAH ISOLATED FORM
+FC41 ARABIC LIGATURE LAM WITH KHAH ISOLATED FORM
+FC42 ARABIC LIGATURE LAM WITH MEEM ISOLATED FORM
+FC43 ARABIC LIGATURE LAM WITH ALEF MAKSURA ISOLATED FORM
+FC44 ARABIC LIGATURE LAM WITH YEH ISOLATED FORM
+FC45 ARABIC LIGATURE MEEM WITH JEEM ISOLATED FORM
+FC46 ARABIC LIGATURE MEEM WITH HAH ISOLATED FORM
+FC47 ARABIC LIGATURE MEEM WITH KHAH ISOLATED FORM
+FC48 ARABIC LIGATURE MEEM WITH MEEM ISOLATED FORM
+FC49 ARABIC LIGATURE MEEM WITH ALEF MAKSURA ISOLATED FORM
+FC4A ARABIC LIGATURE MEEM WITH YEH ISOLATED FORM
+FC4B ARABIC LIGATURE NOON WITH JEEM ISOLATED FORM
+FC4C ARABIC LIGATURE NOON WITH HAH ISOLATED FORM
+FC4D ARABIC LIGATURE NOON WITH KHAH ISOLATED FORM
+FC4E ARABIC LIGATURE NOON WITH MEEM ISOLATED FORM
+FC4F ARABIC LIGATURE NOON WITH ALEF MAKSURA ISOLATED FORM
+FC50 ARABIC LIGATURE NOON WITH YEH ISOLATED FORM
+FC51 ARABIC LIGATURE HEH WITH JEEM ISOLATED FORM
+FC52 ARABIC LIGATURE HEH WITH MEEM ISOLATED FORM
+FC53 ARABIC LIGATURE HEH WITH ALEF MAKSURA ISOLATED FORM
+FC54 ARABIC LIGATURE HEH WITH YEH ISOLATED FORM
+FC55 ARABIC LIGATURE YEH WITH JEEM ISOLATED FORM
+FC56 ARABIC LIGATURE YEH WITH HAH ISOLATED FORM
+FC57 ARABIC LIGATURE YEH WITH KHAH ISOLATED FORM
+FC58 ARABIC LIGATURE YEH WITH MEEM ISOLATED FORM
+FC59 ARABIC LIGATURE YEH WITH ALEF MAKSURA ISOLATED FORM
+FC5A ARABIC LIGATURE YEH WITH YEH ISOLATED FORM
+FC5B ARABIC LIGATURE THAL WITH SUPERSCRIPT ALEF ISOLATED FORM
+FC5C ARABIC LIGATURE REH WITH SUPERSCRIPT ALEF ISOLATED FORM
+FC5D ARABIC LIGATURE ALEF MAKSURA WITH SUPERSCRIPT ALEF ISOLATED FORM
+FC5E ARABIC LIGATURE SHADDA WITH DAMMATAN ISOLATED FORM
+FC5F ARABIC LIGATURE SHADDA WITH KASRATAN ISOLATED FORM
+FC60 ARABIC LIGATURE SHADDA WITH FATHA ISOLATED FORM
+FC61 ARABIC LIGATURE SHADDA WITH DAMMA ISOLATED FORM
+FC62 ARABIC LIGATURE SHADDA WITH KASRA ISOLATED FORM
+FC63 ARABIC LIGATURE SHADDA WITH SUPERSCRIPT ALEF ISOLATED FORM
+FC64 ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH REH FINAL FORM
+FC65 ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH ZAIN FINAL FORM
+FC66 ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH MEEM FINAL FORM
+FC67 ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH NOON FINAL FORM
+FC68 ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH ALEF MAKSURA FINAL FORM
+FC69 ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH YEH FINAL FORM
+FC6A ARABIC LIGATURE BEH WITH REH FINAL FORM
+FC6B ARABIC LIGATURE BEH WITH ZAIN FINAL FORM
+FC6C ARABIC LIGATURE BEH WITH MEEM FINAL FORM
+FC6D ARABIC LIGATURE BEH WITH NOON FINAL FORM
+FC6E ARABIC LIGATURE BEH WITH ALEF MAKSURA FINAL FORM
+FC6F ARABIC LIGATURE BEH WITH YEH FINAL FORM
+FC70 ARABIC LIGATURE TEH WITH REH FINAL FORM
+FC71 ARABIC LIGATURE TEH WITH ZAIN FINAL FORM
+FC72 ARABIC LIGATURE TEH WITH MEEM FINAL FORM
+FC73 ARABIC LIGATURE TEH WITH NOON FINAL FORM
+FC74 ARABIC LIGATURE TEH WITH ALEF MAKSURA FINAL FORM
+FC75 ARABIC LIGATURE TEH WITH YEH FINAL FORM
+FC76 ARABIC LIGATURE THEH WITH REH FINAL FORM
+FC77 ARABIC LIGATURE THEH WITH ZAIN FINAL FORM
+FC78 ARABIC LIGATURE THEH WITH MEEM FINAL FORM
+FC79 ARABIC LIGATURE THEH WITH NOON FINAL FORM
+FC7A ARABIC LIGATURE THEH WITH ALEF MAKSURA FINAL FORM
+FC7B ARABIC LIGATURE THEH WITH YEH FINAL FORM
+FC7C ARABIC LIGATURE FEH WITH ALEF MAKSURA FINAL FORM
+FC7D ARABIC LIGATURE FEH WITH YEH FINAL FORM
+FC7E ARABIC LIGATURE QAF WITH ALEF MAKSURA FINAL FORM
+FC7F ARABIC LIGATURE QAF WITH YEH FINAL FORM
+FC80 ARABIC LIGATURE KAF WITH ALEF FINAL FORM
+FC81 ARABIC LIGATURE KAF WITH LAM FINAL FORM
+FC82 ARABIC LIGATURE KAF WITH MEEM FINAL FORM
+FC83 ARABIC LIGATURE KAF WITH ALEF MAKSURA FINAL FORM
+FC84 ARABIC LIGATURE KAF WITH YEH FINAL FORM
+FC85 ARABIC LIGATURE LAM WITH MEEM FINAL FORM
+FC86 ARABIC LIGATURE LAM WITH ALEF MAKSURA FINAL FORM
+FC87 ARABIC LIGATURE LAM WITH YEH FINAL FORM
+FC88 ARABIC LIGATURE MEEM WITH ALEF FINAL FORM
+FC89 ARABIC LIGATURE MEEM WITH MEEM FINAL FORM
+FC8A ARABIC LIGATURE NOON WITH REH FINAL FORM
+FC8B ARABIC LIGATURE NOON WITH ZAIN FINAL FORM
+FC8C ARABIC LIGATURE NOON WITH MEEM FINAL FORM
+FC8D ARABIC LIGATURE NOON WITH NOON FINAL FORM
+FC8E ARABIC LIGATURE NOON WITH ALEF MAKSURA FINAL FORM
+FC8F ARABIC LIGATURE NOON WITH YEH FINAL FORM
+FC90 ARABIC LIGATURE ALEF MAKSURA WITH SUPERSCRIPT ALEF FINAL FORM
+FC91 ARABIC LIGATURE YEH WITH REH FINAL FORM
+FC92 ARABIC LIGATURE YEH WITH ZAIN FINAL FORM
+FC93 ARABIC LIGATURE YEH WITH MEEM FINAL FORM
+FC94 ARABIC LIGATURE YEH WITH NOON FINAL FORM
+FC95 ARABIC LIGATURE YEH WITH ALEF MAKSURA FINAL FORM
+FC96 ARABIC LIGATURE YEH WITH YEH FINAL FORM
+FC97 ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH JEEM INITIAL FORM
+FC98 ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH HAH INITIAL FORM
+FC99 ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH KHAH INITIAL FORM
+FC9A ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH MEEM INITIAL FORM
+FC9B ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH HEH INITIAL FORM
+FC9C ARABIC LIGATURE BEH WITH JEEM INITIAL FORM
+FC9D ARABIC LIGATURE BEH WITH HAH INITIAL FORM
+FC9E ARABIC LIGATURE BEH WITH KHAH INITIAL FORM
+FC9F ARABIC LIGATURE BEH WITH MEEM INITIAL FORM
+FCA0 ARABIC LIGATURE BEH WITH HEH INITIAL FORM
+FCA1 ARABIC LIGATURE TEH WITH JEEM INITIAL FORM
+FCA2 ARABIC LIGATURE TEH WITH HAH INITIAL FORM
+FCA3 ARABIC LIGATURE TEH WITH KHAH INITIAL FORM
+FCA4 ARABIC LIGATURE TEH WITH MEEM INITIAL FORM
+FCA5 ARABIC LIGATURE TEH WITH HEH INITIAL FORM
+FCA6 ARABIC LIGATURE THEH WITH MEEM INITIAL FORM
+FCA7 ARABIC LIGATURE JEEM WITH HAH INITIAL FORM
+FCA8 ARABIC LIGATURE JEEM WITH MEEM INITIAL FORM
+FCA9 ARABIC LIGATURE HAH WITH JEEM INITIAL FORM
+FCAA ARABIC LIGATURE HAH WITH MEEM INITIAL FORM
+FCAB ARABIC LIGATURE KHAH WITH JEEM INITIAL FORM
+FCAC ARABIC LIGATURE KHAH WITH MEEM INITIAL FORM
+FCAD ARABIC LIGATURE SEEN WITH JEEM INITIAL FORM
+FCAE ARABIC LIGATURE SEEN WITH HAH INITIAL FORM
+FCAF ARABIC LIGATURE SEEN WITH KHAH INITIAL FORM
+FCB0 ARABIC LIGATURE SEEN WITH MEEM INITIAL FORM
+FCB1 ARABIC LIGATURE SAD WITH HAH INITIAL FORM
+FCB2 ARABIC LIGATURE SAD WITH KHAH INITIAL FORM
+FCB3 ARABIC LIGATURE SAD WITH MEEM INITIAL FORM
+FCB4 ARABIC LIGATURE DAD WITH JEEM INITIAL FORM
+FCB5 ARABIC LIGATURE DAD WITH HAH INITIAL FORM
+FCB6 ARABIC LIGATURE DAD WITH KHAH INITIAL FORM
+FCB7 ARABIC LIGATURE DAD WITH MEEM INITIAL FORM
+FCB8 ARABIC LIGATURE TAH WITH HAH INITIAL FORM
+FCB9 ARABIC LIGATURE ZAH WITH MEEM INITIAL FORM
+FCBA ARABIC LIGATURE AIN WITH JEEM INITIAL FORM
+FCBB ARABIC LIGATURE AIN WITH MEEM INITIAL FORM
+FCBC ARABIC LIGATURE GHAIN WITH JEEM INITIAL FORM
+FCBD ARABIC LIGATURE GHAIN WITH MEEM INITIAL FORM
+FCBE ARABIC LIGATURE FEH WITH JEEM INITIAL FORM
+FCBF ARABIC LIGATURE FEH WITH HAH INITIAL FORM
+FCC0 ARABIC LIGATURE FEH WITH KHAH INITIAL FORM
+FCC1 ARABIC LIGATURE FEH WITH MEEM INITIAL FORM
+FCC2 ARABIC LIGATURE QAF WITH HAH INITIAL FORM
+FCC3 ARABIC LIGATURE QAF WITH MEEM INITIAL FORM
+FCC4 ARABIC LIGATURE KAF WITH JEEM INITIAL FORM
+FCC5 ARABIC LIGATURE KAF WITH HAH INITIAL FORM
+FCC6 ARABIC LIGATURE KAF WITH KHAH INITIAL FORM
+FCC7 ARABIC LIGATURE KAF WITH LAM INITIAL FORM
+FCC8 ARABIC LIGATURE KAF WITH MEEM INITIAL FORM
+FCC9 ARABIC LIGATURE LAM WITH JEEM INITIAL FORM
+FCCA ARABIC LIGATURE LAM WITH HAH INITIAL FORM
+FCCB ARABIC LIGATURE LAM WITH KHAH INITIAL FORM
+FCCC ARABIC LIGATURE LAM WITH MEEM INITIAL FORM
+FCCD ARABIC LIGATURE LAM WITH HEH INITIAL FORM
+FCCE ARABIC LIGATURE MEEM WITH JEEM INITIAL FORM
+FCCF ARABIC LIGATURE MEEM WITH HAH INITIAL FORM
+FCD0 ARABIC LIGATURE MEEM WITH KHAH INITIAL FORM
+FCD1 ARABIC LIGATURE MEEM WITH MEEM INITIAL FORM
+FCD2 ARABIC LIGATURE NOON WITH JEEM INITIAL FORM
+FCD3 ARABIC LIGATURE NOON WITH HAH INITIAL FORM
+FCD4 ARABIC LIGATURE NOON WITH KHAH INITIAL FORM
+FCD5 ARABIC LIGATURE NOON WITH MEEM INITIAL FORM
+FCD6 ARABIC LIGATURE NOON WITH HEH INITIAL FORM
+FCD7 ARABIC LIGATURE HEH WITH JEEM INITIAL FORM
+FCD8 ARABIC LIGATURE HEH WITH MEEM INITIAL FORM
+FCD9 ARABIC LIGATURE HEH WITH SUPERSCRIPT ALEF INITIAL FORM
+FCDA ARABIC LIGATURE YEH WITH JEEM INITIAL FORM
+FCDB ARABIC LIGATURE YEH WITH HAH INITIAL FORM
+FCDC ARABIC LIGATURE YEH WITH KHAH INITIAL FORM
+FCDD ARABIC LIGATURE YEH WITH MEEM INITIAL FORM
+FCDE ARABIC LIGATURE YEH WITH HEH INITIAL FORM
+FCDF ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH MEEM MEDIAL FORM
+FCE0 ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH HEH MEDIAL FORM
+FCE1 ARABIC LIGATURE BEH WITH MEEM MEDIAL FORM
+FCE2 ARABIC LIGATURE BEH WITH HEH MEDIAL FORM
+FCE3 ARABIC LIGATURE TEH WITH MEEM MEDIAL FORM
+FCE4 ARABIC LIGATURE TEH WITH HEH MEDIAL FORM
+FCE5 ARABIC LIGATURE THEH WITH MEEM MEDIAL FORM
+FCE6 ARABIC LIGATURE THEH WITH HEH MEDIAL FORM
+FCE7 ARABIC LIGATURE SEEN WITH MEEM MEDIAL FORM
+FCE8 ARABIC LIGATURE SEEN WITH HEH MEDIAL FORM
+FCE9 ARABIC LIGATURE SHEEN WITH MEEM MEDIAL FORM
+FCEA ARABIC LIGATURE SHEEN WITH HEH MEDIAL FORM
+FCEB ARABIC LIGATURE KAF WITH LAM MEDIAL FORM
+FCEC ARABIC LIGATURE KAF WITH MEEM MEDIAL FORM
+FCED ARABIC LIGATURE LAM WITH MEEM MEDIAL FORM
+FCEE ARABIC LIGATURE NOON WITH MEEM MEDIAL FORM
+FCEF ARABIC LIGATURE NOON WITH HEH MEDIAL FORM
+FCF0 ARABIC LIGATURE YEH WITH MEEM MEDIAL FORM
+FCF1 ARABIC LIGATURE YEH WITH HEH MEDIAL FORM
+FCF2 ARABIC LIGATURE SHADDA WITH FATHA MEDIAL FORM
+FCF3 ARABIC LIGATURE SHADDA WITH DAMMA MEDIAL FORM
+FCF4 ARABIC LIGATURE SHADDA WITH KASRA MEDIAL FORM
+FCF5 ARABIC LIGATURE TAH WITH ALEF MAKSURA ISOLATED FORM
+FCF6 ARABIC LIGATURE TAH WITH YEH ISOLATED FORM
+FCF7 ARABIC LIGATURE AIN WITH ALEF MAKSURA ISOLATED FORM
+FCF8 ARABIC LIGATURE AIN WITH YEH ISOLATED FORM
+FCF9 ARABIC LIGATURE GHAIN WITH ALEF MAKSURA ISOLATED FORM
+FCFA ARABIC LIGATURE GHAIN WITH YEH ISOLATED FORM
+FCFB ARABIC LIGATURE SEEN WITH ALEF MAKSURA ISOLATED FORM
+FCFC ARABIC LIGATURE SEEN WITH YEH ISOLATED FORM
+FCFD ARABIC LIGATURE SHEEN WITH ALEF MAKSURA ISOLATED FORM
+FCFE ARABIC LIGATURE SHEEN WITH YEH ISOLATED FORM
+FCFF ARABIC LIGATURE HAH WITH ALEF MAKSURA ISOLATED FORM
+FD00 ARABIC LIGATURE HAH WITH YEH ISOLATED FORM
+FD01 ARABIC LIGATURE JEEM WITH ALEF MAKSURA ISOLATED FORM
+FD02 ARABIC LIGATURE JEEM WITH YEH ISOLATED FORM
+FD03 ARABIC LIGATURE KHAH WITH ALEF MAKSURA ISOLATED FORM
+FD04 ARABIC LIGATURE KHAH WITH YEH ISOLATED FORM
+FD05 ARABIC LIGATURE SAD WITH ALEF MAKSURA ISOLATED FORM
+FD06 ARABIC LIGATURE SAD WITH YEH ISOLATED FORM
+FD07 ARABIC LIGATURE DAD WITH ALEF MAKSURA ISOLATED FORM
+FD08 ARABIC LIGATURE DAD WITH YEH ISOLATED FORM
+FD09 ARABIC LIGATURE SHEEN WITH JEEM ISOLATED FORM
+FD0A ARABIC LIGATURE SHEEN WITH HAH ISOLATED FORM
+FD0B ARABIC LIGATURE SHEEN WITH KHAH ISOLATED FORM
+FD0C ARABIC LIGATURE SHEEN WITH MEEM ISOLATED FORM
+FD0D ARABIC LIGATURE SHEEN WITH REH ISOLATED FORM
+FD0E ARABIC LIGATURE SEEN WITH REH ISOLATED FORM
+FD0F ARABIC LIGATURE SAD WITH REH ISOLATED FORM
+FD10 ARABIC LIGATURE DAD WITH REH ISOLATED FORM
+FD11 ARABIC LIGATURE TAH WITH ALEF MAKSURA FINAL FORM
+FD12 ARABIC LIGATURE TAH WITH YEH FINAL FORM
+FD13 ARABIC LIGATURE AIN WITH ALEF MAKSURA FINAL FORM
+FD14 ARABIC LIGATURE AIN WITH YEH FINAL FORM
+FD15 ARABIC LIGATURE GHAIN WITH ALEF MAKSURA FINAL FORM
+FD16 ARABIC LIGATURE GHAIN WITH YEH FINAL FORM
+FD17 ARABIC LIGATURE SEEN WITH ALEF MAKSURA FINAL FORM
+FD18 ARABIC LIGATURE SEEN WITH YEH FINAL FORM
+FD19 ARABIC LIGATURE SHEEN WITH ALEF MAKSURA FINAL FORM
+FD1A ARABIC LIGATURE SHEEN WITH YEH FINAL FORM
+FD1B ARABIC LIGATURE HAH WITH ALEF MAKSURA FINAL FORM
+FD1C ARABIC LIGATURE HAH WITH YEH FINAL FORM
+FD1D ARABIC LIGATURE JEEM WITH ALEF MAKSURA FINAL FORM
+FD1E ARABIC LIGATURE JEEM WITH YEH FINAL FORM
+FD1F ARABIC LIGATURE KHAH WITH ALEF MAKSURA FINAL FORM
+FD20 ARABIC LIGATURE KHAH WITH YEH FINAL FORM
+FD21 ARABIC LIGATURE SAD WITH ALEF MAKSURA FINAL FORM
+FD22 ARABIC LIGATURE SAD WITH YEH FINAL FORM
+FD23 ARABIC LIGATURE DAD WITH ALEF MAKSURA FINAL FORM
+FD24 ARABIC LIGATURE DAD WITH YEH FINAL FORM
+FD25 ARABIC LIGATURE SHEEN WITH JEEM FINAL FORM
+FD26 ARABIC LIGATURE SHEEN WITH HAH FINAL FORM
+FD27 ARABIC LIGATURE SHEEN WITH KHAH FINAL FORM
+FD28 ARABIC LIGATURE SHEEN WITH MEEM FINAL FORM
+FD29 ARABIC LIGATURE SHEEN WITH REH FINAL FORM
+FD2A ARABIC LIGATURE SEEN WITH REH FINAL FORM
+FD2B ARABIC LIGATURE SAD WITH REH FINAL FORM
+FD2C ARABIC LIGATURE DAD WITH REH FINAL FORM
+FD2D ARABIC LIGATURE SHEEN WITH JEEM INITIAL FORM
+FD2E ARABIC LIGATURE SHEEN WITH HAH INITIAL FORM
+FD2F ARABIC LIGATURE SHEEN WITH KHAH INITIAL FORM
+FD30 ARABIC LIGATURE SHEEN WITH MEEM INITIAL FORM
+FD31 ARABIC LIGATURE SEEN WITH HEH INITIAL FORM
+FD32 ARABIC LIGATURE SHEEN WITH HEH INITIAL FORM
+FD33 ARABIC LIGATURE TAH WITH MEEM INITIAL FORM
+FD34 ARABIC LIGATURE SEEN WITH JEEM MEDIAL FORM
+FD35 ARABIC LIGATURE SEEN WITH HAH MEDIAL FORM
+FD36 ARABIC LIGATURE SEEN WITH KHAH MEDIAL FORM
+FD37 ARABIC LIGATURE SHEEN WITH JEEM MEDIAL FORM
+FD38 ARABIC LIGATURE SHEEN WITH HAH MEDIAL FORM
+FD39 ARABIC LIGATURE SHEEN WITH KHAH MEDIAL FORM
+FD3A ARABIC LIGATURE TAH WITH MEEM MEDIAL FORM
+FD3B ARABIC LIGATURE ZAH WITH MEEM MEDIAL FORM
+FD3C ARABIC LIGATURE ALEF WITH FATHATAN FINAL FORM
+FD3D ARABIC LIGATURE ALEF WITH FATHATAN ISOLATED FORM
+FD3E ORNATE LEFT PARENTHESIS
+FD3F ORNATE RIGHT PARENTHESIS
+FD50 ARABIC LIGATURE TEH WITH JEEM WITH MEEM INITIAL FORM
+FD51 ARABIC LIGATURE TEH WITH HAH WITH JEEM FINAL FORM
+FD52 ARABIC LIGATURE TEH WITH HAH WITH JEEM INITIAL FORM
+FD53 ARABIC LIGATURE TEH WITH HAH WITH MEEM INITIAL FORM
+FD54 ARABIC LIGATURE TEH WITH KHAH WITH MEEM INITIAL FORM
+FD55 ARABIC LIGATURE TEH WITH MEEM WITH JEEM INITIAL FORM
+FD56 ARABIC LIGATURE TEH WITH MEEM WITH HAH INITIAL FORM
+FD57 ARABIC LIGATURE TEH WITH MEEM WITH KHAH INITIAL FORM
+FD58 ARABIC LIGATURE JEEM WITH MEEM WITH HAH FINAL FORM
+FD59 ARABIC LIGATURE JEEM WITH MEEM WITH HAH INITIAL FORM
+FD5A ARABIC LIGATURE HAH WITH MEEM WITH YEH FINAL FORM
+FD5B ARABIC LIGATURE HAH WITH MEEM WITH ALEF MAKSURA FINAL FORM
+FD5C ARABIC LIGATURE SEEN WITH HAH WITH JEEM INITIAL FORM
+FD5D ARABIC LIGATURE SEEN WITH JEEM WITH HAH INITIAL FORM
+FD5E ARABIC LIGATURE SEEN WITH JEEM WITH ALEF MAKSURA FINAL FORM
+FD5F ARABIC LIGATURE SEEN WITH MEEM WITH HAH FINAL FORM
+FD60 ARABIC LIGATURE SEEN WITH MEEM WITH HAH INITIAL FORM
+FD61 ARABIC LIGATURE SEEN WITH MEEM WITH JEEM INITIAL FORM
+FD62 ARABIC LIGATURE SEEN WITH MEEM WITH MEEM FINAL FORM
+FD63 ARABIC LIGATURE SEEN WITH MEEM WITH MEEM INITIAL FORM
+FD64 ARABIC LIGATURE SAD WITH HAH WITH HAH FINAL FORM
+FD65 ARABIC LIGATURE SAD WITH HAH WITH HAH INITIAL FORM
+FD66 ARABIC LIGATURE SAD WITH MEEM WITH MEEM FINAL FORM
+FD67 ARABIC LIGATURE SHEEN WITH HAH WITH MEEM FINAL FORM
+FD68 ARABIC LIGATURE SHEEN WITH HAH WITH MEEM INITIAL FORM
+FD69 ARABIC LIGATURE SHEEN WITH JEEM WITH YEH FINAL FORM
+FD6A ARABIC LIGATURE SHEEN WITH MEEM WITH KHAH FINAL FORM
+FD6B ARABIC LIGATURE SHEEN WITH MEEM WITH KHAH INITIAL FORM
+FD6C ARABIC LIGATURE SHEEN WITH MEEM WITH MEEM FINAL FORM
+FD6D ARABIC LIGATURE SHEEN WITH MEEM WITH MEEM INITIAL FORM
+FD6E ARABIC LIGATURE DAD WITH HAH WITH ALEF MAKSURA FINAL FORM
+FD6F ARABIC LIGATURE DAD WITH KHAH WITH MEEM FINAL FORM
+FD70 ARABIC LIGATURE DAD WITH KHAH WITH MEEM INITIAL FORM
+FD71 ARABIC LIGATURE TAH WITH MEEM WITH HAH FINAL FORM
+FD72 ARABIC LIGATURE TAH WITH MEEM WITH HAH INITIAL FORM
+FD73 ARABIC LIGATURE TAH WITH MEEM WITH MEEM INITIAL FORM
+FD74 ARABIC LIGATURE TAH WITH MEEM WITH YEH FINAL FORM
+FD75 ARABIC LIGATURE AIN WITH JEEM WITH MEEM FINAL FORM
+FD76 ARABIC LIGATURE AIN WITH MEEM WITH MEEM FINAL FORM
+FD77 ARABIC LIGATURE AIN WITH MEEM WITH MEEM INITIAL FORM
+FD78 ARABIC LIGATURE AIN WITH MEEM WITH ALEF MAKSURA FINAL FORM
+FD79 ARABIC LIGATURE GHAIN WITH MEEM WITH MEEM FINAL FORM
+FD7A ARABIC LIGATURE GHAIN WITH MEEM WITH YEH FINAL FORM
+FD7B ARABIC LIGATURE GHAIN WITH MEEM WITH ALEF MAKSURA FINAL FORM
+FD7C ARABIC LIGATURE FEH WITH KHAH WITH MEEM FINAL FORM
+FD7D ARABIC LIGATURE FEH WITH KHAH WITH MEEM INITIAL FORM
+FD7E ARABIC LIGATURE QAF WITH MEEM WITH HAH FINAL FORM
+FD7F ARABIC LIGATURE QAF WITH MEEM WITH MEEM FINAL FORM
+FD80 ARABIC LIGATURE LAM WITH HAH WITH MEEM FINAL FORM
+FD81 ARABIC LIGATURE LAM WITH HAH WITH YEH FINAL FORM
+FD82 ARABIC LIGATURE LAM WITH HAH WITH ALEF MAKSURA FINAL FORM
+FD83 ARABIC LIGATURE LAM WITH JEEM WITH JEEM INITIAL FORM
+FD84 ARABIC LIGATURE LAM WITH JEEM WITH JEEM FINAL FORM
+FD85 ARABIC LIGATURE LAM WITH KHAH WITH MEEM FINAL FORM
+FD86 ARABIC LIGATURE LAM WITH KHAH WITH MEEM INITIAL FORM
+FD87 ARABIC LIGATURE LAM WITH MEEM WITH HAH FINAL FORM
+FD88 ARABIC LIGATURE LAM WITH MEEM WITH HAH INITIAL FORM
+FD89 ARABIC LIGATURE MEEM WITH HAH WITH JEEM INITIAL FORM
+FD8A ARABIC LIGATURE MEEM WITH HAH WITH MEEM INITIAL FORM
+FD8B ARABIC LIGATURE MEEM WITH HAH WITH YEH FINAL FORM
+FD8C ARABIC LIGATURE MEEM WITH JEEM WITH HAH INITIAL FORM
+FD8D ARABIC LIGATURE MEEM WITH JEEM WITH MEEM INITIAL FORM
+FD8E ARABIC LIGATURE MEEM WITH KHAH WITH JEEM INITIAL FORM
+FD8F ARABIC LIGATURE MEEM WITH KHAH WITH MEEM INITIAL FORM
+FD92 ARABIC LIGATURE MEEM WITH JEEM WITH KHAH INITIAL FORM
+FD93 ARABIC LIGATURE HEH WITH MEEM WITH JEEM INITIAL FORM
+FD94 ARABIC LIGATURE HEH WITH MEEM WITH MEEM INITIAL FORM
+FD95 ARABIC LIGATURE NOON WITH HAH WITH MEEM INITIAL FORM
+FD96 ARABIC LIGATURE NOON WITH HAH WITH ALEF MAKSURA FINAL FORM
+FD97 ARABIC LIGATURE NOON WITH JEEM WITH MEEM FINAL FORM
+FD98 ARABIC LIGATURE NOON WITH JEEM WITH MEEM INITIAL FORM
+FD99 ARABIC LIGATURE NOON WITH JEEM WITH ALEF MAKSURA FINAL FORM
+FD9A ARABIC LIGATURE NOON WITH MEEM WITH YEH FINAL FORM
+FD9B ARABIC LIGATURE NOON WITH MEEM WITH ALEF MAKSURA FINAL FORM
+FD9C ARABIC LIGATURE YEH WITH MEEM WITH MEEM FINAL FORM
+FD9D ARABIC LIGATURE YEH WITH MEEM WITH MEEM INITIAL FORM
+FD9E ARABIC LIGATURE BEH WITH KHAH WITH YEH FINAL FORM
+FD9F ARABIC LIGATURE TEH WITH JEEM WITH YEH FINAL FORM
+FDA0 ARABIC LIGATURE TEH WITH JEEM WITH ALEF MAKSURA FINAL FORM
+FDA1 ARABIC LIGATURE TEH WITH KHAH WITH YEH FINAL FORM
+FDA2 ARABIC LIGATURE TEH WITH KHAH WITH ALEF MAKSURA FINAL FORM
+FDA3 ARABIC LIGATURE TEH WITH MEEM WITH YEH FINAL FORM
+FDA4 ARABIC LIGATURE TEH WITH MEEM WITH ALEF MAKSURA FINAL FORM
+FDA5 ARABIC LIGATURE JEEM WITH MEEM WITH YEH FINAL FORM
+FDA6 ARABIC LIGATURE JEEM WITH HAH WITH ALEF MAKSURA FINAL FORM
+FDA7 ARABIC LIGATURE JEEM WITH MEEM WITH ALEF MAKSURA FINAL FORM
+FDA8 ARABIC LIGATURE SEEN WITH KHAH WITH ALEF MAKSURA FINAL FORM
+FDA9 ARABIC LIGATURE SAD WITH HAH WITH YEH FINAL FORM
+FDAA ARABIC LIGATURE SHEEN WITH HAH WITH YEH FINAL FORM
+FDAB ARABIC LIGATURE DAD WITH HAH WITH YEH FINAL FORM
+FDAC ARABIC LIGATURE LAM WITH JEEM WITH YEH FINAL FORM
+FDAD ARABIC LIGATURE LAM WITH MEEM WITH YEH FINAL FORM
+FDAE ARABIC LIGATURE YEH WITH HAH WITH YEH FINAL FORM
+FDAF ARABIC LIGATURE YEH WITH JEEM WITH YEH FINAL FORM
+FDB0 ARABIC LIGATURE YEH WITH MEEM WITH YEH FINAL FORM
+FDB1 ARABIC LIGATURE MEEM WITH MEEM WITH YEH FINAL FORM
+FDB2 ARABIC LIGATURE QAF WITH MEEM WITH YEH FINAL FORM
+FDB3 ARABIC LIGATURE NOON WITH HAH WITH YEH FINAL FORM
+FDB4 ARABIC LIGATURE QAF WITH MEEM WITH HAH INITIAL FORM
+FDB5 ARABIC LIGATURE LAM WITH HAH WITH MEEM INITIAL FORM
+FDB6 ARABIC LIGATURE AIN WITH MEEM WITH YEH FINAL FORM
+FDB7 ARABIC LIGATURE KAF WITH MEEM WITH YEH FINAL FORM
+FDB8 ARABIC LIGATURE NOON WITH JEEM WITH HAH INITIAL FORM
+FDB9 ARABIC LIGATURE MEEM WITH KHAH WITH YEH FINAL FORM
+FDBA ARABIC LIGATURE LAM WITH JEEM WITH MEEM INITIAL FORM
+FDBB ARABIC LIGATURE KAF WITH MEEM WITH MEEM FINAL FORM
+FDBC ARABIC LIGATURE LAM WITH JEEM WITH MEEM FINAL FORM
+FDBD ARABIC LIGATURE NOON WITH JEEM WITH HAH FINAL FORM
+FDBE ARABIC LIGATURE JEEM WITH HAH WITH YEH FINAL FORM
+FDBF ARABIC LIGATURE HAH WITH JEEM WITH YEH FINAL FORM
+FDC0 ARABIC LIGATURE MEEM WITH JEEM WITH YEH FINAL FORM
+FDC1 ARABIC LIGATURE FEH WITH MEEM WITH YEH FINAL FORM
+FDC2 ARABIC LIGATURE BEH WITH HAH WITH YEH FINAL FORM
+FDC3 ARABIC LIGATURE KAF WITH MEEM WITH MEEM INITIAL FORM
+FDC4 ARABIC LIGATURE AIN WITH JEEM WITH MEEM INITIAL FORM
+FDC5 ARABIC LIGATURE SAD WITH MEEM WITH MEEM INITIAL FORM
+FDC6 ARABIC LIGATURE SEEN WITH KHAH WITH YEH FINAL FORM
+FDC7 ARABIC LIGATURE NOON WITH JEEM WITH YEH FINAL FORM
+FDF0 ARABIC LIGATURE SALLA USED AS KORANIC STOP SIGN ISOLATED FORM
+FDF1 ARABIC LIGATURE QALA USED AS KORANIC STOP SIGN ISOLATED FORM
+FDF2 ARABIC LIGATURE ALLAH ISOLATED FORM
+FDF3 ARABIC LIGATURE AKBAR ISOLATED FORM
+FDF4 ARABIC LIGATURE MOHAMMAD ISOLATED FORM
+FDF5 ARABIC LIGATURE SALAM ISOLATED FORM
+FDF6 ARABIC LIGATURE RASOUL ISOLATED FORM
+FDF7 ARABIC LIGATURE ALAYHE ISOLATED FORM
+FDF8 ARABIC LIGATURE WASALLAM ISOLATED FORM
+FDF9 ARABIC LIGATURE SALLA ISOLATED FORM
+FDFA ARABIC LIGATURE SALLALLAHOU ALAYHE WASALLAM
+FDFB ARABIC LIGATURE JALLAJALALOUHOU
+FE20 COMBINING LIGATURE LEFT HALF
+FE21 COMBINING LIGATURE RIGHT HALF
+FE22 COMBINING DOUBLE TILDE LEFT HALF
+FE23 COMBINING DOUBLE TILDE RIGHT HALF
+FE30 PRESENTATION FORM FOR VERTICAL TWO DOT LEADER
+FE31 PRESENTATION FORM FOR VERTICAL EM DASH
+FE32 PRESENTATION FORM FOR VERTICAL EN DASH
+FE33 PRESENTATION FORM FOR VERTICAL LOW LINE
+FE34 PRESENTATION FORM FOR VERTICAL WAVY LOW LINE
+FE35 PRESENTATION FORM FOR VERTICAL LEFT PARENTHESIS
+FE36 PRESENTATION FORM FOR VERTICAL RIGHT PARENTHESIS
+FE37 PRESENTATION FORM FOR VERTICAL LEFT CURLY BRACKET
+FE38 PRESENTATION FORM FOR VERTICAL RIGHT CURLY BRACKET
+FE39 PRESENTATION FORM FOR VERTICAL LEFT TORTOISE SHELL BRACKET
+FE3A PRESENTATION FORM FOR VERTICAL RIGHT TORTOISE SHELL BRACKET
+FE3B PRESENTATION FORM FOR VERTICAL LEFT BLACK LENTICULAR BRACKET
+FE3C PRESENTATION FORM FOR VERTICAL RIGHT BLACK LENTICULAR BRACKET
+FE3D PRESENTATION FORM FOR VERTICAL LEFT DOUBLE ANGLE BRACKET
+FE3E PRESENTATION FORM FOR VERTICAL RIGHT DOUBLE ANGLE BRACKET
+FE3F PRESENTATION FORM FOR VERTICAL LEFT ANGLE BRACKET
+FE40 PRESENTATION FORM FOR VERTICAL RIGHT ANGLE BRACKET
+FE41 PRESENTATION FORM FOR VERTICAL LEFT CORNER BRACKET
+FE42 PRESENTATION FORM FOR VERTICAL RIGHT CORNER BRACKET
+FE43 PRESENTATION FORM FOR VERTICAL LEFT WHITE CORNER BRACKET
+FE44 PRESENTATION FORM FOR VERTICAL RIGHT WHITE CORNER BRACKET
+FE49 DASHED OVERLINE
+FE4A CENTRELINE OVERLINE
+FE4B WAVY OVERLINE
+FE4C DOUBLE WAVY OVERLINE
+FE4D DASHED LOW LINE
+FE4E CENTRELINE LOW LINE
+FE4F WAVY LOW LINE
+FE50 SMALL COMMA
+FE51 SMALL IDEOGRAPHIC COMMA
+FE52 SMALL FULL STOP
+FE54 SMALL SEMICOLON
+FE55 SMALL COLON
+FE56 SMALL QUESTION MARK
+FE57 SMALL EXCLAMATION MARK
+FE58 SMALL EM DASH
+FE59 SMALL LEFT PARENTHESIS
+FE5A SMALL RIGHT PARENTHESIS
+FE5B SMALL LEFT CURLY BRACKET
+FE5C SMALL RIGHT CURLY BRACKET
+FE5D SMALL LEFT TORTOISE SHELL BRACKET
+FE5E SMALL RIGHT TORTOISE SHELL BRACKET
+FE5F SMALL NUMBER SIGN
+FE60 SMALL AMPERSAND
+FE61 SMALL ASTERISK
+FE62 SMALL PLUS SIGN
+FE63 SMALL HYPHEN-MINUS
+FE64 SMALL LESS-THAN SIGN
+FE65 SMALL GREATER-THAN SIGN
+FE66 SMALL EQUALS SIGN
+FE68 SMALL REVERSE SOLIDUS
+FE69 SMALL DOLLAR SIGN
+FE6A SMALL PERCENT SIGN
+FE6B SMALL COMMERCIAL AT
+FE70 ARABIC FATHATAN ISOLATED FORM
+FE71 ARABIC TATWEEL WITH FATHATAN ABOVE
+FE72 ARABIC DAMMATAN ISOLATED FORM
+FE74 ARABIC KASRATAN ISOLATED FORM
+FE76 ARABIC FATHA ISOLATED FORM
+FE77 ARABIC FATHA MEDIAL FORM
+FE78 ARABIC DAMMA ISOLATED FORM
+FE79 ARABIC DAMMA MEDIAL FORM
+FE7A ARABIC KASRA ISOLATED FORM
+FE7B ARABIC KASRA MEDIAL FORM
+FE7C ARABIC SHADDA ISOLATED FORM
+FE7D ARABIC SHADDA MEDIAL FORM
+FE7E ARABIC SUKUN ISOLATED FORM
+FE7F ARABIC SUKUN MEDIAL FORM
+FE80 ARABIC LETTER HAMZA ISOLATED FORM
+FE81 ARABIC LETTER ALEF WITH MADDA ABOVE ISOLATED FORM
+FE82 ARABIC LETTER ALEF WITH MADDA ABOVE FINAL FORM
+FE83 ARABIC LETTER ALEF WITH HAMZA ABOVE ISOLATED FORM
+FE84 ARABIC LETTER ALEF WITH HAMZA ABOVE FINAL FORM
+FE85 ARABIC LETTER WAW WITH HAMZA ABOVE ISOLATED FORM
+FE86 ARABIC LETTER WAW WITH HAMZA ABOVE FINAL FORM
+FE87 ARABIC LETTER ALEF WITH HAMZA BELOW ISOLATED FORM
+FE88 ARABIC LETTER ALEF WITH HAMZA BELOW FINAL FORM
+FE89 ARABIC LETTER YEH WITH HAMZA ABOVE ISOLATED FORM
+FE8A ARABIC LETTER YEH WITH HAMZA ABOVE FINAL FORM
+FE8B ARABIC LETTER YEH WITH HAMZA ABOVE INITIAL FORM
+FE8C ARABIC LETTER YEH WITH HAMZA ABOVE MEDIAL FORM
+FE8D ARABIC LETTER ALEF ISOLATED FORM
+FE8E ARABIC LETTER ALEF FINAL FORM
+FE8F ARABIC LETTER BEH ISOLATED FORM
+FE90 ARABIC LETTER BEH FINAL FORM
+FE91 ARABIC LETTER BEH INITIAL FORM
+FE92 ARABIC LETTER BEH MEDIAL FORM
+FE93 ARABIC LETTER TEH MARBUTA ISOLATED FORM
+FE94 ARABIC LETTER TEH MARBUTA FINAL FORM
+FE95 ARABIC LETTER TEH ISOLATED FORM
+FE96 ARABIC LETTER TEH FINAL FORM
+FE97 ARABIC LETTER TEH INITIAL FORM
+FE98 ARABIC LETTER TEH MEDIAL FORM
+FE99 ARABIC LETTER THEH ISOLATED FORM
+FE9A ARABIC LETTER THEH FINAL FORM
+FE9B ARABIC LETTER THEH INITIAL FORM
+FE9C ARABIC LETTER THEH MEDIAL FORM
+FE9D ARABIC LETTER JEEM ISOLATED FORM
+FE9E ARABIC LETTER JEEM FINAL FORM
+FE9F ARABIC LETTER JEEM INITIAL FORM
+FEA0 ARABIC LETTER JEEM MEDIAL FORM
+FEA1 ARABIC LETTER HAH ISOLATED FORM
+FEA2 ARABIC LETTER HAH FINAL FORM
+FEA3 ARABIC LETTER HAH INITIAL FORM
+FEA4 ARABIC LETTER HAH MEDIAL FORM
+FEA5 ARABIC LETTER KHAH ISOLATED FORM
+FEA6 ARABIC LETTER KHAH FINAL FORM
+FEA7 ARABIC LETTER KHAH INITIAL FORM
+FEA8 ARABIC LETTER KHAH MEDIAL FORM
+FEA9 ARABIC LETTER DAL ISOLATED FORM
+FEAA ARABIC LETTER DAL FINAL FORM
+FEAB ARABIC LETTER THAL ISOLATED FORM
+FEAC ARABIC LETTER THAL FINAL FORM
+FEAD ARABIC LETTER REH ISOLATED FORM
+FEAE ARABIC LETTER REH FINAL FORM
+FEAF ARABIC LETTER ZAIN ISOLATED FORM
+FEB0 ARABIC LETTER ZAIN FINAL FORM
+FEB1 ARABIC LETTER SEEN ISOLATED FORM
+FEB2 ARABIC LETTER SEEN FINAL FORM
+FEB3 ARABIC LETTER SEEN INITIAL FORM
+FEB4 ARABIC LETTER SEEN MEDIAL FORM
+FEB5 ARABIC LETTER SHEEN ISOLATED FORM
+FEB6 ARABIC LETTER SHEEN FINAL FORM
+FEB7 ARABIC LETTER SHEEN INITIAL FORM
+FEB8 ARABIC LETTER SHEEN MEDIAL FORM
+FEB9 ARABIC LETTER SAD ISOLATED FORM
+FEBA ARABIC LETTER SAD FINAL FORM
+FEBB ARABIC LETTER SAD INITIAL FORM
+FEBC ARABIC LETTER SAD MEDIAL FORM
+FEBD ARABIC LETTER DAD ISOLATED FORM
+FEBE ARABIC LETTER DAD FINAL FORM
+FEBF ARABIC LETTER DAD INITIAL FORM
+FEC0 ARABIC LETTER DAD MEDIAL FORM
+FEC1 ARABIC LETTER TAH ISOLATED FORM
+FEC2 ARABIC LETTER TAH FINAL FORM
+FEC3 ARABIC LETTER TAH INITIAL FORM
+FEC4 ARABIC LETTER TAH MEDIAL FORM
+FEC5 ARABIC LETTER ZAH ISOLATED FORM
+FEC6 ARABIC LETTER ZAH FINAL FORM
+FEC7 ARABIC LETTER ZAH INITIAL FORM
+FEC8 ARABIC LETTER ZAH MEDIAL FORM
+FEC9 ARABIC LETTER AIN ISOLATED FORM
+FECA ARABIC LETTER AIN FINAL FORM
+FECB ARABIC LETTER AIN INITIAL FORM
+FECC ARABIC LETTER AIN MEDIAL FORM
+FECD ARABIC LETTER GHAIN ISOLATED FORM
+FECE ARABIC LETTER GHAIN FINAL FORM
+FECF ARABIC LETTER GHAIN INITIAL FORM
+FED0 ARABIC LETTER GHAIN MEDIAL FORM
+FED1 ARABIC LETTER FEH ISOLATED FORM
+FED2 ARABIC LETTER FEH FINAL FORM
+FED3 ARABIC LETTER FEH INITIAL FORM
+FED4 ARABIC LETTER FEH MEDIAL FORM
+FED5 ARABIC LETTER QAF ISOLATED FORM
+FED6 ARABIC LETTER QAF FINAL FORM
+FED7 ARABIC LETTER QAF INITIAL FORM
+FED8 ARABIC LETTER QAF MEDIAL FORM
+FED9 ARABIC LETTER KAF ISOLATED FORM
+FEDA ARABIC LETTER KAF FINAL FORM
+FEDB ARABIC LETTER KAF INITIAL FORM
+FEDC ARABIC LETTER KAF MEDIAL FORM
+FEDD ARABIC LETTER LAM ISOLATED FORM
+FEDE ARABIC LETTER LAM FINAL FORM
+FEDF ARABIC LETTER LAM INITIAL FORM
+FEE0 ARABIC LETTER LAM MEDIAL FORM
+FEE1 ARABIC LETTER MEEM ISOLATED FORM
+FEE2 ARABIC LETTER MEEM FINAL FORM
+FEE3 ARABIC LETTER MEEM INITIAL FORM
+FEE4 ARABIC LETTER MEEM MEDIAL FORM
+FEE5 ARABIC LETTER NOON ISOLATED FORM
+FEE6 ARABIC LETTER NOON FINAL FORM
+FEE7 ARABIC LETTER NOON INITIAL FORM
+FEE8 ARABIC LETTER NOON MEDIAL FORM
+FEE9 ARABIC LETTER HEH ISOLATED FORM
+FEEA ARABIC LETTER HEH FINAL FORM
+FEEB ARABIC LETTER HEH INITIAL FORM
+FEEC ARABIC LETTER HEH MEDIAL FORM
+FEED ARABIC LETTER WAW ISOLATED FORM
+FEEE ARABIC LETTER WAW FINAL FORM
+FEEF ARABIC LETTER ALEF MAKSURA ISOLATED FORM
+FEF0 ARABIC LETTER ALEF MAKSURA FINAL FORM
+FEF1 ARABIC LETTER YEH ISOLATED FORM
+FEF2 ARABIC LETTER YEH FINAL FORM
+FEF3 ARABIC LETTER YEH INITIAL FORM
+FEF4 ARABIC LETTER YEH MEDIAL FORM
+FEF5 ARABIC LIGATURE LAM WITH ALEF WITH MADDA ABOVE ISOLATED FORM
+FEF6 ARABIC LIGATURE LAM WITH ALEF WITH MADDA ABOVE FINAL FORM
+FEF7 ARABIC LIGATURE LAM WITH ALEF WITH HAMZA ABOVE ISOLATED FORM
+FEF8 ARABIC LIGATURE LAM WITH ALEF WITH HAMZA ABOVE FINAL FORM
+FEF9 ARABIC LIGATURE LAM WITH ALEF WITH HAMZA BELOW ISOLATED FORM
+FEFA ARABIC LIGATURE LAM WITH ALEF WITH HAMZA BELOW FINAL FORM
+FEFB ARABIC LIGATURE LAM WITH ALEF ISOLATED FORM
+FEFC ARABIC LIGATURE LAM WITH ALEF FINAL FORM
+FEFF ZERO WIDTH NO-BREAK SPACE
+FF01 FULLWIDTH EXCLAMATION MARK
+FF02 FULLWIDTH QUOTATION MARK
+FF03 FULLWIDTH NUMBER SIGN
+FF04 FULLWIDTH DOLLAR SIGN
+FF05 FULLWIDTH PERCENT SIGN
+FF06 FULLWIDTH AMPERSAND
+FF07 FULLWIDTH APOSTROPHE
+FF08 FULLWIDTH LEFT PARENTHESIS
+FF09 FULLWIDTH RIGHT PARENTHESIS
+FF0A FULLWIDTH ASTERISK
+FF0B FULLWIDTH PLUS SIGN
+FF0C FULLWIDTH COMMA
+FF0D FULLWIDTH HYPHEN-MINUS
+FF0E FULLWIDTH FULL STOP
+FF0F FULLWIDTH SOLIDUS
+FF10 FULLWIDTH DIGIT ZERO
+FF11 FULLWIDTH DIGIT ONE
+FF12 FULLWIDTH DIGIT TWO
+FF13 FULLWIDTH DIGIT THREE
+FF14 FULLWIDTH DIGIT FOUR
+FF15 FULLWIDTH DIGIT FIVE
+FF16 FULLWIDTH DIGIT SIX
+FF17 FULLWIDTH DIGIT SEVEN
+FF18 FULLWIDTH DIGIT EIGHT
+FF19 FULLWIDTH DIGIT NINE
+FF1A FULLWIDTH COLON
+FF1B FULLWIDTH SEMICOLON
+FF1C FULLWIDTH LESS-THAN SIGN
+FF1D FULLWIDTH EQUALS SIGN
+FF1E FULLWIDTH GREATER-THAN SIGN
+FF1F FULLWIDTH QUESTION MARK
+FF20 FULLWIDTH COMMERCIAL AT
+FF21 FULLWIDTH LATIN CAPITAL LETTER A
+FF22 FULLWIDTH LATIN CAPITAL LETTER B
+FF23 FULLWIDTH LATIN CAPITAL LETTER C
+FF24 FULLWIDTH LATIN CAPITAL LETTER D
+FF25 FULLWIDTH LATIN CAPITAL LETTER E
+FF26 FULLWIDTH LATIN CAPITAL LETTER F
+FF27 FULLWIDTH LATIN CAPITAL LETTER G
+FF28 FULLWIDTH LATIN CAPITAL LETTER H
+FF29 FULLWIDTH LATIN CAPITAL LETTER I
+FF2A FULLWIDTH LATIN CAPITAL LETTER J
+FF2B FULLWIDTH LATIN CAPITAL LETTER K
+FF2C FULLWIDTH LATIN CAPITAL LETTER L
+FF2D FULLWIDTH LATIN CAPITAL LETTER M
+FF2E FULLWIDTH LATIN CAPITAL LETTER N
+FF2F FULLWIDTH LATIN CAPITAL LETTER O
+FF30 FULLWIDTH LATIN CAPITAL LETTER P
+FF31 FULLWIDTH LATIN CAPITAL LETTER Q
+FF32 FULLWIDTH LATIN CAPITAL LETTER R
+FF33 FULLWIDTH LATIN CAPITAL LETTER S
+FF34 FULLWIDTH LATIN CAPITAL LETTER T
+FF35 FULLWIDTH LATIN CAPITAL LETTER U
+FF36 FULLWIDTH LATIN CAPITAL LETTER V
+FF37 FULLWIDTH LATIN CAPITAL LETTER W
+FF38 FULLWIDTH LATIN CAPITAL LETTER X
+FF39 FULLWIDTH LATIN CAPITAL LETTER Y
+FF3A FULLWIDTH LATIN CAPITAL LETTER Z
+FF3B FULLWIDTH LEFT SQUARE BRACKET
+FF3C FULLWIDTH REVERSE SOLIDUS
+FF3D FULLWIDTH RIGHT SQUARE BRACKET
+FF3E FULLWIDTH CIRCUMFLEX ACCENT
+FF3F FULLWIDTH LOW LINE
+FF40 FULLWIDTH GRAVE ACCENT
+FF41 FULLWIDTH LATIN SMALL LETTER A
+FF42 FULLWIDTH LATIN SMALL LETTER B
+FF43 FULLWIDTH LATIN SMALL LETTER C
+FF44 FULLWIDTH LATIN SMALL LETTER D
+FF45 FULLWIDTH LATIN SMALL LETTER E
+FF46 FULLWIDTH LATIN SMALL LETTER F
+FF47 FULLWIDTH LATIN SMALL LETTER G
+FF48 FULLWIDTH LATIN SMALL LETTER H
+FF49 FULLWIDTH LATIN SMALL LETTER I
+FF4A FULLWIDTH LATIN SMALL LETTER J
+FF4B FULLWIDTH LATIN SMALL LETTER K
+FF4C FULLWIDTH LATIN SMALL LETTER L
+FF4D FULLWIDTH LATIN SMALL LETTER M
+FF4E FULLWIDTH LATIN SMALL LETTER N
+FF4F FULLWIDTH LATIN SMALL LETTER O
+FF50 FULLWIDTH LATIN SMALL LETTER P
+FF51 FULLWIDTH LATIN SMALL LETTER Q
+FF52 FULLWIDTH LATIN SMALL LETTER R
+FF53 FULLWIDTH LATIN SMALL LETTER S
+FF54 FULLWIDTH LATIN SMALL LETTER T
+FF55 FULLWIDTH LATIN SMALL LETTER U
+FF56 FULLWIDTH LATIN SMALL LETTER V
+FF57 FULLWIDTH LATIN SMALL LETTER W
+FF58 FULLWIDTH LATIN SMALL LETTER X
+FF59 FULLWIDTH LATIN SMALL LETTER Y
+FF5A FULLWIDTH LATIN SMALL LETTER Z
+FF5B FULLWIDTH LEFT CURLY BRACKET
+FF5C FULLWIDTH VERTICAL LINE
+FF5D FULLWIDTH RIGHT CURLY BRACKET
+FF5E FULLWIDTH TILDE
+FF61 HALFWIDTH IDEOGRAPHIC FULL STOP
+FF62 HALFWIDTH LEFT CORNER BRACKET
+FF63 HALFWIDTH RIGHT CORNER BRACKET
+FF64 HALFWIDTH IDEOGRAPHIC COMMA
+FF65 HALFWIDTH KATAKANA MIDDLE DOT
+FF66 HALFWIDTH KATAKANA LETTER WO
+FF67 HALFWIDTH KATAKANA LETTER SMALL A
+FF68 HALFWIDTH KATAKANA LETTER SMALL I
+FF69 HALFWIDTH KATAKANA LETTER SMALL U
+FF6A HALFWIDTH KATAKANA LETTER SMALL E
+FF6B HALFWIDTH KATAKANA LETTER SMALL O
+FF6C HALFWIDTH KATAKANA LETTER SMALL YA
+FF6D HALFWIDTH KATAKANA LETTER SMALL YU
+FF6E HALFWIDTH KATAKANA LETTER SMALL YO
+FF6F HALFWIDTH KATAKANA LETTER SMALL TU
+FF70 HALFWIDTH KATAKANA-HIRAGANA PROLONGED SOUND MARK
+FF71 HALFWIDTH KATAKANA LETTER A
+FF72 HALFWIDTH KATAKANA LETTER I
+FF73 HALFWIDTH KATAKANA LETTER U
+FF74 HALFWIDTH KATAKANA LETTER E
+FF75 HALFWIDTH KATAKANA LETTER O
+FF76 HALFWIDTH KATAKANA LETTER KA
+FF77 HALFWIDTH KATAKANA LETTER KI
+FF78 HALFWIDTH KATAKANA LETTER KU
+FF79 HALFWIDTH KATAKANA LETTER KE
+FF7A HALFWIDTH KATAKANA LETTER KO
+FF7B HALFWIDTH KATAKANA LETTER SA
+FF7C HALFWIDTH KATAKANA LETTER SI
+FF7D HALFWIDTH KATAKANA LETTER SU
+FF7E HALFWIDTH KATAKANA LETTER SE
+FF7F HALFWIDTH KATAKANA LETTER SO
+FF80 HALFWIDTH KATAKANA LETTER TA
+FF81 HALFWIDTH KATAKANA LETTER TI
+FF82 HALFWIDTH KATAKANA LETTER TU
+FF83 HALFWIDTH KATAKANA LETTER TE
+FF84 HALFWIDTH KATAKANA LETTER TO
+FF85 HALFWIDTH KATAKANA LETTER NA
+FF86 HALFWIDTH KATAKANA LETTER NI
+FF87 HALFWIDTH KATAKANA LETTER NU
+FF88 HALFWIDTH KATAKANA LETTER NE
+FF89 HALFWIDTH KATAKANA LETTER NO
+FF8A HALFWIDTH KATAKANA LETTER HA
+FF8B HALFWIDTH KATAKANA LETTER HI
+FF8C HALFWIDTH KATAKANA LETTER HU
+FF8D HALFWIDTH KATAKANA LETTER HE
+FF8E HALFWIDTH KATAKANA LETTER HO
+FF8F HALFWIDTH KATAKANA LETTER MA
+FF90 HALFWIDTH KATAKANA LETTER MI
+FF91 HALFWIDTH KATAKANA LETTER MU
+FF92 HALFWIDTH KATAKANA LETTER ME
+FF93 HALFWIDTH KATAKANA LETTER MO
+FF94 HALFWIDTH KATAKANA LETTER YA
+FF95 HALFWIDTH KATAKANA LETTER YU
+FF96 HALFWIDTH KATAKANA LETTER YO
+FF97 HALFWIDTH KATAKANA LETTER RA
+FF98 HALFWIDTH KATAKANA LETTER RI
+FF99 HALFWIDTH KATAKANA LETTER RU
+FF9A HALFWIDTH KATAKANA LETTER RE
+FF9B HALFWIDTH KATAKANA LETTER RO
+FF9C HALFWIDTH KATAKANA LETTER WA
+FF9D HALFWIDTH KATAKANA LETTER N
+FF9E HALFWIDTH KATAKANA VOICED SOUND MARK
+FF9F HALFWIDTH KATAKANA SEMI-VOICED SOUND MARK
+FFA0 HALFWIDTH HANGUL FILLER
+FFA1 HALFWIDTH HANGUL LETTER KIYEOK
+FFA2 HALFWIDTH HANGUL LETTER SSANGKIYEOK
+FFA3 HALFWIDTH HANGUL LETTER KIYEOK-SIOS
+FFA4 HALFWIDTH HANGUL LETTER NIEUN
+FFA5 HALFWIDTH HANGUL LETTER NIEUN-CIEUC
+FFA6 HALFWIDTH HANGUL LETTER NIEUN-HIEUH
+FFA7 HALFWIDTH HANGUL LETTER TIKEUT
+FFA8 HALFWIDTH HANGUL LETTER SSANGTIKEUT
+FFA9 HALFWIDTH HANGUL LETTER RIEUL
+FFAA HALFWIDTH HANGUL LETTER RIEUL-KIYEOK
+FFAB HALFWIDTH HANGUL LETTER RIEUL-MIEUM
+FFAC HALFWIDTH HANGUL LETTER RIEUL-PIEUP
+FFAD HALFWIDTH HANGUL LETTER RIEUL-SIOS
+FFAE HALFWIDTH HANGUL LETTER RIEUL-THIEUTH
+FFAF HALFWIDTH HANGUL LETTER RIEUL-PHIEUPH
+FFB0 HALFWIDTH HANGUL LETTER RIEUL-HIEUH
+FFB1 HALFWIDTH HANGUL LETTER MIEUM
+FFB2 HALFWIDTH HANGUL LETTER PIEUP
+FFB3 HALFWIDTH HANGUL LETTER SSANGPIEUP
+FFB4 HALFWIDTH HANGUL LETTER PIEUP-SIOS
+FFB5 HALFWIDTH HANGUL LETTER SIOS
+FFB6 HALFWIDTH HANGUL LETTER SSANGSIOS
+FFB7 HALFWIDTH HANGUL LETTER IEUNG
+FFB8 HALFWIDTH HANGUL LETTER CIEUC
+FFB9 HALFWIDTH HANGUL LETTER SSANGCIEUC
+FFBA HALFWIDTH HANGUL LETTER CHIEUCH
+FFBB HALFWIDTH HANGUL LETTER KHIEUKH
+FFBC HALFWIDTH HANGUL LETTER THIEUTH
+FFBD HALFWIDTH HANGUL LETTER PHIEUPH
+FFBE HALFWIDTH HANGUL LETTER HIEUH
+FFC2 HALFWIDTH HANGUL LETTER A
+FFC3 HALFWIDTH HANGUL LETTER AE
+FFC4 HALFWIDTH HANGUL LETTER YA
+FFC5 HALFWIDTH HANGUL LETTER YAE
+FFC6 HALFWIDTH HANGUL LETTER EO
+FFC7 HALFWIDTH HANGUL LETTER E
+FFCA HALFWIDTH HANGUL LETTER YEO
+FFCB HALFWIDTH HANGUL LETTER YE
+FFCC HALFWIDTH HANGUL LETTER O
+FFCD HALFWIDTH HANGUL LETTER WA
+FFCE HALFWIDTH HANGUL LETTER WAE
+FFCF HALFWIDTH HANGUL LETTER OE
+FFD2 HALFWIDTH HANGUL LETTER YO
+FFD3 HALFWIDTH HANGUL LETTER U
+FFD4 HALFWIDTH HANGUL LETTER WEO
+FFD5 HALFWIDTH HANGUL LETTER WE
+FFD6 HALFWIDTH HANGUL LETTER WI
+FFD7 HALFWIDTH HANGUL LETTER YU
+FFDA HALFWIDTH HANGUL LETTER EU
+FFDB HALFWIDTH HANGUL LETTER YI
+FFDC HALFWIDTH HANGUL LETTER I
+FFE0 FULLWIDTH CENT SIGN
+FFE1 FULLWIDTH POUND SIGN
+FFE2 FULLWIDTH NOT SIGN
+FFE3 FULLWIDTH MACRON
+FFE4 FULLWIDTH BROKEN BAR
+FFE5 FULLWIDTH YEN SIGN
+FFE6 FULLWIDTH WON SIGN
+FFE8 HALFWIDTH FORMS LIGHT VERTICAL
+FFE9 HALFWIDTH LEFTWARDS ARROW
+FFEA HALFWIDTH UPWARDS ARROW
+FFEB HALFWIDTH RIGHTWARDS ARROW
+FFEC HALFWIDTH DOWNWARDS ARROW
+FFED HALFWIDTH BLACK SQUARE
+FFEE HALFWIDTH WHITE CIRCLE
+FFFC OBJECT REPLACEMENT CHARACTER
+FFFD REPLACEMENT CHARACTER
+"""
+
+def _makeunicode():
+ from string import split, atoi
+ import re
+ firstRE = re.compile("<(.*?), First>")
+ firstREmatch = firstRE.match
+ lastRE = re.compile("<(.*?), Last>")
+ lastREmatch = lastRE.match
+
+ lines = split(_unicode, '\n')
+ while not lines[-1]:
+ del lines[-1] # empty string
+
+ unicode = ['????'] * 0x10000
+ i = 0
+ lenLines = len(lines)
+ while i < lenLines:
+ line = lines[i]
+ num, name = split(line, '\t')
+ num = atoi(num, 16)
+ if firstREmatch(name) is not None:
+ i = i + 1
+ line = lines[i]
+ numLast, nameLast = split(line, '\t')
+ m = lastREmatch(nameLast)
+ assert m is not None
+ name = m.group(1)
+ numLast = atoi(numLast, 16)
+ unicode[num:numLast+1] = [name] * (numLast - num + 1)
+ else:
+ unicode[num] = name
+ i = i + 1
+ while not unicode[-1]:
+ del unicode[-1] # empty string
+ return unicode
+
+
+class _Unicode:
+
+ def __init__(self):
+ self.codes = _makeunicode()
+
+ def __getitem__(self, charCode):
+ if charCode > 0xffff:
+ raise IndexError, "charCode out of range"
+ try:
+ return self.codes[charCode]
+ except IndexError:
+ return "????"
+
+
+Unicode = _Unicode()