@@ -46,10 +46,12 @@ class Type1Font:
46
46
parts : tuple
47
47
A 3-tuple of the cleartext part, the encrypted part, and the finale of
48
48
zeros.
49
+ decrypted : bytes
50
+ The decrypted form of parts[1].
49
51
prop : dict[str, Any]
50
52
A dictionary of font properties.
51
53
"""
52
- __slots__ = ('parts' , 'prop' )
54
+ __slots__ = ('parts' , 'decrypted' , ' prop' )
53
55
54
56
def __init__ (self , input ):
55
57
"""
@@ -68,6 +70,7 @@ def __init__(self, input):
68
70
data = self ._read (file )
69
71
self .parts = self ._split (data )
70
72
73
+ self .decrypted = self ._decrypt (self .parts [1 ], 'eexec' )
71
74
self ._parse ()
72
75
73
76
def _read (self , file ):
@@ -139,6 +142,59 @@ def _split(self, data):
139
142
_token_re = re .compile (br'/{0,2}[^]\0\t\r\v\n ()<>{}/%[]+' )
140
143
_instring_re = re .compile (br'[()\\]' )
141
144
145
+ @staticmethod
146
+ def _decrypt (ciphertext , key , ndiscard = 4 ):
147
+ """
148
+ Decrypt ciphertext using the Type-1 font algorithm
149
+
150
+ The algorithm is described in Adobe's "Adobe Type 1 Font Format".
151
+ The key argument can be an integer, or one of the strings
152
+ 'eexec' and 'charstring', which map to the key specified for the
153
+ corresponding part of Type-1 fonts.
154
+
155
+ The ndiscard argument should be an integer, usually 4.
156
+ That number of bytes is discarded from the beginning of plaintext.
157
+ """
158
+
159
+ key = {'eexec' : 55665 , 'charstring' : 4330 }.get (key , key )
160
+ if not isinstance (key , int ):
161
+ raise ValueError (f'Invalid decryption key { key !r} ' )
162
+
163
+ plaintext = bytearray (len (ciphertext ))
164
+ for i , byte in enumerate (ciphertext ):
165
+ plaintext [i ] = byte ^ (key >> 8 )
166
+ key = ((key + byte ) * 52845 + 22719 ) & 0xffff
167
+
168
+ return bytes (plaintext [ndiscard :])
169
+
170
+ @staticmethod
171
+ def _encrypt (plaintext , key , ndiscard = 4 ):
172
+ """
173
+ Encrypt plaintext using the Type-1 font algorithm
174
+
175
+ The algorithm is described in Adobe's "Adobe Type 1 Font Format".
176
+ The key argument can be an integer, or one of the strings
177
+ 'eexec' and 'charstring', which map to the key specified for the
178
+ corresponding part of Type-1 fonts.
179
+
180
+ The ndiscard argument should be an integer, usually 4. That
181
+ number of bytes is prepended to the plaintext before encryption.
182
+ This function prepends NUL bytes for reproducibility, even though
183
+ the original algorithm uses random bytes, presumably to avoid
184
+ cryptanalysis.
185
+ """
186
+
187
+ key = {'eexec' : 55665 , 'charstring' : 4330 }.get (key , key )
188
+ if not isinstance (key , int ):
189
+ raise ValueError (f'Invalid encryption key { key !r} ' )
190
+
191
+ ciphertext = bytearray (len (plaintext ) + ndiscard )
192
+ for i , byte in enumerate (b'\0 ' * ndiscard + plaintext ):
193
+ ciphertext [i ] = byte ^ (key >> 8 )
194
+ key = ((key + ciphertext [i ]) * 52845 + 22719 ) & 0xffff
195
+
196
+ return bytes (ciphertext )
197
+
142
198
@classmethod
143
199
def _tokens (cls , text ):
144
200
"""
0 commit comments