24
24
import binascii
25
25
import enum
26
26
import itertools
27
+ import logging
27
28
import re
28
29
import struct
29
30
30
31
import numpy as np
31
32
32
33
from matplotlib .cbook import _format_approx
34
+ from . import _api
33
35
36
+ _log = logging .getLogger (__name__ )
34
37
35
38
# token types
36
39
_TokenType = enum .Enum ('_TokenType' ,
@@ -46,10 +49,12 @@ class Type1Font:
46
49
parts : tuple
47
50
A 3-tuple of the cleartext part, the encrypted part, and the finale of
48
51
zeros.
52
+ decrypted : bytes
53
+ The decrypted form of parts[1].
49
54
prop : dict[str, Any]
50
55
A dictionary of font properties.
51
56
"""
52
- __slots__ = ('parts' , 'prop' )
57
+ __slots__ = ('parts' , 'decrypted' , ' prop' )
53
58
54
59
def __init__ (self , input ):
55
60
"""
@@ -68,6 +73,7 @@ def __init__(self, input):
68
73
data = self ._read (file )
69
74
self .parts = self ._split (data )
70
75
76
+ self .decrypted = self ._decrypt (self .parts [1 ], 'eexec' )
71
77
self ._parse ()
72
78
73
79
def _read (self , file ):
@@ -125,20 +131,71 @@ def _split(self, data):
125
131
zeros -= 1
126
132
idx -= 1
127
133
if zeros :
128
- raise RuntimeError ('Insufficiently many zeros in Type 1 font' )
134
+ # this may have been a problem on old implementations that
135
+ # used the zeros as necessary padding
136
+ _log .info ('Insufficiently many zeros in Type 1 font' )
129
137
130
138
# Convert encrypted part to binary (if we read a pfb file, we may end
131
139
# up converting binary to hexadecimal to binary again; but if we read
132
140
# a pfa file, this part is already in hex, and I am not quite sure if
133
141
# even the pfb format guarantees that it will be in binary).
134
- binary = binascii .unhexlify (data [len1 :idx + 1 ])
142
+ idx1 = len1 + ((idx - len1 + 2 ) & ~ 1 ) # ensure an even number of bytes
143
+ binary = binascii .unhexlify (data [len1 :idx1 ])
135
144
136
145
return data [:len1 ], binary , data [idx + 1 :]
137
146
138
147
_whitespace_or_comment_re = re .compile (br'[\0\t\r\014\n ]+|%[^\r\n\v]*' )
139
148
_token_re = re .compile (br'/{0,2}[^]\0\t\r\v\n ()<>{}/%[]+' )
140
149
_instring_re = re .compile (br'[()\\]' )
141
150
151
+ @staticmethod
152
+ def _decrypt (ciphertext , key , ndiscard = 4 ):
153
+ """
154
+ Decrypt ciphertext using the Type-1 font algorithm
155
+
156
+ The algorithm is described in Adobe's "Adobe Type 1 Font Format".
157
+ The key argument can be an integer, or one of the strings
158
+ 'eexec' and 'charstring', which map to the key specified for the
159
+ corresponding part of Type-1 fonts.
160
+
161
+ The ndiscard argument should be an integer, usually 4.
162
+ That number of bytes is discarded from the beginning of plaintext.
163
+ """
164
+
165
+ key = _api .check_getitem ({'eexec' : 55665 , 'charstring' : 4330 }, key = key )
166
+ plaintext = []
167
+ for byte in ciphertext :
168
+ plaintext .append (byte ^ (key >> 8 ))
169
+ key = ((key + byte ) * 52845 + 22719 )
A3E2
& 0xffff
170
+
171
+ return bytes (plaintext [ndiscard :])
172
+
173
+ @staticmethod
174
+ def _encrypt (plaintext , key , ndiscard = 4 ):
175
+ """
176
+ Encrypt plaintext using the Type-1 font algorithm
177
+
178
+ The algorithm is described in Adobe's "Adobe Type 1 Font Format".
179
+ The key argument can be an integer, or one of the strings
180
+ 'eexec' and 'charstring', which map to the key specified for the
181
+ corresponding part of Type-1 fonts.
182
+
183
+ The ndiscard argument should be an integer, usually 4. That
184
+ number of bytes is prepended to the plaintext before encryption.
185
+ This function prepends NUL bytes for reproducibility, even though
186
+ the original algorithm uses random bytes, presumably to avoid
187
+ cryptanalysis.
188
+ """
189
+
190
+ key = _api .check_getitem ({'eexec' : 55665 , 'charstring' : 4330 }, key = key )
191
+ ciphertext = []
192
+ for byte in b'\0 ' * ndiscard + plaintext :
193
+ c = byte ^ (key >> 8 )
194
+ ciphertext .append (c )
195
+ key = ((key + c ) * 52845 + 22719 ) & 0xffff
196
+
197
+ return bytes (ciphertext )
198
+
142
199
@classmethod
143
200
def _tokens (cls , text ):
144
201
"""
0 commit comments