fontTools.misc.* documentation, part 2 (#1981)
* Document misc.filenames * Document misc.fixedTools * Document misc.intTools * Document misc.loggingTools * Document misc.macCreatorType * Document misc.macRes * Document misc.plistlib
This commit is contained in:
parent
80dac9c0f4
commit
775dc6074e
@ -1,8 +1,7 @@
|
||||
#########
|
||||
filenames
|
||||
#########
|
||||
##########################################################
|
||||
filenames: Implements UFO User Name to File Name Algorithm
|
||||
##########################################################
|
||||
|
||||
.. automodule:: fontTools.misc.filenames
|
||||
:inherited-members:
|
||||
:members:
|
||||
:members: userNameToFileName
|
||||
:undoc-members:
|
||||
|
@ -1,6 +1,6 @@
|
||||
##########
|
||||
fixedTools
|
||||
##########
|
||||
######################################################
|
||||
fixedTools: Tools for working with fixed-point numbers
|
||||
######################################################
|
||||
|
||||
.. automodule:: fontTools.misc.fixedTools
|
||||
:inherited-members:
|
||||
|
@ -21,6 +21,7 @@ utilities by fontTools, but some of which may be more generally useful.
|
||||
loggingTools
|
||||
macCreatorType
|
||||
macRes
|
||||
plistlib
|
||||
psCharStrings
|
||||
psLib
|
||||
psOperators
|
||||
|
@ -1,8 +1,6 @@
|
||||
########
|
||||
intTools
|
||||
########
|
||||
###############################################
|
||||
intTools: Tools for working with integer values
|
||||
###############################################
|
||||
|
||||
.. automodule:: fontTools.misc.intTools
|
||||
:inherited-members:
|
||||
:members:
|
||||
:undoc-members:
|
@ -1,8 +1,7 @@
|
||||
############
|
||||
loggingTools
|
||||
############
|
||||
###################################################################
|
||||
loggingTools: tools for interfacing with the Python logging package
|
||||
###################################################################
|
||||
|
||||
.. automodule:: fontTools.misc.loggingTools
|
||||
:inherited-members:
|
||||
:members:
|
||||
:undoc-members:
|
||||
|
@ -1,8 +1,9 @@
|
||||
##############
|
||||
macCreatorType
|
||||
##############
|
||||
##############################################################
|
||||
macCreatorType: Functions for working with Mac file attributes
|
||||
##############################################################
|
||||
|
||||
This module requires the `xattr <https://pypi.org/project/xattr/>`_ module
|
||||
to be installed in order to function correctly.
|
||||
|
||||
.. automodule:: fontTools.misc.macCreatorType
|
||||
:inherited-members:
|
||||
:members:
|
||||
:undoc-members:
|
||||
|
@ -1,8 +1,11 @@
|
||||
######
|
||||
macRes
|
||||
######
|
||||
############################################
|
||||
macRes: Tools for reading Mac resource forks
|
||||
############################################
|
||||
|
||||
Classic Mac OS files are made up of two parts - the "data fork" which contains the file contents proper, and the "resource fork" which contains a number of structured data items called "resources". Some fonts, such as Mac "font suitcases" and Type 1 LWFN fonts, still use the resource fork for this kind of structured data, and so to read them, fontTools needs to have access to resource forks.
|
||||
|
||||
The Inside Macintosh volume `More Macintosh Toolbox <https://developer.apple.com/library/archive/documentation/mac/pdf/MoreMacintoshToolbox.pdf#page=34>`_ explains the structure of resource and data forks.
|
||||
|
||||
.. automodule:: fontTools.misc.macRes
|
||||
:inherited-members:
|
||||
:members:
|
||||
:undoc-members:
|
||||
:members: ResourceReader, Resource
|
||||
:member-order: bysource
|
9
Doc/source/misc/plistlib.rst
Normal file
9
Doc/source/misc/plistlib.rst
Normal file
@ -0,0 +1,9 @@
|
||||
#########################################
|
||||
plistlib: Tools for handling .plist files
|
||||
#########################################
|
||||
|
||||
.. automodule:: fontTools.misc.plistlib
|
||||
:members: totree, fromtree, load, loads, dump, dumps
|
||||
|
||||
.. autoclass:: fontTools.misc.plistlib.Data
|
||||
:members:
|
@ -1,19 +1,23 @@
|
||||
"""
|
||||
User name to file name conversion based on the UFO 3 spec:
|
||||
http://unifiedfontobject.org/versions/ufo3/conventions/
|
||||
This module implements the algorithm for converting between a "user name" -
|
||||
something that a user can choose arbitrarily inside a font editor - and a file
|
||||
name suitable for use in a wide range of operating systems and filesystems.
|
||||
|
||||
The code was copied from:
|
||||
https://github.com/unified-font-object/ufoLib/blob/8747da7/Lib/ufoLib/filenames.py
|
||||
The `UFO 3 specification <http://unifiedfontobject.org/versions/ufo3/conventions/>`_
|
||||
provides an example of an algorithm for such conversion, which avoids illegal
|
||||
characters, reserved file names, ambiguity between upper- and lower-case
|
||||
characters, and clashes with existing files.
|
||||
|
||||
Author: Tal Leming
|
||||
Copyright (c) 2005-2016, The RoboFab Developers:
|
||||
Erik van Blokland
|
||||
Tal Leming
|
||||
Just van Rossum
|
||||
This code was originally copied from
|
||||
`ufoLib <https://github.com/unified-font-object/ufoLib/blob/8747da7/Lib/ufoLib/filenames.py>`_
|
||||
by Tal Leming and is copyright (c) 2005-2016, The RoboFab Developers:
|
||||
|
||||
- Erik van Blokland
|
||||
- Tal Leming
|
||||
- Just van Rossum
|
||||
"""
|
||||
from fontTools.misc.py23 import basestring, unicode
|
||||
|
||||
|
||||
illegalCharacters = r"\" * + / : < > ? [ \ ] | \0".split(" ")
|
||||
illegalCharacters += [chr(i) for i in range(1, 32)]
|
||||
illegalCharacters += [chr(0x7F)]
|
||||
@ -27,54 +31,69 @@ class NameTranslationError(Exception):
|
||||
|
||||
|
||||
def userNameToFileName(userName, existing=[], prefix="", suffix=""):
|
||||
"""
|
||||
existing should be a case-insensitive list
|
||||
of all existing file names.
|
||||
"""Converts from a user name to a file name.
|
||||
|
||||
>>> userNameToFileName("a") == "a"
|
||||
True
|
||||
>>> userNameToFileName("A") == "A_"
|
||||
True
|
||||
>>> userNameToFileName("AE") == "A_E_"
|
||||
True
|
||||
>>> userNameToFileName("Ae") == "A_e"
|
||||
True
|
||||
>>> userNameToFileName("ae") == "ae"
|
||||
True
|
||||
>>> userNameToFileName("aE") == "aE_"
|
||||
True
|
||||
>>> userNameToFileName("a.alt") == "a.alt"
|
||||
True
|
||||
>>> userNameToFileName("A.alt") == "A_.alt"
|
||||
True
|
||||
>>> userNameToFileName("A.Alt") == "A_.A_lt"
|
||||
True
|
||||
>>> userNameToFileName("A.aLt") == "A_.aL_t"
|
||||
True
|
||||
>>> userNameToFileName(u"A.alT") == "A_.alT_"
|
||||
True
|
||||
>>> userNameToFileName("T_H") == "T__H_"
|
||||
True
|
||||
>>> userNameToFileName("T_h") == "T__h"
|
||||
True
|
||||
>>> userNameToFileName("t_h") == "t_h"
|
||||
True
|
||||
>>> userNameToFileName("F_F_I") == "F__F__I_"
|
||||
True
|
||||
>>> userNameToFileName("f_f_i") == "f_f_i"
|
||||
True
|
||||
>>> userNameToFileName("Aacute_V.swash") == "A_acute_V_.swash"
|
||||
True
|
||||
>>> userNameToFileName(".notdef") == "_notdef"
|
||||
True
|
||||
>>> userNameToFileName("con") == "_con"
|
||||
True
|
||||
>>> userNameToFileName("CON") == "C_O_N_"
|
||||
True
|
||||
>>> userNameToFileName("con.alt") == "_con.alt"
|
||||
True
|
||||
>>> userNameToFileName("alt.con") == "alt._con"
|
||||
True
|
||||
Takes care to avoid illegal characters, reserved file names, ambiguity between
|
||||
upper- and lower-case characters, and clashes with existing files.
|
||||
|
||||
Args:
|
||||
userName (str): The input file name.
|
||||
existing: A case-insensitive list of all existing file names.
|
||||
prefix: Prefix to be prepended to the file name.
|
||||
suffix: Suffix to be appended to the file name.
|
||||
|
||||
Returns:
|
||||
A suitable filename.
|
||||
|
||||
Raises:
|
||||
NameTranslationError: If no suitable name could be generated.
|
||||
|
||||
Examples::
|
||||
|
||||
>>> userNameToFileName("a") == "a"
|
||||
True
|
||||
>>> userNameToFileName("A") == "A_"
|
||||
True
|
||||
>>> userNameToFileName("AE") == "A_E_"
|
||||
True
|
||||
>>> userNameToFileName("Ae") == "A_e"
|
||||
True
|
||||
>>> userNameToFileName("ae") == "ae"
|
||||
True
|
||||
>>> userNameToFileName("aE") == "aE_"
|
||||
True
|
||||
>>> userNameToFileName("a.alt") == "a.alt"
|
||||
True
|
||||
>>> userNameToFileName("A.alt") == "A_.alt"
|
||||
True
|
||||
>>> userNameToFileName("A.Alt") == "A_.A_lt"
|
||||
True
|
||||
>>> userNameToFileName("A.aLt") == "A_.aL_t"
|
||||
True
|
||||
>>> userNameToFileName(u"A.alT") == "A_.alT_"
|
||||
True
|
||||
>>> userNameToFileName("T_H") == "T__H_"
|
||||
True
|
||||
>>> userNameToFileName("T_h") == "T__h"
|
||||
True
|
||||
>>> userNameToFileName("t_h") == "t_h"
|
||||
True
|
||||
>>> userNameToFileName("F_F_I") == "F__F__I_"
|
||||
True
|
||||
>>> userNameToFileName("f_f_i") == "f_f_i"
|
||||
True
|
||||
>>> userNameToFileName("Aacute_V.swash") == "A_acute_V_.swash"
|
||||
True
|
||||
>>> userNameToFileName(".notdef") == "_notdef"
|
||||
True
|
||||
>>> userNameToFileName("con") == "_con"
|
||||
True
|
||||
>>> userNameToFileName("CON") == "C_O_N_"
|
||||
True
|
||||
>>> userNameToFileName("con.alt") == "_con.alt"
|
||||
True
|
||||
>>> userNameToFileName("alt.con") == "alt._con"
|
||||
True
|
||||
"""
|
||||
# the incoming name must be a unicode string
|
||||
if not isinstance(userName, unicode):
|
||||
|
@ -1,4 +1,20 @@
|
||||
"""fontTools.misc.fixedTools.py -- tools for working with fixed numbers.
|
||||
"""
|
||||
The `OpenType specification <https://docs.microsoft.com/en-us/typography/opentype/spec/otff#data-types>`_
|
||||
defines two fixed-point data types:
|
||||
|
||||
``Fixed``
|
||||
A 32-bit signed fixed-point number with a 16 bit twos-complement
|
||||
magnitude component and 16 fractional bits.
|
||||
``F2DOT14``
|
||||
A 16-bit signed fixed-point number with a 2 bit twos-complement
|
||||
magnitude component and 14 fractional bits.
|
||||
|
||||
To support reading and writing data with these data types, this module provides
|
||||
functions for converting between fixed-point, float and string representations.
|
||||
|
||||
.. data:: MAX_F2DOT14
|
||||
|
||||
The maximum value that can still fit in an F2Dot14. (1.99993896484375)
|
||||
"""
|
||||
|
||||
from fontTools.misc.py23 import *
|
||||
@ -8,6 +24,7 @@ import logging
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
__all__ = [
|
||||
"MAX_F2DOT14",
|
||||
"otRound",
|
||||
"fixedToFloat",
|
||||
"floatToFixed",
|
||||
@ -21,78 +38,126 @@ __all__ = [
|
||||
]
|
||||
|
||||
|
||||
# the max value that can still fit in an F2Dot14:
|
||||
# 1.99993896484375
|
||||
MAX_F2DOT14 = 0x7FFF / (1 << 14)
|
||||
|
||||
|
||||
def otRound(value):
|
||||
"""Round float value to nearest integer towards +Infinity.
|
||||
For fractional values of 0.5 and higher, take the next higher integer;
|
||||
for other fractional values, truncate.
|
||||
"""Round float value to nearest integer towards ``+Infinity``.
|
||||
|
||||
https://docs.microsoft.com/en-us/typography/opentype/spec/otvaroverview
|
||||
https://github.com/fonttools/fonttools/issues/1248#issuecomment-383198166
|
||||
The OpenType spec (in the section on `"normalization" of OpenType Font Variations <https://docs.microsoft.com/en-us/typography/opentype/spec/otvaroverview#coordinate-scales-and-normalization>`_)
|
||||
defines the required method for converting floating point values to
|
||||
fixed-point. In particular it specifies the following rounding strategy:
|
||||
|
||||
for fractional values of 0.5 and higher, take the next higher integer;
|
||||
for other fractional values, truncate.
|
||||
|
||||
This function rounds the floating-point value according to this strategy
|
||||
in preparation for conversion to fixed-point.
|
||||
|
||||
Args:
|
||||
value (float): The input floating-point value.
|
||||
|
||||
Returns
|
||||
float: The rounded value.
|
||||
"""
|
||||
# See this thread for how we ended up with this implementation:
|
||||
# https://github.com/fonttools/fonttools/issues/1248#issuecomment-383198166
|
||||
return int(math.floor(value + 0.5))
|
||||
|
||||
|
||||
def fixedToFloat(value, precisionBits):
|
||||
"""Converts a fixed-point number to a float given the number of
|
||||
precisionBits. Ie. value / (1 << precisionBits)
|
||||
precision bits.
|
||||
|
||||
>>> import math
|
||||
>>> f = fixedToFloat(-10139, precisionBits=14)
|
||||
>>> math.isclose(f, -0.61883544921875)
|
||||
True
|
||||
Args:
|
||||
value (int): Number in fixed-point format.
|
||||
precisionBits (int): Number of precision bits.
|
||||
|
||||
Returns:
|
||||
Floating point value.
|
||||
|
||||
Examples::
|
||||
|
||||
>>> import math
|
||||
>>> f = fixedToFloat(-10139, precisionBits=14)
|
||||
>>> math.isclose(f, -0.61883544921875)
|
||||
True
|
||||
"""
|
||||
return value / (1 << precisionBits)
|
||||
|
||||
|
||||
def floatToFixed(value, precisionBits):
|
||||
"""Converts a float to a fixed-point number given the number of
|
||||
precisionBits. Ie. round(value * (1 << precisionBits)).
|
||||
precision bits.
|
||||
|
||||
>>> floatToFixed(-0.61883544921875, precisionBits=14)
|
||||
-10139
|
||||
>>> floatToFixed(-0.61884, precisionBits=14)
|
||||
-10139
|
||||
Args:
|
||||
value (float): Floating point value.
|
||||
precisionBits (int): Number of precision bits.
|
||||
|
||||
Returns:
|
||||
int: Fixed-point representation.
|
||||
|
||||
Examples::
|
||||
|
||||
>>> floatToFixed(-0.61883544921875, precisionBits=14)
|
||||
-10139
|
||||
>>> floatToFixed(-0.61884, precisionBits=14)
|
||||
-10139
|
||||
"""
|
||||
return otRound(value * (1 << precisionBits))
|
||||
|
||||
|
||||
def floatToFixedToFloat(value, precisionBits):
|
||||
"""Converts a float to a fixed-point number given the number of
|
||||
precisionBits, round it, then convert it back to float again.
|
||||
Ie. round(value * (1<<precisionBits)) / (1<<precisionBits)
|
||||
Note: this **is** equivalent to fixedToFloat(floatToFixed(value)).
|
||||
"""Converts a float to a fixed-point number and back again.
|
||||
|
||||
>>> import math
|
||||
>>> f1 = -0.61884
|
||||
>>> f2 = floatToFixedToFloat(-0.61884, precisionBits=14)
|
||||
>>> f1 != f2
|
||||
True
|
||||
>>> math.isclose(f2, -0.61883544921875)
|
||||
True
|
||||
By converting the float to fixed, rounding it, and converting it back
|
||||
to float again, this returns a floating point values which is exactly
|
||||
representable in fixed-point format.
|
||||
|
||||
Note: this **is** equivalent to ``fixedToFloat(floatToFixed(value))``.
|
||||
|
||||
Args:
|
||||
value (float): The input floating point value.
|
||||
precisionBits (int): Number of precision bits.
|
||||
|
||||
Returns:
|
||||
float: The transformed and rounded value.
|
||||
|
||||
Examples::
|
||||
>>> import math
|
||||
>>> f1 = -0.61884
|
||||
>>> f2 = floatToFixedToFloat(-0.61884, precisionBits=14)
|
||||
>>> f1 != f2
|
||||
True
|
||||
>>> math.isclose(f2, -0.61883544921875)
|
||||
True
|
||||
"""
|
||||
scale = 1 << precisionBits
|
||||
return otRound(value * scale) / scale
|
||||
|
||||
|
||||
def fixedToStr(value, precisionBits):
|
||||
"""Converts a fixed-point number with 'precisionBits' number of fractional binary
|
||||
digits to a string representing a decimal float, choosing the float that has the
|
||||
shortest decimal representation (the least number of fractional decimal digits).
|
||||
Eg. to convert a fixed-point number in a 2.14 format, use precisionBits=14:
|
||||
"""Converts a fixed-point number to a string representing a decimal float.
|
||||
|
||||
>>> fixedToStr(-10139, precisionBits=14)
|
||||
'-0.61884'
|
||||
This chooses the float that has the shortest decimal representation (the least
|
||||
number of fractional decimal digits).
|
||||
|
||||
This is pretty slow compared to the simple division used in fixedToFloat.
|
||||
For example, to convert a fixed-point number in a 2.14 format, use
|
||||
``precisionBits=14``::
|
||||
|
||||
>>> fixedToStr(-10139, precisionBits=14)
|
||||
'-0.61884'
|
||||
|
||||
This is pretty slow compared to the simple division used in ``fixedToFloat``.
|
||||
Use sporadically when you need to serialize or print the fixed-point number in
|
||||
a human-readable form.
|
||||
|
||||
NOTE: precisionBits is only supported up to 16.
|
||||
Args:
|
||||
value (int): The fixed-point value to convert.
|
||||
precisionBits (int): Number of precision bits, *up to a maximum of 16*.
|
||||
|
||||
Returns:
|
||||
str: A string representation of the value.
|
||||
"""
|
||||
if not value: return "0.0"
|
||||
|
||||
@ -118,10 +183,18 @@ def fixedToStr(value, precisionBits):
|
||||
|
||||
|
||||
def strToFixed(string, precisionBits):
|
||||
"""Convert the string representation of a decimal float number to a fixed-point
|
||||
number with 'precisionBits' fractional binary digits.
|
||||
E.g. to convert a float string to a 2.14 fixed-point number:
|
||||
"""Converts a string representing a decimal float to a fixed-point number.
|
||||
|
||||
Args:
|
||||
string (str): A string representing a decimal float.
|
||||
precisionBits (int): Number of precision bits, *up to a maximum of 16*.
|
||||
|
||||
Returns:
|
||||
int: Fixed-point representation.
|
||||
|
||||
Examples::
|
||||
|
||||
>>> ## to convert a float string to a 2.14 fixed-point number:
|
||||
>>> strToFixed('-0.61884', precisionBits=14)
|
||||
-10139
|
||||
"""
|
||||
@ -130,18 +203,31 @@ def strToFixed(string, precisionBits):
|
||||
|
||||
|
||||
def strToFixedToFloat(string, precisionBits):
|
||||
"""Convert a string to a decimal float, by first converting the float to a
|
||||
fixed-point number with precisionBits fractional binary digits.
|
||||
"""Convert a string to a decimal float with fixed-point rounding.
|
||||
|
||||
This first converts string to a float, then turns it into a fixed-point
|
||||
number with ``precisionBits`` fractional binary digits, then back to a
|
||||
float again.
|
||||
|
||||
This is simply a shorthand for fixedToFloat(floatToFixed(float(s))).
|
||||
|
||||
>>> import math
|
||||
>>> s = '-0.61884'
|
||||
>>> bits = 14
|
||||
>>> f = strToFixedToFloat(s, precisionBits=bits)
|
||||
>>> math.isclose(f, -0.61883544921875)
|
||||
True
|
||||
>>> f == fixedToFloat(floatToFixed(float(s), precisionBits=bits), precisionBits=bits)
|
||||
True
|
||||
Args:
|
||||
string (str): A string representing a decimal float.
|
||||
precisionBits (int): Number of precision bits.
|
||||
|
||||
Returns:
|
||||
float: The transformed and rounded value.
|
||||
|
||||
Examples::
|
||||
|
||||
>>> import math
|
||||
>>> s = '-0.61884'
|
||||
>>> bits = 14
|
||||
>>> f = strToFixedToFloat(s, precisionBits=bits)
|
||||
>>> math.isclose(f, -0.61883544921875)
|
||||
True
|
||||
>>> f == fixedToFloat(floatToFixed(float(s), precisionBits=bits), precisionBits=bits)
|
||||
True
|
||||
"""
|
||||
value = float(string)
|
||||
scale = 1 << precisionBits
|
||||
@ -149,21 +235,43 @@ def strToFixedToFloat(string, precisionBits):
|
||||
|
||||
|
||||
def floatToFixedToStr(value, precisionBits):
|
||||
"""Convert float to string using the shortest decimal representation (ie. the least
|
||||
number of fractional decimal digits) to represent the equivalent fixed-point number
|
||||
with 'precisionBits' fractional binary digits.
|
||||
"""Convert float to string with fixed-point rounding.
|
||||
|
||||
This uses the shortest decimal representation (ie. the least
|
||||
number of fractional decimal digits) to represent the equivalent
|
||||
fixed-point number with ``precisionBits`` fractional binary digits.
|
||||
It uses fixedToStr under the hood.
|
||||
|
||||
>>> floatToFixedToStr(-0.61883544921875, precisionBits=14)
|
||||
'-0.61884'
|
||||
|
||||
Args:
|
||||
value (float): The float value to convert.
|
||||
precisionBits (int): Number of precision bits, *up to a maximum of 16*.
|
||||
|
||||
Returns:
|
||||
str: A string representation of the value.
|
||||
|
||||
"""
|
||||
fixed = otRound(value * (1 << precisionBits))
|
||||
return fixedToStr(fixed, precisionBits)
|
||||
|
||||
|
||||
def ensureVersionIsLong(value):
|
||||
"""Ensure a table version is an unsigned long (unsigned short major,
|
||||
unsigned short minor) instead of a float."""
|
||||
"""Ensure a table version is an unsigned long.
|
||||
|
||||
OpenType table version numbers are expressed as a single unsigned long
|
||||
comprising of an unsigned short major version and unsigned short minor
|
||||
version. This function detects if the value to be used as a version number
|
||||
looks too small (i.e. is less than ``0x10000``), and converts it to
|
||||
fixed-point using :func:`floatToFixed` if so.
|
||||
|
||||
Args:
|
||||
value (Number): a candidate table version number.
|
||||
|
||||
Returns:
|
||||
int: A table version number, possibly corrected to fixed-point.
|
||||
"""
|
||||
if value < 0x10000:
|
||||
newValue = floatToFixed(value, 16)
|
||||
log.warning(
|
||||
@ -174,7 +282,14 @@ def ensureVersionIsLong(value):
|
||||
|
||||
|
||||
def versionToFixed(value):
|
||||
"""Converts a table version to a fixed"""
|
||||
"""Ensure a table version number is fixed-point.
|
||||
|
||||
Args:
|
||||
value (str): a candidate table version number.
|
||||
|
||||
Returns:
|
||||
int: A table version number, possibly corrected to fixed-point.
|
||||
"""
|
||||
value = int(value, 0) if value.startswith("0") else float(value)
|
||||
value = ensureVersionIsLong(value)
|
||||
return value
|
||||
|
@ -1,12 +1,25 @@
|
||||
"""Misc integer tools."""
|
||||
|
||||
from fontTools.misc.py23 import *
|
||||
|
||||
__all__ = ['popCount']
|
||||
|
||||
|
||||
def popCount(v):
|
||||
"""Return number of 1 bits in an integer."""
|
||||
"""Return number of 1 bits (population count) of an integer.
|
||||
|
||||
If the integer is negative, the number of 1 bits in the
|
||||
twos-complement representation of the integer is returned. i.e.
|
||||
``popCount(-30) == 28`` because -30 is::
|
||||
|
||||
1111 1111 1111 1111 1111 1111 1110 0010
|
||||
|
||||
Uses the algorithm from `HAKMEM item 169 <https://www.inwap.com/pdp10/hbaker/hakmem/hacks.html#item169>`_.
|
||||
|
||||
Args:
|
||||
v (int): Value to count.
|
||||
|
||||
Returns:
|
||||
Number of 1 bits in the binary representation of ``v``.
|
||||
"""
|
||||
|
||||
if v > 0xFFFFFFFF:
|
||||
return popCount(v >> 32) + popCount(v & 0xFFFFFFFF)
|
||||
|
@ -1,7 +1,3 @@
|
||||
""" fontTools.misc.loggingTools.py -- tools for interfacing with the Python
|
||||
logging package.
|
||||
"""
|
||||
|
||||
from fontTools.misc.py23 import *
|
||||
import sys
|
||||
import logging
|
||||
@ -25,10 +21,18 @@ DEFAULT_FORMATS = {
|
||||
|
||||
|
||||
class LevelFormatter(logging.Formatter):
|
||||
""" Formatter class which optionally takes a dict of logging levels to
|
||||
"""Log formatter with level-specific formatting.
|
||||
|
||||
Formatter class which optionally takes a dict of logging levels to
|
||||
format strings, allowing to customise the log records appearance for
|
||||
specific levels.
|
||||
The '*' key identifies the default format string.
|
||||
|
||||
|
||||
Attributes:
|
||||
fmt: A dictionary mapping logging levels to format strings.
|
||||
The ``*`` key identifies the default format string.
|
||||
datefmt: As per py:class:`logging.Formatter`
|
||||
style: As per py:class:`logging.Formatter`
|
||||
|
||||
>>> import sys
|
||||
>>> handler = logging.StreamHandler(sys.stdout)
|
||||
@ -83,46 +87,48 @@ class LevelFormatter(logging.Formatter):
|
||||
|
||||
|
||||
def configLogger(**kwargs):
|
||||
""" Do basic configuration for the logging system. This is more or less
|
||||
the same as logging.basicConfig with some additional options and defaults.
|
||||
"""A more sophisticated logging system configuation manager.
|
||||
|
||||
The default behaviour is to create a StreamHandler which writes to
|
||||
sys.stderr, set a formatter using the DEFAULT_FORMATS strings, and add
|
||||
This is more or less the same as :py:func:`logging.basicConfig`,
|
||||
with some additional options and defaults.
|
||||
|
||||
The default behaviour is to create a ``StreamHandler`` which writes to
|
||||
sys.stderr, set a formatter using the ``DEFAULT_FORMATS`` strings, and add
|
||||
the handler to the top-level library logger ("fontTools").
|
||||
|
||||
A number of optional keyword arguments may be specified, which can alter
|
||||
the default behaviour.
|
||||
|
||||
logger Specifies the logger name or a Logger instance to be configured.
|
||||
(it defaults to "fontTools" logger). Unlike basicConfig, this
|
||||
function can be called multiple times to reconfigure a logger.
|
||||
If the logger or any of its children already exists before the
|
||||
call is made, they will be reset before the new configuration
|
||||
is applied.
|
||||
filename Specifies that a FileHandler be created, using the specified
|
||||
filename, rather than a StreamHandler.
|
||||
filemode Specifies the mode to open the file, if filename is specified
|
||||
(if filemode is unspecified, it defaults to 'a').
|
||||
format Use the specified format string for the handler. This argument
|
||||
also accepts a dictionary of format strings keyed by level name,
|
||||
to allow customising the records appearance for specific levels.
|
||||
The special '*' key is for 'any other' level.
|
||||
datefmt Use the specified date/time format.
|
||||
level Set the logger level to the specified level.
|
||||
stream Use the specified stream to initialize the StreamHandler. Note
|
||||
that this argument is incompatible with 'filename' - if both
|
||||
are present, 'stream' is ignored.
|
||||
handlers If specified, this should be an iterable of already created
|
||||
handlers, which will be added to the logger. Any handler
|
||||
in the list which does not have a formatter assigned will be
|
||||
assigned the formatter created in this function.
|
||||
filters If specified, this should be an iterable of already created
|
||||
filters, which will be added to the handler(s), if the latter
|
||||
do(es) not already have filters assigned.
|
||||
propagate All loggers have a "propagate" attribute initially set to True,
|
||||
which determines whether to continue searching for handlers up
|
||||
the logging hierarchy. By default, this arguments sets the
|
||||
"propagate" attribute to False.
|
||||
Args:
|
||||
|
||||
logger: Specifies the logger name or a Logger instance to be
|
||||
configured. (Defaults to "fontTools" logger). Unlike ``basicConfig``,
|
||||
this function can be called multiple times to reconfigure a logger.
|
||||
If the logger or any of its children already exists before the call is
|
||||
made, they will be reset before the new configuration is applied.
|
||||
filename: Specifies that a ``FileHandler`` be created, using the
|
||||
specified filename, rather than a ``StreamHandler``.
|
||||
filemode: Specifies the mode to open the file, if filename is
|
||||
specified. (If filemode is unspecified, it defaults to ``a``).
|
||||
format: Use the specified format string for the handler. This
|
||||
argument also accepts a dictionary of format strings keyed by
|
||||
level name, to allow customising the records appearance for
|
||||
specific levels. The special ``'*'`` key is for 'any other' level.
|
||||
datefmt: Use the specified date/time format.
|
||||
level: Set the logger level to the specified level.
|
||||
stream: Use the specified stream to initialize the StreamHandler. Note
|
||||
that this argument is incompatible with ``filename`` - if both
|
||||
are present, ``stream`` is ignored.
|
||||
handlers: If specified, this should be an iterable of already created
|
||||
handlers, which will be added to the logger. Any handler in the
|
||||
list which does not have a formatter assigned will be assigned the
|
||||
formatter created in this function.
|
||||
filters: If specified, this should be an iterable of already created
|
||||
filters. If the ``handlers`` do not already have filters assigned,
|
||||
these filters will be added to them.
|
||||
propagate: All loggers have a ``propagate`` attribute which determines
|
||||
whether to continue searching for handlers up the logging hierarchy.
|
||||
If not provided, the "propagate" attribute will be set to ``False``.
|
||||
"""
|
||||
# using kwargs to enforce keyword-only arguments in py2.
|
||||
handlers = kwargs.pop("handlers", None)
|
||||
@ -382,9 +388,11 @@ class Timer(object):
|
||||
|
||||
|
||||
class ChannelsFilter(logging.Filter):
|
||||
""" Filter out records emitted from a list of enabled channel names,
|
||||
including their children. It works the same as the logging.Filter class,
|
||||
but allows to specify multiple channel names.
|
||||
"""Provides a hierarchical filter for log entries based on channel names.
|
||||
|
||||
Filters out records emitted from a list of enabled channel names,
|
||||
including their children. It works the same as the ``logging.Filter``
|
||||
class, but allows the user to specify multiple channel names.
|
||||
|
||||
>>> import sys
|
||||
>>> handler = logging.StreamHandler(sys.stdout)
|
||||
@ -469,10 +477,12 @@ class CapturingLogHandler(logging.Handler):
|
||||
|
||||
class LogMixin(object):
|
||||
""" Mixin class that adds logging functionality to another class.
|
||||
You can define a new class that subclasses from LogMixin as well as
|
||||
|
||||
You can define a new class that subclasses from ``LogMixin`` as well as
|
||||
other base classes through multiple inheritance.
|
||||
All instances of that class will have a 'log' property that returns
|
||||
a logging.Logger named after their respective <module>.<class>.
|
||||
All instances of that class will have a ``log`` property that returns
|
||||
a ``logging.Logger`` named after their respective ``<module>.<class>``.
|
||||
|
||||
For example:
|
||||
|
||||
>>> class BaseClass(object):
|
||||
|
@ -17,6 +17,16 @@ def _reverseString(s):
|
||||
|
||||
|
||||
def getMacCreatorAndType(path):
|
||||
"""Returns file creator and file type codes for a path.
|
||||
|
||||
Args:
|
||||
path (str): A file path.
|
||||
|
||||
Returns:
|
||||
A tuple of two :py:class:`fontTools.py23.Tag` objects, the first
|
||||
representing the file creator and the second representing the
|
||||
file type.
|
||||
"""
|
||||
if xattr is not None:
|
||||
try:
|
||||
finderInfo = xattr.getxattr(path, 'com.apple.FinderInfo')
|
||||
@ -40,6 +50,17 @@ def getMacCreatorAndType(path):
|
||||
|
||||
|
||||
def setMacCreatorAndType(path, fileCreator, fileType):
|
||||
"""Set file creator and file type codes for a path.
|
||||
|
||||
Note that if the ``xattr`` module is not installed, no action is
|
||||
taken but no error is raised.
|
||||
|
||||
Args:
|
||||
path (str): A file path.
|
||||
fileCreator: A four-character file creator tag.
|
||||
fileType: A four-character file type tag.
|
||||
|
||||
"""
|
||||
if xattr is not None:
|
||||
from fontTools.misc.textTools import pad
|
||||
if not all(len(s) == 4 for s in (fileCreator, fileType)):
|
||||
|
@ -1,4 +1,3 @@
|
||||
""" Tools for reading Mac resource forks. """
|
||||
from fontTools.misc.py23 import *
|
||||
import struct
|
||||
from fontTools.misc import sstruct
|
||||
@ -11,8 +10,25 @@ class ResourceError(Exception):
|
||||
|
||||
|
||||
class ResourceReader(MutableMapping):
|
||||
"""Reader for Mac OS resource forks.
|
||||
|
||||
Parses a resource fork and returns resources according to their type.
|
||||
If run on OS X, this will open the resource fork in the filesystem.
|
||||
Otherwise, it will open the file itself and attempt to read it as
|
||||
though it were a resource fork.
|
||||
|
||||
The returned object can be indexed by type and iterated over,
|
||||
returning in each case a list of py:class:`Resource` objects
|
||||
representing all the resources of a certain type.
|
||||
|
||||
"""
|
||||
def __init__(self, fileOrPath):
|
||||
"""Open a file
|
||||
|
||||
Args:
|
||||
fileOrPath: Either an object supporting a ``read`` method, an
|
||||
``os.PathLike`` object, or a string.
|
||||
"""
|
||||
self._resources = OrderedDict()
|
||||
if hasattr(fileOrPath, 'read'):
|
||||
self.file = fileOrPath
|
||||
@ -121,6 +137,7 @@ class ResourceReader(MutableMapping):
|
||||
|
||||
@property
|
||||
def types(self):
|
||||
"""A list of the types of resources in the resource fork."""
|
||||
return list(self._resources.keys())
|
||||
|
||||
def countResources(self, resType):
|
||||
@ -131,6 +148,7 @@ class ResourceReader(MutableMapping):
|
||||
return 0
|
||||
|
||||
def getIndices(self, resType):
|
||||
"""Returns a list of indices of resources of a given type."""
|
||||
numRes = self.countResources(resType)
|
||||
if numRes:
|
||||
return list(range(1, numRes+1))
|
||||
@ -167,6 +185,15 @@ class ResourceReader(MutableMapping):
|
||||
|
||||
|
||||
class Resource(object):
|
||||
"""Represents a resource stored within a resource fork.
|
||||
|
||||
Attributes:
|
||||
type: resource type.
|
||||
data: resource data.
|
||||
id: ID.
|
||||
name: resource name.
|
||||
attr: attributes.
|
||||
"""
|
||||
|
||||
def __init__(self, resType=None, resData=None, resID=None, resName=None,
|
||||
resAttr=None):
|
||||
|
@ -91,8 +91,13 @@ def _encode_base64(data, maxlinelength=76, indent_level=1):
|
||||
|
||||
|
||||
class Data:
|
||||
"""Wrapper for binary data returned in place of the built-in bytes type
|
||||
when loading property list data with use_builtin_types=False.
|
||||
"""Represents binary data when ``use_builtin_types=False.``
|
||||
|
||||
This class wraps binary data loaded from a plist file when the
|
||||
``use_builtin_types`` argument to the loading function (:py:func:`fromtree`,
|
||||
:py:func:`load`, :py:func:`loads`) is false.
|
||||
|
||||
The actual binary data is retrieved using the ``data`` attribute.
|
||||
"""
|
||||
|
||||
def __init__(self, data):
|
||||
@ -395,6 +400,31 @@ def totree(
|
||||
pretty_print=True,
|
||||
indent_level=1,
|
||||
):
|
||||
"""Convert a value derived from a plist into an XML tree.
|
||||
|
||||
Args:
|
||||
value: Any kind of value to be serialized to XML.
|
||||
sort_keys: Whether keys of dictionaries should be sorted.
|
||||
skipkeys (bool): Whether to silently skip non-string dictionary
|
||||
keys.
|
||||
use_builtin_types (bool): If true, byte strings will be
|
||||
encoded in Base-64 and wrapped in a ``data`` tag; if
|
||||
false, they will be either stored as ASCII strings or an
|
||||
exception raised if they cannot be decoded as such. Defaults
|
||||
to ``True`` if not present. Deprecated.
|
||||
pretty_print (bool): Whether to indent the output.
|
||||
indent_level (int): Level of indentation when serializing.
|
||||
|
||||
Returns: an ``etree`` ``Element`` object.
|
||||
|
||||
Raises:
|
||||
``TypeError``
|
||||
if non-string dictionary keys are serialized
|
||||
and ``skipkeys`` is false.
|
||||
``ValueError``
|
||||
if non-ASCII binary data is present
|
||||
and `use_builtin_types` is false.
|
||||
"""
|
||||
if use_builtin_types is None:
|
||||
use_builtin_types = USE_BUILTIN_TYPES
|
||||
else:
|
||||
@ -410,6 +440,17 @@ def totree(
|
||||
|
||||
|
||||
def fromtree(tree, use_builtin_types=None, dict_type=dict):
|
||||
"""Convert an XML tree to a plist structure.
|
||||
|
||||
Args:
|
||||
tree: An ``etree`` ``Element``.
|
||||
use_builtin_types: If True, binary data is deserialized to
|
||||
bytes strings. If False, it is wrapped in :py:class:`Data`
|
||||
objects. Defaults to True if not provided. Deprecated.
|
||||
dict_type: What type to use for dictionaries.
|
||||
|
||||
Returns: An object (usually a dictionary).
|
||||
"""
|
||||
target = PlistTarget(
|
||||
use_builtin_types=use_builtin_types, dict_type=dict_type
|
||||
)
|
||||
@ -429,6 +470,20 @@ def fromtree(tree, use_builtin_types=None, dict_type=dict):
|
||||
|
||||
|
||||
def load(fp, use_builtin_types=None, dict_type=dict):
|
||||
"""Load a plist file into an object.
|
||||
|
||||
Args:
|
||||
fp: An opened file.
|
||||
use_builtin_types: If True, binary data is deserialized to
|
||||
bytes strings. If False, it is wrapped in :py:class:`Data`
|
||||
objects. Defaults to True if not provided. Deprecated.
|
||||
dict_type: What type to use for dictionaries.
|
||||
|
||||
Returns:
|
||||
An object (usually a dictionary) representing the top level of
|
||||
the plist file.
|
||||
"""
|
||||
|
||||
if not hasattr(fp, "read"):
|
||||
raise AttributeError(
|
||||
"'%s' object has no attribute 'read'" % type(fp).__name__
|
||||
@ -447,6 +502,20 @@ def load(fp, use_builtin_types=None, dict_type=dict):
|
||||
|
||||
|
||||
def loads(value, use_builtin_types=None, dict_type=dict):
|
||||
"""Load a plist file from a string into an object.
|
||||
|
||||
Args:
|
||||
value: A string containing a plist.
|
||||
use_builtin_types: If True, binary data is deserialized to
|
||||
bytes strings. If False, it is wrapped in :py:class:`Data`
|
||||
objects. Defaults to True if not provided. Deprecated.
|
||||
dict_type: What type to use for dictionaries.
|
||||
|
||||
Returns:
|
||||
An object (usually a dictionary) representing the top level of
|
||||
the plist file.
|
||||
"""
|
||||
|
||||
fp = BytesIO(value)
|
||||
return load(fp, use_builtin_types=use_builtin_types, dict_type=dict_type)
|
||||
|
||||
@ -459,6 +528,30 @@ def dump(
|
||||
use_builtin_types=None,
|
||||
pretty_print=True,
|
||||
):
|
||||
"""Write a Python object to a plist file.
|
||||
|
||||
Args:
|
||||
value: An object to write.
|
||||
fp: A file opened for writing.
|
||||
sort_keys (bool): Whether keys of dictionaries should be sorted.
|
||||
skipkeys (bool): Whether to silently skip non-string dictionary
|
||||
keys.
|
||||
use_builtin_types (bool): If true, byte strings will be
|
||||
encoded in Base-64 and wrapped in a ``data`` tag; if
|
||||
false, they will be either stored as ASCII strings or an
|
||||
exception raised if they cannot be represented. Defaults
|
||||
pretty_print (bool): Whether to indent the output.
|
||||
indent_level (int): Level of indentation when serializing.
|
||||
|
||||
Raises:
|
||||
``TypeError``
|
||||
if non-string dictionary keys are serialized
|
||||
and ``skipkeys`` is false.
|
||||
``ValueError``
|
||||
if non-representable binary data is present
|
||||
and `use_builtin_types` is false.
|
||||
"""
|
||||
|
||||
if not hasattr(fp, "write"):
|
||||
raise AttributeError(
|
||||
"'%s' object has no attribute 'write'" % type(fp).__name__
|
||||
@ -493,6 +586,31 @@ def dumps(
|
||||
use_builtin_types=None,
|
||||
pretty_print=True,
|
||||
):
|
||||
"""Write a Python object to a string in plist format.
|
||||
|
||||
Args:
|
||||
value: An object to write.
|
||||
sort_keys (bool): Whether keys of dictionaries should be sorted.
|
||||
skipkeys (bool): Whether to silently skip non-string dictionary
|
||||
keys.
|
||||
use_builtin_types (bool): If true, byte strings will be
|
||||
encoded in Base-64 and wrapped in a ``data`` tag; if
|
||||
false, they will be either stored as strings or an
|
||||
exception raised if they cannot be represented. Defaults
|
||||
pretty_print (bool): Whether to indent the output.
|
||||
indent_level (int): Level of indentation when serializing.
|
||||
|
||||
Returns:
|
||||
string: A plist representation of the Python object.
|
||||
|
||||
Raises:
|
||||
``TypeError``
|
||||
if non-string dictionary keys are serialized
|
||||
and ``skipkeys`` is false.
|
||||
``ValueError``
|
||||
if non-representable binary data is present
|
||||
and `use_builtin_types` is false.
|
||||
"""
|
||||
fp = BytesIO()
|
||||
dump(
|
||||
value,
|
||||
|
Loading…
x
Reference in New Issue
Block a user