forked from matplotlib/matplotlib
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtype1font.py
More file actions
105 lines (85 loc) · 3.39 KB
/
Copy pathtype1font.py
File metadata and controls
105 lines (85 loc) · 3.39 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
"""
A class representing a Type 1 font.
This version merely reads pfa and pfb files and splits them for
embedding in pdf files. There is no support yet for subsetting or
anything like that.
Usage (subject to change):
font = Type1Font(filename)
clear_part, encrypted_part, finale = font.parts
Source: Adobe Technical Note #5040, Supporting Downloadable PostScript
Language Fonts.
If extending this class, see also: Adobe Type 1 Font Format, Adobe
Systems Incorporated, third printing, v1.1, 1993. ISBN 0-201-57044-0.
"""
import struct
class Type1Font(object):
def __init__(self, filename):
file = open(filename, 'rb')
try:
data = self._read(file)
finally:
file.close()
self.parts = self._split(data)
def _read(self, file):
rawdata = file.read()
if not rawdata.startswith(chr(128)):
return rawdata
data = ''
while len(rawdata) > 0:
if not rawdata.startswith(chr(128)):
raise RuntimeError, \
'Broken pfb file (expected byte 128, got %d)' % \
ord(rawdata[0])
type = ord(rawdata[1])
if type in (1,2):
length, = struct.unpack('<i', rawdata[2:6])
segment = rawdata[6:6+length]
rawdata = rawdata[6+length:]
if type == 1: # ASCII text: include verbatim
data += segment
elif type == 2: # binary data: encode in hexadecimal
data += ''.join(['%02x' % ord(char)
for char in segment])
elif type == 3: # end of file
break
else:
raise RuntimeError, \
'Unknown segment type %d in pfb file' % type
return data
def _split(self, data):
"""
Split the Type 1 font into its three main parts.
The three parts are: (1) the cleartext part, which ends in a
eexec operator; (2) the encrypted part; (3) the fixed part,
which contains 512 ASCII zeros possibly divided on various
lines, a cleartomark operator, and possibly something else.
"""
# Cleartext part: just find the eexec and skip whitespace
idx = data.index('eexec')
idx += len('eexec')
while data[idx] in ' \t\r\n':
idx += 1
len1 = idx
# Encrypted part: find the cleartomark operator and count
# zeros backward
idx = data.rindex('cleartomark') - 1
zeros = 512
while zeros and data[idx] in ('0', '\n', '\r'):
if data[idx] == '0':
zeros -= 1
idx -= 1
if zeros:
raise RuntimeError, 'Insufficiently many zeros in Type 1 font'
# Convert encrypted part to binary (if we read a pfb file, we
# may end up converting binary to hexadecimal to binary again;
# but if we read a pfa file, this part is already in hex, and
# I am not quite sure if even the pfb format guarantees that
# it will be in binary).
binary = ''.join([chr(int(data[i:i+2], 16))
for i in range(len1, idx, 2)])
return data[:len1], binary, data[idx:]
if __name__ == '__main__':
import sys
font = Type1Font(sys.argv[1])
parts = font.parts
print len(parts[0]), len(parts[1]), len(parts[2])