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

Skip to content

Fixed Issue #8068 - SVG encoding #8415

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 7 commits into from
Closed
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
24 changes: 24 additions & 0 deletions lib/matplotlib/tests/test_backend_svg.py
Original file line number Diff line number Diff line change
Expand Up @@ -208,3 +208,27 @@ def psfont(*args, **kwargs):
ax.text(0.5, 0.5, 'hello')
with tempfile.TemporaryFile() as tmpfile, pytest.raises(ValueError):
fig.savefig(tmpfile, format='svg')


@needs_tex
def test_unicode_won():
matplotlib.rcParams['text.usetex'] = True
matplotlib.rcParams['text.latex.unicode'] = True

fig = plt.figure()
fig.suptitle(r'\textwon')

fd = BytesIO()
fig.savefig(fd, format='svg')
fd.seek(0)
buf = fd.read().decode()
fd.close()

won_id = 'Computer_Modern_Roman-142'
empty_def = '<path id="{0}"/>'.format(won_id)
expected_def = 'id="{0}"'.format(won_id)
expected_ref = 'xlink:href="#{0}"'.format(won_id)

assert empty_def not in buf
assert expected_def in buf
assert expected_ref in buf
30 changes: 17 additions & 13 deletions lib/matplotlib/textpath.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,6 @@ def __init__(self):

self._texmanager = None

self._adobe_standard_encoding = None

def _get_adobe_standard_encoding(self):
enc_name = dviread.find_tex_file('8a.enc')
enc = dviread.Encoding(enc_name)
return {c: i for i, c in enumerate(enc.encoding)}

def _get_font(self, prop):
"""
find a ttf font.
Expand Down Expand Up @@ -299,14 +292,12 @@ def get_glyphs_tex(self, prop, s, glyph_map=None,
# codes are modstly borrowed from pdf backend.

texmanager = self.get_texmanager()
use_glyph = False

if self.tex_font_map is None:
self.tex_font_map = dviread.PsfontsMap(
dviread.find_tex_file('pdftex.map'))

if self._adobe_standard_encoding is None:
self._adobe_standard_encoding = self._get_adobe_standard_encoding()

fontsize = prop.get_size_in_points()
if hasattr(texmanager, "get_dvi"):
dvifilelike = texmanager.get_dvi(s, self.FONT_SCALE)
Expand Down Expand Up @@ -358,11 +349,21 @@ def get_glyphs_tex(self, prop, s, glyph_map=None,
warnings.warn("No supported encoding in font (%s)." %
font_bunch.filename)

# Character is a glyph and needs to be mapped to corresponding index
if charmap_name == "ADOBE_STANDARD" and font_bunch.encoding:
use_glyph = True
enc0 = dviread.Encoding(font_bunch.encoding)
enc = {i: self._adobe_standard_encoding.get(c, None)
for i, c in enumerate(enc0.encoding)}

# Make a list of each glyph by splitting the encoding
enc0_list = []
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is the same as enc0_list = [e.split('/') for e in enc0.encoding] ?

Why do this splitting?

Copy link
Author

@sughandj sughandj Apr 18, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, that'll make a 2D list, but we need 1D.

The encoding that is generated looks like this ['Grave/Acute/Circumflex/Tilde/Dieresis/Hungarumlaut/Ring.....']
Thus splitting at "/" gives us individual character names.
Not only that, each index actually corresponds to its character code
(eg: enc0_list[142] = 'uni20A9' which is the Won character)
Therefore, line 363-364, enumerates the list with i = character code and c = character name and creates a dictionary character code => font index
Later, in the code the glyph is retrieved using this dictionary

if enc:
    charcode = enc.get(glyph, None)

Hope this makes sense :)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how has this ever worked if that is the case?

for e in enc0.encoding:
enc0_list += e.split("/")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: one needs to do e.decode("ascii").split("/") to test this PR on Py3.


# Encoding provided by the font file mapping names to index
enc = {i: font.get_name_index(c) or None
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is in a block for when charmap_name == "ADOBE_STANDARD", why change to not use the standard encoding?

I think the fix should probably be fixed above to select a better character map for the file?

Copy link
Author

@sughandj sughandj Apr 18, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this is the block where if charmap_name == "ADOBE_STANDARD" and font_bunch.encoding:
Since font_bunch already has Unicode values in them, we don't need to specially use the adobe standard file.
Thus, it was removed completely.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then the conditional should be changed, not just silently internally ignored.

for i, c in enumerate(enc0_list)}
else:
use_glyph = False
enc = {}
self._ps_fontd[dvifont.texname] = font, enc

Expand All @@ -382,7 +383,10 @@ def get_glyphs_tex(self, prop, s, glyph_map=None,
charcode = glyph

if charcode is not None:
glyph0 = font.load_char(charcode, flags=ft2font_flag)
if use_glyph:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should probably be merged up into the conditionals above to simplify the logic?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The charcode is set right before we reach this condition if charcode is not None:
Therefore, it seems like the right spot to decide which font method to use to load the charcode.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

your right, I missed that there was a path to get a non-empty enc that did not set use_glyph to True.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, this is very problematic, the code path above where you set use_glyph is a caching mechanism so the second time around this may have the wrong value of use_glyph?

glyph0 = font.load_glyph(charcode, flags=ft2font_flag)
else:
glyph0 = font.load_char(charcode, flags=ft2font_flag)
else:
warnings.warn("The glyph (%d) of font (%s) cannot be "
"converted with the encoding. Glyph may "
Expand Down