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

Skip to content

Add _repr_html_ for fonts #18039

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
story645 opened this issue Jul 23, 2020 · 11 comments · Fixed by #21904
Closed

Add _repr_html_ for fonts #18039

story645 opened this issue Jul 23, 2020 · 11 comments · Fixed by #21904
Labels
Difficulty: Easy https://matplotlib.org/devdocs/devel/contribute.html#good-first-issues Good first issue Open a pull request against these issues if there are no active ones! New feature status: needs comment/discussion needs consensus on next step topic: text/fonts
Milestone

Comments

@story645
Copy link
Member

I'm not totally sure which font object this would go on, but in the vein of colormaps (#15616), it be helpful if you could get a rendering of the font and pull up a font_list (realized I couldn't get a font working 'cause I spelled it wrong).

Was inspired by this blog post that does this: jonathansoma.com/lede/data-studio/matplotlib/list-all-fonts-available-in-matplotlib-plus-samples/

The hard part here is deciding if it's something that would be accepted and on which object it should go.

@story645 story645 added New feature Difficulty: Easy https://matplotlib.org/devdocs/devel/contribute.html#good-first-issues topic: text/fonts status: needs comment/discussion needs consensus on next step labels Jul 23, 2020
@timhoffm
Copy link
Member

Put no thought into this, but you could make it similar to matplotlib.style.available.

@aitikgupta
Copy link
Contributor

I'm not sure how IPython invokes the __repr__ or _repr_html_, however this simple patch on class FontEntry should work if we directly invoke it, i.e:

diff --git a/lib/matplotlib/font_manager.py b/lib/matplotlib/font_manager.py
index a5fdc32b2..767b62020 100644
--- a/lib/matplotlib/font_manager.py
+++ b/lib/matplotlib/font_manager.py
@@ -373,6 +373,9 @@ class FontEntry:
             self.name, os.path.basename(self.fname), self.style, self.variant,
             self.weight, self.stretch)
 
+    def _repr_html_(self):
+        return f"<span style='font-family:{self.name}'>{self.name}</span>"
+
 
 def ttfFontProperty(font):
     """

Invoking it in IPython:

import matplotlib.font_manager
from IPython.core.display import HTML

font = matplotlib.font_manager.fontManager.ttflist[0]       # any FontEntry

HTML(font._repr_html_())

Only direct invocation works, since if we do a HTML(font)-> __repr__ is called and not _repr_html_ (not sure why)

@aitikgupta
Copy link
Contributor

Another issue is, if fonts have a _repr_html_ using the above way (or even the way it's in the blog mentioned), we really just rely on browsers/notebooks/etc. to render that font (using font-family) - matplotlib would just provide it with the names of those fonts.

Let's say someday someone wants to use a custom font, through matplotlib they'd be able to generate text via that font, but if we use this _repr_html_ implementation, they won't get the same font on browsers, etc. (it'll fall back to some web-safe-font)


Now it could be solved if we were to render the fonts as PNGs, but is it worth considering?

@timhoffm
Copy link
Member

timhoffm commented May 2, 2021

Rendering to png is not worth the added complexity. You'll run into HiDPI scaling issues.

Overall, I consider this feature super-optional. There are various ways of showing fonts (without or with matplotlib). Rendering font examples is not a primary or necessary feature for Matplotlib. I'm ok if we add the two-line repr_html as a nicety, but anything beyond is overkill.

@aitikgupta
Copy link
Contributor

Uh, recent PR by @QuLogic converted the FontEntry to a dataclass.
I'm not sure how they work, but #20118 removed even the __repr__ function, not sure how a _repr_html_ could be added. :p

@story645
Copy link
Member Author

Yeah, I think the follow on to this then is probably deciding if there'd be interest/utility in a font.available type thing that prints everything to screen?

@tacaswell
Copy link
Member

We dropped our own __repr__ because data classes come with a nice one out of the box!

We can add a _repr_html_ either by injecting it into the namespace dictionary to make_dataclass (https://docs.python.org/3/library/dataclasses.html#dataclasses.make_dataclass) or by subclassing the result of make_dataclass.

@anntzer
Copy link
Contributor

anntzer commented May 21, 2021

Adding _repr_png_ (which as @aitikgupta mentioned would be more robust than _repr_html_) is easy enough

diff --git i/lib/matplotlib/font_manager.py w/lib/matplotlib/font_manager.py
index 5a9ed79535..aa277ef0f7 100644
--- i/lib/matplotlib/font_manager.py
+++ w/lib/matplotlib/font_manager.py
@@ -25,6 +25,7 @@ Future versions may implement the Level 2 or 2.1 specifications.
 
 import dataclasses
 from functools import lru_cache
+from io import BytesIO
 import json
 import logging
 from numbers import Number
@@ -343,8 +344,7 @@ def findSystemFonts(fontpaths=None, fontext='ttf'):
     return [fname for fname in fontfiles if os.path.exists(fname)]
 
 
-FontEntry = dataclasses.make_dataclass(
-    'FontEntry', [
+class FontEntry(dataclasses.make_dataclass('FontEntry', [
         ('fname', str, dataclasses.field(default='')),
         ('name', str, dataclasses.field(default='')),
         ('style', str, dataclasses.field(default='normal')),
@@ -352,13 +352,20 @@ FontEntry = dataclasses.make_dataclass(
         ('weight', str, dataclasses.field(default='normal')),
         ('stretch', str, dataclasses.field(default='normal')),
         ('size', str, dataclasses.field(default='medium')),
-    ],
-    namespace={
-        '__doc__': """
+])):
+    """
     A class for storing Font properties.
 
     It is used when populating the font lookup dictionary.
-    """})
+    """
+
+    def _repr_png_(self):
+        from matplotlib.figure import Figure  # Circular import.
+        fig = Figure()
+        fig.text(0, 0, self.name, font=Path(self.fname))
+        with BytesIO() as buf:
+            fig.savefig(buf, bbox_inches="tight", transparent=True)
+            return buf.getvalue()
 
 
 def ttfFontProperty(font):

but even then there's the problem that some of the fonts we ship (e.g. computer modern symbol) or some system fonts (wingdings-like fonts) don't even have all the glyphs to represent their own names...

@aitikgupta
Copy link
Contributor

some of the fonts we ship (e.g. computer modern symbol) or some system fonts (wingdings-like fonts) don't even have all the glyphs to represent their own names...

But should that be a problem?
If a certain font doesn't have req glyphs this would render incorrectly, whereas browsers should render something web-safe.

I think repr of a font object should just stick to itself, even if it means it can't display its own name.
(choice between incorrect and more-incorrect :p)

@timhoffm
Copy link
Member

PNG has the problem of hidpi (more generally dpi scaling) handling. Without that the PNGs look ugly, particularly with detailed graphics such as fibers. Dpi scaling will become more and more common. I do don‘t think we want to get into that business just for font visualization. So, I recommend not to use PNG.

@aitikgupta aitikgupta added the Good first issue Open a pull request against these issues if there are no active ones! label Sep 4, 2021
@QuLogic QuLogic added this to the v3.6.0 milestone Dec 16, 2021
@khurchla
Copy link

khurchla commented Feb 6, 2022

Thanks again for this folks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Difficulty: Easy https://matplotlib.org/devdocs/devel/contribute.html#good-first-issues Good first issue Open a pull request against these issues if there are no active ones! New feature status: needs comment/discussion needs consensus on next step topic: text/fonts
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants