diff --git a/Lib/fontTools/__init__.py b/Lib/fontTools/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/Lib/fontTools/afmLib.py b/Lib/fontTools/afmLib.py new file mode 100644 index 000000000..a725e8326 --- /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 '' % self.FullName + else: + return '' % 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 000000000..6ddc1e75d --- /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 +# 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 000000000..76476c332 --- /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 000000000..94468cb60 --- /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 "" % 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 000000000..e69de29bb diff --git a/Lib/fontTools/misc/textTools.py b/Lib/fontTools/misc/textTools.py new file mode 100644 index 000000000..304f45ad5 --- /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 000000000..08a54bab2 --- /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 000000000..d61878435 --- /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 000000000..840868092 --- /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 000000000..533f484a7 --- /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 "" % self.name + +class ps_procedure(ps_object): + literal = 0 + def __repr__(self): + return "" + 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 "" + +_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 '' + +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 "" + +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 000000000..a2c63fbb4 --- /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 000000000..e0874659b --- /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 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 000000000..bace3b4a7 --- /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 000000000..2fdeb99f0 --- /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 "" % (self.tag, id(self)) + else: + return "" % 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 000000000..4ef2eb460 --- /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 000000000..8148f0662 --- /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 000000000..bea5654b0 --- /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 000000000..745f237fc --- /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 000000000..7bec887f5 --- /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 "" + 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 000000000..d0c4dce92 --- /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 000000000..20701d61b --- /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 000000000..498275fbb --- /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 000000000..988d29a1e --- /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 000000000..8ee4e160e --- /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 000000000..15c02abaa --- /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 000000000..eb4087cfb --- /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 000000000..35b9ee12d --- /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 000000000..2f1763704 --- /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 000000000..b067ac66b --- /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 000000000..ced5523a9 --- /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 000000000..f7cc8a3d7 --- /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 000000000..e49ccebde --- /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 000000000..29f9be514 --- /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 000000000..e5c396c4b --- /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 000000000..fc33edbe7 --- /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 ! + 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 000000000..fca75cb39 --- /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 000000000..35fa8cd37 --- /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 000000000..283c52fbf --- /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 000000000..332a70892 --- /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 000000000..1c75e6d5e --- /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 000000000..05cde335a --- /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 "" % ( + 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 000000000..273de795c --- /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 000000000..005c8eee4 --- /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 000000000..43fd6143c --- /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 000000000..dabefe603 --- /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 000000000..c312c6376 --- /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 000000000..2c04bf857 --- /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') + +>>> ttLib.getTableClass('glyf') + +>>> + +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 000000000..8dd9faa6b --- /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 000000000..1c5437594 --- /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 000000000..92d3b425d --- /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 000000000..dc6ba0cc7 --- /dev/null +++ b/Lib/fontTools/unicode.py @@ -0,0 +1,6646 @@ +"""Unicode version 2.1.2""" + +_unicode = """\ +0000 +0001 +0002 +0003 +0004 +0005 +0006 +0007 +0008 +0009 +000A +000B +000C +000D +000E +000F +0010 +0011 +0012 +0013 +0014 +0015 +0016 +0017 +0018 +0019 +001A +001B +001C +001D +001E +001F +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 +0080 +0081 +0082 +0083 +0084 +0085 +0086 +0087 +0088 +0089 +008A +008B +008C +008D +008E +008F +0090 +0091 +0092 +0093 +0094 +0095 +0096 +0097 +0098 +0099 +009A +009B +009C +009D +009E +009F +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 +9FA5 +AC00 +D7A3 +D800 +DB7F +DB80 +DBFF +DC00 +DFFF +E000 +F8FF +F900 +FA2D +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() diff --git a/Lib/sstruct.py b/Lib/sstruct.py new file mode 100644 index 000000000..7bc4772e0 --- /dev/null +++ b/Lib/sstruct.py @@ -0,0 +1,204 @@ +"""sstruct.py -- SuperStruct + +Higher level layer on top of the struct module, enabling to +bind names to struct elements. The interface is similar to +struct, except the objects passed and returned are not tuples +(or argument lists), but dictionaries or instances. + +Just like struct, we use format strings to describe a data +structure, except we use one line per element. Lines are +separated by newlines or semi-colons. Each line contains +either one of the special struct characters ('@', '=', '<', +'>' or '!') or a 'name:formatchar' combo (eg. 'myFloat:f'). +Repetitions, like the struct module offers them are not useful +in this context, except for fixed length strings (eg. 'myInt:5h' +is not allowed but 'myString:5s' is). The 'x' format character +(pad byte) is treated as 'special', since it is by definition +anonymous. Extra whitespace is allowed everywhere. + +The sstruct module offers one feature that the "normal" struct +module doesn't: support for fixed point numbers. These are spelled +as "n.mF", where n is the number of bits before the point, and m +the number of bits after the point. Fixed point numbers get +converted to floats. + +pack(format, object): + 'object' is either a dictionary or an instance (or actually + anything that has a __dict__ attribute). If it is a dictionary, + its keys are used for names. If it is an instance, it's + attributes are used to grab struct elements from. Returns + a string containing the data. + +unpack(format, data, object=None) + If 'object' is omitted (or None), a new dictionary will be + returned. If 'object' is a dictionary, it will be used to add + struct elements to. If it is an instance (or in fact anything + that has a __dict__ attribute), an attribute will be added for + each struct element. In the latter two cases, 'object' itself + is returned. + +unpack2(format, data, object=None) + Convenience function. Same as unpack, except data may be longer + than needed. The returned value is a tuple: (object, leftoverdata). + +calcsize(format) + like struct.calcsize(), but uses our own format strings: + it returns the size of the data in bytes. +""" + +# XXX I would like to support pascal strings, too, but I'm not +# sure if that's wise. Would be nice if struct supported them +# "properly", but that would certainly break calcsize()... + +__version__ = "1.2" +__copyright__ = "Copyright 1998, Just van Rossum " + +import struct +import re +import types + + +error = "sstruct.error" + +def pack(format, object): + formatstring, names, fixes = getformat(format) + elements = [] + if type(object) is not types.DictType: + object = object.__dict__ + for name in names: + value = object[name] + if fixes.has_key(name): + # fixed point conversion + value = int(round(value*fixes[name])) + elements.append(value) + data = apply(struct.pack, (formatstring,) + tuple(elements)) + return data + +def unpack(format, data, object=None): + if object is None: + object = {} + formatstring, names, fixes = getformat(format) + if type(object) is types.DictType: + dict = object + else: + dict = object.__dict__ + elements = struct.unpack(formatstring, data) + for i in range(len(names)): + name = names[i] + value = elements[i] + if fixes.has_key(name): + # fixed point conversion + value = value / fixes[name] + dict[name] = value + return object + +def unpack2(format, data, object=None): + length = calcsize(format) + return unpack(format, data[:length], object), data[length:] + +def calcsize(format): + formatstring, names, fixes = getformat(format) + return struct.calcsize(formatstring) + + +# matches "name:formatchar" (whitespace is allowed) +_elementRE = re.compile( + "\s*" # whitespace + "([A-Za-z_][A-Za-z_0-9]*)" # name (python identifier) + "\s*:\s*" # whitespace : whitespace + "([cbBhHiIlLfd]|[0-9]+[ps]|" # formatchar... + "([0-9]+)\.([0-9]+)(F))" # ...formatchar + "\s*" # whitespace + "(#.*)?$" # [comment] + end of string + ) + +# matches the special struct format chars and 'x' (pad byte) +_extraRE = re.compile("\s*([x@=<>!])\s*(#.*)?$") + +# matches an "empty" string, possibly containing whitespace and/or a comment +_emptyRE = re.compile("\s*(#.*)?$") + +_fixedpointmappings = { + 8: "b", + 16: "h", + 32: "l"} + +_formatcache = {} + +def getformat(format): + try: + formatstring, names, fixes = _formatcache[format] + except KeyError: + lines = re.split("[\n;]", format) + formatstring = "" + names = [] + fixes = {} + for line in lines: + if _emptyRE.match(line): + continue + m = _extraRE.match(line) + if m: + formatchar = m.group(1) + if formatchar <> 'x' and formatstring: + raise error, "a special format char must be first" + else: + m = _elementRE.match(line) + if not m: + raise error, "syntax error in format: '%s'" % line + name = m.group(1) + names.append(name) + formatchar = m.group(2) + if m.group(3): + # fixed point + before = int(m.group(3)) + after = int(m.group(4)) + bits = before + after + if bits not in [8, 16, 32]: + raise error, "fixed point must be 8, 16 or 32 bits long" + formatchar = _fixedpointmappings[bits] + assert m.group(5) == "F" + fixes[name] = float(1 << after) + formatstring = formatstring + formatchar + _formatcache[format] = formatstring, names, fixes + return formatstring, names, fixes + +def _test(): + format = """ + # comments are allowed + > # big endian (see documentation for struct) + # empty lines are allowed: + + ashort: h + along: l + abyte: b # a byte + achar: c + astr: 5s + afloat: f; adouble: d # multiple "statements" are allowed + afixed: 16.16F + """ + + print 'size:', calcsize(format) + + class foo: + pass + + i = foo() + + i.ashort = 0x7fff + i.along = 0x7fffffff + i.abyte = 0x7f + i.achar = "a" + i.astr = "12345" + i.afloat = 0.5 + i.adouble = 0.5 + i.afixed = 1.5 + + data = pack(format, i) + print 'data:', `data` + print unpack(format, data) + i2 = foo() + unpack(format, data, i2) + print vars(i2) + +if __name__ == "__main__": + _test() diff --git a/Lib/xmlWriter.py b/Lib/xmlWriter.py new file mode 100644 index 000000000..b078959ca --- /dev/null +++ b/Lib/xmlWriter.py @@ -0,0 +1,172 @@ +"""xmlWriter.py -- Simple XML authoring class""" + +__author__ = "jvr" +__version__ = "0.9" + +import string +import struct + +INDENT = " " + +class XMLWriter: + + def __init__(self, file, dtd=None, indentwhite=INDENT): + if type(file) == type(""): + self.file = open(file, "w") + else: + # assume writable file object + self.file = file + self.dtd = dtd + self.indentwhite = indentwhite + self.indentlevel = 0 + self.stack = [] + self.needindent = 1 + self.writeraw("") + self.newline() + if self.dtd: + # DOCTYPE??? + self.newline() + + def close(self): + self.file.close() + + def write(self, data): + self.writeraw(escape(data)) + + def write_noindent(self, data): + self.file.write(escape(data)) + + def write8bit(self, data): + self.writeraw(escape8bit(data)) + + def write16bit(self, data): + self.writeraw(escape16bit(data)) + + def writeraw(self, data): + if self.needindent: + self.file.write(self.indentlevel * self.indentwhite) + self.needindent = 0 + self.file.write(data) + + def newline(self): + self.file.write("\n") + self.needindent = 1 + + def comment(self, data): + data = escape(data) + lines = string.split(data, "\n") + self.writeraw("") + + def simpletag(self, _TAG_, *args, **kwargs): + attrdata = apply(self.stringifyattrs, args, kwargs) + data = "<%s%s/>" % (_TAG_, attrdata) + self.writeraw(data) + + def begintag(self, _TAG_, *args, **kwargs): + attrdata = apply(self.stringifyattrs, args, kwargs) + data = "<%s%s>" % (_TAG_, attrdata) + self.writeraw(data) + self.stack.append(_TAG_) + self.indent() + + def endtag(self, _TAG_): + assert self.stack and self.stack[-1] == _TAG_, "nonmatching endtag" + del self.stack[-1] + self.dedent() + data = "" % _TAG_ + self.writeraw(data) + + def dumphex(self, data): + linelength = 16 + hexlinelength = linelength * 2 + chunksize = 8 + for i in range(0, len(data), linelength): + hexline = hexStr(data[i:i+linelength]) + line = "" + white = "" + for j in range(0, hexlinelength, chunksize): + line = line + white + hexline[j:j+chunksize] + white = " " + self.writeraw(line) + self.newline() + + def indent(self): + self.indentlevel = self.indentlevel + 1 + + def dedent(self): + assert self.indentlevel > 0 + self.indentlevel = self.indentlevel - 1 + + def stringifyattrs(self, *args, **kwargs): + if kwargs: + assert not args + attributes = kwargs.items() + attributes.sort() + elif args: + assert len(args) == 1 + attributes = args[0] + else: + return "" + data = "" + for attr, value in attributes: + data = data + ' %s="%s"' % (attr, escapeattr(str(value))) + return data + + +def escape(data): + data = string.replace(data, "&", "&") + data = string.replace(data, "<", "<") + return data + +def escapeattr(data): + data = string.replace(data, "&", "&") + data = string.replace(data, "<", "<") + data = string.replace(data, '"', """) + return data + +def escape8bit(data): + def escapechar(c): + n = ord(c) + if c in "<&": + if c == "&": + return "&" + else: + return "<" + elif 32 <= n <= 127: + return c + else: + return "&#" + `n` + ";" + return string.join(map(escapechar, data), "") + +needswap = struct.pack("h", 1) == "\001\000" + +def escape16bit(data): + import array + a = array.array("H") + a.fromstring(data) + if needswap: + a.byteswap() + def escapenum(n, amp=ord("&"), lt=ord("<")): + if n == amp: + return "&" + elif n == lt: + return "<" + elif 32 <= n <= 127: + return chr(n) + else: + return "&#" + `n` + ";" + return string.join(map(escapenum, a), "") + + +def hexStr(s): + h = string.hexdigits + r = '' + for c in s: + i = ord(c) + r = r + h[(i >> 4) & 0xF] + h[i & 0xF] + return r +