fonttools/Tests/misc/psCharStrings_test.py

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

205 lines
6.4 KiB
Python
Raw Normal View History

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",
2022-12-13 11:26:36 +00:00
],
)
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),
]
2023-11-03 10:25:15 +00:00
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
2022-12-13 11:26:36 +00:00
sys.exit(unittest.main())