py23 Add fixedTools: Smart fixedToFloat() and floatToFixed()
Apparently Python 2 and Python 3 have different default
print precisions for floats. Or at least with my build it
is the case. As such, for example with amiri-regular.ttf
I see these annoying differences in the ttx output of the
two versions:
77203c77203
< <component ... scale="0.5999755859375" flags="0x1004"/>
---
> <component ... scale="0.599975585938" flags="0x1004"/>
That's just gross. Specially when these numbers are show to
humans, while we know that's just 0.6. The fixedToFloat()
routine in this module is smart enough to do that.
Not used yet.
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()