Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 7d50020

Browse files
authored
Merge pull request #20400 from QuLogic/stricter-psfontsmap
Make pdftex.map parsing stricter
2 parents e592b61 + 70245b1 commit 7d50020

File tree

3 files changed

+67
-8
lines changed

3 files changed

+67
-8
lines changed

lib/matplotlib/dviread.py

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -828,14 +828,19 @@ def __new__(cls, filename):
828828
# store the unparsed lines (keyed by the first word, which is the
829829
# texname) and parse them on-demand.
830830
with open(filename, 'rb') as file:
831-
self._unparsed = {line.split(b' ', 1)[0]: line for line in file}
831+
self._unparsed = {}
832+
for line in file:
833+
tfmname = line.split(b' ', 1)[0]
834+
self._unparsed.setdefault(tfmname, []).append(line)
832835
self._parsed = {}
833836
return self
834837

835838
def __getitem__(self, texname):
836839
assert isinstance(texname, bytes)
837840
if texname in self._unparsed:
838-
self._parse_and_cache_line(self._unparsed.pop(texname))
841+
for line in self._unparsed.pop(texname):
842+
if self._parse_and_cache_line(line):
843+
break
839844
try:
840845
return self._parsed[texname]
841846
except KeyError:
@@ -879,6 +884,7 @@ def _parse_and_cache_line(self, line):
879884
if not line or line.startswith((b" ", b"%", b"*", b";", b"#")):
880885
return
881886
tfmname = basename = special = encodingfile = fontfile = None
887+
is_subsetted = is_t1 = is_truetype = False
882888
matches = re.finditer(br'"([^"]*)(?:"|$)|(\S+)', line)
883889
for match in matches:
884890
quoted, unquoted = match.groups()
@@ -897,14 +903,13 @@ def _parse_and_cache_line(self, line):
897903
encodingfile = word
898904
else:
899905
fontfile = word
906+
is_subsetted = True
900907
elif tfmname is None:
901908
tfmname = unquoted
902909
elif basename is None:
903910
basename = unquoted
904911
elif quoted:
905912
special = quoted
906-
if basename is None:
907-
basename = tfmname
908913
effects = {}
909914
if special:
910915
words = reversed(special.split())
@@ -913,13 +918,35 @@ def _parse_and_cache_line(self, line):
913918
effects["slant"] = float(next(words))
914919
elif word == b"ExtendFont":
915920
effects["extend"] = float(next(words))
916-
if encodingfile is not None and not encodingfile.startswith(b"/"):
921+
922+
# Verify some properties of the line that would cause it to be ignored
923+
# otherwise.
924+
if fontfile is not None:
925+
if fontfile.endswith((b".ttf", b".ttc")):
926+
is_truetype = True
927+
elif not fontfile.endswith(b".otf"):
928+
is_t1 = True
929+
elif basename is not None:
930+
is_t1 = True
931+
if is_truetype and is_subsetted and encodingfile is None:
932+
return
933+
if not is_t1 and ("slant" in effects or "extend" in effects):
934+
return
935+
if abs(effects.get("slant", 0)) > 1:
936+
return
937+
if abs(effects.get("extend", 0)) > 2:
938+
return
939+
940+
if basename is None:
941+
basename = tfmname
942+
if encodingfile is not None:
917943
encodingfile = find_tex_file(encodingfile)
918-
if fontfile is not None and not fontfile.startswith(b"/"):
944+
if fontfile is not None:
919945
fontfile = find_tex_file(fontfile)
920946
self._parsed[tfmname] = PsFont(
921947
texname=tfmname, psname=basename, effects=effects,
922948
encoding=encodingfile, filename=fontfile)
949+
return True
923950

924951

925952
def _parse_enc(path):

lib/matplotlib/tests/baseline_images/dviread/test.map

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,32 @@
22
TeXfont1 PSfont1 <font1.pfb <font1.enc
33
TeXfont2 PSfont2 <font2.enc <font2.pfa
44
TeXfont3 PSfont3 "1.23 UnknownEffect" <[enc3.foo < font3.pfa
5-
TeXfont4 PSfont4 "-0.1 SlantFont 2.2 ExtendFont" <font4.enc <font4.pfa
5+
TeXfont4 PSfont4 "-0.1 SlantFont 1.2 ExtendFont" <font4.enc <font4.pfa
66
TeXfont5 PSfont5 <encoding1.enc <encoding2.enc <font5.pfb
77
TeXfont6 PSfont6
88
TeXfont7 PSfont7 < font7.enc
99
TeXfont8 PSfont8 <<font8.pfb
1010
TeXfont9 </absolute/font9.pfb
11+
% Only the first of a duplicate key is used.
12+
TeXfontA PSfontA1
13+
TeXfontA PSfontA2
14+
%
15+
% Check SlantFont/ExtendFont
16+
%
17+
% Options are only allowed on T1 fonts (3 TrueType, 1 bitmap, and 1 T1 below).
18+
TeXfontB PSfontB1 "-0.1 SlantFont 1.2 ExtendFont" <fontB1.enc <fontB1.ttf
19+
TeXfontB PSfontB2 "-0.1 SlantFont" <fontB2.enc <fontB2.ttc
20+
TeXfontB PSfontB3 "1.2 ExtendFont" <fontB3.enc <fontB3.otf
21+
TeXfontB "0.1 SlantFont 1.2 ExtendFont"
22+
% Also, only within range ±1 / ±2.
23+
TeXfontB PSfontB4 "1.1 SlantFont 1.2 ExtendFont"
24+
TeXfontB PSfontB5 "0.1 SlantFont 2.2 ExtendFont"
25+
% This is the only one that works:
26+
TeXfontB PSfontB6
27+
%
28+
% Invalid TrueType entry.
29+
%
30+
% Must have encodingfile if subsetted.
31+
TeXfontC PSfontC1 <fontC1.ttf
32+
TeXfontC PSfontC2 <fontC2.ttc
33+
TeXfontC PSfontC3 <fontC3.ttf <8r.enc

lib/matplotlib/tests/test_dviread.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ def test_PsfontsMap(monkeypatch):
2828
else:
2929
assert entry.filename == b'font%d.pfb' % n
3030
if n == 4:
31-
assert entry.effects == {'slant': -0.1, 'extend': 2.2}
31+
assert entry.effects == {'slant': -0.1, 'extend': 1.2}
3232
else:
3333
assert entry.effects == {}
3434
# Some special cases
@@ -44,6 +44,15 @@ def test_PsfontsMap(monkeypatch):
4444
entry = fontmap[b'TeXfont9']
4545
assert entry.psname == b'TeXfont9'
4646
assert entry.filename == b'/absolute/font9.pfb'
47+
# First of duplicates only.
48+
entry = fontmap[b'TeXfontA']
49+
assert entry.psname == b'PSfontA1'
50+
# Slant/Extend only works for T1 fonts.
51+
entry = fontmap[b'TeXfontB']
52+
assert entry.psname == b'PSfontB6'
53+
# Subsetted TrueType must have encoding.
54+
entry = fontmap[b'TeXfontC']
55+
assert entry.psname == b'PSfontC3'
4756
# Missing font
4857
with pytest.raises(KeyError, match='no-such-font'):
4958
fontmap[b'no-such-font']

0 commit comments

Comments
 (0)