2017-05-18 15:01:05 +09:00
|
|
|
from fontTools.cffLib import PrivateDict
|
|
|
|
from fontTools.cffLib.specializer import stringToProgram
|
2019-10-03 18:08:51 +01:00
|
|
|
from fontTools.misc.testTools import getXML, parseXML
|
|
|
|
from fontTools.misc.psCharStrings import (
|
|
|
|
T2CharString,
|
|
|
|
encodeFloat,
|
|
|
|
encodeFixed,
|
|
|
|
read_fixed1616,
|
|
|
|
read_realNumber,
|
|
|
|
)
|
2022-04-09 14:50:59 +02:00
|
|
|
from fontTools.pens.recordingPen import RecordingPen
|
2017-05-18 15:01:05 +09:00
|
|
|
import unittest
|
|
|
|
|
|
|
|
|
2019-10-03 18:08:51 +01:00
|
|
|
def hexenc(s):
|
|
|
|
return " ".join("%02x" % x for x in s)
|
|
|
|
|
|
|
|
|
2017-05-18 15:01:05 +09:00
|
|
|
class T2CharStringTest(unittest.TestCase):
|
|
|
|
@classmethod
|
|
|
|
def stringToT2CharString(cls, string):
|
|
|
|
return T2CharString(program=stringToProgram(string), private=PrivateDict())
|
|
|
|
|
2017-08-01 10:35:39 +09:00
|
|
|
def test_calcBounds_empty(self):
|
2017-05-18 15:01:05 +09:00
|
|
|
cs = self.stringToT2CharString("endchar")
|
2018-01-26 16:53:04 -08:00
|
|
|
bounds = cs.calcBounds(None)
|
2017-08-01 10:35:39 +09:00
|
|
|
self.assertEqual(bounds, None)
|
2017-05-18 15:01:05 +09:00
|
|
|
|
2017-08-01 10:35:39 +09:00
|
|
|
def test_calcBounds_line(self):
|
2017-05-18 15:01:05 +09:00
|
|
|
cs = self.stringToT2CharString(
|
|
|
|
"100 100 rmoveto 40 10 rlineto -20 50 rlineto endchar"
|
|
|
|
)
|
2018-01-26 16:53:04 -08:00
|
|
|
bounds = cs.calcBounds(None)
|
2017-08-01 10:35:39 +09:00
|
|
|
self.assertEqual(bounds, (100, 100, 140, 160))
|
2017-05-18 15:01:05 +09:00
|
|
|
|
2017-08-01 10:35:39 +09:00
|
|
|
def test_calcBounds_curve(self):
|
2017-05-18 15:01:05 +09:00
|
|
|
cs = self.stringToT2CharString(
|
|
|
|
"100 100 rmoveto -50 -150 200 0 -50 150 rrcurveto endchar"
|
|
|
|
)
|
2018-01-26 16:53:04 -08:00
|
|
|
bounds = cs.calcBounds(None)
|
2017-08-01 10:35:39 +09:00
|
|
|
self.assertEqual(bounds, (91.90524980688875, -12.5, 208.09475019311125, 100))
|
2017-05-18 15:01:05 +09:00
|
|
|
|
2018-06-27 13:04:56 -07:00
|
|
|
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
|
|
|
],
|
2018-06-27 13:04:56 -07: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"]
|
|
|
|
)
|
|
|
|
|
2019-01-04 15:48:14 -08:00
|
|
|
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
|
2019-01-05 11:06:59 -08:00
|
|
|
(0.0, "1e 0f"), # 0
|
2019-01-04 15:48:14 -08:00
|
|
|
(-0.0, "1e 0f"), # 0
|
2019-01-05 11:06:59 -08:00
|
|
|
(1.0, "1e 1f"), # 1
|
|
|
|
(-1.0, "1e e1 ff"), # -1
|
2019-01-04 15:48:14 -08:00
|
|
|
(98765.37e2, "1e 98 76 53 7f"), # 9876537
|
2024-05-08 16:43:20 -04:00
|
|
|
(1234567890.0, "1e 12 34 56 79 b2 ff"), # 12345679E2
|
|
|
|
(9.876537e-4, "1e 98 76 53 7c 10 ff"), # 9876537E-10
|
|
|
|
(9.876537e4, "1e 98 76 5a 37 ff"), # 98765.37
|
|
|
|
(1000.0, "1e 1b 3f"), # 1E3
|
|
|
|
(-1000.0, "1e e1 b3 ff"), # -1E3
|
|
|
|
(1e8, "1e 1b 8f"), # 1E8
|
|
|
|
(1e-5, "1e 1c 5f"), # 1E-5
|
|
|
|
(1.2e8, "1e 12 b7 ff"), # 12E7
|
|
|
|
(1.2345e-5, "1e 12 34 5c 9f"), # 12345E-9
|
|
|
|
(9.0987654e8, "1e 90 98 76 54 0f"), # 909876540
|
|
|
|
(0.1, "1e a1 ff"), # .1
|
|
|
|
(-0.1, "1e ea 1f"), # -.1
|
|
|
|
(0.01, "1e 1c 2f"), # 1e-2
|
|
|
|
(-0.01, "1e e1 c2 ff"), # -1e-2
|
|
|
|
(0.0123, "1e 12 3c 4f"), # 123e-4
|
|
|
|
(-0.0123, "1e e1 23 c4 ff"), # -123e-4
|
2019-01-04 15:48:14 -08:00
|
|
|
]
|
2019-01-05 11:06:59 -08:00
|
|
|
|
2019-01-04 15:48:14 -08:00
|
|
|
for sample in testNums:
|
|
|
|
encoded_result = encodeFloat(sample[0])
|
2019-10-03 18:08:51 +01:00
|
|
|
|
2019-01-04 15:48:14 -08:00
|
|
|
# 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,
|
|
|
|
)
|
2019-01-05 11:06:59 -08:00
|
|
|
self.assertEqual(decoded_result[0], float("%.8g" % sample[0]))
|
|
|
|
# We limit to 8 digits of precision to match the implementation
|
|
|
|
# of encodeFloat.
|
2019-01-04 15:48:14 -08:00
|
|
|
|
2019-10-03 18:08:51 +01:00
|
|
|
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:
|
2019-10-03 18:08:51 +01:00
|
|
|
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)
|
|
|
|
|
2022-04-09 14:50:59 +02:00
|
|
|
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", ()))
|
|
|
|
|
2017-05-18 15:01:05 +09:00
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
import sys
|
2022-12-13 11:26:36 +00:00
|
|
|
|
2017-05-18 15:01:05 +09:00
|
|
|
sys.exit(unittest.main())
|