From: <md...@us...> - 2008-10-28 18:11:02
|
Revision: 6342 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6342&view=rev Author: mdboom Date: 2008-10-28 18:10:51 +0000 (Tue, 28 Oct 2008) Log Message: ----------- Add nearest neighbor search for fonts. Modified Paths: -------------- trunk/matplotlib/examples/pylab_examples/fonts_demo.py trunk/matplotlib/examples/pylab_examples/fonts_demo_kw.py trunk/matplotlib/lib/matplotlib/font_manager.py trunk/matplotlib/lib/matplotlib/fontconfig_pattern.py trunk/matplotlib/lib/matplotlib/text.py Modified: trunk/matplotlib/examples/pylab_examples/fonts_demo.py =================================================================== --- trunk/matplotlib/examples/pylab_examples/fonts_demo.py 2008-10-28 12:51:39 UTC (rev 6341) +++ trunk/matplotlib/examples/pylab_examples/fonts_demo.py 2008-10-28 18:10:51 UTC (rev 6342) @@ -11,7 +11,7 @@ subplot(111, axisbg='w') font0 = FontProperties() -alignment = {'horizontalalignment':'center', 'verticalalignment':'center'} +alignment = {'horizontalalignment':'center', 'verticalalignment':'baseline'} ### Show family options family = ['serif', 'sans-serif', 'cursive', 'fantasy', 'monospace'] @@ -53,7 +53,7 @@ t = text(0.0, 0.9, 'variant', fontproperties=font1, **alignment) -for k in range(1): +for k in range(2): font = font0.copy() font.set_family('serif') font.set_variant(variant[k]) Modified: trunk/matplotlib/examples/pylab_examples/fonts_demo_kw.py =================================================================== --- trunk/matplotlib/examples/pylab_examples/fonts_demo_kw.py 2008-10-28 12:51:39 UTC (rev 6341) +++ trunk/matplotlib/examples/pylab_examples/fonts_demo_kw.py 2008-10-28 18:10:51 UTC (rev 6342) @@ -8,7 +8,7 @@ from pylab import * subplot(111, axisbg='w') -alignment = {'horizontalalignment':'center', 'verticalalignment':'center'} +alignment = {'horizontalalignment':'center', 'verticalalignment':'baseline'} ### Show family options family = ['serif', 'sans-serif', 'cursive', 'fantasy', 'monospace'] @@ -40,7 +40,7 @@ t = text(0.0, 0.9, 'variant', **alignment) -for k in range(1): +for k in range(2): t = text( 0.0, yp[k], variant[k], family='serif', variant=variant[k], **alignment) Modified: trunk/matplotlib/lib/matplotlib/font_manager.py =================================================================== --- trunk/matplotlib/lib/matplotlib/font_manager.py 2008-10-28 12:51:39 UTC (rev 6341) +++ trunk/matplotlib/lib/matplotlib/font_manager.py 2008-10-28 18:10:51 UTC (rev 6342) @@ -4,9 +4,9 @@ This module provides a single :class:`FontManager` instance that can be shared across backends and platforms. The :func:`findfont` function returns the best TrueType (TTF) font file in the local or -system font path that matches the specified FontProperties. The -FontManager also handles Adobe Font Metrics (AFM) font files for use -by the PostScript backend. +system font path that matches the specified :class:`FontProperties` +instance. The :class:`FontManager` also handles Adobe Font Metrics +(AFM) font files for use by the PostScript backend. The design is based on the `W3C Cascading Style Sheet, Level 1 (CSS1) font specification <http://www.w3.org/TR/1998/REC-CSS2-19980512/>`_. @@ -64,15 +64,53 @@ verbose = matplotlib.verbose -font_scalings = {'xx-small': 0.579, 'x-small': 0.694, 'small': 0.833, - 'medium': 1.0, 'large': 1.200, 'x-large': 1.440, - 'xx-large': 1.728, 'larger': 1.2, 'smaller': 0.833} +font_scalings = { + 'xx-small' : 0.579, + 'x-small' : 0.694, + 'small' : 0.833, + 'medium' : 1.0, + 'large' : 1.200, + 'x-large' : 1.440, + 'xx-large' : 1.728, + 'larger' : 1.2, + 'smaller' : 0.833, + None : 1.0} -weight_dict = {'light': 200, 'normal': 400, 'regular': 400, 'book': 400, - 'medium': 500, 'roman': 500, 'semibold': 600, 'demibold': 600, - 'demi': 600, 'bold': 700, 'heavy': 800, 'extra bold': 800, - 'black': 900} +stretch_dict = { + 'ultra-condensed' : 100, + 'extra-condensed' : 200, + 'condensed' : 300, + 'semi-condensed' : 400, + 'normal' : 500, + 'semi-expanded' : 600, + 'expanded' : 700, + 'extra-expanded' : 800, + 'ultra-expanded' : 900} +weight_dict = { + 'ultralight' : 100, + 'light' : 200, + 'normal' : 400, + 'regular' : 400, + 'book' : 400, + 'medium' : 500, + 'roman' : 500, + 'semibold' : 600, + 'demibold' : 600, + 'demi' : 600, + 'bold' : 700, + 'heavy' : 800, + 'extra bold' : 800, + 'black' : 900} + +font_family_aliases = set([ + 'serif', + 'sans-serif', + 'cursive', + 'fantasy', + 'monospace', + 'sans']) + # OS Font paths MSFolders = \ r'Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders' @@ -117,12 +155,14 @@ 'afm': ('afm',)}[fontext] def win32FontDirectory(): - """Return the user-specified font directory for Win32. This is + """ + Return the user-specified font directory for Win32. This is looked up from the registry key:: \\HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders\Fonts - If the key is not found, $WINDIR/Fonts will be returned.""" + If the key is not found, $WINDIR/Fonts will be returned. + """ try: import _winreg except ImportError: @@ -331,19 +371,21 @@ return weight -class FontKey(object): +class FontEntry(object): """ A class for storing Font properties. It is used when populating the font lookup dictionary. """ def __init__(self, + fname ='', name ='', style ='normal', variant='normal', weight ='normal', stretch='normal', - size ='medium' + size ='medium', ): + self.fname = fname self.name = name self.style = style self.variant = variant @@ -355,12 +397,12 @@ self.size = size -def ttfFontProperty(font): +def ttfFontProperty(fontpath, font): """ A function for populating the :class:`FontKey` by extracting information from the TrueType font file. - *font* is an :class:`FT2Font` instance. + *font* is a :class:`FT2Font` instance. """ name = font.family_name @@ -447,10 +489,10 @@ # !!!! Incomplete size_adjust = None - return FontKey(name, style, variant, weight, stretch, size) + return FontEntry(fontpath, name, style, variant, weight, stretch, size) -def afmFontProperty(font): +def afmFontProperty(fontpath, font): """ A function for populating a :class:`FontKey` instance by extracting information from the AFM font file. @@ -507,37 +549,17 @@ # !!!! Incomplete size_adjust = None - return FontKey(name, style, variant, weight, stretch, size) + return FontEntry(fontpath, name, style, variant, weight, stretch, size) -def add_filename(fontdict, prop, fname): +def createFontList(fontfiles, fontext='ttf'): """ - A function to add a font file name to the font dictionary using - the :class:`FontKey` properties. If a font property has no - dictionary, then create it. + A function to create a font lookup list. The default is to create + a list of TrueType fonts. An AFM font list can optionally be + created. """ - try: - size = str(float(prop.size)) - except ValueError: - size = prop.size - d = fontdict. \ - setdefault(prop.name, {}).\ - setdefault(prop.style, {}).\ - setdefault(prop.variant, {}).\ - setdefault(prop.weight, {}).\ - setdefault(prop.stretch, {}) - d[size] = fname - - -def createFontDict(fontfiles, fontext='ttf'): - """ - A function to create a font lookup dictionary. The default is to - create a dictionary for TrueType fonts. An AFM font dictionary - can optionally be created. - """ - - fontdict = {} + fontlist = [] # Add fonts from list of known font files. seen = {} for fpath in fontfiles: @@ -559,7 +581,7 @@ except RuntimeError: verbose.report("Could not parse font file %s"%fpath) continue - prop = afmFontProperty(font) + prop = afmFontProperty(fpath, font) else: try: font = ft2font.FT2Font(str(fpath)) @@ -570,59 +592,12 @@ verbose.report("Cannot handle unicode filenames") #print >> sys.stderr, 'Bad file is', fpath continue - try: prop = ttfFontProperty(font) + try: prop = ttfFontProperty(fpath, font) except: continue - add_filename(fontdict, prop, fpath) - return fontdict + fontlist.append(prop) + return fontlist -def setWeights(font): - """ - A function to populate missing values in a font weight - dictionary. This proceedure is necessary since the font finding - algorithm always matches on the weight property. - """ - - # !!!! Not completely correct - temp = font.copy() - if len(temp) == 1: - wgt = temp.keys()[0] - for j in range(100, 1000, 100): - font[j] = temp[wgt] - - if 400 in temp: - for j in range(100, 1000, 100): - font[j] = temp[400] - if 500 in temp: - if 400 in temp: - for j in range(500, 1000, 100): - font[j] = temp[500] - else: - for j in range(100, 1000, 100): - font[j] = temp[500] - - if 300 in temp: - for j in [100, 200, 300]: - font[j] = temp[300] - if 200 in temp: - if 300 in temp: - for j in [100, 200]: - font[j] = temp[200] - else: - for j in [100, 200, 300]: - font[j] = temp[200] - - if 800 in temp: - for j in [600, 700, 800, 900]: - font[j] = temp[800] - if 700 in temp: - if 800 in temp: - for j in [600, 700]: - font[j] = temp[700] - else: - for j in [600, 700, 800, 900]: - font[j] = temp[700] - class FontProperties(object): """ A class for storing and manipulating font properties. @@ -633,31 +608,33 @@ specification. The six properties are: - family: A list of font names in decreasing order of priority. - The last item is the default font name and is given the name - of the font family, either 'serif', 'sans-serif', 'cursive', - 'fantasy', or 'monospace'. + The items may include a generic font family name, either + 'serif', 'sans-serif', 'cursive', 'fantasy', or 'monospace'. + In that case, the actual font to be used will be looked up + from the associated rcParam in :file:`matplotlibrc`. - style: Either 'normal', 'italic' or 'oblique'. - variant: Either 'normal' or 'small-caps'. - - stretch: Either an absolute value of 'ultra-condensed', - 'extra- condensed', 'condensed', 'semi-condensed', 'normal', - 'semi-expanded', 'expanded', 'extra-expanded' or - 'ultra-expanded'; or a relative value of narrower or wider. - This property is currently not implemented and is set to - normal. + - stretch: A numeric value in the range 0-1000 or one of + 'ultra-condensed', 'extra-condensed', 'condensed', + 'semi-condensed', 'normal', 'semi-expanded', 'expanded', + 'extra-expanded' or 'ultra-expanded' - - weight: A numeric value in the range 100, 200, 300, ..., 900. + - weight: A numeric value in the range 0-1000 or one of + 'ultralight', 'light', 'normal', 'regular', 'book', 'medium', + 'roman', 'semibold', 'demibold', 'demi', 'bold', 'heavy', + 'extra bold', 'black' - - size: Either an absolute value of 'xx-small', 'x-small', 'small', - 'medium', 'large', 'x-large', 'xx-large'; or a relative value - of smaller or larger; or an absolute font size, e.g. 12; - or 'scalable'. + - size: Either an relative value of 'xx-small', 'x-small', + 'small', 'medium', 'large', 'x-large', 'xx-large' or an + absolute font size, e.g. 12 - The default font property for TrueType fonts is:: + The default font property for TrueType fonts (as specified in the + default :file:`matplotlibrc` file) is:: - sans-serif, normal, normal, normal, 400, scalable. + sans-serif, normal, normal, normal, normal, scalable. Alternatively, a font may be specified using an absolute path to a .ttf file, by using the *fname* kwarg. @@ -672,9 +649,8 @@ <http://www.fontconfig.org/>`_ pattern, if it is the only argument provided. See the documentation on `fontconfig patterns <http://www.fontconfig.org/fontconfig-user.html>`_. This support - does not require fontconfig to be installed or support for it to - be enabled. We are merely borrowing its pattern syntax for use - here. + does not require fontconfig to be installed. We are merely + borrowing its pattern syntax for use here. Note that matplotlib's internal font manager and fontconfig use a different algorithm to lookup fonts, so the results of the same pattern @@ -760,6 +736,7 @@ if self._slant is None: return rcParams['font.style'] return self._slant + get_slant = get_style def get_variant(self): """ @@ -772,7 +749,10 @@ def get_weight(self): """ - Return the font weight. + Set the font weight. Options are: A numeric value in the + range 0-1000 or one of 'light', 'normal', 'regular', 'book', + 'medium', 'roman', 'semibold', 'demibold', 'demi', 'bold', + 'heavy', 'extra bold', 'black' """ if self._weight is None: return rcParams['font.weight'] @@ -780,8 +760,9 @@ def get_stretch(self): """ - Return the font stretch or width. Options are: 'normal', - 'narrow', 'condensed', or 'wide'. + Return the font stretch or width. Options are: 'ultra-condensed', + 'extra-condensed', 'condensed', 'semi-condensed', 'normal', + 'semi-expanded', 'expanded', 'extra-expanded', 'ultra-expanded'. """ if self._stretch is None: return rcParams['font.stretch'] @@ -793,8 +774,17 @@ """ if self._size is None: return rcParams['font.size'] - return float(self._size) + return self._size + def get_size_in_points(self): + if self._size is not None: + try: + return float(self._size) + except ValueError: + pass + default_size = fontManager.get_default_size() + return default_size * font_scalings.get(self._size) + def get_file(self): """ Return the filename of the associated font. @@ -849,46 +839,51 @@ def set_weight(self, weight): """ - Set the font weight. + Set the font weight. May be either a numeric value in the + range 0-1000 or one of 'ultralight', 'light', 'normal', + 'regular', 'book', 'medium', 'roman', 'semibold', 'demibold', + 'demi', 'bold', 'heavy', 'extra bold', 'black' """ - if (weight is not None and - weight not in weight_dict and - weight not in weight_dict.keys()): - raise ValueError("weight is invalid") + if weight is not None: + try: + weight = int(weight) + if weight < 0 or weight > 1000: + raise ValueError() + except ValueError: + if weight not in weight_dict: + raise ValueError("weight is invalid") self._weight = weight def set_stretch(self, stretch): """ - Set the font stretch or width. Options are: 'normal', 'narrow', - 'condensed', or 'wide'. + Set the font stretch or width. Options are: 'ultra-condensed', + 'extra-condensed', 'condensed', 'semi-condensed', 'normal', + 'semi-expanded', 'expanded', 'extra-expanded' or + 'ultra-expanded', or a numeric value in the range 0-1000. """ - if stretch not in ('normal', 'narrow', 'condensed', 'wide', None): - raise ValueError("stretch is invalid") + if stretch is not None: + try: + stretch = int(stretch) + if stretch < 0 or stretch > 1000: + raise ValueError() + except ValueError: + if stretch not in stretch_dict: + raise ValueError("stretch is invalid") self._stretch = stretch def set_size(self, size): """ - Set the font size. Either an absolute value of 'xx-small', - 'x-small', 'small', 'medium', 'large', 'x-large', 'xx-large'; - or a relative value of smaller or larger; or an absolute font - size, e.g. 12; or 'scalable'. + Set the font size. Either an relative value of 'xx-small', + 'x-small', 'small', 'medium', 'large', 'x-large', 'xx-large' + or an absolute font size, e.g. 12. """ - if size is None: - self._size = None - else: - if is_string_like(size): - parent_size = fontManager.get_default_size() - scaling = font_scalings.get(size) - if scaling is not None: - size = parent_size * scaling - else: - try: - size = float(size) - except ValueError: - size = parent_size - assert(type(size) in (int, float)) - self._size = size - get_size_in_points = get_size + if size is not None: + try: + size = float(size) + except ValueError: + if size is not None and size not in font_scalings: + raise ValueError("size is invalid") + self._size = size def set_file(self, file): """ @@ -958,24 +953,18 @@ class FontManager: """ On import, the :class:`FontManager` singleton instance creates a - dictionary of TrueType fonts based on the font properties: name, - style, variant, weight, stretch, and size. The :meth:`findfont` - method searches this dictionary for a font file name that exactly - matches the font properties of the specified text. If none is - found, a default font is returned. By updating the dictionary - with the properties of the found font, the font dictionary can act - like a font cache. - - .. note:: The font lookup mechanism is very exact and brittle. If - the *exact* font specified is not found, a default font is - always returned. This should be improved in the future. + list of TrueType fonts based on the font properties: name, style, + variant, weight, stretch, and size. The :meth:`findfont` method + does a nearest neighbor search to find the font that most closely + matches the specification. If no good enough match is found, a + default font is returned. """ def __init__(self, size=None, weight='normal'): self.__default_weight = weight self.default_size = size - paths = [os.path.join(rcParams['datapath'],'fonts','ttf'), - os.path.join(rcParams['datapath'],'fonts','afm')] + paths = [os.path.join(rcParams['datapath'], 'fonts', 'ttf'), + os.path.join(rcParams['datapath'], 'fonts', 'afm')] # Create list of font paths for pathname in ['TTFPATH', 'AFMPATH']: @@ -1002,7 +991,7 @@ # use anything self.defaultFont = self.ttffiles[0] - self.ttfdict = createFontDict(self.ttffiles) + self.ttflist = createFontList(self.ttffiles) if rcParams['pdf.use14corefonts']: # Load only the 14 PDF core fonts. These fonts do not need to be @@ -1013,12 +1002,15 @@ # ZapfDingbats. afmpath = os.path.join(rcParams['datapath'],'fonts','pdfcorefonts') afmfiles = findSystemFonts(afmpath, fontext='afm') - self.afmdict = createFontDict(afmfiles, fontext='afm') + self.afmlist = createFontList(afmfiles, fontext='afm') else: self.afmfiles = findSystemFonts(paths, fontext='afm') + \ findSystemFonts(fontext='afm') - self.afmdict = createFontDict(self.afmfiles, fontext='afm') + self.afmlist = createFontList(self.afmfiles, fontext='afm') + self.ttf_lookup_cache = {} + self.afm_lookup_cache = {} + def get_default_weight(self): """ Return the default font weight. @@ -1054,17 +1046,133 @@ # !!!! Needs implementing raise NotImplementedError + # Each of the scoring functions below should return a value between + # 0.0 (perfect match) and 1.0 (terrible match) + def score_family(self, families, family2): + """ + Returns a match score between the list of font families in + *families* and the font family name *family2*. + + An exact match anywhere in the list returns 0.0. + + A match by generic font name will return 0.1. + + No match will return 1.0. + """ + for i, family1 in enumerate(families): + if family1.lower() in font_family_aliases: + if family1 == 'sans': + family1 == 'sans-serif' + options = rcParams['font.' + family1] + if family2 in options: + idx = options.index(family2) + return 0.1 + elif family1.lower() == family2.lower(): + return 0.0 + return 1.0 + + def score_style(self, style1, style2): + """ + Returns a match score between *style1* and *style2*. + + An exact match returns 0.0. + + A match between 'italic' and 'oblique' returns 0.1. + + No match returns 1.0. + """ + if style1 == style2: + return 0.0 + elif style1 in ('italic', 'oblique') and \ + style2 in ('italic', 'oblique'): + return 0.1 + return 1.0 + + def score_variant(self, variant1, variant2): + """ + Returns a match score between *variant1* and *variant2*. + + An exact match returns 0.0, otherwise 1.0. + """ + if variant1 == variant2: + return 0.0 + else: + return 1.0 + + def score_stretch(self, stretch1, stretch2): + """ + Returns a match score between *stretch1* and *stretch2*. + + The result is the absolute value of the difference between the + CSS numeric values of *stretch1* and *stretch2*, normalized + between 0.0 and 1.0. + """ + try: + stretchval1 = int(stretch1) + except ValueError: + stretchval1 = stretch_dict.get(stretch1, 500) + try: + stretchval2 = int(stretch2) + except ValueError: + stretchval2 = stretch_dict.get(stretch2, 500) + return abs(stretchval1 - stretchval2) / 1000.0 + + def score_weight(self, weight1, weight2): + """ + Returns a match score between *weight1* and *weight2*. + + The result is the absolute value of the difference between the + CSS numeric values of *weight1* and *weight2*, normalized + between 0.0 and 1.0. + """ + try: + weightval1 = int(weight1) + except ValueError: + weightval1 = weight_dict.get(weight1, 500) + try: + weightval2 = int(weight2) + except ValueError: + weightval2 = weight_dict.get(weight2, 500) + return abs(weightval1 - weightval2) / 1000.0 + + def score_size(self, size1, size2): + """ + Returns a match score between *size1* and *size2*. + + If *size2* (the size specified in the font file) is 'scalable', this + function always returns 0.0, since any font size can be generated. + + Otherwise, the result is the absolute distance between *size1* and + *size2*, normalized so that the usual range of font sizes (6pt - + 72pt) will lie between 0.0 and 1.0. + """ + if size2 == 'scalable': + return 0.0 + # Size value should have already been + try: + sizeval1 = float(size1) + except ValueError: + sizeval1 = self.default_size * font_scalings(size1) + try: + sizeval2 = float(size2) + except ValueError: + return 1.0 + return abs(sizeval1 - sizeval2) / 72.0 + def findfont(self, prop, fontext='ttf'): """ - Search the font dictionary for a font that exactly or closely - matches the specified font properties. See the - :class:`FontProperties` class for a description. + Search the font list for the font that most closely matches + the :class:`FontProperties` *prop*. - The properties are searched in the following order: name, - style, variant, weight, stretch, and size. The font weight - always matches returning the closest weight, and the font size - always matches for scalable fonts. An oblique style font will - be used inplace of a missing italic style font if present. + :meth:`findfont` performs a nearest neighbor search. Each + font is given a similarity score to the target font + properties. The first font with the highest score is + returned. If no matches below a certain threshold are found, + the default font (usually Vera Sans) is returned. + + The result is cached, so subsequent lookups don't have to + perform the O(n) nearest neighbor search. + See the `W3C Cascading Style Sheet, Level 1 <http://www.w3.org/TR/1998/REC-CSS2-19980512/>`_ documentation for a description of the font finding algorithm. @@ -1078,108 +1186,45 @@ return fname if fontext == 'afm': - fontdict = self.afmdict + font_cache = self.afm_lookup_cache + fontlist = self.afmlist else: - fontdict = self.ttfdict + font_cache = self.ttf_lookup_cache + fontlist = self.ttflist - original_name = prop.get_family()[0] - style = prop.get_style() - variant = prop.get_variant() - weight = weight_as_number(prop.get_weight()) - stretch = prop.get_stretch() - size = str(prop.get_size_in_points()) + cached = font_cache.get(prop) + if cached: + return cached - def lookup_name(name): - try: - fname = fontdict[name][style][variant][weight][stretch][size] - verbose.report('\tfindfont cached %(name)s, %(style)s, %(variant)s, %(weight)s, %(stretch)s, %(size)s'%locals(), 'debug') - verbose.report('findfont returning %s'%fname, 'debug') - return fname - except KeyError: - pass + best_score = 1e64 + best_font = None + for font in fontlist: + # Matching family should have highest priority, so it is multiplied + # by 10.0 + score = \ + self.score_family(prop.get_family(), font.name) * 10.0 + \ + self.score_style(prop.get_style(), font.style) + \ + self.score_variant(prop.get_variant(), font.variant) + \ + self.score_weight(prop.get_weight(), font.weight) + \ + self.score_stretch(prop.get_stretch(), font.stretch) + \ + self.score_size(prop.get_size(), font.size) + if score < best_score: + best_score = score + best_font = font - fname = None - font = fontdict - if name in font: - font = font[name] - else: - verbose.report('\tfindfont failed %(name)s'%locals(), 'debug') - return None + 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 + else: + verbose.report('findfont: Matching %s to %s (%s) with score of %f' % + (prop, best_font.name, best_font.fname, best_score)) + result = best_font.fname - if style in font: - font = font[style] - elif style == 'italic' and 'oblique' in font: - font = font['oblique'] - elif style == 'oblique' and 'italic' in font: - font = font['italic'] - else: - verbose.report('\tfindfont failed %(name)s, %(style)s'%locals(), 'debug') - return None + font_cache[prop] = result + return result - if variant in font: - font = font[variant] - else: - verbose.report('\tfindfont failed %(name)s, %(style)s, %(variant)s'%locals(), 'debug') - return None - if weight not in font: - setWeights(font) - if weight not in font: - return None - font = font[weight] - - if stretch in font: - stretch_font = font[stretch] - if 'scalable' in stretch_font: - fname = stretch_font['scalable'] - elif size in stretch_font: - fname = stretch_font[size] - - if fname is None: - for val in font.values(): - if 'scalable' in val: - fname = val['scalable'] - break - - if fname is None: - for val in font.values(): - if size in val: - fname = val[size] - break - - if fname is None: - verbose.report('\tfindfont failed %(name)s, %(style)s, %(variant)s %(weight)s, %(stretch)s'%locals(), 'debug') - else: - fontkey = FontKey(",".join(prop.get_family()), style, variant, weight, stretch, size) - add_filename(fontdict, fontkey, fname) - verbose.report('\tfindfont found %(name)s, %(style)s, %(variant)s %(weight)s, %(stretch)s, %(size)s'%locals(), 'debug') - verbose.report('findfont returning %s'%fname, 'debug') - return fname - - font_family_aliases = set(['serif', 'sans-serif', 'cursive', - 'fantasy', 'monospace', 'sans']) - - for name in prop.get_family(): - if name in font_family_aliases: - if name == 'sans': - name = 'sans-serif' - for name2 in rcParams['font.' + name]: - fname = lookup_name(name2) - if fname: - break - else: - fname = lookup_name(name) - if fname: - break - - if not fname: - fontkey = FontKey(",".join(prop.get_family()), style, variant, weight, stretch, size) - add_filename(fontdict, fontkey, self.defaultFont) - verbose.report('Could not match %s, %s, %s. Returning %s' % (name, style, weight, self.defaultFont)) - return self.defaultFont - return fname - - _is_opentype_cff_font_cache = {} def is_opentype_cff_font(filename): """ @@ -1232,7 +1277,7 @@ return result else: - _fmcache = os.path.join(get_configdir(), 'fontManager.cache') + _fmcache = os.path.join(get_configdir(), 'fontList.cache') fontManager = None Modified: trunk/matplotlib/lib/matplotlib/fontconfig_pattern.py =================================================================== --- trunk/matplotlib/lib/matplotlib/fontconfig_pattern.py 2008-10-28 12:51:39 UTC (rev 6341) +++ trunk/matplotlib/lib/matplotlib/fontconfig_pattern.py 2008-10-28 18:10:51 UTC (rev 6342) @@ -178,5 +178,4 @@ if val != []: val = ','.join(val) props.append(":%s=%s" % (key, val)) - print parse_fontconfig_pattern(''.join(props)) return ''.join(props) Modified: trunk/matplotlib/lib/matplotlib/text.py =================================================================== --- trunk/matplotlib/lib/matplotlib/text.py 2008-10-28 12:51:39 UTC (rev 6341) +++ trunk/matplotlib/lib/matplotlib/text.py 2008-10-28 18:10:51 UTC (rev 6342) @@ -445,9 +445,17 @@ "Return the :class:`~font_manager.FontProperties` object" return self._fontproperties + def get_family(self): + "Return the list of font families used for font lookup" + return self._fontproperties.get_family() + + def get_fontfamily(self): + 'alias for get_family' + return self.get_family() + def get_name(self): "Return the font name as string" - return self._fontproperties.get_family()[-1] # temporary hack. + return self._fontproperties.get_name() def get_style(self): "Return the font style as string" @@ -457,26 +465,42 @@ "Return the font size as integer" return self._fontproperties.get_size_in_points() + def get_variant(self): + "Return the font variant as a string" + return self._fontproperties.get_variant() + + def get_fontvariant(self): + 'alias for get_variant' + return self.get_variant() + def get_weight(self): - "Get the font weight as string" + "Get the font weight as string or number" return self._fontproperties.get_weight() def get_fontname(self): 'alias for get_name' - return self._fontproperties.get_family()[-1] # temporary hack. + return self.get_name() def get_fontstyle(self): 'alias for get_style' - return self._fontproperties.get_style() + return self.get_style() def get_fontsize(self): 'alias for get_size' - return self._fontproperties.get_size_in_points() + return self.get_size() def get_fontweight(self): 'alias for get_weight' - return self._fontproperties.get_weight() + return self.get_weight() + def get_stretch(self): + 'Get the font stretch as a string or number' + return self._fontproperties.get_stretch() + + def get_fontstretch(self): + 'alias for get_stretch' + return self.get_stretch() + def get_ha(self): 'alias for get_horizontalalignment' return self.get_horizontalalignment() @@ -645,35 +669,35 @@ def set_family(self, fontname): """ - Set the font family + Set the font family. May be either a single string, or a list + of strings in decreasing priority. Each string may be either + a real font name or a generic font class name. If the latter, + the specific font names will be looked up in the + :file:`matplotlibrc` file. - ACCEPTS: [ 'serif' | 'sans-serif' | 'cursive' | 'fantasy' | 'monospace' ] + ACCEPTS: [ FONTNAME | 'serif' | 'sans-serif' | 'cursive' | 'fantasy' | 'monospace' ] """ self._fontproperties.set_family(fontname) def set_variant(self, variant): """ - Set the font variant, eg, + Set the font variant, either 'normal' or 'small-caps'. ACCEPTS: [ 'normal' | 'small-caps' ] """ self._fontproperties.set_variant(variant) def set_name(self, fontname): - """ - Set the font name, + """alias for set_family""" + return self.set_family(fontname) - ACCEPTS: string eg, ['Sans' | 'Courier' | 'Helvetica' ...] - """ - self._fontproperties.set_family(fontname) - def set_fontname(self, fontname): - 'alias for set_name' + """alias for set_family""" self.set_family(fontname) def set_style(self, fontstyle): """ - Set the font style + Set the font style. ACCEPTS: [ 'normal' | 'italic' | 'oblique'] """ @@ -681,33 +705,45 @@ def set_fontstyle(self, fontstyle): 'alias for set_style' - self._fontproperties.set_style(fontstyle) + return self.set_style(fontstyle) def set_size(self, fontsize): """ - Set the font size, eg., 8, 10, 12, 14... + Set the font size. May be either a size string, relative to + the default font size, or an absolute font size in points. - ACCEPTS: [ size in points | relative size eg 'smaller', 'x-large' ] + ACCEPTS: [ size in points | 'xx-small' | 'x-small' | 'small' | 'medium' | 'large' | 'x-large' | 'xx-large' ] """ self._fontproperties.set_size(fontsize) def set_fontsize(self, fontsize): 'alias for set_size' - self._fontproperties.set_size(fontsize) + return self.set_size(fontsize) + def set_weight(self, weight): + """ + Set the font weight. + + ACCEPTS: [ a numeric value in range 0-1000 | 'ultralight' | 'light' | 'normal' | 'regular' | 'book' | 'medium' | 'roman' | 'semibold' | 'demibold' | 'demi' | 'bold' | 'heavy' | 'extra bold' | 'black' ] + """ + self._fontproperties.set_weight(weight) + def set_fontweight(self, weight): 'alias for set_weight' - self._fontproperties.set_weight(weight) + return self.set_weight(weight) - def set_weight(self, weight): + def set_stretch(self, stretch): """ - Set the font weight + Set the font stretch (horizontal condensation or expansion). - ACCEPTS: [ 'normal' | 'bold' | 'heavy' | 'light' | 'ultrabold' | - 'ultralight'] + ACCEPTS: [ a numeric value in range 0-1000 | 'ultra-condensed' | 'extra-condensed' | 'condensed' | 'semi-condensed' | 'normal' | 'semi-expanded' | 'expanded' | 'extra-expanded' | 'ultra-expanded' ] """ - self._fontproperties.set_weight(weight) + self._fontproperties.set_stretch(stretch) + def set_fontstretch(self, stretch): + 'alias for set_stretch' + return self.set_stretch(stretch) + def set_position(self, xy): """ Set the (*x*, *y*) position of the text This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |