FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

Add custom math fallback by andrzejnovak · Pull Request #16509 · matplotlib/matplotlib · GitHub

Repository navigation

Add custom math fallback - #16509

Merged
timhoffm merged 1 commit into
matplotlib:masterfrom
andrzejnovak:fallbackto
Mar 3, 2020
Merged

timhoffm merged 1 commit into
matplotlib:masterfrom
andrzejnovak:fallbackto

Conversation

andrzejnovak commented Feb 14, 2020 •
edited
Loading

Copy link
Copy Markdown
Contributor

PR Summary

Follow-up on #11644

I've switched the logic so it's more transparent when mathtext.fallback is by default 'cm' . The original mathtext.fallback_to_cm defaults to None, but either True or False will raise a warning and override the mathtext.fallback setting.

PR Checklist

  • Has Pytest style unit tests
  • Code is Flake 8 compliant
  • New features are documented, with examples if plot related
  • Documentation is sphinx and numpydoc compliant
  • Added an entry to doc/users/next_whats_new/ if major new feature (follow instructions in README.rst there)
  • Documented in doc/api/api_changes.rst if API changed in a backward-incompatible way

Copy link
Copy Markdown
Contributor Author

@anntzer This should about do it. Seems mpl ships only with DejaVu Sans/STIX so I couldn't test a real figure against an incomplete font that would have to get substituted.

Unsure what's failing the docs though

Comment thread tutorials/text/mathtext.py Outdated

anntzer commented Feb 15, 2020 •
edited
Loading

Copy link
Copy Markdown
Contributor

@geib7592 Can you please comment on this PR? Thanks!

anntzer commented Feb 15, 2020

Copy link
Copy Markdown
Contributor

Looking a bit below, it appears that DejaVuFonts (the actual default) already implements its own fallback solution, which goes to stix, not not cm. If you set mathtext.fallback, does that also override the fallback for dejavu?

I guess this suggests that the proper solution may instead to make mathtext.fontset accept a list of fonts, falling back from one to the next (so e.g. mathtext.fontset: dejavusans, stix or custom, cm or whatnot -- where custom is again defined by mathtext.{cal,rm,tt,it,bf,sf}).

Comment thread lib/matplotlib/mathtext.py Outdated

Copy link
Copy Markdown
Contributor Author

Looking a bit below, it appears that DejaVuFonts (the actual default) already implements its own fallback solution, which goes to stix, not not cm. If you set mathtext.fallback, does that also override the fallback for dejavu?

I guess this suggests that the proper solution may instead to make mathtext.fontset accept a list of fonts, falling back from one to the next (so e.g. mathtext.fontset: dejavusans, stix or custom, cm or whatnot -- where custom is again defined by mathtext.{cal,rm,tt,it,bf,sf}).

IIUC fallback_to_cm only came to effect when using a math.text = custom and the behaviour should not be changed in this PR.

I agree it would be nicer to have a fallback list, but that seems like it would require major code changes, meanwhile the functionality in this PR (or the original PR indeed) is good enough.

Though it could be easily extended to also so StixSansFonts.

Copy link
Copy Markdown

Hello, it's me from #8619. I'm excited to see someone working on this issue. It seems that since matplotlib ships with stix and dejavu sans, adding a fallback to dajavu sans (and stix sans) could be another (very similar) PR, and in my opinion, it would be worthwhile for flexibility in using custom sans-serif fonts.

Allowing a custom fallback font is likely more work, but would be useful for cases I pointed out in #8619. (@anntzer, your suggestion for having mathtext.fontset be a list seems to preclude the future possibility of having a custom fallback font)

I think @anntzer also suggested I add my two cents about why this PR is important. Since posting the feature request in 2017, I've thought a fair amount about how to typeset publication-quality scientific figures. Currently, I don't think any free (or even non-free) software does it perfectly, and it's an area where, with a few well chosen tweaks, matplotlib could really excel.

For example, major scientific publishing companies like Nature and Science explicitly prefer Helvetica for the text of their figures. For Greek letters and mathematical symbols, they use a similarly weighted serif font used in their main text. This PR as it stands really helps move in the right direction; the example in #11644 shows how to use Helvetica with a fallback to stix, which gets closer to Nature's guideline.

I'd love to see more development toward industry standard design principles in the future with similar tweaks to the mathtext and latex engines. If the matplotlib devs are interested, I'd be happy to discuss and get more involved.

Thanks for all your work!

anntzer commented Feb 15, 2020

Copy link
Copy Markdown
Contributor

I'm fine with not doing the most general approach; I just want the various fallback options and their interaction to be properly documented (e.g. whether mathtext.fallback affects dejavu fallback as well).

your suggestion for having mathtext.fontset be a list seems to preclude the future possibility of having a custom fallback font

why?

Copy link
Copy Markdown

Maybe I misunderstood, but right now custom mathfont requires mathtext.fontset=custom, so if you also wanted a custom fallback, you'd need a second custom setting, like allowing mathtext.{cal,rm,tt,it,bf,sf} to be lists too, where missing characters are sourced down the list.

anntzer commented Feb 15, 2020

Copy link
Copy Markdown
Contributor

Ah yes, you want to fallback from one custom font to another one...
I guess making all of mathtext.cal/rm/... lists too would work indeed? (not that it has to be done in this PR)

anntzer commented Feb 16, 2020

Copy link
Copy Markdown
Contributor

If you look a bit below, class DejaVuFonts has

class DejaVuFonts(UnicodeFonts):
    use_cmex = False

    def __init__(self, *args, **kwargs):
        # This must come first so the backend's owner is set correctly
        if isinstance(self, DejaVuSerifFonts):
            self.cm_fallback = StixFonts(*args, **kwargs)
        else:
            self.cm_fallback = StixSansFonts(*args, **kwargs)
        self.bakoma = BakomaFonts(*args, **kwargs)
        TruetypeFonts.__init__(self, *args, **kwargs)

(the point I was mentioning above)

I'm actually now a bit confused by why DejaVuFonts inherits from UnicodeFonts but calls TruetypeFonts.__init__ in its __init__, and am not even sure anymore that DejaVuFonts.cm_fallback is ever used?

Copy link
Copy Markdown
Contributor Author

I think we all agree that what we want is a fallback list for math as well. However, since that's major surgery, especially since the default fonts have a bunch of no-warnings/no-docs fallback which should likely be overhauled, we should open this as a new issue for a new PR.

This PR bring major improvement in just being able to have a sans-serif math fallback for custom fonts. Fulfilling most of what was requested in the 3 separate issues #8619, #11644 and #16492

Re some details, I added the option to explicitly ask for stixsans but actually have trouble finding characters that would be missing in a default font (say Arial) and would be different between stix and stixsans, which seem to differ for regular alphabet chars, but not so much for math symbols. (Basically I can't test is)

Otherwise, if we can agree on this functionality I'll update the tut/whats_new etc...

Comment thread matplotlibrc.template
Comment thread lib/matplotlib/rcsetup.py Outdated

anntzer commented Feb 18, 2020

Copy link
Copy Markdown
Contributor

I think you can add a test by picking some math strings that include a character that's not present in e.g. dejavusans but present in cm/stix/stixsans, and then saving a svg file with that string and rcParams["svg.fonttype"] = None, with fallback configured to cm/stix/stixsans/nofallback, which will saves the individual glyphs; you can then check that fallback correctly occurred (or not, if not falling back).

Copy link
Copy Markdown
Contributor Author

Ok, I did the SVG check and the fallbacks work fine, the issue is just that some of the math characters are the same for both stix/stixsans e.g.("#STIXGeneral-Regular-10938") In reality it looks like it won't matter if the math fallback is stix or stixsans

Comment thread lib/matplotlib/mathtext.py Outdated
Comment thread lib/matplotlib/mathtext.py Outdated

anntzer commented Feb 19, 2020

Copy link
Copy Markdown
Contributor

It's fine if the test doesn't cover all cases, it would still be an improvement over the current situation.

Comment thread lib/matplotlib/mathtext.py Outdated

def __init__(self, *args, **kwargs):
self._stix_fallback = StixFonts(*args, **kwargs)
self.name = "Computer Modern"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

Well, the hope was that you would not have to hardcode this but could instead infer it from the FT2Font object, but I guess that somehow didn't work?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

Maybe I'm doing it wrong, but the only reasonable way I found was self.cm_fallback.default_font_prop.get_family() but that just returns ['sans-serif'] which doesn't help here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

Or fontmap which returns sth like {'cal': '/home/anovak/matplotlib/lib/matplotlib/mpl-data/fonts/ttf/cmsy10.ttf', ... 'cmsy10': '/home/anovak/matplotlib/lib/matplotlib/mpl- data/fonts/ttf/cmex10.ttf', 'cmex10': '/home/anovak/matplotlib/lib/matplotlib/mpl-data/fonts/ttf/cmex10.ttf'} which would need some extra matching overhead to spit out a useful message.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

I was basically considering something like

                if (fontname in ('it', 'regular')
                        and isinstance(self.cm_fallback, StixFonts)):
                    fontname = 'rm'

                g = self.cm_fallback._get_glyph(
                    fontname, font_class, sym, fontsize)
                _log.warning("Substituting with a symbol from {}".format(g[0].family_name))
                # or .postscript_name
                return g

As it turns out, while this would work for stix fallback and most other fonts (I guess), this would fail specifically for computer modern fallback because cmmi10.ttf has poor metadata (it lists its family as "cmmi10"...), but I guess you can always special-case cm; at least this should work for "all" other fallbacks...

andrzejnovak Feb 20, 2020 •
edited
Loading

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

Ah nice, that's quite fun, because you can report per symbol. Checkout:

import matplotlib as mpl
test_str = r'a$b\AA\Finv\breve\gimel\succnapprox$'

mpl.rcParams['mathtext.fontset'] = 'custom'
mpl.rcParams['mathtext.rm'] = 'Courier New'
mpl.rcParams['mathtext.it'] = 'Courier New:italic'
mpl.rcParams['mathtext.bf'] = 'Courier New:bold'

fig, ax = plt.subplots(figsize=(8, 8))

mpl.rcParams['mathtext.fallback'] = 'cm'
txt = fig.text(.5, .5, test_str, fontsize=40, ha='center')

Output:

Substituting with a \Finv from STIXGeneral
Substituting with a \gimel from STIXGeneral
Substituting with a \combiningbreve from Computer Modern
Substituting with a \succnapprox from STIXGeneral
Substituting with a \Finv from STIXGeneral
Substituting with a \gimel from STIXGeneral
Substituting with a \combiningbreve from Computer Modern
Substituting with a \succnapprox from STIXGeneral

Can't figure out why is it doing two loops though. It will do yet another loop if a giure is saved, which is probably too many warnings to show.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

@anntzer yeah, warnings fail tests

anntzer Feb 20, 2020 •
edited
Loading

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

You can use recwarn or pytest.warns (https://docs.pytest.org/en/5.3.5/warnings.html#assertwarnings) to catch them.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

My point was that you actually want to emit a test here, but in the tests you can check that the warning is correctly thrown using pytest.warns.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

better just go back to logging, than changing all those tests

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

that's fine with me too, I don't think the extra logging is too bad(?)

Comment thread lib/matplotlib/mathtext.py Outdated
Comment thread lib/matplotlib/mathtext.py Outdated
Comment thread lib/matplotlib/mathtext.py Outdated
Comment thread lib/matplotlib/mathtext.py Outdated

Copy link
Copy Markdown
Contributor Author

@anntzer that should be it

anntzer commented Feb 24, 2020

Copy link
Copy Markdown
Contributor

A test (per #16509 (comment)) would be nice.

Comment thread lib/matplotlib/tests/test_mathtext.py Outdated
Comment thread lib/matplotlib/tests/test_mathtext.py Outdated

anntzer commented Feb 25, 2020

Copy link
Copy Markdown
Contributor

lgtm, can you please squash?

andrzejnovak commented Feb 25, 2020 •
edited
Loading

Copy link
Copy Markdown
Contributor Author

phew I'm just gonna drop this here when I inevitably need it again https://blog.appsignal.com/2016/09/27/git-rebasing-strategies.html

anntzer commented Feb 25, 2020

Copy link
Copy Markdown
Contributor

looks like you got it to work :)

Copy link
Copy Markdown
Contributor Author

what's failing the codecov?

anntzer commented Feb 25, 2020

Copy link
Copy Markdown
Contributor

It's not failing?

anntzer commented Feb 25, 2020

Copy link
Copy Markdown
Contributor

flake8 is unhappy though

./lib/matplotlib/rcsetup.py:443:25: E128 continuation line under-indented for visual indent
2390./lib/matplotlib/rcsetup.py:444:25: E128 continuation line under-indented for visual indent
23912     E128 continuation line under-indented for visual indent

Copy link
Copy Markdown
Contributor Author

It's not failing?

somehow it's transient?

anntzer commented Feb 25, 2020

Copy link
Copy Markdown
Contributor

Now it's back.
It's pretty flaky anyways (they likely have some race conditions against when the CI run finishes), so we don't overly worry about it.

andrzejnovak force-pushed the fallbackto branch 3 times, most recently from 8c6734f to 5424ff5 Compare February 26, 2020 09:40

anntzer left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

conditional on CI

Copy link
Copy Markdown
Contributor Author

@anntzer what about the codecov

anntzer commented Mar 2, 2020

Copy link
Copy Markdown
Contributor

it's fine.

timhoffm merged commit 09436b4 into matplotlib:master Mar 3, 2020
QuLogic changed the title Add custom math fallback [WIP] Add custom math fallback Mar 3, 2020
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants


Back | FazBrowse Home | New Git URL