Proposed changes to matplotlib fonts

As a final suggestion, we could have the Text object

    > initialize the fontfamily attribute to a sans-serif font
    > list, e.g. the one given above, since that seems to be a
    > popular font family for many users.

I think all of the suggestions are good ones. I wasn't aware of the
CSS standards, but GTK (which provided the matplotlib font model) is
very close to it, including the use of xxlarge, etc.

    > Please let me know what you think about this suggestion and
    > if you have any changes to the design.

I think font handling should be factored out of the Text class into a
dedicated class.

class Font:

   pass

class Text(dpi, bbox, font, etc...):
  pass

AFAIK, noone is directly instantiating Text instances in their code so
I don't think we'll have many API complaints. Then the respective
font finders (ttf and afm) can use the font instance in combination
with the CSS algorithm to find the fonts.

This won't be too hard on the backends since most of them are already
using the font finders to get the ttf or afm filenames, so most of
these changes will be insulated except for changing the arguments of a
few functional calls. The major overhaul will be in matplotlib.text
and in the classes that instantiate text (Axis, Axes, Figure). I like
the idea of using relative sizes in all these classes.

For user API compatibility, the critical thing is to preserve the
getters and setters of Text, since the following is legal code

  t = title('hi mom', fontname='Courier', fontsize=12)

which calls text.set_fontname and text.fontsize under the hood.

But the setters and getters can just forward the call to the new font
instance as necessary.

Vis-a-vis backends:

GTKAgg, Agg, TkAgg, GD, Paint: no problems here as they all use
    ttf_font_finder

PS: As far as the finder algorithm is concerned, it would be nice if
    it was sufficiently generic that backend_ps could use it too. If
    you define an API that the algorithm needs vis-a-vis font
    properties, we modify the AFM and FT2Font classes to provide
    these.

GTK: I think with minor changes we can make GTK play nicely with any
      ttf font on the system. I have to do a little more research.
      If not, the GTK font setup is so close to the CSS one that I can
      do the mapping pretty easily.

WX: There is a standard set of WX fonts. I'm not sure how to map the
     generic ideas you put forth to the concrete fonts wx knows about,
     other than extending the fontnames dictionary to handle as many
     of the typical font names as possible and/or having users set the
     wx fontnames in matplotlibrc.

     It would also help to provide some helper functions which map
     numeric font weights to one of normal|bold|heavy|etc and the
     string font sizes to points (eg 'medium'->12pt) for the backends
     that don't know about these newfangled options.

     That way in backend_wx we could do

        s = font.get_weight_as_string()
        weight = self.fontweights[s] # this dict already exists in WX and GTK

        size = font.get_size_in_points()
        ...etc...

     In addition, Jeremy has indicated an interest in implementing
     WXAgg backend, which would get the new font handling for free.

Division of labor: if you want to make the required changes to
text.Text, ttf_font_finder and (optionally) matplotlibrc I'm all for
it. Once you have a prototype, I can help with all the text
instantiators if you like, or you can do this too. Whichever way, I
can definitely help with the backend ports once the above issues are
resolved.

I've already been planning to refactor the backend text API, that
would have required a font instance anyway. For all the other
attributes (lines, rectangles, etc), the backends don't know about the
matplotlib objects. eg, we say draw_line(x1,y1, x2,y2), not
draw_line(lineObject). But I am currently passing a text instance in
to draw_text.

I realized this was a problem when I wanted to fix newline handling
across backends. The best way to do this is for the Text class to
split strings on newlines, do the layout in the front end, and then
call draw_text(x,y,s,font) for all the newline split strings in the
original string. Likewise, most of the text layout stuff like
alignment that is currently done in the backend should be moved to the
Text class. So I'll work on these changes in parallel.

JDH

John Hunter wrote:

I think font handling should be factored out of the Text class into a
dedicated class.

class Font:

   pass

class Text(dpi, bbox, font, etc...):
  pass

Just to make sure that I have this straight, the Text class should look like this:

     def __init__(self, ..., font, ...):
         ...

         self._color = color
         self._text = text
         self._verticalalignment = verticalalignment
         self._horizontalalignment = horizontalalignment
         self._rotation = rotation
         self._font = font

instead of this:

     def __init__(self, ...):
         ...

         self._color = color
         self._text = text
         self._verticalalignment = verticalalignment
         self._horizontalalignment = horizontalalignment
         self._rotation = rotation
         self._fontname = fontname
         self._fontsize = fontsize
         self._fontweight = fontweight
         self._fontangle = fontangle

If so, do you planning on changing get_fontxxxx() for get_font()?

For user API compatibility, the critical thing is to preserve the
getters and setters of Text, since the following is legal code

  t = title('hi mom', fontname='Courier', fontsize=12)

which calls text.set_fontname and text.fontsize under the hood.

But the setters and getters can just forward the call to the new font
instance as necessary.

OK.

Vis-a-vis backends:

PS: As far as the finder algorithm is concerned, it would be nice if
    it was sufficiently generic that backend_ps could use it too. If
    you define an API that the algorithm needs vis-a-vis font
    properties, we modify the AFM and FT2Font classes to provide
    these.

Yes, this is my intention.

Division of labor: if you want to make the required changes to
text.Text, ttf_font_finder and (optionally) matplotlibrc I'm all for
it. Once you have a prototype, I can help with all the text
instantiators if you like, or you can do this too. Whichever way, I
can definitely help with the backend ports once the above issues are
resolved.

I should be able to do most of it.

  -- Paul

···

--
Paul Barrett, PhD Space Telescope Science Institute
Phone: 410-338-4475 ESS/Science Software Branch
FAX: 410-338-4767 Baltimore, MD 21218