Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 30 additions & 29 deletions lib/matplotlib/_type1font.py
Original file line number Diff line number Diff line change
Expand Up @@ -523,12 +523,18 @@ def _parse(self):

# Some values need special parsing
if key in ('Subrs', 'CharStrings', 'Encoding', 'OtherSubrs'):
prop[key], endpos = {
parser = {
'Subrs': self._parse_subrs,
'CharStrings': self._parse_charstrings,
'Encoding': self._parse_encoding,
'OtherSubrs': self._parse_othersubrs
}[key](source, data)
}[key]
try:
prop[key], endpos = parser(source, data)
except StopIteration:
raise RuntimeError(
f"Malformed Type1 font file: Incomplete /{key}"
) from None
pos.setdefault(key, []).append((keypos, endpos))
continue

Expand Down Expand Up @@ -618,33 +624,28 @@ def _parse_subrs(self, tokens, _data):
# front lets a malformed font declare a huge count in a few bytes and
# force a large allocation before it is rejected.
entries = {}
try:
for _ in range(count):
next(t for t in tokens if t.is_keyword('dup'))
index_token = next(tokens)
if not index_token.is_number():
raise RuntimeError(
"Token following dup in Subrs definition must be a "
f"number, was {index_token}"
)
nbytes_token = next(tokens)
if not nbytes_token.is_number():
raise RuntimeError(
"Second token following dup in Subrs definition must "
f"be a number, was {nbytes_token}"
)
token = next(tokens)
if not token.is_keyword(self._abbr['RD']):
raise RuntimeError(
f"Token preceding subr must be {self._abbr['RD']}, "
f"was {token}"
)
binary_token = tokens.send(1+nbytes_token.value())
entries[index_token.value()] = binary_token.value()
except StopIteration:
raise RuntimeError(
"Malformed Type1 font file: Incomplete /Subrs"
) from None
for _ in range(count):
next(t for t in tokens if t.is_keyword('dup'))
index_token = next(tokens)
if not index_token.is_number():
raise RuntimeError(
"Token following dup in Subrs definition must be a "
f"number, was {index_token}"
)
nbytes_token = next(tokens)
if not nbytes_token.is_number():
raise RuntimeError(
"Second token following dup in Subrs definition must "
f"be a number, was {nbytes_token}"
)
token = next(tokens)
if not token.is_keyword(self._abbr['RD']):
raise RuntimeError(
f"Token preceding subr must be {self._abbr['RD']}, "
f"was {token}"
)
binary_token = tokens.send(1+nbytes_token.value())
entries[index_token.value()] = binary_token.value()

# The indices must cover 0 to count-1 exactly.
if (len(entries) != count
Expand Down
16 changes: 16 additions & 0 deletions lib/matplotlib/tests/test_type1font.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,3 +210,19 @@ def test_Subrs_bad_indices(tmp_path, indices):
# past the end nor a duplicate may reach the returned array.
with pytest.raises(RuntimeError, match='indices do not cover'):
t1f.Type1Font(_write_subrs_pfa(tmp_path / 'x.pfa', indices))


@pytest.mark.parametrize('private, section', [
(b'/CharStrings 1 begin\n/.notdef 5 RD \x00\x01\x02\x03\x04 ND\n',
'CharStrings'),
(b'/CharStrings 1\n', 'CharStrings'),
(b'/CharStrings 1 begin\n', 'CharStrings'),
(b'/Encoding 1 array\ndup 0 /.notdef put\n', 'Encoding'),
(b'/OtherSubrs [ {} {} \n', 'OtherSubrs'),
])
def test_incomplete_sections(tmp_path, private, section):
# A font that ends in the middle of a section used to raise a bare
# StopIteration out of the parser, which says nothing about the file.
path = _write_pfa(tmp_path / 'x.pfa', private)
with pytest.raises(RuntimeError, match=f'Incomplete /{section}'):
t1f.Type1Font(path)
Loading