Skip to content

Commit

Permalink
Fix a number of font manager issues:
Browse files Browse the repository at this point in the history
1) AFM fonts now store stretch information in the FontManager database
2) pdf.use14corefonts and ps.useafm will now only use the afm files for their respective formats
3) The fontList.cache file is now versioned -- if the version doesn't match the current version of matplotlib it is thrown away and regenerated

svn path=/trunk/matplotlib/; revision=7956
  • Loading branch information
mdboom committed Nov 12, 2009
1 parent 8c200da commit af9954d
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 37 deletions.
15 changes: 13 additions & 2 deletions lib/matplotlib/backends/backend_pdf.py
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,8 @@ def __init__(self, filename):
else:
raise ValueError("filename must be a path or a file-like object")

self._core14fontdir = os.path.join(
rcParams['datapath'], 'fonts', 'pdfcorefonts')
self.fh = fh
self.currentstream = None # stream object to write to, if any
fh.write("%PDF-1.4\n") # 1.4 is the first version to have alpha
Expand Down Expand Up @@ -507,7 +509,11 @@ def fontName(self, fontprop):
if is_string_like(fontprop):
filename = fontprop
elif rcParams['pdf.use14corefonts']:
filename = findfont(fontprop, fontext='afm')
filename = findfont(
fontprop, fontext='afm', directory=self._core14fontdir)
if filename is None:
filename = findfont(
"Helvetica", fontext='afm', directory=self._core14fontdir)
else:
filename = findfont(fontprop)

Expand Down Expand Up @@ -1743,7 +1749,12 @@ def _get_font_afm(self, prop):
key = hash(prop)
font = self.afm_font_cache.get(key)
if font is None:
filename = findfont(prop, fontext='afm')
filename = findfont(
prop, fontext='afm', directory=self.file._core14fontdir)
if filename is None:
filename = findfont(
"Helvetica", fontext='afm',
directory=self.file._core14fontdir)
font = self.afm_font_cache.get(filename)
if font is None:
fh = file(filename)
Expand Down
10 changes: 8 additions & 2 deletions lib/matplotlib/backends/backend_ps.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,9 @@ def __init__(self, width, height, pswriter, imagedpi=72):
self.used_characters = {}
self.mathtext_parser = MathTextParser("PS")

self._afm_font_dir = os.path.join(
rcParams['datapath'], 'fonts', 'afm')

def track_characters(self, font, s):
"""Keeps track of which characters are required from
each font."""
Expand Down Expand Up @@ -312,10 +315,13 @@ def _get_font_afm(self, prop):
key = hash(prop)
font = self.afmfontd.get(key)
if font is None:
fname = findfont(prop, fontext='afm')
fname = findfont(prop, fontext='afm', directory=self._afm_font_dir)
if fname is None:
fname = findfont(
"Helvetica", fontext='afm', directory=self._afm_font_dir)
font = self.afmfontd.get(fname)
if font is None:
font = AFM(file(findfont(prop, fontext='afm')))
font = AFM(file(fname))
self.afmfontd[fname] = font
self.afmfontd[key] = font
return font
Expand Down
76 changes: 44 additions & 32 deletions lib/matplotlib/font_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,7 @@ def get_fontconfig_fonts(fontext='ttf'):
except OSError:
# Calling fc-list did not work, so we'll just return nothing
return fontfiles

if pipe.returncode == 0:
for line in output.split('\n'):
fname = line.split(':')[0]
Expand Down Expand Up @@ -463,8 +463,7 @@ def ttfFontProperty(font):
# Relative stretches are: wider, narrower
# Child value is: inherit

# !!!! Incomplete
if sfnt4.find('narrow') >= 0 or sfnt4.find('condensed') >= 0 or \
if sfnt4.find('narrow') >= 0 or sfnt4.find('condensed') >= 0 or \
sfnt4.find('cond') >= 0:
stretch = 'condensed'
elif sfnt4.find('demi cond') >= 0:
Expand Down Expand Up @@ -502,6 +501,7 @@ def afmFontProperty(fontpath, font):
"""

name = font.get_familyname()
fontname = font.get_fontname().lower()

# Styles are: italic, oblique, and normal (default)

Expand Down Expand Up @@ -532,9 +532,15 @@ def afmFontProperty(fontpath, font):
# and ultra-expanded.
# Relative stretches are: wider, narrower
# Child value is: inherit

# !!!! Incomplete
stretch = 'normal'
if fontname.find('narrow') >= 0 or fontname.find('condensed') >= 0 or \
fontname.find('cond') >= 0:
stretch = 'condensed'
elif fontname.find('demi cond') >= 0:
stretch = 'semi-condensed'
elif fontname.find('wide') >= 0 or fontname.find('expanded') >= 0:
stretch = 'expanded'
else:
stretch = 'normal'

# Sizes can be absolute and relative.
# Absolute sizes are: xx-small, x-small, small, medium, large, x-large,
Expand Down Expand Up @@ -960,12 +966,20 @@ class FontManager:
matches the specification. If no good enough match is found, a
default font is returned.
"""
# Increment this version number whenever the font cache data
# format or behavior has changed and requires a existing font
# cache files to be rebuilt.
__version__ = 5

def __init__(self, size=None, weight='normal'):
self._version = self.__version__

self.__default_weight = weight
self.default_size = size

paths = [os.path.join(rcParams['datapath'], 'fonts', 'ttf'),
os.path.join(rcParams['datapath'], 'fonts', 'afm')]
os.path.join(rcParams['datapath'], 'fonts', 'afm'),
os.path.join(rcParams['datapath'], 'fonts', 'pdfcorefonts')]

# Create list of font paths
for pathname in ['TTFPATH', 'AFMPATH']:
Expand All @@ -982,32 +996,23 @@ def __init__(self, size=None, weight='normal'):
# Load TrueType fonts and create font dictionary.

self.ttffiles = findSystemFonts(paths) + findSystemFonts()
self.defaultFont = {}

for fname in self.ttffiles:
verbose.report('trying fontname %s' % fname, 'debug')
if fname.lower().find('vera.ttf')>=0:
self.defaultFont = fname
self.defaultFont['ttf'] = fname
break
else:
# use anything
self.defaultFont = self.ttffiles[0]
self.defaultFont['ttf'] = self.ttffiles[0]

self.ttflist = createFontList(self.ttffiles)

if rcParams['pdf.use14corefonts']:
# Load only the 14 PDF core fonts. These fonts do not need to be
# embedded; every PDF viewing application is required to have them:
# Helvetica, Helvetica-Bold, Helvetica-Oblique, Helvetica-BoldOblique,
# Courier, Courier-Bold, Courier-Oblique, Courier-BoldOblique,
# Times-Roman, Times-Bold, Times-Italic, Times-BoldItalic, Symbol,
# ZapfDingbats.
afmpath = os.path.join(rcParams['datapath'],'fonts','pdfcorefonts')
afmfiles = findSystemFonts(afmpath, fontext='afm')
self.afmlist = createFontList(afmfiles, fontext='afm')
else:
self.afmfiles = findSystemFonts(paths, fontext='afm') + \
findSystemFonts(fontext='afm')
self.afmlist = createFontList(self.afmfiles, fontext='afm')
self.afmfiles = findSystemFonts(paths, fontext='afm') + \
findSystemFonts(fontext='afm')
self.afmlist = createFontList(self.afmfiles, fontext='afm')
self.defaultFont['afm'] = None

self.ttf_lookup_cache = {}
self.afm_lookup_cache = {}
Expand Down Expand Up @@ -1151,7 +1156,7 @@ def score_size(self, size1, size2):
return 1.0
return abs(sizeval1 - sizeval2) / 72.0

def findfont(self, prop, fontext='ttf'):
def findfont(self, prop, fontext='ttf', directory=None):
"""
Search the font list for the font that most closely matches
the :class:`FontProperties` *prop*.
Expand All @@ -1162,6 +1167,9 @@ def findfont(self, prop, fontext='ttf'):
returned. If no matches below a certain threshold are found,
the default font (usually Vera Sans) is returned.
`directory`, is specified, will only return fonts from the
given directory (or subdirectory of that directory).
The result is cached, so subsequent lookups don't have to
perform the O(n) nearest neighbor search.
Expand Down Expand Up @@ -1194,6 +1202,10 @@ def findfont(self, prop, fontext='ttf'):
best_font = None

for font in fontlist:
if (directory is not None and
os.path.commonprefix([font.fname, directory]) != directory):
print directory, font.fname, os.path.commonprefix([font.fname, directory])
continue
# Matching family should have highest priority, so it is multiplied
# by 10.0
score = \
Expand All @@ -1211,8 +1223,8 @@ def findfont(self, prop, fontext='ttf'):

if best_font is None or best_score >= 10.0:
verbose.report('findfont: Could not match %s. Returning %s' %
(prop, self.defaultFont))
result = self.defaultFont
(prop, self.defaultFont[fontext]))
result = self.defaultFont[fontext]
else:
verbose.report('findfont: Matching %s to %s (%s) with score of %f' %
(prop, best_font.name, best_font.fname, best_score))
Expand Down Expand Up @@ -1289,16 +1301,16 @@ def _rebuild():

try:
fontManager = pickle_load(_fmcache)
fontManager.default_size = None
verbose.report("Using fontManager instance from %s" % _fmcache)
if (not hasattr(fontManager, '_version') or
fontManager._version != FontManager.__version__):
_rebuild()
else:
fontManager.default_size = None
verbose.report("Using fontManager instance from %s" % _fmcache)
except:
_rebuild()

def findfont(prop, **kw):
global fontManager
font = fontManager.findfont(prop, **kw)
if not os.path.exists(font):
verbose.report("%s returned by pickled fontManager does not exist" % font)
_rebuild()
font = fontManager.findfont(prop, **kw)
return font
6 changes: 5 additions & 1 deletion lib/matplotlib/mathtext.py
Original file line number Diff line number Diff line change
Expand Up @@ -1032,7 +1032,11 @@ def __init__(self, default_font_prop):
self.glyphd = {}
self.fonts = {}

filename = findfont(default_font_prop, fontext='afm')
filename = findfont(default_font_prop, fontext='afm',
directory=self.basepath)
if filename is None:
filename = findfont('Helvetica', fontext='afm',
directory=self.basepath)
default_font = AFM(file(filename, 'r'))
default_font.fname = filename

Expand Down

0 comments on commit af9954d

Please sign in to comment.