blob: 5eb2f774bb542c2316feaaf009af7fafd56408d6 [file] [log] [blame]
from fontTools.cffLib import PrivateDict
from fontTools.cffLib.specializer import stringToProgram
from fontTools.misc.testTools import getXML, parseXML
from fontTools.misc.psCharStrings import (
T2CharString,
encodeFloat,
encodeFixed,
read_fixed1616,
read_realNumber,
)
from fontTools.pens.recordingPen import RecordingPen
import unittest
def hexenc(s):
return " ".join("%02x" % x for x in s)
class T2CharStringTest(unittest.TestCase):
@classmethod
def stringToT2CharString(cls, string):
return T2CharString(program=stringToProgram(string), private=PrivateDict())
def test_calcBounds_empty(self):
cs = self.stringToT2CharString("endchar")
bounds = cs.calcBounds(None)
self.assertEqual(bounds, None)
def test_calcBounds_line(self):
cs = self.stringToT2CharString(
"100 100 rmoveto 40 10 rlineto -20 50 rlineto endchar"
)
bounds = cs.calcBounds(None)
self.assertEqual(bounds, (100, 100, 140, 160))
def test_calcBounds_curve(self):
cs = self.stringToT2CharString(
"100 100 rmoveto -50 -150 200 0 -50 150 rrcurveto endchar"
)
bounds = cs.calcBounds(None)
self.assertEqual(bounds, (91.90524980688875, -12.5, 208.09475019311125, 100))
def test_charstring_bytecode_optimization(self):
cs = self.stringToT2CharString(
"100.0 100 rmoveto -50.0 -150 200.5 0.0 -50 150 rrcurveto endchar"
)
cs.isCFF2 = False
cs.private._isCFF2 = False
cs.compile()
cs.decompile()
self.assertEqual(
cs.program,
[
100,
100,
"rmoveto",
-50,
-150,
200.5,
0,
-50,
150,
"rrcurveto",
"endchar",
],
)
cs2 = self.stringToT2CharString(
"100.0 rmoveto -50.0 -150 200.5 0.0 -50 150 rrcurveto"
)
cs2.isCFF2 = True
cs2.private._isCFF2 = True
cs2.compile(isCFF2=True)
cs2.decompile()
self.assertEqual(
cs2.program, [100, "rmoveto", -50, -150, 200.5, 0, -50, 150, "rrcurveto"]
)
def test_encodeFloat(self):
testNums = [
# value expected result
(-9.399999999999999, "1e e9 a4 ff"), # -9.4
(9.399999999999999999, "1e 9a 4f"), # 9.4
(456.8, "1e 45 6a 8f"), # 456.8
(0.0, "1e 0f"), # 0
(-0.0, "1e 0f"), # 0
(1.0, "1e 1f"), # 1
(-1.0, "1e e1 ff"), # -1
(98765.37e2, "1e 98 76 53 7f"), # 9876537
(1234567890.0, "1e 1a 23 45 67 9b 09 ff"), # 1234567890
(9.876537e-4, "1e a0 00 98 76 53 7f"), # 9.876537e-24
(9.876537e4, "1e 98 76 5a 37 ff"), # 9.876537e+24
]
for sample in testNums:
encoded_result = encodeFloat(sample[0])
# check to see if we got the expected bytes
self.assertEqual(hexenc(encoded_result), sample[1])
# check to see if we get the same value by decoding the data
decoded_result = read_realNumber(
None,
None,
encoded_result,
1,
)
self.assertEqual(decoded_result[0], float("%.8g" % sample[0]))
# We limit to 8 digits of precision to match the implementation
# of encodeFloat.
def test_encode_decode_fixed(self):
testNums = [
# value expected hex expected float
(-9.399999999999999, "ff ff f6 99 9a", -9.3999939),
(-9.4, "ff ff f6 99 9a", -9.3999939),
(9.399999999999999999, "ff 00 09 66 66", 9.3999939),
(9.4, "ff 00 09 66 66", 9.3999939),
(456.8, "ff 01 c8 cc cd", 456.8000031),
(-456.8, "ff fe 37 33 33", -456.8000031),
]
for value, expected_hex, expected_float in testNums:
encoded_result = encodeFixed(value)
# check to see if we got the expected bytes
self.assertEqual(hexenc(encoded_result), expected_hex)
# check to see if we get the same value by decoding the data
decoded_result = read_fixed1616(
None,
None,
encoded_result,
1,
)
self.assertAlmostEqual(decoded_result[0], expected_float)
def test_toXML(self):
program = [
"107 53.4004 166.199 hstem",
"174.6 163.801 vstem",
"338.4 142.8 rmoveto",
"28 0 21.9 9 15.8 18 15.8 18 7.9 20.79959 0 23.6 rrcurveto",
"endchar",
]
cs = self.stringToT2CharString(" ".join(program))
self.assertEqual(getXML(cs.toXML), program)
def test_fromXML(self):
cs = T2CharString()
for name, attrs, content in parseXML(
[
'<CharString name="period">' " 338.4 142.8 rmoveto",
" 28 0 21.9 9 15.8 18 15.8 18 7.9 20.79959 0 23.6 rrcurveto",
" endchar" "</CharString>",
]
):
cs.fromXML(name, attrs, content)
expected_program = [
338.3999939,
142.8000031,
"rmoveto",
28,
0,
21.8999939,
9,
15.8000031,
18,
15.8000031,
18,
7.8999939,
20.7995911,
0,
23.6000061,
"rrcurveto",
"endchar",
]
self.assertEqual(len(cs.program), len(expected_program))
for arg, expected_arg in zip(cs.program, expected_program):
if isinstance(arg, str):
self.assertIsInstance(expected_arg, str)
self.assertEqual(arg, expected_arg)
else:
self.assertNotIsInstance(expected_arg, str)
self.assertAlmostEqual(arg, expected_arg)
def test_pen_closePath(self):
# Test CFF2/T2 charstring: it does NOT end in "endchar"
# https://github.com/fonttools/fonttools/issues/2455
cs = self.stringToT2CharString(
"100 100 rmoveto -50 -150 200 0 -50 150 rrcurveto"
)
pen = RecordingPen()
cs.draw(pen)
self.assertEqual(pen.value[-1], ("closePath", ()))
if __name__ == "__main__":
import sys
sys.exit(unittest.main())