Merge branch 'merge'
diff --git a/Doc/documentation.html b/Doc/documentation.html
index ac5dfcb..e2821fe 100644
--- a/Doc/documentation.html
+++ b/Doc/documentation.html
@@ -42,7 +42,7 @@
The following tables are currently supported:
<BLOCKQUOTE><TT>
<!-- begin table list -->
-BASE, CBDT, CBLC, CFF, COLR, CPAL, DSIG, EBDT, EBLC, GDEF, GMAP, GPKG, GPOS, GSUB, JSTF, LTSH, META, OS/2, SING, SVG, TSI0, TSI1, TSI2, TSI3, TSI5, TSIB, TSID, TSIJ, TSIP, TSIS, TSIV, VORG, cmap, cvt, fpgm, gasp, glyf, hdmx, head, hhea, hmtx, kern, loca, maxp, name, post, prep, vhea and vmtx
+BASE, CBDT, CBLC, CFF, COLR, CPAL, DSIG, EBDT, EBLC, FFTM, GDEF, GMAP, GPKG, GPOS, GSUB, JSTF, LTSH, META, OS/2, SING, SVG, TSI0, TSI1, TSI2, TSI3, TSI5, TSIB, TSID, TSIJ, TSIP, TSIS, TSIV, VORG, cmap, cvt, fpgm, gasp, glyf, hdmx, head, hhea, hmtx, kern, loca, maxp, name, post, prep, sbix, vhea and vmtx
<!-- end table list -->
</TT></BLOCKQUOTE>
Other tables are dumped as hexadecimal data.
diff --git a/Doc/install.txt b/Doc/install.txt
index 9bb4573..f2f3b0c 100644
--- a/Doc/install.txt
+++ b/Doc/install.txt
@@ -68,9 +68,9 @@
contribute, you can also subscribe to the fonttools-checkins mailing list.
-Anonymous SVN-access
+Anonymous VCS access
-The FontTools sources are also accessible through SVN, see:
+The FontTools sources are also accessible here:
http://sourceforge.net/projects/fonttools/
Let me know if you'd like to become a co-developer.
diff --git a/Lib/fontTools/afmLib.py b/Lib/fontTools/afmLib.py
index aa303ad..2104b9e 100644
--- a/Lib/fontTools/afmLib.py
+++ b/Lib/fontTools/afmLib.py
@@ -4,12 +4,9 @@
# It does not implement the full spec (Adobe Technote 5004, Adobe Font Metrics
# File Format Specification). Still, it should read most "common" AFM files.
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
import re
-import string
-import types
-
-__version__ = "$Id: afmLib.py,v 1.6 2003-05-24 12:50:47 jvr Exp $"
-
# every single line starts with a "word"
identifierRE = re.compile("^([A-Za-z]+).*")
@@ -83,7 +80,7 @@
class error(Exception): pass
-class AFM:
+class AFM(object):
_attrs = None
@@ -112,15 +109,15 @@
def read(self, path):
lines = readlines(path)
for line in lines:
- if not string.strip(line):
+ if not line.strip():
continue
m = identifierRE.match(line)
if m is None:
- raise error, "syntax error in AFM file: " + `line`
+ raise error("syntax error in AFM file: " + repr(line))
pos = m.regs[1][1]
word = line[:pos]
- rest = string.strip(line[pos:])
+ rest = line[pos:].strip()
if word in self._keywords:
continue
if word == "C":
@@ -135,35 +132,35 @@
def parsechar(self, rest):
m = charRE.match(rest)
if m is None:
- raise error, "syntax error in AFM file: " + `rest`
+ raise error("syntax error in AFM file: " + repr(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)
+ charnum, width, l, b, r, t = (int(thing) for thing in 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`
+ raise error("syntax error in AFM file: " + repr(rest))
things = []
for fr, to in m.regs[1:]:
things.append(rest[fr:to])
leftchar, rightchar, value = things
- value = string.atoi(value)
+ value = int(value)
self._kerning[(leftchar, rightchar)] = value
def parseattr(self, word, rest):
if word == "FontBBox":
- l, b, r, t = map(string.atoi, string.split(rest))
+ l, b, r, t = [int(thing) for thing in rest.split()]
self._attrs[word] = l, b, r, t
elif word == "Comment":
self._comments.append(rest)
else:
try:
- value = string.atoi(rest)
+ value = int(rest)
except (ValueError, OverflowError):
self._attrs[word] = rest
else:
@@ -172,15 +169,15 @@
def parsecomposite(self, rest):
m = compositeRE.match(rest)
if m is None:
- raise error, "syntax error in AFM file: " + `rest`
+ raise error("syntax error in AFM file: " + repr(rest))
charname = m.group(1)
ncomponents = int(m.group(2))
rest = rest[m.regs[0][1]:]
components = []
- while 1:
+ while True:
m = componentRE.match(rest)
if m is None:
- raise error, "syntax error in AFM file: " + `rest`
+ raise error("syntax error in AFM file: " + repr(rest))
basechar = m.group(1)
xoffset = int(m.group(2))
yoffset = int(m.group(3))
@@ -194,8 +191,7 @@
def write(self, path, sep='\r'):
import time
lines = [ "StartFontMetrics 2.0",
- "Comment Generated by afmLib, version %s; at %s" %
- (string.split(__version__)[2],
+ "Comment Generated by afmLib; at %s" % (
time.strftime("%m/%d/%Y %H:%M:%S",
time.localtime(time.time())))]
@@ -208,35 +204,30 @@
# a preferred order
attrs = self._attrs
for attr in preferredAttributeOrder:
- if attrs.has_key(attr):
+ if attr in attrs:
value = attrs[attr]
if attr == "FontBBox":
value = "%s %s %s %s" % value
lines.append(attr + " " + str(value))
# then write the attributes we don't know about,
# in alphabetical order
- items = attrs.items()
- items.sort()
+ items = sorted(attrs.items())
for attr, value in items:
if attr in preferredAttributeOrder:
continue
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())
+ lines.append("StartCharMetrics " + repr(len(self._chars)))
+ items = [(charnum, (charname, width, box)) for charname, (charnum, width, box) in self._chars.items()]
- def myCmp(a, b):
- """Custom compare function to make sure unencoded chars (-1)
+ def myKey(a):
+ """Custom key 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)
+ return a
+ items.sort(key=myKey)
for charnum, (charname, width, (l, b, r, t)) in items:
lines.append("C %d ; WX %d ; N %s ; B %d %d %d %d ;" %
@@ -245,17 +236,15 @@
# write kerning info
lines.append("StartKernData")
- lines.append("StartKernPairs " + `len(self._kerning)`)
- items = self._kerning.items()
- items.sort() # XXX is order important?
+ lines.append("StartKernPairs " + repr(len(self._kerning)))
+ items = sorted(self._kerning.items())
for (leftchar, rightchar), value in items:
lines.append("KPX %s %s %d" % (leftchar, rightchar, value))
lines.append("EndKernPairs")
lines.append("EndKernData")
if self._composites:
- composites = self._composites.items()
- composites.sort()
+ composites = sorted(self._composites.items())
lines.append("StartComposites %s" % len(self._composites))
for charname, components in composites:
line = "CC %s %s ;" % (charname, len(components))
@@ -269,16 +258,16 @@
writelines(path, lines, sep)
def has_kernpair(self, pair):
- return self._kerning.has_key(pair)
+ return pair in self._kerning
def kernpairs(self):
- return self._kerning.keys()
+ return list(self._kerning.keys())
def has_char(self, char):
- return self._chars.has_key(char)
+ return char in self._chars
def chars(self):
- return self._chars.keys()
+ return list(self._chars.keys())
def comments(self):
return self._comments
@@ -290,10 +279,10 @@
self._composites[glyphName] = components
def __getattr__(self, attr):
- if self._attrs.has_key(attr):
+ if attr in self._attrs:
return self._attrs[attr]
else:
- raise AttributeError, attr
+ raise AttributeError(attr)
def __setattr__(self, attr, value):
# all attrs *not* starting with "_" are consider to be AFM keywords
@@ -308,15 +297,15 @@
try:
del self.__dict__[attr]
except KeyError:
- raise AttributeError, attr
+ raise AttributeError(attr)
else:
try:
del self._attrs[attr]
except KeyError:
- raise AttributeError, attr
+ raise AttributeError(attr)
def __getitem__(self, key):
- if type(key) == types.TupleType:
+ if isinstance(key, tuple):
# key is a tuple, return the kernpair
return self._kerning[key]
else:
@@ -324,7 +313,7 @@
return self._chars[key]
def __setitem__(self, key, value):
- if type(key) == types.TupleType:
+ if isinstance(key, tuple):
# key is a tuple, set kernpair
self._kerning[key] = value
else:
@@ -332,7 +321,7 @@
self._chars[key] = value
def __delitem__(self, key):
- if type(key) == types.TupleType:
+ if isinstance(key, tuple):
# key is a tuple, del kernpair
del self._kerning[key]
else:
@@ -356,7 +345,7 @@
sep = sep + '\r' # mac or dos
if '\n' in data:
sep = sep + '\n' # unix or dos
- return string.split(data, sep)
+ return data.split(sep)
def writelines(path, lines, sep='\r'):
f = open(path, 'wb')
@@ -373,16 +362,16 @@
afm = AFM(path)
char = 'A'
if afm.has_char(char):
- print afm[char] # print charnum, width and boundingbox
+ 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
+ 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.comments())
#print afm.chars()
#print afm.kernpairs()
- print afm
+ print(afm)
afm.write(path + ".muck")
diff --git a/Lib/fontTools/agl.py b/Lib/fontTools/agl.py
index dc371af..597df7d 100644
--- a/Lib/fontTools/agl.py
+++ b/Lib/fontTools/agl.py
@@ -1,103 +1,119 @@
# The table below is taken from
# http://www.adobe.com/devnet/opentype/archives/aglfn.txt
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
+
_aglText = """\
-# ###################################################################################
-# Copyright (c) 2003,2005,2006,2007 Adobe Systems Incorporated
-#
-# Permission is hereby granted, free of charge, to any person obtaining a
-# copy of this documentation file to use, copy, publish, distribute,
-# sublicense, and/or sell copies of the documentation, and to permit
-# others to do the same, provided that:
-# - No modification, editing or other alteration of this document is
-# allowed; and
-# - The above copyright notice and this permission notice shall be
-# included in all copies of the documentation.
-#
-# Permission is hereby granted, free of charge, to any person obtaining a
-# copy of this documentation file, to create their own derivative works
-# from the content of this document to use, copy, publish, distribute,
-# sublicense, and/or sell the derivative works, and to permit others to do
-# the same, provided that the derived work is not represented as being a
-# copy or version of this document.
-#
-# Adobe shall not be liable to any party for any loss of revenue or profit
-# or for indirect, incidental, special, consequential, or other similar
-# damages, whether based on tort (including without limitation negligence
-# or strict liability), contract or other legal or equitable grounds even
-# if Adobe has been advised or had reason to know of the possibility of
-# such damages. The Adobe materials are provided on an "AS IS" basis.
-# Adobe specifically disclaims all express, statutory, or implied
-# warranties relating to the Adobe materials, including but not limited to
-# those concerning merchantability or fitness for a particular purpose or
-# non-infringement of any third party rights regarding the Adobe
-# materials.
-# ###################################################################################
+# -----------------------------------------------------------
+# Copyright 2003, 2005-2008, 2010 Adobe Systems Incorporated.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or
+# without modification, are permitted provided that the
+# following conditions are met:
+#
+# Redistributions of source code must retain the above
+# copyright notice, this list of conditions and the following
+# disclaimer.
+#
+# Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following
+# disclaimer in the documentation and/or other materials
+# provided with the distribution.
+#
+# Neither the name of Adobe Systems Incorporated nor the names
+# of its contributors may be used to endorse or promote
+# products derived from this software without specific prior
+# written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+# CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+# -----------------------------------------------------------
# Name: Adobe Glyph List For New Fonts
-# Table version: 1.6
-# Date: 30 Januaury 2006
+# Table version: 1.7
+# Date: November 6, 2008
+# URL: http://sourceforge.net/adobe/aglfn/
#
# Description:
#
-# The Adobe Glyph List For New Fonts (AGLFN) is meant to provide a list of
-# base glyph names which are compatible with the AGL specification at
-# http://partners.adobe.com/asn/developer/type/unicodegn.html.
-# and which can be used as described in section 6 of that document.
+# AGLFN (Adobe Glyph List For New Fonts) provides a list of base glyph
+# names that are recommended for new fonts, which are compatible with
+# the AGL (Adobe Glyph List) Specification, and which should be used
+# as described in Section 6 of that document. AGLFN comprises the set
+# of glyph names from AGL that map via the AGL Specification rules to
+# the semantically correct UV (Unicode Value). For example, "Asmall"
+# is omitted because AGL maps this glyph name to the PUA (Private Use
+# Area) value U+F761, rather than to the UV that maps from the glyph
+# name "A." Also omitted is "ffi," because AGL maps this to the
+# Alphabetic Presentation Forms value U+FB03, rather than decomposing
+# it into the following sequence of three UVs: U+0066, U+0066, and
+# U+0069. The name "arrowvertex" has been omitted because this glyph
+# now has a real UV, and AGL is now incorrect in mapping it to the PUA
+# value U+F8E6. If you do not find an appropriate name for your glyph
+# in this list, then please refer to Section 6 of the AGL
+# Specification.
#
-# This list comprises the set of glyph names from the AGLv2,0 which map
-# to via the AGL rules to the semanticly correct Unicode value. For
-# example, Asmall is omitted as the AGL maps this to the Unicode
-# Private Use Area value F761, rather than to the Unicode value for the
-# character "A". "ffi" is also omitted, as the AGL maps this to the
-# Alphabetic Presentation Forms Area value FB03, rather than
-# decomposing it to the three-value Unicode sequence 0066,0066,0069.
-# See section 7.1 of the Unicode Standard 4.0 on this issue.
-# "arrowvertex" is omitted becuase this now has a real Unicode
-# character value, and the AGL is now incorrect in mapping this to the
-# Private Use Area value F8E6.
-#
-# If you do not find an appropriate name for your glyph in this list,
-# then please refer to section 6 of the document:
-# http://partners.adobe.com/asn/developer/typeforum/unicodegn.html.
-#
-# The Unicode values and names are given for convenience.
-#
-# Format: Semicolon-delimited fields:
-#
-# (1) Standard UV or CUS UV. (4 uppercase hexadecimal digits)
-#
-# (2) Glyph name. (upper- and lowercase letters, digits)
-#
+# Format: three semicolon-delimited fields:
+# (1) Standard UV or CUS UV--four uppercase hexadecimal digits
+# (2) Glyph name--upper/lowercase letters and digits
# (3) Character names: Unicode character names for standard UVs, and
-# descriptive names for CUS UVs. (uppercase letters, hyphen, space)
+# descriptive names for CUS UVs--uppercase letters, hyphen, and
+# space
#
-# The entries are sorted by glyph name in increasing ASCII order; entries
-# with the same glyph name are sorted in decreasing priority order.
+# The records are sorted by glyph name in increasing ASCII order,
+# entries with the same glyph name are sorted in decreasing priority
+# order, the UVs and Unicode character names are provided for
+# convenience, lines starting with "#" are comments, and blank lines
+# should be ignored.
#
-# Lines starting with "#" are comments; blank lines should be ignored.
+# Revision History:
#
-# 1.6 [30 January 2006]
-# - Completed work intended in 1.5
+# 1.7 [6 November 2008]
+# - Reverted to the original 1.4 and earlier mappings for Delta,
+# Omega, and mu.
+# - Removed mappings for "afii" names. These should now be assigned
+# "uni" names.
+# - Removed mappings for "commaaccent" names. These should now be
+# assigned "uni" names.
#
-# 1.5 [23 November 2005]
-# - removed duplicated block at end of file
-# - changed mappings:
-# 2206;Delta;INCREMENT changed to 0394;Delta;GREEK CAPITAL LETTER DELTA
-# 2126;Omega;OHM SIGN changed to 03A9;Omega;GREEK CAPITAL LETTER OMEGA
-# 03BC;mu;MICRO SIGN changed to 03BC;mu;GREEK SMALL LETTER MU
-# - corrected statement above about why ffi is omitted.
-
-# 1.4 [24 September 2003] Changed version to 1.4, to avoid confusion
-# with the AGL 1.3
-# fixed spelling errors in the header
-# fully removed arrowvertex, as it is mapped only to a PUA Unicode value in some fonts.
+# 1.6 [30 January 2006]
+# - Completed work intended in 1.5.
#
-# 1.1 [17 April 2003] Renamed [Tt]cedilla back to [Tt]commaaccent:
+# 1.5 [23 November 2005]
+# - Removed duplicated block at end of file.
+# - Changed mappings:
+# 2206;Delta;INCREMENT changed to 0394;Delta;GREEK CAPITAL LETTER DELTA
+# 2126;Omega;OHM SIGN changed to 03A9;Omega;GREEK CAPITAL LETTER OMEGA
+# 03BC;mu;MICRO SIGN changed to 03BC;mu;GREEK SMALL LETTER MU
+# - Corrected statement above about why "ffi" is omitted.
#
-# 1.0 [31 Jan 2003] Original version. Derived from the AGLv1.2 by:
-# - removing the PUA area codes
-# - removing duplicate Unicode mappings, and
-# - renaming tcommaaccent to tcedilla and Tcommaaccent to Tcedilla
+# 1.4 [24 September 2003]
+# - Changed version to 1.4, to avoid confusion with the AGL 1.3.
+# - Fixed spelling errors in the header.
+# - Fully removed "arrowvertex," as it is mapped only to a PUA Unicode
+# value in some fonts.
+#
+# 1.1 [17 April 2003]
+# - Renamed [Tt]cedilla back to [Tt]commaaccent.
+#
+# 1.0 [31 January 2003]
+# - Original version.
+# - Derived from the AGLv1.2 by:
+# removing the PUA area codes;
+# removing duplicate Unicode mappings; and
+# renaming "tcommaaccent" to "tcedilla" and "Tcommaaccent" to "Tcedilla"
#
0041;A;LATIN CAPITAL LETTER A
00C6;AE;LATIN CAPITAL LETTER AE
@@ -126,7 +142,7 @@
0044;D;LATIN CAPITAL LETTER D
010E;Dcaron;LATIN CAPITAL LETTER D WITH CARON
0110;Dcroat;LATIN CAPITAL LETTER D WITH STROKE
-0394;Delta;GREEK CAPITAL LETTER DELTA
+2206;Delta;INCREMENT
0045;E;LATIN CAPITAL LETTER E
00C9;Eacute;LATIN CAPITAL LETTER E WITH ACUTE
0114;Ebreve;LATIN CAPITAL LETTER E WITH BREVE
@@ -150,7 +166,6 @@
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
0048;H;LATIN CAPITAL LETTER H
25CF;H18533;BLACK CIRCLE
@@ -178,12 +193,10 @@
0134;Jcircumflex;LATIN CAPITAL LETTER J WITH CIRCUMFLEX
004B;K;LATIN CAPITAL LETTER K
039A;Kappa;GREEK CAPITAL LETTER KAPPA
-0136;Kcommaaccent;LATIN CAPITAL LETTER K WITH CEDILLA
004C;L;LATIN CAPITAL LETTER L
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
004D;M;LATIN CAPITAL LETTER M
@@ -191,7 +204,6 @@
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
00D1;Ntilde;LATIN CAPITAL LETTER N WITH TILDE
039D;Nu;GREEK CAPITAL LETTER NU
004F;O;LATIN CAPITAL LETTER O
@@ -204,7 +216,7 @@
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
-03A9;Omega;GREEK CAPITAL LETTER OMEGA
+2126;Omega;OHM SIGN
038F;Omegatonos;GREEK CAPITAL LETTER OMEGA WITH TONOS
039F;Omicron;GREEK CAPITAL LETTER OMICRON
038C;Omicrontonos;GREEK CAPITAL LETTER OMICRON WITH TONOS
@@ -219,7 +231,6 @@
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
0053;S;LATIN CAPITAL LETTER S
@@ -267,13 +278,11 @@
0160;Scaron;LATIN CAPITAL LETTER S WITH CARON
015E;Scedilla;LATIN CAPITAL LETTER S WITH CEDILLA
015C;Scircumflex;LATIN CAPITAL LETTER S WITH CIRCUMFLEX
-0218;Scommaaccent;LATIN CAPITAL LETTER S WITH COMMA BELOW
03A3;Sigma;GREEK CAPITAL LETTER SIGMA
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
0398;Theta;GREEK CAPITAL LETTER THETA
00DE;Thorn;LATIN CAPITAL LETTER THORN
0055;U;LATIN CAPITAL LETTER U
@@ -319,241 +328,6 @@
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
-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
-045F;afii10193;CYRILLIC SMALL LETTER DZHE
-0463;afii10194;CYRILLIC SMALL LETTER YAT
-0473;afii10195;CYRILLIC SMALL LETTER FITA
-0475;afii10196;CYRILLIC SMALL LETTER IZHITSA
-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
-05F0;afii57716;HEBREW LIGATURE YIDDISH DOUBLE VAV
-05F1;afii57717;HEBREW LIGATURE YIDDISH VAV YOD
-05F2;afii57718;HEBREW LIGATURE YIDDISH DOUBLE YOD
-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
@@ -684,7 +458,6 @@
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
@@ -730,13 +503,11 @@
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
@@ -754,7 +525,7 @@
2642;male;MALE SIGN
2212;minus;MINUS SIGN
2032;minute;PRIME
-03BC;mu;GREEK SMALL LETTER MU
+00B5;mu;MICRO SIGN
00D7;multiply;MULTIPLICATION SIGN
266A;musicalnote;EIGHTH NOTE
266B;musicalnotedbl;BEAMED EIGHTH NOTES
@@ -762,7 +533,6 @@
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
2209;notelement;NOT AN ELEMENT OF
2260;notequal;NOT EQUAL TO
@@ -837,7 +607,6 @@
0155;racute;LATIN SMALL LETTER R WITH ACUTE
221A;radical;SQUARE ROOT
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
@@ -850,7 +619,6 @@
0161;scaron;LATIN SMALL LETTER S WITH CARON
015F;scedilla;LATIN SMALL LETTER S WITH CEDILLA
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
@@ -873,7 +641,6 @@
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
2234;therefore;THEREFORE
03B8;theta;GREEK SMALL LETTER THETA
03D1;theta1;GREEK THETA SYMBOL
@@ -934,6 +701,7 @@
017C;zdotaccent;LATIN SMALL LETTER Z WITH DOT ABOVE
0030;zero;DIGIT ZERO
03B6;zeta;GREEK SMALL LETTER ZETA
+#END
"""
@@ -954,12 +722,12 @@
continue
m = parseAGL_RE.match(line)
if not m:
- raise AGLError, "syntax error in glyphlist.txt: %s" % repr(line[:20])
+ raise AGLError("syntax error in glyphlist.txt: %s" % repr(line[:20]))
unicode = m.group(1)
assert len(unicode) == 4
unicode = int(unicode, 16)
glyphName = m.group(2)
- if AGL2UV.has_key(glyphName):
+ if glyphName in AGL2UV:
# the above table contains identical duplicates
assert AGL2UV[glyphName] == unicode
else:
diff --git a/Lib/fontTools/cffLib.py b/Lib/fontTools/cffLib.py
index dd0112c..a4a85bd 100644
--- a/Lib/fontTools/cffLib.py
+++ b/Lib/fontTools/cffLib.py
@@ -4,11 +4,12 @@
# $Id: cffLib.py,v 1.34 2008-03-07 19:56:17 jvr Exp $
#
-import struct
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
from fontTools.misc import sstruct
-import string
from fontTools.misc import psCharStrings
from fontTools.misc.textTools import safeEval
+import struct
DEBUG = 0
@@ -20,7 +21,7 @@
offSize: B
"""
-class CFFFontSet:
+class CFFFontSet(object):
def __init__(self):
pass
@@ -51,7 +52,7 @@
try:
index = self.fontNames.index(name)
except ValueError:
- raise KeyError, name
+ raise KeyError(name)
return self.topDictIndex[index]
def compile(self, file, otFont):
@@ -78,9 +79,8 @@
writer.toFile(file)
def toXML(self, xmlWriter, progress=None):
- xmlWriter.newline()
for fontName in self.fontNames:
- xmlWriter.begintag("CFFFont", name=fontName)
+ xmlWriter.begintag("CFFFont", name=tostr(fontName))
xmlWriter.newline()
font = self[fontName]
font.toXML(xmlWriter, progress)
@@ -92,9 +92,8 @@
self.GlobalSubrs.toXML(xmlWriter, progress)
xmlWriter.endtag("GlobalSubrs")
xmlWriter.newline()
- xmlWriter.newline()
- def fromXML(self, (name, attrs, content)):
+ def fromXML(self, name, attrs, content):
if not hasattr(self, "GlobalSubrs"):
self.GlobalSubrs = GlobalSubrsIndex()
self.major = 1
@@ -113,18 +112,19 @@
for element in content:
if isinstance(element, basestring):
continue
- topDict.fromXML(element)
+ name, attrs, content = element
+ topDict.fromXML(name, attrs, content)
elif name == "GlobalSubrs":
for element in content:
if isinstance(element, basestring):
continue
name, attrs, content = element
subr = psCharStrings.T2CharString()
- subr.fromXML((name, attrs, content))
+ subr.fromXML(name, attrs, content)
self.GlobalSubrs.append(subr)
-class CFFWriter:
+class CFFWriter(object):
def __init__(self):
self.data = []
@@ -135,9 +135,9 @@
def toFile(self, file):
lastPosList = None
count = 1
- while 1:
+ while True:
if DEBUG:
- print "CFFWriter.toFile() iteration:", count
+ print("CFFWriter.toFile() iteration:", count)
count = count + 1
pos = 0
posList = [pos]
@@ -154,7 +154,7 @@
break
lastPosList = posList
if DEBUG:
- print "CFFWriter.toFile() writing to file."
+ print("CFFWriter.toFile() writing to file.")
begin = file.tell()
posList = [0]
for item in self.data:
@@ -178,7 +178,7 @@
return offSize
-class IndexCompiler:
+class IndexCompiler(object):
def __init__(self, items, strings, parent):
self.items = self.getItems(items, strings)
@@ -224,7 +224,7 @@
if hasattr(item, "toFile"):
item.toFile(file)
else:
- file.write(item)
+ file.write(tobytes(item, encoding="latin1"))
class IndexedStringsCompiler(IndexCompiler):
@@ -301,7 +301,7 @@
self.parent.rawDict["CharStrings"] = pos
-class Index:
+class Index(object):
"""This class represents what the CFF spec calls an INDEX."""
@@ -313,7 +313,7 @@
self.items = []
return
if DEBUG:
- print "loading %s at %s" % (name, file.tell())
+ print("loading %s at %s" % (name, file.tell()))
self.file = file
count = readCard16(file)
self.count = count
@@ -323,10 +323,10 @@
return
offSize = readCard8(file)
if DEBUG:
- print " index count: %s offSize: %s" % (count, offSize)
+ print(" index count: %s offSize: %s" % (count, offSize))
assert offSize <= 4, "offSize too large: %s" % offSize
self.offsets = offsets = []
- pad = '\0' * (4 - offSize)
+ pad = b'\0' * (4 - offSize)
for index in range(count+1):
chunk = file.read(offSize)
chunk = pad + chunk
@@ -335,7 +335,7 @@
self.offsetBase = file.tell() - 1
file.seek(self.offsetBase + offsets[-1]) # pretend we've read the whole lot
if DEBUG:
- print " end of %s at %s" % (name, file.tell())
+ print(" end of %s at %s" % (name, file.tell()))
def __len__(self):
return len(self.items)
@@ -400,11 +400,11 @@
xmlWriter.endtag("CharString")
xmlWriter.newline()
- def fromXML(self, (name, attrs, content)):
- if name <> "CharString":
+ def fromXML(self, name, attrs, content):
+ if name != "CharString":
return
subr = psCharStrings.T2CharString()
- subr.fromXML((name, attrs, content))
+ subr.fromXML(name, attrs, content)
self.append(subr)
def getItemAndSelector(self, index):
@@ -440,14 +440,15 @@
compilerClass = FDArrayIndexCompiler
- def fromXML(self, (name, attrs, content)):
- if name <> "FontDict":
+ def fromXML(self, name, attrs, content):
+ if name != "FontDict":
return
fontDict = FontDict()
for element in content:
if isinstance(element, basestring):
continue
- fontDict.fromXML(element)
+ name, attrs, content = element
+ fontDict.fromXML(name, attrs, content)
self.append(fontDict)
@@ -476,10 +477,10 @@
gidArray[glyphID] = fd
self.gidArray = gidArray
else:
- assert 0, "unsupported FDSelect format: %s" % format
+ assert False, "unsupported FDSelect format: %s" % format
else:
# reading from XML. Make empty gidArray,, and leave format as passed in.
- # format == None will result in the smallest representation being used.
+ # format is None will result in the smallest representation being used.
self.format = format
self.gidArray = []
@@ -497,7 +498,7 @@
self.gidArray.append(fdSelectValue)
-class CharStrings:
+class CharStrings(object):
def __init__(self, file, charset, globalSubrs, private, fdSelect, fdArray):
if file is not None:
@@ -511,22 +512,22 @@
self.charStringsAreIndexed = 0
self.globalSubrs = globalSubrs
self.private = private
- if fdSelect != None:
+ if fdSelect is not None:
self.fdSelect = fdSelect
- if fdArray!= None:
+ if fdArray is not None:
self.fdArray = fdArray
def keys(self):
- return self.charStrings.keys()
+ return list(self.charStrings.keys())
def values(self):
if self.charStringsAreIndexed:
return self.charStringsIndex
else:
- return self.charStrings.values()
+ return list(self.charStrings.values())
def has_key(self, name):
- return self.charStrings.has_key(name)
+ return name in self.charStrings
def __len__(self):
return len(self.charStrings)
@@ -556,8 +557,7 @@
return self.charStrings[name], sel
def toXML(self, xmlWriter, progress):
- names = self.keys()
- names.sort()
+ names = sorted(self.keys())
i = 0
step = 10
numGlyphs = len(names)
@@ -578,15 +578,15 @@
xmlWriter.newline()
if not i % step and progress is not None:
progress.setLabel("Dumping 'CFF ' table... (%s)" % name)
- progress.increment(step / float(numGlyphs))
+ progress.increment(step / numGlyphs)
i = i + 1
- def fromXML(self, (name, attrs, content)):
+ def fromXML(self, name, attrs, content):
for element in content:
if isinstance(element, basestring):
continue
name, attrs, content = element
- if name <> "CharString":
+ if name != "CharString":
continue
fdID = -1
if hasattr(self, "fdArray"):
@@ -599,27 +599,27 @@
charString = psCharStrings.T2CharString(
private=private,
globalSubrs=self.globalSubrs)
- charString.fromXML((name, attrs, content))
+ charString.fromXML(name, attrs, content)
if fdID >= 0:
charString.fdSelectIndex = fdID
self[glyphName] = charString
def readCard8(file):
- return ord(file.read(1))
+ return byteord(file.read(1))
def readCard16(file):
value, = struct.unpack(">H", file.read(2))
return value
def writeCard8(file, value):
- file.write(chr(value))
+ file.write(bytechr(value))
def writeCard16(file, value):
file.write(struct.pack(">H", value))
def packCard8(value):
- return chr(value)
+ return bytechr(value)
def packCard16(value):
return struct.pack(">H", value)
@@ -634,9 +634,9 @@
d = {}
for op, name, arg, default, conv in table:
if isinstance(op, tuple):
- op = chr(op[0]) + chr(op[1])
+ op = bytechr(op[0]) + bytechr(op[1])
else:
- op = chr(op)
+ op = bytechr(op)
d[name] = (op, arg)
return d
@@ -660,7 +660,7 @@
return d
-class SimpleConverter:
+class SimpleConverter(object):
def read(self, parent, value):
return value
def write(self, parent, value):
@@ -668,13 +668,30 @@
def xmlWrite(self, xmlWriter, name, value, progress):
xmlWriter.simpletag(name, value=value)
xmlWriter.newline()
- def xmlRead(self, (name, attrs, content), parent):
+ def xmlRead(self, name, attrs, content, parent):
return attrs["value"]
+class ASCIIConverter(SimpleConverter):
+ def read(self, parent, value):
+ return tostr(value, encoding='ascii')
+ def write(self, parent, value):
+ return tobytes(value, encoding='ascii')
+ def xmlWrite(self, xmlWriter, name, value, progress):
+ xmlWriter.simpletag(name, value=tostr(value, encoding="ascii"))
+ xmlWriter.newline()
+ def xmlRead(self, name, attrs, content, parent):
+ return tobytes(attrs["value"], encoding=("ascii"))
+
class Latin1Converter(SimpleConverter):
- def xmlRead(self, (name, attrs, content), parent):
- s = unicode(attrs["value"], "utf-8")
- return s.encode("latin-1")
+ def read(self, parent, value):
+ return tostr(value, encoding='latin1')
+ def write(self, parent, value):
+ return tobytes(value, encoding='latin1')
+ def xmlWrite(self, xmlWriter, name, value, progress):
+ xmlWriter.simpletag(name, value=tostr(value, encoding="latin1"))
+ xmlWriter.newline()
+ def xmlRead(self, name, attrs, content, parent):
+ return tobytes(attrs["value"], encoding=("latin1"))
def parseNum(s):
@@ -685,17 +702,17 @@
return value
class NumberConverter(SimpleConverter):
- def xmlRead(self, (name, attrs, content), parent):
+ def xmlRead(self, name, attrs, content, parent):
return parseNum(attrs["value"])
class ArrayConverter(SimpleConverter):
def xmlWrite(self, xmlWriter, name, value, progress):
- value = map(str, value)
- xmlWriter.simpletag(name, value=" ".join(value))
+ value = " ".join(map(str, value))
+ xmlWriter.simpletag(name, value=value)
xmlWriter.newline()
- def xmlRead(self, (name, attrs, content), parent):
+ def xmlRead(self, name, attrs, content, parent):
values = attrs["value"].split()
- return map(parseNum, values)
+ return [parseNum(value) for value in values]
class TableConverter(SimpleConverter):
def xmlWrite(self, xmlWriter, name, value, progress):
@@ -704,12 +721,13 @@
value.toXML(xmlWriter, progress)
xmlWriter.endtag(name)
xmlWriter.newline()
- def xmlRead(self, (name, attrs, content), parent):
+ def xmlRead(self, name, attrs, content, parent):
ob = self.getClass()()
for element in content:
if isinstance(element, basestring):
continue
- ob.fromXML(element)
+ name, attrs, content = element
+ ob.fromXML(name, attrs, content)
return ob
class PrivateDictConverter(TableConverter):
@@ -721,7 +739,7 @@
priv = PrivateDict(parent.strings, file, offset)
file.seek(offset)
data = file.read(size)
- len(data) == size
+ assert len(data) == size
priv.decompile(data)
return priv
def write(self, parent, value):
@@ -752,7 +770,7 @@
return CharStrings(file, charset, globalSubrs, private, fdSelect, fdArray)
def write(self, parent, value):
return 0 # dummy value
- def xmlRead(self, (name, attrs, content), parent):
+ def xmlRead(self, name, attrs, content, parent):
if hasattr(parent, "ROS"):
# if it is a CID-keyed font, then the private Dict is extracted from the parent.FDArray
private, fdSelect, fdArray = None, parent.FDSelect, parent.FDArray
@@ -760,10 +778,10 @@
# if it is a name-keyed font, then the private dict is in the top dict, and there is no fdArray.
private, fdSelect, fdArray = parent.Private, None, None
charStrings = CharStrings(None, None, parent.GlobalSubrs, private, fdSelect, fdArray)
- charStrings.fromXML((name, attrs, content))
+ charStrings.fromXML(name, attrs, content)
return charStrings
-class CharsetConverter:
+class CharsetConverter(object):
def read(self, parent, value):
isCID = hasattr(parent, "ROS")
if value > 2:
@@ -771,7 +789,7 @@
file = parent.file
file.seek(value)
if DEBUG:
- print "loading charset at %s" % value
+ print("loading charset at %s" % value)
format = readCard8(file)
if format == 0:
charset = parseCharset0(numGlyphs, file, parent.strings, isCID)
@@ -781,9 +799,9 @@
raise NotImplementedError
assert len(charset) == numGlyphs
if DEBUG:
- print " charset end at %s" % file.tell()
+ print(" charset end at %s" % file.tell())
else: # offset == 0 -> no charset data.
- if isCID or not parent.rawDict.has_key("CharStrings"):
+ if isCID or "CharStrings" not in parent.rawDict:
assert value == 0 # We get here only when processing fontDicts from the FDArray of CFF-CID fonts. Only the real topDict references the chrset.
charset = None
elif value == 0:
@@ -802,12 +820,12 @@
##xmlWriter.simpletag("charset")
xmlWriter.comment("charset is dumped separately as the 'GlyphOrder' element")
xmlWriter.newline()
- def xmlRead(self, (name, attrs, content), parent):
+ def xmlRead(self, name, attrs, content, parent):
if 0:
return safeEval(attrs["value"])
-class CharsetCompiler:
+class CharsetCompiler(object):
def __init__(self, strings, charset, parent):
assert charset[0] == '.notdef'
@@ -837,8 +855,8 @@
return strings.getSID(name)
def packCharset0(charset, isCID, strings):
- format = 0
- data = [packCard8(format)]
+ fmt = 0
+ data = [packCard8(fmt)]
if isCID:
getNameID = getCIDfromName
else:
@@ -846,11 +864,11 @@
for name in charset[1:]:
data.append(packCard16(getNameID(name,strings)))
- return "".join(data)
+ return bytesjoin(data)
def packCharset(charset, isCID, strings):
- format = 1
+ fmt = 1
ranges = []
first = None
end = 0
@@ -863,43 +881,43 @@
SID = getNameID(name, strings)
if first is None:
first = SID
- elif end + 1 <> SID:
+ elif end + 1 != SID:
nLeft = end - first
if nLeft > 255:
- format = 2
+ fmt = 2
ranges.append((first, nLeft))
first = SID
end = SID
nLeft = end - first
if nLeft > 255:
- format = 2
+ fmt = 2
ranges.append((first, nLeft))
- data = [packCard8(format)]
- if format == 1:
+ data = [packCard8(fmt)]
+ if fmt == 1:
nLeftFunc = packCard8
else:
nLeftFunc = packCard16
for first, nLeft in ranges:
data.append(packCard16(first) + nLeftFunc(nLeft))
- return "".join(data)
+ return bytesjoin(data)
def parseCharset0(numGlyphs, file, strings, isCID):
charset = [".notdef"]
if isCID:
for i in range(numGlyphs - 1):
CID = readCard16(file)
- charset.append("cid" + string.zfill(str(CID), 5) )
+ charset.append("cid" + str(CID).zfill(5))
else:
for i in range(numGlyphs - 1):
SID = readCard16(file)
charset.append(strings[SID])
return charset
-def parseCharset(numGlyphs, file, strings, isCID, format):
+def parseCharset(numGlyphs, file, strings, isCID, fmt):
charset = ['.notdef']
count = 1
- if format == 1:
+ if fmt == 1:
nLeftFunc = readCard8
else:
nLeftFunc = readCard16
@@ -908,7 +926,7 @@
nLeft = nLeftFunc(file)
if isCID:
for CID in range(first, first+nLeft+1):
- charset.append("cid" + string.zfill(str(CID), 5) )
+ charset.append("cid" + str(CID).zfill(5))
else:
for SID in range(first, first+nLeft+1):
charset.append(strings[SID])
@@ -916,7 +934,7 @@
return charset
-class EncodingCompiler:
+class EncodingCompiler(object):
def __init__(self, strings, encoding, parent):
assert not isinstance(encoding, basestring)
@@ -950,16 +968,16 @@
file = parent.file
file.seek(value)
if DEBUG:
- print "loading Encoding at %s" % value
- format = readCard8(file)
- haveSupplement = format & 0x80
+ print("loading Encoding at %s" % value)
+ fmt = readCard8(file)
+ haveSupplement = fmt & 0x80
if haveSupplement:
- raise NotImplementedError, "Encoding supplements are not yet supported"
- format = format & 0x7f
- if format == 0:
+ raise NotImplementedError("Encoding supplements are not yet supported")
+ fmt = fmt & 0x7f
+ if fmt == 0:
encoding = parseEncoding0(parent.charset, file, haveSupplement,
parent.strings)
- elif format == 1:
+ elif fmt == 1:
encoding = parseEncoding1(parent.charset, file, haveSupplement,
parent.strings)
return encoding
@@ -986,8 +1004,8 @@
xmlWriter.endtag(name)
xmlWriter.newline()
- def xmlRead(self, (name, attrs, content), parent):
- if attrs.has_key("name"):
+ def xmlRead(self, name, attrs, content, parent):
+ if "name" in attrs:
return attrs["name"]
encoding = [".notdef"] * 256
for element in content:
@@ -1023,7 +1041,7 @@
return encoding
def packEncoding0(charset, encoding, strings):
- format = 0
+ fmt = 0
m = {}
for code in range(len(encoding)):
name = encoding[code]
@@ -1037,15 +1055,15 @@
while codes and codes[-1] is None:
codes.pop()
- data = [packCard8(format), packCard8(len(codes))]
+ data = [packCard8(fmt), packCard8(len(codes))]
for code in codes:
if code is None:
code = 0
data.append(packCard8(code))
- return "".join(data)
+ return bytesjoin(data)
def packEncoding1(charset, encoding, strings):
- format = 1
+ fmt = 1
m = {}
for code in range(len(encoding)):
name = encoding[code]
@@ -1058,7 +1076,7 @@
code = m.get(name, -1)
if first is None:
first = code
- elif end + 1 <> code:
+ elif end + 1 != code:
nLeft = end - first
ranges.append((first, nLeft))
first = code
@@ -1070,12 +1088,12 @@
while ranges and ranges[-1][0] == -1:
ranges.pop()
- data = [packCard8(format), packCard8(len(ranges))]
+ data = [packCard8(fmt), packCard8(len(ranges))]
for first, nLeft in ranges:
if first == -1: # unencoded
first = 0
data.append(packCard8(first) + packCard8(nLeft))
- return "".join(data)
+ return bytesjoin(data)
class FDArrayConverter(TableConverter):
@@ -1091,16 +1109,17 @@
def write(self, parent, value):
return 0 # dummy value
- def xmlRead(self, (name, attrs, content), parent):
+ def xmlRead(self, name, attrs, content, parent):
fdArray = FDArrayIndex()
for element in content:
if isinstance(element, basestring):
continue
- fdArray.fromXML(element)
+ name, attrs, content = element
+ fdArray.fromXML(name, attrs, content)
return fdArray
-class FDSelectConverter:
+class FDSelectConverter(object):
def read(self, parent, value):
file = parent.file
@@ -1117,24 +1136,24 @@
xmlWriter.simpletag(name, [('format', value.format)])
xmlWriter.newline()
- def xmlRead(self, (name, attrs, content), parent):
- format = safeEval(attrs["format"])
+ def xmlRead(self, name, attrs, content, parent):
+ fmt = safeEval(attrs["format"])
file = None
numGlyphs = None
- fdSelect = FDSelect(file, numGlyphs, format)
+ fdSelect = FDSelect(file, numGlyphs, fmt)
return fdSelect
def packFDSelect0(fdSelectArray):
- format = 0
- data = [packCard8(format)]
+ fmt = 0
+ data = [packCard8(fmt)]
for index in fdSelectArray:
data.append(packCard8(index))
- return "".join(data)
+ return bytesjoin(data)
def packFDSelect3(fdSelectArray):
- format = 3
+ fmt = 3
fdRanges = []
first = None
end = 0
@@ -1147,23 +1166,23 @@
lastFDIndex = fdIndex
sentinelGID = i + 1
- data = [packCard8(format)]
+ data = [packCard8(fmt)]
data.append(packCard16( len(fdRanges) ))
for fdRange in fdRanges:
data.append(packCard16(fdRange[0]))
data.append(packCard8(fdRange[1]))
data.append(packCard16(sentinelGID))
- return "".join(data)
+ return bytesjoin(data)
-class FDSelectCompiler:
+class FDSelectCompiler(object):
def __init__(self, fdSelect, parent):
- format = fdSelect.format
+ fmt = fdSelect.format
fdSelectArray = fdSelect.gidArray
- if format == 0:
+ if fmt == 0:
self.data = packFDSelect0(fdSelectArray)
- elif format == 3:
+ elif fmt == 3:
self.data = packFDSelect3(fdSelectArray)
else:
# choose smaller of the two formats
@@ -1192,11 +1211,11 @@
def xmlWrite(self, xmlWriter, name, value, progress):
registry, order, supplement = value
- xmlWriter.simpletag(name, [('Registry', registry), ('Order', order),
+ xmlWriter.simpletag(name, [('Registry', tostr(registry)), ('Order', tostr(order)),
('Supplement', supplement)])
xmlWriter.newline()
- def xmlRead(self, (name, attrs, content), parent):
+ def xmlRead(self, name, attrs, content, parent):
return (attrs['Registry'], attrs['Order'], safeEval(attrs['Supplement']))
@@ -1277,9 +1296,9 @@
elif arg == "number":
conv = NumberConverter()
elif arg == "SID":
- conv = SimpleConverter()
+ conv = ASCIIConverter()
else:
- assert 0
+ assert False
table[i] = op, name, arg, default, conv
addConverters(privateDictOperators)
@@ -1294,7 +1313,7 @@
operators = buildOperatorDict(privateDictOperators)
-class DictCompiler:
+class DictCompiler(object):
def __init__(self, dictObj, strings, parent):
assert isinstance(strings, IndexedStrings)
@@ -1321,8 +1340,8 @@
def compile(self, reason):
if DEBUG:
- print "-- compiling %s for %s" % (self.__class__.__name__, reason)
- print "in baseDict: ", self
+ print("-- compiling %s for %s" % (self.__class__.__name__, reason))
+ print("in baseDict: ", self)
rawDict = self.rawDict
data = []
for name in self.dictObj.order:
@@ -1342,7 +1361,7 @@
arghandler = getattr(self, "arg_" + argType)
data.append(arghandler(value))
data.append(op)
- return "".join(data)
+ return bytesjoin(data)
def toFile(self, file):
file.write(self.compile("toFile"))
@@ -1355,7 +1374,7 @@
data = []
for num in value:
data.append(encodeNumber(num))
- return "".join(data)
+ return bytesjoin(data)
def arg_delta(self, value):
out = []
last = 0
@@ -1365,7 +1384,7 @@
data = []
for num in out:
data.append(encodeNumber(num))
- return "".join(data)
+ return bytesjoin(data)
def encodeNumber(num):
@@ -1396,7 +1415,6 @@
if len(fdSelect) == 0: # probably read in from XML; assume fdIndex in CharString data
charStrings = self.dictObj.CharStrings
for name in self.dictObj.charset:
- charstring = charStrings[name]
fdSelect.append(charStrings[name].fdSelectIndex)
fdSelectComp = FDSelectCompiler(fdSelect, self)
children.append(fdSelectComp)
@@ -1450,12 +1468,12 @@
return children
-class BaseDict:
+class BaseDict(object):
def __init__(self, strings=None, file=None, offset=None):
self.rawDict = {}
if DEBUG:
- print "loading %s at %s" % (self.__class__.__name__, offset)
+ print("loading %s at %s" % (self.__class__.__name__, offset))
self.file = file
self.offset = offset
self.strings = strings
@@ -1463,7 +1481,7 @@
def decompile(self, data):
if DEBUG:
- print " length %s is %s" % (self.__class__.__name__, len(data))
+ print(" length %s is %s" % (self.__class__.__name__, len(data)))
dec = self.decompilerClass(self.strings)
dec.decompile(data)
self.rawDict = dec.getDict()
@@ -1480,7 +1498,7 @@
if value is None:
value = self.defaults.get(name)
if value is None:
- raise AttributeError, name
+ raise AttributeError(name)
conv = self.converters[name]
value = conv.read(self, value)
setattr(self, name, value)
@@ -1496,9 +1514,9 @@
conv = self.converters[name]
conv.xmlWrite(xmlWriter, name, value, progress)
- def fromXML(self, (name, attrs, content)):
+ def fromXML(self, name, attrs, content):
conv = self.converters[name]
- value = conv.xmlRead((name, attrs, content), self)
+ value = conv.xmlRead(name, attrs, content, self)
setattr(self, name, value)
@@ -1544,10 +1562,10 @@
try:
charString.decompile()
except:
- print "Error in charstring ", i
+ print("Error in charstring ", i)
import sys
- type, value = sys. exc_info()[0:2]
- raise type(value)
+ typ, value = sys.exc_info()[0:2]
+ raise typ(value)
if not i % 30 and progress:
progress.increment(0) # update
i = i + 1
@@ -1582,7 +1600,7 @@
compilerClass = PrivateDictCompiler
-class IndexedStrings:
+class IndexedStrings(object):
"""SID -> string mapping."""
@@ -1590,7 +1608,7 @@
if file is None:
strings = []
else:
- strings = list(Index(file))
+ strings = [tostr(s, encoding="latin1") for s in Index(file)]
self.strings = strings
def getCompiler(self):
@@ -1608,9 +1626,9 @@
def getSID(self, s):
if not hasattr(self, "stringMapping"):
self.buildStringMapping()
- if cffStandardStringMapping.has_key(s):
+ if s in cffStandardStringMapping:
SID = cffStandardStringMapping[s]
- elif self.stringMapping.has_key(s):
+ elif s in self.stringMapping:
SID = self.stringMapping[s]
else:
SID = len(self.strings) + cffStandardStringCount
diff --git a/Lib/fontTools/fondLib.py b/Lib/fontTools/fondLib.py
deleted file mode 100644
index 74ae13b..0000000
--- a/Lib/fontTools/fondLib.py
+++ /dev/null
@@ -1,554 +0,0 @@
-import os
-import struct
-from fontTools.misc import sstruct
-import string
-try:
- from Carbon import Res
-except ImportError:
- import Res
-
-
-error = "fondLib.error"
-
-DEBUG = 0
-
-headerformat = """
- >
- ffFlags: h
- ffFamID: h
- ffFirstChar: h
- ffLastChar: h
- ffAscent: h
- ffDescent: h
- ffLeading: h
- ffWidMax: h
- ffWTabOff: l
- ffKernOff: l
- ffStylOff: l
-"""
-
-FONDheadersize = 52
-
-class FontFamily:
-
- def __init__(self, theRes, mode = 'r'):
- self.ID, type, self.name = theRes.GetResInfo()
- if type <> 'FOND':
- raise ValueError, "FOND resource required"
- self.FOND = theRes
- self.mode = mode
- self.changed = 0
-
- if DEBUG:
- self.parsedthings = []
-
- def parse(self):
- self._getheader()
- self._getfontassociationtable()
- self._getoffsettable()
- self._getboundingboxtable()
- self._getglyphwidthtable()
- self._getstylemappingtable()
- self._getglyphencodingsubtable()
- self._getkerningtables()
-
- def minimalparse(self):
- self._getheader()
- self._getglyphwidthtable()
- self._getstylemappingtable()
-
- def __repr__(self):
- return "<FontFamily instance of %s>" % self.name
-
- def getflags(self):
- return self.fondClass
-
- def setflags(self, flags):
- self.changed = 1
- self.fondClass = flags
-
- def save(self, destresfile = None):
- if self.mode <> 'w':
- raise error, "can't save font: no write permission"
- self._buildfontassociationtable()
- self._buildoffsettable()
- self._buildboundingboxtable()
- self._buildglyphwidthtable()
- self._buildkerningtables()
- self._buildstylemappingtable()
- self._buildglyphencodingsubtable()
- rawnames = [ "_rawheader",
- "_rawfontassociationtable",
- "_rawoffsettable",
- "_rawglyphwidthtable",
- "_rawstylemappingtable",
- "_rawglyphencodingsubtable",
- "_rawkerningtables"
- ]
- for name in rawnames[1:]: # skip header
- data = getattr(self, name)
- if len(data) & 1:
- setattr(self, name, data + '\0')
-
- self.ffWTabOff = FONDheadersize + len(self._rawfontassociationtable) + len(self._rawoffsettable)
- self.ffStylOff = self.ffWTabOff + len(self._rawglyphwidthtable)
- self.ffKernOff = self.ffStylOff + len(self._rawstylemappingtable) + len(self._rawglyphencodingsubtable)
- self.glyphTableOffset = len(self._rawstylemappingtable)
-
- if not self._rawglyphwidthtable:
- self.ffWTabOff = 0
- if not self._rawstylemappingtable:
- self.ffStylOff = 0
- if not self._rawglyphencodingsubtable:
- self.glyphTableOffset = 0
- if not self._rawkerningtables:
- self.ffKernOff = 0
-
- self._buildheader()
-
- # glyphTableOffset has only just been calculated
- self._updatestylemappingtable()
-
- newdata = ""
- for name in rawnames:
- newdata = newdata + getattr(self, name)
- if destresfile is None:
- self.FOND.data = newdata
- self.FOND.ChangedResource()
- self.FOND.WriteResource()
- else:
- ID, type, name = self.FOND.GetResInfo()
- self.FOND.DetachResource()
- self.FOND.data = newdata
- saveref = Res.CurResFile()
- Res.UseResFile(destresfile)
- self.FOND.AddResource(type, ID, name)
- Res.UseResFile(saveref)
- self.changed = 0
-
- def _getheader(self):
- data = self.FOND.data
- sstruct.unpack(headerformat, data[:28], self)
- self.ffProperty = struct.unpack(">9h", data[28:46])
- self.ffIntl = struct.unpack(">hh", data[46:50])
- self.ffVersion, = struct.unpack(">h", data[50:FONDheadersize])
-
- if DEBUG:
- self._rawheader = data[:FONDheadersize]
- self.parsedthings.append((0, FONDheadersize, 'header'))
-
- def _buildheader(self):
- header = sstruct.pack(headerformat, self)
- header = header + apply(struct.pack, (">9h",) + self.ffProperty)
- header = header + apply(struct.pack, (">hh",) + self.ffIntl)
- header = header + struct.pack(">h", self.ffVersion)
- if DEBUG:
- print "header is the same?", self._rawheader == header and 'yes.' or 'no.'
- if self._rawheader <> header:
- print len(self._rawheader), len(header)
- self._rawheader = header
-
- def _getfontassociationtable(self):
- data = self.FOND.data
- offset = FONDheadersize
- numberofentries, = struct.unpack(">h", data[offset:offset+2])
- numberofentries = numberofentries + 1
- size = numberofentries * 6
- self.fontAssoc = []
- for i in range(offset + 2, offset + size, 6):
- self.fontAssoc.append(struct.unpack(">3h", data[i:i+6]))
-
- self._endoffontassociationtable = offset + size + 2
- if DEBUG:
- self._rawfontassociationtable = data[offset:self._endoffontassociationtable]
- self.parsedthings.append((offset, self._endoffontassociationtable, 'fontassociationtable'))
-
- def _buildfontassociationtable(self):
- data = struct.pack(">h", len(self.fontAssoc) - 1)
- for size, stype, ID in self.fontAssoc:
- data = data + struct.pack(">3h", size, stype, ID)
-
- if DEBUG:
- print "font association table is the same?", self._rawfontassociationtable == data and 'yes.' or 'no.'
- if self._rawfontassociationtable <> data:
- print len(self._rawfontassociationtable), len(data)
- self._rawfontassociationtable = data
-
- def _getoffsettable(self):
- if self.ffWTabOff == 0:
- self._rawoffsettable = ""
- return
- data = self.FOND.data
- # Quick'n'Dirty. What's the spec anyway? Can't find it...
- offset = self._endoffontassociationtable
- count = self.ffWTabOff
- self._rawoffsettable = data[offset:count]
- if DEBUG:
- self.parsedthings.append((offset, count, 'offsettable&bbtable'))
-
- def _buildoffsettable(self):
- if not hasattr(self, "_rawoffsettable"):
- self._rawoffsettable = ""
-
- def _getboundingboxtable(self):
- self.boundingBoxes = None
- if self._rawoffsettable[:6] <> '\0\0\0\0\0\6': # XXX ????
- return
- boxes = {}
- data = self._rawoffsettable[6:]
- numstyles = struct.unpack(">h", data[:2])[0] + 1
- data = data[2:]
- for i in range(numstyles):
- style, l, b, r, t = struct.unpack(">hhhhh", data[:10])
- boxes[style] = (l, b, r, t)
- data = data[10:]
- self.boundingBoxes = boxes
-
- def _buildboundingboxtable(self):
- if self.boundingBoxes and self._rawoffsettable[:6] == '\0\0\0\0\0\6':
- boxes = self.boundingBoxes.items()
- boxes.sort()
- data = '\0\0\0\0\0\6' + struct.pack(">h", len(boxes) - 1)
- for style, (l, b, r, t) in boxes:
- data = data + struct.pack(">hhhhh", style, l, b, r, t)
- self._rawoffsettable = data
-
- def _getglyphwidthtable(self):
- self.widthTables = {}
- if self.ffWTabOff == 0:
- return
- data = self.FOND.data
- offset = self.ffWTabOff
- numberofentries, = struct.unpack(">h", data[offset:offset+2])
- numberofentries = numberofentries + 1
- count = offset + 2
- for i in range(numberofentries):
- stylecode, = struct.unpack(">h", data[count:count+2])
- widthtable = self.widthTables[stylecode] = []
- count = count + 2
- for j in range(3 + self.ffLastChar - self.ffFirstChar):
- width, = struct.unpack(">h", data[count:count+2])
- widthtable.append(width)
- count = count + 2
-
- if DEBUG:
- self._rawglyphwidthtable = data[offset:count]
- self.parsedthings.append((offset, count, 'glyphwidthtable'))
-
- def _buildglyphwidthtable(self):
- if not self.widthTables:
- self._rawglyphwidthtable = ""
- return
- numberofentries = len(self.widthTables)
- data = struct.pack('>h', numberofentries - 1)
- tables = self.widthTables.items()
- tables.sort()
- for stylecode, table in tables:
- data = data + struct.pack('>h', stylecode)
- if len(table) <> (3 + self.ffLastChar - self.ffFirstChar):
- raise error, "width table has wrong length"
- for width in table:
- data = data + struct.pack('>h', width)
- if DEBUG:
- print "glyph width table is the same?", self._rawglyphwidthtable == data and 'yes.' or 'no.'
- self._rawglyphwidthtable = data
-
- def _getkerningtables(self):
- self.kernTables = {}
- if self.ffKernOff == 0:
- return
- data = self.FOND.data
- offset = self.ffKernOff
- numberofentries, = struct.unpack(">h", data[offset:offset+2])
- numberofentries = numberofentries + 1
- count = offset + 2
- for i in range(numberofentries):
- stylecode, = struct.unpack(">h", data[count:count+2])
- count = count + 2
- numberofpairs, = struct.unpack(">h", data[count:count+2])
- count = count + 2
- kerntable = self.kernTables[stylecode] = []
- for j in range(numberofpairs):
- firstchar, secondchar, kerndistance = struct.unpack(">cch", data[count:count+4])
- kerntable.append((ord(firstchar), ord(secondchar), kerndistance))
- count = count + 4
-
- if DEBUG:
- self._rawkerningtables = data[offset:count]
- self.parsedthings.append((offset, count, 'kerningtables'))
-
- def _buildkerningtables(self):
- if self.kernTables == {}:
- self._rawkerningtables = ""
- self.ffKernOff = 0
- return
- numberofentries = len(self.kernTables)
- data = [struct.pack('>h', numberofentries - 1)]
- tables = self.kernTables.items()
- tables.sort()
- for stylecode, table in tables:
- data.append(struct.pack('>h', stylecode))
- data.append(struct.pack('>h', len(table))) # numberofpairs
- for firstchar, secondchar, kerndistance in table:
- data.append(struct.pack(">cch", chr(firstchar), chr(secondchar), kerndistance))
-
- data = string.join(data, '')
-
- if DEBUG:
- print "kerning table is the same?", self._rawkerningtables == data and 'yes.' or 'no.'
- if self._rawkerningtables <> data:
- print len(self._rawkerningtables), len(data)
- self._rawkerningtables = data
-
- def _getstylemappingtable(self):
- offset = self.ffStylOff
- self.styleStrings = []
- self.styleIndices = ()
- self.glyphTableOffset = 0
- self.fondClass = 0
- if offset == 0:
- return
- data = self.FOND.data
- self.fondClass, self.glyphTableOffset, self.styleMappingReserved, = \
- struct.unpack(">hll", data[offset:offset+10])
- self.styleIndices = struct.unpack('>48b', data[offset + 10:offset + 58])
- stringcount, = struct.unpack('>h', data[offset+58:offset+60])
-
- count = offset + 60
- for i in range(stringcount):
- str_len = ord(data[count])
- self.styleStrings.append(data[count + 1:count + 1 + str_len])
- count = count + 1 + str_len
-
- self._unpackstylestrings()
-
- data = data[offset:count]
- if len(data) % 2:
- data = data + '\0'
- if DEBUG:
- self._rawstylemappingtable = data
- self.parsedthings.append((offset, count, 'stylemappingtable'))
-
- def _buildstylemappingtable(self):
- if not self.styleIndices:
- self._rawstylemappingtable = ""
- return
- data = struct.pack(">hll", self.fondClass, self.glyphTableOffset,
- self.styleMappingReserved)
-
- self._packstylestrings()
- data = data + apply(struct.pack, (">48b",) + self.styleIndices)
-
- stringcount = len(self.styleStrings)
- data = data + struct.pack(">h", stringcount)
- for string in self.styleStrings:
- data = data + chr(len(string)) + string
-
- if len(data) % 2:
- data = data + '\0'
-
- if DEBUG:
- print "style mapping table is the same?", self._rawstylemappingtable == data and 'yes.' or 'no.'
- self._rawstylemappingtable = data
-
- def _unpackstylestrings(self):
- psNames = {}
- self.ffFamilyName = self.styleStrings[0]
- for i in self.widthTables.keys():
- index = self.styleIndices[i]
- if index == 1:
- psNames[i] = self.styleStrings[0]
- else:
- style = self.styleStrings[0]
- codes = map(ord, self.styleStrings[index - 1])
- for code in codes:
- style = style + self.styleStrings[code - 1]
- psNames[i] = style
- self.psNames = psNames
-
- def _packstylestrings(self):
- nameparts = {}
- splitnames = {}
- for style, name in self.psNames.items():
- split = splitname(name, self.ffFamilyName)
- splitnames[style] = split
- for part in split:
- nameparts[part] = None
- del nameparts[self.ffFamilyName]
- nameparts = nameparts.keys()
- nameparts.sort()
- items = splitnames.items()
- items.sort()
- numindices = 0
- for style, split in items:
- if len(split) > 1:
- numindices = numindices + 1
- numindices = max(numindices, max(self.styleIndices) - 1)
- styleStrings = [self.ffFamilyName] + numindices * [""] + 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 necessary 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'):
- 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
- self.resref = Res.FSOpenResFile(path, 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/inspect.py b/Lib/fontTools/inspect.py
index 4726e11..00ecfbe 100644
--- a/Lib/fontTools/inspect.py
+++ b/Lib/fontTools/inspect.py
@@ -5,13 +5,14 @@
"""GUI font inspector.
"""
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
+from fontTools import misc, ttLib, cffLib
import pygtk
pygtk.require('2.0')
import gtk
import sys
-import array
-from fontTools import misc, ttLib, cffLib
class Row(object):
@@ -74,7 +75,7 @@
# Make sure item is decompiled
try:
value["asdf"]
- except (AttributeError, KeyError, ttLib.TTLibError):
+ except (AttributeError, KeyError, TypeError, ttLib.TTLibError):
pass
if isinstance(value, ttLib.getTableModule('glyf').Glyph):
# Glyph type needs explicit expanding to be useful
@@ -118,7 +119,7 @@
return len(self._children)
if hasattr(self, '_items'):
return len(self._items)
- assert 0
+ assert False
def _ensure_children(self):
if hasattr(self, '_children'):
@@ -217,7 +218,7 @@
def on_iter_parent(self, rowref):
return rowref.get_parent()
-class Inspect:
+class Inspect(object):
def _delete_event(self, widget, event, data=None):
gtk.main_quit()
@@ -233,7 +234,7 @@
self.scrolled_window = gtk.ScrolledWindow()
self.window.add(self.scrolled_window)
- self.font = ttLib.TTFont(fontfile)
+ self.font = ttLib.TTFont(fontfile, lazy=True)
self.treemodel = FontTreeModel(self.font)
self.treeview = gtk.TreeView(self.treemodel)
#self.treeview.set_reorderable(True)
@@ -254,7 +255,7 @@
def main(args):
if len(args) < 1:
- print >>sys.stderr, "usage: pyftinspect font..."
+ print("usage: pyftinspect font...", file=sys.stderr)
sys.exit(1)
for arg in args:
Inspect(arg)
diff --git a/Lib/fontTools/misc/arrayTools.py b/Lib/fontTools/misc/arrayTools.py
index 3f39e7e..c05e195 100644
--- a/Lib/fontTools/misc/arrayTools.py
+++ b/Lib/fontTools/misc/arrayTools.py
@@ -4,6 +4,8 @@
#
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
import math
def calcBounds(array):
@@ -16,13 +18,27 @@
ys = [y for x, y in array]
return min(xs), min(ys), max(xs), max(ys)
-def updateBounds(bounds, (x, y), min=min, max=max):
+def calcIntBounds(array):
+ """Return the integer bounding rectangle of a 2D points array as a
+ tuple: (xMin, yMin, xMax, yMax)
+ """
+ xMin, yMin, xMax, yMax = calcBounds(array)
+ xMin = int(math.floor(xMin))
+ xMax = int(math.ceil(xMax))
+ yMin = int(math.floor(yMin))
+ yMax = int(math.ceil(yMax))
+ return xMin, yMin, xMax, yMax
+
+
+def updateBounds(bounds, p, min=min, max=max):
"""Return the bounding recangle of rectangle bounds and point (x, y)."""
+ (x, y) = p
xMin, yMin, xMax, yMax = bounds
return min(xMin, x), min(yMin, y), max(xMax, x), max(yMax, y)
-def pointInRect((x, y), rect):
+def pointInRect(p, rect):
"""Return True when point (x, y) is inside rect."""
+ (x, y) = p
xMin, yMin, xMax, yMax = rect
return (xMin <= x <= xMax) and (yMin <= y <= yMax)
@@ -45,53 +61,62 @@
return [int(math.floor(i+0.5)) for i in array]
-def normRect((xMin, yMin, xMax, yMax)):
+def normRect(rect):
"""Normalize the rectangle so that the following holds:
xMin <= xMax and yMin <= yMax
"""
+ (xMin, yMin, xMax, yMax) = rect
return min(xMin, xMax), min(yMin, yMax), max(xMin, xMax), max(yMin, yMax)
-def scaleRect((xMin, yMin, xMax, yMax), x, y):
+def scaleRect(rect, x, y):
"""Scale the rectangle by x, y."""
+ (xMin, yMin, xMax, yMax) = rect
return xMin * x, yMin * y, xMax * x, yMax * y
-def offsetRect((xMin, yMin, xMax, yMax), dx, dy):
+def offsetRect(rect, dx, dy):
"""Offset the rectangle by dx, dy."""
+ (xMin, yMin, xMax, yMax) = rect
return xMin+dx, yMin+dy, xMax+dx, yMax+dy
-def insetRect((xMin, yMin, xMax, yMax), dx, dy):
+def insetRect(rect, dx, dy):
"""Inset the rectangle by dx, dy on all sides."""
+ (xMin, yMin, xMax, yMax) = rect
return xMin+dx, yMin+dy, xMax-dx, yMax-dy
-def sectRect((xMin1, yMin1, xMax1, yMax1), (xMin2, yMin2, xMax2, yMax2)):
+def sectRect(rect1, rect2):
"""Return a boolean and a rectangle. If the input rectangles intersect, return
True and the intersecting rectangle. Return False and (0, 0, 0, 0) if the input
rectangles don't intersect.
"""
+ (xMin1, yMin1, xMax1, yMax1) = rect1
+ (xMin2, yMin2, xMax2, yMax2) = rect2
xMin, yMin, xMax, yMax = (max(xMin1, xMin2), max(yMin1, yMin2),
min(xMax1, xMax2), min(yMax1, yMax2))
if xMin >= xMax or yMin >= yMax:
- return 0, (0, 0, 0, 0)
- return 1, (xMin, yMin, xMax, yMax)
+ return False, (0, 0, 0, 0)
+ return True, (xMin, yMin, xMax, yMax)
-def unionRect((xMin1, yMin1, xMax1, yMax1), (xMin2, yMin2, xMax2, yMax2)):
+def unionRect(rect1, rect2):
"""Return the smallest rectangle in which both input rectangles are fully
enclosed. In other words, return the total bounding rectangle of both input
rectangles.
"""
+ (xMin1, yMin1, xMax1, yMax1) = rect1
+ (xMin2, yMin2, xMax2, yMax2) = rect2
xMin, yMin, xMax, yMax = (min(xMin1, xMin2), min(yMin1, yMin2),
max(xMax1, xMax2), max(yMax1, yMax2))
return (xMin, yMin, xMax, yMax)
-def rectCenter((xMin, yMin, xMax, yMax)):
+def rectCenter(rect0):
"""Return the center of the rectangle as an (x, y) coordinate."""
+ (xMin, yMin, xMax, yMax) = rect0
return (xMin+xMax)/2, (yMin+yMax)/2
-def intRect((xMin, yMin, xMax, yMax)):
+def intRect(rect1):
"""Return the rectangle, rounded off to integer values, but guaranteeing that
the resulting rectangle is NOT smaller than the original.
"""
- import math
+ (xMin, yMin, xMax, yMax) = rect1
xMin = int(math.floor(xMin))
yMin = int(math.floor(yMin))
xMax = int(math.ceil(xMax))
@@ -147,9 +172,9 @@
>>> unionRect((0, 10, 20, 30), (0, 40, 20, 50))
(0, 10, 20, 50)
>>> rectCenter((0, 0, 100, 200))
- (50, 100)
+ (50.0, 100.0)
>>> rectCenter((0, 0, 100, 199.0))
- (50, 99.5)
+ (50.0, 99.5)
>>> intRect((0.9, 2.9, 3.1, 4.1))
(0, 2, 4, 5)
"""
diff --git a/Lib/fontTools/misc/bezierTools.py b/Lib/fontTools/misc/bezierTools.py
index 4c897d5..9e38422 100644
--- a/Lib/fontTools/misc/bezierTools.py
+++ b/Lib/fontTools/misc/bezierTools.py
@@ -1,6 +1,8 @@
"""fontTools.misc.bezierTools.py -- tools for working with bezier path segments.
"""
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
__all__ = [
"calcQuadraticBounds",
@@ -86,19 +88,17 @@
"""
pt1x, pt1y = pt1
pt2x, pt2y = pt2
-
+
ax = (pt2x - pt1x)
ay = (pt2y - pt1y)
-
+
bx = pt1x
by = pt1y
-
- ax1 = (ax, ay)[isHorizontal]
-
+
if ax == 0:
return [(pt1, pt2)]
-
- t = float(where - (bx, by)[isHorizontal]) / ax
+
+ t = (where - (bx, by)[isHorizontal]) / ax
if 0 <= t < 1:
midPt = ax * t + bx, ay * t + by
return [(pt1, midPt), (midPt, pt2)]
@@ -132,8 +132,7 @@
a, b, c = calcQuadraticParameters(pt1, pt2, pt3)
solutions = solveQuadratic(a[isHorizontal], b[isHorizontal],
c[isHorizontal] - where)
- solutions = [t for t in solutions if 0 <= t < 1]
- solutions.sort()
+ solutions = sorted([t for t in solutions if 0 <= t < 1])
if not solutions:
return [(pt1, pt2, pt3)]
return _splitQuadraticAtT(a, b, c, *solutions)
@@ -157,8 +156,7 @@
a, b, c, d = calcCubicParameters(pt1, pt2, pt3, pt4)
solutions = solveCubic(a[isHorizontal], b[isHorizontal], c[isHorizontal],
d[isHorizontal] - where)
- solutions = [t for t in solutions if 0 <= t < 1]
- solutions.sort()
+ solutions = sorted([t for t in solutions if 0 <= t < 1])
if not solutions:
return [(pt1, pt2, pt3, pt4)]
return _splitCubicAtT(a, b, c, d, *solutions)
@@ -281,8 +279,7 @@
return roots
-def solveCubic(a, b, c, d,
- abs=abs, pow=pow, sqrt=sqrt, cos=cos, acos=acos, pi=pi):
+def solveCubic(a, b, c, d):
"""Solve a cubic equation where a, b, c and d are real.
a*x*x*x + b*x*x + c*x + d = 0
This function returns a list of roots. Note that the returned list
@@ -402,7 +399,7 @@
segments on a single line as a tuple.
"""
for segment in segments:
- print _segmentrepr(segment)
+ print(_segmentrepr(segment))
if __name__ == "__main__":
import doctest
diff --git a/Lib/fontTools/misc/eexec.py b/Lib/fontTools/misc/eexec.py
index ade0789..396f56e 100644
--- a/Lib/fontTools/misc/eexec.py
+++ b/Lib/fontTools/misc/eexec.py
@@ -2,36 +2,28 @@
charstring encryption algorithm as used by PostScript Type 1 fonts.
"""
-# Warning: Although a Python implementation is provided here,
-# all four public functions get overridden by the *much* faster
-# C extension module eexecOp, if available.
-
-import string
-
-error = "eexec.error"
-
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
def _decryptChar(cipher, R):
- cipher = ord(cipher)
+ cipher = byteord(cipher)
plain = ( (cipher ^ (R>>8)) ) & 0xFF
- R = ( (cipher + R) * 52845L + 22719L ) & 0xFFFF
- return chr(plain), R
+ R = ( (cipher + R) * 52845 + 22719 ) & 0xFFFF
+ return bytechr(plain), R
def _encryptChar(plain, R):
- plain = ord(plain)
+ plain = byteord(plain)
cipher = ( (plain ^ (R>>8)) ) & 0xFF
- R = ( (cipher + R) * 52845L + 22719L ) & 0xFFFF
- return chr(cipher), R
+ R = ( (cipher + R) * 52845 + 22719 ) & 0xFFFF
+ return bytechr(cipher), R
def decrypt(cipherstring, R):
- # I could probably speed this up by inlining _decryptChar,
- # but... we've got eexecOp, so who cares ;-)
plainList = []
for cipher in cipherstring:
plain, R = _decryptChar(cipher, R)
plainList.append(plain)
- plainstring = string.join(plainList, '')
+ plainstring = strjoin(plainList)
return plainstring, int(R)
def encrypt(plainstring, R):
@@ -39,7 +31,7 @@
for plain in plainstring:
cipher, R = _encryptChar(plain, R)
cipherList.append(cipher)
- cipherstring = string.join(cipherList, '')
+ cipherstring = strjoin(cipherList)
return cipherstring, int(R)
@@ -49,25 +41,15 @@
def deHexString(h):
import binascii
- h = "".join(h.split())
+ h = strjoin(h.split())
return binascii.unhexlify(h)
def _test():
- import fontTools.misc.eexecOp as eexecOp
testStr = "\0\0asdadads asds\265"
- print decrypt, decrypt(testStr, 12321)
- print eexecOp.decrypt, eexecOp.decrypt(testStr, 12321)
- print encrypt, encrypt(testStr, 12321)
- print eexecOp.encrypt, eexecOp.encrypt(testStr, 12321)
+ print(decrypt, decrypt(testStr, 12321))
+ print(encrypt, encrypt(testStr, 12321))
if __name__ == "__main__":
_test()
-
-
-try:
- from fontTools.misc.eexecOp import *
-except ImportError:
- pass # Use the slow Python versions
-
diff --git a/Lib/fontTools/misc/fixedTools.py b/Lib/fontTools/misc/fixedTools.py
new file mode 100644
index 0000000..037a0e2
--- /dev/null
+++ b/Lib/fontTools/misc/fixedTools.py
@@ -0,0 +1,65 @@
+"""fontTools.misc.fixedTools.py -- tools for working with fixed numbers.
+"""
+
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
+
+__all__ = [
+ "fixedToFloat",
+ "floatToFixed",
+]
+
+def fixedToFloat(value, precisionBits):
+ """Converts a fixed-point number to a float, choosing the float
+ that has the shortest decimal reprentation. Eg. to convert a
+ fixed number in a 2.14 format, use precisionBits=14. This is
+ pretty slow compared to a simple division. Use sporadically.
+
+ >>> fixedToFloat(13107, 14)
+ 0.8
+ >>> fixedToFloat(0, 14)
+ 0.0
+ >>> fixedToFloat(0x4000, 14)
+ 1.0
+ """
+
+ if not value: return 0.0
+
+ scale = 1 << precisionBits
+ value /= scale
+ eps = .5 / scale
+ digits = (precisionBits + 2) // 3
+ fmt = "%%.%df" % digits
+ lo = fmt % (value - eps)
+ hi = fmt % (value + eps)
+ out = []
+ length = min(len(lo), len(hi))
+ for i in range(length):
+ if lo[i] != hi[i]:
+ break;
+ out.append(lo[i])
+ outlen = len(out)
+ if outlen < length:
+ out.append(max(lo[outlen], hi[outlen]))
+ return float(strjoin(out))
+
+def floatToFixed(value, precisionBits):
+ """Converts a float to a fixed-point number given the number of
+ precisionBits. Ie. int(round(value * (1<<precisionBits))).
+
+ >>> floatToFixed(0.8, 14)
+ 13107
+ >>> floatToFixed(1.0, 14)
+ 16384
+ >>> floatToFixed(1, 14)
+ 16384
+ >>> floatToFixed(0, 14)
+ 0
+ """
+
+ return int(round(value * (1<<precisionBits)))
+
+
+if __name__ == "__main__":
+ import doctest
+ doctest.testmod()
diff --git a/Lib/fontTools/misc/homeResFile.py b/Lib/fontTools/misc/homeResFile.py
index b9463e4..dc61c2f 100644
--- a/Lib/fontTools/misc/homeResFile.py
+++ b/Lib/fontTools/misc/homeResFile.py
@@ -1,5 +1,7 @@
"""Mac-only module to find the home file of a resource."""
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
from fontTools.misc import sstruct
import array
import calldll
@@ -49,7 +51,7 @@
ioFCBParID: l
"""
-class ParamBlock:
+class ParamBlock(object):
"""Wrapper for the very low level FCBPB record."""
@@ -70,14 +72,14 @@
ptr = buf.buffer_info()[0]
err = _getInfo(ptr)
if err:
- raise Res.Error, ("can't get file info", err)
+ raise Res.Error("can't get file info", err)
sstruct.unpack(_FCBPBFormat, buf.tostring(), self)
self.__haveInfo = 1
def getFileName(self):
self.getInfo()
data = self.__fileName.tostring()
- return data[1:ord(data[0])+1]
+ return data[1:byteord(data[0])+1]
def getFSSpec(self):
self.getInfo()
@@ -91,4 +93,4 @@
if __name__ == "__main__":
fond = Res.GetNamedResource("FOND", "Helvetica")
- print HomeResFile(fond)
+ print(HomeResFile(fond))
diff --git a/Lib/fontTools/misc/macCreatorType.py b/Lib/fontTools/misc/macCreatorType.py
index 57d158c..bcbc4cb 100644
--- a/Lib/fontTools/misc/macCreatorType.py
+++ b/Lib/fontTools/misc/macCreatorType.py
@@ -1,13 +1,16 @@
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
import sys
try:
import MacOS
except ImportError:
MacOS = None
+from .py23 import *
def _reverseString(s):
s = list(s)
s.reverse()
- return "".join(s)
+ return strjoin(s)
def getMacCreatorAndType(path):
diff --git a/Lib/fontTools/misc/psCharStrings.py b/Lib/fontTools/misc/psCharStrings.py
index ccb7e1c..571bc89 100644
--- a/Lib/fontTools/misc/psCharStrings.py
+++ b/Lib/fontTools/misc/psCharStrings.py
@@ -2,9 +2,9 @@
CFF dictionary data and Type1/Type2 CharStrings.
"""
-import types
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
import struct
-import string
DEBUG = 0
@@ -35,38 +35,35 @@
realNibblesDict[realNibbles[_i]] = _i
-class ByteCodeBase:
+class ByteCodeBase(object):
def read_byte(self, b0, data, index):
return b0 - 139, index
def read_smallInt1(self, b0, data, index):
- b1 = ord(data[index])
+ b1 = byteord(data[index])
return (b0-247)*256 + b1 + 108, index+1
def read_smallInt2(self, b0, data, index):
- b1 = ord(data[index])
+ b1 = byteord(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)
+ value, = struct.unpack(">h", data[index:index+2])
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)
+ value, = struct.unpack(">l", data[index:index+4])
return value, index+4
def read_fixed1616(self, b0, data, index):
- bin = data[index] + data[index+1] + data[index+2] + data[index+3]
- value, = struct.unpack(">l", bin)
- return value / 65536.0, index+4
+ value, = struct.unpack(">l", data[index:index+4])
+ return value / 65536, index+4
def read_realNumber(self, b0, data, index):
number = ''
- while 1:
- b = ord(data[index])
+ while True:
+ b = byteord(data[index])
index = index + 1
nibble0 = (b & 0xf0) >> 4
nibble1 = b & 0x0f
@@ -87,7 +84,7 @@
oper[item[0]] = item[1]
else:
oper[item[0]] = item[1:]
- if type(item[0]) == types.TupleType:
+ if isinstance(item[0], tuple):
opc[item[1]] = item[0]
else:
opc[item[1]] = (item[0],)
@@ -154,27 +151,27 @@
def getIntEncoder(format):
if format == "cff":
- fourByteOp = chr(29)
+ fourByteOp = bytechr(29)
elif format == "t1":
- fourByteOp = chr(255)
+ fourByteOp = bytechr(255)
else:
assert format == "t2"
fourByteOp = None
- def encodeInt(value, fourByteOp=fourByteOp, chr=chr,
+ def encodeInt(value, fourByteOp=fourByteOp, bytechr=bytechr,
pack=struct.pack, unpack=struct.unpack):
if -107 <= value <= 107:
- code = chr(value + 139)
+ code = bytechr(value + 139)
elif 108 <= value <= 1131:
value = value - 108
- code = chr((value >> 8) + 247) + chr(value & 0xFF)
+ code = bytechr((value >> 8) + 247) + bytechr(value & 0xFF)
elif -1131 <= value <= -108:
value = -value - 108
- code = chr((value >> 8) + 251) + chr(value & 0xFF)
+ code = bytechr((value >> 8) + 251) + bytechr(value & 0xFF)
elif fourByteOp is None:
# T2 only supports 2 byte ints
if -32768 <= value <= 32767:
- code = chr(28) + pack(">h", value)
+ code = bytechr(28) + pack(">h", value)
else:
# Backwards compatible hack: due to a previous bug in FontTools,
# 16.16 fixed numbers were written out as 4-byte ints. When
@@ -188,7 +185,7 @@
sys.stderr.write("Warning: 4-byte T2 number got passed to the "
"IntType handler. This should happen only when reading in "
"old XML files.\n")
- code = chr(255) + pack(">l", value)
+ code = bytechr(255) + pack(">l", value)
else:
code = fourByteOp + pack(">l", value)
return code
@@ -202,7 +199,7 @@
def encodeFixed(f, pack=struct.pack):
# For T2 only
- return "\xff" + pack(">l", int(round(f * 65536)))
+ return b"\xff" + pack(">l", int(round(f * 65536)))
def encodeFloat(f):
# For CFF only, used in cffLib
@@ -222,9 +219,9 @@
nibbles.append(0xf)
if len(nibbles) % 2:
nibbles.append(0xf)
- d = chr(30)
+ d = bytechr(30)
for i in range(0, len(nibbles), 2):
- d = d + chr(nibbles[i] << 4 | nibbles[i+1])
+ d = d + bytechr(nibbles[i] << 4 | nibbles[i+1])
return d
@@ -242,7 +239,7 @@
self.bytecode = bytecode
self.program = program
self.private = private
- self.globalSubrs = globalSubrs
+ self.globalSubrs = globalSubrs if globalSubrs is not None else []
def __repr__(self):
if self.bytecode is None:
@@ -287,24 +284,24 @@
token = program[i]
i = i + 1
tp = type(token)
- if tp == types.StringType:
+ if issubclass(tp, basestring):
try:
- bytecode.extend(map(chr, opcodes[token]))
+ bytecode.extend(bytechr(b) for b in opcodes[token])
except KeyError:
- raise CharStringCompileError, "illegal operator: %s" % token
+ raise CharStringCompileError("illegal operator: %s" % token)
if token in ('hintmask', 'cntrmask'):
bytecode.append(program[i]) # hint mask
i = i + 1
- elif tp == types.IntType:
+ elif tp == int:
bytecode.append(encodeInt(token))
- elif tp == types.FloatType:
+ elif tp == float:
bytecode.append(encodeFixed(token))
else:
assert 0, "unsupported type: %s" % tp
try:
- bytecode = "".join(bytecode)
+ bytecode = bytesjoin(bytecode)
except TypeError:
- print bytecode
+ print(bytecode)
raise
self.setBytecode(bytecode)
@@ -320,11 +317,11 @@
self.program = None
def getToken(self, index,
- len=len, ord=ord, getattr=getattr, type=type, StringType=types.StringType):
+ len=len, byteord=byteord, getattr=getattr, type=type, StringType=str):
if self.bytecode is not None:
if index >= len(self.bytecode):
return None, 0, 0
- b0 = ord(self.bytecode[index])
+ b0 = byteord(self.bytecode[index])
index = index + 1
code = self.operandEncoding[b0]
handler = getattr(self, code)
@@ -334,7 +331,7 @@
return None, 0, 0
token = self.program[index]
index = index + 1
- isOperator = type(token) == StringType
+ isOperator = isinstance(token, StringType)
return token, isOperator, index
def getBytes(self, index, nBytes):
@@ -350,7 +347,7 @@
def do_operator(self, b0, data, index):
if b0 == 12:
- op = (b0, ord(data[index]))
+ op = (b0, byteord(data[index]))
index = index+1
else:
op = b0
@@ -364,33 +361,33 @@
else:
index = 0
args = []
- while 1:
+ while True:
token, isOperator, index = self.getToken(index)
if token is None:
break
if isOperator:
- args = map(str, args)
+ args = [str(arg) for arg in args]
if token in ('hintmask', 'cntrmask'):
hintMask, isOperator, index = self.getToken(index)
bits = []
for byte in hintMask:
- bits.append(num2binary(ord(byte), 8))
- hintMask = string.join(bits, "")
- line = string.join(args + [token, hintMask], " ")
+ bits.append(num2binary(byteord(byte), 8))
+ hintMask = strjoin(bits)
+ line = ' '.join(args + [token, hintMask])
else:
- line = string.join(args + [token], " ")
+ line = ' '.join(args + [token])
xmlWriter.write(line)
xmlWriter.newline()
args = []
else:
args.append(token)
- def fromXML(self, (name, attrs, content)):
+ def fromXML(self, name, attrs, content):
from fontTools.misc.textTools import binary2num, readHex
if attrs.get("raw"):
self.setBytecode(readHex(content))
return
- content = "".join(content)
+ content = strjoin(content)
content = content.split()
program = []
end = len(content)
@@ -407,9 +404,9 @@
program.append(token)
if token in ('hintmask', 'cntrmask'):
mask = content[i]
- maskBytes = ""
+ maskBytes = b""
for j in range(0, len(mask), 8):
- maskBytes = maskBytes + chr(binary2num(mask[j:j+8]))
+ maskBytes = maskBytes + bytechr(binary2num(mask[j:j+8]))
program.append(maskBytes)
i = i + 1
else:
@@ -468,11 +465,11 @@
raise TypeError("Type 1 charstrings don't support floating point operands")
def decompile(self):
- if self.program is not None:
+ if self.bytecode is None:
return
program = []
index = 0
- while 1:
+ while True:
token, isOperator, index = self.getToken(index)
if token is None:
break
@@ -485,7 +482,7 @@
self.width = extractor.width
-class SimpleT2Decompiler:
+class SimpleT2Decompiler(object):
def __init__(self, localSubrs, globalSubrs):
self.localSubrs = localSubrs
@@ -510,7 +507,7 @@
pushToProgram = lambda x: None
pushToStack = self.operandStack.append
index = 0
- while 1:
+ while True:
token, isOperator, index = charString.getToken(index)
if token is None:
break # we're done!
@@ -579,7 +576,7 @@
def op_hintmask(self, index):
if not self.hintMaskBytes:
self.countHints()
- self.hintMaskBytes = (self.hintCount + 7) / 8
+ self.hintMaskBytes = (self.hintCount + 7) // 8
hintMaskBytes, index = self.callingStack[-1].getBytes(index, self.hintMaskBytes)
return hintMaskBytes, index
@@ -587,7 +584,7 @@
def countHints(self):
args = self.popall()
- self.hintCount = self.hintCount + len(args) / 2
+ self.hintCount = self.hintCount + len(args) // 2
# misc
def op_and(self, index):
@@ -695,7 +692,7 @@
def countHints(self):
args = self.popallWidth()
- self.hintCount = self.hintCount + len(args) / 2
+ self.hintCount = self.hintCount + len(args) // 2
#
# hint operators
@@ -862,7 +859,7 @@
# MultipleMaster. Well...
#
def op_blend(self, index):
- args = self.popall()
+ self.popall()
# misc
def op_and(self, index):
@@ -882,8 +879,8 @@
def op_div(self, index):
num2 = self.pop()
num1 = self.pop()
- d1 = num1/num2
- d2 = float(num1)/num2
+ d1 = num1//num2
+ d2 = num1/num2
if d1 == d2:
self.push(d1)
else:
@@ -918,7 +915,7 @@
raise NotImplementedError
#
- # miscelaneous helpers
+ # miscellaneous helpers
#
def alternatingLineto(self, isHorizontal):
args = self.popall()
@@ -1114,7 +1111,7 @@
lenData = len(data)
push = self.stack.append
while index < lenData:
- b0 = ord(data[index])
+ b0 = byteord(data[index])
index = index + 1
code = self.operandEncoding[b0]
handler = getattr(self, code)
@@ -1128,13 +1125,13 @@
return value
def popall(self):
- all = self.stack[:]
+ args = self.stack[:]
del self.stack[:]
- return all
+ return args
def do_operator(self, b0, data, index):
if b0 == 12:
- op = (b0, ord(data[index]))
+ op = (b0, byteord(data[index]))
index = index+1
else:
op = b0
@@ -1143,7 +1140,7 @@
return None, index
def handle_operator(self, operator, argType):
- if type(argType) == type(()):
+ if isinstance(argType, type(())):
value = ()
for i in range(len(argType)-1, -1, -1):
arg = argType[i]
diff --git a/Lib/fontTools/misc/psLib.py b/Lib/fontTools/misc/psLib.py
index 3c7bd37..5b99cb3 100644
--- a/Lib/fontTools/misc/psLib.py
+++ b/Lib/fontTools/misc/psLib.py
@@ -1,10 +1,10 @@
-import re
-import types
-from string import whitespace
-import StringIO
-
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
from fontTools.misc import eexec
-from psOperators import *
+from .psOperators import *
+import re
+import collections
+from string import whitespace
ps_special = '()<>[]{}%' # / is one too, but we take care of that one differently
@@ -38,9 +38,9 @@
class PSError(Exception): pass
-class PSTokenizer(StringIO.StringIO):
+class PSTokenizer(StringIO):
- def getnexttoken(self,
+ def getnexttoken(self,
# localize some stuff, for performance
len=len,
ps_special=ps_special,
@@ -69,18 +69,18 @@
tokentype = 'do_string'
m = stringmatch(buf, pos)
if m is None:
- raise PSTokenError, 'bad string at character %d' % pos
+ raise PSTokenError('bad string at character %d' % pos)
_, nextpos = m.span()
token = buf[pos:nextpos]
elif char == '<':
tokentype = 'do_hexstring'
m = hexstringmatch(buf, pos)
if m is None:
- raise PSTokenError, 'bad hexstring at character %d' % pos
+ raise PSTokenError('bad hexstring at character %d' % pos)
_, nextpos = m.span()
token = buf[pos:nextpos]
else:
- raise PSTokenError, 'bad token at character %d' % pos
+ raise PSTokenError('bad token at character %d' % pos)
else:
if char == '/':
tokentype = 'do_literal'
@@ -89,7 +89,7 @@
tokentype = ''
m = endmatch(buf, pos)
if m is None:
- raise PSTokenError, 'bad token at character %d' % pos
+ raise PSTokenError('bad token at character %d' % pos)
_, nextpos = m.span()
token = buf[pos:nextpos]
self.pos = pos + len(token)
@@ -143,7 +143,7 @@
def suckoperators(self, systemdict, klass):
for name in dir(klass):
attr = getattr(self, name)
- if callable(attr) and name[:3] == 'ps_':
+ if isinstance(attr, collections.Callable) and name[:3] == 'ps_':
name = name[3:]
systemdict[name] = ps_operator(name, attr)
for baseclass in klass.__bases__:
@@ -172,15 +172,15 @@
finally:
if self.tokenizer is not None:
if 0:
- 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 '- - - - - - -'
+ 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':
+ if object.type != 'operatortype':
object = self.resolve_name(object.value)
if object.literal:
self.push(object)
@@ -200,9 +200,9 @@
def resolve_name(self, name):
dictstack = self.dictstack
for i in range(len(dictstack)-1, -1, -1):
- if dictstack[i].has_key(name):
+ if name in dictstack[i]:
return dictstack[i][name]
- raise PSError, 'name error: ' + str(name)
+ raise PSError('name error: ' + str(name))
def do_token(self, token,
int=int,
@@ -270,7 +270,7 @@
elif token == ']':
return ps_name(']')
else:
- raise PSTokenError, 'huh?'
+ raise PSTokenError('huh?')
def push(self, object):
self.stack.append(object)
@@ -278,11 +278,11 @@
def pop(self, *types):
stack = self.stack
if not stack:
- raise PSError, 'stack underflow'
+ raise PSError('stack underflow')
object = stack[-1]
if types:
if object.type not in types:
- raise PSError, 'typecheck, expected %s, found %s' % (`types`, object.type)
+ raise PSError('typecheck, expected %s, found %s' % (repr(types), object.type))
del stack[-1]
return object
@@ -304,11 +304,11 @@
def unpack_item(item):
tp = type(item.value)
- if tp == types.DictionaryType:
+ if tp == dict:
newitem = {}
for key, value in item.value.items():
newitem[key] = unpack_item(value)
- elif tp == types.ListType:
+ elif tp == list:
newitem = [None] * len(item.value)
for i in range(len(item.value)):
newitem[i] = unpack_item(item.value[i])
@@ -320,20 +320,20 @@
def suckfont(data):
import re
- m = re.search(r"/FontName\s+/([^ \t\n\r]+)\s+def", data)
+ m = re.search(br"/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(b"/Helvetica 4 dict dup /Encoding StandardEncoding put definefont pop")
interpreter.interpret(data)
fontdir = interpreter.dictstack[0]['FontDirectory'].value
- if fontdir.has_key(fontName):
+ if fontName in fontdir:
rawfont = fontdir[fontName]
else:
# fall back, in case fontName wasn't found
- fontNames = fontdir.keys()
+ fontNames = list(fontdir.keys())
if len(fontNames) > 1:
fontNames.remove("Helvetica")
fontNames.sort()
diff --git a/Lib/fontTools/misc/psOperators.py b/Lib/fontTools/misc/psOperators.py
index 9fb3723..c26830d 100644
--- a/Lib/fontTools/misc/psOperators.py
+++ b/Lib/fontTools/misc/psOperators.py
@@ -1,3 +1,6 @@
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
+
_accessstrings = {0: "", 1: "readonly", 2: "executeonly", 3: "noaccess"}
@@ -116,13 +119,12 @@
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':
+ 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()
+ items = sorted(charstrings.items())
return 'xxx'
class ps_font(ps_object):
@@ -135,8 +137,7 @@
pass
else:
psstring = psstring + _type1_item_repr(key, value)
- items = self.value.items()
- items.sort()
+ items = sorted(self.value.items())
for key, value in items:
if key not in _type1_pre_eexec_order + _type1_post_eexec_order:
psstring = psstring + _type1_item_repr(key, value)
@@ -159,9 +160,7 @@
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)
+ items = sorted(self.value.items())
for key, value in items:
access = _accessstrings[value.access]
if access:
@@ -194,23 +193,23 @@
class ps_string(ps_object):
def __str__(self):
- return "(%s)" % `self.value`[1:-1]
+ return "(%s)" % repr(self.value)[1:-1]
class ps_integer(ps_object):
def __str__(self):
- return `self.value`
+ return repr(self.value)
class ps_real(ps_object):
def __str__(self):
- return `self.value`
+ return repr(self.value)
class PSOperators:
def ps_def(self):
- object = self.pop()
+ obj = self.pop()
name = self.pop()
- self.dictstack[-1][name.value] = object
+ self.dictstack[-1][name.value] = obj
def ps_bind(self):
proc = self.pop('proceduretype')
@@ -225,16 +224,16 @@
else:
if not item.literal:
try:
- object = self.resolve_name(item.value)
+ obj = self.resolve_name(item.value)
except:
pass
else:
- if object.type == 'operatortype':
- proc.value[i] = object
+ if obj.type == 'operatortype':
+ proc.value[i] = obj
def ps_exch(self):
if len(self.stack) < 2:
- raise RuntimeError, 'stack underflow'
+ raise RuntimeError('stack underflow')
obj1 = self.pop()
obj2 = self.pop()
self.push(obj1)
@@ -242,15 +241,15 @@
def ps_dup(self):
if not self.stack:
- raise RuntimeError, 'stack underflow'
+ raise RuntimeError('stack underflow')
self.push(self.stack[-1])
def ps_exec(self):
- object = self.pop()
- if object.type == 'proceduretype':
- self.call_procedure(object)
+ obj = self.pop()
+ if obj.type == 'proceduretype':
+ self.call_procedure(obj)
else:
- self.handle_object(object)
+ self.handle_object(obj)
def ps_count(self):
self.push(ps_integer(len(self.stack)))
@@ -263,7 +262,7 @@
def ps_ne(self):
any1 = self.pop()
any2 = self.pop()
- self.push(ps_boolean(any1.value <> any2.value))
+ self.push(ps_boolean(any1.value != any2.value))
def ps_cvx(self):
obj = self.pop()
@@ -287,7 +286,7 @@
key = self.pop()
name = key.value
for i in range(len(self.dictstack)-1, -1, -1):
- if self.dictstack[i].has_key(name):
+ if name in self.dictstack[i]:
self.dictstack[i][name] = value
break
self.dictstack[-1][name] = value
@@ -310,17 +309,17 @@
self.push(ps_file(self.tokenizer))
def ps_eexec(self):
- file = self.pop('filetype').value
- file.starteexec()
+ f = self.pop('filetype').value
+ f.starteexec()
def ps_closefile(self):
- file = self.pop('filetype').value
- file.skipwhite()
- file.stopeexec()
+ f = self.pop('filetype').value
+ f.skipwhite()
+ f.stopeexec()
def ps_cleartomark(self):
obj = self.pop()
- while obj <> self.mark:
+ while obj != self.mark:
obj = self.pop()
def ps_readstring(self,
@@ -328,31 +327,29 @@
len = len):
s = self.pop('stringtype')
oldstr = s.value
- file = self.pop('filetype')
+ f = 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))
+ f.value.pos = f.value.pos + 1
+ newstr = f.value.read(len(oldstr))
s.value = newstr
self.push(s)
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)))
+ d = self.pop('dicttype', 'fonttype')
+ self.push(ps_boolean(key.value in d.value))
def ps_if(self):
proc = self.pop('proceduretype')
- bool = self.pop('booleantype')
- if bool.value:
+ if self.pop('booleantype').value:
self.call_procedure(proc)
def ps_ifelse(self):
proc2 = self.pop('proceduretype')
proc1 = self.pop('proceduretype')
- bool = self.pop('booleantype')
- if bool.value:
+ if self.pop('booleantype').value:
self.call_procedure(proc1)
else:
self.call_procedure(proc2)
@@ -384,7 +381,7 @@
def ps_print(self):
str = self.pop('stringtype')
- print 'PS output --->', str.value
+ print('PS output --->', str.value)
def ps_anchorsearch(self):
seek = self.pop('stringtype')
@@ -411,8 +408,7 @@
def ps_load(self):
name = self.pop()
- object = self.resolve_name(name.value)
- self.push(object)
+ self.push(self.resolve_name(name.value))
def ps_put(self):
obj1 = self.pop()
@@ -440,7 +436,7 @@
elif tp == 'stringtype':
self.push(ps_integer(ord(obj2.value[obj1.value])))
else:
- assert 0, "shouldn't get here"
+ assert False, "shouldn't get here"
def ps_getinterval(self):
obj1 = self.pop('integertype')
@@ -466,13 +462,12 @@
obj3.value = newstr
def ps_cvn(self):
- str = self.pop('stringtype')
- self.push(ps_name(str.value))
+ self.push(ps_name(self.pop('stringtype').value))
def ps_index(self):
n = self.pop('integertype').value
if n < 0:
- raise RuntimeError, 'index may not be negative'
+ raise RuntimeError('index may not be negative')
self.push(self.stack[-1-n])
def ps_for(self):
@@ -528,21 +523,19 @@
self.pop()
def ps_dict(self):
- num = self.pop('integertype')
- dict = ps_dict({})
- self.push(dict)
+ self.pop('integertype')
+ self.push(ps_dict({}))
def ps_begin(self):
- dict = self.pop('dicttype')
- self.dictstack.append(dict.value)
+ self.dictstack.append(self.pop('dicttype').value)
def ps_end(self):
if len(self.dictstack) > 2:
del self.dictstack[-1]
else:
- raise RuntimeError, 'dictstack underflow'
+ raise RuntimeError('dictstack underflow')
notdef = '.notdef'
from fontTools.encodings.StandardEncoding import StandardEncoding
-ps_StandardEncoding = map(ps_name, StandardEncoding)
+ps_StandardEncoding = list(map(ps_name, StandardEncoding))
diff --git a/Lib/fontTools/misc/py23.py b/Lib/fontTools/misc/py23.py
new file mode 100644
index 0000000..0e5e45f
--- /dev/null
+++ b/Lib/fontTools/misc/py23.py
@@ -0,0 +1,83 @@
+"""Python 2/3 compat layer."""
+
+from __future__ import print_function, division
+
+try:
+ basestring
+except NameError:
+ basestring = str
+
+try:
+ unicode
+except NameError:
+ unicode = str
+
+try:
+ unichr
+ bytechr = chr
+ byteord = ord
+except:
+ unichr = chr
+ def bytechr(n):
+ return bytes([n])
+ def byteord(c):
+ return c if isinstance(c, int) else ord(c)
+
+try:
+ from StringIO import StringIO
+except ImportError:
+ from io import BytesIO as StringIO
+
+def strjoin(iterable):
+ return ''.join(iterable)
+if str == bytes:
+ class Tag(str):
+ def tobytes(self):
+ if isinstance(self, bytes):
+ return self
+ else:
+ return self.encode('latin1')
+
+ def tostr(s, encoding='ascii'):
+ if not isinstance(s, str):
+ return s.encode(encoding)
+ else:
+ return s
+ tobytes = tostr
+
+ bytesjoin = strjoin
+else:
+ class Tag(str):
+
+ @staticmethod
+ def transcode(blob):
+ if not isinstance(blob, str):
+ blob = blob.decode('latin-1')
+ return blob
+
+ def __new__(self, content):
+ return str.__new__(self, self.transcode(content))
+ def __ne__(self, other):
+ return not self.__eq__(other)
+ def __eq__(self, other):
+ return str.__eq__(self, self.transcode(other))
+
+ def __hash__(self):
+ return str.__hash__(self)
+
+ def tobytes(self):
+ return self.encode('latin-1')
+
+ def tostr(s, encoding='ascii'):
+ if not isinstance(s, str):
+ return s.decode(encoding)
+ else:
+ return s
+ def tobytes(s, encoding='ascii'):
+ if not isinstance(s, bytes):
+ return s.encode(encoding)
+ else:
+ return s
+
+ def bytesjoin(iterable):
+ return b''.join(tobytes(item) for item in iterable)
diff --git a/Lib/fontTools/misc/sstruct.py b/Lib/fontTools/misc/sstruct.py
index 7bc4772..e8e11cf 100644
--- a/Lib/fontTools/misc/sstruct.py
+++ b/Lib/fontTools/misc/sstruct.py
@@ -5,14 +5,14 @@
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
+Just like struct, we use fmt 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
+is not allowed but 'myString:5s' is). The 'x' fmt character
(pad byte) is treated as 'special', since it is by definition
anonymous. Extra whitespace is allowed everywhere.
@@ -22,14 +22,14 @@
the number of bits after the point. Fixed point numbers get
converted to floats.
-pack(format, object):
+pack(fmt, 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)
+unpack(fmt, 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
@@ -37,67 +37,74 @@
each struct element. In the latter two cases, 'object' itself
is returned.
-unpack2(format, data, object=None)
+unpack2(fmt, 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:
+calcsize(fmt)
+ like struct.calcsize(), but uses our own fmt 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()...
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
+from fontTools.misc.fixedTools import fixedToFloat as fi2fl, floatToFixed as fl2fi
+import struct
+import re
__version__ = "1.2"
__copyright__ = "Copyright 1998, Just van Rossum <just@letterror.com>"
-import struct
-import re
-import types
+class Error(Exception):
+ pass
-error = "sstruct.error"
-
-def pack(format, object):
- formatstring, names, fixes = getformat(format)
+def pack(fmt, obj):
+ formatstring, names, fixes = getformat(fmt)
elements = []
- if type(object) is not types.DictType:
- object = object.__dict__
+ if not isinstance(obj, dict):
+ obj = obj.__dict__
for name in names:
- value = object[name]
- if fixes.has_key(name):
+ value = obj[name]
+ if name in fixes:
# fixed point conversion
- value = int(round(value*fixes[name]))
+ value = fl2fi(value, fixes[name])
+ elif isinstance(value, basestring):
+ value = tobytes(value)
elements.append(value)
- data = apply(struct.pack, (formatstring,) + tuple(elements))
+ data = 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
+def unpack(fmt, data, obj=None):
+ if obj is None:
+ obj = {}
+ data = tobytes(data)
+ formatstring, names, fixes = getformat(fmt)
+ if isinstance(obj, dict):
+ d = obj
else:
- dict = object.__dict__
+ d = obj.__dict__
elements = struct.unpack(formatstring, data)
for i in range(len(names)):
name = names[i]
value = elements[i]
- if fixes.has_key(name):
+ if name in fixes:
# fixed point conversion
- value = value / fixes[name]
- dict[name] = value
- return object
+ value = fi2fl(value, fixes[name])
+ elif isinstance(value, bytes):
+ try:
+ value = tostr(value)
+ except UnicodeDecodeError:
+ pass
+ d[name] = value
+ return obj
-def unpack2(format, data, object=None):
- length = calcsize(format)
- return unpack(format, data[:length], object), data[length:]
+def unpack2(fmt, data, obj=None):
+ length = calcsize(fmt)
+ return unpack(fmt, data[:length], obj), data[length:]
-def calcsize(format):
- formatstring, names, fixes = getformat(format)
+def calcsize(fmt):
+ formatstring, names, fixes = getformat(fmt)
return struct.calcsize(formatstring)
@@ -106,13 +113,13 @@
"\s*" # whitespace
"([A-Za-z_][A-Za-z_0-9]*)" # name (python identifier)
"\s*:\s*" # whitespace : whitespace
- "([cbBhHiIlLfd]|[0-9]+[ps]|" # formatchar...
+ "([cbBhHiIlLqQfd]|[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)
+# matches the special struct fmt chars and 'x' (pad byte)
_extraRE = re.compile("\s*([x@=<>!])\s*(#.*)?$")
# matches an "empty" string, possibly containing whitespace and/or a comment
@@ -125,11 +132,11 @@
_formatcache = {}
-def getformat(format):
+def getformat(fmt):
try:
- formatstring, names, fixes = _formatcache[format]
+ formatstring, names, fixes = _formatcache[fmt]
except KeyError:
- lines = re.split("[\n;]", format)
+ lines = re.split("[\n;]", fmt)
formatstring = ""
names = []
fixes = {}
@@ -139,12 +146,12 @@
m = _extraRE.match(line)
if m:
formatchar = m.group(1)
- if formatchar <> 'x' and formatstring:
- raise error, "a special format char must be first"
+ if formatchar != 'x' and formatstring:
+ raise Error("a special fmt char must be first")
else:
m = _elementRE.match(line)
if not m:
- raise error, "syntax error in format: '%s'" % line
+ raise Error("syntax error in fmt: '%s'" % line)
name = m.group(1)
names.append(name)
formatchar = m.group(2)
@@ -154,16 +161,16 @@
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"
+ 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)
+ fixes[name] = after
formatstring = formatstring + formatchar
- _formatcache[format] = formatstring, names, fixes
+ _formatcache[fmt] = formatstring, names, fixes
return formatstring, names, fixes
def _test():
- format = """
+ fmt = """
# comments are allowed
> # big endian (see documentation for struct)
# empty lines are allowed:
@@ -177,9 +184,9 @@
afixed: 16.16F
"""
- print 'size:', calcsize(format)
+ print('size:', calcsize(fmt))
- class foo:
+ class foo(object):
pass
i = foo()
@@ -193,12 +200,12 @@
i.adouble = 0.5
i.afixed = 1.5
- data = pack(format, i)
- print 'data:', `data`
- print unpack(format, data)
+ data = pack(fmt, i)
+ print('data:', repr(data))
+ print(unpack(fmt, data))
i2 = foo()
- unpack(format, data, i2)
- print vars(i2)
+ unpack(fmt, data, i2)
+ print(vars(i2))
if __name__ == "__main__":
_test()
diff --git a/Lib/fontTools/misc/textTools.py b/Lib/fontTools/misc/textTools.py
index 2fd5d0b..4d0f05a 100644
--- a/Lib/fontTools/misc/textTools.py
+++ b/Lib/fontTools/misc/textTools.py
@@ -1,28 +1,29 @@
-"""fontTools.misc.textTools.py -- miscelaneous routines."""
+"""fontTools.misc.textTools.py -- miscellaneous routines."""
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
import string
def safeEval(data, eval=eval):
"""A (kindof) safe replacement for eval."""
- return eval(data, {"__builtins__":{}}, {})
+ return eval(data, {"__builtins__":{}})
def readHex(content):
"""Convert a list of hex strings to binary data."""
- return deHexStr(''.join([ chunk for chunk in content if isinstance(chunk,str) ]))
+ return deHexStr(strjoin(chunk for chunk in content if isinstance(chunk, basestring)))
def deHexStr(hexdata):
"""Convert a hex string to binary data."""
- parts = string.split(hexdata)
- hexdata = string.join(parts, "")
+ hexdata = strjoin(hexdata.split())
if len(hexdata) % 2:
hexdata = hexdata + "0"
data = []
for i in range(0, len(hexdata), 2):
- data.append(chr(string.atoi(hexdata[i:i+2], 16)))
- return "".join(data)
+ data.append(bytechr(int(hexdata[i:i+2], 16)))
+ return bytesjoin(data)
def hexStr(data):
@@ -30,36 +31,36 @@
h = string.hexdigits
r = ''
for c in data:
- i = ord(c)
+ i = byteord(c)
r = r + h[(i >> 4) & 0xF] + h[i & 0xF]
return r
def num2binary(l, bits=32):
- all = []
- bin = ""
+ items = []
+ binary = ""
for i in range(bits):
if l & 0x1:
- bin = "1" + bin
+ binary = "1" + binary
else:
- bin = "0" + bin
+ binary = "0" + binary
l = l >> 1
if not ((i+1) % 8):
- all.append(bin)
- bin = ""
- if bin:
- all.append(bin)
- all.reverse()
+ items.append(binary)
+ binary = ""
+ if binary:
+ items.append(binary)
+ items.reverse()
assert l in (0, -1), "number doesn't fit in number of bits"
- return string.join(all, " ")
+ return ' '.join(items)
def binary2num(bin):
- bin = string.join(string.split(bin), "")
+ bin = strjoin(bin.split())
l = 0
for digit in bin:
l = l << 1
- if digit <> "0":
+ if digit != "0":
l = l | 0x1
return l
@@ -70,19 +71,7 @@
"""
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)
+ return sorted(alist, key=lambda a: (a.lower(), a))
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)
+ return sorted(alist)
diff --git a/Lib/fontTools/misc/transform.py b/Lib/fontTools/misc/transform.py
index 769475f..a6bbbcb 100644
--- a/Lib/fontTools/misc/transform.py
+++ b/Lib/fontTools/misc/transform.py
@@ -45,6 +45,8 @@
>>>
"""
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
__all__ = ["Transform", "Identity", "Offset", "Scale"]
@@ -64,7 +66,7 @@
return v
-class Transform:
+class Transform(object):
"""2x2 transformation matrix plus offset, a.k.a. Affine transform.
Transform instances are immutable: all transforming methods, eg.
@@ -96,7 +98,7 @@
"""
self.__affine = xx, xy, yx, yy, dx, dy
- def transformPoint(self, (x, y)):
+ def transformPoint(self, p):
"""Transform a point.
Example:
@@ -105,6 +107,7 @@
>>> t.transformPoint((100, 100))
(250.0, 550.0)
"""
+ (x, y) = p
xx, xy, yx, yy, dx, dy = self.__affine
return (xx*x + yx*y + dx, xy*x + yy*y + dy)
@@ -233,7 +236,7 @@
if self.__affine == (1, 0, 0, 1, 0, 0):
return self
xx, xy, yx, yy, dx, dy = self.__affine
- det = float(xx*yy - yx*xy)
+ det = xx*yy - yx*xy
xx, xy, yx, yy = yy/det, -xy/det, -yx/det, xx/det
dx, dy = -xx*dx - yx*dy, -xy*dx - yy*dy
return self.__class__(xx, xy, yx, yy, dx, dy)
@@ -265,19 +268,9 @@
"""
return self.__affine[index]
- def __getslice__(self, i, j):
- """Transform instances also behave like sequences and even support
- slicing:
- >>> t = Offset(100, 200)
- >>> t
- <Transform [1 0 0 1 100 200]>
- >>> t[4:]
- (100, 200)
- >>>
- """
- return self.__affine[i:j]
-
- def __cmp__(self, other):
+ def __ne__(self, other):
+ return not self.__eq__(other)
+ def __eq__(self, other):
"""Transform instances are comparable:
>>> t1 = Identity.scale(2, 3).translate(4, 6)
>>> t2 = Identity.translate(8, 18).scale(2, 3)
@@ -298,8 +291,8 @@
"""
xx1, xy1, yx1, yy1, dx1, dy1 = self.__affine
xx2, xy2, yx2, yy2, dx2, dy2 = other
- return cmp((xx1, xy1, yx1, yy1, dx1, dy1),
- (xx2, xy2, yx2, yy2, dx2, dy2))
+ return (xx1, xy1, yx1, yy1, dx1, dy1) == \
+ (xx2, xy2, yx2, yy2, dx2, dy2)
def __hash__(self):
"""Transform instances are hashable, meaning you can use them as
@@ -328,7 +321,7 @@
return hash(self.__affine)
def __repr__(self):
- return "<%s [%s %s %s %s %s %s]>" % ((self.__class__.__name__,)
+ return "<%s [%s %s %s %s %s %s]>" % ((self.__class__.__name__,) \
+ tuple(map(str, self.__affine)))
@@ -358,9 +351,6 @@
return Transform(x, 0, 0, y, 0, 0)
-def _test():
- import doctest, transform
- return doctest.testmod(transform)
-
if __name__ == "__main__":
- _test()
+ import doctest
+ doctest.testmod()
diff --git a/Lib/fontTools/ttLib/xmlImport.py b/Lib/fontTools/misc/xmlReader.py
similarity index 63%
rename from Lib/fontTools/ttLib/xmlImport.py
rename to Lib/fontTools/misc/xmlReader.py
index f110c8b..41315c1 100644
--- a/Lib/fontTools/ttLib/xmlImport.py
+++ b/Lib/fontTools/misc/xmlReader.py
@@ -1,3 +1,5 @@
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
from fontTools import ttLib
from fontTools.misc.textTools import safeEval
from fontTools.ttLib.tables.DefaultTable import DefaultTable
@@ -9,49 +11,52 @@
BUFSIZE = 0x4000
-class ExpatParser:
+class XMLReader(object):
- def __init__(self, ttFont, fileName, progress=None):
+ def __init__(self, fileName, ttFont, progress=None, quiet=False):
self.ttFont = ttFont
self.fileName = fileName
self.progress = progress
+ self.quiet = quiet
self.root = None
self.contentStack = []
self.stackSize = 0
- def parse(self):
+ def read(self):
+ if self.progress:
+ import stat
+ self.progress.set(0, os.stat(self.fileName)[stat.ST_SIZE] // 100 or 1)
file = open(self.fileName)
- self.parseFile(file)
+ self._parseFile(file)
file.close()
- def parseFile(self, file):
+ def _parseFile(self, file):
from xml.parsers.expat import ParserCreate
parser = ParserCreate()
- parser.returns_unicode = 0
- parser.StartElementHandler = self.startElementHandler
- parser.EndElementHandler = self.endElementHandler
- parser.CharacterDataHandler = self.characterDataHandler
+ parser.StartElementHandler = self._startElementHandler
+ parser.EndElementHandler = self._endElementHandler
+ parser.CharacterDataHandler = self._characterDataHandler
pos = 0
- while 1:
+ while True:
chunk = file.read(BUFSIZE)
if not chunk:
parser.Parse(chunk, 1)
break
pos = pos + len(chunk)
if self.progress:
- self.progress.set(pos / 100)
+ self.progress.set(pos // 100)
parser.Parse(chunk, 0)
- def startElementHandler(self, name, attrs):
+ def _startElementHandler(self, name, attrs):
stackSize = self.stackSize
self.stackSize = stackSize + 1
if not stackSize:
- if name <> "ttFont":
- raise TTXParseError, "illegal root tag: %s" % name
+ if name != "ttFont":
+ raise TTXParseError("illegal root tag: %s" % name)
sfntVersion = attrs.get("sfntVersion")
if sfntVersion is not None:
- if len(sfntVersion) <> 4:
+ if len(sfntVersion) != 4:
sfntVersion = safeEval('"' + sfntVersion + '"')
self.ttFont.sfntVersion = sfntVersion
self.contentStack.append([])
@@ -59,7 +64,8 @@
subFile = attrs.get("src")
if subFile is not None:
subFile = os.path.join(os.path.dirname(self.fileName), subFile)
- importXML(self.ttFont, subFile, self.progress)
+ subReader = XMLReader(subFile, self.ttFont, self.progress, self.quiet)
+ subReader.read()
self.contentStack.append([])
return
tag = ttLib.xmlToTag(name)
@@ -69,16 +75,17 @@
elif self.ttFont.verbose:
ttLib.debugmsg(msg)
else:
- print msg
+ if not self.quiet:
+ print(msg)
if tag == "GlyphOrder":
tableClass = ttLib.GlyphOrder
- elif attrs.has_key("ERROR"):
+ elif "ERROR" in attrs:
tableClass = DefaultTable
else:
tableClass = ttLib.getTableClass(tag)
if tableClass is None:
tableClass = DefaultTable
- if tag == 'loca' and self.ttFont.has_key(tag):
+ if tag == 'loca' and tag in self.ttFont:
# Special-case the 'loca' table as we need the
# original if the 'glyf' table isn't recompiled.
self.currentTable = self.ttFont[tag]
@@ -90,28 +97,29 @@
self.contentStack.append([])
self.root = (name, attrs, self.contentStack[-1])
else:
- list = []
- self.contentStack[-1].append((name, attrs, list))
- self.contentStack.append(list)
+ l = []
+ self.contentStack[-1].append((name, attrs, l))
+ self.contentStack.append(l)
- def characterDataHandler(self, data):
+ def _characterDataHandler(self, data):
if self.stackSize > 1:
self.contentStack[-1].append(data)
- def endElementHandler(self, name):
+ def _endElementHandler(self, name):
self.stackSize = self.stackSize - 1
del self.contentStack[-1]
if self.stackSize == 1:
self.root = None
elif self.stackSize == 2:
- self.currentTable.fromXML(self.root, self.ttFont)
+ name, attrs, content = self.root
+ self.currentTable.fromXML(name, attrs, content, self.ttFont)
self.root = None
-class ProgressPrinter:
+class ProgressPrinter(object):
def __init__(self, title, maxval=100):
- print title
+ print(title)
def set(self, val, maxval=None):
pass
@@ -120,16 +128,5 @@
pass
def setLabel(self, text):
- print text
-
-
-def importXML(ttFont, fileName, progress=None):
- """Import a TTX file (an XML-based text format), so as to recreate
- a font object.
- """
- if progress:
- import stat
- progress.set(0, os.stat(fileName)[stat.ST_SIZE] / 100 or 1)
- p = ExpatParser(ttFont, fileName, progress)
- p.parse()
+ print(text)
diff --git a/Lib/fontTools/misc/xmlWriter.py b/Lib/fontTools/misc/xmlWriter.py
index aa70f13..93f57e3 100644
--- a/Lib/fontTools/misc/xmlWriter.py
+++ b/Lib/fontTools/misc/xmlWriter.py
@@ -1,17 +1,22 @@
"""xmlWriter.py -- Simple XML authoring class"""
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
+import sys
import string
-import struct
-import os
INDENT = " "
-class XMLWriter:
+class XMLWriter(object):
- def __init__(self, fileOrPath, indentwhite=INDENT, idlefunc=None, encoding="utf-8"):
+ def __init__(self, fileOrPath, indentwhite=INDENT, idlefunc=None):
if not hasattr(fileOrPath, "write"):
- self.file = open(fileOrPath, "w")
+ try:
+ # Python3 has encoding support.
+ self.file = open(fileOrPath, "w", encoding="utf-8")
+ except TypeError:
+ self.file = open(fileOrPath, "w")
else:
# assume writable file object
self.file = fileOrPath
@@ -21,32 +26,43 @@
self.needindent = 1
self.idlefunc = idlefunc
self.idlecounter = 0
- if encoding:
- self.writeraw('<?xml version="1.0" encoding="%s"?>' % encoding)
- else:
- self.writeraw('<?xml version="1.0"?>')
+ self._writeraw('<?xml version="1.0" encoding="utf-8"?>')
self.newline()
def close(self):
self.file.close()
- def write(self, data):
- self.writeraw(escape(data))
+ def write(self, string, indent=True):
+ """Writes text."""
+ self._writeraw(escape(string), indent=indent)
+
+ def writecdata(self, string):
+ """Writes text in a CDATA section."""
+ self._writeraw("<![CDATA[" + string + "]]>")
+
+ def write8bit(self, data, strip=False):
+ """Writes a bytes() sequence into the XML, escaping
+ non-ASCII bytes. When this is read in xmlReader,
+ the original bytes can be recovered by encoding to
+ 'latin-1'."""
+ self._writeraw(escape8bit(data), strip=strip)
+
+ def write16bit(self, data, strip=False):
+ self._writeraw(escape16bit(data), strip=strip)
- def write_noindent(self, data):
- self.file.write(escape(data))
+ def write_noindent(self, string):
+ """Writes text without indentation."""
+ self._writeraw(escape(string), indent=False)
- def write8bit(self, data):
- self.writeraw(escape8bit(data))
-
- def write16bit(self, data):
- self.writeraw(escape16bit(data))
-
- def writeraw(self, data):
- if self.needindent:
+ def _writeraw(self, data, indent=True, strip=False):
+ """Writes bytes, possibly indented."""
+ if indent and self.needindent:
self.file.write(self.indentlevel * self.indentwhite)
self.needindent = 0
- self.file.write(data)
+ s = tostr(data, encoding="utf-8")
+ if (strip):
+ s = s.strip()
+ self.file.write(s)
def newline(self):
self.file.write("\n")
@@ -58,22 +74,22 @@
def comment(self, data):
data = escape(data)
- lines = string.split(data, "\n")
- self.writeraw("<!-- " + lines[0])
+ lines = data.split("\n")
+ self._writeraw("<!-- " + lines[0])
for line in lines[1:]:
self.newline()
- self.writeraw(" " + line)
- self.writeraw(" -->")
+ self._writeraw(" " + line)
+ self._writeraw(" -->")
def simpletag(self, _TAG_, *args, **kwargs):
- attrdata = apply(self.stringifyattrs, args, kwargs)
+ attrdata = self.stringifyattrs(*args, **kwargs)
data = "<%s%s/>" % (_TAG_, attrdata)
- self.writeraw(data)
+ self._writeraw(data)
def begintag(self, _TAG_, *args, **kwargs):
- attrdata = apply(self.stringifyattrs, args, kwargs)
+ attrdata = self.stringifyattrs(*args, **kwargs)
data = "<%s%s>" % (_TAG_, attrdata)
- self.writeraw(data)
+ self._writeraw(data)
self.stack.append(_TAG_)
self.indent()
@@ -82,7 +98,7 @@
del self.stack[-1]
self.dedent()
data = "</%s>" % _TAG_
- self.writeraw(data)
+ self._writeraw(data)
def dumphex(self, data):
linelength = 16
@@ -95,7 +111,7 @@
for j in range(0, hexlinelength, chunksize):
line = line + white + hexline[j:j+chunksize]
white = " "
- self.writeraw(line)
+ self._writeraw(line)
self.newline()
def indent(self):
@@ -108,8 +124,7 @@
def stringifyattrs(self, *args, **kwargs):
if kwargs:
assert not args
- attributes = kwargs.items()
- attributes.sort()
+ attributes = sorted(kwargs.items())
elif args:
assert len(args) == 1
attributes = args[0]
@@ -122,39 +137,34 @@
def escape(data):
- data = string.replace(data, "&", "&")
- data = string.replace(data, "<", "<")
+ data = tostr(data, 'utf-8')
+ data = data.replace("&", "&")
+ data = data.replace("<", "<")
+ data = data.replace(">", ">")
return data
def escapeattr(data):
- data = string.replace(data, "&", "&")
- data = string.replace(data, "<", "<")
- data = string.replace(data, '"', """)
+ data = escape(data)
+ data = data.replace('"', """)
return data
def escape8bit(data):
+ """Input is Unicode string."""
def escapechar(c):
n = ord(c)
- if c in "<&":
- if c == "&":
- return "&"
- else:
- return "<"
- elif 32 <= n <= 127:
+ if 32 <= n <= 127 and c not in "<&>":
return c
else:
- return "&#" + `n` + ";"
- return string.join(map(escapechar, data), "")
-
-needswap = struct.pack("h", 1) == "\001\000"
+ return "&#" + repr(n) + ";"
+ return strjoin(map(escapechar, data.decode('latin-1')))
def escape16bit(data):
import array
a = array.array("H")
a.fromstring(data)
- if needswap:
+ if sys.byteorder != "big":
a.byteswap()
- def escapenum(n, amp=ord("&"), lt=ord("<")):
+ def escapenum(n, amp=byteord("&"), lt=byteord("<")):
if n == amp:
return "&"
elif n == lt:
@@ -162,15 +172,14 @@
elif 32 <= n <= 127:
return chr(n)
else:
- return "&#" + `n` + ";"
- return string.join(map(escapenum, a), "")
+ return "&#" + repr(n) + ";"
+ return strjoin(map(escapenum, a))
def hexStr(s):
h = string.hexdigits
r = ''
for c in s:
- i = ord(c)
+ i = byteord(c)
r = r + h[(i >> 4) & 0xF] + h[i & 0xF]
return r
-
diff --git a/Lib/fontTools/nfntLib.py b/Lib/fontTools/nfntLib.py
deleted file mode 100644
index c834fc5..0000000
--- a/Lib/fontTools/nfntLib.py
+++ /dev/null
@@ -1,304 +0,0 @@
-import struct
-from fontTools.misc import sstruct
-import string
-import types
-
-
-# FontRec header
-nfntHeaderFormat = """
- > # big endian
- fontType: h # font type
- firstChar: h # ASCII code of first character
- lastChar: h # ASCII code of last character
- widMax: h # maximum character width
- kernMax: h # negative of maximum character kern
- nDescent: h # negative of descent
- fRectWidth: h # width of font rectangle
- fRectHeight: h # height of font rectangle
- owTLoc: H # offset to offset/width table (in words from _this_ point)
- ascent: h # ascent
- descent: h # descent
- leading: h # leading
- rowWords: h # row width of bit image / 2
-"""
-headerSize = sstruct.calcsize(nfntHeaderFormat)
-assert headerSize == 26
-
-
-class NFNT:
-
- def __init__(self, data=None):
- if data is not None:
- self.decompile(data)
-
- def decompile(self, data):
- # header; FontRec
- sstruct.unpack(nfntHeaderFormat, data[:headerSize], self)
-
- #assert self.fRectHeight == (self.ascent + self.descent)
-
- # rest
- tableSize = 2 * (self.lastChar - self.firstChar + 3)
- bitmapSize = 2 * self.rowWords * self.fRectHeight
-
- self.bits = data[headerSize:headerSize + bitmapSize]
-
- # XXX deal with self.nDescent being a positive number
- assert (headerSize + bitmapSize + tableSize - 16) / 2 == self.owTLoc # ugh...
-
- locTable = data[headerSize + bitmapSize:headerSize + bitmapSize + tableSize]
- if len(locTable) <> tableSize:
- raise ValueError, 'invalid NFNT format'
-
- owTable = data[headerSize + bitmapSize + tableSize:headerSize + bitmapSize + 2 * tableSize]
- if len(owTable) <> tableSize:
- raise ValueError, 'invalid NFNT format'
-
- # fill tables
- self.offsetTable = []
- self.widthTable = []
- self.locTable = []
- 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.locTable.append(loc)
-
- def compile(self):
- header = sstruct.pack(nfntHeaderFormat, self)
- nEntries = len(self.widthTable)
- owTable = [None] * nEntries
- locTable = [None] * nEntries
- for i in range(nEntries):
- owTable[i] = chr(self.offsetTable[i]) + chr(self.widthTable[i])
- locTable[i] = struct.pack("h", self.locTable[i])
- owTable = string.join(owTable, "")
- locTable = string.join(locTable, "")
- assert len(locTable) == len(owTable) == 2 * (self.lastChar - self.firstChar + 3)
- return header + self.bits + locTable + owTable
-
- def unpackGlyphs(self):
- import numpy
- nGlyphs = len(self.locTable) - 1
- self.glyphs = [None] * nGlyphs
-
- rowBytes = self.rowWords * 2
- imageWidth = self.rowWords * 16
- imageHeight = self.fRectHeight
- bits = self.bits
- bitImage = numpy.zeros((imageWidth, imageHeight), numpy.int8)
-
- for y in range(imageHeight):
- for xByte in range(rowBytes):
- byte = bits[y * rowBytes + xByte]
- for xBit in range(8):
- x = 8 * xByte + xBit
- bit = (ord(byte) >> (7 - xBit)) & 0x01
- bitImage[x, y] = bit
-
- for i in range(nGlyphs):
- width = self.widthTable[i]
- offset = self.offsetTable[i]
- if width == 255 and offset == 255:
- self.glyphs[i] = None
- else:
- imageL = self.locTable[i]
- imageR = self.locTable[i+1]
- imageWidth = imageR - imageL
- offset = offset + self.kernMax
- self.glyphs[i] = glyph = Glyph(width, offset, bitImage[imageL:imageR])
-
- def packGlyphs(self):
- import numpy
- imageWidth = 0
- kernMax = 0
- imageHeight = None
- widMax = 0
- fRectWidth = 0
- for glyph in self.glyphs:
- if glyph is None:
- continue
- if imageHeight is None:
- imageHeight = glyph.pixels.shape[1]
- else:
- assert imageHeight == glyph.pixels.shape[1]
- imageWidth = imageWidth + glyph.pixels.shape[0]
- kernMax = min(kernMax, glyph.offset)
- widMax = max(widMax, glyph.width)
- fRectWidth = max(fRectWidth, glyph.pixels.shape[0] + glyph.offset)
-
- fRectWidth = fRectWidth - kernMax
- imageWidth = 16 * ((imageWidth - 1) / 16 + 1)
- rowBytes = imageWidth / 8
- rowWords = rowBytes / 2
- bitImage = numpy.zeros((imageWidth, imageHeight), numpy.int8)
- locTable = []
- widthTable = []
- offsetTable = []
- loc = 0
- for glyph in self.glyphs:
- locTable.append(loc)
- if glyph is None:
- widthTable.append(255)
- offsetTable.append(255)
- continue
- widthTable.append(glyph.width)
- offsetTable.append(glyph.offset - kernMax)
- imageWidth = glyph.pixels.shape[0]
- bitImage[loc:loc+imageWidth] = glyph.pixels
- loc = loc + imageWidth
-
- locTable.append(loc)
- widthTable.append(255)
- offsetTable.append(255)
-
- bits = []
- for y in range(imageHeight):
- for xByte in range(rowBytes):
- byte = 0
- for x in range(8):
- byte = byte | ((bitImage[8 * xByte + x, y] & 0x01) << (7 - x))
- bits.append(chr(byte))
- bits = string.join(bits, "")
-
- # assign values
- self.fontType = 0x9000
- self.lastChar = self.firstChar + len(self.glyphs) - 2
- self.widMax = widMax
- self.kernMax = kernMax
- self.descent = imageHeight - self.ascent
- self.nDescent = -self.descent
- self.fRectWidth = fRectWidth
- self.fRectHeight = imageHeight
- self.rowWords = rowWords
-
- tableSize = 2 * (self.lastChar - self.firstChar + 3)
- self.owTLoc = (headerSize + len(bits) + tableSize - 16) / 2
-
- self.bits = bits
- self.locTable = locTable
- self.widthTable = widthTable
- self.offsetTable = offsetTable
-
- def getMissing(self):
- return self.glyphs[-1]
-
- def __getitem__(self, charNum):
- if charNum > self.lastChar or charNum < 0:
- raise IndexError, "no such character"
- index = charNum - self.firstChar
- if index < 0:
- return None
- return self.glyphs[index]
-
- def __setitem__(self, charNum, glyph):
- if charNum > self.lastChar or charNum < 0:
- raise IndexError, "no such character"
- index = charNum - self.firstChar
- if index < 0:
- raise IndexError, "no such character"
- self.glyphs[index] = glyph
-
- def __len__(self):
- return len(self.locTable) - 2 + self.firstChar
-
- #
- # XXX old cruft
- #
-
- def createQdBitImage(self):
- import Qd
- self.bitImage = Qd.BitMap(self.bits, 2 * self.rowWords, (0, 0, self.rowWords * 16, self.fRectHeight))
-
- 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):
- import Qd
- 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.locTable[cindex]
- location1 = self.locTable[cindex + 1]
- srcbounds = (location0, 0, location1, self.fRectHeight)
- destbounds = ( offset + self.kernMax,
- 0,
- offset + self.kernMax + location1 - location0,
- self.fRectHeight )
- return width, srcbounds, destbounds
-
-
-class Glyph:
-
- def __init__(self, width, offset, pixels=None, pixelDepth=1):
- self.width = width
- self.offset = offset
- self.pixelDepth = pixelDepth
- self.pixels = pixels
-
-
-def dataFromFile(pathOrFSSpec, nameOrID="", resType='NFNT'):
- from Carbon import Res
- resref = Res.FSOpenResFile(pathOrFSSpec, 1) # readonly
- try:
- Res.UseResFile(resref)
- if not nameOrID:
- # just take the first in the file
- res = Res.Get1IndResource(resType, 1)
- elif type(nameOrID) == types.IntType:
- res = Res.Get1Resource(resType, nameOrID)
- else:
- res = Res.Get1NamedResource(resType, nameOrID)
- theID, theType, name = res.GetResInfo()
- data = res.data
- finally:
- Res.CloseResFile(resref)
- return data
-
-
-def fromFile(pathOrFSSpec, nameOrID="", resType='NFNT'):
- data = dataFromFile(pathOrFSSpec, nameOrID, resType)
- return NFNT(data)
-
-
-if __name__ == "__main__":
- import EasyDialogs
- path = EasyDialogs.AskFileForOpen()
- if path:
- data = dataFromFile(path)
- font = NFNT(data)
- font.unpackGlyphs()
- font.packGlyphs()
- data2 = font.compile()
- print "xxxxx", data == data2, len(data) == len(data2)
diff --git a/Lib/fontTools/pens/basePen.py b/Lib/fontTools/pens/basePen.py
index 04da75a..0ad866c 100644
--- a/Lib/fontTools/pens/basePen.py
+++ b/Lib/fontTools/pens/basePen.py
@@ -36,6 +36,8 @@
sequence of length 2 will do.
"""
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
__all__ = ["AbstractPen", "NullPen", "BasePen",
"decomposeSuperBezierSegment", "decomposeQuadraticSegment"]
@@ -248,7 +250,7 @@
elif n == 0:
self.lineTo(points[0])
else:
- raise AssertionError, "can't get there from here"
+ raise AssertionError("can't get there from here")
def qCurveTo(self, *points):
n = len(points) - 1 # 'n' is the number of control points
@@ -296,9 +298,8 @@
for i in range(2, n+1):
# calculate points in between control points.
nDivisions = min(i, 3, n-i+2)
- d = float(nDivisions)
for j in range(1, nDivisions):
- factor = j / d
+ factor = j / nDivisions
temp1 = points[i-1]
temp2 = points[i-2]
temp = (temp2[0] + factor * (temp1[0] - temp2[0]),
@@ -339,14 +340,14 @@
class _TestPen(BasePen):
"""Test class that prints PostScript to stdout."""
def _moveTo(self, pt):
- print "%s %s moveto" % (pt[0], pt[1])
+ print("%s %s moveto" % (pt[0], pt[1]))
def _lineTo(self, pt):
- print "%s %s lineto" % (pt[0], pt[1])
+ print("%s %s lineto" % (pt[0], pt[1]))
def _curveToOne(self, bcp1, bcp2, pt):
- print "%s %s %s %s %s %s curveto" % (bcp1[0], bcp1[1],
- bcp2[0], bcp2[1], pt[0], pt[1])
+ print("%s %s %s %s %s %s curveto" % (bcp1[0], bcp1[1],
+ bcp2[0], bcp2[1], pt[0], pt[1]))
def _closePath(self):
- print "closepath"
+ print("closepath")
if __name__ == "__main__":
diff --git a/Lib/fontTools/pens/boundsPen.py b/Lib/fontTools/pens/boundsPen.py
index 3fde6e3..144acc4 100644
--- a/Lib/fontTools/pens/boundsPen.py
+++ b/Lib/fontTools/pens/boundsPen.py
@@ -1,6 +1,8 @@
-from fontTools.pens.basePen import BasePen
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
from fontTools.misc.arrayTools import updateBounds, pointInRect, unionRect
from fontTools.misc.bezierTools import calcCubicBounds, calcQuadraticBounds
+from fontTools.pens.basePen import BasePen
__all__ = ["BoundsPen", "ControlBoundsPen"]
@@ -86,8 +88,8 @@
pen = ControlBoundsPen(None)
draw(pen)
- print pen.bounds
+ print(pen.bounds)
pen = BoundsPen(None)
draw(pen)
- print pen.bounds
+ print(pen.bounds)
diff --git a/Lib/fontTools/pens/cocoaPen.py b/Lib/fontTools/pens/cocoaPen.py
index ef3bf03..59a4c81 100644
--- a/Lib/fontTools/pens/cocoaPen.py
+++ b/Lib/fontTools/pens/cocoaPen.py
@@ -1,3 +1,5 @@
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
from fontTools.pens.basePen import BasePen
@@ -13,14 +15,14 @@
path = NSBezierPath.bezierPath()
self.path = path
- def _moveTo(self, (x, y)):
- self.path.moveToPoint_((x, y))
+ def _moveTo(self, p):
+ self.path.moveToPoint_(p)
- def _lineTo(self, (x, y)):
- self.path.lineToPoint_((x, y))
+ def _lineTo(self, p):
+ self.path.lineToPoint_(p)
- def _curveToOne(self, (x1, y1), (x2, y2), (x3, y3)):
- self.path.curveToPoint_controlPoint1_controlPoint2_((x3, y3), (x1, y1), (x2, y2))
+ def _curveToOne(self, p1, p2, p3):
+ self.path.curveToPoint_controlPoint1_controlPoint2_(p3, p1, p2)
def _closePath(self):
self.path.closePath()
diff --git a/Lib/fontTools/pens/pointInsidePen.py b/Lib/fontTools/pens/pointInsidePen.py
index 131c75f..4966815 100644
--- a/Lib/fontTools/pens/pointInsidePen.py
+++ b/Lib/fontTools/pens/pointInsidePen.py
@@ -2,6 +2,8 @@
for shapes.
"""
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
from fontTools.pens.basePen import BasePen
from fontTools.misc.bezierTools import solveQuadratic, solveCubic
@@ -96,7 +98,7 @@
dx = x2 - x1
dy = y2 - y1
- t = float(y - y1) / dy
+ t = (y - y1) / dy
ix = dx * t + x1
if ix < x:
return
@@ -120,8 +122,7 @@
cy = (y2 - dy) * 3.0
by = (y3 - y2) * 3.0 - cy
ay = y4 - dy - cy - by
- solutions = solveCubic(ay, by, cy, dy - y)
- solutions.sort()
+ solutions = sorted(solveCubic(ay, by, cy, dy - y))
solutions = [t for t in solutions if ZERO_MINUS_EPSILON <= t <= ONE_PLUS_EPSILON]
if not solutions:
return
@@ -176,8 +177,7 @@
c = y1
b = (y2 - c) * 2.0
a = y3 - c - b
- solutions = solveQuadratic(a, b, c - y)
- solutions.sort()
+ solutions = sorted(solveQuadratic(a, b, c - y))
solutions = [t for t in solutions if ZERO_MINUS_EPSILON <= t <= ONE_PLUS_EPSILON]
if not solutions:
return
diff --git a/Lib/fontTools/pens/reportLabPen.py b/Lib/fontTools/pens/reportLabPen.py
index 1c3fcf8..d383100 100644
--- a/Lib/fontTools/pens/reportLabPen.py
+++ b/Lib/fontTools/pens/reportLabPen.py
@@ -1,4 +1,7 @@
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
from fontTools.pens.basePen import BasePen
+from reportlab.graphics.shapes import Path
class ReportLabPen(BasePen):
@@ -8,17 +11,21 @@
def __init__(self, glyphSet, path=None):
BasePen.__init__(self, glyphSet)
if path is None:
- from reportlab.graphics.shapes import Path
path = Path()
self.path = path
- def _moveTo(self, (x,y)):
+ def _moveTo(self, p):
+ (x,y) = p
self.path.moveTo(x,y)
- def _lineTo(self, (x,y)):
+ def _lineTo(self, p):
+ (x,y) = p
self.path.lineTo(x,y)
- def _curveToOne(self, (x1,y1), (x2,y2), (x3,y3)):
+ def _curveToOne(self, p1, p2, p3):
+ (x1,y1) = p1
+ (x2,y2) = p2
+ (x3,y3) = p3
self.path.curveTo(x1, y1, x2, y2, x3, y3)
def _closePath(self):
@@ -28,15 +35,14 @@
if __name__=="__main__":
import sys
if len(sys.argv) < 3:
- print "Usage: reportLabPen.py <OTF/TTF font> <glyphname> [<image file to create>]"
- print " If no image file name is created, by default <glyphname>.png is created."
- print " example: reportLabPen.py Arial.TTF R test.png"
- print " (The file format will be PNG, regardless of the image file name supplied)"
+ print("Usage: reportLabPen.py <OTF/TTF font> <glyphname> [<image file to create>]")
+ print(" If no image file name is created, by default <glyphname>.png is created.")
+ print(" example: reportLabPen.py Arial.TTF R test.png")
+ print(" (The file format will be PNG, regardless of the image file name supplied)")
sys.exit(0)
from fontTools.ttLib import TTFont
from reportlab.lib import colors
- from reportlab.graphics.shapes import Path
path = sys.argv[1]
glyphName = sys.argv[2]
diff --git a/Lib/fontTools/pens/transformPen.py b/Lib/fontTools/pens/transformPen.py
index 63c9323..8069ecd 100644
--- a/Lib/fontTools/pens/transformPen.py
+++ b/Lib/fontTools/pens/transformPen.py
@@ -1,3 +1,5 @@
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
from fontTools.pens.basePen import AbstractPen
diff --git a/Lib/fontTools/subset.py b/Lib/fontTools/subset.py
index ca66942..8ce37f2 100644
--- a/Lib/fontTools/subset.py
+++ b/Lib/fontTools/subset.py
@@ -7,15 +7,16 @@
Later grown into full OpenType subsetter, supporting all standard tables.
"""
-import sys
-import struct
-import time
-import array
-
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
from fontTools import ttLib
from fontTools.ttLib.tables import otTables
from fontTools.misc import psCharStrings
from fontTools.pens import basePen
+import sys
+import struct
+import time
+import array
def _add_method(*clazzes):
@@ -24,10 +25,10 @@
def wrapper(method):
for clazz in clazzes:
assert clazz.__name__ != 'DefaultTable', 'Oops, table class not found.'
- assert not hasattr(clazz, method.func_name), \
+ assert not hasattr(clazz, method.__name__), \
"Oops, class '%s' has method '%s'." % (clazz.__name__,
- method.func_name)
- setattr(clazz, method.func_name, method)
+ method.__name__)
+ setattr(clazz, method.__name__, method)
return None
return wrapper
@@ -68,25 +69,25 @@
"Returns ascending list of matching class values."
return _uniq_sort(
([0] if any(g not in self.classDefs for g in glyphs) else []) +
- [v for g,v in self.classDefs.iteritems() if g in glyphs])
+ [v for g,v in self.classDefs.items() if g in glyphs])
@_add_method(otTables.ClassDef)
def intersect_class(self, glyphs, klass):
"Returns set of glyphs matching class."
if klass == 0:
return set(g for g in glyphs if g not in self.classDefs)
- return set(g for g,v in self.classDefs.iteritems()
+ return set(g for g,v in self.classDefs.items()
if v == klass and g in glyphs)
@_add_method(otTables.ClassDef)
def subset(self, glyphs, remap=False):
"Returns ascending list of remaining classes."
- self.classDefs = dict((g,v) for g,v in self.classDefs.iteritems() if g in glyphs)
+ self.classDefs = dict((g,v) for g,v in self.classDefs.items() if g in glyphs)
# Note: while class 0 has the special meaning of "not matched",
# if no glyph will ever /not match/, we can optimize class 0 out too.
indices = _uniq_sort(
([0] if any(g not in self.classDefs for g in glyphs) else []) +
- self.classDefs.values())
+ list(self.classDefs.values()))
if remap:
self.remap(indices)
return indices
@@ -95,97 +96,73 @@
def remap(self, class_map):
"Remaps classes."
self.classDefs = dict((g,class_map.index(v))
- for g,v in self.classDefs.iteritems())
+ for g,v in self.classDefs.items())
@_add_method(otTables.SingleSubst)
def closure_glyphs(self, s, cur_glyphs=None):
- if cur_glyphs == None: cur_glyphs = s.glyphs
- if self.Format in [1, 2]:
- s.glyphs.update(v for g,v in self.mapping.iteritems() if g in cur_glyphs)
- else:
- assert 0, "unknown format: %s" % self.Format
+ if cur_glyphs is None: cur_glyphs = s.glyphs
+ s.glyphs.update(v for g,v in self.mapping.items() if g in cur_glyphs)
@_add_method(otTables.SingleSubst)
def subset_glyphs(self, s):
- if self.Format in [1, 2]:
- self.mapping = dict((g,v) for g,v in self.mapping.iteritems()
- if g in s.glyphs and v in s.glyphs)
- return bool(self.mapping)
- else:
- assert 0, "unknown format: %s" % self.Format
+ self.mapping = dict((g,v) for g,v in self.mapping.items()
+ if g in s.glyphs and v in s.glyphs)
+ return bool(self.mapping)
@_add_method(otTables.MultipleSubst)
def closure_glyphs(self, s, cur_glyphs=None):
- if cur_glyphs == None: cur_glyphs = s.glyphs
- if self.Format == 1:
- indices = self.Coverage.intersect(cur_glyphs)
- _set_update(s.glyphs, *(self.Sequence[i].Substitute for i in indices))
- else:
- assert 0, "unknown format: %s" % self.Format
+ if cur_glyphs is None: cur_glyphs = s.glyphs
+ indices = self.Coverage.intersect(cur_glyphs)
+ _set_update(s.glyphs, *(self.Sequence[i].Substitute for i in indices))
@_add_method(otTables.MultipleSubst)
def subset_glyphs(self, s):
- if self.Format == 1:
- indices = self.Coverage.subset(s.glyphs)
- self.Sequence = [self.Sequence[i] for i in indices]
- # Now drop rules generating glyphs we don't want
- indices = [i for i,seq in enumerate(self.Sequence)
- if all(sub in s.glyphs for sub in seq.Substitute)]
- self.Sequence = [self.Sequence[i] for i in indices]
- self.Coverage.remap(indices)
- self.SequenceCount = len(self.Sequence)
- return bool(self.SequenceCount)
- else:
- assert 0, "unknown format: %s" % self.Format
+ indices = self.Coverage.subset(s.glyphs)
+ self.Sequence = [self.Sequence[i] for i in indices]
+ # Now drop rules generating glyphs we don't want
+ indices = [i for i,seq in enumerate(self.Sequence)
+ if all(sub in s.glyphs for sub in seq.Substitute)]
+ self.Sequence = [self.Sequence[i] for i in indices]
+ self.Coverage.remap(indices)
+ self.SequenceCount = len(self.Sequence)
+ return bool(self.SequenceCount)
@_add_method(otTables.AlternateSubst)
def closure_glyphs(self, s, cur_glyphs=None):
- if cur_glyphs == None: cur_glyphs = s.glyphs
- if self.Format == 1:
- _set_update(s.glyphs, *(vlist for g,vlist in self.alternates.iteritems()
- if g in cur_glyphs))
- else:
- assert 0, "unknown format: %s" % self.Format
+ if cur_glyphs is None: cur_glyphs = s.glyphs
+ _set_update(s.glyphs, *(vlist for g,vlist in self.alternates.items()
+ if g in cur_glyphs))
@_add_method(otTables.AlternateSubst)
def subset_glyphs(self, s):
- if self.Format == 1:
- self.alternates = dict((g,vlist)
- for g,vlist in self.alternates.iteritems()
- if g in s.glyphs and
- all(v in s.glyphs for v in vlist))
- return bool(self.alternates)
- else:
- assert 0, "unknown format: %s" % self.Format
+ self.alternates = dict((g,vlist)
+ for g,vlist in self.alternates.items()
+ if g in s.glyphs and
+ all(v in s.glyphs for v in vlist))
+ return bool(self.alternates)
@_add_method(otTables.LigatureSubst)
def closure_glyphs(self, s, cur_glyphs=None):
- if cur_glyphs == None: cur_glyphs = s.glyphs
- if self.Format == 1:
- _set_update(s.glyphs, *([seq.LigGlyph for seq in seqs
- if all(c in s.glyphs for c in seq.Component)]
- for g,seqs in self.ligatures.iteritems()
- if g in cur_glyphs))
- else:
- assert 0, "unknown format: %s" % self.Format
+ if cur_glyphs is None: cur_glyphs = s.glyphs
+ _set_update(s.glyphs, *([seq.LigGlyph for seq in seqs
+ if all(c in s.glyphs for c in seq.Component)]
+ for g,seqs in self.ligatures.items()
+ if g in cur_glyphs))
@_add_method(otTables.LigatureSubst)
def subset_glyphs(self, s):
- if self.Format == 1:
- self.ligatures = dict((g,v) for g,v in self.ligatures.iteritems()
- if g in s.glyphs)
- self.ligatures = dict((g,[seq for seq in seqs
- if seq.LigGlyph in s.glyphs and
- all(c in s.glyphs for c in seq.Component)])
- for g,seqs in self.ligatures.iteritems())
- self.ligatures = dict((g,v) for g,v in self.ligatures.iteritems() if v)
- return bool(self.ligatures)
- else:
- assert 0, "unknown format: %s" % self.Format
+ self.ligatures = dict((g,v) for g,v in self.ligatures.items()
+ if g in s.glyphs)
+ self.ligatures = dict((g,[seq for seq in seqs
+ if seq.LigGlyph in s.glyphs and
+ all(c in s.glyphs for c in seq.Component)])
+ for g,seqs in self.ligatures.items())
+ self.ligatures = dict((g,v) for g,v in self.ligatures.items() if v)
+ return bool(self.ligatures)
@_add_method(otTables.ReverseChainSingleSubst)
def closure_glyphs(self, s, cur_glyphs=None):
- if cur_glyphs == None: cur_glyphs = s.glyphs
+ if cur_glyphs is None: cur_glyphs = s.glyphs
if self.Format == 1:
indices = self.Coverage.intersect(cur_glyphs)
if(not indices or
@@ -279,6 +256,7 @@
@_add_method(otTables.Anchor)
def prune_hints(self):
# Drop device tables / contour anchor point
+ self.ensureDecompiled()
self.Format = 1
@_add_method(otTables.CursivePos)
@@ -317,10 +295,12 @@
def prune_post_subset(self, options):
if not options.hinting:
for m in self.MarkArray.MarkRecord:
- m.MarkAnchor.prune_hints()
+ if m.MarkAnchor:
+ m.MarkAnchor.prune_hints()
for b in self.BaseArray.BaseRecord:
for a in b.BaseAnchor:
- a.prune_hints()
+ if a:
+ a.prune_hints()
return True
@_add_method(otTables.MarkLigPos)
@@ -352,11 +332,13 @@
def prune_post_subset(self, options):
if not options.hinting:
for m in self.MarkArray.MarkRecord:
- m.MarkAnchor.prune_hints()
+ if m.MarkAnchor:
+ m.MarkAnchor.prune_hints()
for l in self.LigatureArray.LigatureAttach:
for c in l.ComponentRecord:
for a in c.LigatureAnchor:
- a.prune_hints()
+ if a:
+ a.prune_hints()
return True
@_add_method(otTables.MarkMarkPos)
@@ -388,10 +370,12 @@
if not options.hinting:
# Drop device tables or contour anchor point
for m in self.Mark1Array.MarkRecord:
- m.MarkAnchor.prune_hints()
+ if m.MarkAnchor:
+ m.MarkAnchor.prune_hints()
for b in self.Mark2Array.Mark2Record:
for m in b.Mark2Anchor:
- m.prune_hints()
+ if m:
+ m.prune_hints()
return True
@_add_method(otTables.SingleSubst,
@@ -551,6 +535,7 @@
self.Intersect = lambda glyphs, c, r: c.intersect_class(glyphs, r)
self.ClassDef = 'InputClassDef' if Chain else 'ClassDef'
+ self.ClassDefIndex = 1 if Chain else 0
self.Input = 'Input' if Chain else 'Class'
if self.Format not in [1, 2, 3]:
@@ -565,7 +550,7 @@
@_add_method(otTables.ContextSubst,
otTables.ChainContextSubst)
def closure_glyphs(self, s, cur_glyphs=None):
- if cur_glyphs == None: cur_glyphs = s.glyphs
+ if cur_glyphs is None: cur_glyphs = s.glyphs
c = self.__classify_context()
indices = c.Coverage(self).intersect(s.glyphs)
@@ -576,8 +561,9 @@
if self.Format == 1:
ContextData = c.ContextData(self)
rss = getattr(self, c.RuleSet)
+ rssCount = getattr(self, c.RuleSetCount)
for i in indices:
- if not rss[i]: continue
+ if i >= rssCount or not rss[i]: continue
for r in getattr(rss[i], c.Rule):
if not r: continue
if all(all(c.Intersect(s.glyphs, cd, k) for k in klist)
@@ -601,8 +587,9 @@
indices = ClassDef.intersect(cur_glyphs)
ContextData = c.ContextData(self)
rss = getattr(self, c.RuleSet)
+ rssCount = getattr(self, c.RuleSetCount)
for i in indices:
- if not rss[i]: continue
+ if i >= rssCount or not rss[i]: continue
for r in getattr(rss[i], c.Rule):
if not r: continue
if all(all(c.Intersect(s.glyphs, cd, k) for k in klist)
@@ -670,12 +657,21 @@
elif self.Format == 2:
if not self.Coverage.subset(s.glyphs):
return False
- indices = getattr(self, c.ClassDef).subset(self.Coverage.glyphs,
- remap=False)
- rss = getattr(self, c.RuleSet)
- rss = [rss[i] for i in indices]
ContextData = c.ContextData(self)
klass_maps = [x.subset(s.glyphs, remap=True) for x in ContextData]
+
+ # Keep rulesets for class numbers that survived.
+ indices = klass_maps[c.ClassDefIndex]
+ rss = getattr(self, c.RuleSet)
+ rssCount = getattr(self, c.RuleSetCount)
+ rss = [rss[i] for i in indices if i < rssCount]
+ del rssCount
+ # Delete, but not renumber, unreachable rulesets.
+ indices = getattr(self, c.ClassDef).intersect(self.Coverage.glyphs)
+ rss = [rss if i in indices else None for i,rss in enumerate(rss)]
+ while rss and rss[-1] is None:
+ del rss[-1]
+
for rs in rss:
if not rs: continue
ss = getattr(rs, c.Rule)
@@ -689,10 +685,6 @@
for r in ss:
c.SetRuleData(r, [[klass_map.index(k) for k in klist]
for klass_map,klist in zip(klass_maps, c.RuleData(r))])
- # Prune empty subrulesets
- rss = [rs for rs in rss if rs and getattr(rs, c.Rule)]
- setattr(self, c.RuleSet, rss)
- setattr(self, c.RuleSetCount, len(rss))
return bool(rss)
elif self.Format == 3:
return all(x.subset(s.glyphs) for x in c.RuleData(self))
@@ -864,6 +856,7 @@
@_add_method(otTables.LookupList)
def subset_lookups(self, lookup_indices):
+ self.ensureDecompiled()
self.Lookup = [self.Lookup[i] for i in lookup_indices
if i < self.LookupCount]
self.LookupCount = len(self.Lookup)
@@ -893,7 +886,7 @@
self.LookupListIndex = [lookup_indices.index(l)
for l in self.LookupListIndex]
self.LookupCount = len(self.LookupListIndex)
- return self.LookupCount
+ return self.LookupCount or self.FeatureParams
@_add_method(otTables.Feature)
def collect_lookups(self):
@@ -902,8 +895,13 @@
@_add_method(otTables.FeatureList)
def subset_lookups(self, lookup_indices):
"Returns the indices of nonempty features."
+ # Note: Never ever drop feature 'pref', even if it's empty.
+ # HarfBuzz chooses shaper for Khmer based on presence of this
+ # feature. See thread at:
+ # http://lists.freedesktop.org/archives/harfbuzz/2012-November/002660.html
feature_indices = [i for i,f in enumerate(self.FeatureRecord)
- if f.Feature.subset_lookups(lookup_indices)]
+ if (f.Feature.subset_lookups(lookup_indices) or
+ f.FeatureTag == 'pref')]
self.subset_features(feature_indices)
return feature_indices
@@ -915,6 +913,7 @@
@_add_method(otTables.FeatureList)
def subset_features(self, feature_indices):
+ self.ensureDecompiled()
self.FeatureRecord = [self.FeatureRecord[i] for i in feature_indices]
self.FeatureCount = len(self.FeatureRecord)
return bool(self.FeatureCount)
@@ -973,23 +972,33 @@
@_add_method(ttLib.getTableClass('GSUB'))
def closure_glyphs(self, s):
s.table = self.table
- feature_indices = self.table.ScriptList.collect_features()
- lookup_indices = self.table.FeatureList.collect_lookups(feature_indices)
- while True:
- orig_glyphs = s.glyphs.copy()
- for i in lookup_indices:
- if i >= self.table.LookupList.LookupCount: continue
- if not self.table.LookupList.Lookup[i]: continue
- self.table.LookupList.Lookup[i].closure_glyphs(s)
- if orig_glyphs == s.glyphs:
- break
+ if self.table.ScriptList:
+ feature_indices = self.table.ScriptList.collect_features()
+ else:
+ feature_indices = []
+ if self.table.FeatureList:
+ lookup_indices = self.table.FeatureList.collect_lookups(feature_indices)
+ else:
+ lookup_indices = []
+ if self.table.LookupList:
+ while True:
+ orig_glyphs = s.glyphs.copy()
+ for i in lookup_indices:
+ if i >= self.table.LookupList.LookupCount: continue
+ if not self.table.LookupList.Lookup[i]: continue
+ self.table.LookupList.Lookup[i].closure_glyphs(s)
+ if orig_glyphs == s.glyphs:
+ break
del s.table
@_add_method(ttLib.getTableClass('GSUB'),
ttLib.getTableClass('GPOS'))
def subset_glyphs(self, s):
s.glyphs = s.glyphs_gsubed
- lookup_indices = self.table.LookupList.subset_glyphs(s)
+ if self.table.LookupList:
+ lookup_indices = self.table.LookupList.subset_glyphs(s)
+ else:
+ lookup_indices = []
self.subset_lookups(lookup_indices)
self.prune_lookups()
return True
@@ -997,43 +1006,139 @@
@_add_method(ttLib.getTableClass('GSUB'),
ttLib.getTableClass('GPOS'))
def subset_lookups(self, lookup_indices):
- """Retrains specified lookups, then removes empty features, language
+ """Retains specified lookups, then removes empty features, language
systems, and scripts."""
- self.table.LookupList.subset_lookups(lookup_indices)
- feature_indices = self.table.FeatureList.subset_lookups(lookup_indices)
- self.table.ScriptList.subset_features(feature_indices)
+ if self.table.LookupList:
+ self.table.LookupList.subset_lookups(lookup_indices)
+ if self.table.FeatureList:
+ feature_indices = self.table.FeatureList.subset_lookups(lookup_indices)
+ else:
+ feature_indices = []
+ if self.table.ScriptList:
+ self.table.ScriptList.subset_features(feature_indices)
@_add_method(ttLib.getTableClass('GSUB'),
ttLib.getTableClass('GPOS'))
def prune_lookups(self):
"Remove unreferenced lookups"
- feature_indices = self.table.ScriptList.collect_features()
- lookup_indices = self.table.FeatureList.collect_lookups(feature_indices)
- lookup_indices = self.table.LookupList.closure_lookups(lookup_indices)
+ if self.table.ScriptList:
+ feature_indices = self.table.ScriptList.collect_features()
+ else:
+ feature_indices = []
+ if self.table.FeatureList:
+ lookup_indices = self.table.FeatureList.collect_lookups(feature_indices)
+ else:
+ lookup_indices = []
+ if self.table.LookupList:
+ lookup_indices = self.table.LookupList.closure_lookups(lookup_indices)
+ else:
+ lookup_indices = []
self.subset_lookups(lookup_indices)
@_add_method(ttLib.getTableClass('GSUB'),
ttLib.getTableClass('GPOS'))
def subset_feature_tags(self, feature_tags):
- feature_indices = [i for i,f in
- enumerate(self.table.FeatureList.FeatureRecord)
- if f.FeatureTag in feature_tags]
- self.table.FeatureList.subset_features(feature_indices)
- self.table.ScriptList.subset_features(feature_indices)
+ if self.table.FeatureList:
+ feature_indices = [i for i,f in
+ enumerate(self.table.FeatureList.FeatureRecord)
+ if f.FeatureTag in feature_tags]
+ self.table.FeatureList.subset_features(feature_indices)
+ else:
+ feature_indices = []
+ if self.table.ScriptList:
+ self.table.ScriptList.subset_features(feature_indices)
+
+@_add_method(ttLib.getTableClass('GSUB'),
+ ttLib.getTableClass('GPOS'))
+def prune_features(self):
+ "Remove unreferenced featurs"
+ if self.table.ScriptList:
+ feature_indices = self.table.ScriptList.collect_features()
+ else:
+ feature_indices = []
+ if self.table.FeatureList:
+ self.table.FeatureList.subset_features(feature_indices)
+ if self.table.ScriptList:
+ self.table.ScriptList.subset_features(feature_indices)
@_add_method(ttLib.getTableClass('GSUB'),
ttLib.getTableClass('GPOS'))
def prune_pre_subset(self, options):
+ # Drop undesired features
if '*' not in options.layout_features:
self.subset_feature_tags(options.layout_features)
+ # Drop unreferenced lookups
self.prune_lookups()
- self.table.LookupList.prune_pre_subset(options);
+ # Prune lookups themselves
+ if self.table.LookupList:
+ self.table.LookupList.prune_pre_subset(options);
return True
@_add_method(ttLib.getTableClass('GSUB'),
ttLib.getTableClass('GPOS'))
+def remove_redundant_langsys(self):
+ table = self.table
+ if not table.ScriptList or not table.FeatureList:
+ return
+
+ features = table.FeatureList.FeatureRecord
+
+ for s in table.ScriptList.ScriptRecord:
+ d = s.Script.DefaultLangSys
+ if not d:
+ continue
+ for lr in s.Script.LangSysRecord[:]:
+ l = lr.LangSys
+ # Compare d and l
+ if len(d.FeatureIndex) != len(l.FeatureIndex):
+ continue
+ if (d.ReqFeatureIndex == 65535) != (l.ReqFeatureIndex == 65535):
+ continue
+
+ if d.ReqFeatureIndex != 65535:
+ if features[d.ReqFeatureIndex] != features[l.ReqFeatureIndex]:
+ continue
+
+ for i in range(len(d.FeatureIndex)):
+ if features[d.FeatureIndex[i]] != features[l.FeatureIndex[i]]:
+ break
+ else:
+ # LangSys and default are equal; delete LangSys
+ s.Script.LangSysRecord.remove(lr)
+
+@_add_method(ttLib.getTableClass('GSUB'),
+ ttLib.getTableClass('GPOS'))
def prune_post_subset(self, options):
- self.table.LookupList.prune_post_subset(options);
+ table = self.table
+
+ # LookupList looks good. Just prune lookups themselves
+ if table.LookupList:
+ table.LookupList.prune_post_subset(options);
+ # XXX Next two lines disabled because OTS is stupid and
+ # doesn't like NULL offsetse here.
+ #if not table.LookupList.Lookup:
+ # table.LookupList = None
+
+ if not table.LookupList:
+ table.FeatureList = None
+
+ if table.FeatureList:
+ self.remove_redundant_langsys()
+ # Remove unreferenced features
+ self.prune_features()
+
+ # XXX Next two lines disabled because OTS is stupid and
+ # doesn't like NULL offsetse here.
+ #if table.FeatureList and not table.FeatureList.FeatureRecord:
+ # table.FeatureList = None
+
+ # Never drop scripts themselves as them just being available
+ # holds semantic significance.
+ # XXX Next two lines disabled because OTS is stupid and
+ # doesn't like NULL offsetse here.
+ #if table.ScriptList and not table.ScriptList.ScriptRecord:
+ # table.ScriptList = None
+
return True
@_add_method(ttLib.getTableClass('GDEF'))
@@ -1045,33 +1150,52 @@
table.LigCaretList.LigGlyph = [table.LigCaretList.LigGlyph[i]
for i in indices]
table.LigCaretList.LigGlyphCount = len(table.LigCaretList.LigGlyph)
- if not table.LigCaretList.LigGlyphCount:
- table.LigCaretList = None
if table.MarkAttachClassDef:
table.MarkAttachClassDef.classDefs = dict((g,v) for g,v in
table.MarkAttachClassDef.
- classDefs.iteritems()
+ classDefs.items()
if g in glyphs)
- if not table.MarkAttachClassDef.classDefs:
- table.MarkAttachClassDef = None
if table.GlyphClassDef:
table.GlyphClassDef.classDefs = dict((g,v) for g,v in
table.GlyphClassDef.
- classDefs.iteritems()
+ classDefs.items()
if g in glyphs)
- if not table.GlyphClassDef.classDefs:
- table.GlyphClassDef = None
if table.AttachList:
indices = table.AttachList.Coverage.subset(glyphs)
+ GlyphCount = table.AttachList.GlyphCount
table.AttachList.AttachPoint = [table.AttachList.AttachPoint[i]
- for i in indices]
+ for i in indices
+ if i < GlyphCount]
table.AttachList.GlyphCount = len(table.AttachList.AttachPoint)
- if not table.AttachList.GlyphCount:
- table.AttachList = None
+ if hasattr(table, "MarkGlyphSetsDef") and table.MarkGlyphSetsDef:
+ for coverage in table.MarkGlyphSetsDef.Coverage:
+ coverage.subset(glyphs)
+ # TODO: The following is disabled. If enabling, we need to go fixup all
+ # lookups that use MarkFilteringSet and map their set.
+ #indices = table.MarkGlyphSetsDef.Coverage = [c for c in table.MarkGlyphSetsDef.Coverage if c.glyphs]
+ return True
+
+@_add_method(ttLib.getTableClass('GDEF'))
+def prune_post_subset(self, options):
+ table = self.table
+ # XXX check these against OTS
+ if table.LigCaretList and not table.LigCaretList.LigGlyphCount:
+ table.LigCaretList = None
+ if table.MarkAttachClassDef and not table.MarkAttachClassDef.classDefs:
+ table.MarkAttachClassDef = None
+ if table.GlyphClassDef and not table.GlyphClassDef.classDefs:
+ table.GlyphClassDef = None
+ if table.AttachList and not table.AttachList.GlyphCount:
+ table.AttachList = None
+ if hasattr(table, "MarkGlyphSetsDef") and table.MarkGlyphSetsDef and not table.MarkGlyphSetsDef.Coverage:
+ table.MarkGlyphSetsDef = None
+ if table.Version == 0x00010002/0x10000:
+ table.Version = 1.0
return bool(table.LigCaretList or
- table.MarkAttachClassDef or
- table.GlyphClassDef or
- table.AttachList)
+ table.MarkAttachClassDef or
+ table.GlyphClassDef or
+ table.AttachList or
+ (table.Version >= 0x00010002/0x10000 and table.MarkGlyphSetsDef))
@_add_method(ttLib.getTableClass('kern'))
def prune_pre_subset(self, options):
@@ -1083,26 +1207,30 @@
def subset_glyphs(self, s):
glyphs = s.glyphs_gsubed
for t in self.kernTables:
- t.kernTable = dict(((a,b),v) for (a,b),v in t.kernTable.iteritems()
+ t.kernTable = dict(((a,b),v) for (a,b),v in t.kernTable.items()
if a in glyphs and b in glyphs)
self.kernTables = [t for t in self.kernTables if t.kernTable]
return bool(self.kernTables)
-@_add_method(ttLib.getTableClass('vmtx'),
- ttLib.getTableClass('hmtx'))
+@_add_method(ttLib.getTableClass('vmtx'))
def subset_glyphs(self, s):
- self.metrics = dict((g,v) for g,v in self.metrics.iteritems() if g in s.glyphs)
+ self.metrics = dict((g,v) for g,v in self.metrics.items() if g in s.glyphs)
return bool(self.metrics)
+@_add_method(ttLib.getTableClass('hmtx'))
+def subset_glyphs(self, s):
+ self.metrics = dict((g,v) for g,v in self.metrics.items() if g in s.glyphs)
+ return True # Required table
+
@_add_method(ttLib.getTableClass('hdmx'))
def subset_glyphs(self, s):
- self.hdmx = dict((sz,_dict((g,v) for g,v in l.iteritems() if g in s.glyphs))
- for sz,l in self.hdmx.iteritems())
+ self.hdmx = dict((sz,dict((g,v) for g,v in l.items() if g in s.glyphs))
+ for sz,l in self.hdmx.items())
return bool(self.hdmx)
@_add_method(ttLib.getTableClass('VORG'))
def subset_glyphs(self, s):
- self.VOriginRecords = dict((g,v) for g,v in self.VOriginRecords.iteritems()
+ self.VOriginRecords = dict((g,v) for g,v in self.VOriginRecords.items()
if g in s.glyphs)
self.numVertOriginYMetrics = len(self.VOriginRecords)
return True # Never drop; has default metrics
@@ -1111,12 +1239,12 @@
def prune_pre_subset(self, options):
if not options.glyph_names:
self.formatType = 3.0
- return True
+ return True # Required table
@_add_method(ttLib.getTableClass('post'))
def subset_glyphs(self, s):
self.extraNames = [] # This seems to do it
- return True
+ return True # Required table
@_add_method(ttLib.getTableModule('glyf').Glyph)
def remapComponentsFast(self, indices):
@@ -1173,9 +1301,9 @@
@_add_method(ttLib.getTableClass('glyf'))
def subset_glyphs(self, s):
- self.glyphs = dict((g,v) for g,v in self.glyphs.iteritems() if g in s.glyphs)
+ self.glyphs = dict((g,v) for g,v in self.glyphs.items() if g in s.glyphs)
indices = [i for i,g in enumerate(self.glyphOrder) if g in s.glyphs]
- for v in self.glyphs.itervalues():
+ for v in self.glyphs.values():
if hasattr(v, "data"):
v.remapComponentsFast(indices)
else:
@@ -1187,7 +1315,7 @@
@_add_method(ttLib.getTableClass('glyf'))
def prune_post_subset(self, options):
if not options.hinting:
- for v in self.glyphs.itervalues():
+ for v in self.glyphs.values():
v.removeHinting()
return True
@@ -1230,11 +1358,11 @@
sel.format = None
sel.gidArray = [sel.gidArray[i] for i in indices]
cs.charStrings = dict((g,indices.index(v))
- for g,v in cs.charStrings.iteritems()
+ for g,v in cs.charStrings.items()
if g in s.glyphs)
else:
cs.charStrings = dict((g,v)
- for g,v in cs.charStrings.iteritems()
+ for g,v in cs.charStrings.items()
if g in s.glyphs)
font.charset = [g for g in font.charset if g in s.glyphs]
font.numGlyphs = len(font.charset)
@@ -1245,12 +1373,12 @@
def subset_subroutines(self, subrs, gsubrs):
p = self.program
assert len(p)
- for i in xrange(1, len(p)):
+ for i in range(1, len(p)):
if p[i] == 'callsubr':
- assert type(p[i-1]) is int
+ assert isinstance(p[i-1], int)
p[i-1] = subrs._used.index(p[i-1] + subrs._old_bias) - subrs._new_bias
elif p[i] == 'callgsubr':
- assert type(p[i-1]) is int
+ assert isinstance(p[i-1], int)
p[i-1] = gsubrs._used.index(p[i-1] + gsubrs._old_bias) - gsubrs._new_bias
@_add_method(psCharStrings.T2CharString)
@@ -1274,6 +1402,8 @@
continue
i += 1
+ # TODO: we currently don't drop calls to "empty" subroutines.
+
assert len(self.program)
del self._hints
@@ -1298,7 +1428,7 @@
class _DehintingT2Decompiler(psCharStrings.SimpleT2Decompiler):
- class Hints:
+ class Hints(object):
def __init__(self):
# Whether calling this charstring produces any hint stems
self.has_hint = False
@@ -1335,8 +1465,8 @@
if hints.status != 2:
# Check from last_check, make sure we didn't have any operators.
- for i in xrange(hints.last_checked, len(charString.program) - 1):
- if type(charString.program[i]) == str:
+ for i in range(hints.last_checked, len(charString.program) - 1):
+ if isinstance(charString.program[i], str):
hints.status = 2
break;
else:
@@ -1381,15 +1511,15 @@
hints.has_hintmask = True
if hints.status != 2 and hints.has_hint:
# Check from last_check, see if we may be an implicit vstem
- for i in xrange(hints.last_checked, index - 1):
- if type(cs.program[i]) == str:
+ for i in range(hints.last_checked, index - 1):
+ if isinstance(cs.program[i], str):
hints.status = 2
break;
if hints.status != 2:
# We are an implicit vstem
hints.last_hint = index + 1
hints.status = 0
- hints.last_checked = index + 1
+ hints.last_checked = index + 1
def processHint(self, index):
cs = self.callingStack[-1]
@@ -1424,11 +1554,17 @@
if hints.status != 2:
# Check from last_check, make sure we didn't have
# any operators.
- for i in xrange(hints.last_checked, index - 1):
- if type(cs.program[i]) == str:
+ for i in range(hints.last_checked, index - 1):
+ if isinstance(cs.program[i], str):
hints.status = 2
break;
hints.last_checked = index
+ if hints.status != 2:
+ # Decide where to chop off from
+ if subr_hints.status == 0:
+ hints.last_hint = index
+ else:
+ hints.last_hint = index - 2 # Leave the subr call in
@_add_method(ttLib.getTableClass('CFF '))
def prune_post_subset(self, options):
@@ -1492,9 +1628,9 @@
else:
all_privs.append(font.Private)
for priv in all_privs:
- priv.BlueValues = []
- for k in ['OtherBlues', 'StemSnapH', 'StemSnapV', 'StdHW', 'StdVW', \
- 'FamilyBlues', 'FamilyOtherBlues']:
+ for k in ['BlueValues', 'OtherBlues', 'FamilyBlues', 'FamilyOtherBlues',
+ 'BlueScale', 'BlueShift', 'BlueFuzz',
+ 'StemSnapH', 'StemSnapV', 'StdHW', 'StdVW']:
if hasattr(priv, k):
setattr(priv, k, None)
@@ -1549,7 +1685,7 @@
if hasattr(subrs, 'offsets'):
del subrs.offsets
- for i in xrange (subrs.count):
+ for i in range (subrs.count):
subrs[i].subset_subroutines (local_subrs, font.GlobalSubrs)
# Cleanup
@@ -1585,7 +1721,7 @@
# For now, drop format=0 which can't be subset_glyphs easily?
self.tables = [t for t in self.tables if t.format != 0]
self.numSubTables = len(self.tables)
- return bool(self.tables)
+ return True # Required table
@_add_method(ttLib.getTableClass('cmap'))
def subset_glyphs(self, s):
@@ -1600,10 +1736,10 @@
if t.format == 14:
# TODO(behdad) XXX We drop all the default-UVS mappings(g==None).
t.uvsDict = dict((v,[(u,g) for u,g in l if g in s.glyphs])
- for v,l in t.uvsDict.iteritems())
- t.uvsDict = dict((v,l) for v,l in t.uvsDict.iteritems() if l)
+ for v,l in t.uvsDict.items())
+ t.uvsDict = dict((v,l) for v,l in t.uvsDict.items() if l)
else:
- t.cmap = dict((u,g) for u,g in t.cmap.iteritems()
+ t.cmap = dict((u,g) for u,g in t.cmap.items()
if g in s.glyphs_requested or u in s.unicodes_requested)
self.tables = [t for t in self.tables
if (t.cmap if t.format != 14 else t.uvsDict)]
@@ -1612,7 +1748,7 @@
# In particular, if we have a format=12 without non-BMP
# characters, either drop format=12 one or convert it
# to format=4 if there's not one.
- return bool(self.tables)
+ return True # Required table
@_add_method(ttLib.getTableClass('name'))
def prune_pre_subset(self, options):
@@ -1623,16 +1759,21 @@
if n.platformID == 3 and n.platEncID == 1]
if '*' not in options.name_languages:
self.names = [n for n in self.names if n.langID in options.name_languages]
- return True # Retain even if empty
+ return True # Required table
# TODO(behdad) OS/2 ulUnicodeRange / ulCodePageRange?
+# TODO(behdad) Drop AAT tables.
# TODO(behdad) Drop unneeded GSUB/GPOS Script/LangSys entries.
# TODO(behdad) Drop empty GSUB/GPOS, and GDEF if no GSUB/GPOS left
# TODO(behdad) Drop GDEF subitems if unused by lookups
# TODO(behdad) Avoid recursing too much (in GSUB/GPOS and in CFF)
# TODO(behdad) Text direction considerations.
# TODO(behdad) Text script / language considerations.
+# TODO(behdad) Optionally drop 'kern' table if GPOS available
+# TODO(behdad) Implement --unicode='*' to choose all cmap'ed
+# TODO(behdad) Drop old-spec Indic scripts
+
class Options(object):
@@ -1659,19 +1800,19 @@
'arabic': ['init', 'medi', 'fina', 'isol', 'med2', 'fin2', 'fin3',
'cswh', 'mset'],
'hangul': ['ljmo', 'vjmo', 'tjmo'],
- 'tibetal': ['abvs', 'blws', 'abvm', 'blwm'],
+ 'tibetan': ['abvs', 'blws', 'abvm', 'blwm'],
'indic': ['nukt', 'akhn', 'rphf', 'rkrf', 'pref', 'blwf', 'half',
'abvf', 'pstf', 'cfar', 'vatu', 'cjct', 'init', 'pres',
'abvs', 'blws', 'psts', 'haln', 'dist', 'abvm', 'blwm'],
}
_layout_features_default = _uniq_sort(sum(
- _layout_features_groups.itervalues(), []))
+ iter(_layout_features_groups.values()), []))
drop_tables = _drop_tables_default
no_subset_tables = _no_subset_tables_default
hinting_tables = _hinting_tables_default
layout_features = _layout_features_default
- hinting = False
+ hinting = True
glyph_names = False
legacy_cmap = False
symbol_cmap = False
@@ -1690,7 +1831,7 @@
self.set(**kwargs)
def set(self, **kwargs):
- for k,v in kwargs.iteritems():
+ for k,v in kwargs.items():
if not hasattr(self, k):
raise self.UnknownOptionError("Unknown option '%s'" % k)
setattr(self, k, v)
@@ -1721,7 +1862,7 @@
v = a[i+1:]
k = k.replace('-', '_')
if not hasattr(self, k):
- if ignore_unknown == True or k in ignore_unknown:
+ if ignore_unknown is True or k in ignore_unknown:
ret.append(orig_a)
continue
else:
@@ -1748,7 +1889,7 @@
if x in v:
v.remove(x)
else:
- assert 0
+ assert False
opts[k] = v
self.set(**opts)
@@ -1773,7 +1914,7 @@
def populate(self, glyphs=[], unicodes=[], text=""):
self.unicodes_requested.update(unicodes)
- if isinstance(text, str):
+ if isinstance(text, bytes):
text = text.decode("utf8")
for u in text:
self.unicodes_requested.add(ord(u))
@@ -1807,10 +1948,13 @@
def _closure_glyphs(self, font):
+ realGlyphs = set(font.getGlyphOrder())
+
self.glyphs = self.glyphs_requested.copy()
if 'cmap' in font:
font['cmap'].closure_glyphs(self)
+ self.glyphs.intersection_update(realGlyphs)
self.glyphs_cmaped = self.glyphs
if self.options.notdef_glyph:
@@ -1822,7 +1966,7 @@
self.log("Added .notdef to subset")
if self.options.recommended_glyphs:
if 'glyf' in font:
- for i in range(4):
+ for i in range(min(4, len(font.getGlyphOrder()))):
self.glyphs.add(font.getGlyphName(i))
self.log("Added first four glyphs to subset")
@@ -1831,6 +1975,7 @@
len(self.glyphs))
self.log.glyphs(self.glyphs, font=font)
font['GSUB'].closure_glyphs(self)
+ self.glyphs.intersection_update(realGlyphs)
self.log("Closed glyph list over 'GSUB': %d glyphs after" %
len(self.glyphs))
self.log.glyphs(self.glyphs, font=font)
@@ -1842,6 +1987,7 @@
len(self.glyphs))
self.log.glyphs(self.glyphs, font=font)
font['glyf'].closure_glyphs(self)
+ self.glyphs.intersection_update(realGlyphs)
self.log("Closed glyph list over 'glyf': %d glyphs after" %
len(self.glyphs))
self.log.glyphs(self.glyphs, font=font)
@@ -1852,6 +1998,9 @@
self.log("Retaining %d glyphs: " % len(self.glyphs_all))
+ del self.glyphs
+
+
def _subset_glyphs(self, font):
for tag in font.keys():
if tag == 'GlyphOrder': continue
@@ -1863,7 +2012,7 @@
table = font[tag]
self.glyphs = self.glyphs_all
retain = table.subset_glyphs(self)
- self.glyphs = self.glyphs_all
+ del self.glyphs
self.log.lapse("subset '%s'" % tag)
if not retain:
self.log(tag, "subsetted to empty; dropped")
@@ -1921,17 +2070,19 @@
def __call__(self, *things):
if not self.verbose:
return
- print ' '.join(str(x) for x in things)
+ print(' '.join(str(x) for x in things))
def lapse(self, *things):
if not self.timing:
return
new_time = time.time()
- print "Took %0.3fs to %s" %(new_time - self.last_time,
- ' '.join(str(x) for x in things))
+ print("Took %0.3fs to %s" %(new_time - self.last_time,
+ ' '.join(str(x) for x in things)))
self.last_time = new_time
def glyphs(self, glyphs, font=None):
+ if not self.verbose:
+ return
self("Names: ", sorted(glyphs))
if font:
reverseGlyphMap = font.getReverseGlyphMap()
@@ -1942,7 +2093,6 @@
return
from fontTools.misc import xmlWriter
writer = xmlWriter.XMLWriter(file)
- font.disassembleInstructions = False # Work around ttLib bug
for tag in font.keys():
writer.begintag(tag)
writer.newline()
@@ -1953,12 +2103,16 @@
def load_font(fontFile,
options,
+ allowVID=False,
checkChecksums=False,
- dontLoadGlyphNames=False):
+ dontLoadGlyphNames=False,
+ lazy=True):
font = ttLib.TTFont(fontFile,
- checkChecksums=checkChecksums,
- recalcBBoxes=options.recalc_bounds)
+ allowVID=allowVID,
+ checkChecksums=checkChecksums,
+ recalcBBoxes=options.recalc_bounds,
+ lazy=lazy)
# Hack:
#
@@ -1995,7 +2149,7 @@
args = options.parse_opts(args, ignore_unknown=['text'])
if len(args) < 2:
- print >>sys.stderr, "usage: pyftsubset font-file glyph... [--text=ABC]... [--option=value]..."
+ print("usage: pyftsubset font-file glyph... [--text=ABC]... [--option=value]...", file=sys.stderr)
sys.exit(1)
fontfile = args[0]
@@ -2040,7 +2194,7 @@
elif g.startswith('glyph') and len(g) > 5:
g = g[5:]
try:
- glyphs.append(font.getGlyphName(int(g), requireReal=1))
+ glyphs.append(font.getGlyphName(int(g), requireReal=True))
except ValueError:
raise Exception("Invalid glyph identifier: %s" % g)
continue
diff --git a/Lib/fontTools/t1Lib.py b/Lib/fontTools/t1Lib.py
index 42d95c2..abd4f32 100644
--- a/Lib/fontTools/t1Lib.py
+++ b/Lib/fontTools/t1Lib.py
@@ -8,24 +8,24 @@
to by 'path'.
Raises an error when the file does not contain valid Type 1 data.
-write(path, data, kind='OTHER', dohex=0)
+write(path, data, kind='OTHER', dohex=False)
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'.
"""
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
+from fontTools.misc import eexec
+from fontTools.misc.macCreatorType import getMacCreatorAndType
+import os
+import re
__author__ = "jvr"
__version__ = "1.0b2"
DEBUG = 0
-from fontTools.misc import eexec
-from fontTools.misc.macCreatorType import getMacCreatorAndType
-import string
-import re
-import os
-
try:
try:
@@ -42,7 +42,7 @@
class T1Error(Exception): pass
-class T1Font:
+class T1Font(object):
"""Type 1 font class.
@@ -100,20 +100,20 @@
# low level T1 data read and write functions
-def read(path, onlyHeader=0):
+def read(path, onlyHeader=False):
"""reads any Type 1 font file, returns raw data"""
- normpath = string.lower(path)
- creator, type = getMacCreatorAndType(path)
- if type == 'LWFN':
+ normpath = path.lower()
+ creator, typ = getMacCreatorAndType(path)
+ if typ == 'LWFN':
return readLWFN(path, onlyHeader), 'LWFN'
if normpath[-4:] == '.pfb':
return readPFB(path, onlyHeader), 'PFB'
else:
return readOther(path), 'OTHER'
-def write(path, data, kind='OTHER', dohex=0):
+def write(path, data, kind='OTHER', dohex=False):
assertType1(data)
- kind = string.upper(kind)
+ kind = kind.upper()
try:
os.remove(path)
except os.error:
@@ -141,7 +141,7 @@
HEXLINELENGTH = 80
-def readLWFN(path, onlyHeader=0):
+def readLWFN(path, onlyHeader=False):
"""reads an LWFN font file, returns raw data"""
resRef = Res.FSOpenResFile(path, 1) # read-only
try:
@@ -150,9 +150,9 @@
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 T1Error, 'corrupt LWFN file'
+ code = byteord(res.data[0])
+ if byteord(res.data[1]) != 0:
+ raise T1Error('corrupt LWFN file')
if code in [1, 2]:
if onlyHeader and code == 2:
break
@@ -166,21 +166,21 @@
elif code == 0:
pass # comment, ignore
else:
- raise T1Error, 'bad chunk code: ' + `code`
+ raise T1Error('bad chunk code: ' + repr(code))
finally:
Res.CloseResFile(resRef)
- data = string.join(data, '')
+ data = bytesjoin(data)
assertType1(data)
return data
-def readPFB(path, onlyHeader=0):
+def readPFB(path, onlyHeader=False):
"""reads a PFB font file, returns raw data"""
f = open(path, "rb")
data = []
- while 1:
- if f.read(1) <> chr(128):
- raise T1Error, 'corrupt PFB file'
- code = ord(f.read(1))
+ while True:
+ if f.read(1) != bytechr(128):
+ raise T1Error('corrupt PFB file')
+ code = byteord(f.read(1))
if code in [1, 2]:
chunklen = stringToLong(f.read(4))
chunk = f.read(chunklen)
@@ -189,11 +189,11 @@
elif code == 3:
break
else:
- raise T1Error, 'bad chunk code: ' + `code`
+ raise T1Error('bad chunk code: ' + repr(code))
if onlyHeader:
break
f.close()
- data = string.join(data, '')
+ data = bytesjoin(data)
assertType1(data)
return data
@@ -211,7 +211,7 @@
data.append(deHexString(chunk))
else:
data.append(chunk)
- return string.join(data, '')
+ return bytesjoin(data)
# file writing tools
@@ -228,11 +228,11 @@
else:
code = 1
while chunk:
- res = Res.Resource(chr(code) + '\0' + chunk[:LWFNCHUNKSIZE - 2])
+ res = Res.Resource(bytechr(code) + '\0' + chunk[:LWFNCHUNKSIZE - 2])
res.AddResource('POST', resID, '')
chunk = chunk[LWFNCHUNKSIZE - 2:]
resID = resID + 1
- res = Res.Resource(chr(5) + '\0')
+ res = Res.Resource(bytechr(5) + '\0')
res.AddResource('POST', resID, '')
finally:
Res.CloseResFile(resRef)
@@ -246,18 +246,18 @@
code = 2
else:
code = 1
- f.write(chr(128) + chr(code))
+ f.write(bytechr(128) + bytechr(code))
f.write(longToString(len(chunk)))
f.write(chunk)
- f.write(chr(128) + chr(3))
+ f.write(bytechr(128) + bytechr(3))
finally:
f.close()
-def writeOther(path, data, dohex = 0):
+def writeOther(path, data, dohex=False):
chunks = findEncryptedChunks(data)
f = open(path, "wb")
try:
- hexlinelen = HEXLINELENGTH / 2
+ hexlinelen = HEXLINELENGTH // 2
for isEncrypted, chunk in chunks:
if isEncrypted:
code = 2
@@ -297,9 +297,9 @@
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 T1Error, "invalid end of eexec part"
+ if decrypted[-len(EEXECINTERNALEND)-1:-1] != EEXECINTERNALEND \
+ and decrypted[-len(EEXECINTERNALEND)-2:-2] != EEXECINTERNALEND:
+ raise T1Error("invalid end of eexec part")
decrypted = decrypted[:-len(EEXECINTERNALEND)-2] + '\r'
data.append(EEXECBEGINMARKER + decrypted + EEXECENDMARKER)
else:
@@ -307,25 +307,25 @@
data.append(chunk[:-len(EEXECBEGIN)-1])
else:
data.append(chunk)
- return string.join(data, '')
+ return bytesjoin(data)
def findEncryptedChunks(data):
chunks = []
- while 1:
- eBegin = string.find(data, EEXECBEGIN)
+ while True:
+ eBegin = data.find(EEXECBEGIN)
if eBegin < 0:
break
eBegin = eBegin + len(EEXECBEGIN) + 1
- eEnd = string.find(data, EEXECEND, eBegin)
+ eEnd = data.find(EEXECEND, eBegin)
if eEnd < 0:
- raise T1Error, "can't find end of eexec part"
+ raise T1Error("can't find end of eexec part")
cypherText = data[eBegin:eEnd + 2]
if isHex(cypherText[:4]):
cypherText = deHexString(cypherText)
plainText, R = eexec.decrypt(cypherText, 55665)
- eEndLocal = string.find(plainText, EEXECINTERNALEND)
+ eEndLocal = plainText.find(EEXECINTERNALEND)
if eEndLocal < 0:
- raise T1Error, "can't find end of eexec part"
+ raise T1Error("can't find end of eexec part")
chunks.append((0, data[:eBegin]))
chunks.append((1, cypherText[:eEndLocal + len(EEXECINTERNALEND) + 1]))
data = data[eEnd:]
@@ -333,23 +333,23 @@
return chunks
def deHexString(hexstring):
- return eexec.deHexString(string.join(string.split(hexstring), ""))
+ return eexec.deHexString(strjoin(hexstring.split()))
# Type 1 assertion
-_fontType1RE = re.compile(r"/FontType\s+1\s+def")
+_fontType1RE = re.compile(br"/FontType\s+1\s+def")
def assertType1(data):
- for head in ['%!PS-AdobeFont', '%!FontType1']:
+ for head in [b'%!PS-AdobeFont', b'%!FontType1']:
if data[:len(head)] == head:
break
else:
- raise T1Error, "not a PostScript font"
+ raise T1Error("not a PostScript font")
if not _fontType1RE.search(data):
- raise T1Error, "not a Type 1 font"
- if string.find(data, "currentfile eexec") < 0:
- raise T1Error, "not an encrypted Type 1 font"
+ raise T1Error("not a Type 1 font")
+ if data.find(b"currentfile eexec") < 0:
+ raise T1Error("not an encrypted Type 1 font")
# XXX what else?
return data
@@ -357,16 +357,16 @@
# pfb helpers
def longToString(long):
- str = ""
+ s = ""
for i in range(4):
- str = str + chr((long & (0xff << (i * 8))) >> i * 8)
- return str
+ s += bytechr((long & (0xff << (i * 8))) >> i * 8)
+ return s
-def stringToLong(str):
- if len(str) <> 4:
- raise ValueError, 'string must be 4 bytes long'
- long = 0
+def stringToLong(s):
+ if len(s) != 4:
+ raise ValueError('string must be 4 bytes long')
+ l = 0
for i in range(4):
- long = long + (ord(str[i]) << (i * 8))
- return long
+ l += byteord(s[i]) << (i * 8)
+ return l
diff --git a/Lib/fontTools/ttLib/__init__.py b/Lib/fontTools/ttLib/__init__.py
index b1456d9..59c12fd 100644
--- a/Lib/fontTools/ttLib/__init__.py
+++ b/Lib/fontTools/ttLib/__init__.py
@@ -45,9 +45,10 @@
# $Id: __init__.py,v 1.51 2009-02-22 08:55:00 pabs3 Exp $
#
-import sys
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
import os
-import string
+import sys
haveMacSupport = 0
if sys.platform == "mac":
@@ -60,7 +61,7 @@
class TTLibError(Exception): pass
-class TTFont:
+class TTFont(object):
"""The main font object. It manages file input and output, and offers
a convenient way of accessing tables.
@@ -69,9 +70,9 @@
"""
def __init__(self, file=None, res_name_or_index=None,
- sfntVersion="\000\001\000\000", flavor=None, checkChecksums=0,
- verbose=0, recalcBBoxes=1, allowVID=0, ignoreDecompileErrors=False,
- fontNumber=-1):
+ sfntVersion="\000\001\000\000", flavor=None, checkChecksums=False,
+ verbose=False, recalcBBoxes=True, allowVID=False, ignoreDecompileErrors=False,
+ fontNumber=-1, lazy=False, quiet=False):
"""The constructor can be called with a few different arguments.
When reading a font from disk, 'file' should be either a pathname
@@ -109,7 +110,7 @@
supported. Asking for a glyph ID with a glyph name or GID that is not in
the font will return a virtual GID. This is valid for GSUB and cmap
tables. For SING glyphlets, the cmap table is used to specify Unicode
- values for virtual GI's used in GSUB/GPOS rules. If the gid Nis requested
+ values for virtual GI's used in GSUB/GPOS rules. If the gid N is requested
and does not exist in the font, or the glyphname has the form glyphN
and does not exist in the font, then N is used as the virtual GID.
Else, the first virtual GID is assigned as 0x1000 -1; for subsequent new
@@ -119,10 +120,15 @@
individual tables during decompilation will be ignored, falling
back to the DefaultTable implementation, which simply keeps the
binary data.
+
+ If lazy is set to True, many data structures are loaded lazily, upon
+ access only.
"""
from fontTools.ttLib import sfnt
self.verbose = verbose
+ self.quiet = quiet
+ self.lazy = lazy
self.recalcBBoxes = recalcBBoxes
self.tables = {}
self.reader = None
@@ -143,7 +149,7 @@
# assume file is a string
if haveMacSupport and res_name_or_index is not None:
# on the mac, we deal with sfnt resources as well as flat files
- import macUtils
+ from . import macUtils
if res_name_or_index == 0:
if macUtils.getSFNTResIndices(file):
# get the first available sfnt font.
@@ -166,7 +172,7 @@
if self.reader is not None:
self.reader.close()
- def save(self, file, makeSuitcase=0, reorderTables=1):
+ def save(self, file, makeSuitcase=False, reorderTables=True):
"""Save the font to disk. Similarly to the constructor,
the 'file' argument can be either a pathname or a writable
file object.
@@ -178,7 +184,7 @@
if not hasattr(file, "write"):
closeStream = 1
if os.name == "mac" and makeSuitcase:
- import macUtils
+ from . import macUtils
file = macUtils.SFNTResourceWriter(file, self)
else:
file = open(file, "wb")
@@ -189,7 +195,7 @@
# assume "file" is a writable file object
closeStream = 0
- tags = self.keys()
+ tags = list(self.keys())
if "GlyphOrder" in tags:
tags.remove("GlyphOrder")
numTables = len(tags)
@@ -215,8 +221,8 @@
if closeStream:
file.close()
- def saveXML(self, fileOrPath, progress=None,
- tables=None, skipTables=None, splitTables=0, disassembleInstructions=1,
+ def saveXML(self, fileOrPath, progress=None, quiet=False,
+ tables=None, skipTables=None, splitTables=False, disassembleInstructions=True,
bitmapGlyphDataFormat='raw'):
"""Export the font as TTX (an XML-based text file), or as a series of text
files when splitTables is true. In the latter case, the 'fileOrPath'
@@ -231,7 +237,7 @@
self.disassembleInstructions = disassembleInstructions
self.bitmapGlyphDataFormat = bitmapGlyphDataFormat
if not tables:
- tables = self.keys()
+ tables = list(self.keys())
if "GlyphOrder" not in tables:
tables = ["GlyphOrder"] + tables
if skipTables:
@@ -246,7 +252,7 @@
idlefunc = None
writer = xmlWriter.XMLWriter(fileOrPath, idlefunc=idlefunc)
- writer.begintag("ttFont", sfntVersion=`self.sfntVersion`[1:-1],
+ writer.begintag("ttFont", sfntVersion=repr(self.sfntVersion)[1:-1],
ttLibVersion=version)
writer.newline()
@@ -271,7 +277,7 @@
writer.newline()
else:
tableWriter = writer
- self._tableToXML(tableWriter, tag, progress)
+ self._tableToXML(tableWriter, tag, progress, quiet)
if splitTables:
tableWriter.endtag("ttFont")
tableWriter.newline()
@@ -284,8 +290,8 @@
if self.verbose:
debugmsg("Done dumping TTX")
- def _tableToXML(self, writer, tag, progress):
- if self.has_key(tag):
+ def _tableToXML(self, writer, tag, progress, quiet):
+ if tag in self:
table = self[tag]
report = "Dumping '%s' table..." % tag
else:
@@ -295,8 +301,9 @@
elif self.verbose:
debugmsg(report)
else:
- print report
- if not self.has_key(tag):
+ if not quiet:
+ print(report)
+ if tag not in self:
return
xmlTag = tagToXML(tag)
if hasattr(table, "ERROR"):
@@ -312,40 +319,43 @@
writer.newline()
writer.newline()
- def importXML(self, file, progress=None):
+ def importXML(self, file, progress=None, quiet=False):
"""Import a TTX file (an XML-based text format), so as to recreate
a font object.
"""
- if self.has_key("maxp") and self.has_key("post"):
+ if "maxp" in self and "post" in self:
# Make sure the glyph order is loaded, as it otherwise gets
# lost if the XML doesn't contain the glyph order, yet does
# contain the table which was originally used to extract the
# glyph names from (ie. 'post', 'cmap' or 'CFF ').
self.getGlyphOrder()
- import xmlImport
- xmlImport.importXML(self, file, progress)
+
+ from fontTools.misc import xmlReader
+
+ reader = xmlReader.XMLReader(file, self, progress, quiet)
+ reader.read()
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)
+ return tag in self.tables
def has_key(self, tag):
if self.isLoaded(tag):
- return 1
- elif self.reader and self.reader.has_key(tag):
- return 1
+ return True
+ elif self.reader and tag in self.reader:
+ return True
elif tag == "GlyphOrder":
- return 1
+ return True
else:
- return 0
+ return False
__contains__ = has_key
def keys(self):
- keys = self.tables.keys()
+ keys = list(self.tables.keys())
if self.reader:
- for key in self.reader.keys():
+ for key in list(self.reader.keys()):
if key not in keys:
keys.append(key)
@@ -355,9 +365,10 @@
return ["GlyphOrder"] + keys
def __len__(self):
- return len(self.keys())
+ return len(list(self.keys()))
def __getitem__(self, tag):
+ tag = Tag(tag)
try:
return self.tables[tag]
except KeyError:
@@ -381,10 +392,9 @@
if not self.ignoreDecompileErrors:
raise
# fall back to DefaultTable, retaining the binary table data
- print "An exception occurred during the decompilation of the '%s' table" % tag
- from tables.DefaultTable import DefaultTable
- import StringIO
- file = StringIO.StringIO()
+ print("An exception occurred during the decompilation of the '%s' table" % tag)
+ from .tables.DefaultTable import DefaultTable
+ file = StringIO()
traceback.print_exc(file=file)
table = DefaultTable(tag)
table.ERROR = file.getvalue()
@@ -392,17 +402,17 @@
table.decompile(data, self)
return table
else:
- raise KeyError, "'%s' table not found" % tag
+ raise KeyError("'%s' table not found" % tag)
def __setitem__(self, tag, table):
- self.tables[tag] = table
+ self.tables[Tag(tag)] = table
def __delitem__(self, tag):
- if not self.has_key(tag):
- raise KeyError, "'%s' table not found" % tag
- if self.tables.has_key(tag):
+ if tag not in self:
+ raise KeyError("'%s' table not found" % tag)
+ if tag in self.tables:
del self.tables[tag]
- if self.reader and self.reader.has_key(tag):
+ if self.reader and tag in self.reader:
del self.reader[tag]
def setGlyphOrder(self, glyphOrder):
@@ -413,10 +423,10 @@
return self.glyphOrder
except AttributeError:
pass
- if self.has_key('CFF '):
+ if 'CFF ' in self:
cff = self['CFF ']
self.glyphOrder = cff.getGlyphOrder()
- elif self.has_key('post'):
+ elif 'post' in self:
# TrueType font
glyphOrder = self['post'].getGlyphOrder()
if glyphOrder is None:
@@ -476,24 +486,23 @@
cmap = tempcmap.cmap
# create a reverse cmap dict
reversecmap = {}
- for unicode, name in cmap.items():
+ for unicode, name in list(cmap.items()):
reversecmap[name] = unicode
allNames = {}
for i in range(numGlyphs):
tempName = glyphOrder[i]
- if reversecmap.has_key(tempName):
+ if tempName in reversecmap:
unicode = reversecmap[tempName]
- if agl.UV2AGL.has_key(unicode):
+ if unicode in agl.UV2AGL:
# get name from the Adobe Glyph List
glyphName = agl.UV2AGL[unicode]
else:
# create uni<CODE> name
- glyphName = "uni" + string.upper(string.zfill(
- hex(unicode)[2:], 4))
+ glyphName = "uni%04X" % unicode
tempName = glyphName
n = 1
- while allNames.has_key(tempName):
- tempName = glyphName + "#" + `n`
+ while tempName in allNames:
+ tempName = glyphName + "#" + repr(n)
n = n + 1
glyphOrder[i] = tempName
allNames[tempName] = 1
@@ -510,8 +519,7 @@
def getGlyphNames(self):
"""Get a list of glyph names, sorted alphabetically."""
- glyphNames = self.getGlyphOrder()[:]
- glyphNames.sort()
+ glyphNames = sorted(self.getGlyphOrder()[:])
return glyphNames
def getGlyphNames2(self):
@@ -521,7 +529,7 @@
from fontTools.misc import textTools
return textTools.caselessSort(self.getGlyphOrder())
- def getGlyphName(self, glyphID, requireReal=0):
+ def getGlyphName(self, glyphID, requireReal=False):
try:
return self.getGlyphOrder()[glyphID]
except IndexError:
@@ -540,18 +548,25 @@
self.VIDDict[glyphID] = glyphName
return glyphName
- def getGlyphID(self, glyphName, requireReal = 0):
+ def getGlyphID(self, glyphName, requireReal=False):
if not hasattr(self, "_reverseGlyphOrderDict"):
self._buildReverseGlyphOrderDict()
glyphOrder = self.getGlyphOrder()
d = self._reverseGlyphOrderDict
- if not d.has_key(glyphName):
+ if glyphName not in d:
if glyphName in glyphOrder:
self._buildReverseGlyphOrderDict()
return self.getGlyphID(glyphName)
else:
- if requireReal or not self.allowVID:
- raise KeyError, glyphName
+ if requireReal:
+ raise KeyError(glyphName)
+ elif not self.allowVID:
+ # Handle glyphXXX only
+ if glyphName[:5] == "glyph":
+ try:
+ return int(glyphName[5:])
+ except (NameError, ValueError):
+ raise KeyError(glyphName)
else:
# user intends virtual GID support
try:
@@ -563,7 +578,7 @@
glyphID = int(glyphName[5:])
except (NameError, ValueError):
glyphID = None
- if glyphID == None:
+ if glyphID is None:
glyphID = self.last_vid -1
self.last_vid = glyphID
self.reverseVIDDict[glyphName] = glyphID
@@ -571,12 +586,12 @@
return glyphID
glyphID = d[glyphName]
- if glyphName <> glyphOrder[glyphID]:
+ if glyphName != glyphOrder[glyphID]:
self._buildReverseGlyphOrderDict()
return self.getGlyphID(glyphName)
return glyphID
- def getReverseGlyphMap(self, rebuild=0):
+ def getReverseGlyphMap(self, rebuild=False):
if rebuild or not hasattr(self, "_reverseGlyphOrderDict"):
self._buildReverseGlyphOrderDict()
return self._reverseGlyphOrderDict
@@ -596,7 +611,7 @@
tableClass = getTableClass(tag)
for masterTable in tableClass.dependencies:
if masterTable not in done:
- if self.has_key(masterTable):
+ if masterTable in self:
self._writeTable(masterTable, writer, done)
else:
done.append(masterTable)
@@ -609,18 +624,19 @@
def getTableData(self, tag):
"""Returns raw table data, whether compiled or directly read from disk.
"""
+ tag = Tag(tag)
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):
+ elif self.reader and tag in self.reader:
if self.verbose:
debugmsg("Reading '%s' table from disk" % tag)
return self.reader[tag]
else:
- raise KeyError, tag
+ raise KeyError(tag)
- def getGlyphSet(self, preferCFF=1):
+ def getGlyphSet(self, preferCFF=True):
"""Return a generic GlyphSet, which is a dict-like object
mapping glyph names to glyph objects. The returned glyph objects
have a .draw() method that supports the Pen protocol, and will
@@ -632,16 +648,16 @@
If the font contains both a 'CFF ' and a 'glyf' table, you can use
the 'preferCFF' argument to specify which one should be taken.
"""
- if preferCFF and self.has_key("CFF "):
- return self["CFF "].cff.values()[0].CharStrings
- if self.has_key("glyf"):
+ if preferCFF and "CFF " in self:
+ return list(self["CFF "].cff.values())[0].CharStrings
+ if "glyf" in self:
return _TTGlyphSet(self)
- if self.has_key("CFF "):
- return self["CFF "].cff.values()[0].CharStrings
- raise TTLibError, "Font contains no outlines"
+ if "CFF " in self:
+ return list(self["CFF "].cff.values())[0].CharStrings
+ raise TTLibError("Font contains no outlines")
-class _TTGlyphSet:
+class _TTGlyphSet(object):
"""Generic dict-like GlyphSet class, meant as a TrueType counterpart
to CFF's CharString dict. See TTFont.getGlyphSet().
@@ -655,10 +671,10 @@
self._ttFont = ttFont
def keys(self):
- return self._ttFont["glyf"].keys()
+ return list(self._ttFont["glyf"].keys())
def has_key(self, glyphName):
- return self._ttFont["glyf"].has_key(glyphName)
+ return glyphName in self._ttFont["glyf"]
__contains__ = has_key
@@ -672,7 +688,7 @@
return default
-class _TTGlyph:
+class _TTGlyph(object):
"""Wrapper for a TrueType glyph that supports the Pen protocol, meaning
that it has a .draw() method that takes a pen object as its only
@@ -733,7 +749,7 @@
pen.closePath()
-class GlyphOrder:
+class GlyphOrder(object):
"""A pseudo table. The glyph order isn't in the font as a separate
table, but it's nice to present it as such in the TTX format.
@@ -752,7 +768,7 @@
writer.simpletag("GlyphID", id=i, name=glyphName)
writer.newline()
- def fromXML(self, (name, attrs, content), ttFont):
+ def fromXML(self, name, attrs, content, ttFont):
if not hasattr(self, "glyphOrder"):
self.glyphOrder = []
ttFont.setGlyphOrder(self.glyphOrder)
@@ -764,11 +780,11 @@
"""Fetch the packer/unpacker module for a table.
Return None when no module is found.
"""
- import tables
+ from . import tables
pyTag = tagToIdentifier(tag)
try:
__import__("fontTools.ttLib.tables." + pyTag)
- except ImportError, err:
+ except ImportError as err:
# If pyTag is found in the ImportError message,
# means table is not implemented. If it's not
# there, then some other module is missing, don't
@@ -787,7 +803,7 @@
"""
module = getTableModule(tag)
if module is None:
- from tables.DefaultTable import DefaultTable
+ from .tables.DefaultTable import DefaultTable
return DefaultTable
pyTag = tagToIdentifier(tag)
tableClass = getattr(module, "table_" + pyTag)
@@ -808,7 +824,7 @@
elif re.match("[A-Z]", c):
return c + "_"
else:
- return hex(ord(c))[2:]
+ return hex(byteord(c))[2:]
def tagToIdentifier(tag):
@@ -825,6 +841,7 @@
'OS/2' -> 'O_S_2f_2'
"""
import re
+ tag = Tag(tag)
if tag == "GlyphOrder":
return tag
assert len(tag) == 4, "tag should be 4 characters long"
@@ -853,10 +870,10 @@
tag = tag + ident[i]
else:
# assume hex
- tag = tag + chr(int(ident[i:i+2], 16))
+ tag = tag + bytechr(int(ident[i:i+2], 16))
# append trailing spaces
tag = tag + (4 - len(tag)) * ' '
- return tag
+ return Tag(tag)
def tagToXML(tag):
@@ -865,12 +882,13 @@
case sensitive, this is a fairly simple/readable translation.
"""
import re
+ tag = Tag(tag)
if tag == "OS/2":
return "OS_2"
elif tag == "GlyphOrder":
return tag
if re.match("[A-Za-z_][A-Za-z_0-9]* *$", tag):
- return string.strip(tag)
+ return tag.strip()
else:
return tagToIdentifier(tag)
@@ -878,17 +896,16 @@
def xmlToTag(tag):
"""The opposite of tagToXML()"""
if tag == "OS_2":
- return "OS/2"
+ return Tag("OS/2")
if len(tag) == 8:
return identifierToTag(tag)
else:
- return tag + " " * (4 - len(tag))
- return tag
+ return Tag(tag + " " * (4 - len(tag)))
def debugmsg(msg):
import time
- print msg + time.strftime(" (%H:%M:%S)", time.localtime(time.time()))
+ print(msg + time.strftime(" (%H:%M:%S)", time.localtime(time.time())))
# Table order as recommended in the OpenType specification 1.4
@@ -904,8 +921,7 @@
specification, or according to a custom tableOrder. If given and not
None, tableOrder needs to be a list of tag names.
"""
- tagList = list(tagList)
- tagList.sort()
+ tagList = sorted(tagList)
if tableOrder is None:
if "DSIG" in tagList:
# DSIG should be last (XXX spec reference?)
@@ -924,14 +940,14 @@
return orderedTables
-def reorderFontTables(inFile, outFile, tableOrder=None, checkChecksums=0):
+def reorderFontTables(inFile, outFile, tableOrder=None, checkChecksums=False):
"""Rewrite a font file, ordering the tables as recommended by the
OpenType specification 1.4.
"""
from fontTools.ttLib.sfnt import SFNTReader, SFNTWriter
reader = SFNTReader(inFile, checkChecksums=checkChecksums)
- writer = SFNTWriter(outFile, reader.numTables, reader.sfntVersion, reader.flavor, reader.flavorData)
- tables = reader.keys()
+ writer = SFNTWriter(outFile, len(reader.tables), reader.sfntVersion, reader.flavor, reader.flavorData)
+ tables = list(reader.keys())
for tag in sortedTagList(tables, tableOrder):
writer[tag] = reader[tag]
writer.close()
diff --git a/Lib/fontTools/ttLib/macUtils.py b/Lib/fontTools/ttLib/macUtils.py
index 2d5aedb..c7c261d 100644
--- a/Lib/fontTools/ttLib/macUtils.py
+++ b/Lib/fontTools/ttLib/macUtils.py
@@ -1,24 +1,25 @@
"""ttLib.macUtils.py -- Various Mac-specific stuff."""
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
import sys
import os
if sys.platform not in ("mac", "darwin"):
- raise ImportError, "This module is Mac-only!"
-
-import cStringIO
+ raise ImportError("This module is Mac-only!")
try:
from Carbon import Res
except ImportError:
import Res
+
def MyOpenResFile(path):
mode = 1 # read only
try:
resref = Res.FSOpenResFile(path, mode)
except Res.Error:
# try data fork
- resref = Res.FSOpenResourceFile(path, u'', mode)
+ resref = Res.FSOpenResourceFile(path, unicode(), mode)
return resref
@@ -31,7 +32,7 @@
Res.UseResFile(resref)
numSFNTs = Res.Count1Resources('sfnt')
Res.CloseResFile(resref)
- return range(1, numSFNTs + 1)
+ return list(range(1, numSFNTs + 1))
def openTTFonts(path):
@@ -49,22 +50,22 @@
for index in sfnts:
fonts.append(ttLib.TTFont(path, index))
if not fonts:
- raise ttLib.TTLibError, "no fonts found in file '%s'" % path
+ raise ttLib.TTLibError("no fonts found in file '%s'" % path)
return fonts
-class SFNTResourceReader:
+class SFNTResourceReader(object):
"""Simple (Mac-only) read-only file wrapper for 'sfnt' resources."""
def __init__(self, path, res_name_or_index):
resref = MyOpenResFile(path)
Res.UseResFile(resref)
- if type(res_name_or_index) == type(""):
+ if isinstance(res_name_or_index, basestring):
res = Res.Get1NamedResource('sfnt', res_name_or_index)
else:
res = Res.Get1IndResource('sfnt', res_name_or_index)
- self.file = cStringIO.StringIO(res.data)
+ self.file = StringIO(res.data)
Res.CloseResFile(resref)
self.name = path
@@ -73,12 +74,12 @@
return getattr(self.file, attr)
-class SFNTResourceWriter:
+class SFNTResourceWriter(object):
"""Simple (Mac-only) file wrapper for 'sfnt' resources."""
def __init__(self, path, ttFont, res_id=None):
- self.file = cStringIO.StringIO()
+ self.file = StringIO()
self.name = path
self.closed = 0
fullname = ttFont['name'].getName(4, 1, 0) # Full name, mac, default encoding
@@ -86,15 +87,15 @@
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"
+ 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)]:
+ 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]:
+ if self.familyname[i] != self.psname[i]:
break
self.familyname = self.psname[:i]
@@ -157,7 +158,7 @@
fond.styleMappingReserved = 0
# calc:
- scale = 4096.0 / self.ttFont['head'].unitsPerEm
+ scale = 4096 / 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
@@ -172,20 +173,20 @@
names = {}
for code, name in cmap.cmap.items():
names[name] = code
- if self.ttFont.has_key('kern'):
+ if 'kern' in self.ttFont:
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):
+ if left in names and right in names:
fondkerning.append((names[left], names[right], scale * value))
fondkerning.sort()
fond.kernTables = {0: fondkerning}
- if self.ttFont.has_key('hmtx'):
+ if 'hmtx' in self.ttFont:
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):
+ if name in names:
fondwidths[names[name]] = scale * width
fond.widthTables = {0: fondwidths}
fond.save()
diff --git a/Lib/fontTools/ttLib/sfnt.py b/Lib/fontTools/ttLib/sfnt.py
index 82e3e04..6291510 100644
--- a/Lib/fontTools/ttLib/sfnt.py
+++ b/Lib/fontTools/ttLib/sfnt.py
@@ -12,13 +12,13 @@
a table's length chages you need to rewrite the whole file anyway.
"""
-import sys
-import struct
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
from fontTools.misc import sstruct
-import os
+import struct
-class SFNTReader:
+class SFNTReader(object):
def __init__(self, file, checkChecksums=1, fontNumber=-1):
self.file = file
@@ -29,33 +29,34 @@
self.DirectoryEntry = SFNTDirectoryEntry
self.sfntVersion = self.file.read(4)
self.file.seek(0)
- if self.sfntVersion == "ttcf":
+ if self.sfntVersion == b"ttcf":
sstruct.unpack(ttcHeaderFormat, self.file.read(ttcHeaderSize), self)
assert self.Version == 0x00010000 or self.Version == 0x00020000, "unrecognized TTC version 0x%08x" % self.Version
if not 0 <= fontNumber < self.numFonts:
from fontTools import ttLib
- raise ttLib.TTLibError, "specify a font number between 0 and %d (inclusive)" % (self.numFonts - 1)
+ raise ttLib.TTLibError("specify a font number between 0 and %d (inclusive)" % (self.numFonts - 1))
offsetTable = struct.unpack(">%dL" % self.numFonts, self.file.read(self.numFonts * 4))
if self.Version == 0x00020000:
pass # ignoring version 2.0 signatures
self.file.seek(offsetTable[fontNumber])
sstruct.unpack(sfntDirectoryFormat, self.file.read(sfntDirectorySize), self)
- elif self.sfntVersion == "wOFF":
+ elif self.sfntVersion == b"wOFF":
self.flavor = "woff"
self.DirectoryEntry = WOFFDirectoryEntry
sstruct.unpack(woffDirectoryFormat, self.file.read(woffDirectorySize), self)
else:
sstruct.unpack(sfntDirectoryFormat, self.file.read(sfntDirectorySize), self)
+ self.sfntVersion = Tag(self.sfntVersion)
- if self.sfntVersion not in ("\000\001\000\000", "OTTO", "true"):
+ if self.sfntVersion not in ("\x00\x01\x00\x00", "OTTO", "true"):
from fontTools import ttLib
- raise ttLib.TTLibError, "Not a TrueType or OpenType font (bad sfntVersion)"
+ raise ttLib.TTLibError("Not a TrueType or OpenType font (bad sfntVersion)")
self.tables = {}
for i in range(self.numTables):
entry = self.DirectoryEntry()
entry.fromFile(self.file)
if entry.length > 0:
- self.tables[entry.tag] = entry
+ self.tables[Tag(entry.tag)] = entry
else:
# Ignore zero-length tables. This doesn't seem to be documented,
# yet it's apparently how the Windows TT rasterizer behaves.
@@ -68,43 +69,45 @@
self.flavorData = WOFFFlavorData(self)
def has_key(self, tag):
- return self.tables.has_key(tag)
+ return tag in self.tables
+
+ __contains__ = has_key
def keys(self):
return self.tables.keys()
def __getitem__(self, tag):
"""Fetch the raw table data."""
- entry = self.tables[tag]
+ entry = self.tables[Tag(tag)]
data = entry.loadData (self.file)
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:])
+ checksum = calcChecksum(data[:8] + b'\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:
+ elif checksum != entry.checkSum:
# Be friendly, and just print a warning.
- print "bad checksum for '%s' table" % tag
+ print("bad checksum for '%s' table" % tag)
return data
def __delitem__(self, tag):
- del self.tables[tag]
+ del self.tables[Tag(tag)]
def close(self):
self.file.close()
-class SFNTWriter:
+class SFNTWriter(object):
def __init__(self, file, numTables, sfntVersion="\000\001\000\000",
flavor=None, flavorData=None):
self.file = file
self.numTables = numTables
- self.sfntVersion = sfntVersion
+ self.sfntVersion = Tag(sfntVersion)
self.flavor = flavor
self.flavorData = flavorData
@@ -126,27 +129,27 @@
# clear out directory area
self.file.seek(self.nextTableOffset)
# make sure we're actually where we want to be. (old cStringIO bug)
- self.file.write('\0' * (self.nextTableOffset - self.file.tell()))
+ self.file.write(b'\0' * (self.nextTableOffset - self.file.tell()))
self.tables = {}
def __setitem__(self, tag, data):
"""Write raw table data to disk."""
reuse = False
- if self.tables.has_key(tag):
+ if tag in self.tables:
# We've written this table to file before. If the length
# of the data is still the same, we allow overwriting it.
entry = self.tables[tag]
assert not hasattr(entry.__class__, 'encodeData')
- if len(data) <> entry.length:
+ if len(data) != entry.length:
from fontTools import ttLib
- raise ttLib.TTLibError, "cannot rewrite '%s' table: length does not match directory entry" % tag
+ raise ttLib.TTLibError("cannot rewrite '%s' table: length does not match directory entry" % tag)
reuse = True
else:
entry = self.DirectoryEntry()
entry.tag = tag
if tag == 'head':
- entry.checkSum = calcChecksum(data[:8] + '\0\0\0\0' + data[12:])
+ entry.checkSum = calcChecksum(data[:8] + b'\0\0\0\0' + data[12:])
self.headTable = data
entry.uncompressed = True
else:
@@ -162,7 +165,7 @@
# Don't depend on f.seek() as we need to add the padding even if no
# subsequent write follows (seek is lazy), ie. after the final table
# in the font.
- self.file.write('\0' * (self.nextTableOffset - self.file.tell()))
+ self.file.write(b'\0' * (self.nextTableOffset - self.file.tell()))
assert self.nextTableOffset == self.file.tell()
self.tables[tag] = entry
@@ -171,14 +174,13 @@
"""All tables must have been written to disk. Now write the
directory.
"""
- tables = self.tables.items()
- tables.sort()
- if len(tables) <> self.numTables:
+ tables = sorted(self.tables.items())
+ if len(tables) != self.numTables:
from fontTools import ttLib
- raise ttLib.TTLibError, "wrong number of tables; expected %d, found %d" % (self.numTables, len(tables))
+ raise ttLib.TTLibError("wrong number of tables; expected %d, found %d" % (self.numTables, len(tables)))
if self.flavor == "woff":
- self.signature = "wOFF"
+ self.signature = b"wOFF"
self.reserved = 0
self.totalSfntSize = 12
@@ -187,7 +189,7 @@
self.totalSfntSize += (entry.origLength + 3) & ~3
data = self.flavorData if self.flavorData else WOFFFlavorData()
- if data.majorVersion != None and data.minorVersion != None:
+ if data.majorVersion is not None and data.minorVersion is not None:
self.majorVersion = data.majorVersion
self.minorVersion = data.minorVersion
else:
@@ -199,6 +201,7 @@
self.metaOrigLength = len(data.metaData)
self.file.seek(0,2)
self.metaOffset = self.file.tell()
+ import zlib
compressedMetaData = zlib.compress(data.metaData)
self.metaLength = len(compressedMetaData)
self.file.write(compressedMetaData)
@@ -237,7 +240,7 @@
def _calcMasterChecksum(self, directory):
# calculate checkSumAdjustment
- tags = self.tables.keys()
+ tags = list(self.tables.keys())
checksums = []
for i in range(len(tags)):
checksums.append(self.tables[tags[i]].checkSum)
@@ -248,8 +251,7 @@
# Create a SFNT directory for checksum calculation purposes
self.searchRange, self.entrySelector, self.rangeShift = getSearchRange(self.numTables)
directory = sstruct.pack(sfntDirectoryFormat, self)
- tables = self.tables.items()
- tables.sort()
+ tables = sorted(self.tables.items())
for tag, entry in tables:
sfntEntry = SFNTDirectoryEntry()
for item in ['tag', 'checkSum', 'offset', 'length']:
@@ -339,7 +341,7 @@
woffDirectoryEntrySize = sstruct.calcsize(woffDirectoryEntryFormat)
-class DirectoryEntry:
+class DirectoryEntry(object):
def __init__(self):
self.uncompressed = False # if True, always embed entry raw
@@ -429,14 +431,15 @@
self.minorVersion = reader.minorVersion
if reader.metaLength:
reader.file.seek(reader.metaOffset)
- rawData = read.file.read(reader.metaLength)
+ rawData = reader.file.read(reader.metaLength)
assert len(rawData) == reader.metaLength
+ import zlib
data = zlib.decompress(rawData)
assert len(data) == reader.metaOrigLength
self.metaData = data
if reader.privLength:
reader.file.seek(reader.privOffset)
- data = read.file.read(reader.privLength)
+ data = reader.file.read(reader.privLength)
assert len(data) == reader.privLength
self.privData = data
@@ -450,18 +453,18 @@
If the data length is not a multiple of four, it assumes
it is to be padded with null byte.
- >>> print calcChecksum("abcd")
+ >>> print calcChecksum(b"abcd")
1633837924
- >>> print calcChecksum("abcdxyz")
+ >>> print calcChecksum(b"abcdxyz")
3655064932
"""
remainder = len(data) % 4
if remainder:
- data += "\0" * (4 - remainder)
+ data += b"\0" * (4 - remainder)
value = 0
blockSize = 4096
assert blockSize % 4 == 0
- for i in xrange(0, len(data), blockSize):
+ for i in range(0, len(data), blockSize):
block = data[i:i+blockSize]
longs = struct.unpack(">%dL" % (len(block) // 4), block)
value = (value + sum(longs)) & 0xffffffff
@@ -484,7 +487,6 @@
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
diff --git a/Lib/fontTools/ttLib/tables/B_A_S_E_.py b/Lib/fontTools/ttLib/tables/B_A_S_E_.py
index 53975c8..9551e2c 100644
--- a/Lib/fontTools/ttLib/tables/B_A_S_E_.py
+++ b/Lib/fontTools/ttLib/tables/B_A_S_E_.py
@@ -1,4 +1,4 @@
-from otBase import BaseTTXConverter
+from .otBase import BaseTTXConverter
class table_B_A_S_E_(BaseTTXConverter):
diff --git a/Lib/fontTools/ttLib/tables/BitmapGlyphMetrics.py b/Lib/fontTools/ttLib/tables/BitmapGlyphMetrics.py
index 71514e1..6e5e1f0 100644
--- a/Lib/fontTools/ttLib/tables/BitmapGlyphMetrics.py
+++ b/Lib/fontTools/ttLib/tables/BitmapGlyphMetrics.py
@@ -1,7 +1,8 @@
# Since bitmap glyph metrics are shared between EBLC and EBDT
# this class gets its own python file.
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
from fontTools.misc import sstruct
-from types import TupleType
from fontTools.misc.textTools import safeEval
@@ -26,7 +27,7 @@
Advance: B
"""
-class BitmapGlyphMetrics:
+class BitmapGlyphMetrics(object):
def toXML(self, writer, ttFont):
writer.begintag(self.__class__.__name__)
@@ -37,17 +38,17 @@
writer.endtag(self.__class__.__name__)
writer.newline()
- def fromXML(self, (name, attrs, content), ttFont):
+ def fromXML(self, name, attrs, content, ttFont):
metricNames = set(sstruct.getformat(self.__class__.binaryFormat)[1])
for element in content:
- if type(element) != TupleType:
+ if not isinstance(element, tuple):
continue
name, attrs, content = element
# Make sure this is a metric that is needed by GlyphMetrics.
if name in metricNames:
vars(self)[name] = safeEval(attrs['value'])
else:
- print "Warning: unknown name '%s' being ignored in %s." % name, self.__class__.__name__
+ print("Warning: unknown name '%s' being ignored in %s." % name, self.__class__.__name__)
class BigGlyphMetrics(BitmapGlyphMetrics):
diff --git a/Lib/fontTools/ttLib/tables/C_B_D_T_.py b/Lib/fontTools/ttLib/tables/C_B_D_T_.py
index 2289594..cd2a975 100644
--- a/Lib/fontTools/ttLib/tables/C_B_D_T_.py
+++ b/Lib/fontTools/ttLib/tables/C_B_D_T_.py
@@ -3,12 +3,13 @@
# Google Author(s): Matt Fontaine
-import E_B_D_T_
-import string
-import struct
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
from fontTools.misc import sstruct
-from BitmapGlyphMetrics import BigGlyphMetrics, bigGlyphMetricsFormat, SmallGlyphMetrics, smallGlyphMetricsFormat
-from E_B_D_T_ import BitmapGlyph, BitmapPlusSmallMetricsMixin, BitmapPlusBigMetricsMixin
+from . import E_B_D_T_
+from .BitmapGlyphMetrics import BigGlyphMetrics, bigGlyphMetricsFormat, SmallGlyphMetrics, smallGlyphMetricsFormat
+from .E_B_D_T_ import BitmapGlyph, BitmapPlusSmallMetricsMixin, BitmapPlusBigMetricsMixin
+import struct
class table_C_B_D_T_(E_B_D_T_.table_E_B_D_T_):
@@ -51,7 +52,7 @@
dataList.append(sstruct.pack(smallGlyphMetricsFormat, self.metrics))
dataList.append(struct.pack(">L", len(self.imageData)))
dataList.append(self.imageData)
- return string.join(dataList, "")
+ return bytesjoin(dataList)
class cbdt_bitmap_format_18(BitmapPlusBigMetricsMixin, ColorBitmapGlyph):
@@ -70,7 +71,7 @@
dataList.append(sstruct.pack(bigGlyphMetricsFormat, self.metrics))
dataList.append(struct.pack(">L", len(self.imageData)))
dataList.append(self.imageData)
- return string.join(dataList, "")
+ return bytesjoin(dataList)
class cbdt_bitmap_format_19(ColorBitmapGlyph):
diff --git a/Lib/fontTools/ttLib/tables/C_B_L_C_.py b/Lib/fontTools/ttLib/tables/C_B_L_C_.py
index 886f50c..2f78571 100644
--- a/Lib/fontTools/ttLib/tables/C_B_L_C_.py
+++ b/Lib/fontTools/ttLib/tables/C_B_L_C_.py
@@ -2,7 +2,7 @@
#
# Google Author(s): Matt Fontaine
-import E_B_L_C_
+from . import E_B_L_C_
class table_C_B_L_C_(E_B_L_C_.table_E_B_L_C_):
diff --git a/Lib/fontTools/ttLib/tables/C_F_F_.py b/Lib/fontTools/ttLib/tables/C_F_F_.py
index 6a083c3..6c86947 100644
--- a/Lib/fontTools/ttLib/tables/C_F_F_.py
+++ b/Lib/fontTools/ttLib/tables/C_F_F_.py
@@ -1,5 +1,7 @@
-import DefaultTable
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
from fontTools import cffLib
+from . import DefaultTable
class table_C_F_F_(DefaultTable.DefaultTable):
@@ -7,30 +9,28 @@
def __init__(self, tag):
DefaultTable.DefaultTable.__init__(self, tag)
self.cff = cffLib.CFFFontSet()
- self._gaveGlyphOrder = 0
+ self._gaveGlyphOrder = False
def decompile(self, data, otFont):
- from cStringIO import StringIO
self.cff.decompile(StringIO(data), otFont)
assert len(self.cff) == 1, "can't deal with multi-font CFF tables."
def compile(self, otFont):
- from cStringIO import StringIO
f = StringIO()
self.cff.compile(f, otFont)
return f.getvalue()
def haveGlyphNames(self):
if hasattr(self.cff[self.cff.fontNames[0]], "ROS"):
- return 0 # CID-keyed font
+ return False # CID-keyed font
else:
- return 1
+ return True
def getGlyphOrder(self):
if self._gaveGlyphOrder:
from fontTools import ttLib
- raise ttLib.TTLibError, "illegal use of getGlyphOrder()"
- self._gaveGlyphOrder = 1
+ raise ttLib.TTLibError("illegal use of getGlyphOrder()")
+ self._gaveGlyphOrder = True
return self.cff[self.cff.fontNames[0]].getGlyphOrder()
def setGlyphOrder(self, glyphOrder):
@@ -41,8 +41,8 @@
def toXML(self, writer, otFont, progress=None):
self.cff.toXML(writer, progress)
- def fromXML(self, (name, attrs, content), otFont):
+ def fromXML(self, name, attrs, content, otFont):
if not hasattr(self, "cff"):
self.cff = cffLib.CFFFontSet()
- self.cff.fromXML((name, attrs, content))
+ self.cff.fromXML(name, attrs, content)
diff --git a/Lib/fontTools/ttLib/tables/C_O_L_R_.py b/Lib/fontTools/ttLib/tables/C_O_L_R_.py
index b39a4a0..0fdf133 100644
--- a/Lib/fontTools/ttLib/tables/C_O_L_R_.py
+++ b/Lib/fontTools/ttLib/tables/C_O_L_R_.py
@@ -2,12 +2,12 @@
#
# Google Author(s): Behdad Esfahbod
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
+from fontTools.misc.textTools import safeEval
+from . import DefaultTable
import operator
-import DefaultTable
import struct
-from fontTools.ttLib import sfnt
-from fontTools.misc.textTools import safeEval, readHex
-from types import IntType, StringType
class table_C_O_L_R_(DefaultTable.DefaultTable):
@@ -44,17 +44,17 @@
self.ColorLayers = colorLayerLists = {}
try:
- names = map(operator.getitem, [glyphOrder]*numBaseGlyphRecords, gids)
+ names = list(map(operator.getitem, [glyphOrder]*numBaseGlyphRecords, gids))
except IndexError:
getGlyphName = self.getGlyphName
- names = map(getGlyphName, gids )
+ names = list(map(getGlyphName, gids ))
- map(operator.setitem, [colorLayerLists]*numBaseGlyphRecords, names, layerLists)
+ list(map(operator.setitem, [colorLayerLists]*numBaseGlyphRecords, names, layerLists))
def compile(self, ttFont):
ordered = []
- ttFont.getReverseGlyphMap(rebuild=1)
+ ttFont.getReverseGlyphMap(rebuild=True)
glyphNames = self.ColorLayers.keys()
for glyphName in glyphNames:
try:
@@ -74,7 +74,7 @@
dataList = [struct.pack(">HHLLH", self.version, len(glyphMap), 14, 14+6*len(glyphMap), len(layerMap))]
dataList.extend(glyphMap)
dataList.extend(layerMap)
- data = "".join(dataList)
+ data = bytesjoin(dataList)
return data
def toXML(self, writer, ttFont):
@@ -97,49 +97,48 @@
writer.endtag("ColorGlyph")
writer.newline()
- def fromXML(self, (name, attrs, content), ttFont):
+ def fromXML(self, name, attrs, content, ttFont):
if not hasattr(self, "ColorLayers"):
self.ColorLayers = {}
self.getGlyphName = ttFont.getGlyphName # for use in get/set item functions, for access by GID
if name == "ColorGlyph":
glyphName = attrs["name"]
for element in content:
- if isinstance(element, StringType):
+ if isinstance(element, basestring):
continue
layers = []
for element in content:
- if isinstance(element, StringType):
+ if isinstance(element, basestring):
continue
layer = LayerRecord()
- layer.fromXML(element, ttFont)
+ layer.fromXML(element[0], element[1], element[2], ttFont)
layers.append (layer)
operator.setitem(self, glyphName, layers)
- elif attrs.has_key("value"):
- value = safeEval(attrs["value"])
- setattr(self, name, value)
+ elif "value" in attrs:
+ setattr(self, name, safeEval(attrs["value"]))
def __getitem__(self, glyphSelector):
- if type(glyphSelector) == IntType:
+ if isinstance(glyphSelector, int):
# its a gid, convert to glyph name
glyphSelector = self.getGlyphName(glyphSelector)
- if not self.ColorLayers.has_key(glyphSelector):
+ if glyphSelector not in self.ColorLayers:
return None
return self.ColorLayers[glyphSelector]
def __setitem__(self, glyphSelector, value):
- if type(glyphSelector) == IntType:
+ if isinstance(glyphSelector, int):
# its a gid, convert to glyph name
glyphSelector = self.getGlyphName(glyphSelector)
if value:
self.ColorLayers[glyphSelector] = value
- elif self.ColorLayers.has_key(glyphSelector):
+ elif glyphSelector in self.ColorLayers:
del self.ColorLayers[glyphSelector]
-class LayerRecord:
+class LayerRecord(object):
def __init__(self, name = None, colorID = None):
self.name = name
@@ -149,15 +148,11 @@
writer.simpletag("layer", name=self.name, colorID=self.colorID)
writer.newline()
- def fromXML(self, (eltname, attrs, content), ttFont):
+ def fromXML(self, eltname, attrs, content, ttFont):
for (name, value) in attrs.items():
if name == "name":
- if type(value) == IntType:
+ if isinstance(value, int):
value = ttFont.getGlyphName(value)
setattr(self, name, value)
else:
- try:
- value = safeEval(value)
- except OverflowError:
- value = long(value)
- setattr(self, name, value)
+ setattr(self, name, safeEval(value))
diff --git a/Lib/fontTools/ttLib/tables/C_P_A_L_.py b/Lib/fontTools/ttLib/tables/C_P_A_L_.py
index 1e5a878..fc96caa 100644
--- a/Lib/fontTools/ttLib/tables/C_P_A_L_.py
+++ b/Lib/fontTools/ttLib/tables/C_P_A_L_.py
@@ -2,12 +2,11 @@
#
# Google Author(s): Behdad Esfahbod
-import operator
-import DefaultTable
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
+from fontTools.misc.textTools import safeEval
+from . import DefaultTable
import struct
-from fontTools.ttLib import sfnt
-from fontTools.misc.textTools import safeEval, readHex
-from types import IntType, StringType
class table_C_P_A_L_(DefaultTable.DefaultTable):
@@ -36,7 +35,7 @@
assert(len(palette) == self.numPaletteEntries)
for color in palette:
dataList.append(struct.pack(">BBBB", color.blue,color.green,color.red,color.alpha))
- data = "".join(dataList)
+ data = bytesjoin(dataList)
return data
def toXML(self, writer, ttFont):
@@ -53,27 +52,27 @@
writer.endtag("palette")
writer.newline()
- def fromXML(self, (name, attrs, content), ttFont):
+ def fromXML(self, name, attrs, content, ttFont):
if not hasattr(self, "palettes"):
self.palettes = []
if name == "palette":
palette = []
for element in content:
- if isinstance(element, StringType):
+ if isinstance(element, basestring):
continue
palette = []
for element in content:
- if isinstance(element, StringType):
+ if isinstance(element, basestring):
continue
color = Color()
- color.fromXML(element, ttFont)
+ color.fromXML(element[0], element[1], element[2], ttFont)
palette.append (color)
self.palettes.append(palette)
- elif attrs.has_key("value"):
+ elif "value" in attrs:
value = safeEval(attrs["value"])
setattr(self, name, value)
-class Color:
+class Color(object):
def __init__(self, blue=None, green=None, red=None, alpha=None):
self.blue = blue
@@ -91,7 +90,7 @@
writer.simpletag("color", value=self.hex(), index=index)
writer.newline()
- def fromXML(self, (eltname, attrs, content), ttFont):
+ def fromXML(self, eltname, attrs, content, ttFont):
value = attrs["value"]
if value[0] == '#':
value = value[1:]
diff --git a/Lib/fontTools/ttLib/tables/D_S_I_G_.py b/Lib/fontTools/ttLib/tables/D_S_I_G_.py
index 995069d..20f14ee 100644
--- a/Lib/fontTools/ttLib/tables/D_S_I_G_.py
+++ b/Lib/fontTools/ttLib/tables/D_S_I_G_.py
@@ -1,6 +1,9 @@
-import DefaultTable
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
from fontTools.misc.textTools import safeEval
from fontTools.misc import sstruct
+from . import DefaultTable
+import base64
DSIG_HeaderFormat = """
> # big endian
@@ -69,7 +72,10 @@
sigrec.ulOffset = offset
headers.append(sstruct.pack(DSIG_SignatureFormat, sigrec))
offset += sigrec.ulLength
- return ''.join(headers+data)
+ if offset % 2:
+ # Pad to even bytes
+ data.append(b'\0')
+ return bytesjoin(headers+data)
def toXML(self, xmlWriter, ttFont):
xmlWriter.comment("note that the Digital Signature will be invalid after recompilation!")
@@ -80,7 +86,7 @@
sigrec.toXML(xmlWriter, ttFont)
xmlWriter.newline()
- def fromXML(self, (name, attrs, content), ttFont):
+ def fromXML(self, name, attrs, content, ttFont):
if name == "tableHeader":
self.signatureRecords = []
self.ulVersion = safeEval(attrs["version"])
@@ -89,14 +95,24 @@
return
if name == "SignatureRecord":
sigrec = SignatureRecord()
- sigrec.fromXML((name, attrs, content), ttFont)
+ sigrec.fromXML(name, attrs, content, ttFont)
self.signatureRecords.append(sigrec)
pem_spam = lambda l, spam = {
"-----BEGIN PKCS7-----": True, "-----END PKCS7-----": True, "": True
}: not spam.get(l.strip())
-class SignatureRecord:
+def b64encode(b):
+ s = base64.b64encode(b)
+ # Line-break at 76 chars.
+ items = []
+ while s:
+ items.append(tostr(s[:76]))
+ items.append('\n')
+ s = s[76:]
+ return strjoin(items)
+
+class SignatureRecord(object):
def __repr__(self):
return "<%s: %s>" % (self.__class__.__name__, self.__dict__)
@@ -104,12 +120,12 @@
writer.begintag(self.__class__.__name__, format=self.ulFormat)
writer.newline()
writer.write_noindent("-----BEGIN PKCS7-----\n")
- writer.write_noindent(self.pkcs7.encode('base64'))
+ writer.write_noindent(b64encode(self.pkcs7))
writer.write_noindent("-----END PKCS7-----\n")
writer.endtag(self.__class__.__name__)
- def fromXML(self, (name, attrs, content), ttFont):
+ def fromXML(self, name, attrs, content, ttFont):
self.ulFormat = safeEval(attrs["format"])
self.usReserved1 = safeEval(attrs.get("reserved1", "0"))
self.usReserved2 = safeEval(attrs.get("reserved2", "0"))
- self.pkcs7 = "".join(filter(pem_spam, content)).decode('base64')
+ self.pkcs7 = base64.b64decode(tobytes(strjoin(filter(pem_spam, content))))
diff --git a/Lib/fontTools/ttLib/tables/DefaultTable.py b/Lib/fontTools/ttLib/tables/DefaultTable.py
index ff45bc3..f5a6d00 100644
--- a/Lib/fontTools/ttLib/tables/DefaultTable.py
+++ b/Lib/fontTools/ttLib/tables/DefaultTable.py
@@ -1,12 +1,12 @@
-import string
-import sys
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
-class DefaultTable:
+class DefaultTable(object):
dependencies = []
def __init__(self, tag):
- self.tableTag = tag
+ self.tableTag = Tag(tag)
def decompile(self, data, ttFont):
self.data = data
@@ -14,7 +14,7 @@
def compile(self, ttFont):
return self.data
- def toXML(self, writer, ttFont):
+ def toXML(self, writer, ttFont, progress=None):
if hasattr(self, "ERROR"):
writer.comment("An error occurred during the decompilation of this table")
writer.newline()
@@ -26,20 +26,19 @@
writer.endtag("hexdata")
writer.newline()
- def fromXML(self, (name, attrs, content), ttFont):
+ 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
+ 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):
- if type(self) != type(other) or \
- self.__class__ != other.__class__:
- return cmp(id(self), id(other))
-
- return cmp(self.__dict__, other.__dict__)
-
+ def __ne__(self, other):
+ return not self.__eq__(other)
+ def __eq__(self, other):
+ if type(self) != type(other):
+ return NotImplemented
+ return self.__dict__ == other.__dict__
diff --git a/Lib/fontTools/ttLib/tables/E_B_D_T_.py b/Lib/fontTools/ttLib/tables/E_B_D_T_.py
index 93d3009..35ab65c 100644
--- a/Lib/fontTools/ttLib/tables/E_B_D_T_.py
+++ b/Lib/fontTools/ttLib/tables/E_B_D_T_.py
@@ -1,13 +1,12 @@
-
-import DefaultTable
-import os
-import string
-import struct
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
from fontTools.misc import sstruct
-import itertools
-from types import TupleType
from fontTools.misc.textTools import safeEval, readHex, hexStr, deHexStr
-from BitmapGlyphMetrics import BigGlyphMetrics, bigGlyphMetricsFormat, SmallGlyphMetrics, smallGlyphMetricsFormat
+from .BitmapGlyphMetrics import BigGlyphMetrics, bigGlyphMetricsFormat, SmallGlyphMetrics, smallGlyphMetricsFormat
+from . import DefaultTable
+import itertools
+import os
+import struct
ebdtTableVersionFormat = """
> # big endian
@@ -52,7 +51,7 @@
bitmapGlyphDict = {}
self.strikeData.append(bitmapGlyphDict)
for indexSubTable in curStrike.indexSubTables:
- dataIter = itertools.izip(indexSubTable.names, indexSubTable.locations)
+ dataIter = zip(indexSubTable.names, indexSubTable.locations)
for curName, curLoc in dataIter:
# Don't create duplicate data entries for the same glyphs.
# Instead just use the structures that already exist if they exist.
@@ -82,7 +81,7 @@
# recalculation is defered to the EblcIndexSubTable class and just
# pass what is known about bitmap glyphs from this particular table.
locator = ttFont[self.__class__.locatorName]
- for curStrike, curGlyphDict in itertools.izip(locator.strikes, self.strikeData):
+ for curStrike, curGlyphDict in zip(locator.strikes, self.strikeData):
for curIndexSubTable in curStrike.indexSubTables:
dataLocations = []
for curName in curIndexSubTable.names:
@@ -114,7 +113,7 @@
# of any of the problems in the convertion that may arise.
curIndexSubTable.locations = dataLocations
- return string.join(dataList, "")
+ return bytesjoin(dataList)
def toXML(self, writer, ttFont):
# When exporting to XML if one of the data export formats
@@ -122,7 +121,7 @@
# In this case populate the bitmaps with "export metrics".
if ttFont.bitmapGlyphDataFormat in ('row', 'bitwise'):
locator = ttFont[self.__class__.locatorName]
- for curStrike, curGlyphDict in itertools.izip(locator.strikes, self.strikeData):
+ for curStrike, curGlyphDict in zip(locator.strikes, self.strikeData):
for curIndexSubTable in curStrike.indexSubTables:
for curName in curIndexSubTable.names:
glyph = curGlyphDict[curName]
@@ -145,7 +144,7 @@
writer.endtag('strikedata')
writer.newline()
- def fromXML(self, (name, attrs, content), ttFont):
+ def fromXML(self, name, attrs, content, ttFont):
if name == 'header':
self.version = safeEval(attrs['version'])
elif name == 'strikedata':
@@ -155,7 +154,7 @@
bitmapGlyphDict = {}
for element in content:
- if type(element) != TupleType:
+ if not isinstance(element, tuple):
continue
name, attrs, content = element
if name[4:].startswith(_bitmapGlyphSubclassPrefix[4:]):
@@ -163,20 +162,20 @@
glyphName = attrs['name']
imageFormatClass = self.getImageFormatClass(imageFormat)
curGlyph = imageFormatClass(None, None)
- curGlyph.fromXML(element, ttFont)
+ curGlyph.fromXML(name, attrs, content, ttFont)
assert glyphName not in bitmapGlyphDict, "Duplicate glyphs with the same name '%s' in the same strike." % glyphName
bitmapGlyphDict[glyphName] = curGlyph
else:
- print "Warning: %s being ignored by %s", name, self.__class__.__name__
+ print("Warning: %s being ignored by %s", name, self.__class__.__name__)
# Grow the strike data array to the appropriate size. The XML
# format allows the strike index value to be out of order.
if strikeIndex >= len(self.strikeData):
self.strikeData += [None] * (strikeIndex + 1 - len(self.strikeData))
- assert self.strikeData[strikeIndex] == None, "Duplicate strike EBDT indices."
+ assert self.strikeData[strikeIndex] is None, "Duplicate strike EBDT indices."
self.strikeData[strikeIndex] = bitmapGlyphDict
-class EbdtComponent:
+class EbdtComponent(object):
def toXML(self, writer, ttFont):
writer.begintag('ebdtComponent', [('name', self.name)])
@@ -187,45 +186,45 @@
writer.endtag('ebdtComponent')
writer.newline()
- def fromXML(self, (name, attrs, content), ttFont):
+ def fromXML(self, name, attrs, content, ttFont):
self.name = attrs['name']
componentNames = set(sstruct.getformat(ebdtComponentFormat)[1][1:])
for element in content:
- if type(element) != TupleType:
+ if not isinstance(element, tuple):
continue
name, attrs, content = element
if name in componentNames:
vars(self)[name] = safeEval(attrs['value'])
else:
- print "Warning: unknown name '%s' being ignored by EbdtComponent." % name
+ print("Warning: unknown name '%s' being ignored by EbdtComponent." % name)
# Helper functions for dealing with binary.
def _data2binary(data, numBits):
binaryList = []
for curByte in data:
- value = ord(curByte)
+ value = byteord(curByte)
numBitsCut = min(8, numBits)
- for i in xrange(numBitsCut):
+ for i in range(numBitsCut):
if value & 0x1:
binaryList.append('1')
else:
binaryList.append('0')
value = value >> 1
numBits -= numBitsCut
- return string.join(binaryList, "")
+ return strjoin(binaryList)
def _binary2data(binary):
byteList = []
- for bitLoc in xrange(0, len(binary), 8):
+ for bitLoc in range(0, len(binary), 8):
byteString = binary[bitLoc:bitLoc+8]
curByte = 0
for curBit in reversed(byteString):
curByte = curByte << 1
if curBit == '1':
curByte |= 1
- byteList.append(chr(curByte))
- return string.join(byteList, "")
+ byteList.append(bytechr(curByte))
+ return bytesjoin(byteList)
def _memoize(f):
class memodict(dict):
@@ -243,14 +242,14 @@
@_memoize
def _reverseBytes(data):
if len(data) != 1:
- return string.join(map(_reverseBytes, data), "")
- byte = ord(data)
+ return bytesjoin(map(_reverseBytes, data))
+ byte = byteord(data)
result = 0
- for i in xrange(8):
+ for i in range(8):
result = result << 1
result |= byte & 1
byte = byte >> 1
- return chr(result)
+ return bytechr(result)
# This section of code is for reading and writing image data to/from XML.
@@ -261,7 +260,7 @@
writer.endtag('rawimagedata')
writer.newline()
-def _readRawImageData(bitmapObject, (name, attrs, content), ttFont):
+def _readRawImageData(bitmapObject, name, attrs, content, ttFont):
bitmapObject.imageData = readHex(content)
def _writeRowImageData(strikeIndex, glyphName, bitmapObject, writer, ttFont):
@@ -272,14 +271,14 @@
writer.begintag('rowimagedata', bitDepth=bitDepth, width=metrics.width, height=metrics.height)
writer.newline()
- for curRow in xrange(metrics.height):
+ for curRow in range(metrics.height):
rowData = bitmapObject.getRow(curRow, bitDepth=bitDepth, metrics=metrics)
writer.simpletag('row', value=hexStr(rowData))
writer.newline()
writer.endtag('rowimagedata')
writer.newline()
-def _readRowImageData(bitmapObject, (name, attrs, content), ttFont):
+def _readRowImageData(bitmapObject, name, attrs, content, ttFont):
bitDepth = safeEval(attrs['bitDepth'])
metrics = SmallGlyphMetrics()
metrics.width = safeEval(attrs['width'])
@@ -287,7 +286,7 @@
dataRows = []
for element in content:
- if type(element) != TupleType:
+ if not isinstance(element, tuple):
continue
name, attr, content = element
# Chop off 'imagedata' from the tag to get just the option.
@@ -306,17 +305,17 @@
writer.begintag('bitwiseimagedata', bitDepth=bitDepth, width=metrics.width, height=metrics.height)
writer.newline()
- for curRow in xrange(metrics.height):
+ for curRow in range(metrics.height):
rowData = bitmapObject.getRow(curRow, bitDepth=1, metrics=metrics, reverseBytes=True)
rowData = _data2binary(rowData, metrics.width)
# Make the output a readable ASCII art form.
- rowData = string.join(map(binaryConv.get, rowData), "")
+ rowData = strjoin(map(binaryConv.get, rowData))
writer.simpletag('row', value=rowData)
writer.newline()
writer.endtag('bitwiseimagedata')
writer.newline()
-def _readBitwiseImageData(bitmapObject, (name, attrs, content), ttFont):
+def _readBitwiseImageData(bitmapObject, name, attrs, content, ttFont):
bitDepth = safeEval(attrs['bitDepth'])
metrics = SmallGlyphMetrics()
metrics.width = safeEval(attrs['width'])
@@ -328,12 +327,12 @@
dataRows = []
for element in content:
- if type(element) != TupleType:
+ if not isinstance(element, tuple):
continue
name, attr, content = element
if name == 'row':
- mapParams = itertools.izip(attr['value'], itertools.repeat('1'))
- rowData = string.join(itertools.starmap(binaryConv.get, mapParams), "")
+ mapParams = zip(attr['value'], itertools.repeat('1'))
+ rowData = strjoin(itertools.starmap(binaryConv.get, mapParams))
dataRows.append(_binary2data(rowData))
bitmapObject.setRows(dataRows, bitDepth=bitDepth, metrics=metrics, reverseBytes=True)
@@ -354,7 +353,7 @@
with open(fullPath, "wb") as file:
file.write(bitmapObject.imageData)
-def _readExtFileImageData(bitmapObject, (name, attrs, content), ttFont):
+def _readExtFileImageData(bitmapObject, name, attrs, content, ttFont):
fullPath = attrs['value']
with open(fullPath, "rb") as file:
bitmapObject.imageData = file.read()
@@ -365,7 +364,7 @@
# in XML.
_bitmapGlyphSubclassPrefix = 'ebdt_bitmap_format_'
-class BitmapGlyph:
+class BitmapGlyph(object):
# For the external file format. This can be changed in subclasses. This way
# when the extfile option is turned on files have the form: glyphName.ext
@@ -383,15 +382,19 @@
def __init__(self, data, ttFont):
self.data = data
self.ttFont = ttFont
+ # TODO Currently non-lazy decompilation is untested here...
+ #if not ttFont.lazy:
+ # self.decompile()
+ # del self.data
def __getattr__(self, attr):
# Allow lazy decompile.
if attr[:2] == '__':
- raise AttributeError, attr
- if self.data == None:
- raise AttributeError, attr
+ raise AttributeError(attr)
+ if not hasattr(self, "data"):
+ raise AttributeError(attr)
self.decompile()
- self.data = None
+ del self.data
return getattr(self, attr)
# Not a fan of this but it is needed for safer safety checking.
@@ -409,16 +412,18 @@
writer.endtag(self.__class__.__name__)
writer.newline()
- def fromXML(self, (name, attrs, content), ttFont):
- self.readMetrics((name, attrs, content), ttFont)
+ def fromXML(self, name, attrs, content, ttFont):
+ self.readMetrics(name, attrs, content, ttFont)
for element in content:
- if type(element) != TupleType:
+ if not isinstance(element, tuple):
continue
name, attr, content = element
+ if not name.endswith('imagedata'):
+ continue
# Chop off 'imagedata' from the tag to get just the option.
option = name[:-len('imagedata')]
- if option in self.__class__.xmlDataFunctions:
- self.readData(element, ttFont)
+ assert option in self.__class__.xmlDataFunctions
+ self.readData(name, attrs, content, ttFont)
# Some of the glyphs have the metrics. This allows for metrics to be
# added if the glyph format has them. Default behavior is to do nothing.
@@ -426,7 +431,7 @@
pass
# The opposite of write metrics.
- def readMetrics(self, (name, attrs, content), ttFont):
+ def readMetrics(self, name, attrs, content, ttFont):
pass
def writeData(self, strikeIndex, glyphName, writer, ttFont):
@@ -436,11 +441,11 @@
writeFunc = _writeRawImageData
writeFunc(strikeIndex, glyphName, self, writer, ttFont)
- def readData(self, (name, attrs, content), ttFont):
+ def readData(self, name, attrs, content, ttFont):
# Chop off 'imagedata' from the tag to get just the option.
option = name[:-len('imagedata')]
writeFunc, readFunc = self.__class__.xmlDataFunctions[option]
- readFunc(self, (name, attrs, content), ttFont)
+ readFunc(self, name, attrs, content, ttFont)
# A closure for creating a mixin for the two types of metrics handling.
@@ -454,21 +459,21 @@
metricsId = metricStrings.index(curMetricsName)
oppositeMetricsName = metricStrings[1-metricsId]
- class BitmapPlusMetricsMixin:
+ class BitmapPlusMetricsMixin(object):
def writeMetrics(self, writer, ttFont):
self.metrics.toXML(writer, ttFont)
- def readMetrics(self, (name, attrs, content), ttFont):
+ def readMetrics(self, name, attrs, content, ttFont):
for element in content:
- if type(element) != TupleType:
+ if not isinstance(element, tuple):
continue
name, attrs, content = element
if name == curMetricsName:
self.metrics = metricsClass()
- self.metrics.fromXML(element, ttFont)
+ self.metrics.fromXML(name, attrs, content, ttFont)
elif name == oppositeMetricsName:
- print "Warning: %s being ignored in format %d." % oppositeMetricsName, self.getFormat()
+ print("Warning: %s being ignored in format %d." % oppositeMetricsName, self.getFormat())
return BitmapPlusMetricsMixin
@@ -479,7 +484,7 @@
# Data that is bit aligned can be tricky to deal with. These classes implement
# helper functionality for dealing with the data and getting a particular row
# of bitwise data. Also helps implement fancy data export/import in XML.
-class BitAlignedBitmapMixin:
+class BitAlignedBitmapMixin(object):
def _getBitRange(self, row, bitDepth, metrics):
rowBits = (bitDepth * metrics.width)
@@ -487,7 +492,7 @@
return (bitOffset, bitOffset+rowBits)
def getRow(self, row, bitDepth=1, metrics=None, reverseBytes=False):
- if metrics == None:
+ if metrics is None:
metrics = self.metrics
assert 0 <= row and row < metrics.height, "Illegal row access in bitmap"
@@ -509,55 +514,55 @@
dataList = []
bitRange = self._getBitRange(row, bitDepth, metrics)
stepRange = bitRange + (8,)
- for curBit in xrange(*stepRange):
+ for curBit in range(*stepRange):
endBit = min(curBit+8, bitRange[1])
numBits = endBit - curBit
cutPoint = curBit % 8
- firstByteLoc = curBit / 8
- secondByteLoc = endBit / 8
+ firstByteLoc = curBit // 8
+ secondByteLoc = endBit // 8
if firstByteLoc < secondByteLoc:
numBitsCut = 8 - cutPoint
else:
numBitsCut = endBit - curBit
curByte = _reverseBytes(self.imageData[firstByteLoc])
- firstHalf = ord(curByte) >> cutPoint
+ firstHalf = byteord(curByte) >> cutPoint
firstHalf = ((1<<numBitsCut)-1) & firstHalf
newByte = firstHalf
if firstByteLoc < secondByteLoc and secondByteLoc < len(self.imageData):
curByte = _reverseBytes(self.imageData[secondByteLoc])
- secondHalf = ord(curByte) << numBitsCut
+ secondHalf = byteord(curByte) << numBitsCut
newByte = (firstHalf | secondHalf) & ((1<<numBits)-1)
- dataList.append(chr(newByte))
+ dataList.append(bytechr(newByte))
# The way the data is kept is opposite the algorithm used.
- data = string.join(dataList, "")
+ data = bytesjoin(dataList)
if not reverseBytes:
data = _reverseBytes(data)
return data
def setRows(self, dataRows, bitDepth=1, metrics=None, reverseBytes=False):
- if metrics == None:
+ if metrics is None:
metrics = self.metrics
if not reverseBytes:
- dataRows = map(_reverseBytes, dataRows)
+ dataRows = list(map(_reverseBytes, dataRows))
# Keep track of a list of ordinal values as they are easier to modify
# than a list of strings. Map to actual strings later.
- numBytes = (self._getBitRange(len(dataRows), bitDepth, metrics)[0] + 7) / 8
+ numBytes = (self._getBitRange(len(dataRows), bitDepth, metrics)[0] + 7) // 8
ordDataList = [0] * numBytes
for row, data in enumerate(dataRows):
bitRange = self._getBitRange(row, bitDepth, metrics)
stepRange = bitRange + (8,)
- for curBit, curByte in itertools.izip(xrange(*stepRange), data):
+ for curBit, curByte in zip(range(*stepRange), data):
endBit = min(curBit+8, bitRange[1])
cutPoint = curBit % 8
- firstByteLoc = curBit / 8
- secondByteLoc = endBit / 8
+ firstByteLoc = curBit // 8
+ secondByteLoc = endBit // 8
if firstByteLoc < secondByteLoc:
numBitsCut = 8 - cutPoint
else:
numBitsCut = endBit - curBit
- curByte = ord(curByte)
+ curByte = byteord(curByte)
firstByte = curByte & ((1<<numBitsCut)-1)
ordDataList[firstByteLoc] |= (firstByte << cutPoint)
if firstByteLoc < secondByteLoc and secondByteLoc < numBytes:
@@ -565,17 +570,17 @@
ordDataList[secondByteLoc] |= secondByte
# Save the image data with the bits going the correct way.
- self.imageData = _reverseBytes(string.join(map(chr, ordDataList), ""))
+ self.imageData = _reverseBytes(bytesjoin(map(bytechr, ordDataList)))
-class ByteAlignedBitmapMixin:
+class ByteAlignedBitmapMixin(object):
def _getByteRange(self, row, bitDepth, metrics):
- rowBytes = (bitDepth * metrics.width + 7) / 8
+ rowBytes = (bitDepth * metrics.width + 7) // 8
byteOffset = row * rowBytes
return (byteOffset, byteOffset+rowBytes)
def getRow(self, row, bitDepth=1, metrics=None, reverseBytes=False):
- if metrics == None:
+ if metrics is None:
metrics = self.metrics
assert 0 <= row and row < metrics.height, "Illegal row access in bitmap"
byteRange = self._getByteRange(row, bitDepth, metrics)
@@ -585,11 +590,11 @@
return data
def setRows(self, dataRows, bitDepth=1, metrics=None, reverseBytes=False):
- if metrics == None:
+ if metrics is None:
metrics = self.metrics
if reverseBytes:
dataRows = map(_reverseBytes, dataRows)
- self.imageData = string.join(dataRows, "")
+ self.imageData = bytesjoin(dataRows)
class ebdt_bitmap_format_1(ByteAlignedBitmapMixin, BitmapPlusSmallMetricsMixin, BitmapGlyph):
@@ -665,24 +670,24 @@
writer.endtag(self.__class__.__name__)
writer.newline()
- def fromXML(self, (name, attrs, content), ttFont):
- self.readMetrics((name, attrs, content), ttFont)
+ def fromXML(self, name, attrs, content, ttFont):
+ self.readMetrics(name, attrs, content, ttFont)
for element in content:
- if type(element) != TupleType:
+ if not isinstance(element, tuple):
continue
name, attr, content = element
if name == 'components':
self.componentArray = []
for compElement in content:
- if type(compElement) != TupleType:
+ if not isinstance(compElement, tuple):
continue
- name, attr, content = compElement
+ name, attrs, content = compElement
if name == 'ebdtComponent':
curComponent = EbdtComponent()
- curComponent.fromXML(compElement, ttFont)
+ curComponent.fromXML(name, attrs, content, ttFont)
self.componentArray.append(curComponent)
else:
- print "Warning: '%s' being ignored in component array." % name
+ print("Warning: '%s' being ignored in component array." % name)
class ebdt_bitmap_format_8(BitmapPlusSmallMetricsMixin, ComponentBitmapGlyph):
@@ -695,7 +700,7 @@
(numComponents,) = struct.unpack(">H", data[:2])
data = data[2:]
self.componentArray = []
- for i in xrange(numComponents):
+ for i in range(numComponents):
curComponent = EbdtComponent()
dummy, data = sstruct.unpack2(ebdtComponentFormat, data, curComponent)
curComponent.name = self.ttFont.getGlyphName(curComponent.glyphCode)
@@ -704,12 +709,12 @@
def compile(self, ttFont):
dataList = []
dataList.append(sstruct.pack(smallGlyphMetricsFormat, self.metrics))
- dataList.append('\0')
+ dataList.append(b'\0')
dataList.append(struct.pack(">H", len(self.componentArray)))
for curComponent in self.componentArray:
curComponent.glyphCode = ttFont.getGlyphID(curComponent.name)
dataList.append(sstruct.pack(ebdtComponentFormat, curComponent))
- return string.join(dataList, "")
+ return bytesjoin(dataList)
class ebdt_bitmap_format_9(BitmapPlusBigMetricsMixin, ComponentBitmapGlyph):
@@ -720,7 +725,7 @@
(numComponents,) = struct.unpack(">H", data[:2])
data = data[2:]
self.componentArray = []
- for i in xrange(numComponents):
+ for i in range(numComponents):
curComponent = EbdtComponent()
dummy, data = sstruct.unpack2(ebdtComponentFormat, data, curComponent)
curComponent.name = self.ttFont.getGlyphName(curComponent.glyphCode)
@@ -733,7 +738,7 @@
for curComponent in self.componentArray:
curComponent.glyphCode = ttFont.getGlyphID(curComponent.name)
dataList.append(sstruct.pack(ebdtComponentFormat, curComponent))
- return string.join(dataList, "")
+ return bytesjoin(dataList)
# Dictionary of bitmap formats to the class representing that format
diff --git a/Lib/fontTools/ttLib/tables/E_B_L_C_.py b/Lib/fontTools/ttLib/tables/E_B_L_C_.py
index 500e833..f885318 100644
--- a/Lib/fontTools/ttLib/tables/E_B_L_C_.py
+++ b/Lib/fontTools/ttLib/tables/E_B_L_C_.py
@@ -1,13 +1,12 @@
-
-import DefaultTable
-import string
-import struct
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
from fontTools.misc import sstruct
-import itertools
-from types import TupleType
-from collections import deque
+from . import DefaultTable
from fontTools.misc.textTools import safeEval
-from BitmapGlyphMetrics import BigGlyphMetrics, bigGlyphMetricsFormat, SmallGlyphMetrics, smallGlyphMetricsFormat
+from .BitmapGlyphMetrics import BigGlyphMetrics, bigGlyphMetricsFormat, SmallGlyphMetrics, smallGlyphMetricsFormat
+import struct
+import itertools
+from collections import deque
eblcHeaderFormat = """
> # big endian
@@ -76,7 +75,7 @@
dummy, data = sstruct.unpack2(eblcHeaderFormat, data, self)
self.strikes = []
- for curStrikeIndex in xrange(self.numSizes):
+ for curStrikeIndex in range(self.numSizes):
curStrike = Strike()
self.strikes.append(curStrike)
curTable = curStrike.bitmapSizeTable
@@ -89,7 +88,7 @@
for curStrike in self.strikes:
curTable = curStrike.bitmapSizeTable
- for subtableIndex in xrange(curTable.numberOfIndexSubTables):
+ for subtableIndex in range(curTable.numberOfIndexSubTables):
lowerBound = curTable.indexSubTableArrayOffset + subtableIndex * indexSubTableArraySize
upperBound = lowerBound + indexSubTableArraySize
data = origData[lowerBound:upperBound]
@@ -171,7 +170,7 @@
indexSubTableDataList = []
for indexSubTable in curStrike.indexSubTables:
indexSubTable.additionalOffsetToIndexSubtable = dataSize - curTable.indexSubTableArrayOffset
- glyphIds = map(ttFont.getGlyphID, indexSubTable.names)
+ glyphIds = list(map(ttFont.getGlyphID, indexSubTable.names))
indexSubTable.firstGlyphIndex = min(glyphIds)
indexSubTable.lastGlyphIndex = max(glyphIds)
data = indexSubTable.compile(ttFont)
@@ -198,7 +197,7 @@
dataList.append(data)
dataList.extend(indexSubTablePairDataList)
- return string.join(dataList, "")
+ return bytesjoin(dataList)
def toXML(self, writer, ttFont):
writer.simpletag('header', [('version', self.version)])
@@ -206,7 +205,7 @@
for curIndex, curStrike in enumerate(self.strikes):
curStrike.toXML(curIndex, writer, ttFont)
- def fromXML(self, (name, attrs, content), ttFont):
+ def fromXML(self, name, attrs, content, ttFont):
if name == 'header':
self.version = safeEval(attrs['version'])
elif name == 'strike':
@@ -214,16 +213,16 @@
self.strikes = []
strikeIndex = safeEval(attrs['index'])
curStrike = Strike()
- curStrike.fromXML((name, attrs, content), ttFont, self)
+ curStrike.fromXML(name, attrs, content, ttFont, self)
# Grow the strike array to the appropriate size. The XML format
# allows for the strike index value to be out of order.
if strikeIndex >= len(self.strikes):
self.strikes += [None] * (strikeIndex + 1 - len(self.strikes))
- assert self.strikes[strikeIndex] == None, "Duplicate strike EBLC indices."
+ assert self.strikes[strikeIndex] is None, "Duplicate strike EBLC indices."
self.strikes[strikeIndex] = curStrike
-class Strike:
+class Strike(object):
def __init__(self):
self.bitmapSizeTable = BitmapSizeTable()
@@ -240,23 +239,23 @@
writer.endtag('strike')
writer.newline()
- def fromXML(self, (name, attrs, content), ttFont, locator):
+ def fromXML(self, name, attrs, content, ttFont, locator):
for element in content:
- if type(element) != TupleType:
+ if not isinstance(element, tuple):
continue
name, attrs, content = element
if name == 'bitmapSizeTable':
- self.bitmapSizeTable.fromXML(element, ttFont)
+ self.bitmapSizeTable.fromXML(name, attrs, content, ttFont)
elif name.startswith(_indexSubTableSubclassPrefix):
indexFormat = safeEval(name[len(_indexSubTableSubclassPrefix):])
indexFormatClass = locator.getIndexFormatClass(indexFormat)
indexSubTable = indexFormatClass(None, None)
indexSubTable.indexFormat = indexFormat
- indexSubTable.fromXML(element, ttFont)
+ indexSubTable.fromXML(name, attrs, content, ttFont)
self.indexSubTables.append(indexSubTable)
-class BitmapSizeTable:
+class BitmapSizeTable(object):
# Returns all the simple metric names that bitmap size table
# cares about in terms of XML creation.
@@ -277,27 +276,27 @@
writer.endtag('bitmapSizeTable')
writer.newline()
- def fromXML(self, (name, attrs, content), ttFont):
+ def fromXML(self, name, attrs, content, ttFont):
# Create a lookup for all the simple names that make sense to
# bitmap size table. Only read the information from these names.
dataNames = set(self._getXMLMetricNames())
for element in content:
- if type(element) != TupleType:
+ if not isinstance(element, tuple):
continue
name, attrs, content = element
if name == 'sbitLineMetrics':
direction = attrs['direction']
assert direction in ('hori', 'vert'), "SbitLineMetrics direction specified invalid."
metricObj = SbitLineMetrics()
- metricObj.fromXML(element, ttFont)
+ metricObj.fromXML(name, attrs, content, ttFont)
vars(self)[direction] = metricObj
elif name in dataNames:
vars(self)[name] = safeEval(attrs['value'])
else:
- print "Warning: unknown name '%s' being ignored in BitmapSizeTable." % name
+ print("Warning: unknown name '%s' being ignored in BitmapSizeTable." % name)
-class SbitLineMetrics:
+class SbitLineMetrics(object):
def toXML(self, name, writer, ttFont):
writer.begintag('sbitLineMetrics', [('direction', name)])
@@ -308,10 +307,10 @@
writer.endtag('sbitLineMetrics')
writer.newline()
- def fromXML(self, (name, attrs, content), ttFont):
+ def fromXML(self, name, attrs, content, ttFont):
metricNames = set(sstruct.getformat(sbitLineMetricsFormat)[1])
for element in content:
- if type(element) != TupleType:
+ if not isinstance(element, tuple):
continue
name, attrs, content = element
if name in metricNames:
@@ -320,21 +319,24 @@
# Important information about the naming scheme. Used for identifying subtables.
_indexSubTableSubclassPrefix = 'eblc_index_sub_table_'
-class EblcIndexSubTable:
+class EblcIndexSubTable(object):
def __init__(self, data, ttFont):
self.data = data
self.ttFont = ttFont
+ # TODO Currently non-lazy decompiling doesn't work for this class...
+ #if not ttFont.lazy:
+ # self.decompile()
+ # del self.data, self.ttFont
def __getattr__(self, attr):
# Allow lazy decompile.
if attr[:2] == '__':
- raise AttributeError, attr
- if self.data == None:
- raise AttributeError, attr
+ raise AttributeError(attr)
+ if not hasattr(self, "data"):
+ raise AttributeError(attr)
self.decompile()
- self.data = None
- self.ttFont = None
+ del self.data, self.ttFont
return getattr(self, attr)
# This method just takes care of the indexSubHeader. Implementing subclasses
@@ -358,13 +360,13 @@
# For font debugging of consecutive formats the ids are also written.
# The ids are not read when moving from the XML format.
glyphIds = map(ttFont.getGlyphID, self.names)
- for glyphName, glyphId in itertools.izip(self.names, glyphIds):
+ for glyphName, glyphId in zip(self.names, glyphIds):
writer.simpletag('glyphLoc', name=glyphName, id=glyphId)
writer.newline()
writer.endtag(self.__class__.__name__)
writer.newline()
- def fromXML(self, (name, attrs, content), ttFont):
+ def fromXML(self, name, attrs, content, ttFont):
# Read all the attributes. Even though the glyph indices are
# recalculated, they are still read in case there needs to
# be an immediate export of the data.
@@ -372,11 +374,11 @@
self.firstGlyphIndex = safeEval(attrs['firstGlyphIndex'])
self.lastGlyphIndex = safeEval(attrs['lastGlyphIndex'])
- self.readMetrics((name, attrs, content), ttFont)
+ self.readMetrics(name, attrs, content, ttFont)
self.names = []
for element in content:
- if type(element) != TupleType:
+ if not isinstance(element, tuple):
continue
name, attrs, content = element
if name == 'glyphLoc':
@@ -389,7 +391,7 @@
pass
# A helper method that is the inverse of writeMetrics.
- def readMetrics(self, (name, attrs, content), ttFont):
+ def readMetrics(self, name, attrs, content, ttFont):
pass
# This method is for fixed glyph data sizes. There are formats where
@@ -406,11 +408,12 @@
def removeSkipGlyphs(self):
# Determines if a name, location pair is a valid data location.
# Skip glyphs are marked when the size is equal to zero.
- def isValidLocation((name, (startByte, endByte))):
+ def isValidLocation(args):
+ (name, (startByte, endByte)) = args
return startByte < endByte
# Remove all skip glyphs.
- dataPairs = filter(isValidLocation, zip(self.names, self.locations))
- self.names, self.locations = map(list, zip(*dataPairs))
+ dataPairs = list(filter(isValidLocation, zip(self.names, self.locations)))
+ self.names, self.locations = list(map(list, zip(*dataPairs)))
# A closure for creating a custom mixin. This is done because formats 1 and 3
# are very similar. The only difference between them is the size per offset
@@ -421,37 +424,37 @@
dataFormat = '>'+formatStringForDataType
offsetDataSize = struct.calcsize(dataFormat)
- class OffsetArrayIndexSubTableMixin:
+ class OffsetArrayIndexSubTableMixin(object):
def decompile(self):
numGlyphs = self.lastGlyphIndex - self.firstGlyphIndex + 1
- indexingOffsets = [glyphIndex * offsetDataSize for glyphIndex in xrange(numGlyphs+2)]
+ indexingOffsets = [glyphIndex * offsetDataSize for glyphIndex in range(numGlyphs+2)]
indexingLocations = zip(indexingOffsets, indexingOffsets[1:])
offsetArray = [struct.unpack(dataFormat, self.data[slice(*loc)])[0] for loc in indexingLocations]
- glyphIds = range(self.firstGlyphIndex, self.lastGlyphIndex+1)
+ glyphIds = list(range(self.firstGlyphIndex, self.lastGlyphIndex+1))
modifiedOffsets = [offset + self.imageDataOffset for offset in offsetArray]
- self.locations = zip(modifiedOffsets, modifiedOffsets[1:])
+ self.locations = list(zip(modifiedOffsets, modifiedOffsets[1:]))
- self.names = map(self.ttFont.getGlyphName, glyphIds)
+ self.names = list(map(self.ttFont.getGlyphName, glyphIds))
self.removeSkipGlyphs()
def compile(self, ttFont):
# First make sure that all the data lines up properly. Formats 1 and 3
# must have all its data lined up consecutively. If not this will fail.
- for curLoc, nxtLoc in itertools.izip(self.locations, self.locations[1:]):
+ for curLoc, nxtLoc in zip(self.locations, self.locations[1:]):
assert curLoc[1] == nxtLoc[0], "Data must be consecutive in indexSubTable offset formats"
- glyphIds = map(ttFont.getGlyphID, self.names)
+ glyphIds = list(map(ttFont.getGlyphID, self.names))
# Make sure that all ids are sorted strictly increasing.
- assert all(glyphIds[i] < glyphIds[i+1] for i in xrange(len(glyphIds)-1))
+ assert all(glyphIds[i] < glyphIds[i+1] for i in range(len(glyphIds)-1))
# Run a simple algorithm to add skip glyphs to the data locations at
# the places where an id is not present.
idQueue = deque(glyphIds)
locQueue = deque(self.locations)
- allGlyphIds = range(self.firstGlyphIndex, self.lastGlyphIndex+1)
+ allGlyphIds = list(range(self.firstGlyphIndex, self.lastGlyphIndex+1))
allLocations = []
for curId in allGlyphIds:
if curId != idQueue[0]:
@@ -475,38 +478,38 @@
# Take care of any padding issues. Only occurs in format 3.
if offsetDataSize * len(dataList) % 4 != 0:
dataList.append(struct.pack(dataFormat, 0))
- return string.join(dataList, "")
+ return bytesjoin(dataList)
return OffsetArrayIndexSubTableMixin
# A Mixin for functionality shared between the different kinds
# of fixed sized data handling. Both kinds have big metrics so
# that kind of special processing is also handled in this mixin.
-class FixedSizeIndexSubTableMixin:
+class FixedSizeIndexSubTableMixin(object):
def writeMetrics(self, writer, ttFont):
writer.simpletag('imageSize', value=self.imageSize)
writer.newline()
self.metrics.toXML(writer, ttFont)
- def readMetrics(self, (name, attrs, content), ttFont):
+ def readMetrics(self, name, attrs, content, ttFont):
for element in content:
- if type(element) != TupleType:
+ if not isinstance(element, tuple):
continue
name, attrs, content = element
if name == 'imageSize':
self.imageSize = safeEval(attrs['value'])
elif name == BigGlyphMetrics.__name__:
self.metrics = BigGlyphMetrics()
- self.metrics.fromXML(element, ttFont)
+ self.metrics.fromXML(name, attrs, content, ttFont)
elif name == SmallGlyphMetrics.__name__:
- print "Warning: SmallGlyphMetrics being ignored in format %d." % self.indexFormat
+ print("Warning: SmallGlyphMetrics being ignored in format %d." % self.indexFormat)
def padBitmapData(self, data):
# Make sure that the data isn't bigger than the fixed size.
assert len(data) <= self.imageSize, "Data in indexSubTable format %d must be less than the fixed size." % self.indexFormat
# Pad the data so that it matches the fixed size.
- pad = (self.imageSize - len(data)) * '\0'
+ pad = (self.imageSize - len(data)) * b'\0'
return data + pad
class eblc_index_sub_table_1(_createOffsetArrayIndexSubTableMixin('L'), EblcIndexSubTable):
@@ -518,21 +521,21 @@
(self.imageSize,) = struct.unpack(">L", self.data[:4])
self.metrics = BigGlyphMetrics()
sstruct.unpack2(bigGlyphMetricsFormat, self.data[4:], self.metrics)
- glyphIds = range(self.firstGlyphIndex, self.lastGlyphIndex+1)
- offsets = [self.imageSize * i + self.imageDataOffset for i in xrange(len(glyphIds)+1)]
- self.locations = zip(offsets, offsets[1:])
- self.names = map(self.ttFont.getGlyphName, glyphIds)
+ glyphIds = list(range(self.firstGlyphIndex, self.lastGlyphIndex+1))
+ offsets = [self.imageSize * i + self.imageDataOffset for i in range(len(glyphIds)+1)]
+ self.locations = list(zip(offsets, offsets[1:]))
+ self.names = list(map(self.ttFont.getGlyphName, glyphIds))
def compile(self, ttFont):
- glyphIds = map(ttFont.getGlyphID, self.names)
+ glyphIds = list(map(ttFont.getGlyphID, self.names))
# Make sure all the ids are consecutive. This is required by Format 2.
- assert glyphIds == range(self.firstGlyphIndex, self.lastGlyphIndex+1), "Format 2 ids must be consecutive."
+ assert glyphIds == list(range(self.firstGlyphIndex, self.lastGlyphIndex+1)), "Format 2 ids must be consecutive."
self.imageDataOffset = min(zip(*self.locations)[0])
dataList = [EblcIndexSubTable.compile(self, ttFont)]
dataList.append(struct.pack(">L", self.imageSize))
dataList.append(sstruct.pack(bigGlyphMetricsFormat, self.metrics))
- return string.join(dataList, "")
+ return bytesjoin(dataList)
class eblc_index_sub_table_3(_createOffsetArrayIndexSubTableMixin('H'), EblcIndexSubTable):
pass
@@ -543,21 +546,21 @@
(numGlyphs,) = struct.unpack(">L", self.data[:4])
data = self.data[4:]
- indexingOffsets = [glyphIndex * codeOffsetPairSize for glyphIndex in xrange(numGlyphs+2)]
+ indexingOffsets = [glyphIndex * codeOffsetPairSize for glyphIndex in range(numGlyphs+2)]
indexingLocations = zip(indexingOffsets, indexingOffsets[1:])
glyphArray = [struct.unpack(codeOffsetPairFormat, data[slice(*loc)]) for loc in indexingLocations]
- glyphIds, offsets = map(list, zip(*glyphArray))
+ glyphIds, offsets = list(map(list, zip(*glyphArray)))
# There are one too many glyph ids. Get rid of the last one.
glyphIds.pop()
offsets = [offset + self.imageDataOffset for offset in offsets]
- self.locations = zip(offsets, offsets[1:])
- self.names = map(self.ttFont.getGlyphName, glyphIds)
+ self.locations = list(zip(offsets, offsets[1:]))
+ self.names = list(map(self.ttFont.getGlyphName, glyphIds))
def compile(self, ttFont):
# First make sure that all the data lines up properly. Format 4
# must have all its data lined up consecutively. If not this will fail.
- for curLoc, nxtLoc in itertools.izip(self.locations, self.locations[1:]):
+ for curLoc, nxtLoc in zip(self.locations, self.locations[1:]):
assert curLoc[1] == nxtLoc[0], "Data must be consecutive in indexSubTable format 4"
offsets = list(self.locations[0]) + [loc[1] for loc in self.locations[1:]]
@@ -566,15 +569,15 @@
# and allows imageDataOffset to not be required to be in the XML version.
self.imageDataOffset = min(offsets)
offsets = [offset - self.imageDataOffset for offset in offsets]
- glyphIds = map(ttFont.getGlyphID, self.names)
+ glyphIds = list(map(ttFont.getGlyphID, self.names))
# Create an iterator over the ids plus a padding value.
idsPlusPad = list(itertools.chain(glyphIds, [0]))
dataList = [EblcIndexSubTable.compile(self, ttFont)]
dataList.append(struct.pack(">L", len(glyphIds)))
- tmp = [struct.pack(codeOffsetPairFormat, *cop) for cop in itertools.izip(idsPlusPad, offsets)]
+ tmp = [struct.pack(codeOffsetPairFormat, *cop) for cop in zip(idsPlusPad, offsets)]
dataList += tmp
- data = string.join(dataList, "")
+ data = bytesjoin(dataList)
return data
class eblc_index_sub_table_5(FixedSizeIndexSubTableMixin, EblcIndexSubTable):
@@ -586,23 +589,23 @@
self.metrics, data = sstruct.unpack2(bigGlyphMetricsFormat, data, BigGlyphMetrics())
(numGlyphs,) = struct.unpack(">L", data[:4])
data = data[4:]
- glyphIds = [struct.unpack(">H", data[2*i:2*(i+1)])[0] for i in xrange(numGlyphs)]
+ glyphIds = [struct.unpack(">H", data[2*i:2*(i+1)])[0] for i in range(numGlyphs)]
- offsets = [self.imageSize * i + self.imageDataOffset for i in xrange(len(glyphIds)+1)]
- self.locations = zip(offsets, offsets[1:])
- self.names = map(self.ttFont.getGlyphName, glyphIds)
+ offsets = [self.imageSize * i + self.imageDataOffset for i in range(len(glyphIds)+1)]
+ self.locations = list(zip(offsets, offsets[1:]))
+ self.names = list(map(self.ttFont.getGlyphName, glyphIds))
def compile(self, ttFont):
self.imageDataOffset = min(zip(*self.locations)[0])
dataList = [EblcIndexSubTable.compile(self, ttFont)]
dataList.append(struct.pack(">L", self.imageSize))
dataList.append(sstruct.pack(bigGlyphMetricsFormat, self.metrics))
- glyphIds = map(ttFont.getGlyphID, self.names)
+ glyphIds = list(map(ttFont.getGlyphID, self.names))
dataList.append(struct.pack(">L", len(glyphIds)))
dataList += [struct.pack(">H", curId) for curId in glyphIds]
if len(glyphIds) % 2 == 1:
dataList.append(struct.pack(">H", 0))
- return string.join(dataList, "")
+ return bytesjoin(dataList)
# Dictionary of indexFormat to the class representing that format.
eblc_sub_table_classes = {
diff --git a/Lib/fontTools/ttLib/tables/F_F_T_M_.py b/Lib/fontTools/ttLib/tables/F_F_T_M_.py
new file mode 100644
index 0000000..e8b1d29
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/F_F_T_M_.py
@@ -0,0 +1,46 @@
+from fontTools.misc.py23 import *
+from fontTools.misc import sstruct
+from fontTools.misc.textTools import safeEval
+from ._h_e_a_d import mac_epoch_diff
+from . import DefaultTable
+import time
+import calendar
+
+FFTMFormat = """
+ > # big endian
+ version: I
+ FFTimeStamp: Q
+ sourceCreated: Q
+ sourceModified: Q
+"""
+
+class table_F_F_T_M_(DefaultTable.DefaultTable):
+
+ def decompile(self, data, ttFont):
+ dummy, rest = sstruct.unpack2(FFTMFormat, data, self)
+
+ def compile(self, ttFont):
+ data = sstruct.pack(FFTMFormat, self)
+ return data
+
+ def toXML(self, writer, ttFont):
+ writer.comment("FontForge's timestamp, font source creation and modification dates")
+ writer.newline()
+ formatstring, names, fixes = sstruct.getformat(FFTMFormat)
+ for name in names:
+ value = getattr(self, name)
+ if name in ("FFTimeStamp", "sourceCreated", "sourceModified"):
+ try:
+ value = time.asctime(time.gmtime(max(0, value + mac_epoch_diff)))
+ except ValueError:
+ value = time.asctime(time.gmtime(0))
+ writer.simpletag(name, value=value)
+ writer.newline()
+
+ def fromXML(self, name, attrs, content, ttFont):
+ value = attrs["value"]
+ if name in ("FFTimeStamp", "sourceCreated", "sourceModified"):
+ value = calendar.timegm(time.strptime(value)) - mac_epoch_diff
+ else:
+ value = safeEval(value)
+ setattr(self, name, value)
\ No newline at end of file
diff --git a/Lib/fontTools/ttLib/tables/G_D_E_F_.py b/Lib/fontTools/ttLib/tables/G_D_E_F_.py
index d763a5d..d4a5741 100644
--- a/Lib/fontTools/ttLib/tables/G_D_E_F_.py
+++ b/Lib/fontTools/ttLib/tables/G_D_E_F_.py
@@ -1,4 +1,4 @@
-from otBase import BaseTTXConverter
+from .otBase import BaseTTXConverter
class table_G_D_E_F_(BaseTTXConverter):
diff --git a/Lib/fontTools/ttLib/tables/G_M_A_P_.py b/Lib/fontTools/ttLib/tables/G_M_A_P_.py
index c4f64f9..57eb8f1 100644
--- a/Lib/fontTools/ttLib/tables/G_M_A_P_.py
+++ b/Lib/fontTools/ttLib/tables/G_M_A_P_.py
@@ -1,7 +1,8 @@
-import DefaultTable
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
from fontTools.misc import sstruct
-from types import StringType
-from fontTools.misc.textTools import safeEval, num2binary, binary2num
+from fontTools.misc.textTools import safeEval
+from . import DefaultTable
GMAPFormat = """
> # big endian
@@ -26,7 +27,7 @@
-class GMAPRecord:
+class GMAPRecord(object):
def __init__(self, uv = 0, cid = 0, gid = 0, ggid = 0, name = ""):
self.UV = uv
self.cid = cid
@@ -51,20 +52,16 @@
writer.newline()
- def fromXML(self, (name, attrs, content), ttFont):
+ def fromXML(self, name, attrs, content, ttFont):
value = attrs["value"]
if name == "GlyphletName":
self.name = value
else:
- try:
- value = safeEval(value)
- except OverflowError:
- value = long(value)
- setattr(self, name, value)
+ setattr(self, name, safeEval(value))
def compile(self, ttFont):
- if self.UV == None:
+ if self.UV is None:
self.UV = 0
nameLen = len(self.name)
if nameLen < 32:
@@ -82,7 +79,7 @@
def decompile(self, data, ttFont):
dummy, newData = sstruct.unpack2(GMAPFormat, data, self)
- self.psFontName = newData[:self.fontNameLength]
+ self.psFontName = tostr(newData[:self.fontNameLength])
assert (self.recordsOffset % 4) == 0, "GMAP error: recordsOffset is not 32 bit aligned."
newData = data[self.recordsOffset:]
self.gmapRecords = []
@@ -95,10 +92,10 @@
def compile(self, ttFont):
self.recordsCount = len(self.gmapRecords)
self.fontNameLength = len(self.psFontName)
- self.recordsOffset = 4 *(((self.fontNameLength + 12) + 3) /4)
+ self.recordsOffset = 4 *(((self.fontNameLength + 12) + 3) // 4)
data = sstruct.pack(GMAPFormat, self)
- data = data + self.psFontName
- data = data + "\0" * (self.recordsOffset - len(data))
+ data = data + tobytes(self.psFontName)
+ data = data + b"\0" * (self.recordsOffset - len(data))
for record in self.gmapRecords:
data = data + record.compile(ttFont)
return data
@@ -117,23 +114,20 @@
for gmapRecord in self.gmapRecords:
gmapRecord.toXML(writer, ttFont)
- def fromXML(self, (name, attrs, content), ttFont):
+ def fromXML(self, name, attrs, content, ttFont):
if name == "GMAPRecord":
if not hasattr(self, "gmapRecords"):
self.gmapRecords = []
gmapRecord = GMAPRecord()
self.gmapRecords.append(gmapRecord)
for element in content:
- if isinstance(element, StringType):
+ if isinstance(element, basestring):
continue
- gmapRecord.fromXML(element, ttFont)
+ name, attrs, content = element
+ gmapRecord.fromXML(name, attrs, content, ttFont)
else:
value = attrs["value"]
if name == "PSFontName":
self.psFontName = value
else:
- try:
- value = safeEval(value)
- except OverflowError:
- value = long(value)
- setattr(self, name, value)
+ setattr(self, name, safeEval(value))
diff --git a/Lib/fontTools/ttLib/tables/G_P_K_G_.py b/Lib/fontTools/ttLib/tables/G_P_K_G_.py
index 6321093..4acccaa 100644
--- a/Lib/fontTools/ttLib/tables/G_P_K_G_.py
+++ b/Lib/fontTools/ttLib/tables/G_P_K_G_.py
@@ -1,10 +1,10 @@
-import sys
-import DefaultTable
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
from fontTools.misc import sstruct
-import array
-from types import StringType
from fontTools.misc.textTools import safeEval, readHex
-from fontTools import ttLib
+from . import DefaultTable
+import sys
+import array
GPKGFormat = """
> # big endian
@@ -25,7 +25,7 @@
GMAPoffsets = array.array("I")
endPos = (self.numGMAPs+1) * 4
GMAPoffsets.fromstring(newData[:endPos])
- if sys.byteorder <> "big":
+ if sys.byteorder != "big":
GMAPoffsets.byteswap()
self.GMAPs = []
for i in range(self.numGMAPs):
@@ -36,7 +36,7 @@
endPos = pos + (self.numGlyplets + 1)*4
glyphletOffsets = array.array("I")
glyphletOffsets.fromstring(newData[pos:endPos])
- if sys.byteorder <> "big":
+ if sys.byteorder != "big":
glyphletOffsets.byteswap()
self.glyphlets = []
for i in range(self.numGlyplets):
@@ -59,7 +59,7 @@
pos += len(self.GMAPs[i-1])
GMAPoffsets[i] = pos
gmapArray = array.array("I", GMAPoffsets)
- if sys.byteorder <> "big":
+ if sys.byteorder != "big":
gmapArray.byteswap()
dataList.append(gmapArray.tostring())
@@ -68,12 +68,12 @@
pos += len(self.glyphlets[i-1])
glyphletOffsets[i] = pos
glyphletArray = array.array("I", glyphletOffsets)
- if sys.byteorder <> "big":
+ if sys.byteorder != "big":
glyphletArray.byteswap()
dataList.append(glyphletArray.tostring())
dataList += self.GMAPs
dataList += self.glyphlets
- data = "".join(dataList)
+ data = bytesjoin(dataList)
return data
def toXML(self, writer, ttFont):
@@ -107,12 +107,12 @@
writer.endtag("glyphlets")
writer.newline()
- def fromXML(self, (name, attrs, content), ttFont):
+ def fromXML(self, name, attrs, content, ttFont):
if name == "GMAPs":
if not hasattr(self, "GMAPs"):
self.GMAPs = []
for element in content:
- if isinstance(element, StringType):
+ if isinstance(element, basestring):
continue
itemName, itemAttrs, itemContent = element
if itemName == "hexdata":
@@ -121,15 +121,10 @@
if not hasattr(self, "glyphlets"):
self.glyphlets = []
for element in content:
- if isinstance(element, StringType):
+ if isinstance(element, basestring):
continue
itemName, itemAttrs, itemContent = element
if itemName == "hexdata":
self.glyphlets.append(readHex(itemContent))
else:
- value = attrs["value"]
- try:
- value = safeEval(value)
- except OverflowError:
- value = long(value)
- setattr(self, name, value)
+ setattr(self, name, safeEval(value))
diff --git a/Lib/fontTools/ttLib/tables/G_P_O_S_.py b/Lib/fontTools/ttLib/tables/G_P_O_S_.py
index 52f44d4..013c820 100644
--- a/Lib/fontTools/ttLib/tables/G_P_O_S_.py
+++ b/Lib/fontTools/ttLib/tables/G_P_O_S_.py
@@ -1,4 +1,4 @@
-from otBase import BaseTTXConverter
+from .otBase import BaseTTXConverter
class table_G_P_O_S_(BaseTTXConverter):
diff --git a/Lib/fontTools/ttLib/tables/G_S_U_B_.py b/Lib/fontTools/ttLib/tables/G_S_U_B_.py
index a523c59..4403649 100644
--- a/Lib/fontTools/ttLib/tables/G_S_U_B_.py
+++ b/Lib/fontTools/ttLib/tables/G_S_U_B_.py
@@ -1,4 +1,4 @@
-from otBase import BaseTTXConverter
+from .otBase import BaseTTXConverter
class table_G_S_U_B_(BaseTTXConverter):
diff --git a/Lib/fontTools/ttLib/tables/J_S_T_F_.py b/Lib/fontTools/ttLib/tables/J_S_T_F_.py
index 8ff395e..ddf5405 100644
--- a/Lib/fontTools/ttLib/tables/J_S_T_F_.py
+++ b/Lib/fontTools/ttLib/tables/J_S_T_F_.py
@@ -1,4 +1,4 @@
-from otBase import BaseTTXConverter
+from .otBase import BaseTTXConverter
class table_J_S_T_F_(BaseTTXConverter):
diff --git a/Lib/fontTools/ttLib/tables/L_T_S_H_.py b/Lib/fontTools/ttLib/tables/L_T_S_H_.py
index 6cd5aad..453f08b 100644
--- a/Lib/fontTools/ttLib/tables/L_T_S_H_.py
+++ b/Lib/fontTools/ttLib/tables/L_T_S_H_.py
@@ -1,7 +1,9 @@
-import DefaultTable
-import array
-import struct
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
from fontTools.misc.textTools import safeEval
+from . import DefaultTable
+import struct
+import array
# 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
@@ -24,7 +26,7 @@
def compile(self, ttFont):
version = 0
- names = self.yPels.keys()
+ names = list(self.yPels.keys())
numGlyphs = len(names)
yPels = [0] * numGlyphs
# ouch: the assertion is not true in Chicago!
@@ -35,16 +37,15 @@
return struct.pack(">HH", version, numGlyphs) + yPels.tostring()
def toXML(self, writer, ttFont):
- names = self.yPels.keys()
- names.sort()
+ names = sorted(self.yPels.keys())
for name in names:
writer.simpletag("yPel", name=name, value=self.yPels[name])
writer.newline()
- def fromXML(self, (name, attrs, content), ttFont):
+ def fromXML(self, name, attrs, content, ttFont):
if not hasattr(self, "yPels"):
self.yPels = {}
- if name <> "yPel":
+ if name != "yPel":
return # ignore unknown tags
self.yPels[attrs["name"]] = safeEval(attrs["value"])
diff --git a/Lib/fontTools/ttLib/tables/M_A_T_H_.py b/Lib/fontTools/ttLib/tables/M_A_T_H_.py
new file mode 100644
index 0000000..d894c08
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/M_A_T_H_.py
@@ -0,0 +1,5 @@
+from .otBase import BaseTTXConverter
+
+
+class table_M_A_T_H_(BaseTTXConverter):
+ pass
diff --git a/Lib/fontTools/ttLib/tables/M_E_T_A_.py b/Lib/fontTools/ttLib/tables/M_E_T_A_.py
index 492ed60..0a3a708 100644
--- a/Lib/fontTools/ttLib/tables/M_E_T_A_.py
+++ b/Lib/fontTools/ttLib/tables/M_E_T_A_.py
@@ -1,10 +1,11 @@
-import DefaultTable
-import struct
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
from fontTools.misc import sstruct
from fontTools.misc.textTools import safeEval
-import string
-from types import FloatType, ListType, StringType, TupleType
-import sys
+from . import DefaultTable
+import struct
+
+
METAHeaderFormat = """
> # big endian
tableVersionMajor: H
@@ -166,28 +167,24 @@
for glyphRec in self.glyphRecords:
glyphRec.toXML(writer, ttFont)
- def fromXML(self, (name, attrs, content), ttFont):
+ def fromXML(self, name, attrs, content, ttFont):
if name == "GlyphRecord":
if not hasattr(self, "glyphRecords"):
self.glyphRecords = []
glyphRec = GlyphRecord()
self.glyphRecords.append(glyphRec)
for element in content:
- if isinstance(element, StringType):
+ if isinstance(element, basestring):
continue
- glyphRec.fromXML(element, ttFont)
+ name, attrs, content = element
+ glyphRec.fromXML(name, attrs, content, ttFont)
glyphRec.offset = -1
glyphRec.nMetaEntry = len(glyphRec.stringRecs)
else:
- value = attrs["value"]
- try:
- value = safeEval(value)
- except OverflowError:
- value = long(value)
- setattr(self, name, value)
+ setattr(self, name, safeEval(attrs["value"]))
-class GlyphRecord:
+class GlyphRecord(object):
def __init__(self):
self.glyphID = -1
self.nMetaEntry = -1
@@ -207,22 +204,17 @@
writer.newline()
- def fromXML(self, (name, attrs, content), ttFont):
+ def fromXML(self, name, attrs, content, ttFont):
if name == "StringRecord":
stringRec = StringRecord()
self.stringRecs.append(stringRec)
for element in content:
- if isinstance(element, StringType):
+ if isinstance(element, basestring):
continue
- stringRec.fromXML(element, ttFont)
+ stringRec.fromXML(name, attrs, content, ttFont)
stringRec.stringLen = len(stringRec.string)
else:
- value = attrs["value"]
- try:
- value = safeEval(value)
- except OverflowError:
- value = long(value)
- setattr(self, name, value)
+ setattr(self, name, safeEval(attrs["value"]))
def compile(self, parentTable):
data = sstruct.pack(METAGlyphRecordFormat, self)
@@ -232,24 +224,14 @@
datum = struct.pack(">L", self.offset)
data = data + datum
return data
-
-
- def __cmp__(self, other):
- """Compare method, so a list of NameRecords can be sorted
- according to the spec by just sorting it..."""
-
- if type(self) != type(other) or \
- self.__class__ != other.__class__:
- return cmp(id(self), id(other))
-
- return cmp(self.glyphID, other.glyphID)
def __repr__(self):
return "GlyphRecord[ glyphID: " + str(self.glyphID) + ", nMetaEntry: " + str(self.nMetaEntry) + ", offset: " + str(self.offset) + " ]"
+# XXX The following two functions are really broken around UTF-8 vs Unicode
def mapXMLToUTF8(string):
- uString = u""
+ uString = unicode()
strLen = len(string)
i = 0
while i < strLen:
@@ -267,7 +249,7 @@
uString = uString + unichr(eval('0x' + valStr))
else:
- uString = uString + unichr(ord(string[i]))
+ uString = uString + unichr(byteord(string[i]))
i = i +1
return uString.encode('utf8')
@@ -279,18 +261,13 @@
for uChar in uString:
i = ord(uChar)
if (i < 0x80) and (i > 0x1F):
- string = string + chr(i)
+ string = string + uChar
else:
string = string + "&#x" + hex(i)[2:] + ";"
return string
-class StringRecord:
- def __init__(self):
- self.labelID = -1
- self.string = ""
- self.stringLen = -1
- self.offset = -1
+class StringRecord(object):
def toXML(self, writer, ttFont):
writer.begintag("StringRecord")
@@ -304,16 +281,16 @@
writer.endtag("StringRecord")
writer.newline()
- def fromXML(self, (name, attrs, content), ttFont):
- value = attrs["value"]
- if name == "string":
- self.string = mapXMLToUTF8(value)
- else:
- try:
- value = safeEval(value)
- except OverflowError:
- value = long(value)
- setattr(self, name, value)
+ def fromXML(self, name, attrs, content, ttFont):
+ for element in content:
+ if isinstance(element, basestring):
+ continue
+ name, attrs, content = element
+ value = attrs["value"]
+ if name == "string":
+ self.string = mapXMLToUTF8(value)
+ else:
+ setattr(self, name, safeEval(value))
def compile(self, parentTable):
data = sstruct.pack(METAStringRecordFormat, self)
@@ -323,16 +300,6 @@
datum = struct.pack(">L", self.offset)
data = data + datum
return data
-
- def __cmp__(self, other):
- """Compare method, so a list of NameRecords can be sorted
- according to the spec by just sorting it..."""
-
- if type(self) != type(other) or \
- self.__class__ != other.__class__:
- return cmp(id(self), id(other))
-
- return cmp(self.labelID, other.labelID)
def __repr__(self):
return "StringRecord [ labelID: " + str(self.labelID) + " aka " + getLabelString(self.labelID) \
diff --git a/Lib/fontTools/ttLib/tables/O_S_2f_2.py b/Lib/fontTools/ttLib/tables/O_S_2f_2.py
index 06d8291..f678c17 100644
--- a/Lib/fontTools/ttLib/tables/O_S_2f_2.py
+++ b/Lib/fontTools/ttLib/tables/O_S_2f_2.py
@@ -1,7 +1,9 @@
-import DefaultTable
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
from fontTools.misc import sstruct
from fontTools.misc.textTools import safeEval, num2binary, binary2num
-from types import TupleType
+from . import DefaultTable
+import warnings
# panose classification
@@ -19,7 +21,7 @@
bXHeight: B
"""
-class Panose:
+class Panose(object):
def toXML(self, writer, ttFont):
formatstring, names, fixes = sstruct.getformat(panoseFormat)
@@ -27,7 +29,7 @@
writer.simpletag(name, value=getattr(self, name))
writer.newline()
- def fromXML(self, (name, attrs, content), ttFont):
+ def fromXML(self, name, attrs, content, ttFont):
setattr(self, name, safeEval(attrs["value"]))
@@ -80,12 +82,19 @@
usMaxContex: H
"""
+OS2_format_5_addition = OS2_format_2_addition + """
+ usLowerOpticalPointSize: H
+ usUpperOpticalPointSize: 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_5 = OS2_format_0 + OS2_format_5_addition
OS2_format_1_addition = bigendian + OS2_format_1_addition
OS2_format_2_addition = bigendian + OS2_format_2_addition
+OS2_format_5_addition = bigendian + OS2_format_5_addition
class table_O_S_2f_2(DefaultTable.DefaultTable):
@@ -94,24 +103,21 @@
def decompile(self, data, ttFont):
dummy, data = sstruct.unpack2(OS2_format_0, data, self)
- # workarounds for buggy fonts (Apple, mona)
- if not data:
- self.version = 0
- elif len(data) == sstruct.calcsize(OS2_format_1_addition):
- self.version = 1
- elif len(data) == sstruct.calcsize(OS2_format_2_addition):
- if self.version not in (2, 3, 4):
- self.version = 1
- else:
- from fontTools import ttLib
- raise ttLib.TTLibError, "unknown format for OS/2 table (incorrect length): version %s" % (self.version, len(data))
+
if self.version == 1:
- sstruct.unpack2(OS2_format_1_addition, data, self)
+ dummy, data = sstruct.unpack2(OS2_format_1_addition, data, self)
elif self.version in (2, 3, 4):
- sstruct.unpack2(OS2_format_2_addition, data, self)
- elif self.version <> 0:
+ dummy, data = sstruct.unpack2(OS2_format_2_addition, data, self)
+ elif self.version == 5:
+ dummy, data = sstruct.unpack2(OS2_format_5_addition, data, self)
+ self.usLowerOpticalPointSize /= 20
+ self.usUpperOpticalPointSize /= 20
+ elif self.version != 0:
from fontTools import ttLib
- raise ttLib.TTLibError, "unknown format for OS/2 table: version %s" % self.version
+ raise ttLib.TTLibError("unknown format for OS/2 table: version %s" % self.version)
+ if len(data):
+ warnings.warn("too much 'OS/2' table data")
+
self.panose = sstruct.unpack(panoseFormat, self.panose, Panose())
def compile(self, ttFont):
@@ -123,9 +129,14 @@
data = sstruct.pack(OS2_format_1, self)
elif self.version in (2, 3, 4):
data = sstruct.pack(OS2_format_2, self)
+ elif self.version == 5:
+ d = self.__dict__.copy()
+ d['usLowerOpticalPointSize'] = int(round(self.usLowerOpticalPointSize * 20))
+ d['usUpperOpticalPointSize'] = int(round(self.usUpperOpticalPointSize * 20))
+ data = sstruct.pack(OS2_format_5, d)
else:
from fontTools import ttLib
- raise ttLib.TTLibError, "unknown format for OS/2 table: version %s" % self.version
+ raise ttLib.TTLibError("unknown format for OS/2 table: version %s" % self.version)
self.panose = panose
return data
@@ -134,13 +145,13 @@
format = OS2_format_1
elif self.version in (2, 3, 4):
format = OS2_format_2
+ elif self.version == 5:
+ format = OS2_format_5
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()
@@ -158,12 +169,13 @@
writer.simpletag(name, value=value)
writer.newline()
- def fromXML(self, (name, attrs, content), ttFont):
+ def fromXML(self, name, attrs, content, ttFont):
if name == "panose":
self.panose = panose = Panose()
for element in content:
- if type(element) == TupleType:
- panose.fromXML(element, ttFont)
+ if isinstance(element, tuple):
+ name, attrs, content = element
+ panose.fromXML(name, attrs, content, ttFont)
elif name in ("ulUnicodeRange1", "ulUnicodeRange2",
"ulUnicodeRange3", "ulUnicodeRange4",
"ulCodePageRange1", "ulCodePageRange2",
diff --git a/Lib/fontTools/ttLib/tables/S_I_N_G_.py b/Lib/fontTools/ttLib/tables/S_I_N_G_.py
index 3f76c93..6d9a412 100644
--- a/Lib/fontTools/ttLib/tables/S_I_N_G_.py
+++ b/Lib/fontTools/ttLib/tables/S_I_N_G_.py
@@ -1,9 +1,8 @@
-import DefaultTable
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
from fontTools.misc import sstruct
-import struct
-import time
-import string
-from fontTools.misc.textTools import safeEval, num2binary, binary2num
+from fontTools.misc.textTools import safeEval
+from . import DefaultTable
SINGFormat = """
> # big endian
@@ -30,52 +29,53 @@
def decompile(self, data, ttFont):
dummy, rest = sstruct.unpack2(SINGFormat, data, self)
self.uniqueName = self.decompileUniqueName(self.uniqueName)
- self.nameLength = ord(self.nameLength)
+ self.nameLength = byteord(self.nameLength)
assert len(rest) == self.nameLength
- self.baseGlyphName = rest
+ self.baseGlyphName = tostr(rest)
rawMETAMD5 = self.METAMD5
- self.METAMD5 = "[" + hex(ord(self.METAMD5[0]))
+ self.METAMD5 = "[" + hex(byteord(self.METAMD5[0]))
for char in rawMETAMD5[1:]:
- self.METAMD5 = self.METAMD5 + ", " + hex(ord(char))
+ self.METAMD5 = self.METAMD5 + ", " + hex(byteord(char))
self.METAMD5 = self.METAMD5 + "]"
def decompileUniqueName(self, data):
name = ""
for char in data:
- val = ord(char)
+ val = byteord(char)
if val == 0:
break
if (val > 31) or (val < 128):
- name = name + char
+ name += chr(val)
else:
octString = oct(val)
if len(octString) > 3:
octString = octString[1:] # chop off that leading zero.
elif len(octString) < 3:
octString.zfill(3)
- name = name + "\\" + octString
+ name += "\\" + octString
return name
def compile(self, ttFont):
- self.nameLength = chr(len(self.baseGlyphName))
- self.uniqueName = self.compilecompileUniqueName(self.uniqueName, 28)
+ d = self.__dict__.copy()
+ d["nameLength"] = bytechr(len(self.baseGlyphName))
+ d["uniqueName"] = self.compilecompileUniqueName(self.uniqueName, 28)
METAMD5List = eval(self.METAMD5)
- self.METAMD5 = ""
+ d["METAMD5"] = b""
for val in METAMD5List:
- self.METAMD5 = self.METAMD5 + chr(val)
- assert (len(self.METAMD5) == 16), "Failed to pack 16 byte MD5 hash in SING table"
- data = sstruct.pack(SINGFormat, self)
- data = data + self.baseGlyphName
+ d["METAMD5"] += bytechr(val)
+ assert (len(d["METAMD5"]) == 16), "Failed to pack 16 byte MD5 hash in SING table"
+ data = sstruct.pack(SINGFormat, d)
+ data = data + tobytes(self.baseGlyphName)
return data
def compilecompileUniqueName(self, name, length):
nameLen = len(name)
if length <= nameLen:
- name[:length-1] + "\000"
+ name = name[:length-1] + "\000"
else:
- name.join( (nameLen - length)* "\000")
+ name += (nameLen - length) * "\000"
return name
@@ -90,13 +90,9 @@
writer.simpletag("baseGlyphName", value=self.baseGlyphName)
writer.newline()
- def fromXML(self, (name, attrs, content), ttFont):
+ def fromXML(self, name, attrs, content, ttFont):
value = attrs["value"]
if name in ["uniqueName", "METAMD5", "baseGlyphName"]:
setattr(self, name, value)
else:
- try:
- value = safeEval(value)
- except OverflowError:
- value = long(value)
- setattr(self, name, value)
+ setattr(self, name, safeEval(value))
diff --git a/Lib/fontTools/ttLib/tables/S_V_G_.py b/Lib/fontTools/ttLib/tables/S_V_G_.py
index 0763435..e4d600c 100644
--- a/Lib/fontTools/ttLib/tables/S_V_G_.py
+++ b/Lib/fontTools/ttLib/tables/S_V_G_.py
@@ -1,3 +1,14 @@
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
+from fontTools.misc import sstruct
+from . import DefaultTable
+try:
+ import xml.etree.cElementTree as ET
+except ImportError:
+ import xml.etree.ElementTree as ET
+import struct
+import re
+
__doc__="""
Compiles/decompiles version 0 and 1 SVG tables from/to XML.
@@ -42,18 +53,6 @@
"""
-import DefaultTable
-import struct
-from fontTools.misc import sstruct
-from fontTools.misc.textTools import safeEval
-try:
- import xml.etree.cElementTree as ET
-except ImportError:
- import xml.etree.ElementTree as ET
-import string
-import types
-import re
-
XML = ET.XML
XMLElement = ET.Element
xmlToString = ET.tostring
@@ -105,16 +104,14 @@
self.decompile_format_1(data, ttFont)
else:
if self.version != 0:
- print "Unknown SVG table version '%s'. Decompiling as version 0." % (self.version)
+ print("Unknown SVG table version '%s'. Decompiling as version 0." % (self.version))
self.decompile_format_0(data, ttFont)
def decompile_format_0(self, data, ttFont):
dummy, data2 = sstruct.unpack2(SVG_format_0, data, self)
# read in SVG Documents Index
- pos = self.offsetToSVGDocIndex
- self.numEntries = numEntries = struct.unpack(">H", data[pos:pos+2])[0]
- self.decompileEntryList(data, pos+2)
+ self.decompileEntryList(data)
# read in colorPalettes table.
self.colorPalettes = colorPalettes = ColorPalettes()
@@ -156,8 +153,11 @@
pos += 2
self.decompileEntryList(data, pos)
- def decompileEntryList(self, data, pos):
+ def decompileEntryList(self, data):
# data starts with the first entry of the entry list.
+ pos = subTableStart = self.offsetToSVGDocIndex
+ self.numEntries = numEntries = struct.unpack(">H", data[pos:pos+2])[0]
+ pos += 2
if self.numEntries > 0:
data2 = data[pos:]
self.docList = []
@@ -169,9 +169,9 @@
i += 1
for entry in entries:
- start = entry.svgDocOffset
+ start = entry.svgDocOffset + subTableStart
end = start + entry.svgDocLength
- doc = data[start:end]
+ doc = tostr(data[start:end], "utf-8")
self.docList.append( [doc, entry.startGlyphID, entry.endGlyphID] )
def compile(self, ttFont):
@@ -184,26 +184,25 @@
def compileFormat0(self, ttFont):
version = 0
offsetToSVGDocIndex = SVG_format_0Size # I start the SVGDocIndex right after the header.
-
# get SGVDoc info.
docList = []
entryList = []
numEntries = len(self.docList)
datum = struct.pack(">H",numEntries)
entryList.append(datum)
- curOffset = offsetToSVGDocIndex + len(datum) + doc_index_entry_format_0Size*numEntries
+ curOffset = len(datum) + doc_index_entry_format_0Size*numEntries
for doc, startGlyphID, endGlyphID in self.docList:
docOffset = curOffset
docLength = len(doc)
curOffset += docLength
- entry = struct.pack(">HHLL", startGlyphID, endGlyphID, docOffset, docLength)
- entryList.append(entry)
- docList.append(doc)
+ entry = struct.pack(">HHLL", startGlyphID, endGlyphID, docOffset, docLength)
+ entryList.append(entry)
+ docList.append(tobytes(doc, encoding="utf-8"))
entryList.extend(docList)
- svgDocData = "".join(entryList)
+ svgDocData = bytesjoin(entryList)
# get colorpalette info.
- if self.colorPalettes == None:
+ if self.colorPalettes is None:
offsetToColorPalettes = 0
palettesData = ""
else:
@@ -224,11 +223,11 @@
for colorRecord in colorPalette.paletteColors:
data = struct.pack(">BBBB", colorRecord.red, colorRecord.green, colorRecord.blue, colorRecord.alpha)
dataList.append(data)
- palettesData = "".join(dataList)
+ palettesData = bytesjoin(dataList)
header = struct.pack(">HLL", version, offsetToSVGDocIndex, offsetToColorPalettes)
data = [header, svgDocData, palettesData]
- data = "".join(data)
+ data = bytesjoin(data)
return data
def compileFormat1(self, ttFont):
@@ -242,11 +241,11 @@
docOffset = curOffset
docLength = len(doc)
curOffset += docLength
- entry = struct.pack(">HHLL", startGlyphID, endGlyphID, docOffset, docLength)
- dataList.append(entry)
- docList.append(doc)
+ entry = struct.pack(">HHLL", startGlyphID, endGlyphID, docOffset, docLength)
+ dataList.append(entry)
+ docList.append(tobytes(doc, encoding="utf-8"))
dataList.extend(docList)
- data = "".join(dataList)
+ data = bytesjoin(dataList)
return data
def toXML(self, writer, ttFont):
@@ -254,12 +253,12 @@
for doc, startGID, endGID in self.docList:
writer.begintag("svgDoc", startGlyphID=startGID, endGlyphID=endGID)
writer.newline()
- writer.writeraw("<![CDATA["+ doc + "]]>")
+ writer.writecdata(doc)
writer.newline()
writer.endtag("svgDoc")
writer.newline()
- if self.colorPalettes.numColorParams != None:
+ if (self.colorPalettes is not None) and (self.colorPalettes.numColorParams is not None):
writer.begintag("colorPalettes")
writer.newline()
for uiNameID in self.colorPalettes.colorParamUINameIDs:
@@ -290,25 +289,25 @@
writer.endtag("colorPalettes")
writer.newline()
- def fromXML(self, (name, attrs, content), ttFont):
+ def fromXML(self, name, attrs, content, ttFont):
import re
if name == "svgDoc":
if not hasattr(self, "docList"):
self.docList = []
- doc = "".join(content)
+ doc = strjoin(content)
doc = doc.strip()
startGID = int(attrs["startGlyphID"])
endGID = int(attrs["endGlyphID"])
self.docList.append( [doc, startGID, endGID] )
elif name == "colorPalettes":
self.colorPalettes = ColorPalettes()
- self.colorPalettes.fromXML((name, attrs, content), ttFont)
+ self.colorPalettes.fromXML(name, attrs, content, ttFont)
if self.colorPalettes.numColorParams == 0:
self.colorPalettes = None
else:
- print "Unknown", name, content
+ print("Unknown", name, content)
-class DocumentIndexEntry:
+class DocumentIndexEntry(object):
def __init__(self):
self.startGlyphID = None # USHORT
self.endGlyphID = None # USHORT
@@ -318,16 +317,16 @@
def __repr__(self):
return "startGlyphID: %s, endGlyphID: %s, svgDocOffset: %s, svgDocLength: %s" % (self.startGlyphID, self.endGlyphID, self.svgDocOffset, self.svgDocLength)
-class ColorPalettes:
+class ColorPalettes(object):
def __init__(self):
self.numColorParams = None # USHORT
self.colorParamUINameIDs = [] # list of name table name ID values that provide UI description of each color palette.
self.numColorPalettes = None # USHORT
self.colorPaletteList = [] # list of ColorPalette records
- def fromXML(self, (name, attrs, content), ttFont):
+ def fromXML(self, name, attrs, content, ttFont):
for element in content:
- if type(element) == type(""):
+ if isinstance(element, type("")):
continue
name, attrib, content = element
if name == "colorParamUINameID":
@@ -344,15 +343,15 @@
if len(colorPalette.paletteColors) != self.numColorParams:
raise ValueError("Number of color records in a colorPalette ('%s') does not match the number of colorParamUINameIDs elements ('%s')." % (len(colorPalette.paletteColors), self.numColorParams))
-class ColorPalette:
+class ColorPalette(object):
def __init__(self):
self.uiNameID = None # USHORT. name table ID that describes user interface strings associated with this color palette.
self.paletteColors = [] # list of ColorRecords
- def fromXML(self, (name, attrs, content), ttFont):
+ def fromXML(self, name, attrs, content, ttFont):
self.uiNameID = int(attrs["uiNameID"])
for element in content:
- if type(element) == type(""):
+ if isinstance(element, type("")):
continue
name, attrib, content = element
if name == "colorRecord":
@@ -363,7 +362,7 @@
colorRecord.blue = eval(attrib["blue"])
colorRecord.alpha = eval(attrib["alpha"])
-class ColorRecord:
+class ColorRecord(object):
def __init__(self):
self.red = 255 # all are one byte values.
self.green = 255
diff --git a/Lib/fontTools/ttLib/tables/T_S_I_B_.py b/Lib/fontTools/ttLib/tables/T_S_I_B_.py
index 31608f8..5cc54e2 100644
--- a/Lib/fontTools/ttLib/tables/T_S_I_B_.py
+++ b/Lib/fontTools/ttLib/tables/T_S_I_B_.py
@@ -1,4 +1,4 @@
-import asciiTable
+from . import asciiTable
class table_T_S_I_B_(asciiTable.asciiTable):
pass
diff --git a/Lib/fontTools/ttLib/tables/T_S_I_D_.py b/Lib/fontTools/ttLib/tables/T_S_I_D_.py
index 0388405..8228f8a 100644
--- a/Lib/fontTools/ttLib/tables/T_S_I_D_.py
+++ b/Lib/fontTools/ttLib/tables/T_S_I_D_.py
@@ -1,4 +1,4 @@
-import asciiTable
+from . import asciiTable
class table_T_S_I_D_(asciiTable.asciiTable):
pass
diff --git a/Lib/fontTools/ttLib/tables/T_S_I_J_.py b/Lib/fontTools/ttLib/tables/T_S_I_J_.py
index 8f6ed8b..0983b57 100644
--- a/Lib/fontTools/ttLib/tables/T_S_I_J_.py
+++ b/Lib/fontTools/ttLib/tables/T_S_I_J_.py
@@ -1,4 +1,4 @@
-import asciiTable
+from . import asciiTable
class table_T_S_I_J_(asciiTable.asciiTable):
pass
diff --git a/Lib/fontTools/ttLib/tables/T_S_I_P_.py b/Lib/fontTools/ttLib/tables/T_S_I_P_.py
index f0de28d..e34a18c 100644
--- a/Lib/fontTools/ttLib/tables/T_S_I_P_.py
+++ b/Lib/fontTools/ttLib/tables/T_S_I_P_.py
@@ -1,4 +1,4 @@
-import asciiTable
+from . import asciiTable
class table_T_S_I_P_(asciiTable.asciiTable):
pass
diff --git a/Lib/fontTools/ttLib/tables/T_S_I_S_.py b/Lib/fontTools/ttLib/tables/T_S_I_S_.py
index fc98317..56373e6 100644
--- a/Lib/fontTools/ttLib/tables/T_S_I_S_.py
+++ b/Lib/fontTools/ttLib/tables/T_S_I_S_.py
@@ -1,4 +1,4 @@
-import asciiTable
+from . import asciiTable
class table_T_S_I_S_(asciiTable.asciiTable):
pass
diff --git a/Lib/fontTools/ttLib/tables/T_S_I_V_.py b/Lib/fontTools/ttLib/tables/T_S_I_V_.py
index 928b7ce..a87e3f7 100644
--- a/Lib/fontTools/ttLib/tables/T_S_I_V_.py
+++ b/Lib/fontTools/ttLib/tables/T_S_I_V_.py
@@ -1,4 +1,4 @@
-import asciiTable
+from . import asciiTable
class table_T_S_I_V_(asciiTable.asciiTable):
pass
diff --git a/Lib/fontTools/ttLib/tables/T_S_I__0.py b/Lib/fontTools/ttLib/tables/T_S_I__0.py
index c527818..174b192 100644
--- a/Lib/fontTools/ttLib/tables/T_S_I__0.py
+++ b/Lib/fontTools/ttLib/tables/T_S_I__0.py
@@ -1,9 +1,11 @@
-import DefaultTable
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
+from . import DefaultTable
import struct
tsi0Format = '>HHl'
-def fixlongs((glyphID, textLength, textOffset)):
+def fixlongs(glyphID, textLength, textOffset):
return int(glyphID), int(textLength), textOffset
@@ -16,7 +18,7 @@
indices = []
size = struct.calcsize(tsi0Format)
for i in range(numGlyphs + 5):
- glyphID, textLength, textOffset = fixlongs(struct.unpack(tsi0Format, data[:size]))
+ glyphID, textLength, textOffset = fixlongs(*struct.unpack(tsi0Format, data[:size]))
indices.append((glyphID, textLength, textOffset))
data = data[size:]
assert len(data) == 0
@@ -26,10 +28,10 @@
def compile(self, ttFont):
if not hasattr(self, "indices"):
- # We have no corresponging table (TSI1 or TSI3); let's return
+ # We have no corresponding table (TSI1 or TSI3); let's return
# no data, which effectively means "ignore us".
return ""
- data = ""
+ data = b""
for index, textLength, textOffset in self.indices:
data = data + struct.pack(tsi0Format, index, textLength, textOffset)
data = data + struct.pack(tsi0Format, 0XFFFE, 0, -1409540300) # 0xABFC1F34
diff --git a/Lib/fontTools/ttLib/tables/T_S_I__1.py b/Lib/fontTools/ttLib/tables/T_S_I__1.py
index fa6f7fe..bcb4049 100644
--- a/Lib/fontTools/ttLib/tables/T_S_I__1.py
+++ b/Lib/fontTools/ttLib/tables/T_S_I__1.py
@@ -1,5 +1,6 @@
-import DefaultTable
-import string
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
+from . import DefaultTable
class table_T_S_I__1(DefaultTable.DefaultTable):
@@ -39,19 +40,19 @@
if not hasattr(self, "glyphPrograms"):
self.glyphPrograms = {}
self.extraPrograms = {}
- data = ''
+ data = b''
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.
+ data = data + b"\015" # align on 2-byte boundaries, fill with return chars. Yum.
name = glyphNames[i]
- if self.glyphPrograms.has_key(name):
+ if name in self.glyphPrograms:
text = self.glyphPrograms[name]
else:
- text = ""
+ text = b""
textLength = len(text)
if textLength >= 0x8000:
textLength = 0x8000 # XXX ???
@@ -59,16 +60,15 @@
data = data + text
extra_indices = []
- codes = self.extras.items()
- codes.sort()
+ codes = sorted(self.extras.items())
for i in range(len(codes)):
if len(data) % 2:
- data = data + "\015" # align on 2-byte boundaries, fill with return chars.
+ data = data + b"\015" # align on 2-byte boundaries, fill with return chars.
code, name = codes[i]
- if self.extraPrograms.has_key(name):
+ if name in self.extraPrograms:
text = self.extraPrograms[name]
else:
- text = ""
+ text = b""
textLength = len(text)
if textLength >= 0x8000:
textLength = 0x8000 # XXX ???
@@ -78,8 +78,7 @@
return data
def toXML(self, writer, ttFont):
- names = self.glyphPrograms.keys()
- names.sort()
+ names = sorted(self.glyphPrograms.keys())
writer.newline()
for name in names:
text = self.glyphPrograms[name]
@@ -87,31 +86,30 @@
continue
writer.begintag("glyphProgram", name=name)
writer.newline()
- writer.write_noindent(string.replace(text, "\r", "\n"))
+ writer.write_noindent(text.replace("\r", "\n"))
writer.newline()
writer.endtag("glyphProgram")
writer.newline()
writer.newline()
- extra_names = self.extraPrograms.keys()
- extra_names.sort()
+ extra_names = sorted(self.extraPrograms.keys())
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.write_noindent(text.replace("\r", "\n"))
writer.newline()
writer.endtag("extraProgram")
writer.newline()
writer.newline()
- def fromXML(self, (name, attrs, content), ttFont):
+ 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")
+ lines = strjoin(content).replace("\r", "\n").split("\n")
+ text = '\r'.join(lines[1:-1])
if name == "glyphProgram":
self.glyphPrograms[attrs["name"]] = text
elif name == "extraProgram":
diff --git a/Lib/fontTools/ttLib/tables/T_S_I__5.py b/Lib/fontTools/ttLib/tables/T_S_I__5.py
index f714b82..9d9c3c3 100644
--- a/Lib/fontTools/ttLib/tables/T_S_I__5.py
+++ b/Lib/fontTools/ttLib/tables/T_S_I__5.py
@@ -1,8 +1,9 @@
-import sys
-import DefaultTable
-import array
-from fontTools import ttLib
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
from fontTools.misc.textTools import safeEval
+from . import DefaultTable
+import sys
+import array
class table_T_S_I__5(DefaultTable.DefaultTable):
@@ -12,7 +13,7 @@
assert len(data) == 2 * numGlyphs
a = array.array("H")
a.fromstring(data)
- if sys.byteorder <> "big":
+ if sys.byteorder != "big":
a.byteswap()
self.glyphGrouping = {}
for i in range(numGlyphs):
@@ -23,21 +24,20 @@
a = array.array("H")
for i in range(len(glyphNames)):
a.append(self.glyphGrouping[glyphNames[i]])
- if sys.byteorder <> "big":
+ if sys.byteorder != "big":
a.byteswap()
return a.tostring()
def toXML(self, writer, ttFont):
- names = self.glyphGrouping.keys()
- names.sort()
+ names = sorted(self.glyphGrouping.keys())
for glyphName in names:
writer.simpletag("glyphgroup", name=glyphName, value=self.glyphGrouping[glyphName])
writer.newline()
- def fromXML(self, (name, attrs, content), ttFont):
+ def fromXML(self, name, attrs, content, ttFont):
if not hasattr(self, "glyphGrouping"):
self.glyphGrouping = {}
- if name <> "glyphgroup":
+ if name != "glyphgroup":
return
self.glyphGrouping[attrs["name"]] = safeEval(attrs["value"])
diff --git a/Lib/fontTools/ttLib/tables/V_O_R_G_.py b/Lib/fontTools/ttLib/tables/V_O_R_G_.py
index f817048..143b971 100644
--- a/Lib/fontTools/ttLib/tables/V_O_R_G_.py
+++ b/Lib/fontTools/ttLib/tables/V_O_R_G_.py
@@ -1,9 +1,9 @@
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
+from fontTools.misc.textTools import safeEval
+from . import DefaultTable
import operator
-import DefaultTable
import struct
-from fontTools.ttLib import sfnt
-from fontTools.misc.textTools import safeEval, readHex
-from types import IntType, StringType
class table_V_O_R_G_(DefaultTable.DefaultTable):
@@ -30,31 +30,31 @@
self.VOriginRecords = vOrig = {}
glyphOrder = ttFont.getGlyphOrder()
try:
- names = map(operator.getitem, [glyphOrder]*self.numVertOriginYMetrics, gids )
+ names = map(operator.getitem, [glyphOrder]*self.numVertOriginYMetrics, gids)
except IndexError:
getGlyphName = self.getGlyphName
names = map(getGlyphName, gids )
- map(operator.setitem, [vOrig]*self.numVertOriginYMetrics, names, vids)
+ list(map(operator.setitem, [vOrig]*self.numVertOriginYMetrics, names, vids))
def compile(self, ttFont):
- vorgs = self.VOriginRecords.values()
- names = self.VOriginRecords.keys()
+ vorgs = list(self.VOriginRecords.values())
+ names = list(self.VOriginRecords.keys())
nameMap = ttFont.getReverseGlyphMap()
lenRecords = len(vorgs)
try:
gids = map(operator.getitem, [nameMap]*lenRecords, names)
except KeyError:
- nameMap = ttFont.getReverseGlyphMap(rebuild=1)
+ nameMap = ttFont.getReverseGlyphMap(rebuild=True)
gids = map(operator.getitem, [nameMap]*lenRecords, names)
- vOriginTable = map(None, gids, vorgs)
+ vOriginTable = list(zip(gids, vorgs))
self.numVertOriginYMetrics = lenRecords
vOriginTable.sort() # must be in ascending GID order
dataList = [ struct.pack(">Hh", rec[0], rec[1]) for rec in vOriginTable]
header = struct.pack(">HHhH", self.majorVersion, self.minorVersion, self.defaultVertOriginY, self.numVertOriginYMetrics)
dataList.insert(0, header)
- data = "".join(dataList)
+ data = bytesjoin(dataList)
return data
def toXML(self, writer, ttFont):
@@ -79,46 +79,43 @@
vOriginRec = VOriginRecord(entry[1], entry[2])
vOriginRec.toXML(writer, ttFont)
- def fromXML(self, (name, attrs, content), ttFont):
+ def fromXML(self, name, attrs, content, ttFont):
if not hasattr(self, "VOriginRecords"):
self.VOriginRecords = {}
self.getGlyphName = ttFont.getGlyphName # for use in get/set item functions, for access by GID
if name == "VOriginRecord":
- for element in content:
- if isinstance(element, StringType):
- continue
vOriginRec = VOriginRecord()
for element in content:
- if isinstance(element, StringType):
+ if isinstance(element, basestring):
continue
- vOriginRec.fromXML(element, ttFont)
+ name, attrs, content = element
+ vOriginRec.fromXML(name, attrs, content, ttFont)
self.VOriginRecords[vOriginRec.glyphName] = vOriginRec.vOrigin
- elif attrs.has_key("value"):
- value = safeEval(attrs["value"])
- setattr(self, name, value)
+ elif "value" in attrs:
+ setattr(self, name, safeEval(attrs["value"]))
def __getitem__(self, glyphSelector):
- if type(glyphSelector) == IntType:
+ if isinstance(glyphSelector, int):
# its a gid, convert to glyph name
glyphSelector = self.getGlyphName(glyphSelector)
- if not self.VOriginRecords.has_key(glyphSelector):
+ if glyphSelector not in self.VOriginRecords:
return self.defaultVertOriginY
return self.VOriginRecords[glyphSelector]
def __setitem__(self, glyphSelector, value):
- if type(glyphSelector) == IntType:
+ if isinstance(glyphSelector, int):
# its a gid, convert to glyph name
glyphSelector = self.getGlyphName(glyphSelector)
if value != self.defaultVertOriginY:
self.VOriginRecords[glyphSelector] = value
- elif self.VOriginRecords.has_key(glyphSelector):
+ elif glyphSelector in self.VOriginRecords:
del self.VOriginRecords[glyphSelector]
-class VOriginRecord:
+class VOriginRecord(object):
def __init__(self, name = None, vOrigin = None):
self.glyphName = name
@@ -134,13 +131,9 @@
writer.endtag("VOriginRecord")
writer.newline()
- def fromXML(self, (name, attrs, content), ttFont):
+ def fromXML(self, name, attrs, content, ttFont):
value = attrs["value"]
if name == "glyphName":
setattr(self, name, value)
else:
- try:
- value = safeEval(value)
- except OverflowError:
- value = long(value)
- setattr(self, name, value)
+ setattr(self, name, safeEval(value))
diff --git a/Lib/fontTools/ttLib/tables/__init__.py b/Lib/fontTools/ttLib/tables/__init__.py
index 205af81..bdf8d96 100644
--- a/Lib/fontTools/ttLib/tables/__init__.py
+++ b/Lib/fontTools/ttLib/tables/__init__.py
@@ -3,52 +3,54 @@
"""Dummy function to let modulefinder know what tables may be
dynamically imported. Generated by MetaTools/buildTableList.py.
"""
- import B_A_S_E_
- import C_B_D_T_
- import C_B_L_C_
- import C_F_F_
- import C_O_L_R_
- import C_P_A_L_
- import D_S_I_G_
- import E_B_D_T_
- import E_B_L_C_
- import G_D_E_F_
- import G_M_A_P_
- import G_P_K_G_
- import G_P_O_S_
- import G_S_U_B_
- import J_S_T_F_
- import L_T_S_H_
- import M_E_T_A_
- import O_S_2f_2
- import S_I_N_G_
- import S_V_G_
- import T_S_I_B_
- import T_S_I_D_
- import T_S_I_J_
- import T_S_I_P_
- import T_S_I_S_
- import T_S_I_V_
- import T_S_I__0
- import T_S_I__1
- import T_S_I__2
- import T_S_I__3
- import T_S_I__5
- import V_O_R_G_
- import _c_m_a_p
- import _c_v_t
- import _f_p_g_m
- import _g_a_s_p
- import _g_l_y_f
- import _h_d_m_x
- import _h_e_a_d
- import _h_h_e_a
- import _h_m_t_x
- import _k_e_r_n
- import _l_o_c_a
- import _m_a_x_p
- import _n_a_m_e
- import _p_o_s_t
- import _p_r_e_p
- import _v_h_e_a
- import _v_m_t_x
+ from . import B_A_S_E_
+ from . import C_B_D_T_
+ from . import C_B_L_C_
+ from . import C_F_F_
+ from . import C_O_L_R_
+ from . import C_P_A_L_
+ from . import D_S_I_G_
+ from . import E_B_D_T_
+ from . import E_B_L_C_
+ from . import F_F_T_M_
+ from . import G_D_E_F_
+ from . import G_M_A_P_
+ from . import G_P_K_G_
+ from . import G_P_O_S_
+ from . import G_S_U_B_
+ from . import J_S_T_F_
+ from . import L_T_S_H_
+ from . import M_E_T_A_
+ from . import O_S_2f_2
+ from . import S_I_N_G_
+ from . import S_V_G_
+ from . import T_S_I_B_
+ from . import T_S_I_D_
+ from . import T_S_I_J_
+ from . import T_S_I_P_
+ from . import T_S_I_S_
+ from . import T_S_I_V_
+ from . import T_S_I__0
+ from . import T_S_I__1
+ from . import T_S_I__2
+ from . import T_S_I__3
+ from . import T_S_I__5
+ from . import V_O_R_G_
+ from . import _c_m_a_p
+ from . import _c_v_t
+ from . import _f_p_g_m
+ from . import _g_a_s_p
+ from . import _g_l_y_f
+ from . import _h_d_m_x
+ from . import _h_e_a_d
+ from . import _h_h_e_a
+ from . import _h_m_t_x
+ from . import _k_e_r_n
+ from . import _l_o_c_a
+ from . import _m_a_x_p
+ from . import _n_a_m_e
+ from . import _p_o_s_t
+ from . import _p_r_e_p
+ from . import _s_b_i_x
+ from . import _v_h_e_a
+ from . import _v_m_t_x
diff --git a/Lib/fontTools/ttLib/tables/_c_m_a_p.py b/Lib/fontTools/ttLib/tables/_c_m_a_p.py
index 7574431..b72d98d 100644
--- a/Lib/fontTools/ttLib/tables/_c_m_a_p.py
+++ b/Lib/fontTools/ttLib/tables/_c_m_a_p.py
@@ -1,11 +1,11 @@
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
+from fontTools.misc.textTools import safeEval, readHex
+from . import DefaultTable
import sys
-import DefaultTable
import struct
import array
import operator
-from fontTools import ttLib
-from fontTools.misc.textTools import safeEval, readHex
-from types import TupleType
class table__c_m_a_p(DefaultTable.DefaultTable):
@@ -27,15 +27,15 @@
">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 format in [8,10,12]:
+ if format in [8,10,12,13]:
format, reserved, length = struct.unpack(">HHL", data[offset:offset+8])
elif format in [14]:
format, length = struct.unpack(">HL", data[offset:offset+6])
if not length:
- print "Error: cmap subtable is reported as having zero length: platformID %s, platEncID %s, format %s offset %s. Skipping table." % (platformID, platEncID,format, offset)
+ print("Error: cmap subtable is reported as having zero length: platformID %s, platEncID %s, format %s offset %s. Skipping table." % (platformID, platEncID,format, offset))
continue
- if not cmap_classes.has_key(format):
+ if format not in cmap_classes:
table = cmap_format_unknown(format)
else:
table = cmap_classes[format](format)
@@ -45,18 +45,18 @@
# any other data gets decompiled only when an attribute of the
# subtable is referenced.
table.decompileHeader(data[offset:offset+int(length)], ttFont)
- if seenOffsets.has_key(offset):
+ if offset in seenOffsets:
table.cmap = tables[seenOffsets[offset]].cmap
else:
seenOffsets[offset] = i
tables.append(table)
def compile(self, ttFont):
- self.tables.sort() # sort according to the spec; see CmapSubtable.__cmp__()
+ self.tables.sort() # sort according to the spec; see CmapSubtable.__lt__()
numSubTables = len(self.tables)
totalOffset = 4 + 8 * numSubTables
data = struct.pack(">HH", self.tableVersion, numSubTables)
- tableData = ""
+ tableData = b""
seen = {} # Some tables are the same object reference. Don't compile them twice.
done = {} # Some tables are different objects, but compile to the same data chunk
for table in self.tables:
@@ -64,7 +64,7 @@
offset = seen[id(table.cmap)]
except KeyError:
chunk = table.compile(ttFont)
- if done.has_key(chunk):
+ if chunk in done:
offset = done[chunk]
else:
offset = seen[id(table.cmap)] = done[chunk] = totalOffset + len(tableData)
@@ -78,26 +78,26 @@
for table in self.tables:
table.toXML(writer, ttFont)
- def fromXML(self, (name, attrs, content), ttFont):
+ def fromXML(self, name, attrs, content, ttFont):
if name == "tableVersion":
self.tableVersion = safeEval(attrs["version"])
return
- if name[:12] <> "cmap_format_":
+ if name[:12] != "cmap_format_":
return
if not hasattr(self, "tables"):
self.tables = []
format = safeEval(name[12:])
- if not cmap_classes.has_key(format):
+ if format not in cmap_classes:
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)
+ table.fromXML(name, attrs, content, ttFont)
self.tables.append(table)
-class CmapSubtable:
+class CmapSubtable(object):
def __init__(self, format):
self.format = format
@@ -107,9 +107,9 @@
def __getattr__(self, attr):
# allow lazy decompilation of subtables.
if attr[:2] == '__': # don't handle requests for member functions like '__lt__'
- raise AttributeError, attr
- if self.data == None:
- raise AttributeError, attr
+ raise AttributeError(attr)
+ if self.data is None:
+ raise AttributeError(attr)
self.decompile(None, None) # use saved data.
self.data = None # Once this table has been decompiled, make sure we don't
# just return the original data. Also avoids recursion when
@@ -132,8 +132,7 @@
("language", self.language),
])
writer.newline()
- codes = self.cmap.items()
- codes.sort()
+ codes = sorted(self.cmap.items())
self._writeCodes(codes, writer)
writer.endtag(self.__class__.__name__)
writer.newline()
@@ -150,23 +149,22 @@
writer.comment(Unicode[code])
writer.newline()
- def __cmp__(self, other):
- if type(self) != type(other) or \
- self.__class__ != other.__class__:
- return cmp(id(self), id(other))
+ def __lt__(self, other):
+ if not isinstance(other, CmapSubtable):
+ return NotImplemented
- # implemented so that list.sort() sorts according to the cmap spec.
+ # implemented so that list.sort() sorts according to the spec.
selfTuple = (
- self.platformID,
- self.platEncID,
- self.language,
- self.__dict__)
+ getattr(self, "platformID", None),
+ getattr(self, "platEncID", None),
+ getattr(self, "language", None),
+ self.__dict__)
otherTuple = (
- other.platformID,
- other.platEncID,
- other.language,
- other.__dict__)
- return cmp(selfTuple, otherTuple)
+ getattr(other, "platformID", None),
+ getattr(other, "platEncID", None),
+ getattr(other, "language", None),
+ other.__dict__)
+ return selfTuple < otherTuple
class cmap_format_0(CmapSubtable):
@@ -174,30 +172,29 @@
def decompile(self, data, ttFont):
# we usually get here indirectly from the subtable __getattr__ function, in which case both args must be None.
# If not, someone is calling the subtable decompile() directly, and must provide both args.
- if data != None and ttFont != None:
+ if data is not None and ttFont is not None:
self.decompileHeader(data[offset:offset+int(length)], ttFont)
else:
- assert (data == None and ttFont == None), "Need both data and ttFont arguments"
+ assert (data is None and ttFont is None), "Need both data and ttFont arguments"
data = self.data # decompileHeader assigns the data after the header to self.data
assert 262 == self.length, "Format 0 cmap subtable not 262 bytes"
glyphIdArray = array.array("B")
glyphIdArray.fromstring(self.data)
self.cmap = cmap = {}
lenArray = len(glyphIdArray)
- charCodes = range(lenArray)
+ charCodes = list(range(lenArray))
names = map(self.ttFont.getGlyphName, glyphIdArray)
- map(operator.setitem, [cmap]*lenArray, charCodes, names)
+ list(map(operator.setitem, [cmap]*lenArray, charCodes, names))
def compile(self, ttFont):
if self.data:
return struct.pack(">HHH", 0, 262, self.language) + self.data
- charCodeList = self.cmap.items()
- charCodeList.sort()
+ charCodeList = sorted(self.cmap.items())
charCodes = [entry[0] for entry in charCodeList]
valueList = [entry[1] for entry in charCodeList]
- assert charCodes == range(256)
+ assert charCodes == list(range(256))
valueList = map(ttFont.getGlyphID, valueList)
glyphIdArray = array.array("B", valueList)
@@ -205,22 +202,22 @@
assert len(data) == 262
return data
- def fromXML(self, (name, attrs, content), ttFont):
+ def fromXML(self, name, attrs, content, ttFont):
self.language = safeEval(attrs["language"])
if not hasattr(self, "cmap"):
self.cmap = {}
cmap = self.cmap
for element in content:
- if type(element) <> TupleType:
+ if not isinstance(element, tuple):
continue
name, attrs, content = element
- if name <> "map":
+ if name != "map":
continue
cmap[safeEval(attrs["code"])] = attrs["name"]
subHeaderFormat = ">HHhH"
-class SubHeader:
+class SubHeader(object):
def __init__(self):
self.firstCode = None
self.entryCount = None
@@ -262,10 +259,10 @@
def decompile(self, data, ttFont):
# we usually get here indirectly from the subtable __getattr__ function, in which case both args must be None.
# If not, someone is calling the subtable decompile() directly, and must provide both args.
- if data != None and ttFont != None:
+ if data is not None and ttFont is not None:
self.decompileHeader(data[offset:offset+int(length)], ttFont)
else:
- assert (data == None and ttFont == None), "Need both data and ttFont arguments"
+ assert (data is None and ttFont is None), "Need both data and ttFont arguments"
data = self.data # decompileHeader assigns the data after the header to self.data
subHeaderKeys = []
@@ -274,9 +271,9 @@
allKeys = array.array("H")
allKeys.fromstring(data[:512])
data = data[512:]
- if sys.byteorder <> "big":
+ if sys.byteorder != "big":
allKeys.byteswap()
- subHeaderKeys = [ key/8 for key in allKeys]
+ subHeaderKeys = [ key//8 for key in allKeys]
maxSubHeaderindex = max(subHeaderKeys)
#Load subHeaders
@@ -290,7 +287,7 @@
giDataPos = pos + subHeader.idRangeOffset-2
giList = array.array("H")
giList.fromstring(data[giDataPos:giDataPos + subHeader.entryCount*2])
- if sys.byteorder <> "big":
+ if sys.byteorder != "big":
giList.byteswap()
subHeader.glyphIndexArray = giList
subHeaderList.append(subHeader)
@@ -331,7 +328,7 @@
# add it to the glyphID to get the final glyphIndex
# value. In this case the final glyph index = 3+ 42 -> 45 for the final glyphIndex. Whew!
- self.data = ""
+ self.data = b""
self.cmap = cmap = {}
notdefGI = 0
for firstByte in range(256):
@@ -365,15 +362,15 @@
# same as mapping it to .notdef.
# cmap values are GID's.
glyphOrder = self.ttFont.getGlyphOrder()
- gids = cmap.values()
- charCodes = cmap.keys()
+ gids = list(cmap.values())
+ charCodes = list(cmap.keys())
lenCmap = len(gids)
try:
- names = map(operator.getitem, [glyphOrder]*lenCmap, gids )
+ names = list(map(operator.getitem, [glyphOrder]*lenCmap, gids ))
except IndexError:
getGlyphName = self.ttFont.getGlyphName
- names = map(getGlyphName, gids )
- map(operator.setitem, [cmap]*lenCmap, charCodes, names)
+ names = list(map(getGlyphName, gids ))
+ list(map(operator.setitem, [cmap]*lenCmap, charCodes, names))
def compile(self, ttFont):
@@ -382,18 +379,17 @@
kEmptyTwoCharCodeRange = -1
notdefGI = 0
- items = self.cmap.items()
- items.sort()
+ items = sorted(self.cmap.items())
charCodes = [item[0] for item in items]
names = [item[1] for item in items]
nameMap = ttFont.getReverseGlyphMap()
lenCharCodes = len(charCodes)
try:
- gids = map(operator.getitem, [nameMap]*lenCharCodes, names)
+ gids = list(map(operator.getitem, [nameMap]*lenCharCodes, names))
except KeyError:
- nameMap = ttFont.getReverseGlyphMap(rebuild=1)
+ nameMap = ttFont.getReverseGlyphMap(rebuild=True)
try:
- gids = map(operator.getitem, [nameMap]*lenCharCodes, names)
+ gids = list(map(operator.getitem, [nameMap]*lenCharCodes, names))
except KeyError:
# allow virtual GIDs in format 2 tables
gids = []
@@ -519,22 +515,22 @@
for subhead in subHeaderList[:-1]:
for gi in subhead.glyphIndexArray:
dataList.append(struct.pack(">H", gi))
- data = "".join(dataList)
+ data = bytesjoin(dataList)
assert (len(data) == length), "Error: cmap format 2 is not same length as calculated! actual: " + str(len(data))+ " calc : " + str(length)
return data
- def fromXML(self, (name, attrs, content), ttFont):
+ def fromXML(self, name, attrs, content, ttFont):
self.language = safeEval(attrs["language"])
if not hasattr(self, "cmap"):
self.cmap = {}
cmap = self.cmap
for element in content:
- if type(element) <> TupleType:
+ if not isinstance(element, tuple):
continue
name, attrs, content = element
- if name <> "map":
+ if name != "map":
continue
cmap[safeEval(attrs["code"])] = attrs["name"]
@@ -632,22 +628,22 @@
def decompile(self, data, ttFont):
# we usually get here indirectly from the subtable __getattr__ function, in which case both args must be None.
# If not, someone is calling the subtable decompile() directly, and must provide both args.
- if data != None and ttFont != None:
+ if data is not None and ttFont is not None:
self.decompileHeader(self.data[offset:offset+int(length)], ttFont)
else:
- assert (data == None and ttFont == None), "Need both data and ttFont arguments"
+ assert (data is None and ttFont is None), "Need both data and ttFont arguments"
data = self.data # decompileHeader assigns the data after the header to self.data
(segCountX2, searchRange, entrySelector, rangeShift) = \
struct.unpack(">4H", data[:8])
data = data[8:]
- segCount = segCountX2 / 2
+ segCount = segCountX2 // 2
allCodes = array.array("H")
allCodes.fromstring(data)
self.data = data = None
- if sys.byteorder <> "big":
+ if sys.byteorder != "big":
allCodes.byteswap()
# divide the data
@@ -665,7 +661,7 @@
charCodes = []
gids = []
for i in range(len(startCode) - 1): # don't do 0xffff!
- rangeCharCodes = range(startCode[i], endCode[i] + 1)
+ rangeCharCodes = list(range(startCode[i], endCode[i] + 1))
charCodes = charCodes + rangeCharCodes
for charCode in rangeCharCodes:
rangeOffset = idRangeOffset[i]
@@ -673,9 +669,9 @@
glyphID = charCode + idDelta[i]
else:
# *someone* needs to get killed.
- index = idRangeOffset[i] / 2 + (charCode - startCode[i]) + i - len(idRangeOffset)
+ index = idRangeOffset[i] // 2 + (charCode - startCode[i]) + i - len(idRangeOffset)
assert (index < lenGIArray), "In format 4 cmap, range (%d), the calculated index (%d) into the glyph index array is not less than the length of the array (%d) !" % (i, index, lenGIArray)
- if glyphIndexArray[index] <> 0: # if not missing glyph
+ if glyphIndexArray[index] != 0: # if not missing glyph
glyphID = glyphIndexArray[index] + idDelta[i]
else:
glyphID = 0 # missing glyph
@@ -685,11 +681,11 @@
lenCmap = len(gids)
glyphOrder = self.ttFont.getGlyphOrder()
try:
- names = map(operator.getitem, [glyphOrder]*lenCmap, gids )
+ names = list(map(operator.getitem, [glyphOrder]*lenCmap, gids ))
except IndexError:
getGlyphName = self.ttFont.getGlyphName
- names = map(getGlyphName, gids )
- map(operator.setitem, [cmap]*lenCmap, charCodes, names)
+ names = list(map(getGlyphName, gids ))
+ list(map(operator.setitem, [cmap]*lenCmap, charCodes, names))
@@ -720,21 +716,21 @@
from fontTools.ttLib.sfnt import maxPowerOfTwo
- charCodes = self.cmap.keys()
+ charCodes = list(self.cmap.keys())
lenCharCodes = len(charCodes)
if lenCharCodes == 0:
startCode = [0xffff]
endCode = [0xffff]
else:
charCodes.sort()
- names = map(operator.getitem, [self.cmap]*lenCharCodes, charCodes)
+ names = list(map(operator.getitem, [self.cmap]*lenCharCodes, charCodes))
nameMap = ttFont.getReverseGlyphMap()
try:
- gids = map(operator.getitem, [nameMap]*lenCharCodes, names)
+ gids = list(map(operator.getitem, [nameMap]*lenCharCodes, names))
except KeyError:
- nameMap = ttFont.getReverseGlyphMap(rebuild=1)
+ nameMap = ttFont.getReverseGlyphMap(rebuild=True)
try:
- gids = map(operator.getitem, [nameMap]*lenCharCodes, names)
+ gids = list(map(operator.getitem, [nameMap]*lenCharCodes, names))
except KeyError:
# allow virtual GIDs in format 4 tables
gids = []
@@ -752,7 +748,7 @@
gids.append(gid)
cmap = {} # code:glyphID mapping
- map(operator.setitem, [cmap]*len(charCodes), charCodes, gids)
+ list(map(operator.setitem, [cmap]*len(charCodes), charCodes, gids))
# Build startCode and endCode lists.
# Split the char codes in ranges of consecutive char codes, then split
@@ -782,7 +778,7 @@
indices = []
for charCode in range(startCode[i], endCode[i] + 1):
indices.append(cmap[charCode])
- if (indices == range(indices[0], indices[0] + len(indices))):
+ if (indices == list(range(indices[0], indices[0] + len(indices)))):
idDeltaTemp = self.setIDDelta(indices[0] - startCode[i])
idDelta.append( idDeltaTemp)
idRangeOffset.append(0)
@@ -805,7 +801,7 @@
charCodeArray = array.array("H", endCode + [0] + startCode)
idDeltaeArray = array.array("h", idDelta)
restArray = array.array("H", idRangeOffset + glyphIndexArray)
- if sys.byteorder <> "big":
+ if sys.byteorder != "big":
charCodeArray.byteswap()
idDeltaeArray.byteswap()
restArray.byteswap()
@@ -816,17 +812,17 @@
segCountX2, searchRange, entrySelector, rangeShift)
return header + data
- def fromXML(self, (name, attrs, content), ttFont):
+ def fromXML(self, name, attrs, content, ttFont):
self.language = safeEval(attrs["language"])
if not hasattr(self, "cmap"):
self.cmap = {}
cmap = self.cmap
for element in content:
- if type(element) <> TupleType:
+ if not isinstance(element, tuple):
continue
nameMap, attrsMap, dummyContent = element
- if nameMap <> "map":
+ if nameMap != "map":
assert 0, "Unrecognized keyword in cmap subtable"
cmap[safeEval(attrsMap["code"])] = attrsMap["name"]
@@ -836,10 +832,10 @@
def decompile(self, data, ttFont):
# we usually get here indirectly from the subtable __getattr__ function, in which case both args must be None.
# If not, someone is calling the subtable decompile() directly, and must provide both args.
- if data != None and ttFont != None:
+ if data is not None and ttFont is not None:
self.decompileHeader(data[offset:offset+int(length)], ttFont)
else:
- assert (data == None and ttFont == None), "Need both data and ttFont arguments"
+ assert (data is None and ttFont is None), "Need both data and ttFont arguments"
data = self.data # decompileHeader assigns the data after the header to self.data
firstCode, entryCount = struct.unpack(">HH", data[:4])
@@ -848,61 +844,59 @@
#assert len(data) == 2 * entryCount # XXX not true in Apple's Helvetica!!!
glyphIndexArray = array.array("H")
glyphIndexArray.fromstring(data[:2 * int(entryCount)])
- if sys.byteorder <> "big":
+ if sys.byteorder != "big":
glyphIndexArray.byteswap()
self.data = data = None
self.cmap = cmap = {}
lenArray = len(glyphIndexArray)
- charCodes = range(firstCode, firstCode + lenArray )
+ charCodes = list(range(firstCode, firstCode + lenArray))
glyphOrder = self.ttFont.getGlyphOrder()
try:
- names = map(operator.getitem, [glyphOrder]*lenArray, glyphIndexArray )
+ names = list(map(operator.getitem, [glyphOrder]*lenArray, glyphIndexArray ))
except IndexError:
getGlyphName = self.ttFont.getGlyphName
- names = map(getGlyphName, glyphIndexArray )
- map(operator.setitem, [cmap]*lenArray, charCodes, names)
+ names = list(map(getGlyphName, glyphIndexArray ))
+ list(map(operator.setitem, [cmap]*lenArray, charCodes, names))
def compile(self, ttFont):
if self.data:
return struct.pack(">HHH", self.format, self.length, self.language) + self.data
cmap = self.cmap
- codes = cmap.keys()
+ codes = list(cmap.keys())
if codes: # yes, there are empty cmap tables.
- codes.sort()
- lenCodes = len(codes)
- assert codes == range(codes[0], codes[0] + lenCodes)
+ codes = list(range(codes[0], codes[-1] + 1))
firstCode = codes[0]
- valueList = map(operator.getitem, [cmap]*lenCodes, codes)
+ valueList = [cmap.get(code, ".notdef") for code in codes]
valueList = map(ttFont.getGlyphID, valueList)
glyphIndexArray = array.array("H", valueList)
- if sys.byteorder <> "big":
+ if sys.byteorder != "big":
glyphIndexArray.byteswap()
data = glyphIndexArray.tostring()
else:
- data = ""
+ data = b""
firstCode = 0
header = struct.pack(">HHHHH",
6, len(data) + 10, self.language, firstCode, len(codes))
return header + data
- def fromXML(self, (name, attrs, content), ttFont):
+ def fromXML(self, name, attrs, content, ttFont):
self.language = safeEval(attrs["language"])
if not hasattr(self, "cmap"):
self.cmap = {}
cmap = self.cmap
for element in content:
- if type(element) <> TupleType:
+ if not isinstance(element, tuple):
continue
name, attrs, content = element
- if name <> "map":
+ if name != "map":
continue
cmap[safeEval(attrs["code"])] = attrs["name"]
-class cmap_format_12(CmapSubtable):
+class cmap_format_12_or_13(CmapSubtable):
def __init__(self, format):
self.format = format
@@ -912,7 +906,7 @@
def decompileHeader(self, data, ttFont):
format, reserved, length, language, nGroups = struct.unpack(">HHLLL", data[:16])
- assert len(data) == (16 + nGroups*12) == (length), "corrupt cmap table format 12 (data length: %d, header length: %d)" % (len(data), length)
+ assert len(data) == (16 + nGroups*12) == (length), "corrupt cmap table format %d (data length: %d, header length: %d)" % (format, len(data), length)
self.format = format
self.reserved = reserved
self.length = length
@@ -924,10 +918,10 @@
def decompile(self, data, ttFont):
# we usually get here indirectly from the subtable __getattr__ function, in which case both args must be None.
# If not, someone is calling the subtable decompile() directly, and must provide both args.
- if data != None and ttFont != None:
+ if data is not None and ttFont is not None:
self.decompileHeader(data[offset:offset+int(length)], ttFont)
else:
- assert (data == None and ttFont == None), "Need both data and ttFont arguments"
+ assert (data is None and ttFont is None), "Need both data and ttFont arguments"
data = self.data # decompileHeader assigns the data after the header to self.data
charCodes = []
@@ -937,32 +931,32 @@
startCharCode, endCharCode, glyphID = struct.unpack(">LLL",data[pos:pos+12] )
pos += 12
lenGroup = 1 + endCharCode - startCharCode
- charCodes += range(startCharCode, endCharCode +1)
- gids += range(glyphID, glyphID + lenGroup)
+ charCodes += list(range(startCharCode, endCharCode +1))
+ gids += self._computeGIDs(glyphID, lenGroup)
self.data = data = None
self.cmap = cmap = {}
lenCmap = len(gids)
glyphOrder = self.ttFont.getGlyphOrder()
try:
- names = map(operator.getitem, [glyphOrder]*lenCmap, gids )
+ names = list(map(operator.getitem, [glyphOrder]*lenCmap, gids ))
except IndexError:
getGlyphName = self.ttFont.getGlyphName
- names = map(getGlyphName, gids )
- map(operator.setitem, [cmap]*lenCmap, charCodes, names)
+ names = list(map(getGlyphName, gids ))
+ list(map(operator.setitem, [cmap]*lenCmap, charCodes, names))
def compile(self, ttFont):
if self.data:
- return struct.pack(">HHLLL", self.format, self.reserved , self.length, self.language, self.nGroups) + self.data
- charCodes = self.cmap.keys()
+ return struct.pack(">HHLLL", self.format, self.reserved, self.length, self.language, self.nGroups) + self.data
+ charCodes = list(self.cmap.keys())
lenCharCodes = len(charCodes)
- names = self.cmap.values()
+ names = list(self.cmap.values())
nameMap = ttFont.getReverseGlyphMap()
try:
- gids = map(operator.getitem, [nameMap]*lenCharCodes, names)
+ gids = list(map(operator.getitem, [nameMap]*lenCharCodes, names))
except KeyError:
- nameMap = ttFont.getReverseGlyphMap(rebuild=1)
+ nameMap = ttFont.getReverseGlyphMap(rebuild=True)
try:
- gids = map(operator.getitem, [nameMap]*lenCharCodes, names)
+ gids = list(map(operator.getitem, [nameMap]*lenCharCodes, names))
except KeyError:
# allow virtual GIDs in format 12 tables
gids = []
@@ -981,13 +975,13 @@
gids.append(gid)
cmap = {} # code:glyphID mapping
- map(operator.setitem, [cmap]*len(charCodes), charCodes, gids)
+ list(map(operator.setitem, [cmap]*len(charCodes), charCodes, gids))
charCodes.sort()
index = 0
startCharCode = charCodes[0]
startGlyphID = cmap[startCharCode]
- lastGlyphID = startGlyphID - 1
+ lastGlyphID = startGlyphID - self._format_step
lastCharCode = startCharCode - 1
nGroups = 0
dataList = []
@@ -995,7 +989,7 @@
for index in range(maxIndex):
charCode = charCodes[index]
glyphID = cmap[charCode]
- if (glyphID != 1 + lastGlyphID) or (charCode != 1 + lastCharCode):
+ if not self._IsInSameRun(glyphID, lastGlyphID, charCode, lastCharCode):
dataList.append(struct.pack(">LLL", startCharCode, lastCharCode, startGlyphID))
startCharCode = charCode
startGlyphID = glyphID
@@ -1004,7 +998,7 @@
lastCharCode = charCode
dataList.append(struct.pack(">LLL", startCharCode, lastCharCode, startGlyphID))
nGroups = nGroups + 1
- data = "".join(dataList)
+ data = bytesjoin(dataList)
lengthSubtable = len(data) +16
assert len(data) == (nGroups*12) == (lengthSubtable-16)
return struct.pack(">HHLLL", self.format, self.reserved , lengthSubtable, self.language, nGroups) + data
@@ -1020,13 +1014,12 @@
("nGroups", self.nGroups),
])
writer.newline()
- codes = self.cmap.items()
- codes.sort()
+ codes = sorted(self.cmap.items())
self._writeCodes(codes, writer)
writer.endtag(self.__class__.__name__)
writer.newline()
- def fromXML(self, (name, attrs, content), ttFont):
+ def fromXML(self, name, attrs, content, ttFont):
self.format = safeEval(attrs["format"])
self.reserved = safeEval(attrs["reserved"])
self.length = safeEval(attrs["length"])
@@ -1037,44 +1030,49 @@
cmap = self.cmap
for element in content:
- if type(element) <> TupleType:
+ if not isinstance(element, tuple):
continue
name, attrs, content = element
- if name <> "map":
+ if name != "map":
continue
cmap[safeEval(attrs["code"])] = attrs["name"]
+class cmap_format_12(cmap_format_12_or_13):
+ def __init__(self, format):
+ cmap_format_12_or_13.__init__(self, format)
+ self._format_step = 1
+
+ def _computeGIDs(self, startingGlyph, numberOfGlyphs):
+ return list(range(startingGlyph, startingGlyph + numberOfGlyphs))
+
+ def _IsInSameRun(self, glyphID, lastGlyphID, charCode, lastCharCode):
+ return (glyphID == 1 + lastGlyphID) and (charCode == 1 + lastCharCode)
+
+
+class cmap_format_13(cmap_format_12_or_13):
+ def __init__(self, format):
+ cmap_format_12_or_13.__init__(self, format)
+ self._format_step = 0
+
+ def _computeGIDs(self, startingGlyph, numberOfGlyphs):
+ return [startingGlyph] * numberOfGlyphs
+
+ def _IsInSameRun(self, glyphID, lastGlyphID, charCode, lastCharCode):
+ return (glyphID == lastGlyphID) and (charCode == 1 + lastCharCode)
+
+
def cvtToUVS(threeByteString):
- if sys.byteorder <> "big":
- data = "\0" +threeByteString
- else:
- data = threeByteString + "\0"
+ data = b"\0" + threeByteString
val, = struct.unpack(">L", data)
return val
def cvtFromUVS(val):
- if sys.byteorder <> "big":
- threeByteString = struct.pack(">L", val)[1:]
- else:
- threeByteString = struct.pack(">L", val)[:3]
- return threeByteString
+ assert 0 <= val < 0x1000000
+ fourByteString = struct.pack(">L", val)
+ return fourByteString[1:]
-def cmpUVSListEntry(first, second):
- uv1, glyphName1 = first
- uv2, glyphName2 = second
-
- if (glyphName1 == None) and (glyphName2 != None):
- return -1
- elif (glyphName2 == None) and (glyphName1 != None):
- return 1
-
- ret = cmp(uv1, uv2)
- if ret:
- return ret
- return cmp(glyphName1, glyphName2)
-
-
+
class cmap_format_14(CmapSubtable):
def decompileHeader(self, data, ttFont):
@@ -1086,10 +1084,10 @@
self.language = 0xFF # has no language.
def decompile(self, data, ttFont):
- if data != None and ttFont != None:
+ if data is not None and ttFont is not None and ttFont.lazy:
self.decompileHeader(data, ttFont)
else:
- assert (data == None and ttFont == None), "Need both data and ttFont arguments"
+ assert (data is None and ttFont is None), "Need both data and ttFont arguments"
data = self.data
self.cmap = {} # so that clients that expect this to exist in a cmap table won't fail.
@@ -1108,13 +1106,13 @@
startOffset += 4
firstBaseUV = cvtToUVS(uv)
cnt = addtlCnt+1
- baseUVList = range(firstBaseUV, firstBaseUV+cnt)
+ baseUVList = list(range(firstBaseUV, firstBaseUV+cnt))
glyphList = [None]*cnt
localUVList = zip(baseUVList, glyphList)
try:
uvsDict[varUVS].extend(localUVList)
except KeyError:
- uvsDict[varUVS] = localUVList
+ uvsDict[varUVS] = list(localUVList)
if nonDefUVSOffset:
startOffset = nonDefUVSOffset - 10
@@ -1144,13 +1142,12 @@
])
writer.newline()
uvsDict = self.uvsDict
- uvsList = uvsDict.keys()
- uvsList.sort()
+ uvsList = sorted(uvsDict.keys())
for uvs in uvsList:
uvList = uvsDict[uvs]
- uvList.sort(cmpUVSListEntry)
+ uvList.sort(key=lambda item: (item[1] is not None, item[0], item[1]))
for uv, gname in uvList:
- if gname == None:
+ if gname is None:
gname = "None"
# I use the arg rather than th keyword syntax in order to preserve the attribute order.
writer.simpletag("map", [ ("uvs",hex(uvs)), ("uv",hex(uv)), ("name", gname)] )
@@ -1158,11 +1155,11 @@
writer.endtag(self.__class__.__name__)
writer.newline()
- def fromXML(self, (name, attrs, content), ttFont):
+ def fromXML(self, name, attrs, content, ttFont):
self.format = safeEval(attrs["format"])
self.length = safeEval(attrs["length"])
self.numVarSelectorRecords = safeEval(attrs["numVarSelectorRecords"])
- self.language = 0xFF # provide a value so that CmapSubtable.__cmp__() won't fail
+ self.language = 0xFF # provide a value so that CmapSubtable.__lt__() won't fail
if not hasattr(self, "cmap"):
self.cmap = {} # so that clients that expect this to exist in a cmap table won't fail.
if not hasattr(self, "uvsDict"):
@@ -1170,10 +1167,10 @@
uvsDict = self.uvsDict
for element in content:
- if type(element) <> TupleType:
+ if not isinstance(element, tuple):
continue
name, attrs, content = element
- if name <> "map":
+ if name != "map":
continue
uvs = safeEval(attrs["uvs"])
uv = safeEval(attrs["uv"])
@@ -1191,8 +1188,7 @@
return struct.pack(">HLL", self.format, self.length , self.numVarSelectorRecords) + self.data
uvsDict = self.uvsDict
- uvsList = uvsDict.keys()
- uvsList.sort()
+ uvsList = sorted(uvsDict.keys())
self.numVarSelectorRecords = len(uvsList)
offset = 10 + self.numVarSelectorRecords*11 # current value is end of VarSelectorRecords block.
data = []
@@ -1200,9 +1196,9 @@
for uvs in uvsList:
entryList = uvsDict[uvs]
- defList = filter(lambda entry: entry[1] == None, entryList)
+ defList = [entry for entry in entryList if entry[1] is None]
if defList:
- defList = map(lambda entry: entry[0], defList)
+ defList = [entry[0] for entry in defList]
defOVSOffset = offset
defList.sort()
@@ -1227,7 +1223,7 @@
else:
defOVSOffset = 0
- ndefList = filter(lambda entry: entry[1] != None, entryList)
+ ndefList = [entry for entry in entryList if entry[1] is not None]
if ndefList:
nonDefUVSOffset = offset
ndefList.sort()
@@ -1245,7 +1241,7 @@
vrec = struct.pack(">3sLL", cvtFromUVS(uvs), defOVSOffset, nonDefUVSOffset)
varSelectorRecords.append(vrec)
- data = "".join(varSelectorRecords) + "".join(data)
+ data = bytesjoin(varSelectorRecords) + bytesjoin(data)
self.length = 10 + len(data)
headerdata = struct.pack(">HLL", self.format, self.length , self.numVarSelectorRecords)
self.data = headerdata + data
@@ -1266,7 +1262,7 @@
writer.endtag(cmapName)
writer.newline()
- def fromXML(self, (name, attrs, content), ttFont):
+ def fromXML(self, name, attrs, content, ttFont):
self.data = readHex(content)
self.cmap = {}
@@ -1277,10 +1273,10 @@
def decompile(self, data, ttFont):
# we usually get here indirectly from the subtable __getattr__ function, in which case both args must be None.
# If not, someone is calling the subtable decompile() directly, and must provide both args.
- if data != None and ttFont != None:
+ if data is not None and ttFont is not None:
self.decompileHeader(data[offset:offset+int(length)], ttFont)
else:
- assert (data == None and ttFont == None), "Need both data and ttFont arguments"
+ assert (data is None and ttFont is None), "Need both data and ttFont arguments"
def compile(self, ttFont):
if self.data:
@@ -1294,5 +1290,6 @@
4: cmap_format_4,
6: cmap_format_6,
12: cmap_format_12,
+ 13: cmap_format_13,
14: cmap_format_14,
}
diff --git a/Lib/fontTools/ttLib/tables/_c_v_t.py b/Lib/fontTools/ttLib/tables/_c_v_t.py
index be08ca3..fc0ceb7 100644
--- a/Lib/fontTools/ttLib/tables/_c_v_t.py
+++ b/Lib/fontTools/ttLib/tables/_c_v_t.py
@@ -1,21 +1,22 @@
-import sys
-import DefaultTable
-import array
-from fontTools import ttLib
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
from fontTools.misc.textTools import safeEval
+from . import DefaultTable
+import sys
+import array
class table__c_v_t(DefaultTable.DefaultTable):
def decompile(self, data, ttFont):
values = array.array("h")
values.fromstring(data)
- if sys.byteorder <> "big":
+ if sys.byteorder != "big":
values.byteswap()
self.values = values
def compile(self, ttFont):
values = self.values[:]
- if sys.byteorder <> "big":
+ if sys.byteorder != "big":
values.byteswap()
return values.tostring()
@@ -25,7 +26,7 @@
writer.simpletag("cv", value=value, index=i)
writer.newline()
- def fromXML(self, (name, attrs, content), ttFont):
+ def fromXML(self, name, attrs, content, ttFont):
if not hasattr(self, "values"):
self.values = array.array("h")
if name == "cv":
diff --git a/Lib/fontTools/ttLib/tables/_f_p_g_m.py b/Lib/fontTools/ttLib/tables/_f_p_g_m.py
index 6f7beee..a6cebae 100644
--- a/Lib/fontTools/ttLib/tables/_f_p_g_m.py
+++ b/Lib/fontTools/ttLib/tables/_f_p_g_m.py
@@ -1,6 +1,7 @@
-import DefaultTable
-import array
-import ttProgram
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
+from . import DefaultTable
+from . import ttProgram
class table__f_p_g_m(DefaultTable.DefaultTable):
@@ -16,9 +17,9 @@
self.program.toXML(writer, ttFont)
writer.newline()
- def fromXML(self, (name, attrs, content), ttFont):
+ def fromXML(self, name, attrs, content, ttFont):
program = ttProgram.Program()
- program.fromXML((name, attrs, content), ttFont)
+ program.fromXML(name, attrs, content, ttFont)
self.program = program
def __len__(self):
diff --git a/Lib/fontTools/ttLib/tables/_g_a_s_p.py b/Lib/fontTools/ttLib/tables/_g_a_s_p.py
index 43f513f..5097a08 100644
--- a/Lib/fontTools/ttLib/tables/_g_a_s_p.py
+++ b/Lib/fontTools/ttLib/tables/_g_a_s_p.py
@@ -1,6 +1,8 @@
-import DefaultTable
-import struct
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
from fontTools.misc.textTools import safeEval
+from . import DefaultTable
+import struct
GASP_SYMMETRIC_GRIDFIT = 0x0004
@@ -24,9 +26,8 @@
def compile(self, ttFont):
version = 0 # ignore self.version
numRanges = len(self.gaspRange)
- data = ""
- items = self.gaspRange.items()
- items.sort()
+ data = b""
+ items = sorted(self.gaspRange.items())
for rangeMaxPPEM, rangeGaspBehavior in items:
data = data + struct.pack(">HH", rangeMaxPPEM, rangeGaspBehavior)
if rangeGaspBehavior & ~(GASP_GRIDFIT | GASP_DOGRAY):
@@ -35,16 +36,15 @@
return data
def toXML(self, writer, ttFont):
- items = self.gaspRange.items()
- items.sort()
+ items = sorted(self.gaspRange.items())
for rangeMaxPPEM, rangeGaspBehavior in items:
writer.simpletag("gaspRange", [
("rangeMaxPPEM", rangeMaxPPEM),
("rangeGaspBehavior", rangeGaspBehavior)])
writer.newline()
- def fromXML(self, (name, attrs, content), ttFont):
- if name <> "gaspRange":
+ def fromXML(self, name, attrs, content, ttFont):
+ if name != "gaspRange":
return
if not hasattr(self, "gaspRange"):
self.gaspRange = {}
diff --git a/Lib/fontTools/ttLib/tables/_g_l_y_f.py b/Lib/fontTools/ttLib/tables/_g_l_y_f.py
index 418d450..57a664e 100644
--- a/Lib/fontTools/ttLib/tables/_g_l_y_f.py
+++ b/Lib/fontTools/ttLib/tables/_g_l_y_f.py
@@ -1,6 +1,21 @@
"""_g_l_y_f.py -- Converter classes for the 'glyf' table."""
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
+from fontTools.misc import sstruct
+from fontTools import ttLib
+from fontTools.misc.textTools import safeEval
+from fontTools.misc.arrayTools import calcBounds, calcIntBounds, pointInRect
+from fontTools.misc.bezierTools import calcQuadraticBounds
+from fontTools.misc.fixedTools import fixedToFloat as fi2fl, floatToFixed as fl2fi
+from . import DefaultTable
+from . import ttProgram
+import sys
+import struct
+import array
+import warnings
+
#
# The Apple and MS rasterizers behave differently for
# scaled composite components: one does scale first and then translate
@@ -14,18 +29,6 @@
SCALE_COMPONENT_OFFSET_DEFAULT = 0 # 0 == MS, 1 == Apple
-import sys
-import struct
-from fontTools.misc import sstruct
-import DefaultTable
-from fontTools import ttLib
-from fontTools.misc.textTools import safeEval, readHex
-from fontTools.misc.arrayTools import calcBounds
-import ttProgram
-import array
-from types import StringType, TupleType
-import warnings
-
class table__g_l_y_f(DefaultTable.DefaultTable):
def decompile(self, data, ttFont):
@@ -42,8 +45,8 @@
glyphName = 'ttxautoglyph%s' % i
next = int(loca[i+1])
glyphdata = data[last:next]
- if len(glyphdata) <> (next - last):
- raise ttLib.TTLibError, "not enough 'glyf' table data"
+ if len(glyphdata) != (next - last):
+ raise ttLib.TTLibError("not enough 'glyf' table data")
glyph = Glyph(glyphdata)
self.glyphs[glyphName] = glyph
last = next
@@ -51,11 +54,13 @@
warnings.warn("too much 'glyf' table data")
if noname:
warnings.warn('%s glyphs have no name' % i)
+ if not ttFont.lazy:
+ for glyph in self.glyphs.values():
+ glyph.expand(self)
def compile(self, ttFont):
if not hasattr(self, "glyphOrder"):
self.glyphOrder = ttFont.getGlyphOrder()
- import string
locations = []
currentLocation = 0
dataList = []
@@ -67,8 +72,9 @@
currentLocation = currentLocation + len(glyphData)
dataList.append(glyphData)
locations.append(currentLocation)
- data = string.join(dataList, "")
- ttFont['loca'].set(locations)
+ data = bytesjoin(dataList)
+ if 'loca' in ttFont:
+ ttFont['loca'].set(locations)
ttFont['maxp'].numGlyphs = len(self.glyphs)
return data
@@ -84,7 +90,7 @@
for glyphName in glyphNames:
if not counter % progressStep and progress is not None:
progress.setLabel("Dumping 'glyf' table... (%s)" % glyphName)
- progress.increment(progressStep / float(numGlyphs))
+ progress.increment(progressStep / numGlyphs)
counter = counter + 1
glyph = self[glyphName]
if glyph.numberOfContours:
@@ -105,8 +111,8 @@
writer.newline()
writer.newline()
- def fromXML(self, (name, attrs, content), ttFont):
- if name <> "TTGlyph":
+ def fromXML(self, name, attrs, content, ttFont):
+ if name != "TTGlyph":
return
if not hasattr(self, "glyphs"):
self.glyphs = {}
@@ -120,9 +126,10 @@
setattr(glyph, attr, safeEval(attrs.get(attr, '0')))
self.glyphs[glyphName] = glyph
for element in content:
- if type(element) <> TupleType:
+ if not isinstance(element, tuple):
continue
- glyph.fromXML(element, ttFont)
+ name, attrs, content = element
+ glyph.fromXML(name, attrs, content, ttFont)
if not ttFont.recalcBBoxes:
glyph.compact(self, 0)
@@ -140,7 +147,7 @@
return self.glyphs.keys()
def has_key(self, glyphName):
- return self.glyphs.has_key(glyphName)
+ return glyphName in self.glyphs
__contains__ = has_key
@@ -198,7 +205,7 @@
UNSCALED_COMPONENT_OFFSET = 0x1000 # composite designed not to have the component offset scaled (designed for MS)
-class Glyph:
+class Glyph(object):
def __init__(self, data=""):
if not data:
@@ -207,7 +214,7 @@
return
self.data = data
- def compact(self, glyfTable, recalcBBoxes=1):
+ def compact(self, glyfTable, recalcBBoxes=True):
data = self.compile(glyfTable, recalcBBoxes)
self.__dict__.clear()
self.data = data
@@ -227,7 +234,7 @@
else:
self.decompileCoordinates(data)
- def compile(self, glyfTable, recalcBBoxes=1):
+ def compile(self, glyfTable, recalcBBoxes=True):
if hasattr(self, "data"):
return self.data
if self.numberOfContours == 0:
@@ -245,7 +252,7 @@
if len(data) % 4:
# add pad bytes
nPadBytes = 4 - (len(data) % 4)
- data = data + "\0" * nPadBytes
+ data = data + b"\0" * nPadBytes
return data
def toXML(self, writer, ttFont):
@@ -277,18 +284,18 @@
writer.endtag("instructions")
writer.newline()
- def fromXML(self, (name, attrs, content), ttFont):
+ def fromXML(self, name, attrs, content, ttFont):
if name == "contour":
if self.numberOfContours < 0:
- raise ttLib.TTLibError, "can't mix composites and contours in glyph"
+ raise ttLib.TTLibError("can't mix composites and contours in glyph")
self.numberOfContours = self.numberOfContours + 1
coordinates = GlyphCoordinates()
flags = []
for element in content:
- if type(element) <> TupleType:
+ if not isinstance(element, tuple):
continue
name, attrs, content = element
- if name <> "pt":
+ if name != "pt":
continue # ignore anything but "pt"
coordinates.append((safeEval(attrs["x"]), safeEval(attrs["y"])))
flags.append(not not safeEval(attrs["on"]))
@@ -303,19 +310,20 @@
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"
+ 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)
+ component.fromXML(name, attrs, content, ttFont)
elif name == "instructions":
self.program = ttProgram.Program()
for element in content:
- if type(element) <> TupleType:
+ if not isinstance(element, tuple):
continue
- self.program.fromXML(element, ttFont)
+ name, attrs, content = element
+ self.program.fromXML(name, attrs, content, ttFont)
def getCompositeMaxpValues(self, glyfTable, maxComponentDepth=1):
assert self.isComposite()
@@ -358,7 +366,7 @@
def decompileCoordinates(self, data):
endPtsOfContours = array.array("h")
endPtsOfContours.fromstring(data[:2*self.numberOfContours])
- if sys.byteorder <> "big":
+ if sys.byteorder != "big":
endPtsOfContours.byteswap()
self.endPtsOfContours = endPtsOfContours.tolist()
@@ -419,12 +427,12 @@
xFormat = ">" # big endian
yFormat = ">" # big endian
i = j = 0
- while 1:
- flag = ord(data[i])
+ while True:
+ flag = byteord(data[i])
i = i + 1
repeat = 1
if flag & flagRepeat:
- repeat = ord(data[i]) + 1
+ repeat = byteord(data[i]) + 1
i = i + 1
for k in range(repeat):
if flag & flagXShort:
@@ -451,7 +459,7 @@
return flags, xCoordinates, yCoordinates
def compileComponents(self, glyfTable):
- data = ""
+ data = b""
lastcomponent = len(self.components) - 1
more = 1
haveInstructions = 0
@@ -469,9 +477,9 @@
def compileCoordinates(self):
assert len(self.coordinates) == len(self.flags)
- data = ""
+ data = b""
endPtsOfContours = array.array("h", self.endPtsOfContours)
- if sys.byteorder <> "big":
+ if sys.byteorder != "big":
endPtsOfContours.byteswap()
data = data + endPtsOfContours.tostring()
instructions = self.program.getBytecode()
@@ -521,30 +529,81 @@
yPoints.append(y)
yFormat = yFormat + 'h'
# handle repeating flags
- if flag == lastflag:
+ if flag == lastflag and repeat != 255:
repeat = repeat + 1
if repeat == 1:
compressedflags.append(flag)
- elif repeat > 1:
- compressedflags[-2] = flag | flagRepeat
- compressedflags[-1] = repeat
else:
+ compressedflags[-2] = flag | flagRepeat
compressedflags[-1] = repeat
else:
repeat = 0
compressedflags.append(flag)
lastflag = flag
data = data + array.array("B", compressedflags).tostring()
- xPoints = map(int, xPoints) # work around struct >= 2.5 bug
- yPoints = map(int, yPoints)
- data = data + apply(struct.pack, (xFormat,)+tuple(xPoints))
- data = data + apply(struct.pack, (yFormat,)+tuple(yPoints))
+ if coordinates.isFloat():
+ # Warn?
+ xPoints = [int(round(x)) for x in xPoints]
+ yPoints = [int(round(y)) for y in xPoints]
+ data = data + struct.pack(*(xFormat,)+tuple(xPoints))
+ data = data + struct.pack(*(yFormat,)+tuple(yPoints))
return data
def recalcBounds(self, glyfTable):
- coordinates, endPts, flags = self.getCoordinates(glyfTable)
- if len(coordinates) > 0:
- self.xMin, self.yMin, self.xMax, self.yMax = calcBounds(coordinates)
+ coords, endPts, flags = self.getCoordinates(glyfTable)
+ if len(coords) > 0:
+ if 0:
+ # This branch calculates exact glyph outline bounds
+ # analytically, handling cases without on-curve
+ # extremas, etc. However, the glyf table header
+ # simply says that the bounds should be min/max x/y
+ # "for coordinate data", so I suppose that means no
+ # fancy thing here, just get extremas of all coord
+ # points (on and off). As such, this branch is
+ # disabled.
+
+ # Collect on-curve points
+ onCurveCoords = [coords[j] for j in range(len(coords))
+ if flags[j] & flagOnCurve]
+ # Add implicit on-curve points
+ start = 0
+ for end in endPts:
+ last = end
+ for j in range(start, end + 1):
+ if not ((flags[j] | flags[last]) & flagOnCurve):
+ x = (coords[last][0] + coords[j][0]) / 2
+ y = (coords[last][1] + coords[j][1]) / 2
+ onCurveCoords.append((x,y))
+ last = j
+ start = end + 1
+ # Add bounds for curves without an explicit extrema
+ start = 0
+ for end in endPts:
+ last = end
+ for j in range(start, end + 1):
+ if not (flags[j] & flagOnCurve):
+ next = j + 1 if j < end else start
+ bbox = calcBounds([coords[last], coords[next]])
+ if not pointInRect(coords[j], bbox):
+ # Ouch!
+ warnings.warn("Outline has curve with implicit extrema.")
+ # Ouch! Find analytical curve bounds.
+ pthis = coords[j]
+ plast = coords[last]
+ if not (flags[last] & flagOnCurve):
+ plast = ((pthis[0]+plast[0])/2, (pthis[1]+plast[1])/2)
+ pnext = coords[next]
+ if not (flags[next] & flagOnCurve):
+ pnext = ((pthis[0]+pnext[0])/2, (pthis[1]+pnext[1])/2)
+ bbox = calcQuadraticBounds(plast, pthis, pnext)
+ onCurveCoords.append((bbox[0],bbox[1]))
+ onCurveCoords.append((bbox[2],bbox[3]))
+ last = j
+ start = end + 1
+
+ self.xMin, self.yMin, self.xMax, self.yMax = calcIntBounds(onCurveCoords)
+ else:
+ self.xMin, self.yMin, self.xMax, self.yMax = calcIntBounds(coords)
else:
self.xMin, self.yMin, self.xMax, self.yMax = (0, 0, 0, 0)
@@ -557,7 +616,7 @@
def __getitem__(self, componentIndex):
if not self.isComposite():
- raise ttLib.TTLibError, "can't use glyph as sequence"
+ raise ttLib.TTLibError("can't use glyph as sequence")
return self.components[componentIndex]
def getCoordinates(self, glyfTable):
@@ -639,7 +698,7 @@
def removeHinting(self):
if not hasattr(self, "data"):
- self.program = ttLib.tables.ttProgram.Program()
+ self.program = ttProgram.Program()
self.program.fromBytecode([])
return
@@ -668,7 +727,7 @@
# padding.
coordBytes = 0
j = 0
- while 1:
+ while True:
flag = data[i]
i = i + 1
repeat = 1
@@ -716,19 +775,19 @@
if len(data) % 4:
# add pad bytes
nPadBytes = 4 - (len(data) % 4)
- data = data + "\0" * nPadBytes
+ data = data + b"\0" * nPadBytes
self.data = data
-
- def __cmp__(self, other):
- if type(self) != type(other) or \
- self.__class__ != other.__class__:
- return cmp(id(self), id(other))
- return cmp(self.__dict__, other.__dict__)
+ def __ne__(self, other):
+ return not self.__eq__(other)
+ def __eq__(self, other):
+ if type(self) != type(other):
+ return NotImplemented
+ return self.__dict__ == other.__dict__
-class GlyphComponent:
+class GlyphComponent(object):
def __init__(self):
pass
@@ -754,7 +813,6 @@
self.glyphName = glyfTable.getGlyphName(int(glyphID))
#print ">>", reprflag(self.flags)
data = data[4:]
- x4000 = float(0x4000)
if self.flags & ARG_1_AND_2_ARE_WORDS:
if self.flags & ARGS_ARE_XY_VALUES:
@@ -773,17 +831,17 @@
if self.flags & WE_HAVE_A_SCALE:
scale, = struct.unpack(">h", data[:2])
- self.transform = [[scale/x4000, 0], [0, scale/x4000]] # fixed 2.14
+ self.transform = [[fi2fl(scale,14), 0], [0, fi2fl(scale,14)]] # 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 = [[xscale/x4000, 0], [0, yscale/x4000]] # fixed 2.14
+ self.transform = [[fi2fl(xscale,14), 0], [0, fi2fl(yscale,14)]] # 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 = [[xscale/x4000, scale01/x4000],
- [scale10/x4000, yscale/x4000]] # fixed 2.14
+ self.transform = [[fi2fl(xscale,14), fi2fl(scale01,14)],
+ [fi2fl(scale10,14), fi2fl(yscale,14)]] # fixed 2.14
data = data[8:]
more = self.flags & MORE_COMPONENTS
haveInstructions = self.flags & WE_HAVE_INSTRUCTIONS
@@ -793,7 +851,7 @@
return more, haveInstructions, data
def compile(self, more, haveInstructions, glyfTable):
- data = ""
+ data = b""
# reset all flags we will calculate ourselves
flags = self.flags & (ROUND_XY_TO_GRID | USE_MY_METRICS |
@@ -819,13 +877,13 @@
flags = flags | ARG_1_AND_2_ARE_WORDS
if hasattr(self, "transform"):
- transform = [[int(x * 0x4000 + .5) for x in row] for row in self.transform]
+ transform = [[fl2fi(x,14) for x in row] for row in self.transform]
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]:
+ 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])
@@ -851,7 +909,7 @@
("scalex", transform[0][0]), ("scale01", transform[0][1]),
("scale10", transform[1][0]), ("scaley", transform[1][1]),
]
- elif transform[0][0] <> transform[1][1]:
+ elif transform[0][0] != transform[1][1]:
attrs = attrs + [
("scalex", transform[0][0]), ("scaley", transform[1][1]),
]
@@ -861,42 +919,57 @@
writer.simpletag("component", attrs)
writer.newline()
- def fromXML(self, (name, attrs, content), ttFont):
+ def fromXML(self, name, attrs, content, ttFont):
self.glyphName = attrs["glyphName"]
- if attrs.has_key("firstPt"):
+ if "firstPt" in attrs:
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"):
+ if "scale01" in attrs:
scalex = safeEval(attrs["scalex"])
scale01 = safeEval(attrs["scale01"])
scale10 = safeEval(attrs["scale10"])
scaley = safeEval(attrs["scaley"])
self.transform = [[scalex, scale01], [scale10, scaley]]
- elif attrs.has_key("scalex"):
+ elif "scalex" in attrs:
scalex = safeEval(attrs["scalex"])
scaley = safeEval(attrs["scaley"])
self.transform = [[scalex, 0], [0, scaley]]
- elif attrs.has_key("scale"):
+ elif "scale" in attrs:
scale = safeEval(attrs["scale"])
self.transform = [[scale, 0], [0, scale]]
self.flags = safeEval(attrs["flags"])
- def __cmp__(self, other):
- if type(self) != type(other) or \
- self.__class__ != other.__class__:
- return cmp(id(self), id(other))
+ def __ne__(self, other):
+ return not self.__eq__(other)
+ def __eq__(self, other):
+ if type(self) != type(other):
+ return NotImplemented
+ return self.__dict__ == other.__dict__
- return cmp(self.__dict__, other.__dict__)
-
-class GlyphCoordinates:
+class GlyphCoordinates(object):
def __init__(self, iterable=[]):
self._a = array.array("h")
self.extend(iterable)
+ def isFloat(self):
+ return self._a.typecode == 'f'
+
+ def _ensureFloat(self):
+ if self.isFloat():
+ return
+ self._a = array.array("f", self._a)
+
+ def _checkFloat(self, p):
+ if any(isinstance(v, float) for v in p):
+ p = [int(v) if int(v) == v else v for v in p]
+ if any(isinstance(v, float) for v in p):
+ self._ensureFloat()
+ return p
+
@staticmethod
def zeros(count):
return GlyphCoordinates([(0,0)] * count)
@@ -907,43 +980,46 @@
return c
def __len__(self):
- return len(self._a) / 2
+ return len(self._a) // 2
def __getitem__(self, k):
if isinstance(k, slice):
- indices = xrange(*k.indices(len(self)))
+ indices = range(*k.indices(len(self)))
return [self[i] for i in indices]
return self._a[2*k],self._a[2*k+1]
def __setitem__(self, k, v):
if isinstance(k, slice):
- indices = xrange(*k.indices(len(self)))
+ indices = range(*k.indices(len(self)))
for j,i in enumerate(indices):
self[i] = v[j]
return
+ v = self._checkFloat(v)
self._a[2*k],self._a[2*k+1] = v
def __repr__(self):
return 'GlyphCoordinates(['+','.join(str(c) for c in self)+'])'
- def append(self, (x,y)):
- self._a.extend((x,y))
+ def append(self, p):
+ p = self._checkFloat(p)
+ self._a.extend(tuple(p))
def extend(self, iterable):
- for x,y in iterable:
- self._a.extend((x,y))
+ for p in iterable:
+ p = self._checkFloat(p)
+ self._a.extend(p)
def relativeToAbsolute(self):
a = self._a
x,y = 0,0
- for i in range(len(a) / 2):
+ for i in range(len(a) // 2):
a[2*i ] = x = a[2*i ] + x
a[2*i+1] = y = a[2*i+1] + y
def absoluteToRelative(self):
a = self._a
x,y = 0,0
- for i in range(len(a) / 2):
+ for i in range(len(a) // 2):
dx = a[2*i ] - x
dy = a[2*i+1] - y
x = a[2*i ]
@@ -951,25 +1027,34 @@
a[2*i ] = dx
a[2*i+1] = dy
- def translate(self, (x,y)):
+ def translate(self, p):
+ (x,y) = p
a = self._a
- for i in range(len(a) / 2):
+ for i in range(len(a) // 2):
a[2*i ] += x
a[2*i+1] += y
def transform(self, t):
a = self._a
- for i in range(len(a) / 2):
+ for i in range(len(a) // 2):
x = a[2*i ]
y = a[2*i+1]
- a[2*i ] = int(.5 + x * t[0][0] + y * t[1][0])
- a[2*i+1] = int(.5 + x * t[0][1] + y * t[1][1])
+ px = x * t[0][0] + y * t[1][0]
+ py = x * t[0][1] + y * t[1][1]
+ self[i] = (px, py)
+
+ def __ne__(self, other):
+ return not self.__eq__(other)
+ def __eq__(self, other):
+ if type(self) != type(other):
+ return NotImplemented
+ return self._a == other._a
def reprflag(flag):
bin = ""
- if type(flag) == StringType:
- flag = ord(flag)
+ if isinstance(flag, str):
+ flag = byteord(flag)
while flag:
if flag & 0x01:
bin = "1" + bin
diff --git a/Lib/fontTools/ttLib/tables/_h_d_m_x.py b/Lib/fontTools/ttLib/tables/_h_d_m_x.py
index 592f426..f9fd557 100644
--- a/Lib/fontTools/ttLib/tables/_h_d_m_x.py
+++ b/Lib/fontTools/ttLib/tables/_h_d_m_x.py
@@ -1,6 +1,7 @@
-import DefaultTable
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
from fontTools.misc import sstruct
-import string
+from . import DefaultTable
hdmxHeaderFormat = """
> # big endian!
@@ -17,11 +18,11 @@
dummy, data = sstruct.unpack2(hdmxHeaderFormat, data, self)
self.hdmx = {}
for i in range(self.numRecords):
- ppem = ord(data[0])
- maxSize = ord(data[1])
+ ppem = byteord(data[0])
+ maxSize = byteord(data[1])
widths = {}
for glyphID in range(numGlyphs):
- widths[glyphOrder[glyphID]] = ord(data[glyphID+2])
+ widths[glyphOrder[glyphID]] = byteord(data[glyphID+2])
self.hdmx[ppem] = widths
data = data[self.recordSize:]
assert len(data) == 0, "too much hdmx data"
@@ -30,25 +31,23 @@
self.version = 0
numGlyphs = ttFont['maxp'].numGlyphs
glyphOrder = ttFont.getGlyphOrder()
- self.recordSize = 4 * ((2 + numGlyphs + 3) / 4)
- pad = (self.recordSize - 2 - numGlyphs) * "\0"
+ self.recordSize = 4 * ((2 + numGlyphs + 3) // 4)
+ pad = (self.recordSize - 2 - numGlyphs) * b"\0"
self.numRecords = len(self.hdmx)
data = sstruct.pack(hdmxHeaderFormat, self)
- items = self.hdmx.items()
- items.sort()
+ items = sorted(self.hdmx.items())
for ppem, widths in items:
- data = data + chr(ppem) + chr(max(widths.values()))
+ data = data + bytechr(ppem) + bytechr(max(widths.values()))
for glyphID in range(len(glyphOrder)):
width = widths[glyphOrder[glyphID]]
- data = data + chr(width)
+ data = data + bytechr(width)
data = data + pad
return data
def toXML(self, writer, ttFont):
writer.begintag("hdmxData")
writer.newline()
- ppems = self.hdmx.keys()
- ppems.sort()
+ ppems = sorted(self.hdmx.keys())
records = []
format = ""
for ppem in ppems:
@@ -58,7 +57,7 @@
glyphNames = ttFont.getGlyphOrder()[:]
glyphNames.sort()
maxNameLen = max(map(len, glyphNames))
- format = "%" + `maxNameLen` + 's:' + format + ' ;'
+ format = "%" + repr(maxNameLen) + 's:' + format + ' ;'
writer.write(format % (("ppem",) + tuple(ppems)))
writer.newline()
writer.newline()
@@ -74,18 +73,18 @@
writer.endtag("hdmxData")
writer.newline()
- def fromXML(self, (name, attrs, content), ttFont):
- if name <> "hdmxData":
+ def fromXML(self, name, attrs, content, ttFont):
+ if name != "hdmxData":
return
- content = string.join(content, "")
- lines = string.split(content, ";")
- topRow = string.split(lines[0])
+ content = strjoin(content)
+ lines = content.split(";")
+ topRow = lines[0].split()
assert topRow[0] == "ppem:", "illegal hdmx format"
- ppems = map(int, topRow[1:])
+ ppems = list(map(int, topRow[1:]))
self.hdmx = hdmx = {}
for ppem in ppems:
hdmx[ppem] = {}
- lines = map(string.split, lines[1:])
+ lines = (line.split() for line in lines[1:])
for line in lines:
if not line:
continue
@@ -94,7 +93,7 @@
if "\\" in glyphName:
from fontTools.misc.textTools import safeEval
glyphName = safeEval('"""' + glyphName + '"""')
- line = map(int, line[1:])
+ line = list(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
index 060930f..1c2e366 100644
--- a/Lib/fontTools/ttLib/tables/_h_e_a_d.py
+++ b/Lib/fontTools/ttLib/tables/_h_e_a_d.py
@@ -1,8 +1,10 @@
-import DefaultTable
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
from fontTools.misc import sstruct
-import time
-import string
from fontTools.misc.textTools import safeEval, num2binary, binary2num
+from . import DefaultTable
+import time
+import calendar
headFormat = """
@@ -13,8 +15,8 @@
magicNumber: I
flags: H
unitsPerEm: H
- created: 8s
- modified: 8s
+ created: Q
+ modified: Q
xMin: h
yMin: h
xMax: h
@@ -35,25 +37,12 @@
if rest:
# this is quite illegal, but there seem to be fonts out there that do this
assert rest == "\0\0"
- self.unitsPerEm = int(self.unitsPerEm)
- self.flags = int(self.flags)
- self.strings2dates()
def compile(self, ttFont):
- self.modified = long(time.time() - mac_epoch_diff)
- self.dates2strings()
+ self.modified = int(time.time() - mac_epoch_diff)
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()
@@ -67,7 +56,7 @@
value = time.asctime(time.gmtime(0))
if name in ("magicNumber", "checkSumAdjustment"):
if value < 0:
- value = value + 0x100000000L
+ value = value + 0x100000000
value = hex(value)
if value[-1:] == "L":
value = value[:-1]
@@ -76,81 +65,16 @@
writer.simpletag(name, value=value)
writer.newline()
- def fromXML(self, (name, attrs, content), ttFont):
+ def fromXML(self, name, attrs, content, ttFont):
value = attrs["value"]
if name in ("created", "modified"):
- value = parse_date(value) - mac_epoch_diff
+ value = calendar.timegm(time.strptime(value)) - mac_epoch_diff
elif name in ("macStyle", "flags"):
value = binary2num(value)
else:
- try:
- value = safeEval(value)
- except OverflowError:
- value = long(value)
+ value = safeEval(value)
setattr(self, name, value)
-
- def __cmp__(self, other):
- if type(self) != type(other) or \
- self.__class__ != other.__class__:
- return cmp(id(self), id(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 = (1972, 1, 1, 0, 0, 0, 0, 0, 0)
- safe_epoch = time.mktime(safe_epoch_t) - time.timezone
- # This assert fails in certain time zones, with certain daylight settings
- #assert time.gmtime(safe_epoch)[:6] == safe_epoch_t[:6]
- seconds1904to1972 = 60 * 60 * 24 * (365 * (1972-1904) + 17) # thanks, Laurence!
- return long(safe_epoch - seconds1904to1972)
-
-mac_epoch_diff = calc_mac_epoch_diff()
-
-
-_months = [' ', 'jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', 'aug',
- 'sep', 'oct', 'nov', 'dec']
-_weekdays = ['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun']
-
-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)
- try:
- return long(time.mktime(t) - time.timezone)
- except OverflowError:
- return 0L
-
-
-def bin2long(data):
- # thanks </F>!
- v = 0L
- for i in map(ord, data):
- v = v<<8 | i
- return v
-
-def long2bin(v, bytes=8):
- mask = long("FF" * bytes, 16)
- data = ""
- while v:
- data = chr(v & 0xff) + data
- v = (v >> 8) & mask
- data = (bytes - len(data)) * "\0" + data
- assert len(data) == 8, "long too long"
- return data
-
+# Difference between the original Mac epoch (1904) to the epoch on this machine.
+mac_epoch_diff = calendar.timegm((1904, 1, 1, 0, 0, 0, 0, 0, 0))
diff --git a/Lib/fontTools/ttLib/tables/_h_h_e_a.py b/Lib/fontTools/ttLib/tables/_h_h_e_a.py
index a8decfd..0f0dfbc 100644
--- a/Lib/fontTools/ttLib/tables/_h_h_e_a.py
+++ b/Lib/fontTools/ttLib/tables/_h_h_e_a.py
@@ -1,6 +1,8 @@
-import DefaultTable
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
from fontTools.misc import sstruct
from fontTools.misc.textTools import safeEval
+from . import DefaultTable
hheaFormat = """
@@ -39,24 +41,36 @@
def recalc(self, ttFont):
hmtxTable = ttFont['hmtx']
- if ttFont.has_key('glyf'):
+ if 'glyf' in ttFont:
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
+ INFINITY = 100000
+ advanceWidthMax = 0
+ minLeftSideBearing = +INFINITY # arbitrary big number
+ minRightSideBearing = +INFINITY # arbitrary big number
+ xMaxExtent = -INFINITY # 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)
+ g = glyfTable[name]
+ if g.numberOfContours == 0:
+ continue
+ if g.numberOfContours < 0 and not hasattr(g, "xMax"):
+ # Composite glyph without extents set.
+ # Calculate those.
+ g.recalcBounds(glyfTable)
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)
+
+ if xMaxExtent == -INFINITY:
+ # No glyph has outlines.
+ minLeftSideBearing = 0
+ minRightSideBearing = 0
+ xMaxExtent = 0
+
self.advanceWidthMax = advanceWidthMax
self.minLeftSideBearing = minLeftSideBearing
self.minRightSideBearing = minRightSideBearing
@@ -69,11 +83,9 @@
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):
+ 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
index 475a93f..a17215f 100644
--- a/Lib/fontTools/ttLib/tables/_h_m_t_x.py
+++ b/Lib/fontTools/ttLib/tables/_h_m_t_x.py
@@ -1,8 +1,9 @@
-import sys
-import DefaultTable
-import array
-from fontTools import ttLib
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
from fontTools.misc.textTools import safeEval
+from . import DefaultTable
+import sys
+import array
import warnings
@@ -20,17 +21,17 @@
numberOfMetrics = numGlyphs # We warn later.
# Note: advanceWidth is unsigned, but we read/write as signed.
metrics = array.array("h", data[:4 * numberOfMetrics])
- if sys.byteorder <> "big":
+ if sys.byteorder != "big":
metrics.byteswap()
data = data[4 * numberOfMetrics:]
numberOfSideBearings = numGlyphs - numberOfMetrics
sideBearings = array.array("h", data[:2 * numberOfSideBearings])
data = data[2 * numberOfSideBearings:]
- if sys.byteorder <> "big":
+ if sys.byteorder != "big":
sideBearings.byteswap()
if data:
- sys.stderr.write("too much data for hmtx/vmtx table\n")
+ warnings.warn("too much 'hmtx'/'vmtx' table data")
self.metrics = {}
for i in range(numberOfMetrics):
glyphName = ttFont.getGlyphName(i)
@@ -57,21 +58,22 @@
metrics = metrics[:lastIndex]
setattr(ttFont[self.headerTag], self.numberOfMetricsName, len(metrics))
- metrics = sum(metrics,[])
- metrics = array.array("h", metrics)
- if sys.byteorder <> "big":
- metrics.byteswap()
- data = metrics.tostring()
+ allMetrics = []
+ for item in metrics:
+ allMetrics.extend(item)
+ allMetrics = array.array("h", allMetrics)
+ if sys.byteorder != "big":
+ allMetrics.byteswap()
+ data = allMetrics.tostring()
additionalMetrics = array.array("h", additionalMetrics)
- if sys.byteorder <> "big":
+ if sys.byteorder != "big":
additionalMetrics.byteswap()
data = data + additionalMetrics.tostring()
return data
def toXML(self, writer, ttFont):
- names = self.metrics.keys()
- names.sort()
+ names = sorted(self.metrics.keys())
for glyphName in names:
advance, sb = self.metrics[glyphName]
writer.simpletag("mtx", [
@@ -81,7 +83,7 @@
])
writer.newline()
- def fromXML(self, (name, attrs, content), ttFont):
+ def fromXML(self, name, attrs, content, ttFont):
if not hasattr(self, "metrics"):
self.metrics = {}
if name == "mtx":
@@ -91,6 +93,6 @@
def __getitem__(self, glyphName):
return self.metrics[glyphName]
- def __setitem__(self, glyphName, (advance, sb)):
- self.metrics[glyphName] = advance, sb
+ def __setitem__(self, glyphName, advance_sb_pair):
+ self.metrics[glyphName] = tuple(advance_sb_pair)
diff --git a/Lib/fontTools/ttLib/tables/_k_e_r_n.py b/Lib/fontTools/ttLib/tables/_k_e_r_n.py
index 005d1ed..4ee9fc4 100644
--- a/Lib/fontTools/ttLib/tables/_k_e_r_n.py
+++ b/Lib/fontTools/ttLib/tables/_k_e_r_n.py
@@ -1,8 +1,10 @@
-import DefaultTable
-import struct
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
from fontTools.ttLib import sfnt
from fontTools.misc.textTools import safeEval, readHex
-from types import TupleType
+from fontTools.misc.fixedTools import fixedToFloat as fi2fl, floatToFixed as fl2fi
+from . import DefaultTable
+import struct
import warnings
@@ -20,7 +22,7 @@
if (len(data) >= 8) and (version == 1):
# AAT Apple's "new" format. Hm.
version, nTables = struct.unpack(">LL", data[:8])
- self.version = version / float(0x10000)
+ self.version = fi2fl(version, 16)
data = data[8:]
apple = True
else:
@@ -36,7 +38,7 @@
else:
version, length = struct.unpack(">HH", data[:4])
length = int(length)
- if not kern_classes.has_key(version):
+ if version not in kern_classes:
subtable = KernTable_format_unkown(version)
else:
subtable = kern_classes[version]()
@@ -52,7 +54,7 @@
nTables = 0
if self.version == 1.0:
# AAT Apple's "new" format.
- data = struct.pack(">ll", self.version * 0x10000, nTables)
+ data = struct.pack(">ll", fl2fi(self.version, 16), nTables)
else:
data = struct.pack(">HH", self.version, nTables)
if hasattr(self, "kernTables"):
@@ -66,24 +68,24 @@
for subtable in self.kernTables:
subtable.toXML(writer, ttFont)
- def fromXML(self, (name, attrs, content), ttFont):
+ def fromXML(self, name, attrs, content, ttFont):
if name == "version":
self.version = safeEval(attrs["value"])
return
- if name <> "kernsubtable":
+ if name != "kernsubtable":
return
if not hasattr(self, "kernTables"):
self.kernTables = []
format = safeEval(attrs["format"])
- if not kern_classes.has_key(format):
+ if format not in kern_classes:
subtable = KernTable_format_unkown(format)
else:
subtable = kern_classes[format]()
self.kernTables.append(subtable)
- subtable.fromXML((name, attrs, content), ttFont)
+ subtable.fromXML(name, attrs, content, ttFont)
-class KernTable_format_0:
+class KernTable_format_0(object):
def decompile(self, data, ttFont):
version, length, coverage = (0,0,0)
@@ -103,7 +105,7 @@
for k in range(nPairs):
if len(data) < 6:
# buggy kern table
- data = ""
+ data = b""
break
left, right, value = struct.unpack(">HHh", data[:6])
data = data[6:]
@@ -120,10 +122,8 @@
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()
+ getGlyphID = ttFont.getGlyphID
+ kernTable = sorted((getGlyphID(left), getGlyphID(right), value) for ((left,right),value) in self.kernTable.items())
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
@@ -131,8 +131,7 @@
def toXML(self, writer, ttFont):
writer.begintag("kernsubtable", coverage=self.coverage, format=0)
writer.newline()
- items = self.kernTable.items()
- items.sort()
+ items = sorted(self.kernTable.items())
for (left, right), value in items:
writer.simpletag("pair", [
("l", left),
@@ -143,13 +142,13 @@
writer.endtag("kernsubtable")
writer.newline()
- def fromXML(self, (name, attrs, content), ttFont):
+ 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) <> TupleType:
+ if not isinstance(element, tuple):
continue
name, attrs, content = element
self.kernTable[(attrs["l"], attrs["r"])] = safeEval(attrs["v"])
@@ -162,16 +161,9 @@
def __delitem__(self, pair):
del self.kernTable[pair]
-
- def __cmp__(self, other):
- if type(self) != type(other) or \
- self.__class__ != other.__class__:
- return cmp(id(self), id(other))
-
- return cmp(self.__dict__, other.__dict__)
-class KernTable_format_2:
+class KernTable_format_2(object):
def decompile(self, data, ttFont):
self.data = data
@@ -186,11 +178,11 @@
writer.endtag("kernsubtable")
writer.newline()
- def fromXML(self, (name, attrs, content), ttFont):
+ def fromXML(self, name, attrs, content, ttFont):
self.decompile(readHex(content), ttFont)
-class KernTable_format_unkown:
+class KernTable_format_unkown(object):
def __init__(self, format):
self.format = format
@@ -210,7 +202,7 @@
writer.endtag("kernsubtable")
writer.newline()
- def fromXML(self, (name, attrs, content), ttFont):
+ def fromXML(self, name, attrs, content, ttFont):
self.decompile(readHex(content), ttFont)
diff --git a/Lib/fontTools/ttLib/tables/_l_o_c_a.py b/Lib/fontTools/ttLib/tables/_l_o_c_a.py
index 761ab9b..daa9d03 100644
--- a/Lib/fontTools/ttLib/tables/_l_o_c_a.py
+++ b/Lib/fontTools/ttLib/tables/_l_o_c_a.py
@@ -1,8 +1,8 @@
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
+from . import DefaultTable
import sys
-import DefaultTable
import array
-from fontTools import ttLib
-import struct
import warnings
class table__l_o_c_a(DefaultTable.DefaultTable):
@@ -17,7 +17,7 @@
format = "H"
locations = array.array(format)
locations.fromstring(data)
- if sys.byteorder <> "big":
+ if sys.byteorder != "big":
locations.byteswap()
if not longFormat:
l = array.array("I")
@@ -29,15 +29,20 @@
self.locations = locations
def compile(self, ttFont):
- if max(self.locations) < 0x20000:
+ try:
+ max_location = max(self.locations)
+ except AttributeError:
+ self.set([])
+ max_location = 0
+ if max_location < 0x20000:
locations = array.array("H")
for i in range(len(self.locations)):
- locations.append(self.locations[i] / 2)
+ locations.append(self.locations[i] // 2)
ttFont['head'].indexToLocFormat = 0
else:
locations = array.array("I", self.locations)
ttFont['head'].indexToLocFormat = 1
- if sys.byteorder <> "big":
+ if sys.byteorder != "big":
locations.byteswap()
return locations.tostring()
@@ -53,11 +58,4 @@
def __len__(self):
return len(self.locations)
-
- def __cmp__(self, other):
- if type(self) != type(other) or \
- self.__class__ != other.__class__:
- return cmp(id(self), id(other))
-
- return cmp(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
index 0f0d3f3..2f42ae5 100644
--- a/Lib/fontTools/ttLib/tables/_m_a_x_p.py
+++ b/Lib/fontTools/ttLib/tables/_m_a_x_p.py
@@ -1,6 +1,8 @@
-import DefaultTable
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
from fontTools.misc import sstruct
from fontTools.misc.textTools import safeEval
+from . import DefaultTable
maxpFormat_0_5 = """
> # big endian
@@ -38,7 +40,7 @@
assert len(data) == 0
def compile(self, ttFont):
- if ttFont.has_key('glyf'):
+ if 'glyf' in ttFont:
if ttFont.isLoaded('glyf') and ttFont.recalcBBoxes:
self.recalc(ttFont)
else:
@@ -60,10 +62,11 @@
hmtxTable = ttFont['hmtx']
headTable = ttFont['head']
self.numGlyphs = len(glyfTable)
- xMin = 100000
- yMin = 100000
- xMax = -100000
- yMax = -100000
+ INFINITY = 100000
+ xMin = +INFINITY
+ yMin = +INFINITY
+ xMax = -INFINITY
+ yMax = -INFINITY
maxPoints = 0
maxContours = 0
maxCompositePoints = 0
@@ -74,7 +77,7 @@
for glyphName in ttFont.getGlyphOrder():
g = glyfTable[glyphName]
if g.numberOfContours:
- if hmtxTable[glyphName][1] <> g.xMin:
+ if hmtxTable[glyphName][1] != g.xMin:
allXMaxIsLsb = 0
xMin = min(xMin, g.xMin)
yMin = min(yMin, g.yMin)
@@ -90,10 +93,16 @@
maxCompositeContours = max(maxCompositeContours, nContours)
maxComponentElements = max(maxComponentElements, len(g.components))
maxComponentDepth = max(maxComponentDepth, componentDepth)
- headTable.xMin = xMin
- headTable.yMin = yMin
- headTable.xMax = xMax
- headTable.yMax = yMax
+ if xMin == +INFINITY:
+ headTable.xMin = 0
+ headTable.yMin = 0
+ headTable.xMax = 0
+ headTable.yMax = 0
+ else:
+ headTable.xMin = xMin
+ headTable.yMin = yMin
+ headTable.xMax = xMax
+ headTable.yMax = yMax
self.maxPoints = maxPoints
self.maxContours = maxContours
self.maxCompositePoints = maxCompositePoints
@@ -105,12 +114,11 @@
headTable.flags = headTable.flags & ~0x2
def testrepr(self):
- items = self.__dict__.items()
- items.sort()
- print ". . . . . . . . ."
+ items = sorted(self.__dict__.items())
+ print(". . . . . . . . .")
for combo in items:
- print " %s: %s" % combo
- print ". . . . . . . . ."
+ print(" %s: %s" % combo)
+ print(". . . . . . . . .")
def toXML(self, writer, ttFont):
if self.tableVersion != 0x00005000:
@@ -122,14 +130,12 @@
names = names + names_1_0
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):
+ 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
index 307fbb5..b45ecc9 100644
--- a/Lib/fontTools/ttLib/tables/_n_a_m_e.py
+++ b/Lib/fontTools/ttLib/tables/_n_a_m_e.py
@@ -1,9 +1,9 @@
-import DefaultTable
-import struct
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
from fontTools.misc import sstruct
from fontTools.misc.textTools import safeEval
-import string
-import types
+from . import DefaultTable
+import struct
nameRecordFormat = """
> # big endian
@@ -25,8 +25,7 @@
expectedStringOffset = 6 + n * nameRecordSize
if stringOffset != expectedStringOffset:
# XXX we need a warn function
- print "Warning: 'name' table stringOffset incorrect.",
- print "Expected: %s; Actual: %s" % (expectedStringOffset, stringOffset)
+ print("Warning: 'name' table stringOffset incorrect. Expected: %s; Actual: %s" % (expectedStringOffset, stringOffset))
stringData = data[stringOffset:]
data = data[6:]
self.names = []
@@ -49,8 +48,8 @@
# only happens when there are NO name table entries read
# from the TTX file
self.names = []
- self.names.sort() # sort according to the spec; see NameRecord.__cmp__()
- stringData = ""
+ self.names.sort() # sort according to the spec; see NameRecord.__lt__()
+ stringData = b""
format = 0
n = len(self.names)
stringOffset = 6 + n * sstruct.calcsize(nameRecordFormat)
@@ -58,7 +57,7 @@
lastoffset = 0
done = {} # remember the data so we can reuse the "pointers"
for name in self.names:
- if done.has_key(name.string):
+ if name.string in done:
name.offset, name.length = done[name.string]
else:
name.offset, name.length = done[name.string] = len(stringData), len(name.string)
@@ -70,14 +69,14 @@
for name in self.names:
name.toXML(writer, ttFont)
- def fromXML(self, (name, attrs, content), ttFont):
- if name <> "namerecord":
+ 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)
+ name.fromXML(name, attrs, content, ttFont)
def getName(self, nameID, platformID, platEncID, langID=None):
for namerecord in self.names:
@@ -87,16 +86,9 @@
if langID is None or namerecord.langID == langID:
return namerecord
return None # not found
-
- def __cmp__(self, other):
- if type(self) != type(other) or \
- self.__class__ != other.__class__:
- return cmp(id(self), id(other))
- return cmp(self.names, other.names)
-
-class NameRecord:
+class NameRecord(object):
def toXML(self, writer, ttFont):
writer.begintag("namerecord", [
@@ -110,50 +102,47 @@
if len(self.string) % 2:
# no, shouldn't happen, but some of the Apple
# tools cause this anyway :-(
- writer.write16bit(self.string + "\0")
+ writer.write16bit(self.string + b"\0", strip=True)
else:
- writer.write16bit(self.string)
+ writer.write16bit(self.string, strip=True)
else:
- writer.write8bit(self.string)
+ writer.write8bit(self.string, strip=True)
writer.newline()
writer.endtag("namerecord")
writer.newline()
- def fromXML(self, (name, attrs, content), ttFont):
+ 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"])
+ s = strjoin(content).strip()
if self.platformID == 0 or (self.platformID == 3 and self.platEncID in (0, 1)):
- s = ""
- for element in content:
- s = s + element
- s = unicode(s, "utf8")
- s = s.strip()
self.string = s.encode("utf_16_be")
else:
- s = string.strip(string.join(content, ""))
- self.string = unicode(s, "utf8").encode("latin1")
+ # This is the inverse of write8bit...
+ self.string = s.encode("latin1")
- def __cmp__(self, other):
- """Compare method, so a list of NameRecords can be sorted
- according to the spec by just sorting it..."""
+ def __lt__(self, other):
+ if type(self) != type(other):
+ return NotImplemented
- if type(self) != type(other) or \
- self.__class__ != other.__class__:
- return cmp(id(self), id(other))
-
- 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)
+ # implemented so that list.sort() sorts according to the spec.
+ selfTuple = (
+ getattr(self, "platformID", None),
+ getattr(self, "platEncID", None),
+ getattr(self, "langID", None),
+ getattr(self, "nameID", None),
+ getattr(self, "string", None),
+ )
+ otherTuple = (
+ getattr(other, "platformID", None),
+ getattr(other, "platEncID", None),
+ getattr(other, "langID", None),
+ getattr(other, "nameID", None),
+ getattr(other, "string", None),
+ )
+ return selfTuple < otherTuple
def __repr__(self):
return "<NameRecord NameID=%d; PlatformID=%d; LanguageID=%d>" % (
diff --git a/Lib/fontTools/ttLib/tables/_p_o_s_t.py b/Lib/fontTools/ttLib/tables/_p_o_s_t.py
index 82e91bc..abaaa09 100644
--- a/Lib/fontTools/ttLib/tables/_p_o_s_t.py
+++ b/Lib/fontTools/ttLib/tables/_p_o_s_t.py
@@ -1,12 +1,13 @@
-import sys
-from fontTools.ttLib.standardGlyphOrder import standardGlyphOrder
-import DefaultTable
-import struct
-from fontTools.misc import sstruct
-import array
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
from fontTools import ttLib
+from fontTools.ttLib.standardGlyphOrder import standardGlyphOrder
+from fontTools.misc import sstruct
from fontTools.misc.textTools import safeEval, readHex
-from types import TupleType
+from . import DefaultTable
+import sys
+import struct
+import array
postFormat = """
@@ -38,7 +39,7 @@
self.decode_format_3_0(data, ttFont)
else:
# supported format
- raise ttLib.TTLibError, "'post' table format %f not supported" % self.formatType
+ raise ttLib.TTLibError("'post' table format %f not supported" % self.formatType)
def compile(self, ttFont):
data = sstruct.pack(postFormat, self)
@@ -50,7 +51,7 @@
pass # we're done
else:
# supported format
- raise ttLib.TTLibError, "'post' table format %f not supported" % self.formatType
+ raise ttLib.TTLibError("'post' table format %f not supported" % self.formatType)
return data
def getGlyphOrder(self):
@@ -59,7 +60,7 @@
or its relatives instead!
"""
if not hasattr(self, "glyphOrder"):
- raise ttLib.TTLibError, "illegal use of getGlyphOrder()"
+ raise ttLib.TTLibError("illegal use of getGlyphOrder()")
glyphOrder = self.glyphOrder
del self.glyphOrder
return glyphOrder
@@ -79,7 +80,7 @@
data = data[2:]
indices = array.array("H")
indices.fromstring(data[:2*numGlyphs])
- if sys.byteorder <> "big":
+ if sys.byteorder != "big":
indices.byteswap()
data = data[2*numGlyphs:]
self.extraNames = extraNames = unpackPStrings(data)
@@ -105,11 +106,11 @@
allNames = {}
for i in range(ttFont['maxp'].numGlyphs):
glyphName = psName = self.glyphOrder[i]
- if allNames.has_key(glyphName):
+ if glyphName in allNames:
# make up a new glyphName that's unique
n = allNames[glyphName]
allNames[glyphName] = n + 1
- glyphName = glyphName + "#" + `n`
+ glyphName = glyphName + "#" + repr(n)
self.glyphOrder[i] = glyphName
mapping[glyphName] = psName
else:
@@ -132,11 +133,11 @@
extraDict[extraNames[i]] = i
for glyphID in range(numGlyphs):
glyphName = glyphOrder[glyphID]
- if self.mapping.has_key(glyphName):
+ if glyphName in self.mapping:
psName = self.mapping[glyphName]
else:
psName = glyphName
- if extraDict.has_key(psName):
+ if psName in extraDict:
index = 258 + extraDict[psName]
elif psName in standardGlyphOrder:
index = standardGlyphOrder.index(psName)
@@ -145,7 +146,7 @@
extraDict[psName] = len(extraNames)
extraNames.append(psName)
indices.append(index)
- if sys.byteorder <> "big":
+ if sys.byteorder != "big":
indices.byteswap()
return struct.pack(">H", numGlyphs) + indices.tostring() + packPStrings(extraNames)
@@ -165,8 +166,7 @@
"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()
+ items = sorted(self.mapping.items())
for name, psName in items:
writer.simpletag("psName", name=name, psName=psName)
writer.newline()
@@ -189,13 +189,13 @@
writer.endtag("hexdata")
writer.newline()
- def fromXML(self, (name, attrs, content), ttFont):
+ 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) <> TupleType:
+ if not isinstance(element, tuple):
continue
name, attrs, content = element
if name == "psName":
@@ -203,7 +203,7 @@
elif name == "extraNames":
self.extraNames = []
for element in content:
- if type(element) <> TupleType:
+ if not isinstance(element, tuple):
continue
name, attrs, content = element
if name == "psName":
@@ -217,15 +217,15 @@
index = 0
dataLen = len(data)
while index < dataLen:
- length = ord(data[index])
- strings.append(data[index+1:index+1+length])
+ length = byteord(data[index])
+ strings.append(tostr(data[index+1:index+1+length], encoding="latin1"))
index = index + 1 + length
return strings
def packPStrings(strings):
- data = ""
+ data = b""
for s in strings:
- data = data + chr(len(s)) + s
+ data = data + bytechr(len(s)) + tobytes(s, encoding="latin1")
return data
diff --git a/Lib/fontTools/ttLib/tables/_s_b_i_x.py b/Lib/fontTools/ttLib/tables/_s_b_i_x.py
new file mode 100644
index 0000000..35c7e50
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/_s_b_i_x.py
@@ -0,0 +1,142 @@
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
+from fontTools.misc import sstruct
+from fontTools.misc.textTools import readHex
+from . import DefaultTable
+from .sbixBitmap import *
+from .sbixBitmapSet import *
+import struct
+
+"""
+sbix Table organization:
+
+USHORT version?
+USHORT version?
+USHORT count number of bitmap sets
+offsetEntry offsetEntry[count] offsetEntries
+(Variable) storage for bitmap sets
+
+
+offsetEntry:
+
+ULONG offset offset from table start to bitmap set
+
+
+bitmap set:
+
+USHORT size height and width in pixels
+USHORT resolution ?
+offsetRecord offsetRecord[]
+(Variable) storage for bitmaps
+
+
+offsetRecord:
+
+ULONG bitmapOffset offset from start of bitmap set to individual bitmap
+
+
+bitmap:
+
+ULONG reserved 00 00 00 00
+char[4] format data type, e.g. "png "
+(Variable) bitmap data
+"""
+
+sbixHeaderFormat = """
+ >
+ usVal1: H # 00 01
+ usVal2: H # 00 01
+ numSets: L # 00 00 00 02 # number of bitmap sets
+"""
+sbixHeaderFormatSize = sstruct.calcsize(sbixHeaderFormat)
+
+
+sbixBitmapSetOffsetFormat = """
+ >
+ offset: L # 00 00 00 10 # offset from table start to each bitmap set
+"""
+sbixBitmapSetOffsetFormatSize = sstruct.calcsize(sbixBitmapSetOffsetFormat)
+
+
+class table__s_b_i_x(DefaultTable.DefaultTable):
+ def __init__(self, tag):
+ self.tableTag = tag
+ self.usVal1 = 1
+ self.usVal2 = 1
+ self.numSets = 0
+ self.bitmapSets = {}
+ self.bitmapSetOffsets = []
+
+ def decompile(self, data, ttFont):
+ # read table header
+ sstruct.unpack(sbixHeaderFormat, data[ : sbixHeaderFormatSize], self)
+ # collect offsets to individual bitmap sets in self.bitmapSetOffsets
+ for i in range(self.numSets):
+ myOffset = sbixHeaderFormatSize + i * sbixBitmapSetOffsetFormatSize
+ offsetEntry = sbixBitmapSetOffset()
+ sstruct.unpack(sbixBitmapSetOffsetFormat, \
+ data[myOffset : myOffset+sbixBitmapSetOffsetFormatSize], \
+ offsetEntry)
+ self.bitmapSetOffsets.append(offsetEntry.offset)
+
+ # decompile BitmapSets
+ for i in range(self.numSets-1, -1, -1):
+ myBitmapSet = BitmapSet(rawdata=data[self.bitmapSetOffsets[i]:])
+ data = data[:self.bitmapSetOffsets[i]]
+ myBitmapSet.decompile(ttFont)
+ #print " BitmapSet length: %xh" % len(bitmapSetData)
+ #print "Number of Bitmaps:", myBitmapSet.numBitmaps
+ if myBitmapSet.size in self.bitmapSets:
+ from fontTools import ttLib
+ raise ttLib.TTLibError("Pixel 'size' must be unique for each BitmapSet")
+ self.bitmapSets[myBitmapSet.size] = myBitmapSet
+
+ # after the bitmaps have been extracted, we don't need the offsets anymore
+ del self.bitmapSetOffsets
+
+ def compile(self, ttFont):
+ sbixData = ""
+ self.numSets = len(self.bitmapSets)
+ sbixHeader = sstruct.pack(sbixHeaderFormat, self)
+
+ # calculate offset to start of first bitmap set
+ setOffset = sbixHeaderFormatSize + sbixBitmapSetOffsetFormatSize * self.numSets
+
+ for si in sorted(self.bitmapSets.keys()):
+ myBitmapSet = self.bitmapSets[si]
+ myBitmapSet.compile(ttFont)
+ # append offset to this bitmap set to table header
+ myBitmapSet.offset = setOffset
+ sbixHeader += sstruct.pack(sbixBitmapSetOffsetFormat, myBitmapSet)
+ setOffset += sbixBitmapSetHeaderFormatSize + len(myBitmapSet.data)
+ sbixData += myBitmapSet.data
+
+ return sbixHeader + sbixData
+
+ def toXML(self, xmlWriter, ttFont):
+ xmlWriter.simpletag("usVal1", value=self.usVal1)
+ xmlWriter.newline()
+ xmlWriter.simpletag("usVal2", value=self.usVal2)
+ xmlWriter.newline()
+ for i in sorted(self.bitmapSets.keys()):
+ self.bitmapSets[i].toXML(xmlWriter, ttFont)
+
+ def fromXML(self, name, attrs, content, ttFont):
+ if name in ["usVal1", "usVal2"]:
+ setattr(self, name, int(attrs["value"]))
+ elif name == "bitmapSet":
+ myBitmapSet = BitmapSet()
+ for element in content:
+ if isinstance(element, tuple):
+ name, attrs, content = element
+ myBitmapSet.fromXML(name, attrs, content, ttFont)
+ self.bitmapSets[myBitmapSet.size] = myBitmapSet
+ else:
+ from fontTools import ttLib
+ raise ttLib.TTLibError("can't handle '%s' element" % name)
+
+
+# Helper classes
+
+class sbixBitmapSetOffset(object):
+ pass
diff --git a/Lib/fontTools/ttLib/tables/_v_h_e_a.py b/Lib/fontTools/ttLib/tables/_v_h_e_a.py
index 9adc63b..0ed0b7a 100644
--- a/Lib/fontTools/ttLib/tables/_v_h_e_a.py
+++ b/Lib/fontTools/ttLib/tables/_v_h_e_a.py
@@ -1,6 +1,8 @@
-import DefaultTable
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
from fontTools.misc import sstruct
from fontTools.misc.textTools import safeEval
+from . import DefaultTable
vheaFormat = """
> # big endian
@@ -36,7 +38,7 @@
def recalc(self, ttFont):
vtmxTable = ttFont['vmtx']
- if ttFont.has_key('glyf'):
+ if 'glyf' in ttFont:
if not ttFont.isLoaded('glyf'):
return
glyfTable = ttFont['glyf']
@@ -68,11 +70,9 @@
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):
+ def fromXML(self, name, attrs, content, ttFont):
setattr(self, name, safeEval(attrs["value"]))
diff --git a/Lib/fontTools/ttLib/tables/asciiTable.py b/Lib/fontTools/ttLib/tables/asciiTable.py
index ee9455d..87ef62a 100644
--- a/Lib/fontTools/ttLib/tables/asciiTable.py
+++ b/Lib/fontTools/ttLib/tables/asciiTable.py
@@ -1,22 +1,23 @@
-import string
-import DefaultTable
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
+from . import DefaultTable
class asciiTable(DefaultTable.DefaultTable):
def toXML(self, writer, ttFont):
- data = self.data
+ data = tostr(self.data)
# removing null bytes. XXX needed??
- data = string.split(data, '\0')
- data = string.join(data, '')
+ data = data.split('\0')
+ data = strjoin(data)
writer.begintag("source")
writer.newline()
- writer.write_noindent(string.replace(data, "\r", "\n"))
+ writer.write_noindent(data.replace("\r", "\n"))
writer.newline()
writer.endtag("source")
writer.newline()
- def fromXML(self, (name, attrs, content), ttFont):
- lines = string.split(string.replace(string.join(content, ""), "\r", "\n"), "\n")
- self.data = string.join(lines[1:-1], "\r")
+ def fromXML(self, name, attrs, content, ttFont):
+ lines = strjoin(content).replace("\r", "\n").split("\n")
+ self.data = tobytes("\r".join(lines[1:-1]))
diff --git a/Lib/fontTools/ttLib/tables/otBase.py b/Lib/fontTools/ttLib/tables/otBase.py
index 0f6a4e8..6a7a63b 100644
--- a/Lib/fontTools/ttLib/tables/otBase.py
+++ b/Lib/fontTools/ttLib/tables/otBase.py
@@ -1,9 +1,9 @@
-from DefaultTable import DefaultTable
-import otData
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
+from .DefaultTable import DefaultTable
import struct
-from types import TupleType
-class OverflowErrorRecord:
+class OverflowErrorRecord(object):
def __init__(self, overflowTuple):
self.tableType = overflowTuple[0]
self.LookupListIndex = overflowTuple[1]
@@ -30,22 +30,27 @@
"""
def decompile(self, data, font):
- import otTables
- cachingStats = None
- reader = OTTableReader(data, self.tableTag, cachingStats=cachingStats)
+ from . import otTables
+ cachingStats = None if True else {}
+ class GlobalState(object):
+ def __init__(self, tableType, cachingStats):
+ self.tableType = tableType
+ self.cachingStats = cachingStats
+ globalState = GlobalState(tableType=self.tableTag,
+ cachingStats=cachingStats)
+ reader = OTTableReader(data, globalState)
tableClass = getattr(otTables, self.tableTag)
self.table = tableClass()
self.table.decompile(reader, font)
- if 0:
- stats = [(v, k) for k, v in cachingStats.items()]
- stats.sort()
+ if cachingStats:
+ stats = sorted([(v, k) for k, v in cachingStats.items()])
stats.reverse()
- print "cachingsstats for ", self.tableTag
+ print("cachingsstats for ", self.tableTag)
for v, k in stats:
if v < 2:
break
- print v, k
- print "---", len(stats)
+ print(v, k)
+ print("---", len(stats))
def compile(self, font):
""" Create a top-level OTFWriter for the GPOS/GSUB table.
@@ -68,7 +73,11 @@
If a lookup subtable overflows an offset, we have to start all over.
"""
- writer = OTTableWriter(self.tableTag)
+ class GlobalState(object):
+ def __init__(self, tableType):
+ self.tableType = tableType
+ globalState = GlobalState(tableType=self.tableTag)
+ writer = OTTableWriter(globalState)
writer.parent = None
self.table.compile(writer, font)
return writer.getAllData()
@@ -76,61 +85,62 @@
def toXML(self, writer, font):
self.table.toXML2(writer, font)
- def fromXML(self, (name, attrs, content), font):
- import otTables
+ def fromXML(self, name, attrs, content, font):
+ from . import otTables
if not hasattr(self, "table"):
tableClass = getattr(otTables, self.tableTag)
self.table = tableClass()
- self.table.fromXML((name, attrs, content), font)
+ self.table.fromXML(name, attrs, content, font)
-class OTTableReader:
-
+class OTTableReader(object):
+
"""Helper class to retrieve data from an OpenType table."""
-
- def __init__(self, data, tableType, offset=0, valueFormat=None, cachingStats=None):
+
+ __slots__ = ('data', 'offset', 'pos', 'globalState', 'localState')
+
+ def __init__(self, data, globalState={}, localState=None, offset=0):
self.data = data
self.offset = offset
self.pos = offset
- self.tableType = tableType
- if valueFormat is None:
- valueFormat = (ValueRecordFactory(), ValueRecordFactory())
- self.valueFormat = valueFormat
- self.cachingStats = cachingStats
-
+ self.globalState = globalState
+ self.localState = localState
+
def getSubReader(self, offset):
offset = self.offset + offset
- if self.cachingStats is not None:
- try:
- self.cachingStats[offset] = self.cachingStats[offset] + 1
- except KeyError:
- self.cachingStats[offset] = 1
-
- subReader = self.__class__(self.data, self.tableType, offset,
- self.valueFormat, self.cachingStats)
- return subReader
-
+ cachingStats = self.globalState.cachingStats
+ if cachingStats is not None:
+ cachingStats[offset] = cachingStats.get(offset, 0) + 1
+ return self.__class__(self.data, self.globalState, self.localState, offset)
+
def readUShort(self):
pos = self.pos
newpos = pos + 2
value, = struct.unpack(">H", self.data[pos:newpos])
self.pos = newpos
return value
-
+
def readShort(self):
pos = self.pos
newpos = pos + 2
value, = struct.unpack(">h", self.data[pos:newpos])
self.pos = newpos
return value
-
+
def readLong(self):
pos = self.pos
newpos = pos + 4
value, = struct.unpack(">l", self.data[pos:newpos])
self.pos = newpos
return value
-
+
+ def readUInt24(self):
+ pos = self.pos
+ newpos = pos + 3
+ value, = struct.unpack(">l", b'\0'+self.data[pos:newpos])
+ self.pos = newpos
+ return value
+
def readULong(self):
pos = self.pos
newpos = pos + 4
@@ -141,41 +151,38 @@
def readTag(self):
pos = self.pos
newpos = pos + 4
- value = self.data[pos:newpos]
+ value = Tag(self.data[pos:newpos])
assert len(value) == 4
self.pos = newpos
return value
-
- 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 setValueFormat(self, format, which):
- self.valueFormat[which].setFormat(format)
-
- def readValueRecord(self, font, which):
- return self.valueFormat[which].readValueRecord(self, font)
+
+ def __setitem__(self, name, value):
+ state = self.localState.copy() if self.localState else dict()
+ state[name] = value
+ self.localState = state
+
+ def __getitem__(self, name):
+ return self.localState[name]
-class OTTableWriter:
+class OTTableWriter(object):
"""Helper class to gather and assemble data for OpenType tables."""
- def __init__(self, tableType, valueFormat=None):
+ def __init__(self, globalState, localState=None):
self.items = []
- self.tableType = tableType
- if valueFormat is None:
- valueFormat = ValueRecordFactory(), ValueRecordFactory()
- self.valueFormat = valueFormat
self.pos = None
-
+ self.globalState = globalState
+ self.localState = localState
+
+ def __setitem__(self, name, value):
+ state = self.localState.copy() if self.localState else dict()
+ state[name] = value
+ self.localState = state
+
+ def __getitem__(self, name):
+ return self.localState[name]
+
# assembler interface
def getAllData(self):
@@ -205,18 +212,14 @@
tableData = table.getData()
data.append(tableData)
- return "".join(data)
+ return bytesjoin(data)
def getDataLength(self):
"""Return the length of this table in bytes, without subtables."""
l = 0
- if hasattr(self, "Extension"):
- longOffset = 1
- else:
- longOffset = 0
for item in self.items:
if hasattr(item, "getData") or hasattr(item, "getCountData"):
- if longOffset:
+ if item.longOffset:
l = l + 4 # sizeof(ULong)
else:
l = l + 2 # sizeof(UShort)
@@ -227,28 +230,22 @@
def getData(self):
"""Assemble the data for this writer/table, without subtables."""
items = list(self.items) # make a shallow copy
- if hasattr(self,"Extension"):
- longOffset = 1
- else:
- longOffset = 0
pos = self.pos
numItems = len(items)
for i in range(numItems):
item = items[i]
if hasattr(item, "getData"):
- if longOffset:
+ if item.longOffset:
items[i] = packULong(item.pos - pos)
else:
try:
items[i] = packUShort(item.pos - pos)
- except AssertionError:
+ except struct.error:
# provide data to fix overflow problem.
- # If the overflow is to a lookup, or from a lookup to a subtable,
- # just report the current item.
- if self.name in [ 'LookupList', 'Lookup']:
- overflowErrorRecord = self.getOverflowErrorRecord(item)
- else:
+ # If the overflow is to a lookup, or from a lookup to a subtable,
+ # just report the current item. Otherwise...
+ if self.name not in [ 'LookupList', 'Lookup']:
# overflow is within a subTable. Life is more complicated.
# If we split the sub-table just before the current item, we may still suffer overflow.
# This is because duplicate table merging is done only within an Extension subTable tree;
@@ -279,20 +276,20 @@
overflowErrorRecord = self.getOverflowErrorRecord(item)
- raise OTLOffsetOverflowError, overflowErrorRecord
+ raise OTLOffsetOverflowError(overflowErrorRecord)
- return "".join(items)
+ return bytesjoin(items)
def __hash__(self):
# only works after self._doneWriting() has been called
return hash(self.items)
- def __cmp__(self, other):
- if type(self) != type(other) or \
- self.__class__ != other.__class__:
- return cmp(id(self), id(other))
-
- return cmp(self.items, other.items)
+ def __ne__(self, other):
+ return not self.__eq__(other)
+ def __eq__(self, other):
+ if type(self) != type(other):
+ return NotImplemented
+ return self.items == other.items
def _doneWriting(self, internedTables=None):
# Convert CountData references to data string items
@@ -305,7 +302,7 @@
if internedTables is None:
internedTables = {}
items = self.items
- iRange = range(len(items))
+ iRange = list(range(len(items)))
if hasattr(self, "Extension"):
newTree = 1
@@ -320,8 +317,9 @@
item._doneWriting()
else:
item._doneWriting(internedTables)
- if internedTables.has_key(item):
- items[i] = item = internedTables[item]
+ internedItem = internedTables.get(item)
+ if internedItem:
+ items[i] = item = internedItem
else:
internedTables[item] = item
self.items = tuple(items)
@@ -342,7 +340,7 @@
done[self] = 1
numItems = len(self.items)
- iRange = range(numItems)
+ iRange = list(range(numItems))
iRange.reverse()
if hasattr(self, "Extension"):
@@ -359,13 +357,12 @@
if hasattr(item, "name") and (item.name == "Coverage"):
sortCoverageLast = 1
break
- if not done.has_key(item):
+ if item not in done:
item._gatherTables(tables, extTables, done)
else:
- index = max(item.parent.keys())
- item.parent[index + 1] = self
+ # We're a new parent of item
+ pass
- saveItem = None
for i in iRange:
item = self.items[i]
if not hasattr(item, "getData"):
@@ -376,15 +373,15 @@
continue
if appendExtensions:
- assert extTables != None, "Program or XML editing error. Extension subtables cannot contain extensions subtables"
+ assert extTables is not None, "Program or XML editing error. Extension subtables cannot contain extensions subtables"
newDone = {}
item._gatherTables(extTables, None, newDone)
- elif not done.has_key(item):
+ elif item not in done:
item._gatherTables(tables, extTables, done)
else:
- index = max(item.parent.keys())
- item.parent[index + 1] = self
+ # We're a new parent of item
+ pass
tables.append(self)
@@ -393,10 +390,11 @@
# interface for gathering data, as used by table.compile()
def getSubWriter(self):
- subwriter = self.__class__(self.tableType, self.valueFormat)
- subwriter.parent = {0:self} # because some subtables have idential values, we discard
- # the duplicates under the getAllData method. Hence some
- # subtable writers can have more than one parent writer.
+ subwriter = self.__class__(self.globalState, self.localState)
+ subwriter.parent = self # because some subtables have idential values, we discard
+ # the duplicates under the getAllData method. Hence some
+ # subtable writers can have more than one parent writer.
+ # But we just care about first one right now.
return subwriter
def writeUShort(self, value):
@@ -405,6 +403,11 @@
def writeShort(self, value):
self.items.append(struct.pack(">h", value))
+
+ def writeUInt24(self, value):
+ assert 0 <= value < 0x1000000
+ b = struct.pack(">L", value)
+ self.items.append(b[1:])
def writeLong(self, value):
self.items.append(struct.pack(">l", value))
@@ -413,6 +416,7 @@
self.items.append(struct.pack(">L", value))
def writeTag(self, tag):
+ tag = Tag(tag).tobytes()
assert len(tag) == 4
self.items.append(tag)
@@ -420,20 +424,16 @@
self.items.append(subWriter)
def writeCountReference(self, table, name):
- self.items.append(CountReference(table, name))
+ ref = CountReference(table, name)
+ self.items.append(ref)
+ return ref
def writeStruct(self, format, values):
- data = apply(struct.pack, (format,) + values)
+ data = struct.pack(*(format,) + values)
self.items.append(data)
def writeData(self, data):
self.items.append(data)
-
- def setValueFormat(self, format, which):
- self.valueFormat[which].setFormat(format)
-
- def writeValueRecord(self, value, font, which):
- return self.valueFormat[which].writeValueRecord(self, font, value)
def getOverflowErrorRecord(self, item):
LookupListIndex = SubTableIndex = itemName = itemIndex = None
@@ -447,39 +447,45 @@
if hasattr(item, 'repeatIndex'):
itemIndex = item.repeatIndex
if self.name == 'SubTable':
- LookupListIndex = self.parent[0].repeatIndex
+ LookupListIndex = self.parent.repeatIndex
SubTableIndex = self.repeatIndex
elif self.name == 'ExtSubTable':
- LookupListIndex = self.parent[0].parent[0].repeatIndex
- SubTableIndex = self.parent[0].repeatIndex
+ LookupListIndex = self.parent.parent.repeatIndex
+ SubTableIndex = self.parent.repeatIndex
else: # who knows how far below the SubTable level we are! Climb back up to the nearest subtable.
- itemName = ".".join(self.name, item.name)
- p1 = self.parent[0]
+ itemName = ".".join([self.name, item.name])
+ p1 = self.parent
while p1 and p1.name not in ['ExtSubTable', 'SubTable']:
- itemName = ".".join(p1.name, item.name)
- p1 = p1.parent[0]
+ itemName = ".".join([p1.name, item.name])
+ p1 = p1.parent
if p1:
if p1.name == 'ExtSubTable':
- LookupListIndex = self.parent[0].parent[0].repeatIndex
- SubTableIndex = self.parent[0].repeatIndex
+ LookupListIndex = p1.parent.parent.repeatIndex
+ SubTableIndex = p1.parent.repeatIndex
else:
- LookupListIndex = self.parent[0].repeatIndex
- SubTableIndex = self.repeatIndex
+ LookupListIndex = p1.parent.repeatIndex
+ SubTableIndex = p1.repeatIndex
- return OverflowErrorRecord( (self.tableType, LookupListIndex, SubTableIndex, itemName, itemIndex) )
+ return OverflowErrorRecord( (self.globalState.tableType, LookupListIndex, SubTableIndex, itemName, itemIndex) )
-class CountReference:
+class CountReference(object):
"""A reference to a Count value, not a count of references."""
def __init__(self, table, name):
self.table = table
self.name = name
+ def setValue(self, value):
+ table = self.table
+ name = self.name
+ if table[name] is None:
+ table[name] = value
+ else:
+ assert table[name] == value, (name, table[name], value)
def getCountData(self):
return packUShort(self.table[self.name])
def packUShort(value):
- assert 0 <= value < 0x10000, value
return struct.pack(">H", value)
@@ -488,64 +494,18 @@
return struct.pack(">L", value)
+class BaseTable(object):
-class TableStack:
- """A stack of table dicts, working as a stack of namespaces so we can
- retrieve values from (and store values to) tables higher up the stack."""
- def __init__(self):
- self.stack = []
- def push(self, table):
- self.stack.append(table)
- def pop(self):
- self.stack.pop()
- def getTop(self):
- return self.stack[-1]
- def getValue(self, name):
- return self.__findTable(name)[name]
- def storeValue(self, name, value):
- table = self.__findTable(name)
- if table[name] is None:
- table[name] = value
- else:
- assert table[name] == value, (table[name], value)
- def __findTable(self, name):
- for table in reversed(self.stack):
- if table.has_key(name):
- return table
- raise KeyError, name
-
-
-class BaseTable:
- def __init__(self):
- self.compileStatus = 0 # 0 means table was created
- # 1 means the table.read() function was called by a table which is subject
- # to delayed compilation
- # 2 means that it was subject to delayed compilation, and
- # has been decompiled
- # 3 means that the start and end fields have been filled out, and that we
- # can use the data string rather than compiling from the table data.
-
- self.recurse = 0
-
def __getattr__(self, attr):
- # we get here only when the table does not have the attribute.
- # This method ovveride exists so that we can try to de-compile
- # a table which is subject to delayed decompilation, and then try
- # to get the value again after decompilation.
- self.recurse +=1
- if self.recurse > 2:
- # shouldn't ever get here - we should only get to two levels of recursion.
- # this guards against self.decompile NOT setting compileStatus to other than 1.
- raise AttributeError, attr
- if self.compileStatus == 1:
- # table.read() has been called, but table has not yet been decompiled
- # This happens only for extension tables.
- self.decompile(self.reader, self.font)
- val = getattr(self, attr)
- self.recurse -=1
- return val
-
- raise AttributeError, attr
+ reader = self.__dict__.get("reader")
+ if reader:
+ del self.reader
+ font = self.font
+ del self.font
+ self.decompile(reader, font)
+ return getattr(self, attr)
+
+ raise AttributeError(attr)
"""Generic base class for all OpenType (sub)tables."""
@@ -556,66 +516,95 @@
def getConverterByName(self, name):
return self.convertersByName[name]
- def decompile(self, reader, font, tableStack=None):
- self.compileStatus = 2 # table has been decompiled.
- if tableStack is None:
- tableStack = TableStack()
+ def decompile(self, reader, font):
self.readFormat(reader)
table = {}
self.__rawTable = table # for debugging
- tableStack.push(table)
- for conv in self.getConverters():
+ converters = self.getConverters()
+ for conv in converters:
if conv.name == "SubTable":
- conv = conv.getConverter(reader.tableType,
+ conv = conv.getConverter(reader.globalState.tableType,
table["LookupType"])
if conv.name == "ExtSubTable":
- conv = conv.getConverter(reader.tableType,
+ conv = conv.getConverter(reader.globalState.tableType,
table["ExtensionLookupType"])
+ if conv.name == "FeatureParams":
+ conv = conv.getConverter(reader["FeatureTag"])
if conv.repeat:
l = []
- for i in range(tableStack.getValue(conv.repeat) + conv.repeatOffset):
- l.append(conv.read(reader, font, tableStack))
+ if conv.repeat in table:
+ countValue = table[conv.repeat]
+ else:
+ # conv.repeat is a propagated count
+ countValue = reader[conv.repeat]
+ for i in range(countValue + conv.aux):
+ l.append(conv.read(reader, font, table))
table[conv.name] = l
else:
- table[conv.name] = conv.read(reader, font, tableStack)
- tableStack.pop()
+ if conv.aux and not eval(conv.aux, None, table):
+ continue
+ table[conv.name] = conv.read(reader, font, table)
+ if conv.isPropagated:
+ reader[conv.name] = table[conv.name]
+
self.postRead(table, font)
+
del self.__rawTable # succeeded, get rid of debugging info
- def preCompile(self):
- pass # used only by the LookupList class
+ def ensureDecompiled(self):
+ reader = self.__dict__.get("reader")
+ if reader:
+ del self.reader
+ font = self.font
+ del self.font
+ self.decompile(reader, font)
- def compile(self, writer, font, tableStack=None):
- if tableStack is None:
- tableStack = TableStack()
+ def compile(self, writer, font):
+ self.ensureDecompiled()
table = self.preWrite(font)
if hasattr(self, 'sortCoverageLast'):
writer.sortCoverageLast = 1
+ if hasattr(self.__class__, 'LookupType'):
+ writer['LookupType'].setValue(self.__class__.LookupType)
+
self.writeFormat(writer)
- tableStack.push(table)
for conv in self.getConverters():
value = table.get(conv.name)
if conv.repeat:
if value is None:
value = []
- tableStack.storeValue(conv.repeat, len(value) - conv.repeatOffset)
+ countValue = len(value) - conv.aux
+ if conv.repeat in table:
+ CountReference(table, conv.repeat).setValue(countValue)
+ else:
+ # conv.repeat is a propagated count
+ writer[conv.repeat].setValue(countValue)
for i in range(len(value)):
- conv.write(writer, font, tableStack, value[i], i)
+ conv.write(writer, font, table, value[i], i)
elif conv.isCount:
# Special-case Count values.
# Assumption: a Count field will *always* precede
- # the actual array.
+ # the actual array(s).
# We need a default value, as it may be set later by a nested
- # table. TableStack.storeValue() will then find it here.
- table[conv.name] = None
+ # table. We will later store it here.
# We add a reference: by the time the data is assembled
# the Count value will be filled in.
- writer.writeCountReference(table, conv.name)
+ ref = writer.writeCountReference(table, conv.name)
+ table[conv.name] = None
+ if conv.isPropagated:
+ writer[conv.name] = ref
+ elif conv.isLookupType:
+ ref = writer.writeCountReference(table, conv.name)
+ table[conv.name] = None
+ writer['LookupType'] = ref
else:
- conv.write(writer, font, tableStack, value)
- tableStack.pop()
+ if conv.aux and not eval(conv.aux, None, table):
+ continue
+ conv.write(writer, font, table, value)
+ if conv.isPropagated:
+ writer[conv.name] = value
def readFormat(self, reader):
pass
@@ -629,8 +618,8 @@
def preWrite(self, font):
return self.__dict__.copy()
- def toXML(self, xmlWriter, font, attrs=None):
- tableName = self.__class__.__name__
+ def toXML(self, xmlWriter, font, attrs=None, name=None):
+ tableName = name if name else self.__class__.__name__
if attrs is None:
attrs = []
if hasattr(self, "Format"):
@@ -646,16 +635,19 @@
# This is because in TTX our parent writes our main tag, and in otBase.py we
# do it ourselves. I think I'm getting schizophrenic...
for conv in self.getConverters():
- value = getattr(self, conv.name)
if conv.repeat:
+ value = getattr(self, conv.name)
for i in range(len(value)):
item = value[i]
conv.xmlWrite(xmlWriter, font, item, conv.name,
[("index", i)])
else:
+ if conv.aux and not eval(conv.aux, None, vars(self)):
+ continue
+ value = getattr(self, conv.name)
conv.xmlWrite(xmlWriter, font, value, conv.name, [])
- def fromXML(self, (name, attrs, content), font):
+ def fromXML(self, name, attrs, content, font):
try:
conv = self.getConverterByName(name)
except KeyError:
@@ -670,12 +662,16 @@
else:
setattr(self, conv.name, value)
- def __cmp__(self, other):
- if type(self) != type(other) or \
- self.__class__ != other.__class__:
- return cmp(id(self), id(other))
+ def __ne__(self, other):
+ return not self.__eq__(other)
+ def __eq__(self, other):
+ if type(self) != type(other):
+ return NotImplemented
- return cmp(self.__dict__, other.__dict__)
+ self.ensureDecompiled()
+ other.ensureDecompiled()
+
+ return self.__dict__ == other.__dict__
class FormatSwitchingBaseTable(BaseTable):
@@ -691,11 +687,14 @@
def readFormat(self, reader):
self.Format = reader.readUShort()
- assert self.Format <> 0, (self, reader.pos, len(reader.data))
+ assert self.Format != 0, (self, reader.pos, len(reader.data))
def writeFormat(self, writer):
writer.writeUShort(self.Format)
+ def toXML(self, xmlWriter, font, attrs=None, name=None):
+ BaseTable.toXML(self, xmlWriter, font, attrs, name=self.__class__.__name__)
+
#
# Support for ValueRecords
@@ -735,11 +734,11 @@
valueRecordFormatDict = _buildDict()
-class ValueRecordFactory:
+class ValueRecordFactory(object):
"""Given a format code, this object convert ValueRecords."""
-
- def setFormat(self, valueFormat):
+
+ def __init__(self, valueFormat):
format = []
for mask, name, isDevice, signed in valueRecordFormat:
if valueFormat & mask:
@@ -758,7 +757,7 @@
value = reader.readUShort()
if isDevice:
if value:
- import otTables
+ from . import otTables
subReader = reader.getSubReader(value)
value = getattr(otTables, name)()
value.decompile(subReader, font)
@@ -783,7 +782,7 @@
writer.writeUShort(value)
-class ValueRecord:
+class ValueRecord(object):
# see ValueRecordFactory
@@ -819,24 +818,25 @@
xmlWriter.simpletag(valueName, simpleItems)
xmlWriter.newline()
- def fromXML(self, (name, attrs, content), font):
- import otTables
+ def fromXML(self, name, attrs, content, font):
+ from . import otTables
for k, v in attrs.items():
setattr(self, k, int(v))
for element in content:
- if type(element) <> TupleType:
+ if not isinstance(element, tuple):
continue
name, attrs, content = element
value = getattr(otTables, name)()
for elem2 in content:
- if type(elem2) <> TupleType:
+ if not isinstance(elem2, tuple):
continue
- value.fromXML(elem2, font)
+ name2, attrs2, content2 = elem2
+ value.fromXML(name2, attrs2, content2, font)
setattr(self, name, value)
- def __cmp__(self, other):
- if type(self) != type(other) or \
- self.__class__ != other.__class__:
- return cmp(id(self), id(other))
-
- return cmp(self.__dict__, other.__dict__)
+ def __ne__(self, other):
+ return not self.__eq__(other)
+ def __eq__(self, other):
+ if type(self) != type(other):
+ return NotImplemented
+ return self.__dict__ == other.__dict__
diff --git a/Lib/fontTools/ttLib/tables/otConverters.py b/Lib/fontTools/ttLib/tables/otConverters.py
index 11a8773..433ea8e 100644
--- a/Lib/fontTools/ttLib/tables/otConverters.py
+++ b/Lib/fontTools/ttLib/tables/otConverters.py
@@ -1,5 +1,8 @@
-from types import TupleType
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
from fontTools.misc.textTools import safeEval
+from fontTools.misc.fixedTools import fixedToFloat as fi2fl, floatToFixed as fl2fi
+from .otBase import ValueRecordFactory
def buildConverters(tableSpec, tableNamespace):
@@ -8,63 +11,74 @@
the results are assigned to the corresponding class in otTables.py."""
converters = []
convertersByName = {}
- for tp, name, repeat, repeatOffset, descr in tableSpec:
+ for tp, name, repeat, aux, descr in tableSpec:
+ tableName = name
if name.startswith("ValueFormat"):
assert tp == "uint16"
converterClass = ValueFormat
- elif name == "DeltaValue":
+ elif name.endswith("Count") or name.endswith("LookupType"):
assert tp == "uint16"
- converterClass = DeltaValue
- elif name.endswith("Count"):
- assert tp == "uint16"
- converterClass = Count
+ converterClass = ComputedUShort
elif name == "SubTable":
converterClass = SubTable
elif name == "ExtSubTable":
converterClass = ExtSubTable
+ elif name == "FeatureParams":
+ converterClass = FeatureParams
else:
- converterClass = converterMapping[tp]
- tableClass = tableNamespace.get(name)
- conv = converterClass(name, repeat, repeatOffset, tableClass)
+ if not tp in converterMapping:
+ tableName = tp
+ converterClass = Struct
+ else:
+ converterClass = converterMapping[tp]
+ tableClass = tableNamespace.get(tableName)
+ conv = converterClass(name, repeat, aux, tableClass)
if name in ["SubTable", "ExtSubTable"]:
conv.lookupTypes = tableNamespace['lookupTypes']
# also create reverse mapping
for t in conv.lookupTypes.values():
for cls in t.values():
- convertersByName[cls.__name__] = Table(name, repeat, repeatOffset, cls)
+ convertersByName[cls.__name__] = Table(name, repeat, aux, cls)
+ if name == "FeatureParams":
+ conv.featureParamTypes = tableNamespace['featureParamTypes']
+ conv.defaultFeatureParams = tableNamespace['FeatureParams']
+ for cls in conv.featureParamTypes.values():
+ convertersByName[cls.__name__] = Table(name, repeat, aux, cls)
converters.append(conv)
- assert not convertersByName.has_key(name)
+ assert name not in convertersByName, name
convertersByName[name] = conv
return converters, convertersByName
-class BaseConverter:
+class BaseConverter(object):
"""Base class for converter objects. Apart from the constructor, this
is an abstract class."""
- def __init__(self, name, repeat, repeatOffset, tableClass):
+ def __init__(self, name, repeat, aux, tableClass):
self.name = name
self.repeat = repeat
- self.repeatOffset = repeatOffset
+ self.aux = aux
self.tableClass = tableClass
self.isCount = name.endswith("Count")
+ self.isLookupType = name.endswith("LookupType")
+ self.isPropagated = name in ["ClassCount", "Class2Count", "FeatureTag"]
- def read(self, reader, font, tableStack):
+ def read(self, reader, font, tableDict):
"""Read a value from the reader."""
- raise NotImplementedError, self
+ raise NotImplementedError(self)
- def write(self, writer, font, tableStack, value, repeatIndex=None):
+ def write(self, writer, font, tableDict, value, repeatIndex=None):
"""Write a value to the writer."""
- raise NotImplementedError, self
+ raise NotImplementedError(self)
def xmlRead(self, attrs, content, font):
"""Read a value from XML."""
- raise NotImplementedError, self
+ raise NotImplementedError(self)
def xmlWrite(self, xmlWriter, font, value, name, attrs):
"""Write a value to XML."""
- raise NotImplementedError, self
+ raise NotImplementedError(self)
class SimpleValue(BaseConverter):
@@ -76,196 +90,235 @@
class IntValue(SimpleValue):
def xmlRead(self, attrs, content, font):
- return int(attrs["value"])
+ return int(attrs["value"], 0)
class Long(IntValue):
- def read(self, reader, font, tableStack):
+ def read(self, reader, font, tableDict):
return reader.readLong()
- def write(self, writer, font, tableStack, value, repeatIndex=None):
+ def write(self, writer, font, tableDict, value, repeatIndex=None):
writer.writeLong(value)
-class Fixed(IntValue):
- def read(self, reader, font, tableStack):
- return float(reader.readLong()) / 0x10000
- def write(self, writer, font, tableStack, value, repeatIndex=None):
- writer.writeLong(int(round(value * 0x10000)))
+class Version(BaseConverter):
+ def read(self, reader, font, tableDict):
+ value = reader.readLong()
+ assert (value >> 16) == 1, "Unsupported version 0x%08x" % value
+ return fi2fl(value, 16)
+ def write(self, writer, font, tableDict, value, repeatIndex=None):
+ if value < 0x10000:
+ value = fl2fi(value, 16)
+ value = int(round(value))
+ assert (value >> 16) == 1, "Unsupported version 0x%08x" % value
+ writer.writeLong(value)
def xmlRead(self, attrs, content, font):
- return float(attrs["value"])
+ value = attrs["value"]
+ value = float(int(value, 0)) if value.startswith("0") else float(value)
+ if value >= 0x10000:
+ value = fi2fl(value, 16)
+ return value
+ def xmlWrite(self, xmlWriter, font, value, name, attrs):
+ if value >= 0x10000:
+ value = fi2fl(value, 16)
+ if value % 1 != 0:
+ # Write as hex
+ value = "0x%08x" % fl2fi(value, 16)
+ xmlWriter.simpletag(name, attrs + [("value", value)])
+ xmlWriter.newline()
class Short(IntValue):
- def read(self, reader, font, tableStack):
+ def read(self, reader, font, tableDict):
return reader.readShort()
- def write(self, writer, font, tableStack, value, repeatIndex=None):
+ def write(self, writer, font, tableDict, value, repeatIndex=None):
writer.writeShort(value)
class UShort(IntValue):
- def read(self, reader, font, tableStack):
+ def read(self, reader, font, tableDict):
return reader.readUShort()
- def write(self, writer, font, tableStack, value, repeatIndex=None):
+ def write(self, writer, font, tableDict, value, repeatIndex=None):
writer.writeUShort(value)
-class Count(Short):
+class UInt24(IntValue):
+ def read(self, reader, font, tableDict):
+ return reader.readUInt24()
+ def write(self, writer, font, tableDict, value, repeatIndex=None):
+ writer.writeUInt24(value)
+
+class ComputedUShort(UShort):
def xmlWrite(self, xmlWriter, font, value, name, attrs):
xmlWriter.comment("%s=%s" % (name, value))
xmlWriter.newline()
class Tag(SimpleValue):
- def read(self, reader, font, tableStack):
+ def read(self, reader, font, tableDict):
return reader.readTag()
- def write(self, writer, font, tableStack, value, repeatIndex=None):
+ def write(self, writer, font, tableDict, value, repeatIndex=None):
writer.writeTag(value)
class GlyphID(SimpleValue):
- def read(self, reader, font, tableStack):
+ def read(self, reader, font, tableDict):
value = reader.readUShort()
value = font.getGlyphName(value)
return value
- def write(self, writer, font, tableStack, value, repeatIndex=None):
+ def write(self, writer, font, tableDict, value, repeatIndex=None):
value = font.getGlyphID(value)
writer.writeUShort(value)
+class FloatValue(SimpleValue):
+ def xmlRead(self, attrs, content, font):
+ return float(attrs["value"])
+
+class DeciPoints(FloatValue):
+ def read(self, reader, font, tableDict):
+ value = reader.readUShort()
+ return value / 10
+
+ def write(self, writer, font, tableDict, value, repeatIndex=None):
+ writer.writeUShort(int(round(value * 10)))
class Struct(BaseConverter):
- def read(self, reader, font, tableStack):
+ def read(self, reader, font, tableDict):
table = self.tableClass()
- table.decompile(reader, font, tableStack)
+ table.decompile(reader, font)
return table
- def write(self, writer, font, tableStack, value, repeatIndex=None):
- value.compile(writer, font, tableStack)
+ def write(self, writer, font, tableDict, value, repeatIndex=None):
+ value.compile(writer, font)
def xmlWrite(self, xmlWriter, font, value, name, attrs):
if value is None:
- pass # NULL table, ignore
+ if attrs:
+ # If there are attributes (probably index), then
+ # don't drop this even if it's NULL. It will mess
+ # up the array indices of the containing element.
+ xmlWriter.simpletag(name, attrs + [("empty", True)])
+ xmlWriter.newline()
+ else:
+ pass # NULL table, ignore
else:
- value.toXML(xmlWriter, font, attrs)
+ value.toXML(xmlWriter, font, attrs, name=name)
def xmlRead(self, attrs, content, font):
table = self.tableClass()
+ if attrs.get("empty"):
+ return None
Format = attrs.get("Format")
if Format is not None:
table.Format = int(Format)
for element in content:
- if type(element) == TupleType:
+ if isinstance(element, tuple):
name, attrs, content = element
- table.fromXML((name, attrs, content), font)
+ table.fromXML(name, attrs, content, font)
else:
pass
return table
class Table(Struct):
+
+ longOffset = False
+
+ def readOffset(self, reader):
+ return reader.readUShort()
+
+ def writeNullOffset(self, writer):
+ if self.longOffset:
+ writer.writeULong(0)
+ else:
+ writer.writeUShort(0)
- def read(self, reader, font, tableStack):
- offset = reader.readUShort()
+ def read(self, reader, font, tableDict):
+ offset = self.readOffset(reader)
if offset == 0:
return None
if offset <= 3:
# XXX hack to work around buggy pala.ttf
- print "*** Warning: offset is not 0, yet suspiciously low (%s). table: %s" \
- % (offset, self.tableClass.__name__)
+ print("*** Warning: offset is not 0, yet suspiciously low (%s). table: %s" \
+ % (offset, self.tableClass.__name__))
return None
- subReader = reader.getSubReader(offset)
table = self.tableClass()
- table.decompile(subReader, font, tableStack)
+ reader = reader.getSubReader(offset)
+ if font.lazy:
+ table.reader = reader
+ table.font = font
+ else:
+ table.decompile(reader, font)
return table
- def write(self, writer, font, tableStack, value, repeatIndex=None):
+ def write(self, writer, font, tableDict, value, repeatIndex=None):
if value is None:
- writer.writeUShort(0)
+ self.writeNullOffset(writer)
else:
subWriter = writer.getSubWriter()
+ subWriter.longOffset = self.longOffset
subWriter.name = self.name
if repeatIndex is not None:
subWriter.repeatIndex = repeatIndex
- value.preCompile()
writer.writeSubTable(subWriter)
- value.compile(subWriter, font, tableStack)
+ value.compile(subWriter, font)
+
+class LTable(Table):
+
+ longOffset = True
+
+ def readOffset(self, reader):
+ return reader.readULong()
+
class SubTable(Table):
def getConverter(self, tableType, lookupType):
- lookupTypes = self.lookupTypes[tableType]
- tableClass = lookupTypes[lookupType]
- return SubTable(self.name, self.repeat, self.repeatOffset, tableClass)
+ tableClass = self.lookupTypes[tableType][lookupType]
+ return self.__class__(self.name, self.repeat, self.aux, tableClass)
-class ExtSubTable(Table):
- def getConverter(self, tableType, lookupType):
- lookupTypes = self.lookupTypes[tableType]
- tableClass = lookupTypes[lookupType]
- return ExtSubTable(self.name, self.repeat, self.repeatOffset, tableClass)
+class ExtSubTable(LTable, SubTable):
- def read(self, reader, font, tableStack):
- offset = reader.readULong()
- if offset == 0:
- return None
- subReader = reader.getSubReader(offset)
- table = self.tableClass()
- table.reader = subReader
- table.font = font
- table.compileStatus = 1
- table.start = table.reader.offset
- return table
-
- def write(self, writer, font, tableStack, value, repeatIndex=None):
+ def write(self, writer, font, tableDict, value, repeatIndex=None):
writer.Extension = 1 # actually, mere presence of the field flags it as an Ext Subtable writer.
- if value is None:
- writer.writeULong(0)
- else:
- # If the subtable has not yet been decompiled, we need to do so.
- if value.compileStatus == 1:
- value.decompile(value.reader, value.font, tableStack)
- subWriter = writer.getSubWriter()
- subWriter.name = self.name
- writer.writeSubTable(subWriter)
- # If the subtable has been sorted and we can just write the original
- # data, then do so.
- if value.compileStatus == 3:
- data = value.reader.data[value.start:value.end]
- subWriter.writeData(data)
- else:
- value.compile(subWriter, font, tableStack)
+ Table.write(self, writer, font, tableDict, value, repeatIndex)
+
+class FeatureParams(Table):
+ def getConverter(self, featureTag):
+ tableClass = self.featureParamTypes.get(featureTag, self.defaultFeatureParams)
+ return self.__class__(self.name, self.repeat, self.aux, tableClass)
class ValueFormat(IntValue):
- def __init__(self, name, repeat, repeatOffset, tableClass):
- BaseConverter.__init__(self, name, repeat, repeatOffset, tableClass)
- self.which = name[-1] == "2"
- def read(self, reader, font, tableStack):
+ def __init__(self, name, repeat, aux, tableClass):
+ BaseConverter.__init__(self, name, repeat, aux, tableClass)
+ self.which = "ValueFormat" + ("2" if name[-1] == "2" else "1")
+ def read(self, reader, font, tableDict):
format = reader.readUShort()
- reader.setValueFormat(format, self.which)
+ reader[self.which] = ValueRecordFactory(format)
return format
- def write(self, writer, font, tableStack, format, repeatIndex=None):
+ def write(self, writer, font, tableDict, format, repeatIndex=None):
writer.writeUShort(format)
- writer.setValueFormat(format, self.which)
+ writer[self.which] = ValueRecordFactory(format)
class ValueRecord(ValueFormat):
- def read(self, reader, font, tableStack):
- return reader.readValueRecord(font, self.which)
- def write(self, writer, font, tableStack, value, repeatIndex=None):
- writer.writeValueRecord(value, font, self.which)
+ def read(self, reader, font, tableDict):
+ return reader[self.which].readValueRecord(reader, font)
+ def write(self, writer, font, tableDict, value, repeatIndex=None):
+ writer[self.which].writeValueRecord(writer, font, value)
def xmlWrite(self, xmlWriter, font, value, name, attrs):
if value is None:
pass # NULL table, ignore
else:
value.toXML(xmlWriter, font, self.name, attrs)
def xmlRead(self, attrs, content, font):
- from otBase import ValueRecord
+ from .otBase import ValueRecord
value = ValueRecord()
- value.fromXML((None, attrs, content), font)
+ value.fromXML(None, attrs, content, font)
return value
class DeltaValue(BaseConverter):
- def read(self, reader, font, tableStack):
- table = tableStack.getTop()
- StartSize = table["StartSize"]
- EndSize = table["EndSize"]
- DeltaFormat = table["DeltaFormat"]
+ def read(self, reader, font, tableDict):
+ StartSize = tableDict["StartSize"]
+ EndSize = tableDict["EndSize"]
+ DeltaFormat = tableDict["DeltaFormat"]
assert DeltaFormat in (1, 2, 3), "illegal DeltaFormat"
nItems = EndSize - StartSize + 1
nBits = 1 << DeltaFormat
@@ -285,12 +338,11 @@
DeltaValue.append(value)
return DeltaValue
- def write(self, writer, font, tableStack, value, repeatIndex=None):
- table = tableStack.getTop()
- StartSize = table["StartSize"]
- EndSize = table["EndSize"]
- DeltaFormat = table["DeltaFormat"]
- DeltaValue = table["DeltaValue"]
+ def write(self, writer, font, tableDict, value, repeatIndex=None):
+ StartSize = tableDict["StartSize"]
+ EndSize = tableDict["EndSize"]
+ DeltaFormat = tableDict["DeltaFormat"]
+ DeltaValue = value
assert DeltaFormat in (1, 2, 3), "illegal DeltaFormat"
nItems = EndSize - StartSize + 1
nBits = 1 << DeltaFormat
@@ -304,11 +356,10 @@
if shift == 0:
writer.writeUShort(tmp)
tmp, shift = 0, 16
- if shift <> 16:
+ if shift != 16:
writer.writeUShort(tmp)
def xmlWrite(self, xmlWriter, font, value, name, attrs):
- # XXX this could do with a nicer format
xmlWriter.simpletag(name, attrs + [("value", value)])
xmlWriter.newline()
@@ -320,17 +371,15 @@
# type class
"int16": Short,
"uint16": UShort,
- "ULONG": Long,
- "Fixed": Fixed,
+ "uint24": UInt24,
+ "Version": Version,
"Tag": Tag,
"GlyphID": GlyphID,
+ "DeciPoints": DeciPoints,
"struct": Struct,
"Offset": Table,
- "LOffset": ExtSubTable,
+ "LOffset": LTable,
"ValueRecord": ValueRecord,
+ "DeltaValue": DeltaValue,
}
-# equivalents:
-converterMapping["USHORT"] = converterMapping["uint16"]
-converterMapping["fixed32"] = converterMapping["Fixed"]
-
diff --git a/Lib/fontTools/ttLib/tables/otData.py b/Lib/fontTools/ttLib/tables/otData.py
index 89358e6..10046d8 100644
--- a/Lib/fontTools/ttLib/tables/otData.py
+++ b/Lib/fontTools/ttLib/tables/otData.py
@@ -1,7 +1,7 @@
otData = [
#
- # common (generated from chapter2.htm)
+ # common
#
('ScriptList', [
@@ -48,6 +48,33 @@
('uint16', 'LookupListIndex', 'LookupCount', 0, 'Array of LookupList indices for this feature -zero-based (first lookup is LookupListIndex = 0)'),
]),
+ ('FeatureParams', [
+ ]),
+
+ ('FeatureParamsSize', [
+ ('DeciPoints', 'DesignSize', None, None, 'The design size in 720/inch units (decipoints).'),
+ ('uint16', 'SubfamilyID', None, None, 'Serves as an identifier that associates fonts in a subfamily.'),
+ ('uint16', 'SubfamilyNameID', None, None, 'Subfamily NameID.'),
+ ('DeciPoints', 'RangeStart', None, None, 'Small end of recommended usage range (exclusive) in 720/inch units.'),
+ ('DeciPoints', 'RangeEnd', None, None, 'Large end of recommended usage range (inclusive) in 720/inch units.'),
+ ]),
+
+ ('FeatureParamsStylisticSet', [
+ ('uint16', 'Version', None, None, 'Set to 0.'),
+ ('uint16', 'UINameID', None, None, 'UI NameID.'),
+ ]),
+
+ ('FeatureParamsCharacterVariants', [
+ ('uint16', 'Format', None, None, 'Set to 0.'),
+ ('uint16', 'FeatUILabelNameID', None, None, 'Feature UI label NameID.'),
+ ('uint16', 'FeatUITooltipTextNameID', None, None, 'Feature UI tooltip text NameID.'),
+ ('uint16', 'SampleTextNameID', None, None, 'Sample text NameID.'),
+ ('uint16', 'NumNamedParameters', None, None, 'Number of named parameters.'),
+ ('uint16', 'FirstParamUILabelNameID', None, None, 'First NameID of UI feature parameters.'),
+ ('uint16', 'CharCount', None, None, 'Count of characters this feature provides glyph variants for.'),
+ ('uint24', 'Character', 'CharCount', 0, 'Unicode characters for which this feature provides glyph variants.'),
+ ]),
+
('LookupList', [
('uint16', 'LookupCount', None, None, 'Number of lookups in this table'),
('Offset', 'Lookup', 'LookupCount', 0, 'Array of offsets to Lookup tables-from beginning of LookupList -zero based (first lookup is Lookup index = 0)'),
@@ -58,6 +85,7 @@
('uint16', 'LookupFlag', None, None, 'Lookup qualifiers'),
('uint16', 'SubTableCount', None, None, 'Number of SubTables for this lookup'),
('Offset', 'SubTable', 'SubTableCount', 0, 'Array of offsets to SubTables-from beginning of Lookup table'),
+ ('uint16', 'MarkFilteringSet', None, 'LookupFlag & 0x0010', 'If set, indicates that the lookup table structure is followed by a MarkFilteringSet field. The layout engine skips over all mark glyphs not in the mark filtering set indicated.'),
]),
('CoverageFormat1', [
@@ -101,16 +129,16 @@
('uint16', 'StartSize', None, None, 'Smallest size to correct-in ppem'),
('uint16', 'EndSize', None, None, 'Largest size to correct-in ppem'),
('uint16', 'DeltaFormat', None, None, 'Format of DeltaValue array data: 1, 2, or 3'),
- ('uint16', 'DeltaValue', '', 0, 'Array of compressed data'),
+ ('DeltaValue', 'DeltaValue', '', 0, 'Array of compressed data'),
]),
#
- # gpos (generated from gpos.htm)
+ # gpos
#
('GPOS', [
- ('Fixed', 'Version', None, None, 'Version of the GPOS table-initially = 0x00010000'),
+ ('Version', 'Version', None, None, 'Version of the GPOS table-initially = 0x00010000'),
('Offset', 'ScriptList', None, None, 'Offset to ScriptList table-from beginning of GPOS table'),
('Offset', 'FeatureList', None, None, 'Offset to FeatureList table-from beginning of GPOS table'),
('Offset', 'LookupList', None, None, 'Offset to LookupList table-from beginning of GPOS table'),
@@ -357,9 +385,9 @@
]),
('ExtensionPosFormat1', [
- ('USHORT', 'ExtFormat', None, None, 'Format identifier. Set to 1.'),
- ('USHORT', 'ExtensionLookupType', None, None, 'Lookup type of subtable referenced by ExtensionOffset (i.e. the extension subtable).'),
- ('LOffset', 'ExtSubTable', None, None, 'Array of offsets to Lookup tables-from beginning of LookupList -zero based (first lookup is Lookup index = 0)'),
+ ('uint16', 'ExtFormat', None, None, 'Format identifier. Set to 1.'),
+ ('uint16', 'ExtensionLookupType', None, None, 'Lookup type of subtable referenced by ExtensionOffset (i.e. the extension subtable).'),
+ ('LOffset', 'ExtSubTable', None, None, 'Offset to SubTable'),
]),
('ValueRecord', [
@@ -406,11 +434,11 @@
#
- # gsub (generated from gsub.htm)
+ # gsub
#
('GSUB', [
- ('Fixed', 'Version', None, None, 'Version of the GSUB table-initially set to 0x00010000'),
+ ('Version', 'Version', None, None, 'Version of the GSUB table-initially set to 0x00010000'),
('Offset', 'ScriptList', None, None, 'Offset to ScriptList table-from beginning of GSUB table'),
('Offset', 'FeatureList', None, None, 'Offset to FeatureList table-from beginning of GSUB table'),
('Offset', 'LookupList', None, None, 'Offset to LookupList table-from beginning of GSUB table'),
@@ -585,8 +613,8 @@
]),
('ExtensionSubstFormat1', [
- ('USHORT', 'ExtFormat', None, None, 'Format identifier. Set to 1.'),
- ('USHORT', 'ExtensionLookupType', None, None, 'Lookup type of subtable referenced by ExtensionOffset (i.e. the extension subtable).'),
+ ('uint16', 'ExtFormat', None, None, 'Format identifier. Set to 1.'),
+ ('uint16', 'ExtensionLookupType', None, None, 'Lookup type of subtable referenced by ExtensionOffset (i.e. the extension subtable).'),
('LOffset', 'ExtSubTable', None, None, 'Array of offsets to Lookup tables-from beginning of LookupList -zero based (first lookup is Lookup index = 0)'),
]),
@@ -602,15 +630,16 @@
]),
#
- # gdef (generated from gdef.htm)
+ # gdef
#
('GDEF', [
- ('Fixed', 'Version', None, None, 'Version of the GDEF table-initially 0x00010000'),
+ ('Version', 'Version', None, None, 'Version of the GDEF table-initially 0x00010000'),
('Offset', 'GlyphClassDef', None, None, 'Offset to class definition table for glyph type-from beginning of GDEF header (may be NULL)'),
('Offset', 'AttachList', None, None, 'Offset to list of glyphs with attachment points-from beginning of GDEF header (may be NULL)'),
('Offset', 'LigCaretList', None, None, 'Offset to list of positioning points for ligature carets-from beginning of GDEF header (may be NULL)'),
('Offset', 'MarkAttachClassDef', None, None, 'Offset to class definition table for mark attachment type-from beginning of GDEF header (may be NULL)'),
+ ('Offset', 'MarkGlyphSetsDef', None, 'int(round(Version*0x10000)) >= 0x00010002', 'Offset to the table of mark set definitions-from beginning of GDEF header (may be NULL)'),
]),
('AttachList', [
@@ -651,13 +680,18 @@
('Offset', 'DeviceTable', None, None, 'Offset to Device table for X or Y value-from beginning of CaretValue table'),
]),
+ ('MarkGlyphSetsDef', [
+ ('uint16', 'MarkSetTableFormat', None, None, 'Format identifier == 1'),
+ ('uint16', 'MarkSetCount', None, None, 'Number of mark sets defined'),
+ ('LOffset', 'Coverage', 'MarkSetCount', 0, 'Array of offsets to mark set coverage tables.'),
+ ]),
#
- # base (generated from base.htm)
+ # base
#
('BASE', [
- ('fixed32', 'Version', None, None, 'Version of the BASE table-initially 0x00010000'),
+ ('Version', 'Version', None, None, 'Version of the BASE table-initially 0x00010000'),
('Offset', 'HorizAxis', None, None, 'Offset to horizontal Axis table-from beginning of BASE table-may be NULL'),
('Offset', 'VertAxis', None, None, 'Offset to vertical Axis table-from beginning of BASE table-may be NULL'),
]),
@@ -733,11 +767,11 @@
#
- # jstf (generated from jstf.htm)
+ # jstf
#
('JSTF', [
- ('fixed32', 'Version', None, None, 'Version of the JSTF table-initially set to 0x00010000'),
+ ('Version', 'Version', None, None, 'Version of the JSTF table-initially set to 0x00010000'),
('uint16', 'JstfScriptCount', None, None, 'Number of JstfScriptRecords in this table'),
('struct', 'JstfScriptRecord', 'JstfScriptCount', 0, 'Array of JstfScriptRecords-in alphabetical order, by JstfScriptTag'),
]),
@@ -797,5 +831,152 @@
('Offset', 'Lookup', 'LookupCount', 0, 'Array of offsets to GPOS-type lookup tables-from beginning of JstfMax table-in design order'),
]),
-]
+ #
+ # math
+ #
+ ('MATH', [
+ ('Version', 'Version', None, None, 'Version of the MATH table-initially set to 0x00010000.'),
+ ('Offset', 'MathConstants', None, None, 'Offset to MathConstants table - from the beginning of MATH table.'),
+ ('Offset', 'MathGlyphInfo', None, None, 'Offset to MathGlyphInfo table - from the beginning of MATH table.'),
+ ('Offset', 'MathVariants', None, None, 'Offset to MathVariants table - from the beginning of MATH table.'),
+ ]),
+
+ ('MathValueRecord', [
+ ('int16', 'Value', None, None, 'The X or Y value in design units.'),
+ ('Offset', 'DeviceTable', None, None, 'Offset to the device table - from the beginning of parent table. May be NULL. Suggested format for device table is 1.'),
+ ]),
+
+ ('MathConstants', [
+ ('int16', 'ScriptPercentScaleDown', None, None, 'Percentage of scaling down for script level 1. Suggested value: 80%.'),
+ ('int16', 'ScriptScriptPercentScaleDown', None, None, 'Percentage of scaling down for script level 2 (ScriptScript). Suggested value: 60%.'),
+ ('uint16', 'DelimitedSubFormulaMinHeight', None, None, 'Minimum height required for a delimited expression to be treated as a subformula. Suggested value: normal line height x1.5.'),
+ ('uint16', 'DisplayOperatorMinHeight', None, None, 'Minimum height of n-ary operators (such as integral and summation) for formulas in display mode.'),
+ ('MathValueRecord', 'MathLeading', None, None, 'White space to be left between math formulas to ensure proper line spacing. For example, for applications that treat line gap as a part of line ascender, formulas with ink going above (os2.sTypoAscender + os2.sTypoLineGap - MathLeading) or with ink going below os2.sTypoDescender will result in increasing line height.'),
+ ('MathValueRecord', 'AxisHeight', None, None, 'Axis height of the font.'),
+ ('MathValueRecord', 'AccentBaseHeight', None, None, 'Maximum (ink) height of accent base that does not require raising the accents. Suggested: x-height of the font (os2.sxHeight) plus any possible overshots.'),
+ ('MathValueRecord', 'FlattenedAccentBaseHeight', None, None, 'Maximum (ink) height of accent base that does not require flattening the accents. Suggested: cap height of the font (os2.sCapHeight).'),
+ ('MathValueRecord', 'SubscriptShiftDown', None, None, 'The standard shift down applied to subscript elements. Positive for moving in the downward direction. Suggested: os2.ySubscriptYOffset.'),
+ ('MathValueRecord', 'SubscriptTopMax', None, None, 'Maximum allowed height of the (ink) top of subscripts that does not require moving subscripts further down. Suggested: 4/5 x-height.'),
+ ('MathValueRecord', 'SubscriptBaselineDropMin', None, None, 'Minimum allowed drop of the baseline of subscripts relative to the (ink) bottom of the base. Checked for bases that are treated as a box or extended shape. Positive for subscript baseline dropped below the base bottom.'),
+ ('MathValueRecord', 'SuperscriptShiftUp', None, None, 'Standard shift up applied to superscript elements. Suggested: os2.ySuperscriptYOffset.'),
+ ('MathValueRecord', 'SuperscriptShiftUpCramped', None, None, 'Standard shift of superscripts relative to the base, in cramped style.'),
+ ('MathValueRecord', 'SuperscriptBottomMin', None, None, 'Minimum allowed height of the (ink) bottom of superscripts that does not require moving subscripts further up. Suggested: 1/4 x-height.'),
+ ('MathValueRecord', 'SuperscriptBaselineDropMax', None, None, 'Maximum allowed drop of the baseline of superscripts relative to the (ink) top of the base. Checked for bases that are treated as a box or extended shape. Positive for superscript baseline below the base top.'),
+ ('MathValueRecord', 'SubSuperscriptGapMin', None, None, 'Minimum gap between the superscript and subscript ink. Suggested: 4x default rule thickness.'),
+ ('MathValueRecord', 'SuperscriptBottomMaxWithSubscript', None, None, 'The maximum level to which the (ink) bottom of superscript can be pushed to increase the gap between superscript and subscript, before subscript starts being moved down. Suggested: 4/5 x-height.'),
+ ('MathValueRecord', 'SpaceAfterScript', None, None, 'Extra white space to be added after each subscript and superscript. Suggested: 0.5pt for a 12 pt font.'),
+ ('MathValueRecord', 'UpperLimitGapMin', None, None, 'Minimum gap between the (ink) bottom of the upper limit, and the (ink) top of the base operator.'),
+ ('MathValueRecord', 'UpperLimitBaselineRiseMin', None, None, 'Minimum distance between baseline of upper limit and (ink) top of the base operator.'),
+ ('MathValueRecord', 'LowerLimitGapMin', None, None, 'Minimum gap between (ink) top of the lower limit, and (ink) bottom of the base operator.'),
+ ('MathValueRecord', 'LowerLimitBaselineDropMin', None, None, 'Minimum distance between baseline of the lower limit and (ink) bottom of the base operator.'),
+ ('MathValueRecord', 'StackTopShiftUp', None, None, 'Standard shift up applied to the top element of a stack.'),
+ ('MathValueRecord', 'StackTopDisplayStyleShiftUp', None, None, 'Standard shift up applied to the top element of a stack in display style.'),
+ ('MathValueRecord', 'StackBottomShiftDown', None, None, 'Standard shift down applied to the bottom element of a stack. Positive for moving in the downward direction.'),
+ ('MathValueRecord', 'StackBottomDisplayStyleShiftDown', None, None, 'Standard shift down applied to the bottom element of a stack in display style. Positive for moving in the downward direction.'),
+ ('MathValueRecord', 'StackGapMin', None, None, 'Minimum gap between (ink) bottom of the top element of a stack, and the (ink) top of the bottom element. Suggested: 3x default rule thickness.'),
+ ('MathValueRecord', 'StackDisplayStyleGapMin', None, None, 'Minimum gap between (ink) bottom of the top element of a stack, and the (ink) top of the bottom element in display style. Suggested: 7x default rule thickness.'),
+ ('MathValueRecord', 'StretchStackTopShiftUp', None, None, 'Standard shift up applied to the top element of the stretch stack.'),
+ ('MathValueRecord', 'StretchStackBottomShiftDown', None, None, 'Standard shift down applied to the bottom element of the stretch stack. Positive for moving in the downward direction.'),
+ ('MathValueRecord', 'StretchStackGapAboveMin', None, None, 'Minimum gap between the ink of the stretched element, and the (ink) bottom of the element above. Suggested: UpperLimitGapMin'),
+ ('MathValueRecord', 'StretchStackGapBelowMin', None, None, 'Minimum gap between the ink of the stretched element, and the (ink) top of the element below. Suggested: LowerLimitGapMin.'),
+ ('MathValueRecord', 'FractionNumeratorShiftUp', None, None, 'Standard shift up applied to the numerator.'),
+ ('MathValueRecord', 'FractionNumeratorDisplayStyleShiftUp', None, None, 'Standard shift up applied to the numerator in display style. Suggested: StackTopDisplayStyleShiftUp.'),
+ ('MathValueRecord', 'FractionDenominatorShiftDown', None, None, 'Standard shift down applied to the denominator. Positive for moving in the downward direction.'),
+ ('MathValueRecord', 'FractionDenominatorDisplayStyleShiftDown', None, None, 'Standard shift down applied to the denominator in display style. Positive for moving in the downward direction. Suggested: StackBottomDisplayStyleShiftDown.'),
+ ('MathValueRecord', 'FractionNumeratorGapMin', None, None, 'Minimum tolerated gap between the (ink) bottom of the numerator and the ink of the fraction bar. Suggested: default rule thickness'),
+ ('MathValueRecord', 'FractionNumDisplayStyleGapMin', None, None, 'Minimum tolerated gap between the (ink) bottom of the numerator and the ink of the fraction bar in display style. Suggested: 3x default rule thickness.'),
+ ('MathValueRecord', 'FractionRuleThickness', None, None, 'Thickness of the fraction bar. Suggested: default rule thickness.'),
+ ('MathValueRecord', 'FractionDenominatorGapMin', None, None, 'Minimum tolerated gap between the (ink) top of the denominator and the ink of the fraction bar. Suggested: default rule thickness'),
+ ('MathValueRecord', 'FractionDenomDisplayStyleGapMin', None, None, 'Minimum tolerated gap between the (ink) top of the denominator and the ink of the fraction bar in display style. Suggested: 3x default rule thickness.'),
+ ('MathValueRecord', 'SkewedFractionHorizontalGap', None, None, 'Horizontal distance between the top and bottom elements of a skewed fraction.'),
+ ('MathValueRecord', 'SkewedFractionVerticalGap', None, None, 'Vertical distance between the ink of the top and bottom elements of a skewed fraction.'),
+ ('MathValueRecord', 'OverbarVerticalGap', None, None, 'Distance between the overbar and the (ink) top of he base. Suggested: 3x default rule thickness.'),
+ ('MathValueRecord', 'OverbarRuleThickness', None, None, 'Thickness of overbar. Suggested: default rule thickness.'),
+ ('MathValueRecord', 'OverbarExtraAscender', None, None, 'Extra white space reserved above the overbar. Suggested: default rule thickness.'),
+ ('MathValueRecord', 'UnderbarVerticalGap', None, None, 'Distance between underbar and (ink) bottom of the base. Suggested: 3x default rule thickness.'),
+ ('MathValueRecord', 'UnderbarRuleThickness', None, None, 'Thickness of underbar. Suggested: default rule thickness.'),
+ ('MathValueRecord', 'UnderbarExtraDescender', None, None, 'Extra white space reserved below the underbar. Always positive. Suggested: default rule thickness.'),
+ ('MathValueRecord', 'RadicalVerticalGap', None, None, 'Space between the (ink) top of the expression and the bar over it. Suggested: 1 1/4 default rule thickness.'),
+ ('MathValueRecord', 'RadicalDisplayStyleVerticalGap', None, None, 'Space between the (ink) top of the expression and the bar over it. Suggested: default rule thickness + 1/4 x-height.'),
+ ('MathValueRecord', 'RadicalRuleThickness', None, None, 'Thickness of the radical rule. This is the thickness of the rule in designed or constructed radical signs. Suggested: default rule thickness.'),
+ ('MathValueRecord', 'RadicalExtraAscender', None, None, 'Extra white space reserved above the radical. Suggested: RadicalRuleThickness.'),
+ ('MathValueRecord', 'RadicalKernBeforeDegree', None, None, 'Extra horizontal kern before the degree of a radical, if such is present. Suggested: 5/18 of em.'),
+ ('MathValueRecord', 'RadicalKernAfterDegree', None, None, 'Negative kern after the degree of a radical, if such is present. Suggested: 10/18 of em.'),
+ ('uint16', 'RadicalDegreeBottomRaisePercent', None, None, 'Height of the bottom of the radical degree, if such is present, in proportion to the ascender of the radical sign. Suggested: 60%.'),
+ ]),
+
+ ('MathGlyphInfo', [
+ ('Offset', 'MathItalicsCorrectionInfo', None, None, 'Offset to MathItalicsCorrectionInfo table - from the beginning of MathGlyphInfo table.'),
+ ('Offset', 'MathTopAccentAttachment', None, None, 'Offset to MathTopAccentAttachment table - from the beginning of MathGlyphInfo table.'),
+ ('Offset', 'ExtendedShapeCoverage', None, None, 'Offset to coverage table for Extended Shape glyphs - from the beginning of MathGlyphInfo table. When the left or right glyph of a box is an extended shape variant, the (ink) box (and not the default position defined by values in MathConstants table) should be used for vertical positioning purposes. May be NULL.'),
+ ('Offset', 'MathKernInfo', None, None, 'Offset to MathKernInfo table - from the beginning of MathGlyphInfo table.'),
+ ]),
+
+ ('MathItalicsCorrectionInfo', [
+ ('Offset', 'Coverage', None, None, 'Offset to Coverage table - from the beginning of MathItalicsCorrectionInfo table.'),
+ ('uint16', 'ItalicsCorrectionCount', None, None, 'Number of italics correction values. Should coincide with the number of covered glyphs.'),
+ ('MathValueRecord', 'ItalicsCorrection', 'ItalicsCorrectionCount', 0, 'Array of MathValueRecords defining italics correction values for each covered glyph.'),
+ ]),
+
+ ('MathTopAccentAttachment', [
+ ('Offset', 'TopAccentCoverage', None, None, 'Offset to Coverage table - from the beginning of MathTopAccentAttachment table.'),
+ ('uint16', 'TopAccentAttachmentCount', None, None, 'Number of top accent attachment point values. Should coincide with the number of covered glyphs'),
+ ('MathValueRecord', 'TopAccentAttachment', 'TopAccentAttachmentCount', 0, 'Array of MathValueRecords defining top accent attachment points for each covered glyph'),
+ ]),
+
+ ('MathKernInfo', [
+ ('Offset', 'MathKernCoverage', None, None, 'Offset to Coverage table - from the beginning of the MathKernInfo table.'),
+ ('uint16', 'MathKernCount', None, None, 'Number of MathKernInfoRecords.'),
+ ('MathKernInfoRecord', 'MathKernInfoRecords', 'MathKernCount', 0, 'Array of MathKernInfoRecords, per-glyph information for mathematical positioning of subscripts and superscripts.'),
+ ]),
+
+ ('MathKernInfoRecord', [
+ ('Offset', 'TopRightMathKern', None, None, 'Offset to MathKern table for top right corner - from the beginning of MathKernInfo table. May be NULL.'),
+ ('Offset', 'TopLeftMathKern', None, None, 'Offset to MathKern table for the top left corner - from the beginning of MathKernInfo table. May be NULL.'),
+ ('Offset', 'BottomRightMathKern', None, None, 'Offset to MathKern table for bottom right corner - from the beginning of MathKernInfo table. May be NULL.'),
+ ('Offset', 'BottomLeftMathKern', None, None, 'Offset to MathKern table for bottom left corner - from the beginning of MathKernInfo table. May be NULL.'),
+ ]),
+
+ ('MathKern', [
+ ('uint16', 'HeightCount', None, None, 'Number of heights on which the kern value changes.'),
+ ('MathValueRecord', 'CorrectionHeight', 'HeightCount', 0, 'Array of correction heights at which the kern value changes. Sorted by the height value in design units.'),
+ ('MathValueRecord', 'KernValue', 'HeightCount', 1, 'Array of kern values corresponding to heights. First value is the kern value for all heights less or equal than the first height in this table.Last value is the value to be applied for all heights greater than the last height in this table. Negative values are interpreted as move glyphs closer to each other.'),
+ ]),
+
+ ('MathVariants', [
+ ('uint16', 'MinConnectorOverlap', None, None, 'Minimum overlap of connecting glyphs during glyph construction, in design units.'),
+ ('Offset', 'VertGlyphCoverage', None, None, 'Offset to Coverage table - from the beginning of MathVariants table.'),
+ ('Offset', 'HorizGlyphCoverage', None, None, 'Offset to Coverage table - from the beginning of MathVariants table.'),
+ ('uint16', 'VertGlyphCount', None, None, 'Number of glyphs for which information is provided for vertically growing variants.'),
+ ('uint16', 'HorizGlyphCount', None, None, 'Number of glyphs for which information is provided for horizontally growing variants.'),
+ ('Offset', 'VertGlyphConstruction', 'VertGlyphCount', 0, 'Array of offsets to MathGlyphConstruction tables - from the beginning of the MathVariants table, for shapes growing in vertical direction.'),
+ ('Offset', 'HorizGlyphConstruction', 'HorizGlyphCount', 0, 'Array of offsets to MathGlyphConstruction tables - from the beginning of the MathVariants table, for shapes growing in horizontal direction.'),
+ ]),
+
+ ('MathGlyphConstruction', [
+ ('Offset', 'GlyphAssembly', None, None, 'Offset to GlyphAssembly table for this shape - from the beginning of MathGlyphConstruction table. May be NULL'),
+ ('uint16', 'VariantCount', None, None, 'Count of glyph growing variants for this glyph.'),
+ ('MathGlyphVariantRecord', 'MathGlyphVariantRecord', 'VariantCount', 0, 'MathGlyphVariantRecords for alternative variants of the glyphs.'),
+ ]),
+
+ ('MathGlyphVariantRecord', [
+ ('GlyphID', 'VariantGlyph', None, None, 'Glyph ID for the variant.'),
+ ('uint16', 'AdvanceMeasurement', None, None, 'Advance width/height, in design units, of the variant, in the direction of requested glyph extension.'),
+ ]),
+
+ ('GlyphAssembly', [
+ ('MathValueRecord', 'ItalicsCorrection', None, None, 'Italics correction of this GlyphAssembly. Should not depend on the assembly size.'),
+ ('uint16', 'PartCount', None, None, 'Number of parts in this assembly.'),
+ ('GlyphPartRecord', 'PartRecords', 'PartCount', 0, 'Array of part records, from left to right and bottom to top.'),
+ ]),
+
+ ('GlyphPartRecord', [
+ ('GlyphID', 'glyph', None, None, 'Glyph ID for the part.'),
+ ('uint16', 'StartConnectorLength', None, None, 'Advance width/ height of the straight bar connector material, in design units, is at the beginning of the glyph, in the direction of the extension.'),
+ ('uint16', 'EndConnectorLength', None, None, 'Advance width/ height of the straight bar connector material, in design units, is at the end of the glyph, in the direction of the extension.'),
+ ('uint16', 'FullAdvance', None, None, 'Full advance width/height for this part, in the direction of the extension. In design units.'),
+ ('uint16', 'PartFlags', None, None, 'Part qualifiers. PartFlags enumeration currently uses only one bit: 0x0001 fExtender: If set, the part can be skipped or repeated. 0xFFFE Reserved'),
+ ]),
+
+]
diff --git a/Lib/fontTools/ttLib/tables/otTables.py b/Lib/fontTools/ttLib/tables/otTables.py
index bd02393..5d3d08b 100644
--- a/Lib/fontTools/ttLib/tables/otTables.py
+++ b/Lib/fontTools/ttLib/tables/otTables.py
@@ -4,24 +4,33 @@
Most are constructed upon import from data in otData.py, all are populated with
converter objects from otConverters.py.
"""
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
+from .otBase import BaseTable, FormatSwitchingBaseTable
import operator
-from otBase import BaseTable, FormatSwitchingBaseTable
-from types import TupleType
+import warnings
class LookupOrder(BaseTable):
"""Dummy class; this table isn't defined, but is used, and is always NULL."""
-
class FeatureParams(BaseTable):
- """This class has been used by Adobe, but but this one implementation was done wrong.
- No other use has been made, becuase there is no way to know how to interpret
- the data at the offset.. For now, if we see one, just skip the data on
- decompiling and dumping to XML. """
- # XXX The above is no longer true; the 'size' feature uses FeatureParams now.
- def __init__(self):
- BaseTable.__init__(self)
- self.converters = []
+
+ def compile(self, writer, font):
+ assert featureParamTypes.get(writer['FeatureTag']) == self.__class__, "Wrong FeatureParams type for feature '%s': %s" % (writer['FeatureTag'], self.__class__.__name__)
+ BaseTable.compile(self, writer, font)
+
+ def toXML(self, xmlWriter, font, attrs=None, name=None):
+ BaseTable.toXML(self, xmlWriter, font, attrs, name=self.__class__.__name__)
+
+class FeatureParamsSize(FeatureParams):
+ pass
+
+class FeatureParamsStylisticSet(FeatureParams):
+ pass
+
+class FeatureParamsCharacterVariants(FeatureParams):
+ pass
class Coverage(FormatSwitchingBaseTable):
@@ -29,25 +38,43 @@
def postRead(self, rawTable, font):
if self.Format == 1:
+ # TODO only allow glyphs that are valid?
self.glyphs = rawTable["GlyphArray"]
elif self.Format == 2:
glyphs = self.glyphs = []
ranges = rawTable["RangeRecord"]
glyphOrder = font.getGlyphOrder()
+ # Some SIL fonts have coverage entries that don't have sorted
+ # StartCoverageIndex. If it is so, fixup and warn. We undo
+ # this when writing font out.
+ sorted_ranges = sorted(ranges, key=lambda a: a.StartCoverageIndex)
+ if ranges != sorted_ranges:
+ warnings.warn("GSUB/GPOS Coverage is not sorted by glyph ids.")
+ ranges = sorted_ranges
+ del sorted_ranges
for r in ranges:
assert r.StartCoverageIndex == len(glyphs), \
(r.StartCoverageIndex, len(glyphs))
start = r.Start
end = r.End
- startID = font.getGlyphID(start)
- endID = font.getGlyphID(end)
- glyphs.append(start)
- rangeList = [glyphOrder[glyphID] for glyphID in range(startID + 1, endID) ]
- glyphs += rangeList
- if start != end:
- glyphs.append(end)
+ try:
+ startID = font.getGlyphID(start, requireReal=True)
+ except KeyError:
+ warnings.warn("Coverage table has start glyph ID out of range: %s." % start)
+ continue
+ try:
+ endID = font.getGlyphID(end, requireReal=True) + 1
+ except KeyError:
+ # Apparently some tools use 65535 to "match all" the range
+ if end != 'glyph65535':
+ warnings.warn("Coverage table has end glyph ID out of range: %s." % end)
+ # NOTE: We clobber out-of-range things here. There are legit uses for those,
+ # but none that we have seen in the wild.
+ endID = len(glyphOrder)
+ glyphs.extend(glyphOrder[glyphID] for glyphID in range(startID, endID))
else:
assert 0, "unknown format: %s" % self.Format
+ del self.Format # Don't need this anymore
def preWrite(self, font):
glyphs = getattr(self, "glyphs", None)
@@ -59,6 +86,7 @@
if glyphs:
# find out whether Format 2 is more compact or not
glyphIDs = [getGlyphID(glyphName) for glyphName in glyphs ]
+ brokenOrder = sorted(glyphIDs) != glyphIDs
last = glyphIDs[0]
ranges = [[last]]
@@ -69,17 +97,23 @@
last = glyphID
ranges[-1].append(last)
- if len(ranges) * 3 < len(glyphs): # 3 words vs. 1 word
+ if brokenOrder or len(ranges) * 3 < len(glyphs): # 3 words vs. 1 word
# Format 2 is more compact
index = 0
for i in range(len(ranges)):
start, end = ranges[i]
r = RangeRecord()
+ r.StartID = start
r.Start = font.getGlyphName(start)
r.End = font.getGlyphName(end)
r.StartCoverageIndex = index
ranges[i] = r
index = index + end - start + 1
+ if brokenOrder:
+ warnings.warn("GSUB/GPOS Coverage is not sorted by glyph ids.")
+ ranges.sort(key=lambda a: a.StartID)
+ for r in ranges:
+ del r.StartID
format = 2
rawTable = {"RangeRecord": ranges}
#else:
@@ -92,7 +126,7 @@
xmlWriter.simpletag("Glyph", value=glyphName)
xmlWriter.newline()
- def fromXML(self, (name, attrs, content), font):
+ def fromXML(self, name, attrs, content, font):
glyphs = getattr(self, "glyphs", None)
if glyphs is None:
glyphs = []
@@ -100,55 +134,6 @@
glyphs.append(attrs["value"])
-class LookupList(BaseTable):
- def preCompile(self):
- """ This function is used to optimize writing out extension subtables. This is useful
- when a font has been read in, modified, and we are now writing out a new version. If the
- the extension subtables have not been touched (proof being that they have not been decompiled)
- then we can write them out using the original data, and do not have to recompile them. This can save
- 20-30% of the compile time for fonts with large extension tables, such as Japanese Pro fonts."""
-
- if hasattr(self, 'LookupCount'): #not defined if loading from xml
- lookupCount = self.LookupCount
- else:
- return # The optimization of not recompiling extension lookup subtables is not possible
- # when reading from XML.
-
- liRange = range(lookupCount)
- extTables = []
- for li in liRange:
- lookup = self.Lookup[li]
- if hasattr(lookup, 'SubTableCount'): #not defined if loading from xml
- subtableCount = lookup.SubTableCount
- else:
- subtableCount = len(lookup.SubTable)
- siRange = range(subtableCount)
- for si in siRange:
- subtable = lookup.SubTable[si]
- if hasattr(subtable, 'ExtSubTable'):
- extTable = subtable.ExtSubTable
- extTables.append([extTable.start, extTable] )
-
- # Since offsets in one subtable can and do point forward into later
- # subtables, we can afford to simply copy data only for the last subtables
- # which were not decompiled. So we start figuring out the
- # data segments starting with the last subtTable, and work our way towards
- # the first subtable, and then quit as soon as we see a subtable that was decompiled.
- if extTables:
- extTables.sort()
- extTables.reverse()
- lastTable = extTables[0][1]
- if lastTable.compileStatus == 1:
- lastTable.end = len(lastTable.reader.data)
- lastTable.compileStatus = 3
- for i in range(1, len(extTables)):
- extTable = extTables[i][1]
- if extTable.compileStatus != 1:
- break
- extTable.end = lastTable.start
- extTable.compileStatus = 3
- lastTable = extTable
-
def doModulo(value):
if value < 0:
return value + 65536
@@ -163,29 +148,28 @@
if self.Format == 1:
delta = rawTable["DeltaGlyphID"]
inputGIDS = [ font.getGlyphID(name) for name in input ]
- inputGIDS = map(doModulo, inputGIDS)
outGIDS = [ glyphID + delta for glyphID in inputGIDS ]
- outGIDS = map(doModulo, outGIDS)
+ outGIDS = map(doModulo, outGIDS)
outNames = [ font.getGlyphName(glyphID) for glyphID in outGIDS ]
- map(operator.setitem, [mapping]*lenMapping, input, outNames)
+ list(map(operator.setitem, [mapping]*lenMapping, input, outNames))
elif self.Format == 2:
assert len(input) == rawTable["GlyphCount"], \
"invalid SingleSubstFormat2 table"
subst = rawTable["Substitute"]
- map(operator.setitem, [mapping]*lenMapping, input, subst)
+ list(map(operator.setitem, [mapping]*lenMapping, input, subst))
else:
assert 0, "unknown format: %s" % self.Format
self.mapping = mapping
+ del self.Format # Don't need this anymore
def preWrite(self, font):
mapping = getattr(self, "mapping", None)
if mapping is None:
mapping = self.mapping = {}
- items = mapping.items()
+ items = list(mapping.items())
getGlyphID = font.getGlyphID
- gidItems = [(getGlyphID(item[0]), getGlyphID(item[1])) for item in items]
- sortableItems = zip(gidItems, items)
- sortableItems.sort()
+ gidItems = [(getGlyphID(a), getGlyphID(b)) for a,b in items]
+ sortableItems = sorted(zip(gidItems, items))
# figure out format
format = 2
@@ -193,6 +177,10 @@
for inID, outID in gidItems:
if delta is None:
delta = outID - inID
+ if delta < -32768:
+ delta += 65536
+ elif delta > 32767:
+ delta -= 65536
else:
if delta != outID - inID:
break
@@ -214,14 +202,13 @@
return rawTable
def toXML2(self, xmlWriter, font):
- items = self.mapping.items()
- items.sort()
+ items = sorted(self.mapping.items())
for inGlyph, outGlyph in items:
xmlWriter.simpletag("Substitution",
[("in", inGlyph), ("out", outGlyph)])
xmlWriter.newline()
- def fromXML(self, (name, attrs, content), font):
+ def fromXML(self, name, attrs, content, font):
mapping = getattr(self, "mapping", None)
if mapping is None:
mapping = {}
@@ -233,17 +220,25 @@
def postRead(self, rawTable, font):
classDefs = {}
- getGlyphName = font.getGlyphName
+ glyphOrder = font.getGlyphOrder()
if self.Format == 1:
start = rawTable["StartGlyph"]
classList = rawTable["ClassValueArray"]
- lenList = len(classList)
- glyphID = font.getGlyphID(start)
- gidList = range(glyphID, glyphID + len(classList))
- keyList = [getGlyphName(glyphID) for glyphID in gidList]
+ try:
+ startID = font.getGlyphID(start, requireReal=True)
+ except KeyError:
+ warnings.warn("ClassDef table has start glyph ID out of range: %s." % start)
+ startID = len(glyphOrder)
+ endID = startID + len(classList)
+ if endID > len(glyphOrder):
+ warnings.warn("ClassDef table has entries for out of range glyph IDs: %s,%s." % (start, len(classList)))
+ # NOTE: We clobber out-of-range things here. There are legit uses for those,
+ # but none that we have seen in the wild.
+ endID = len(glyphOrder)
- map(operator.setitem, [classDefs]*lenList, keyList, classList)
+ for glyphID, cls in zip(range(startID, endID), classList):
+ classDefs[glyphOrder[glyphID]] = cls
elif self.Format == 2:
records = rawTable["ClassRangeRecord"]
@@ -251,21 +246,34 @@
start = rec.Start
end = rec.End
cls = rec.Class
- classDefs[start] = cls
- glyphIDs = range(font.getGlyphID(start) + 1, font.getGlyphID(end))
- lenList = len(glyphIDs)
- keyList = [getGlyphName(glyphID) for glyphID in glyphIDs]
- map(operator.setitem, [classDefs]*lenList, keyList, [cls]*lenList)
- classDefs[end] = cls
+ try:
+ startID = font.getGlyphID(start, requireReal=True)
+ except KeyError:
+ warnings.warn("ClassDef table has start glyph ID out of range: %s." % start)
+ continue
+ try:
+ endID = font.getGlyphID(end, requireReal=True) + 1
+ except KeyError:
+ # Apparently some tools use 65535 to "match all" the range
+ if end != 'glyph65535':
+ warnings.warn("ClassDef table has end glyph ID out of range: %s." % end)
+ # NOTE: We clobber out-of-range things here. There are legit uses for those,
+ # but none that we have seen in the wild.
+ endID = len(glyphOrder)
+ for glyphID in range(startID, endID):
+ classDefs[glyphOrder[glyphID]] = cls
else:
assert 0, "unknown format: %s" % self.Format
self.classDefs = classDefs
+ del self.Format # Don't need this anymore
def preWrite(self, font):
classDefs = getattr(self, "classDefs", None)
if classDefs is None:
classDefs = self.classDefs = {}
- items = classDefs.items()
+ items = list(classDefs.items())
+ format = 2
+ rawTable = {"ClassRangeRecord": []}
getGlyphID = font.getGlyphID
for i in range(len(items)):
glyphName, cls = items[i]
@@ -273,34 +281,49 @@
items.sort()
if items:
last, lastName, lastCls = items[0]
- rec = ClassRangeRecord()
- rec.Start = lastName
- rec.Class = lastCls
- ranges = [rec]
+ ranges = [[lastCls, last, lastName]]
for glyphID, glyphName, cls in items[1:]:
if glyphID != last + 1 or cls != lastCls:
- rec.End = lastName
- rec = ClassRangeRecord()
- rec.Start = glyphName
- rec.Class = cls
- ranges.append(rec)
+ ranges[-1].extend([last, lastName])
+ ranges.append([cls, glyphID, glyphName])
last = glyphID
lastName = glyphName
lastCls = cls
- rec.End = lastName
- else:
- ranges = []
- self.Format = 2 # currently no support for Format 1
- return {"ClassRangeRecord": ranges}
+ ranges[-1].extend([last, lastName])
+
+ startGlyph = ranges[0][1]
+ endGlyph = ranges[-1][3]
+ glyphCount = endGlyph - startGlyph + 1
+ if len(ranges) * 3 < glyphCount + 1:
+ # Format 2 is more compact
+ for i in range(len(ranges)):
+ cls, start, startName, end, endName = ranges[i]
+ rec = ClassRangeRecord()
+ rec.Start = startName
+ rec.End = endName
+ rec.Class = cls
+ ranges[i] = rec
+ format = 2
+ rawTable = {"ClassRangeRecord": ranges}
+ else:
+ # Format 1 is more compact
+ startGlyphName = ranges[0][2]
+ classes = [0] * glyphCount
+ for cls, start, startName, end, endName in ranges:
+ for g in range(start - startGlyph, end - startGlyph + 1):
+ classes[g] = cls
+ format = 1
+ rawTable = {"StartGlyph": startGlyphName, "ClassValueArray": classes}
+ self.Format = format
+ return rawTable
def toXML2(self, xmlWriter, font):
- items = self.classDefs.items()
- items.sort()
+ items = sorted(self.classDefs.items())
for glyphName, cls in items:
xmlWriter.simpletag("ClassDef", [("glyph", glyphName), ("class", cls)])
xmlWriter.newline()
- def fromXML(self, (name, attrs, content), font):
+ def fromXML(self, name, attrs, content, font):
classDefs = getattr(self, "classDefs", None)
if classDefs is None:
classDefs = {}
@@ -322,13 +345,14 @@
else:
assert 0, "unknown format: %s" % self.Format
self.alternates = alternates
+ del self.Format # Don't need this anymore
def preWrite(self, font):
self.Format = 1
alternates = getattr(self, "alternates", None)
if alternates is None:
alternates = self.alternates = {}
- items = alternates.items()
+ items = list(alternates.items())
for i in range(len(items)):
glyphName, set = items[i]
items[i] = font.getGlyphID(glyphName), glyphName, set
@@ -350,8 +374,7 @@
return {"Coverage": cov, "AlternateSet": alternates}
def toXML2(self, xmlWriter, font):
- items = self.alternates.items()
- items.sort()
+ items = sorted(self.alternates.items())
for glyphName, alternates in items:
xmlWriter.begintag("AlternateSet", glyph=glyphName)
xmlWriter.newline()
@@ -361,7 +384,7 @@
xmlWriter.endtag("AlternateSet")
xmlWriter.newline()
- def fromXML(self, (name, attrs, content), font):
+ def fromXML(self, name, attrs, content, font):
alternates = getattr(self, "alternates", None)
if alternates is None:
alternates = {}
@@ -370,7 +393,7 @@
set = []
alternates[glyphName] = set
for element in content:
- if type(element) != TupleType:
+ if not isinstance(element, tuple):
continue
name, attrs, content = element
set.append(attrs["glyph"])
@@ -381,7 +404,7 @@
def postRead(self, rawTable, font):
ligatures = {}
if self.Format == 1:
- input = rawTable["Coverage"].glyphs
+ input = _getGlyphsFromCoverageTable(rawTable["Coverage"])
ligSets = rawTable["LigatureSet"]
assert len(input) == len(ligSets)
for i in range(len(input)):
@@ -389,12 +412,14 @@
else:
assert 0, "unknown format: %s" % self.Format
self.ligatures = ligatures
+ del self.Format # Don't need this anymore
def preWrite(self, font):
+ self.Format = 1
ligatures = getattr(self, "ligatures", None)
if ligatures is None:
ligatures = self.ligatures = {}
- items = ligatures.items()
+ items = list(ligatures.items())
for i in range(len(items)):
glyphName, set = items[i]
items[i] = font.getGlyphID(glyphName), glyphName, set
@@ -417,8 +442,7 @@
return {"Coverage": cov, "LigatureSet": ligSets}
def toXML2(self, xmlWriter, font):
- items = self.ligatures.items()
- items.sort()
+ items = sorted(self.ligatures.items())
for glyphName, ligSets in items:
xmlWriter.begintag("LigatureSet", glyph=glyphName)
xmlWriter.newline()
@@ -429,7 +453,7 @@
xmlWriter.endtag("LigatureSet")
xmlWriter.newline()
- def fromXML(self, (name, attrs, content), font):
+ def fromXML(self, name, attrs, content, font):
ligatures = getattr(self, "ligatures", None)
if ligatures is None:
ligatures = {}
@@ -438,7 +462,7 @@
ligs = []
ligatures[glyphName] = ligs
for element in content:
- if type(element) != TupleType:
+ if not isinstance(element, tuple):
continue
name, attrs, content = element
lig = Ligature()
@@ -459,7 +483,8 @@
'LangSys': ('DefaultLangSys',),
'Coverage': ('MarkCoverage', 'BaseCoverage', 'LigatureCoverage', 'Mark1Coverage',
'Mark2Coverage', 'BacktrackCoverage', 'InputCoverage',
- 'LookAheadCoverage'),
+ 'LookAheadCoverage', 'VertGlyphCoverage', 'HorizGlyphCoverage',
+ 'TopAccentCoverage', 'ExtendedShapeCoverage', 'MathKernCoverage'),
'ClassDef': ('ClassDef1', 'ClassDef2', 'BacktrackClassDef', 'InputClassDef',
'LookAheadClassDef', 'GlyphClassDef', 'MarkAttachClassDef'),
'Anchor': ('EntryAnchor', 'ExitAnchor', 'BaseAnchor', 'LigatureAnchor',
@@ -475,6 +500,9 @@
'JstfGPOSModList': ('ShrinkageEnableGPOS', 'ShrinkageDisableGPOS', 'ExtensionEnableGPOS',
'ExtensionDisableGPOS',),
'JstfMax': ('ShrinkageJstfMax', 'ExtensionJstfMax',),
+ 'MathKern': ('TopRightMathKern', 'TopLeftMathKern', 'BottomRightMathKern',
+ 'BottomLeftMathKern'),
+ 'MathGlyphConstruction': ('VertGlyphConstruction', 'HorizGlyphConstruction'),
}
#
@@ -501,14 +529,14 @@
SubTable[0] and contents
...
SubTable[n] and contents
- If the offset to a lookup overflowed (SubTableIndex == None)
+ If the offset to a lookup overflowed (SubTableIndex is None)
we must promote the *previous* lookup to an Extension type.
If the offset from a lookup to subtable overflowed, then we must promote it
to an Extension Lookup type.
"""
ok = 0
lookupIndex = overflowRecord.LookupListIndex
- if (overflowRecord.SubTableIndex == None):
+ if (overflowRecord.SubTableIndex is None):
lookupIndex = lookupIndex - 1
if lookupIndex < 0:
return ok
@@ -544,14 +572,13 @@
if hasattr(oldSubTable, 'sortCoverageLast'):
newSubTable.sortCoverageLast = oldSubTable.sortCoverageLast
- oldAlts = oldSubTable.alternates.items()
- oldAlts.sort()
+ oldAlts = sorted(oldSubTable.alternates.items())
oldLen = len(oldAlts)
if overflowRecord.itemName in [ 'Coverage', 'RangeRecord']:
# Coverage table is written last. overflow is to or within the
# the coverage table. We will just cut the subtable in half.
- newLen = int(oldLen/2)
+ newLen = oldLen//2
elif overflowRecord.itemName == 'AlternateSet':
# We just need to back up by two items
@@ -573,14 +600,13 @@
def splitLigatureSubst(oldSubTable, newSubTable, overflowRecord):
ok = 1
newSubTable.Format = oldSubTable.Format
- oldLigs = oldSubTable.ligatures.items()
- oldLigs.sort()
+ oldLigs = sorted(oldSubTable.ligatures.items())
oldLen = len(oldLigs)
if overflowRecord.itemName in [ 'Coverage', 'RangeRecord']:
# Coverage table is written last. overflow is to or within the
# the coverage table. We will just cut the subtable in half.
- newLen = int(oldLen/2)
+ newLen = oldLen//2
elif overflowRecord.itemName == 'LigatureSet':
# We just need to back up by two items
@@ -669,8 +695,8 @@
def _buildClasses():
- import new, re
- from otData import otData
+ import re
+ from .otData import otData
formatPat = re.compile("([A-Za-z0-9]+)Format(\d+)$")
namespace = globals()
@@ -683,15 +709,15 @@
# XxxFormatN subtable, we only add the "base" table
name = m.group(1)
baseClass = FormatSwitchingBaseTable
- if not namespace.has_key(name):
+ if name not in namespace:
# the class doesn't exist yet, so the base implementation is used.
- cls = new.classobj(name, (baseClass,), {})
+ cls = type(name, (baseClass,), {})
namespace[name] = cls
for base, alts in _equivalents.items():
base = namespace[base]
for alt in alts:
- namespace[alt] = new.classobj(alt, (base,), {})
+ namespace[alt] = type(alt, (base,), {})
global lookupTypes
lookupTypes = {
@@ -721,9 +747,18 @@
for lookupEnum in lookupTypes.values():
for enum, cls in lookupEnum.items():
cls.LookupType = enum
+
+ global featureParamTypes
+ featureParamTypes = {
+ 'size': FeatureParamsSize,
+ }
+ for i in range(1, 20+1):
+ featureParamTypes['ss%02d' % i] = FeatureParamsStylisticSet
+ for i in range(1, 99+1):
+ featureParamTypes['cv%02d' % i] = FeatureParamsCharacterVariants
# add converters to classes
- from otConverters import buildConverters
+ from .otConverters import buildConverters
for name, table in otData:
m = formatPat.match(name)
if m:
diff --git a/Lib/fontTools/ttLib/tables/sbixBitmap.py b/Lib/fontTools/ttLib/tables/sbixBitmap.py
new file mode 100644
index 0000000..991b547
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/sbixBitmap.py
@@ -0,0 +1,104 @@
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
+from fontTools.misc import sstruct
+from fontTools.misc.textTools import readHex
+import struct
+
+
+sbixBitmapHeaderFormat = """
+ >
+ usReserved1: H # 00 00
+ usReserved2: H # 00 00
+ imageFormatTag: 4s # e.g. "png "
+"""
+
+sbixBitmapHeaderFormatSize = sstruct.calcsize(sbixBitmapHeaderFormat)
+
+
+class Bitmap(object):
+ def __init__(self, glyphName=None, referenceGlyphName=None, usReserved1=0, usReserved2=0, imageFormatTag=None, imageData=None, rawdata=None, gid=0):
+ self.gid = gid
+ self.glyphName = glyphName
+ self.referenceGlyphName = referenceGlyphName
+ self.usReserved1 = usReserved1
+ self.usReserved2 = usReserved2
+ self.rawdata = rawdata
+ self.imageFormatTag = imageFormatTag
+ self.imageData = imageData
+
+ def decompile(self, ttFont):
+ self.glyphName = ttFont.getGlyphName(self.gid)
+ if self.rawdata is None:
+ from fontTools import ttLib
+ raise ttLib.TTLibError("No table data to decompile")
+ if len(self.rawdata) > 0:
+ if len(self.rawdata) < sbixBitmapHeaderFormatSize:
+ from fontTools import ttLib
+ #print "Bitmap %i header too short: Expected %x, got %x." % (self.gid, sbixBitmapHeaderFormatSize, len(self.rawdata))
+ raise ttLib.TTLibError("Bitmap header too short.")
+
+ sstruct.unpack(sbixBitmapHeaderFormat, self.rawdata[:sbixBitmapHeaderFormatSize], self)
+
+ if self.imageFormatTag == "dupe":
+ # bitmap is a reference to another glyph's bitmap
+ gid, = struct.unpack(">H", self.rawdata[sbixBitmapHeaderFormatSize:])
+ self.referenceGlyphName = ttFont.getGlyphName(gid)
+ else:
+ self.imageData = self.rawdata[sbixBitmapHeaderFormatSize:]
+ self.referenceGlyphName = None
+ # clean up
+ del self.rawdata
+ del self.gid
+
+ def compile(self, ttFont):
+ if self.glyphName is None:
+ from fontTools import ttLib
+ raise ttLib.TTLibError("Can't compile bitmap without glyph name")
+ # TODO: if ttFont has no maxp, cmap etc., ignore glyph names and compile by index?
+ # (needed if you just want to compile the sbix table on its own)
+ self.gid = struct.pack(">H", ttFont.getGlyphID(self.glyphName))
+ if self.imageFormatTag is None:
+ self.rawdata = ""
+ else:
+ self.rawdata = sstruct.pack(sbixBitmapHeaderFormat, self) + self.imageData
+
+ def toXML(self, xmlWriter, ttFont):
+ if self.imageFormatTag == None:
+ # TODO: ignore empty bitmaps?
+ # a bitmap entry is required for each glyph,
+ # but empty ones can be calculated at compile time
+ xmlWriter.simpletag("bitmap", glyphname=self.glyphName)
+ xmlWriter.newline()
+ return
+ xmlWriter.begintag("bitmap", format=self.imageFormatTag, glyphname=self.glyphName)
+ xmlWriter.newline()
+ #xmlWriter.simpletag("usReserved1", value=self.usReserved1)
+ #xmlWriter.newline()
+ #xmlWriter.simpletag("usReserved2", value=self.usReserved2)
+ #xmlWriter.newline()
+ if self.imageFormatTag == "dupe":
+ # format == "dupe" is apparently a reference to another glyph id.
+ xmlWriter.simpletag("ref", glyphname=self.referenceGlyphName)
+ else:
+ xmlWriter.begintag("hexdata")
+ xmlWriter.newline()
+ xmlWriter.dumphex(self.imageData)
+ xmlWriter.endtag("hexdata")
+ xmlWriter.newline()
+ xmlWriter.endtag("bitmap")
+ xmlWriter.newline()
+
+ def fromXML(self, name, attrs, content, ttFont):
+ #if name in ["usReserved1", "usReserved2"]:
+ # setattr(self, name, int(attrs["value"]))
+ #elif
+ if name == "ref":
+ # bitmap is a "dupe", i.e. a reference to another bitmap.
+ # in this case imageData contains the glyph id of the reference glyph
+ # get glyph id from glyphname
+ self.imageData = struct.pack(">H", ttFont.getGlyphID(attrs["glyphname"]))
+ elif name == "hexdata":
+ self.imageData = readHex(content)
+ else:
+ from fontTools import ttLib
+ raise ttLib.TTLibError("can't handle '%s' element" % name)
diff --git a/Lib/fontTools/ttLib/tables/sbixBitmapSet.py b/Lib/fontTools/ttLib/tables/sbixBitmapSet.py
new file mode 100644
index 0000000..9798666
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/sbixBitmapSet.py
@@ -0,0 +1,138 @@
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
+from fontTools.misc import sstruct
+from fontTools.misc.textTools import readHex
+from .sbixBitmap import *
+import struct
+
+sbixBitmapSetHeaderFormat = """
+ >
+ size: H # 00 28
+ resolution: H # 00 48
+"""
+
+sbixBitmapOffsetEntryFormat = """
+ >
+ ulOffset: L # 00 00 07 E0 # Offset from start of first offset entry to each bitmap
+"""
+
+sbixBitmapSetHeaderFormatSize = sstruct.calcsize(sbixBitmapSetHeaderFormat)
+sbixBitmapOffsetEntryFormatSize = sstruct.calcsize(sbixBitmapOffsetEntryFormat)
+
+
+class BitmapSet(object):
+ def __init__(self, rawdata=None, size=0, resolution=72):
+ self.data = rawdata
+ self.size = size
+ self.resolution = resolution
+ self.bitmaps = {}
+
+ def decompile(self, ttFont):
+ if self.data is None:
+ from fontTools import ttLib
+ raise ttLib.TTLibError
+ if len(self.data) < sbixBitmapSetHeaderFormatSize:
+ from fontTools import ttLib
+ raise(ttLib.TTLibError, "BitmapSet header too short: Expected %x, got %x.") \
+ % (sbixBitmapSetHeaderFormatSize, len(self.data))
+
+ # read BitmapSet header from raw data
+ sstruct.unpack(sbixBitmapSetHeaderFormat, self.data[:sbixBitmapSetHeaderFormatSize], self)
+
+ # calculate number of bitmaps
+ firstBitmapOffset, = struct.unpack(">L", \
+ self.data[sbixBitmapSetHeaderFormatSize : sbixBitmapSetHeaderFormatSize + sbixBitmapOffsetEntryFormatSize])
+ self.numBitmaps = (firstBitmapOffset - sbixBitmapSetHeaderFormatSize) // sbixBitmapOffsetEntryFormatSize - 1
+ # ^ -1 because there's one more offset than bitmaps
+
+ # build offset list for single bitmap offsets
+ self.bitmapOffsets = []
+ for i in range(self.numBitmaps + 1): # + 1 because there's one more offset than bitmaps
+ start = i * sbixBitmapOffsetEntryFormatSize + sbixBitmapSetHeaderFormatSize
+ myOffset, = struct.unpack(">L", self.data[start : start + sbixBitmapOffsetEntryFormatSize])
+ self.bitmapOffsets.append(myOffset)
+
+ # iterate through offset list and slice raw data into bitmaps
+ for i in range(self.numBitmaps):
+ myBitmap = Bitmap(rawdata=self.data[self.bitmapOffsets[i] : self.bitmapOffsets[i+1]], gid=i)
+ myBitmap.decompile(ttFont)
+ self.bitmaps[myBitmap.glyphName] = myBitmap
+ del self.bitmapOffsets
+ del self.data
+
+ def compile(self, ttFont):
+ self.bitmapOffsets = ""
+ self.bitmapData = ""
+
+ glyphOrder = ttFont.getGlyphOrder()
+
+ # first bitmap starts right after the header
+ bitmapOffset = sbixBitmapSetHeaderFormatSize + sbixBitmapOffsetEntryFormatSize * (len(glyphOrder) + 1)
+ for glyphName in glyphOrder:
+ if glyphName in self.bitmaps:
+ # we have a bitmap for this glyph
+ myBitmap = self.bitmaps[glyphName]
+ else:
+ # must add empty bitmap for this glyph
+ myBitmap = Bitmap(glyphName=glyphName)
+ myBitmap.compile(ttFont)
+ myBitmap.ulOffset = bitmapOffset
+ self.bitmapData += myBitmap.rawdata
+ bitmapOffset += len(myBitmap.rawdata)
+ self.bitmapOffsets += sstruct.pack(sbixBitmapOffsetEntryFormat, myBitmap)
+
+ # add last "offset", really the end address of the last bitmap
+ dummy = Bitmap()
+ dummy.ulOffset = bitmapOffset
+ self.bitmapOffsets += sstruct.pack(sbixBitmapOffsetEntryFormat, dummy)
+
+ # bitmap sets are padded to 4 byte boundaries
+ dataLength = len(self.bitmapOffsets) + len(self.bitmapData)
+ if dataLength % 4 != 0:
+ padding = 4 - (dataLength % 4)
+ else:
+ padding = 0
+
+ # pack header
+ self.data = sstruct.pack(sbixBitmapSetHeaderFormat, self)
+ # add offset, image data and padding after header
+ self.data += self.bitmapOffsets + self.bitmapData + "\0" * padding
+
+ def toXML(self, xmlWriter, ttFont):
+ xmlWriter.begintag("bitmapSet")
+ xmlWriter.newline()
+ xmlWriter.simpletag("size", value=self.size)
+ xmlWriter.newline()
+ xmlWriter.simpletag("resolution", value=self.resolution)
+ xmlWriter.newline()
+ glyphOrder = ttFont.getGlyphOrder()
+ for i in range(len(glyphOrder)):
+ if glyphOrder[i] in self.bitmaps:
+ self.bitmaps[glyphOrder[i]].toXML(xmlWriter, ttFont)
+ # TODO: what if there are more bitmaps than glyphs?
+ xmlWriter.endtag("bitmapSet")
+ xmlWriter.newline()
+
+ def fromXML(self, name, attrs, content, ttFont):
+ if name in ["size", "resolution"]:
+ setattr(self, name, int(attrs["value"]))
+ elif name == "bitmap":
+ if "format" in attrs:
+ myFormat = attrs["format"]
+ else:
+ myFormat = None
+ if "glyphname" in attrs:
+ myGlyphName = attrs["glyphname"]
+ else:
+ from fontTools import ttLib
+ raise ttLib.TTLibError("Bitmap must have a glyph name.")
+ myBitmap = Bitmap(glyphName=myGlyphName, imageFormatTag=myFormat)
+ for element in content:
+ if isinstance(element, tuple):
+ name, attrs, content = element
+ myBitmap.fromXML(name, attrs, content, ttFont)
+ myBitmap.compile(ttFont)
+ self.bitmaps[myBitmap.glyphName] = myBitmap
+ else:
+ from fontTools import ttLib
+ raise ttLib.TTLibError("can't handle '%s' element" % name)
diff --git a/Lib/fontTools/ttLib/tables/ttProgram.py b/Lib/fontTools/ttLib/tables/ttProgram.py
index fa6ea69..17c15f2 100644
--- a/Lib/fontTools/ttLib/tables/ttProgram.py
+++ b/Lib/fontTools/ttLib/tables/ttProgram.py
@@ -1,8 +1,10 @@
"""ttLib.tables.ttProgram.py -- Assembler/disassembler for TrueType bytecode programs."""
-import array
-import re, string
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
from fontTools.misc.textTools import num2binary, binary2num, readHex
+import array
+import re
# first, the list of instructions that eat bytes or words from the instruction stream
@@ -161,13 +163,13 @@
mnemonicDict = {}
for op, mnemonic, argBits, name, pops, pushes in instructionList:
assert _mnemonicPat.match(mnemonic)
- mnemonicDict[mnemonic] = op, argBits, name
+ mnemonicDict[mnemonic] = op, argBits
if argBits:
argoffset = op
for i in range(1 << argBits):
- opcodeDict[op+i] = mnemonic, argBits, argoffset, name
+ opcodeDict[op+i] = mnemonic, argBits, argoffset
else:
- opcodeDict[op] = mnemonic, 0, 0, name
+ opcodeDict[op] = mnemonic, 0, 0
return opcodeDict, mnemonicDict
streamOpcodeDict, streamMnemonicDict = _makeDict(streamInstructions)
@@ -191,14 +193,14 @@
_pushCountPat = re.compile(r"[A-Z][A-Z0-9]*\s*\[.*?\]\s*/\* ([0-9]*).*?\*/")
-def _skipWhite(data, pos, _whiteRE=_whiteRE):
+def _skipWhite(data, pos):
m = _whiteRE.match(data, pos)
newPos = m.regs[0][1]
assert newPos >= pos
return newPos
-class Program:
+class Program(object):
def __init__(self):
pass
@@ -224,7 +226,7 @@
return self.assembly
def toXML(self, writer, ttFont):
- if hasattr (ttFont, "disassembleInstructions") and ttFont.disassembleInstructions:
+ if not hasattr (ttFont, "disassembleInstructions") or ttFont.disassembleInstructions:
assembly = self.getAssembly()
writer.begintag("assembly")
writer.newline()
@@ -242,11 +244,11 @@
j = 0
for j in range(nValues):
if j and not (j % 25):
- writer.write(string.join(line, " "))
+ writer.write(' '.join(line))
writer.newline()
line = []
line.append(assembly[i+j])
- writer.write(string.join(line, " "))
+ writer.write(' '.join(line))
writer.newline()
i = i + j + 1
writer.endtag("assembly")
@@ -256,39 +258,41 @@
writer.dumphex(self.getBytecode())
writer.endtag("bytecode")
- def fromXML(self, (name, attrs, content), ttFont):
+ def fromXML(self, name, attrs, content, ttFont):
if name == "assembly":
- self.fromAssembly(string.join(content, ""))
+ self.fromAssembly(strjoin(content))
self._assemble()
del self.assembly
else:
assert name == "bytecode"
self.fromBytecode(readHex(content))
- def _assemble(self,
- skipWhite=_skipWhite, mnemonicDict=mnemonicDict, strip=string.strip,
- binary2num=binary2num):
+ def _assemble(self):
assembly = self.assembly
- if type(assembly) == type([]):
- assembly = string.join(assembly, " ")
+ if isinstance(assembly, type([])):
+ assembly = ' '.join(assembly)
bytecode = []
push = bytecode.append
lenAssembly = len(assembly)
- pos = skipWhite(assembly, 0)
+ pos = _skipWhite(assembly, 0)
while pos < lenAssembly:
m = _tokenRE.match(assembly, pos)
if m is None:
- raise tt_instructions_error, "Syntax error in TT program (%s)" % assembly[pos-5:pos+15]
+ raise tt_instructions_error("Syntax error in TT program (%s)" % assembly[pos-5:pos+15])
dummy, mnemonic, arg, number, comment = m.groups()
pos = m.regs[0][1]
if comment:
continue
- arg = strip(arg)
- if mnemonic not in ("NPUSHB", "NPUSHW", "PUSHB", "PUSHW"):
- op, argBits, name = mnemonicDict[mnemonic]
- if len(arg) <> argBits:
- raise tt_instructions_error, "Incorrect number of argument bits (%s[%s])" % (mnemonic, arg)
+ arg = arg.strip()
+ if mnemonic.startswith("INSTR"):
+ # Unknown instruction
+ op = int(mnemonic[5:])
+ push(op)
+ elif mnemonic not in ("PUSH", "NPUSHB", "NPUSHW", "PUSHB", "PUSHW"):
+ op, argBits = mnemonicDict[mnemonic]
+ if len(arg) != argBits:
+ raise tt_instructions_error("Incorrect number of argument bits (%s[%s])" % (mnemonic, arg))
if arg:
arg = binary2num(arg)
push(op + arg)
@@ -296,44 +300,90 @@
push(op)
else:
args = []
+ pos = _skipWhite(assembly, pos)
while pos < lenAssembly:
- pos = skipWhite(assembly, pos)
m = _tokenRE.match(assembly, pos)
if m is None:
- raise tt_instructions_error, "Syntax error in TT program (%s)" % assembly[pos:pos+15]
- dummy, mnemonic, arg, number, comment = m.groups()
+ raise tt_instructions_error("Syntax error in TT program (%s)" % assembly[pos:pos+15])
+ dummy, _mnemonic, arg, number, comment = m.groups()
if number is None and comment is None:
break
pos = m.regs[0][1]
+ pos = _skipWhite(assembly, pos)
if comment is not None:
continue
args.append(int(number))
- if max(args) > 255 or min(args) < 0:
- words = 1
- mnemonic = "PUSHW"
- else:
- words = 0
- mnemonic = "PUSHB"
nArgs = len(args)
- if nArgs <= 8:
- op, argBits, name = streamMnemonicDict[mnemonic]
- op = op + nArgs - 1
- push(op)
- elif nArgs < 256:
- mnemonic = "N" + mnemonic
- op, argBits, name = streamMnemonicDict[mnemonic]
- push(op)
- push(nArgs)
+ if mnemonic == "PUSH":
+ # Automatically choose the most compact representation
+ nWords = 0
+ while nArgs:
+ while nWords < nArgs and nWords < 255 and not (0 <= args[nWords] <= 255):
+ nWords += 1
+ nBytes = 0
+ while nWords+nBytes < nArgs and nBytes < 255 and 0 <= args[nWords+nBytes] <= 255:
+ nBytes += 1
+ if nBytes < 2 and nWords + nBytes < 255 and nWords + nBytes != nArgs:
+ # Will write bytes as words
+ nWords += nBytes
+ continue
+
+ # Write words
+ if nWords:
+ if nWords <= 8:
+ op, argBits = streamMnemonicDict["PUSHW"]
+ op = op + nWords - 1
+ push(op)
+ else:
+ op, argBits = streamMnemonicDict["NPUSHW"]
+ push(op)
+ push(nWords)
+ for value in args[:nWords]:
+ assert -32768 <= value < 32768, "PUSH value out of range %d" % value
+ push((value >> 8) & 0xff)
+ push(value & 0xff)
+
+ # Write bytes
+ if nBytes:
+ pass
+ if nBytes <= 8:
+ op, argBits = streamMnemonicDict["PUSHB"]
+ op = op + nBytes - 1
+ push(op)
+ else:
+ op, argBits = streamMnemonicDict["NPUSHB"]
+ push(op)
+ push(nBytes)
+ for value in args[nWords:nWords+nBytes]:
+ push(value)
+
+ nTotal = nWords + nBytes
+ args = args[nTotal:]
+ nArgs -= nTotal
+ nWords = 0
else:
- raise tt_instructions_error, "More than 255 push arguments (%s)" % nArgs
- if words:
- for value in args:
- push((value >> 8) & 0xff)
- push(value & 0xff)
- else:
- for value in args:
- push(value)
- pos = skipWhite(assembly, pos)
+ # Write exactly what we've been asked to
+ words = mnemonic[-1] == "W"
+ op, argBits = streamMnemonicDict[mnemonic]
+ if mnemonic[0] != "N":
+ assert nArgs <= 8, nArgs
+ op = op + nArgs - 1
+ push(op)
+ else:
+ assert nArgs < 256
+ push(op)
+ push(nArgs)
+ if words:
+ for value in args:
+ assert -32768 <= value < 32768, "PUSHW value out of range %d" % value
+ push((value >> 8) & 0xff)
+ push(value & 0xff)
+ else:
+ for value in args:
+ assert 0 <= value < 256, "PUSHB value out of range %d" % value
+ push(value)
+
+ pos = _skipWhite(assembly, pos)
if bytecode:
assert max(bytecode) < 256 and min(bytecode) >= 0
@@ -346,44 +396,47 @@
numBytecode = len(bytecode)
while i < numBytecode:
op = bytecode[i]
- arg = 0
try:
- mnemonic, argBits, argoffset, name = opcodeDict[op]
+ mnemonic, argBits, argoffset = 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
+ if op in streamOpcodeDict:
+ values = []
+
+ # Merge consecutive PUSH operations
+ while bytecode[i] in streamOpcodeDict:
+ op = bytecode[i]
+ mnemonic, argBits, argoffset = streamOpcodeDict[op]
+ words = mnemonic[-1] == "W"
+ if argBits:
+ nValues = op - argoffset + 1
+ else:
+ i = i + 1
+ nValues = bytecode[i]
+ i = i + 1
+ assert nValues > 0
+ if not words:
+ for j in range(nValues):
+ value = bytecode[i]
+ values.append(repr(value))
+ i = i + 1
+ else:
+ for j in range(nValues):
+ # cast to signed int16
+ value = (bytecode[i] << 8) | bytecode[i+1]
+ if value >= 0x8000:
+ value = value - 0x10000
+ values.append(repr(value))
+ i = i + 2
+
+ nValues = len(values)
+ if nValues == 1:
+ assembly.append("PUSH[ ]")
else:
- pushWords = op - argoffset + 1
+ assembly.append("PUSH[ ] /* %s values pushed */" % nValues)
+ assembly.extend(values)
else:
+ assembly.append("INSTR%d[ ]" % op)
i = i + 1
- if mnemonic == "NPUSHB":
- pushBytes = bytecode[i]
- else:
- pushWords = bytecode[i]
- i = i + 1
- nValues = pushBytes or pushWords
- assert nValues > 0
- if nValues == 1:
- assembly.append("%s[ ] /* %s value pushed */" % (mnemonic, nValues))
- else:
- assembly.append("%s[ ] /* %s values pushed */" % (mnemonic, nValues))
- for j in range(pushBytes):
- value = bytecode[i]
- assembly.append(`value`)
- i = i + 1
- for j in range(pushWords):
- # cast to signed int16
- value = (bytecode[i] << 8) | bytecode[i+1]
- if value >= 0x8000:
- value = value - 0x10000
- assembly.append(`value`)
- i = i + 2
else:
if argBits:
assembly.append(mnemonic + "[%s]" % num2binary(op - argoffset, argBits))
@@ -400,5 +453,5 @@
p.fromBytecode(bc)
asm = p.getAssembly()
p.fromAssembly(asm)
- print bc == p.getBytecode()
+ print(bc == p.getBytecode())
diff --git a/Lib/fontTools/ttLib/test/__init__.py b/Lib/fontTools/ttLib/test/__init__.py
deleted file mode 100644
index e001bb2..0000000
--- a/Lib/fontTools/ttLib/test/__init__.py
+++ /dev/null
@@ -1,3 +0,0 @@
-"""Empty __init__.py file to signal Python this directory is a package.
-(It can't be completely empty since WinZip seems to skip empty files.)
-"""
diff --git a/Lib/fontTools/ttLib/test/ttBrowser.py b/Lib/fontTools/ttLib/test/ttBrowser.py
deleted file mode 100644
index 5add6bb..0000000
--- a/Lib/fontTools/ttLib/test/ttBrowser.py
+++ /dev/null
@@ -1,351 +0,0 @@
-"""Mac-OS9-only TrueType browser window, deprecated and no longer maintained."""
-
-from fontTools import ttLib
-from fontTools.ttLib import macUtils
-import macfs
-import PyBrowser
-import W, Lists
-import os
-import ATM
-import numpy
-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, ".ttx")
- 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(numpy.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] * numpy.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", ".ttx", ".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
-
-
-def browseTTFont():
- fss, ok = macfs.StandardGetFile()
- if not ok:
- return
- path = fss.as_pathname()
- indices = macUtils.getSFNTResIndices(path)
- if indices:
- for i in indices:
- TableBrowser(path, res_index=i)
- else:
- TableBrowser(path)
-
-
-if __name__ == "__main__":
- browseTTFont()
-
diff --git a/Lib/fontTools/ttx.py b/Lib/fontTools/ttx.py
index 0ed719d..5690e7f 100644
--- a/Lib/fontTools/ttx.py
+++ b/Lib/fontTools/ttx.py
@@ -18,6 +18,8 @@
-o <outputfile> Specify a file to write the output to.
-v Verbose: more messages will be written to stdout about what
is being done.
+ -q Quiet: No messages will be written to stdout about what
+ is being done.
-a allow virtual glyphs ID's on compile or decompile.
Dump options:
@@ -64,51 +66,52 @@
"""
-import sys
-import os
-import getopt
-import re
-from fontTools.ttLib import TTFont
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
+from fontTools.ttLib import TTFont, TTLibError
from fontTools.ttLib.tables.otBase import OTLOffsetOverflowError
from fontTools.ttLib.tables.otTables import fixLookupOverFlows, fixSubTableOverFlows
from fontTools.misc.macCreatorType import getMacCreatorAndType
-from fontTools import version
+import os
+import sys
+import getopt
+import re
def usage():
- print __doc__ % version
+ from fontTools import version
+ print(__doc__ % version)
sys.exit(2)
-numberAddedRE = re.compile("(.*)#\d+$")
+numberAddedRE = re.compile("#\d+$")
opentypeheaderRE = re.compile('''sfntVersion=['"]OTTO["']''')
def makeOutputFileName(input, outputDir, extension):
- dir, file = os.path.split(input)
- file, ext = os.path.splitext(file)
+ dirName, fileName = os.path.split(input)
+ fileName, ext = os.path.splitext(fileName)
if outputDir:
- dir = outputDir
- output = os.path.join(dir, file + extension)
- m = numberAddedRE.match(file)
- if m:
- file = m.group(1)
+ dirName = outputDir
+ fileName = numberAddedRE.split(fileName)[0]
+ output = os.path.join(dirName, fileName + extension)
n = 1
while os.path.exists(output):
- output = os.path.join(dir, file + "#" + repr(n) + extension)
+ output = os.path.join(dirName, fileName + "#" + repr(n) + extension)
n = n + 1
return output
-class Options:
+class Options(object):
- listTables = 0
+ listTables = False
outputDir = None
outputFile = None
- verbose = 0
- splitTables = 0
- disassembleInstructions = 1
+ verbose = False
+ quiet = False
+ splitTables = False
+ disassembleInstructions = True
mergeFile = None
- recalcBBoxes = 1
- allowVID = 0
+ recalcBBoxes = True
+ allowVID = False
ignoreDecompileErrors = True
bitmapGlyphDataFormat = 'raw'
@@ -119,32 +122,35 @@
for option, value in rawOptions:
# general options
if option == "-h":
- print __doc__ % version
+ from fontTools import version
+ print(__doc__ % version)
sys.exit(0)
elif option == "-d":
if not os.path.isdir(value):
- print "The -d option value must be an existing directory"
+ print("The -d option value must be an existing directory")
sys.exit(2)
self.outputDir = value
elif option == "-o":
self.outputFile = value
elif option == "-v":
- self.verbose = 1
+ self.verbose = True
+ elif option == "-q":
+ self.quiet = True
# dump options
elif option == "-l":
- self.listTables = 1
+ self.listTables = True
elif option == "-t":
self.onlyTables.append(value)
elif option == "-x":
self.skipTables.append(value)
elif option == "-s":
- self.splitTables = 1
+ self.splitTables = True
elif option == "-i":
- self.disassembleInstructions = 0
+ self.disassembleInstructions = False
elif option == "-z":
validOptions = ('raw', 'row', 'bitwise', 'extfile')
if value not in validOptions:
- print "-z does not allow %s as a format. Use %s" % (option, validOptions)
+ print("-z does not allow %s as a format. Use %s" % (option, validOptions))
sys.exit(2)
self.bitmapGlyphDataFormat = value
elif option == "-y":
@@ -153,48 +159,49 @@
elif option == "-m":
self.mergeFile = value
elif option == "-b":
- self.recalcBBoxes = 0
+ self.recalcBBoxes = False
elif option == "-a":
- self.allowVID = 1
+ self.allowVID = True
elif option == "-e":
self.ignoreDecompileErrors = False
if self.onlyTables and self.skipTables:
- print "-t and -x options are mutually exclusive"
+ print("-t and -x options are mutually exclusive")
sys.exit(2)
if self.mergeFile and numFiles > 1:
- print "Must specify exactly one TTX source file when using -m"
+ print("Must specify exactly one TTX source file when using -m")
sys.exit(2)
def ttList(input, output, options):
- import string
- ttf = TTFont(input, fontNumber=options.fontNumber)
+ ttf = TTFont(input, fontNumber=options.fontNumber, lazy=True)
reader = ttf.reader
- tags = reader.keys()
- tags.sort()
- print 'Listing table info for "%s":' % input
+ tags = sorted(reader.keys())
+ print('Listing table info for "%s":' % input)
format = " %4s %10s %7s %7s"
- print format % ("tag ", " checksum", " length", " offset")
- print format % ("----", "----------", "-------", "-------")
+ print(format % ("tag ", " checksum", " length", " offset"))
+ print(format % ("----", "----------", "-------", "-------"))
for tag in tags:
entry = reader.tables[tag]
- checkSum = long(entry.checkSum)
+ checkSum = int(entry.checkSum)
if checkSum < 0:
- checkSum = checkSum + 0x100000000L
- checksum = "0x" + string.zfill(hex(checkSum)[2:-1], 8)
- print format % (tag, checksum, entry.length, entry.offset)
- print
+ checkSum = checkSum + 0x100000000
+ checksum = "0x%08X" % checkSum
+ print(format % (tag, checksum, entry.length, entry.offset))
+ print()
ttf.close()
def ttDump(input, output, options):
- print 'Dumping "%s" to "%s"...' % (input, output)
+ if not options.quiet:
+ print('Dumping "%s" to "%s"...' % (input, output))
ttf = TTFont(input, 0, verbose=options.verbose, allowVID=options.allowVID,
+ quiet=options.quiet,
ignoreDecompileErrors=options.ignoreDecompileErrors,
fontNumber=options.fontNumber)
ttf.saveXML(output,
+ quiet=options.quiet,
tables=options.onlyTables,
- skipTables=options.skipTables,
+ skipTables=options.skipTables,
splitTables=options.splitTables,
disassembleInstructions=options.disassembleInstructions,
bitmapGlyphDataFormat=options.bitmapGlyphDataFormat)
@@ -202,22 +209,23 @@
def ttCompile(input, output, options):
- print 'Compiling "%s" to "%s"...' % (input, output)
+ if not options.quiet:
+ print('Compiling "%s" to "%s"...' % (input, output))
ttf = TTFont(options.mergeFile,
recalcBBoxes=options.recalcBBoxes,
verbose=options.verbose, allowVID=options.allowVID)
- ttf.importXML(input)
+ ttf.importXML(input, quiet=options.quiet)
try:
ttf.save(output)
- except OTLOffsetOverflowError, e:
+ except OTLOffsetOverflowError as e:
# XXX This shouldn't be here at all, it should be as close to the
# OTL code as possible.
overflowRecord = e.value
- print "Attempting to fix OTLOffsetOverflowError", e
+ print("Attempting to fix OTLOffsetOverflowError", e)
lastItem = overflowRecord
- while 1:
+ while True:
ok = 0
- if overflowRecord.itemName == None:
+ if overflowRecord.itemName is None:
ok = fixLookupOverFlows(ttf, overflowRecord)
else:
ok = fixSubTableOverFlows(ttf, overflowRecord)
@@ -227,15 +235,15 @@
try:
ttf.save(output)
break
- except OTLOffsetOverflowError, e:
- print "Attempting to fix OTLOffsetOverflowError", e
+ except OTLOffsetOverflowError as e:
+ print("Attempting to fix OTLOffsetOverflowError", e)
overflowRecord = e.value
if overflowRecord == lastItem:
raise
if options.verbose:
import time
- print "finished at", time.strftime("%H:%M:%S", time.localtime(time.time()))
+ print("finished at", time.strftime("%H:%M:%S", time.localtime(time.time())))
def guessFileType(fileName):
@@ -250,15 +258,19 @@
if ext == ".dfont":
return "TTF"
header = f.read(256)
- head = header[:4]
+ head = Tag(header[:4])
if head == "OTTO":
return "OTF"
elif head == "ttcf":
return "TTC"
elif head in ("\0\1\0\0", "true"):
return "TTF"
+ elif head == "wOFF":
+ return "WOFF"
elif head.lower() == "<?xm":
- if opentypeheaderRE.match(header):
+ # Use 'latin1' because that can't fail.
+ header = tostr(header, 'latin1')
+ if opentypeheaderRE.search(header):
return "OTX"
else:
return "TTX"
@@ -267,7 +279,7 @@
def parseOptions(args):
try:
- rawOptions, files = getopt.getopt(args, "ld:o:vht:x:sim:z:baey:")
+ rawOptions, files = getopt.getopt(args, "ld:o:vqht:x:sim:z:baey:")
except getopt.GetoptError:
usage()
@@ -279,7 +291,7 @@
for input in files:
tp = guessFileType(input)
- if tp in ("OTF", "TTF", "TTC"):
+ if tp in ("OTF", "TTF", "TTC", "WOFF"):
extension = ".ttx"
if options.listTables:
action = ttList
@@ -292,7 +304,7 @@
extension = ".otf"
action = ttCompile
else:
- print 'Unknown file type: "%s"' % input
+ print('Unknown file type: "%s"' % input)
continue
if options.outputFile:
@@ -312,7 +324,7 @@
"""Force the DOS Prompt window to stay open so the user gets
a chance to see what's wrong."""
import msvcrt
- print '(Hit any key to exit)'
+ print('(Hit any key to exit)')
while not msvcrt.kbhit():
pass
@@ -322,12 +334,14 @@
try:
process(jobs, options)
except KeyboardInterrupt:
- print "(Cancelled.)"
+ print("(Cancelled.)")
except SystemExit:
if sys.platform == "win32":
waitForKeyPress()
else:
raise
+ except TTLibError as e:
+ print("Error:",e)
except:
if sys.platform == "win32":
import traceback
diff --git a/Lib/fontTools/unicode.py b/Lib/fontTools/unicode.py
index 669d497..ef61b01 100644
--- a/Lib/fontTools/unicode.py
+++ b/Lib/fontTools/unicode.py
@@ -24471,7 +24471,7 @@
return unicodes
-class _Unicode:
+class _Unicode(object):
def __init__(self):
self.codes = _makeunicodes()
diff --git a/MANIFEST.in b/MANIFEST.in
index e1977d5..db16aa2 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -5,14 +5,9 @@
include Doc/*.txt
include Doc/*.html
include MetaTools/*.py
-include MetaTools/*.diff
-include Mac/TTX.py
-include Mac/README.txt
-include Mac/TTX.rsrc.hqx
include Windows/mcmillan.bat
include Windows/ttx.ico
include Windows/README.TXT
include Windows/fonttools-win-setup.iss
include Windows/fonttools-win-setup.txt
include Lib/fontTools/ttLib/tables/table_API_readme.txt
-include Src/eexecOp/README.txt
diff --git a/Mac/README.txt b/Mac/README.txt
deleted file mode 100644
index 2a15038..0000000
--- a/Mac/README.txt
+++ /dev/null
@@ -1 +0,0 @@
-The stuff in this folder is old and rusty, don't pay too much attention to it...
diff --git a/Mac/TTX.py b/Mac/TTX.py
deleted file mode 100644
index dc3f275..0000000
--- a/Mac/TTX.py
+++ /dev/null
@@ -1,322 +0,0 @@
-"""Main TTX application, Mac-only"""
-
-
-#make sure we don't lose events to SIOUX
-import MacOS
-MacOS.EnableAppswitch(-1)
-
-def SetWatchCursor():
- import Qd, QuickDraw
- Qd.SetCursor(Qd.GetCursor(QuickDraw.watchCursor).data)
-
-def SetArrowCursor():
- import Qd
- Qd.SetCursor(Qd.qd.arrow)
-
-SetWatchCursor()
-
-# a few constants
-LOGFILENAME = "TTX errors"
-PREFSFILENAME = "TTX preferences"
-DEFAULTXMLOUTPUT = ":XML output"
-DEFAULTTTOUTPUT = ":TrueType output"
-
-
-import FrameWork
-import MiniAEFrame, AppleEvents
-import EasyDialogs
-import Res
-import macfs
-import os
-import sys, time
-import re, string
-import traceback
-from fontTools import ttLib, version
-from fontTools.ttLib import xmlImport
-from fontTools.ttLib.macUtils import ProgressBar
-
-abouttext = """\
-TTX - The free TrueType to XML to TrueType converter
-(version %s)
-Copyright 1999-2001, Just van Rossum (Letterror)
-just@letterror.com""" % version
-
-
-class TTX(FrameWork.Application, MiniAEFrame.AEServer):
-
- def __init__(self):
- FrameWork.Application.__init__(self)
- MiniAEFrame.AEServer.__init__(self)
- self.installaehandler(
- AppleEvents.kCoreEventClass, AppleEvents.kAEOpenApplication, self.do_nothing)
- self.installaehandler(
- AppleEvents.kCoreEventClass, AppleEvents.kAEPrintDocuments, self.do_nothing)
- self.installaehandler(
- AppleEvents.kCoreEventClass, AppleEvents.kAEOpenDocuments, self.handle_opendocumentsevent)
- self.installaehandler(
- AppleEvents.kCoreEventClass, AppleEvents.kAEQuitApplication, self.handle_quitevent)
-
- def idle(self, event):
- SetArrowCursor()
-
- def makeusermenus(self):
- m = FrameWork.Menu(self.menubar, "File")
- FrameWork.MenuItem(m, "Open...", "O", self.domenu_open)
- FrameWork.Separator(m)
- FrameWork.MenuItem(m, "Quit", "Q", self._quit)
-
- def do_about(self, *args):
- EasyDialogs.Message(abouttext)
-
- def handle_quitevent(self, *args, **kwargs):
- self._quit()
-
- def domenu_open(self, *args):
- fss, ok = macfs.StandardGetFile()
- if ok:
- self.opendocument(fss.as_pathname())
-
- def handle_opendocumentsevent(self, docs, **kwargs):
- if type(docs) <> type([]):
- docs = [docs]
- for doc in docs:
- fss, a = doc.Resolve()
- path = fss.as_pathname()
- self.opendocument(path)
-
- def opendocument(self, path):
- filename = os.path.basename(path)
- filetype = guessfiletype(path)
- handler = getattr(self, "handle_%s_file" % filetype)
- handler(path)
-
- def handle_xml_file(self, path):
- prefs = getprefs()
- makesuitcase = int(prefs.get("makesuitcases", 0))
- dstfolder = prefs.get("ttoutput", DEFAULTTTOUTPUT)
- if not os.path.exists(dstfolder):
- os.mkdir(dstfolder)
- srcfilename = dstfilename = os.path.basename(path)
- if dstfilename[-4:] in (".ttx", ".xml"):
- dstfilename = dstfilename[:-4]
- if dstfilename[-4:] not in (".TTF", ".ttf"):
- dstfilename = dstfilename + ".TTF"
- dst = os.path.join(dstfolder, dstfilename)
-
- if makesuitcase:
- try:
- # see if the destination file is writable,
- # otherwise we'll get an error waaay at the end of
- # the parse procedure
- testref = Res.FSpOpenResFile(macfs.FSSpec(dst), 3) # read-write
- except Res.Error, why:
- if why[0] <> -43: # file not found
- EasyDialogs.Message("Can't create '%s'; file already open" % dst)
- return
- else:
- Res.CloseResFile(testref)
- else:
- try:
- f = open(dst, "wb")
- except IOError, why:
- EasyDialogs.Message("Can't create '%s'; file already open" % dst)
- return
- else:
- f.close()
- pb = ProgressBar("Reading TTX file '%s'..." % srcfilename)
- try:
- tt = ttLib.TTFont()
- tt.importXML(path, pb)
- pb.setlabel("Compiling and saving...")
- tt.save(dst, makesuitcase)
- finally:
- pb.close()
-
- def handle_datafork_file(self, path):
- prefs = getprefs()
- dstfolder = prefs.get("xmloutput", DEFAULTXMLOUTPUT)
- if not os.path.exists(dstfolder):
- os.mkdir(dstfolder)
- filename = os.path.basename(path)
- pb = ProgressBar("Dumping '%s' to XML..." % filename)
- if filename[-4:] in (".TTF", ".ttf"):
- filename = filename[:-4]
- filename = filename + ".ttx"
- dst = os.path.join(dstfolder, filename)
- try:
- tt = ttLib.TTFont(path)
- tt.saveXML(dst, pb)
- finally:
- pb.close()
-
- def handle_resource_file(self, path):
- prefs = getprefs()
- dstfolder = prefs.get("xmloutput", DEFAULTXMLOUTPUT)
- if not os.path.exists(dstfolder):
- os.mkdir(dstfolder)
- filename = os.path.basename(path)
- fss = macfs.FSSpec(path)
- try:
- resref = Res.FSpOpenResFile(fss, 1) # read-only
- except:
- return "unknown"
- Res.UseResFile(resref)
- pb = None
- try:
- n = Res.Count1Resources("sfnt")
- for i in range(1, n+1):
- res = Res.Get1IndResource('sfnt', i)
- resid, restype, resname = res.GetResInfo()
- if not resname:
- resname = filename + `i`
- pb = ProgressBar("Dumping '%s' to XML..." % resname)
- dst = os.path.join(dstfolder, resname + ".ttx")
- try:
- tt = ttLib.TTFont(path, i)
- tt.saveXML(dst, pb)
- finally:
- pb.close()
- finally:
- Res.CloseResFile(resref)
-
- def handle_python_file(self, path):
- pass
- #print "python", path
-
- def handle_unknown_file(self, path):
- EasyDialogs.Message("Cannot open '%s': unknown file kind" % os.path.basename(path))
-
- def do_nothing(self, *args, **kwargs):
- pass
-
- def mainloop(self, mask=FrameWork.everyEvent, wait=0):
- self.quitting = 0
- while not self.quitting:
- try:
- self.do1event(mask, wait)
- except self.__class__:
- # D'OH! FrameWork tries to quit us on cmd-.!
- pass
- except KeyboardInterrupt:
- pass
- except ttLib.xmlImport.xml_parse_error, why:
- EasyDialogs.Message(
- "An error occurred while parsing the XML file:\n" + why)
- except:
- exc = traceback.format_exception(sys.exc_type, sys.exc_value, None)[0]
- exc = string.strip(exc)
- EasyDialogs.Message("An error occurred!\n%s\n[see the logfile '%s' for details]" %
- (exc, LOGFILENAME))
- traceback.print_exc()
-
- def do_kHighLevelEvent(self, event):
- import AE
- AE.AEProcessAppleEvent(event)
-
-
-
-def guessfiletype(path):
- #if path[-3:] == ".py":
- # return "python"
- f = open(path, "rb")
- data = f.read(21)
- f.close()
- if data[:5] == "<?xml":
- return "xml"
- elif data[:4] in ("\000\001\000\000", "OTTO", "true"):
- return "datafork"
- else:
- # assume res fork font
- fss = macfs.FSSpec(path)
- try:
- resref = Res.FSpOpenResFile(fss, 1) # read-only
- except:
- return "unknown"
- Res.UseResFile(resref)
- i = Res.Count1Resources("sfnt")
- Res.CloseResFile(resref)
- if i > 0:
- return "resource"
- return "unknown"
-
-
-default_prefs = """\
-xmloutput: ":XML output"
-ttoutput: ":TrueType output"
-makesuitcases: 1
-"""
-
-def getprefs(path=PREFSFILENAME):
- if not os.path.exists(path):
- f = open(path, "w")
- f.write(default_prefs)
- f.close()
- f = open(path)
- lines = f.readlines()
- prefs = {}
- for line in lines:
- if line[-1:] == "\n":
- line = line[:-1]
- try:
- name, value = re.split(":", line, 1)
- prefs[string.strip(name)] = eval(value)
- except:
- pass
- return prefs
-
-
-class dummy_stdin:
- def readline(self):
- return ""
-sys.stdin = dummy_stdin()
-
-# redirect all output to a log file
-sys.stdout = sys.stderr = open(LOGFILENAME, "w", 0) # unbuffered
-print "Starting TTX at " + time.ctime(time.time())
-
-# fire it up!
-ttx = TTX()
-ttx.mainloop()
-
-
-# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-# clues for BuildApplication/MacFreeze.
-#
-# These modules somehow get imported, but we don't want/have them:
-#
-# macfreeze: exclude msvcrt
-# macfreeze: exclude W
-# macfreeze: exclude SOCKS
-# macfreeze: exclude TERMIOS
-# macfreeze: exclude termios
-# macfreeze: exclude icglue
-# macfreeze: exclude ce
-#
-# these modules are imported dynamically, so MacFreeze won't see them:
-#
-# macfreeze: include fontTools.ttLib.tables._c_m_a_p
-# macfreeze: include fontTools.ttLib.tables._c_v_t
-# macfreeze: include fontTools.ttLib.tables._f_p_g_m
-# macfreeze: include fontTools.ttLib.tables._g_a_s_p
-# macfreeze: include fontTools.ttLib.tables._g_l_y_f
-# macfreeze: include fontTools.ttLib.tables._h_d_m_x
-# macfreeze: include fontTools.ttLib.tables._h_e_a_d
-# macfreeze: include fontTools.ttLib.tables._h_h_e_a
-# macfreeze: include fontTools.ttLib.tables._h_m_t_x
-# macfreeze: include fontTools.ttLib.tables._k_e_r_n
-# macfreeze: include fontTools.ttLib.tables._l_o_c_a
-# macfreeze: include fontTools.ttLib.tables._m_a_x_p
-# macfreeze: include fontTools.ttLib.tables._n_a_m_e
-# macfreeze: include fontTools.ttLib.tables._p_o_s_t
-# macfreeze: include fontTools.ttLib.tables._p_r_e_p
-# macfreeze: include fontTools.ttLib.tables._v_h_e_a
-# macfreeze: include fontTools.ttLib.tables._v_m_t_x
-# macfreeze: include fontTools.ttLib.tables.L_T_S_H_
-# macfreeze: include fontTools.ttLib.tables.O_S_2f_2
-# macfreeze: include fontTools.ttLib.tables.T_S_I__0
-# macfreeze: include fontTools.ttLib.tables.T_S_I__1
-# macfreeze: include fontTools.ttLib.tables.T_S_I__2
-# macfreeze: include fontTools.ttLib.tables.T_S_I__3
-# macfreeze: include fontTools.ttLib.tables.T_S_I__5
-# macfreeze: include fontTools.ttLib.tables.C_F_F_
-
diff --git a/Mac/TTX.rsrc.hqx b/Mac/TTX.rsrc.hqx
deleted file mode 100644
index 0d396b5..0000000
--- a/Mac/TTX.rsrc.hqx
+++ /dev/null
@@ -1,68 +0,0 @@
-(This file must be converted with BinHex 4.0)
-:#&48@#jbFh*M!(*cFQ058d9%!3!!!!!!!!!-5eK3!!!!!!%!!!!+kJ!!#HS!!!&
-KJ'-!!%[qIYf!IJ!)5rjqr6KJ!!")!!!8J*m!!$Kr!!!)9&4B,R*cFQ0c,d0[FQp
-eG'PZCA0c8hPcC'9bC@eLC3),FR0bBe*6483"!!!f!-%!!!!!!!!!!!!!!!!!!!!
-!!!#dKq%9!!!!!!!!$%B!!$L&!'*,rr2T,!-!!%##!!`iB!!!5!!!J)!G!!JS!!!
-!3B)!1)!G!"3S!!!!3B)!*)"LLqJi!!!!N!!$!!#!I3!)JCd!&%J*2df!33!81m-
-!!$J!!!#3!"d!##`Hrrp!JJ!8J'+,f)"M!!",rRi*5!!!+#`H!!""JJ!31(i!!%[
-pE68!!!!39%9B9&)UBfJ!U$!a1$%!!!!!!!`%!!!!!!!!!3%"!!!!!!!"!!!!!!G
-"8&"-!!!!!!!#!2!!!!!!!!!!!!!!!!!!!!$r%4%4%4%4%4%4%4%4%4%Jrr)5%K)
-5%K)5%K)5%K)5X2rr)5%K)5%K)5%K)5%K)E$rrr)5%K)5%K)5%K)5%K+`rrrr)5%
-K)5%K)5%K)5%KX2rrrr)5'lX5%K)5%K)5%V$rrrrr)5rrS5%K)5rk)5'`rrrrmK+
-[rr)5%K,rra)5X2rrrb%Krrrl)5'rrrmK)E$rrr)5'rrrqK)Errrk%K+`rrmK)5V
-krrmK[rrrqL%KX2rb%K)DSUrrS[qUUU)5%V$r)5%K)5'rrrrk)5%K)5'`m4)5%K)
-5(rrrSK)5%K)5X!%K)5%K)5VrqL%K)5%K)E!"%K)5%K)DrrX5%K)5%K+`!5%K)5%
-K,rrk)5%Km5%KX!%5%K)5%[rrra)5%[m5%V!")5'a)5VkrrqUUb(rm5'`!4)DqK+
-[SUrrrrm5rrm5X!%K,rq[qb'rrrrl)IrrmE!"%Krrrl)5[rrrmK,rrrq`!5%[rrS
-K)5Vrrk%Krrrrm!%5(rqL%K)5rrS5%[rrrrm")5'l)5%K)5Ul)5(rrrr`!4)5%K)
-5%K)5%K)5rrrr!!%K)5%K)5%K)5%K)Irrm!!"%K)5%K)5%K)5%K,rr`!!!5%K)5%
-K)5%K)5%Krr!!!!+lZlZlZlZlZlZlZrm!!!!!!!!!!!!!!!!!!!$`!!!!!!!%!2m
-!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!rrm,#`X,#`X,#`X,#`X,#`X
-,#`X,#`X,#`X,#`X,%3$rrrm4%4%4%4%4%4%4%4%4%4%4%4%4%4%4%4%4%4&I!2r
-rrrm4%4%4%4%4%4%4%4%4%4%4%4%4%4%4%4%4%9m!rrrrrrm4%4%4%4%4%4%4%4%
-4%4%4%4%4%4%4%4%4A`$rrrrrrrm4%4%4%4%4%4%4%4%4%4%4%4%4%4%4%4&I!2r
-rrrrrrrm4%4%4AepI04%4%4%4%4%4068e%4%4%9m!rrrrrrrrrrm4%6Arrrq*%4%
-4%4%4%6AJp)N4%4%4A`$rrrrrrrrr%4%4rIrrrrme%4%4%4%ehrrri"%4%4&I!2r
-rrrrrra%4%6Arrrrrrem4%4%4Arlrrrrr%4%4%9m!rrrrrrm4%4%4ArrrrrrrV4%
-4%9rrrrrrrrd4%4%4A`$rrrrr%4%4%4'*rrhrrrrJ04&Ii2rrrrrrV4%4%4&I!2r
-rra%4%4%4%Df*0Dhrrrq*%IlrVB1*rBNe%4%4%9m!rrm4%4%4%4%41c84Arrrrrr
-qrkde%4%4%4%4%4%4A`$r#a%4%4%4%4%4%4%er[rrrrq*04%4%4%4%4%4%4&I!!!
-,%4%4%4%4%4%4%4'YrrrrV684%4%4%4%4%4%4%9m!!!X4%4%4%4%4%4%4%BRrrrp
-I%4%4%4%4%4%4%4%4A`!!#a%4%4%4%4%4%4%er[rrriN4%4%4%4(r%4%4%4&I!!!
-,%4%4%4%4%4%40IlrrrrrrM84%4%4%Irr%4%4%9m!!!X4%4%eAc84%4'Yrkhqrrr
-rLB1YAa%4rrrr%4%4A`!!#a%4%BRdL4%eLIq$%BRrrrrrrrrq04(rrrrr%4&I!!!
-,%4%4r[rqVIlrAa%4Arrrrrrrrem4%Irrrrrr%9m!!!X4%6Arrrrrrem4%4&Irrr
-rrrrI04%4rrrrrrrrA`!!#a%40Irrrrq*%4%4%6@Yrrrrri-4%4(rrrrrrrrr!!!
-,%4%er[rrL4%4%4%4%6[rrrq*04%4%Irrrrrrrrrr!!X4%4%eAeme%4%4%4%4%6Z
-$Ac34%4%4rrrrrrrrr`!!#a%4%4%4%4%4%4%4%4%4%4%4%4%4%4(rrrrrrrm!!!!
-,%4%4%4%4%4%4%4%4%4%4%4%4%4%4%Irrrrrr!!!!!!X4%4%4%4%4%4%4%4%4%4%
-4%4%4%4%4rrrrr`!!!!!!#a%4%4%4%4%4%4%4%4%4%4%4%4%4%4(rrrm!!!!!!!!
-4AepIAepIAepIAepIAepIAepIAepIArrr!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-!!!!!!!!!r`!!!!!!!!!!!!%!J!!!!2rrrrlJ!!!#m!!!![J!!!,m!!!#rK`!![m
-H!B,q2J2#r$m(`[Kr$m,`Iar#i'qrJX!2m!,!"q!#3!I!!N!(J!*!"i##3!r!`N)
-Ic1*(1rcb4r2mqNIMq2j(`IMq4i(`rd-!i2j!!!$m3!!!q%!!!2"!!!$JIrrr`!!
-!!)#!!!!!rrrrr[rrrrlrrrrqrrrrr[rrrrlrrrrqrrrrr[rrrrlrrrrqrrrrr[r
-rrrlrrrrqrrrrr[rrrrjrrrrqIrrrrRrrrrjrrrrqIrrrrRrrrrjrrrrqIrrrrRr
-rrrjrrrrrIrrrrRrrrrarrrriIrrrm(rrrq"rrrr!!!!!J!!!!%$rrm!"i!(h'IF
-jlhR,HB2"JiQAVEh[[Hqjci!1J!crq2rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr
-rrrlrr2ri!!!!J2%4%4%4%4%5rb%K)5%K)5[rmK)5%K)5'rrr+rSK,r%VrrmIra,
-rmK[rmErr,rra+rm5U[rrUU)Em5%KrrSK)5X5%K,rmK,b'a%V+[rkXImV%[rrVrr
-brrX4rrX[rr(rra,rXK,r%[rr%5%K)5%Krr!5%K)5%K,r!#ZlZlZlZr!!!!!"!2m
-,#`X,#`X,#`X,#`X,#a(rra%4%4%4%4%4%4%4%4&Irrrr%4%4%4%4%4%4%4%4Arr
-rrrm40Iq*%4%4Ar34%9rrrrrr09rrra%4r[rr%4&Irrrr%9rrrrmei2rrra%4Arr
-r%4'YLIrrrrqYrBN4%9rr%4%4%4(qrrq*%4%4%4&I#a%4%4%4r[rr%4%4ra%4A`X
-409m4VIrrriQY%Irr%9m,%Ilrr[pIrrrrrcArrrpI#a(rrrpI%BRrrpm4rrrrr`X
-4r[pI%4%lrrme%Irrrrm,%4%4%4%4%4%4%4(rrrm!#a%4%4%4%4%4%4%4rrm!!"&
-IAepIAepIAepIArm!!!!!!!!(+LSU+J!"!!!!!#48G&KU!!!!!8C548B!!3!!!)!
-!!3#"5801)`!"!!!!J!!"!!!!!!!+81!!HK)!!"EMB!!!!!T3i!#lI!!!I2`!!!!
-!#P$J!,Ym!!!@if!!!!!Z!3G!!!!!"Na88Lp54L"")'CbC@8J6'9dG'9bFQpb,e*
-[BQp'EfFJF(*[C(9MG!!!!%X""d!!!!!&-5i`B6Bq-5i`B6FJ,5"MEh"jFQPRD(3
-J-6Nj15db-$!`)%TeFh3JGQ&Z)&*[Fh0eE5`J6'9dG'9bFQpb,e*[BQp'EfF!!!%
-!!!!+kJ!!#HS!!!&K"Z5f9%Y)!!!!(!&5!!a(9EG*!!!!DP"[F(3!!!"f3Nj%6!!
-!!)*8G&KU!!!!MNC548B!!3#DD@0X0!!!!,**3diM!!!![QPMFb-!!!$+D@0c0!!
-!!0CTBh-i!!!!iRCPFR-!!3$Z8dPD43!#!3CTBf`i!!!"+LJ"rrm!!!!!!!!!!!$
-Prrm!!!!8!!!!!!#!rrm!!!NA!!!!!!!!!!!!!!!N!!!!!!#!rrm!!!!T!!!!!!#
-"rrm!!!N-!!!!!!#!rrm!!!!d!!!!!!#!rrm!!!Bm!!!!!!#!rrm!!!G!!!!!!!#
-!rrm!!!H%!!!!!!#!rrm!!!J)!!!!!!!"rrm!!!QE"Z5e,!!#rrm!!!PT"Z5e-2r
-rrrm!!!Nr"Z5e*!!"rrm!!!P0"Z5e+!!!rrm!!!PE"Z5e)!#!rrm!!!)i!!!!!!j
-2GfjPFL"bCA0[GA*MCHPB:
diff --git a/MetaTools/buildChangeLog.py b/MetaTools/buildChangeLog.py
index 2abdc1f..d29e379 100755
--- a/MetaTools/buildChangeLog.py
+++ b/MetaTools/buildChangeLog.py
@@ -6,5 +6,5 @@
os.path.join(os.getcwd(), sys.argv[0]))))
os.chdir(fontToolsDir)
-os.system("svn2cl -o Doc/ChangeLog https://svn.code.sf.net/p/fonttools/code/trunk")
-print "done."
+os.system("git2cl > Doc/ChangeLog")
+print("done.")
diff --git a/MetaTools/buildTableList.py b/MetaTools/buildTableList.py
index bf2df69..1e77492 100755
--- a/MetaTools/buildTableList.py
+++ b/MetaTools/buildTableList.py
@@ -37,7 +37,7 @@
file.write('\tdynamically imported. Generated by MetaTools/buildTableList.py.\n')
file.write('\t"""\n')
for module in modules:
- file.write("\timport %s\n" % module)
+ file.write("\tfrom . import %s\n" % module)
file.close()
diff --git a/MetaTools/build_otData.py b/MetaTools/build_otData.py
deleted file mode 100755
index a58c677..0000000
--- a/MetaTools/build_otData.py
+++ /dev/null
@@ -1,158 +0,0 @@
-#! /usr/bin/env python
-
-
-"""This script builds the Lib/fontTools/ttLib/tables/otData.py file
-from the OpenType HTML documentation. However, it depends on a slightly
-patched version the the HTML, as there are some inconsistencies in the
-markup and the naming of certain fields. See doco.diff for differences,
-but this is probably against a slightly older version of the documentation
-than what is currently online. The documentation was taken from this URL:
- http://www.microsoft.com/typography/otspec/default.htm
-"""
-
-
-from sgmllib import SGMLParser
-
-
-class HTMLParser(SGMLParser):
-
- def __init__(self):
- SGMLParser.__init__(self)
- self.data = None
- self.currenttable = None
- self.lastcaption = None
-
- def handle_data(self, data):
- if self.data is not None:
- self.data.append(data)
-
- def start_i(self, attrs):
- if self.currenttable is None:
- self.data = []
- def end_i(self):
- if self.currenttable is None:
- self.lastcaption = " ".join(self.data)
- self.data = None
-
- def start_b(self, attrs):
- if self.currenttable is None:
- self.data = []
- def end_b(self):
- if self.currenttable is None:
- self.lastcaption = " ".join(self.data)
- self.data = None
-
- def start_table(self, attrs):
- attrs = dict(attrs)
- if attrs.get('width') in ('455', '460'):
- #print "---", attrs
- self.currenttable = []
- else:
- self.currenttable = None
- def end_table(self):
- if self.currenttable is not None and self.lastcaption is not None:
- if self.currenttable[0] == ['Type', 'Name', 'Description'] or \
- self.currenttable[0] == ['Value', 'Type', 'Description']:
- caption = self.lastcaption.split()
- name = caption[0]
- if name == "LookupType" or name == "LookupFlag":
- self.currenttable = None
- return
- elif name == "Device":
- if "Tables" in caption:
- # XXX skip this one
- self.currenttable = None
- return
- buildTable(name, self.currenttable[1:], self.lastcaption)
- self.currenttable = None
-
- def start_tr(self, attrs):
- if self.currenttable is not None:
- self.currenttable.append([])
- def end_tr(self):
- pass
-
- def start_td(self, attrs):
- self.data = []
- def end_td(self):
- if self.currenttable is not None and self.data is not None:
- self.currenttable[-1].append(" ".join(self.data))
- self.data = None
-
-
-globalDups = {}
-localDups = {}
-not3 = []
-
-def buildTable(name, table, caption):
- if globalDups.has_key(name):
- globalDups[name].append(caption)
- else:
- globalDups[name] = [caption]
- print "\t(%s, [" % repr(name)
- allFields = {}
- for row in table:
- row = [" ".join(x.split()) for x in row]
- if len(row) <> 3:
- not3.append(row)
- row = makeRow(row)
- fieldName = row[1]
- if allFields.has_key(fieldName):
- key = (name, fieldName)
- localDups[key] = 1
- allFields[fieldName] = 1
- print "\t\t%s," % (tuple(row),)
- print "\t]),"
- print
-
-
-def makeRow(rawRow):
- tp, name = rawRow[:2]
- name = name.strip()
- rest = tuple(rawRow[2:])
- if '[' in name:
- name, repeat = name.split("[")
- name = name.strip()
- assert repeat[-1] == "]"
- repeat = repeat[:-1].split()
- if repeat[1:]:
- repeatOffset = int("".join(repeat[1:]))
- else:
- repeatOffset = 0
- if not repeat:
- repeat = ""
- else:
- repeat = repeat[0]
- else:
- repeat = None
- repeatOffset = None
- row = (tp, name, repeat, repeatOffset) + rest
- return row
-
-
-if __name__ == "__main__":
- import sys, os
- if "-" not in sys.argv:
- sys.stdout = open("otData.py", "w")
- print "otData = ["
- for file in ["chapter2.htm", "gpos.htm", "gsub.htm", "gdef.htm", "base.htm", "jstf.htm"]:
- name = os.path.splitext(file)[0]
- if name == "chapter2":
- name = "common"
- print
- print "\t#"
- print "\t# %s (generated from %s)" % (name, file)
- print "\t#"
- print
- p = HTMLParser()
- p.feed(open(file).read())
- p.close()
- print "]"
- print
- for k, v in globalDups.items():
- if len(v) > 1:
- print "# XXX duplicate table name:", k, v
- for (name, fieldName), v in localDups.items():
- print "# XXX duplicate field name '%s' in table '%s'" % (fieldName, name)
- for n in not3:
- print "#XXX", not3
diff --git a/MetaTools/doco.diff b/MetaTools/doco.diff
deleted file mode 100644
index 007ec28..0000000
--- a/MetaTools/doco.diff
+++ /dev/null
@@ -1,108 +0,0 @@
---- htmlorig/gpos.htm Fri Apr 5 23:55:58 2002
-+++ htmlbak/gpos.htm Tue May 7 09:53:30 2002
-@@ -270,7 +270,7 @@
-
- <P>Example 2 at the end of this chapter shows a SinglePosFormat1 subtable used to adjust the placement of subscript glyphs.
-
--<P>SinglePosFormat1 subtable: Single positioning value
-+<P><I>SinglePosFormat1 subtable: Single positioning value</I>
- <P>
-
- <TABLE BGCOLOR="#F0F0F0" WIDTH=460 BORDER=0 CELLPADDING=3>
-@@ -312,7 +312,7 @@
-
- <P>Example 3 at the end of this chapter shows how to adjust the spacing of three dash glyphs with a SinglePosFormat2 subtable.
-
--<P>SinglePosFormat2 subtable: Array of positioning values
-+<P><I>SinglePosFormat2 subtable: Array of positioning values</I>
- <P>
-
- <TABLE BGCOLOR="#F0F0F0" WIDTH=460 BORDER=0 CELLPADDING=3>
-@@ -392,8 +392,8 @@
- <TR><TD CLASS=tab VALIGN=TOP>uint16</TD>
- <TD CLASS=tab VALIGN=TOP>PairSetCount</TD>
- <TD CLASS=tab>Number of PairSet tables</TD></TR>
--<TR><TD CLASS=tab VALIGN=TOP>ValueRecord</TD>
--<TD CLASS=tab VALIGN=TOP>PairSet<BR>[Offset]</TD>
-+<TR><TD CLASS=tab VALIGN=TOP>Offset</TD>
-+<TD CLASS=tab VALIGN=TOP>PairSet<BR>[PairSetCount]</TD>
- <TD CLASS=tab>Array of offsets to PairSet tables-from beginning of PairPos subtable-ordered by Coverage Index</TD></TR>
- </TABLE>
-
-@@ -855,7 +855,8 @@
- <TD CLASS=tab>Offset to Base Mark Coverage table-from beginning of MarkMarkPos subtable</TD></TR>
- <TR><TD CLASS=tab VALIGN=TOP>uint16</TD>
- <TD CLASS=tab VALIGN=TOP>ClassCount</TD>
--<TD CLASS=tab>Number of Combining Mark classes defined<TR>
-+<TD CLASS=tab>Number of Combining Mark classes defined</TD>
-+<TR>
- <TD CLASS=tab VALIGN=TOP>Offset</TD>
- <TD CLASS=tab VALIGN=TOP>Mark1Array</TD>
- <TD CLASS=tab VALIGN=TOP>Offset to MarkArray table for Mark1-from beginning of MarkMarkPos subtable</TD></TR>
-@@ -1386,19 +1387,19 @@
- <TD CLASS=tab VALIGN=TOP>BacktrackGlyphCount</TD>
- <TD CLASS=tab>Number of glyphs in the backtracking sequence</TD></TR>
- <TR><TD CLASS=tab VALIGN=TOP>Offset</TD>
--<TD CLASS=tab VALIGN=TOP>Coverage[BacktrackGlyphCount]</TD>
-+<TD CLASS=tab VALIGN=TOP>BacktrackCoverage[BacktrackGlyphCount]</TD>
- <TD CLASS=tab>Array of offsets to coverage tables in backtracking sequence, in glyph sequence order</TD></TR>
- <TR><TD CLASS=tab VALIGN=TOP>uint16</TD>
- <TD CLASS=tab VALIGN=TOP>InputGlyphCount</TD>
- <TD CLASS=tab>Number of glyphs in input sequence</TD></TR>
- <TR><TD CLASS=tab VALIGN=TOP>Offset</TD>
--<TD CLASS=tab VALIGN=TOP>Coverage[InputGlyphCount]</TD>
-+<TD CLASS=tab VALIGN=TOP>InputCoverage[InputGlyphCount]</TD>
- <TD CLASS=tab>Array of offsets to coverage tables in input sequence, in glyph sequence order</TD></TR>
- <TR><TD CLASS=tab VALIGN=TOP>uint16</TD>
- <TD CLASS=tab VALIGN=TOP>LookaheadGlyphCount</TD>
- <TD CLASS=tab>Number of glyphs in lookahead sequence</TD></TR>
- <TR><TD CLASS=tab VALIGN=TOP>Offset</TD>
--<TD CLASS=tab VALIGN=TOP>Coverage[LookaheadGlyphCount]</TD>
-+<TD CLASS=tab VALIGN=TOP>LookaheadCoverage[LookaheadGlyphCount]</TD>
- <TD CLASS=tab>Array of offsets to coverage tables in lookahead sequence, in glyph sequence order</TD></TR>
- <TR><TD CLASS=tab VALIGN=TOP>uint16</TD>
- <TD CLASS=tab VALIGN=TOP>PosCount</TD>
-diff -u htmlorig/gsub.htm htmlbak/gsub.htm
---- htmlorig/gsub.htm Fri Apr 5 23:55:58 2002
-+++ htmlbak/gsub.htm Tue May 7 09:53:17 2002
-@@ -758,7 +758,7 @@
-
- <A HREF="#EX9"><P>Example 9</A> at the end of this chapter substitutes swash glyphs for two out of three glyphs in a sequence.
-
--<P><BR><I>ChainContextSubstFormat3 subtable: Coverage-based context glyph substitution</I><P>
-+<P><BR><I>ContextSubstFormat3 subtable: Coverage-based context glyph substitution</I><P>
-
- <TABLE BGCOLOR="#F0F0F0" WIDTH=460 BORDER=0 CELLPADDING=3>
- <TR>
-@@ -880,7 +880,7 @@
- <TD CLASS=tab VALIGN=TOP>LookaheadGlyphCount</TD>
- <TD CLASS=tab>Total number of glyphs in the look ahead sequence (number of glyphs to be matched after the input sequence)</TD></TR>
- <TR><TD CLASS=tab VALIGN=TOP>GlyphID</TD>
--<TD CLASS=tab VALIGN=TOP>LookAhead<BR>[LookAheadGlyphCount]</TD>
-+<TD CLASS=tab VALIGN=TOP>Lookahead<BR>[LookAheadGlyphCount]</TD>
- <TD CLASS=tab VALIGN=TOP>Array of lookahead GlyphID's (to be matched after the input sequence)</TD></TR>
- <TR><TD CLASS=tab VALIGN=TOP>uint16</TD>
- <TD CLASS=tab VALIGN=TOP>SubstCount</TD>
-@@ -1023,19 +1023,19 @@
- <TD CLASS=tab VALIGN=TOP>BacktrackGlyphCount</TD>
- <TD CLASS=tab>Number of glyphs in the backtracking sequence</TD></TR>
- <TR><TD CLASS=tab VALIGN=TOP>Offset</TD>
--<TD CLASS=tab VALIGN=TOP>Coverage[BacktrackGlyphCount]</TD>
-+<TD CLASS=tab VALIGN=TOP>BacktrackCoverage[BacktrackGlyphCount]</TD>
- <TD CLASS=tab>Array of offsets to coverage tables in backtracking sequence, in glyph sequence order</TD></TR>
- <TR><TD CLASS=tab VALIGN=TOP>uint16</TD>
- <TD CLASS=tab VALIGN=TOP>InputGlyphCount</TD>
- <TD CLASS=tab>Number of glyphs in input sequence</TD></TR>
- <TR><TD CLASS=tab VALIGN=TOP>Offset</TD>
--<TD CLASS=tab VALIGN=TOP>Coverage[InputGlyphCount]</TD>
-+<TD CLASS=tab VALIGN=TOP>InputCoverage[InputGlyphCount]</TD>
- <TD CLASS=tab>Array of offsets to coverage tables in input sequence, in glyph sequence order</TD></TR>
- <TR><TD CLASS=tab VALIGN=TOP>uint16</TD>
- <TD CLASS=tab VALIGN=TOP>LookaheadGlyphCount</TD>
- <TD CLASS=tab>Number of glyphs in lookahead sequence</TD></TR>
- <TR><TD CLASS=tab VALIGN=TOP>Offset</TD>
--<TD CLASS=tab VALIGN=TOP>Coverage[LookaheadGlyphCount]</TD>
-+<TD CLASS=tab VALIGN=TOP>LookaheadCoverage[LookaheadGlyphCount]</TD>
- <TD CLASS=tab>Array of offsets to coverage tables in lookahead sequence, in glyph sequence order</TD></TR>
- <TR><TD CLASS=tab VALIGN=TOP>uint16</TD>
- <TD CLASS=tab VALIGN=TOP>SubstCount</TD>
diff --git a/MetaTools/roundTrip.py b/MetaTools/roundTrip.py
index 9438f0b..122b39b 100755
--- a/MetaTools/roundTrip.py
+++ b/MetaTools/roundTrip.py
@@ -25,7 +25,7 @@
def usage():
- print __doc__
+ print(__doc__)
sys.exit(2)
@@ -46,7 +46,7 @@
diffcmd = 'diff -U2 -I ".*modified value\|checkSumAdjustment.*" "%s" "%s"' % (xmlFile1, xmlFile2)
output = os.popen(diffcmd, "r", 1)
lines = []
- while 1:
+ while True:
line = output.readline()
if not line:
break
@@ -58,7 +58,7 @@
report.write("-------------------------------------------------------------\n")
report.writelines(lines)
else:
- print "(TTX files are the same)"
+ print("(TTX files are the same)")
finally:
for tmpFile in (xmlFile1, ttFile2, xmlFile2):
if os.path.exists(tmpFile):
@@ -80,10 +80,10 @@
try:
roundTrip(ttFile, options, report)
except KeyboardInterrupt:
- print "(Cancelled)"
+ print("(Cancelled)")
break
except:
- print "*** round tripping aborted ***"
+ print("*** round tripping aborted ***")
traceback.print_exc()
report.write("=============================================================\n")
report.write(" An exception occurred while round tripping")
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..c2f7d0c
--- /dev/null
+++ b/README.md
@@ -0,0 +1,34 @@
+### What it is ?
+
+Quoting from [TTX/FontTools Sourceforge Project](http://sourceforge.net/projects/fonttools/)
+> a tool to convert OpenType and TrueType fonts to and from XML. FontTools is a library for manipulating fonts, written in Python. It supports TrueType, OpenType, AFM and to an extent Type 1 and some Mac-specific formats.
+
+### Quick start
+
+```python setup.py install```
+
+From your command line type the above command to get fontools installed on your system.
+
+### Installation
+
+See [install.txt](https://github.com/behdad/fonttools/blob/master/Doc/install.txt) in the 'Doc' subdirectory for instructions on how to build and install TTX/FontTools from the sources.
+
+
+### Documentation
+
+#### What is TTX ?
+
+See [documentation.html](https://github.com/behdad/fonttools/blob/master/Doc/documentation.html) in the "Doc" subdirectory for TTX usage instructions and information about the TTX file format.
+
+### Community
+* https://groups.google.com/d/forum/fonttools
+
+### License
+
+See "LICENSE.txt" for licensing information.
+
+
+
+Have fun!
+
+Just van Rossum <just@letterror.com>
diff --git a/README.txt b/README.txt
deleted file mode 100644
index 019a547..0000000
--- a/README.txt
+++ /dev/null
@@ -1,13 +0,0 @@
-See the file "documentation.html" in the "Doc" subdirectory for TTX
-usage instructions and information about the TTX file format.
-
-See the file "install.txt" in the "Doc" subdirectory for instructions
-how to build and install TTX/FontTools from the sources.
-
-Quick start: run python setup.py install from the command line.
-
-See the file "LICENSE.txt" for licensing info.
-
-Have fun!
-
-Just van Rossum <just@letterror.com>
diff --git a/Src/eexecOp/README.txt b/Src/eexecOp/README.txt
deleted file mode 100644
index 76c2506..0000000
--- a/Src/eexecOp/README.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-eexecOp is imported by the fontTools.misc.eexec module, and the latter
-provides a (slow) Python implementation in case eexecOp isn't there.
-It is designed to be a shared library, to be placed in
- FontTools/Lib/fontTools/misc/
-but it should also work as a (possibly statically linked) top level module.
-
-It is built automatically when you run
-
- python setup.py build
-or
- python setup.py install
-
-in the top level FontTools directory.
-
-Just
diff --git a/Src/eexecOp/eexecOpmodule.c b/Src/eexecOp/eexecOpmodule.c
deleted file mode 100644
index 3befda7..0000000
--- a/Src/eexecOp/eexecOpmodule.c
+++ /dev/null
@@ -1,203 +0,0 @@
-/*
-** Copyright 1996-2001 by Letterror: Just van Rossum, The Netherlands.
-**
-** Open source.
-**
-** Module implementing the eexec and charstring encryption algorithm as
-** used by PostScript Type 1 fonts.
-**
-*/
-
-#include "Python.h"
-#include <ctype.h>
-
-static PyObject *ErrorObject;
-
-/* ----------------------------------------------------- */
-
-static char eexec_decrypt__doc__[] =
-""
-;
-
-static PyObject *
-eexec_decrypt(PyObject *self, PyObject *args)
-{
- PyObject *_res = NULL;
- unsigned short R;
- int tempR; /* can't portably use unsigned shorts between Python versions */
- unsigned short c1 = 52845;
- unsigned short c2 = 22719;
- unsigned char * inbuf;
- unsigned char * outbuf;
- unsigned long counter, insize;
-
- if (!PyArg_ParseTuple(args, "s#i", &inbuf, &insize, &tempR))
- return NULL;
-
- R = (unsigned short)tempR;
-
- if ((outbuf = malloc(insize)) == NULL)
- {
- PyErr_NoMemory();
- return NULL;
- }
- for(counter = 0;counter < insize; counter++) {
- outbuf[counter] = (inbuf[counter] ^ (R>>8));
- R = (inbuf[counter] + R) * c1 + c2;
- }
-
- _res = Py_BuildValue("s#l", outbuf, insize, (unsigned long)R);
- free(outbuf);
- return _res;
-}
-
-static char eexec_encrypt__doc__[] =
-""
-;
-
-static PyObject *
-eexec_encrypt(PyObject *self, PyObject *args)
-{
- PyObject *_res = NULL;
- unsigned short R;
- int tempR; /* can't portably use unsigned shorts between Python versions */
- unsigned short c1 = 52845;
- unsigned short c2 = 22719;
- unsigned char * inbuf;
- unsigned char * outbuf;
- unsigned long counter, insize;
-
- if (!PyArg_ParseTuple(args, "s#i", &inbuf, &insize, &tempR))
- return NULL;
-
- R = (unsigned short)tempR;
-
- if ((outbuf = malloc(insize)) == NULL)
- {
- PyErr_NoMemory();
- return NULL;
- }
- for(counter = 0;counter < insize; counter++) {
- outbuf[counter] = (inbuf[counter] ^ (R>>8));
- R = (outbuf[counter] + R) * c1 + c2;
- }
-
- _res = Py_BuildValue("s#l", outbuf, insize, (unsigned long)R);
- free(outbuf);
- return _res;
-}
-
-static char eexec_hexString__doc__[] =
-""
-;
-
-static PyObject *
-eexec_hexString(PyObject *self, PyObject *args)
-{
- PyObject *_res = NULL;
- unsigned char * inbuf;
- unsigned char * outbuf;
- static const unsigned char hexchars[] = "0123456789ABCDEF";
- unsigned long i, insize;
-
- if (!PyArg_ParseTuple(args, "s#", &inbuf, &insize))
- return NULL;
-
- outbuf = malloc(2 * insize);
- if (outbuf == NULL) {
- PyErr_NoMemory();
- return NULL;
- }
-
- for (i = 0; i < insize; i++) {
- outbuf[2 * i] = hexchars[(inbuf[i] >> 4) & 0xF];
- outbuf[2 * i + 1] = hexchars[inbuf[i] & 0xF];
- }
- _res = Py_BuildValue("s#", outbuf, 2 * insize);
- free(outbuf);
- return _res;
-}
-
-
-#define HEX2DEC(c) ((c) >= 'A' ? ((c) - 'A' + 10) : ((c) - '0'))
-
-static char eexec_deHexString__doc__[] =
-""
-;
-
-static PyObject *
-eexec_deHexString(PyObject *self, PyObject *args)
-{
- PyObject *_res = NULL;
- unsigned char * inbuf;
- unsigned char * outbuf;
- unsigned char c1, c2;
- unsigned long insize, i;
-
- if (!PyArg_ParseTuple(args, "s#", &inbuf, &insize))
- return NULL;
-
- if (insize % 2) {
- PyErr_SetString(ErrorObject, "hex string must have even length");
- return NULL;
- }
-
- outbuf = malloc(insize / 2);
- if (outbuf == NULL) {
- PyErr_NoMemory();
- return NULL;
- }
-
- for ( i = 0; i < insize; i += 2) {
- c1 = toupper(inbuf[i]);
- c2 = toupper(inbuf[i+1]);
- if (!isxdigit(c1) || !isxdigit(c1)) {
- PyErr_SetString(ErrorObject, "non-hex character found");
- goto error;
- }
- outbuf[i/2] = (HEX2DEC(c2)) | (HEX2DEC(c1) << 4);
- }
- _res = Py_BuildValue("s#", outbuf, insize / 2);
-error:
- free(outbuf);
- return _res;
-}
-
-/* List of methods defined in the module */
-
-static struct PyMethodDef eexec_methods[] = {
- {"decrypt", (PyCFunction)eexec_decrypt, METH_VARARGS, eexec_decrypt__doc__},
- {"encrypt", (PyCFunction)eexec_encrypt, METH_VARARGS, eexec_encrypt__doc__},
- {"hexString", (PyCFunction)eexec_hexString, METH_VARARGS, eexec_hexString__doc__},
- {"deHexString", (PyCFunction)eexec_deHexString, METH_VARARGS, eexec_deHexString__doc__},
- {NULL, (PyCFunction)NULL, 0, NULL} /* sentinel */
-};
-
-
-/* Initialization function for the module (*must* be called initeexec) */
-
-static char eexec_module_documentation[] =
-""
-;
-
-void initeexecOp(void); /* prototype to shut up the compiler */
-
-void initeexecOp(void)
-{
- PyObject *m, *d;
-
- /* Create the module and add the functions */
- m = Py_InitModule4("eexecOp", eexec_methods,
- eexec_module_documentation,
- (PyObject*)NULL,PYTHON_API_VERSION);
-
- /* Add some symbolic constants to the module */
- d = PyModule_GetDict(m);
- ErrorObject = PyString_FromString("eexec.error");
- PyDict_SetItemString(d, "error", ErrorObject);
-
- /* Check for errors */
- if (PyErr_Occurred())
- Py_FatalError("can't initialize module eexec");
-}
-
diff --git a/Tools/fontTools b/Tools/fontTools
new file mode 120000
index 0000000..9a21c02
--- /dev/null
+++ b/Tools/fontTools
@@ -0,0 +1 @@
+../Lib/fontTools
\ No newline at end of file
diff --git a/Windows/README.TXT b/Windows/README.TXT
index b16b80d..13f1971 100644
--- a/Windows/README.TXT
+++ b/Windows/README.TXT
@@ -15,7 +15,7 @@
3. Install InnoSetup 4: http://www.jrsoftware.org/
4. Download the latest released source code of TTX/FontTools at
http://sourceforge.net/projects/fonttools/
- Or alternatively grab the sources from SVN:
+ Or alternatively grab the sources from the VCS:
http://fonttools.sourceforge.net/
5. Unzip the source code of TTX/FontTools into a folder.
6. In the folder where you unzipped TTX/FontTools, type:
@@ -36,7 +36,7 @@
5. Put UPX somewhere within your PATH: http://upx.sourceforge.net/
6. Download the latest released source code of TTX/FontTools at
http://sourceforge.net/projects/fonttools/
- Or alternatively grab the sources from SVN:
+ Or alternatively grab the sources from the VCS:
http://fonttools.sourceforge.net/
7. Unzip the source code of TTX/FontTools into a folder.
8. In the folder where you unzipped TTX/FontTools, type:
diff --git a/setup.py b/setup.py
index e37bf26..b3ca286 100755
--- a/setup.py
+++ b/setup.py
@@ -1,5 +1,6 @@
#! /usr/bin/env python
+from __future__ import print_function
import os, sys
from distutils.core import setup, Extension
from distutils.command.build_ext import build_ext
@@ -13,8 +14,8 @@
try:
import xml.parsers.expat
except ImportError:
- print "*** Warning: FontTools needs PyXML, see:"
- print " http://sourceforge.net/projects/pyxml/"
+ print("*** Warning: FontTools needs PyXML, see:")
+ print(" http://sourceforge.net/projects/pyxml/")
class build_ext_optional(build_ext):
@@ -70,29 +71,16 @@
long_description = long_description,
packages = [
- "",
"fontTools",
"fontTools.encodings",
"fontTools.misc",
"fontTools.pens",
"fontTools.ttLib",
"fontTools.ttLib.tables",
- "fontTools.ttLib.test",
],
package_dir = {'': 'Lib'},
extra_path = 'FontTools',
- ext_modules = [
- Extension(
- "fontTools.misc.eexecOp",
- ["Src/eexecOp/eexecOpmodule.c"],
- include_dirs=[],
- define_macros=[],
- library_dirs=[],
- libraries=[],
- )
- ],
scripts = ["Tools/ttx", "Tools/pyftsubset", "Tools/pyftinspect"],
- console = ["Tools/ttx", "Tools/pyftsubset"],
cmdclass = {"build_ext": build_ext_optional},
data_files = [('share/man/man1', ["Doc/ttx.1"])],
**classifiers